// license:BSD-3-Clause
// copyright-holders:Olivier Galibert, R. Belmont, Brad Hughes
//============================================================
//
// input_common.h - Common code for all MAME input modules
//
// SDLMAME by Olivier Galibert and R. Belmont
//
//============================================================
#ifndef INPUT_COMMON_H_
#define INPUT_COMMON_H_
#include "input_module.h"
#include "inputdev.h"
#include <chrono>
#include <memory>
#include <mutex>
#include <queue>
#include <algorithm>
#include <functional>
//============================================================
// PARAMETERS
//============================================================
enum
{
POVDIR_LEFT = 0,
POVDIR_RIGHT,
POVDIR_UP,
POVDIR_DOWN
};
#define MAX_KEYS 256
#define MAX_AXES 32
#define MAX_BUTTONS 32
#define MAX_HATS 8
#define MAX_POV 4
/****************************************************************************
* DirectInput compatible keyboard scan codes
****************************************************************************/
#define KEY_UNKNOWN 0x00
#define KEY_ESCAPE 0x01
#define KEY_1 0x02
#define KEY_2 0x03
#define KEY_3 0x04
#define KEY_4 0x05
#define KEY_5 0x06
#define KEY_6 0x07
#define KEY_7 0x08
#define KEY_8 0x09
#define KEY_9 0x0A
#define KEY_0 0x0B
#define KEY_MINUS 0x0C /* - on main keyboard */
#define KEY_EQUALS 0x0D
#define KEY_BACK 0x0E /* backspace */
#define KEY_TAB 0x0F
#define KEY_Q 0x10
#define KEY_W 0x11
#define KEY_E 0x12
#define KEY_R 0x13
#define KEY_T 0x14
#define KEY_Y 0x15
#define KEY_U 0x16
#define KEY_I 0x17
#define KEY_O 0x18
#define KEY_P 0x19
#define KEY_LBRACKET 0x1A
#define KEY_RBRACKET 0x1B
#define KEY_RETURN 0x1C /* Enter on main keyboard */
#define KEY_LCONTROL 0x1D
#define KEY_A 0x1E
#define KEY_S 0x1F
#define KEY_D 0x20
#define KEY_F 0x21
#define KEY_G 0x22
#define KEY_H 0x23
#define KEY_J 0x24
#define KEY_K 0x25
#define KEY_L 0x26
#define KEY_SEMICOLON 0x27
#define KEY_APOSTROPHE 0x28
#define KEY_GRAVE 0x29 /* accent grave */
#define KEY_LSHIFT 0x2A
#define KEY_BACKSLASH 0x2B
#define KEY_Z 0x2C
#define KEY_X 0x2D
#define KEY_C 0x2E
#define KEY_V 0x2F
#define KEY_B 0x30
#define KEY_N 0x31
#define KEY_M 0x32
#define KEY_COMMA 0x33
#define KEY_PERIOD 0x34 /* . on main keyboard */
#define KEY_SLASH 0x35 /* / on main keyboard */
#define KEY_RSHIFT 0x36
#define KEY_MULTIPLY 0x37 /* * on numeric keypad */
#define KEY_LMENU 0x38 /* left Alt */
#define KEY_SPACE 0x39
#define KEY_CAPITAL 0x3A
#define KEY_F1 0x3B
#define KEY_F2 0x3C
#define KEY_F3 0x3D
#define KEY_F4 0x3E
#define KEY_F5 0x3F
#define KEY_F6 0x40
#define KEY_F7 0x41
#define KEY_F8 0x42
#define KEY_F9 0x43
#define KEY_F10 0x44
#define KEY_NUMLOCK 0x45
#define KEY_SCROLL 0x46 /* Scroll Lock */
#define KEY_NUMPAD7 0x47
#define KEY_NUMPAD8 0x48
#define KEY_NUMPAD9 0x49
#define KEY_SUBTRACT 0x4A /* - on numeric keypad */
#define KEY_NUMPAD4 0x4B
#define KEY_NUMPAD5 0x4C
#define KEY_NUMPAD6 0x4D
#define KEY_ADD 0x4E /* + on numeric keypad */
#define KEY_NUMPAD1 0x4F
#define KEY_NUMPAD2 0x50
#define KEY_NUMPAD3 0x51
#define KEY_NUMPAD0 0x52
#define KEY_DECIMAL 0x53 /* . on numeric keypad */
#define KEY_OEM_102 0x56 /* <> or \| on RT 102-key keyboard (Non-U.S.) */
#define KEY_F11 0x57
#define KEY_F12 0x58
#define KEY_F13 0x64 /* (NEC PC98) */
#define KEY_F14 0x65 /* (NEC PC98) */
#define KEY_F15 0x66 /* (NEC PC98) */
#define KEY_KANA 0x70 /* (Japanese keyboard) */
#define KEY_ABNT_C1 0x73 /* /? on Brazilian keyboard */
#define KEY_CONVERT 0x79 /* (Japanese keyboard) */
#define KEY_NOCONVERT 0x7B /* (Japanese keyboard) */
#define KEY_YEN 0x7D /* (Japanese keyboard) */
#define KEY_ABNT_C2 0x7E /* Numpad . on Brazilian keyboard */
#define KEY_NUMPADEQUALS 0x8D /* = on numeric keypad (NEC PC98) */
#define KEY_PREVTRACK 0x90 /* Previous Track (DIK_CIRCUMFLEX on Japanese keyboard) */
#define KEY_AT 0x91 /* (NEC PC98) */
#define KEY_COLON 0x92 /* (NEC PC98) */
#define KEY_UNDERLINE 0x93 /* (NEC PC98) */
#define KEY_KANJI 0x94 /* (Japanese keyboard) */
#define KEY_STOP 0x95 /* (NEC PC98) */
#define KEY_AX 0x96 /* (Japan AX) */
#define KEY_UNLABELED 0x97 /* (J3100) */
#define KEY_NEXTTRACK 0x99 /* Next Track */
#define KEY_NUMPADENTER 0x9C /* Enter on numeric keypad */
#define KEY_RCONTROL 0x9D
#define KEY_MUTE 0xA0 /* Mute */
#define KEY_CALCULATOR 0xA1 /* Calculator */
#define KEY_PLAYPAUSE 0xA2 /* Play / Pause */
#define KEY_MEDIASTOP 0xA4 /* Media Stop */
#define KEY_VOLUMEDOWN 0xAE /* Volume - */
#define KEY_VOLUMEUP 0xB0 /* Volume + */
#define KEY_WEBHOME 0xB2 /* Web home */
#define KEY_NUMPADCOMMA 0xB3 /* , on numeric keypad (NEC PC98) */
#define KEY_DIVIDE 0xB5 /* / on numeric keypad */
#define KEY_SYSRQ 0xB7
#define KEY_RMENU 0xB8 /* right Alt */
#define KEY_PAUSE 0xC5 /* Pause */
#define KEY_HOME 0xC7 /* Home on arrow keypad */
#define KEY_UP 0xC8 /* UpArrow on arrow keypad */
#define KEY_PRIOR 0xC9 /* PgUp on arrow keypad */
#define KEY_LEFT 0xCB /* LeftArrow on arrow keypad */
#define KEY_RIGHT 0xCD /* RightArrow on arrow keypad */
#define KEY_END 0xCF /* End on arrow keypad */
#define KEY_DOWN 0xD0 /* DownArrow on arrow keypad */
#define KEY_NEXT 0xD1 /* PgDn on arrow keypad */
#define KEY_INSERT 0xD2 /* Insert on arrow keypad */
#define KEY_DELETE 0xD3 /* Delete on arrow keypad */
#define KEY_LWIN 0xDB /* Left Windows key */
#define KEY_RWIN 0xDC /* Right Windows key */
#define KEY_APPS 0xDD /* AppMenu key */
#define KEY_POWER 0xDE /* System Power */
#define KEY_SLEEP 0xDF /* System Sleep */
#define KEY_WAKE 0xE3 /* System Wake */
#define KEY_WEBSEARCH 0xE5 /* Web Search */
#define KEY_WEBFAVORITES 0xE6 /* Web Favorites */
#define KEY_WEBREFRESH 0xE7 /* Web Refresh */
#define KEY_WEBSTOP 0xE8 /* Web Stop */
#define KEY_WEBFORWARD 0xE9 /* Web Forward */
#define KEY_WEBBACK 0xEA /* Web Back */
#define KEY_MYCOMPUTER 0xEB /* My Computer */
#define KEY_MAIL 0xEC /* Mail */
#define KEY_MEDIASELECT 0xED /* Media Select */
//============================================================
// device_info
//============================================================
class input_device_list;
class device_info
{
friend input_device_list;
private:
std::string m_name;
std::string m_id;
input_device * m_device;
running_machine & m_machine;
input_module & m_module;
input_device_class m_deviceclass;
public:
// Constructor
device_info(running_machine &machine, const char *name, const char *id, input_device_class deviceclass, input_module &module)
: m_name(name),
m_id(id),
m_device(nullptr),
m_machine(machine),
m_module(module),
m_deviceclass(deviceclass)
{
}
// Destructor
virtual ~device_info() {}
// Getters
running_machine & machine() const { return m_machine; }
const char * name() const { return m_name.c_str(); }
const char * id() const { return m_id.c_str(); }
input_device * device() const { return m_device; }
input_module & module() const { return m_module; }
input_device_class deviceclass() const { return m_deviceclass; }
// Poll and reset methods
virtual void poll() {};
virtual void reset() = 0;
};
//============================================================
// event_based_device
//============================================================
#define DEFAULT_EVENT_QUEUE_SIZE 20
template <class TEvent>
class event_based_device : public device_info
{
private:
std::queue<TEvent> m_event_queue;
protected:
std::mutex m_device_lock;
virtual void process_event(TEvent &ev) = 0;
public:
event_based_device(running_machine &machine, const char *name, const char *id, input_device_class deviceclass, input_module &module)
: device_info(machine, name, id, deviceclass, module)
{
}
void queue_events(const TEvent *events, int count)
{
std::lock_guard<std::mutex> scope_lock(m_device_lock);
for (int i = 0; i < count; i++)
m_event_queue.push(events[i]);
// If we've gone over the size, remove old events from the queue
while (m_event_queue.size() > DEFAULT_EVENT_QUEUE_SIZE)
m_event_queue.pop();
}
void virtual poll() override
{
std::lock_guard<std::mutex> scope_lock(m_device_lock);
// Process each event until the queue is empty
while (!m_event_queue.empty())
{
TEvent &next_event = m_event_queue.front();
process_event(next_event);
m_event_queue.pop();
}
}
};
//============================================================
// input_device_list class
//============================================================
class input_device_list
{
private:
std::vector<std::unique_ptr<device_info>> m_list;
public:
size_t size() const { return m_list.size(); }
auto begin() { return m_list.begin(); }
auto end() { return m_list.end(); }
void poll_devices()
{
for (auto &device: m_list)
device->poll();
}
void reset_devices()
{
for (auto &device: m_list)
device->reset();
}
void free_device(device_info* devinfo)
{
// find the device to remove
auto device_matches = [devinfo](std::unique_ptr<device_info> &device) { return devinfo == device.get(); };
m_list.erase(std::remove_if(std::begin(m_list), std::end(m_list), device_matches), m_list.end());
}
void for_each_device(std::function<void (device_info*)> action)
{
for (auto &device: m_list)
action(device.get());
}
void free_all_devices()
{
while (!m_list.empty())
m_list.pop_back();
}
template <typename TActual, typename... TArgs>
TActual* create_device(running_machine &machine, const char *name, const char *id, input_module &module, TArgs&&... args)
{
// allocate the device object
auto devinfo = std::make_unique<TActual>(machine, name, id, module, std::forward<TArgs>(args)...);
return add_device(machine, std::move(devinfo));
}
template <typename TActual>
TActual* add_device(running_machine &machine, std::unique_ptr<TActual> devinfo)
{
// Add the device to the machine
devinfo->m_device = machine.input().device_class(devinfo->deviceclass()).add_device(devinfo->name(), devinfo->id(), devinfo.get());
// append us to the list
m_list.push_back(std::move(devinfo));
return static_cast<TActual*>(m_list.back().get());
}
};
// keyboard translation table
struct key_trans_entry {
input_item_id mame_key;
#if defined(OSD_SDL)
int sdl_scancode;
#elif defined(OSD_WINDOWS)
int scan_code;
unsigned char virtual_key;
#elif defined(OSD_UWP)
int scan_code;
Windows::System::VirtualKey virtual_key;
#endif
char ascii_key;
char const * mame_key_name;
char * ui_name;
};
class keyboard_trans_table
{
private:
// default constructor is private
keyboard_trans_table();
static key_trans_entry s_default_table[];
std::unique_ptr<key_trans_entry[]> m_custom_table;
key_trans_entry * m_table;
uint32_t m_table_size;
public:
// constructor
keyboard_trans_table(std::unique_ptr<key_trans_entry[]> table, unsigned int size);
// getters/setters
uint32_t size() const { return m_table_size; }
// public methods
input_item_id lookup_mame_code(const char * scode) const;
int lookup_mame_index(const char * scode) const;
#if defined(OSD_WINDOWS)
input_item_id map_di_scancode_to_itemid(int di_scancode) const;
int vkey_for_mame_code(input_code code) const;
#elif defined(OSD_UWP)
input_item_id map_di_scancode_to_itemid(int di_scancode) const;
const char* ui_label_for_mame_key(input_item_id code) const;
#endif
static keyboard_trans_table& instance()
{
static keyboard_trans_table s_instance;
return s_instance;
}
key_trans_entry & operator [](int i) const { return m_table[i]; }
};
//============================================================
// input_module_base - base class for input modules
//============================================================
class osd_options;
typedef std::chrono::high_resolution_clock clock_type;
typedef std::chrono::time_point<std::chrono::high_resolution_clock> timepoint_type;
// 10 milliseconds polling interval
#define MIN_POLLING_INTERVAL 10
class input_module_base : public input_module
{
public:
input_module_base(const char *type, const char* name)
: input_module(type, name),
m_input_enabled(false),
m_mouse_enabled(false),
m_lightgun_enabled(false),
m_input_paused(false),
m_options(nullptr)
{
}
private:
bool m_input_enabled;
bool m_mouse_enabled;
bool m_lightgun_enabled;
bool m_input_paused;
const osd_options * m_options;
input_device_list m_devicelist;
clock_type m_clock;
timepoint_type m_last_poll;
protected:
void set_mouse_enabled(bool value) { m_mouse_enabled = value; }
public:
const osd_options * options() const { return m_options; }
input_device_list * devicelist() { return &m_devicelist; }
bool input_enabled() const { return m_input_enabled; }
bool input_paused() const { return m_input_paused; }
bool mouse_enabled() const { return m_mouse_enabled; }
bool lightgun_enabled() const { return m_lightgun_enabled; }
int init(const osd_options &options) override;
void poll_if_necessary(running_machine &machine) override
{
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(m_clock.now() - m_last_poll);
if (elapsed.count() >= MIN_POLLING_INTERVAL)
{
poll(machine);
}
}
virtual void poll(running_machine &machine)
{
// ignore if not enabled
if (m_input_enabled)
{
// grab the current time
m_last_poll = m_clock.now();
before_poll(machine);
// track if mouse/lightgun is enabled, for mouse hiding purposes
m_mouse_enabled = machine.input().device_class(DEVICE_CLASS_MOUSE).enabled();
m_lightgun_enabled = machine.input().device_class(DEVICE_CLASS_LIGHTGUN).enabled();
}
// poll all of the devices
if (should_poll_devices(machine))
{
m_devicelist.poll_devices();
}
else
{
m_devicelist.reset_devices();
}
}
virtual void pause() override
{
// keep track of the paused state
m_input_paused = true;
}
virtual void resume() override
{
// keep track of the paused state
m_input_paused = false;
}
virtual void exit() override
{
devicelist()->free_all_devices();
}
protected:
virtual int init_internal() { return 0; }
virtual bool should_poll_devices(running_machine &machine) = 0;
virtual void before_poll(running_machine &machine) {}
};
template <class TItem>
int generic_button_get_state(void *device_internal, void *item_internal)
{
device_info *devinfo = static_cast<device_info *>(device_internal);
TItem *itemdata = static_cast<TItem*>(item_internal);
// return the current state
devinfo->module().poll_if_necessary(devinfo->machine());
return *itemdata >> 7;
}
template <class TItem>
int generic_axis_get_state(void *device_internal, void *item_internal)
{
device_info *devinfo = static_cast<device_info *>(device_internal);
TItem *axisdata = static_cast<TItem*>(item_internal);
// return the current state
devinfo->module().poll_if_necessary(devinfo->machine());
return *axisdata;
}
//============================================================
// default_button_name
//============================================================
inline static const char *default_button_name(int which)
{
static char buffer[20];
snprintf(buffer, std::size(buffer), "B%d", which);
return buffer;
}
//============================================================
// default_pov_name
//============================================================
inline static const char *default_pov_name(int which)
{
static char buffer[20];
snprintf(buffer, std::size(buffer), "POV%d", which);
return buffer;
}
// default axis names
const char *const default_axis_name[] =
{
"X", "Y", "Z", "RX",
"RY", "RZ", "SL1", "SL2"
};
inline static int32_t normalize_absolute_axis(double raw, double rawmin, double rawmax)
{
double center = (rawmax + rawmin) / 2.0;
// make sure we have valid data
if (rawmin >= rawmax)
return int32_t(raw);
// above center
if (raw >= center)
{
double result = (raw - center) * INPUT_ABSOLUTE_MAX / (rawmax - center);
return std::min(result, (double)INPUT_ABSOLUTE_MAX);
}
// below center
else
{
double result = -((center - raw) * (double)-INPUT_ABSOLUTE_MIN / (center - rawmin));
return std::max(result, (double)INPUT_ABSOLUTE_MIN);
}
}
#endif // INPUT_COMMON_H_