diff --git a/scripts/src/emu.lua b/scripts/src/emu.lua index cc0e2f8e649..606c262c504 100644 --- a/scripts/src/emu.lua +++ b/scripts/src/emu.lua @@ -132,6 +132,8 @@ files { MAME_DIR .. "src/emu/mconfig.h", MAME_DIR .. "src/emu/memarray.cpp", MAME_DIR .. "src/emu/memarray.h", + MAME_DIR .. "src/emu/natkeyboard.cpp", + MAME_DIR .. "src/emu/natkeyboard.h", MAME_DIR .. "src/emu/network.cpp", MAME_DIR .. "src/emu/network.h", MAME_DIR .. "src/emu/parameters.cpp", diff --git a/src/devices/machine/pckeybrd.cpp b/src/devices/machine/pckeybrd.cpp index 122677cfc64..2a1d4719013 100644 --- a/src/devices/machine/pckeybrd.cpp +++ b/src/devices/machine/pckeybrd.cpp @@ -20,6 +20,7 @@ #include "emu.h" #include "pckeybrd.h" +#include "natkeyboard.h" /* AT keyboard documentation comes from www.beyondlogic.org and HelpPC documentation */ diff --git a/src/emu/debug/debugcmd.cpp b/src/emu/debug/debugcmd.cpp index 9fd4d1a860b..dbedc270767 100644 --- a/src/emu/debug/debugcmd.cpp +++ b/src/emu/debug/debugcmd.cpp @@ -17,6 +17,7 @@ #include "express.h" #include "debughlp.h" #include "debugvw.h" +#include "natkeyboard.h" #include "render.h" #include diff --git a/src/emu/ioport.cpp b/src/emu/ioport.cpp index bf6a4cd52c4..5c5b328f49f 100644 --- a/src/emu/ioport.cpp +++ b/src/emu/ioport.cpp @@ -96,6 +96,7 @@ #include "xmlfile.h" #include "profiler.h" #include "ui/uimain.h" +#include "natkeyboard.h" #include "osdepend.h" @@ -110,39 +111,14 @@ namespace { #define INPUT_PORT_OVERRIDE_FULLY_NUKES_PREVIOUS 1 -//************************************************************************** -// DEBUGGING -//************************************************************************** - -#define LOG_NATURAL_KEYBOARD 0 - - - //************************************************************************** // CONSTANTS //************************************************************************** const int SPACE_COUNT = 3; -const int KEY_BUFFER_SIZE = 4096; -const unicode_char INVALID_CHAR = '?'; -//************************************************************************** -// TYPE DEFINITIONS -//************************************************************************** - -// character information -struct char_info -{ - unicode_char ch; - const char *name; - const char *alternate; // alternative string, in UTF-8 - - static const char_info *find(unicode_char target); -}; - - //************************************************************************** // INLINE FUNCTIONS //************************************************************************** @@ -181,315 +157,6 @@ inline INT32 apply_scale(INT32 value, INT64 scale) -//************************************************************************** -// GLOBAL VARIABLES -//************************************************************************** - -// XML attributes for the different types -const char *const seqtypestrings[] = { "standard", "increment", "decrement" }; - -// master character info table -const char_info charinfo[] = -{ - { 0x0008, "Backspace", nullptr }, // Backspace - { 0x0009, "Tab", " " }, // Tab - { 0x000c, "Clear", nullptr }, // Clear - { 0x000d, "Enter", nullptr }, // Enter - { 0x001a, "Esc", nullptr }, // Esc - { 0x0020, "Space", " " }, // Space - { 0x0061, nullptr, "A" }, // a - { 0x0062, nullptr, "B" }, // b - { 0x0063, nullptr, "C" }, // c - { 0x0064, nullptr, "D" }, // d - { 0x0065, nullptr, "E" }, // e - { 0x0066, nullptr, "F" }, // f - { 0x0067, nullptr, "G" }, // g - { 0x0068, nullptr, "H" }, // h - { 0x0069, nullptr, "I" }, // i - { 0x006a, nullptr, "J" }, // j - { 0x006b, nullptr, "K" }, // k - { 0x006c, nullptr, "L" }, // l - { 0x006d, nullptr, "M" }, // m - { 0x006e, nullptr, "N" }, // n - { 0x006f, nullptr, "O" }, // o - { 0x0070, nullptr, "P" }, // p - { 0x0071, nullptr, "Q" }, // q - { 0x0072, nullptr, "R" }, // r - { 0x0073, nullptr, "S" }, // s - { 0x0074, nullptr, "T" }, // t - { 0x0075, nullptr, "U" }, // u - { 0x0076, nullptr, "V" }, // v - { 0x0077, nullptr, "W" }, // w - { 0x0078, nullptr, "X" }, // x - { 0x0079, nullptr, "Y" }, // y - { 0x007a, nullptr, "Z" }, // z - { 0x00a0, nullptr, " " }, // non breaking space - { 0x00a1, nullptr, "!" }, // inverted exclaimation mark - { 0x00a6, nullptr, "|" }, // broken bar - { 0x00a9, nullptr, "(c)" }, // copyright sign - { 0x00ab, nullptr, "<<" }, // left pointing double angle - { 0x00ae, nullptr, "(r)" }, // registered sign - { 0x00bb, nullptr, ">>" }, // right pointing double angle - { 0x00bc, nullptr, "1/4" }, // vulgar fraction one quarter - { 0x00bd, nullptr, "1/2" }, // vulgar fraction one half - { 0x00be, nullptr, "3/4" }, // vulgar fraction three quarters - { 0x00bf, nullptr, "?" }, // inverted question mark - { 0x00c0, nullptr, "A" }, // 'A' grave - { 0x00c1, nullptr, "A" }, // 'A' acute - { 0x00c2, nullptr, "A" }, // 'A' circumflex - { 0x00c3, nullptr, "A" }, // 'A' tilde - { 0x00c4, nullptr, "A" }, // 'A' diaeresis - { 0x00c5, nullptr, "A" }, // 'A' ring above - { 0x00c6, nullptr, "AE" }, // 'AE' ligature - { 0x00c7, nullptr, "C" }, // 'C' cedilla - { 0x00c8, nullptr, "E" }, // 'E' grave - { 0x00c9, nullptr, "E" }, // 'E' acute - { 0x00ca, nullptr, "E" }, // 'E' circumflex - { 0x00cb, nullptr, "E" }, // 'E' diaeresis - { 0x00cc, nullptr, "I" }, // 'I' grave - { 0x00cd, nullptr, "I" }, // 'I' acute - { 0x00ce, nullptr, "I" }, // 'I' circumflex - { 0x00cf, nullptr, "I" }, // 'I' diaeresis - { 0x00d0, nullptr, "D" }, // 'ETH' - { 0x00d1, nullptr, "N" }, // 'N' tilde - { 0x00d2, nullptr, "O" }, // 'O' grave - { 0x00d3, nullptr, "O" }, // 'O' acute - { 0x00d4, nullptr, "O" }, // 'O' circumflex - { 0x00d5, nullptr, "O" }, // 'O' tilde - { 0x00d6, nullptr, "O" }, // 'O' diaeresis - { 0x00d7, nullptr, "X" }, // multiplication sign - { 0x00d8, nullptr, "O" }, // 'O' stroke - { 0x00d9, nullptr, "U" }, // 'U' grave - { 0x00da, nullptr, "U" }, // 'U' acute - { 0x00db, nullptr, "U" }, // 'U' circumflex - { 0x00dc, nullptr, "U" }, // 'U' diaeresis - { 0x00dd, nullptr, "Y" }, // 'Y' acute - { 0x00df, nullptr, "SS" }, // sharp S - { 0x00e0, nullptr, "a" }, // 'a' grave - { 0x00e1, nullptr, "a" }, // 'a' acute - { 0x00e2, nullptr, "a" }, // 'a' circumflex - { 0x00e3, nullptr, "a" }, // 'a' tilde - { 0x00e4, nullptr, "a" }, // 'a' diaeresis - { 0x00e5, nullptr, "a" }, // 'a' ring above - { 0x00e6, nullptr, "ae" }, // 'ae' ligature - { 0x00e7, nullptr, "c" }, // 'c' cedilla - { 0x00e8, nullptr, "e" }, // 'e' grave - { 0x00e9, nullptr, "e" }, // 'e' acute - { 0x00ea, nullptr, "e" }, // 'e' circumflex - { 0x00eb, nullptr, "e" }, // 'e' diaeresis - { 0x00ec, nullptr, "i" }, // 'i' grave - { 0x00ed, nullptr, "i" }, // 'i' acute - { 0x00ee, nullptr, "i" }, // 'i' circumflex - { 0x00ef, nullptr, "i" }, // 'i' diaeresis - { 0x00f0, nullptr, "d" }, // 'eth' - { 0x00f1, nullptr, "n" }, // 'n' tilde - { 0x00f2, nullptr, "o" }, // 'o' grave - { 0x00f3, nullptr, "o" }, // 'o' acute - { 0x00f4, nullptr, "o" }, // 'o' circumflex - { 0x00f5, nullptr, "o" }, // 'o' tilde - { 0x00f6, nullptr, "o" }, // 'o' diaeresis - { 0x00f8, nullptr, "o" }, // 'o' stroke - { 0x00f9, nullptr, "u" }, // 'u' grave - { 0x00fa, nullptr, "u" }, // 'u' acute - { 0x00fb, nullptr, "u" }, // 'u' circumflex - { 0x00fc, nullptr, "u" }, // 'u' diaeresis - { 0x00fd, nullptr, "y" }, // 'y' acute - { 0x00ff, nullptr, "y" }, // 'y' diaeresis - { 0x2010, nullptr, "-" }, // hyphen - { 0x2011, nullptr, "-" }, // non-breaking hyphen - { 0x2012, nullptr, "-" }, // figure dash - { 0x2013, nullptr, "-" }, // en dash - { 0x2014, nullptr, "-" }, // em dash - { 0x2015, nullptr, "-" }, // horizontal dash - { 0x2018, nullptr, "\'" }, // left single quotation mark - { 0x2019, nullptr, "\'" }, // right single quotation mark - { 0x201a, nullptr, "\'" }, // single low quotation mark - { 0x201b, nullptr, "\'" }, // single high reversed quotation mark - { 0x201c, nullptr, "\"" }, // left double quotation mark - { 0x201d, nullptr, "\"" }, // right double quotation mark - { 0x201e, nullptr, "\"" }, // double low quotation mark - { 0x201f, nullptr, "\"" }, // double high reversed quotation mark - { 0x2024, nullptr, "." }, // one dot leader - { 0x2025, nullptr, ".." }, // two dot leader - { 0x2026, nullptr, "..." }, // horizontal ellipsis - { 0x2047, nullptr, "??" }, // double question mark - { 0x2048, nullptr, "?!" }, // question exclamation mark - { 0x2049, nullptr, "!?" }, // exclamation question mark - { 0xff01, nullptr, "!" }, // fullwidth exclamation point - { 0xff02, nullptr, "\"" }, // fullwidth quotation mark - { 0xff03, nullptr, "#" }, // fullwidth number sign - { 0xff04, nullptr, "$" }, // fullwidth dollar sign - { 0xff05, nullptr, "%" }, // fullwidth percent sign - { 0xff06, nullptr, "&" }, // fullwidth ampersand - { 0xff07, nullptr, "\'" }, // fullwidth apostrophe - { 0xff08, nullptr, "(" }, // fullwidth left parenthesis - { 0xff09, nullptr, ")" }, // fullwidth right parenthesis - { 0xff0a, nullptr, "*" }, // fullwidth asterisk - { 0xff0b, nullptr, "+" }, // fullwidth plus - { 0xff0c, nullptr, "," }, // fullwidth comma - { 0xff0d, nullptr, "-" }, // fullwidth minus - { 0xff0e, nullptr, "." }, // fullwidth period - { 0xff0f, nullptr, "/" }, // fullwidth slash - { 0xff10, nullptr, "0" }, // fullwidth zero - { 0xff11, nullptr, "1" }, // fullwidth one - { 0xff12, nullptr, "2" }, // fullwidth two - { 0xff13, nullptr, "3" }, // fullwidth three - { 0xff14, nullptr, "4" }, // fullwidth four - { 0xff15, nullptr, "5" }, // fullwidth five - { 0xff16, nullptr, "6" }, // fullwidth six - { 0xff17, nullptr, "7" }, // fullwidth seven - { 0xff18, nullptr, "8" }, // fullwidth eight - { 0xff19, nullptr, "9" }, // fullwidth nine - { 0xff1a, nullptr, ":" }, // fullwidth colon - { 0xff1b, nullptr, ";" }, // fullwidth semicolon - { 0xff1c, nullptr, "<" }, // fullwidth less than sign - { 0xff1d, nullptr, "=" }, // fullwidth equals sign - { 0xff1e, nullptr, ">" }, // fullwidth greater than sign - { 0xff1f, nullptr, "?" }, // fullwidth question mark - { 0xff20, nullptr, "@" }, // fullwidth at sign - { 0xff21, nullptr, "A" }, // fullwidth 'A' - { 0xff22, nullptr, "B" }, // fullwidth 'B' - { 0xff23, nullptr, "C" }, // fullwidth 'C' - { 0xff24, nullptr, "D" }, // fullwidth 'D' - { 0xff25, nullptr, "E" }, // fullwidth 'E' - { 0xff26, nullptr, "F" }, // fullwidth 'F' - { 0xff27, nullptr, "G" }, // fullwidth 'G' - { 0xff28, nullptr, "H" }, // fullwidth 'H' - { 0xff29, nullptr, "I" }, // fullwidth 'I' - { 0xff2a, nullptr, "J" }, // fullwidth 'J' - { 0xff2b, nullptr, "K" }, // fullwidth 'K' - { 0xff2c, nullptr, "L" }, // fullwidth 'L' - { 0xff2d, nullptr, "M" }, // fullwidth 'M' - { 0xff2e, nullptr, "N" }, // fullwidth 'N' - { 0xff2f, nullptr, "O" }, // fullwidth 'O' - { 0xff30, nullptr, "P" }, // fullwidth 'P' - { 0xff31, nullptr, "Q" }, // fullwidth 'Q' - { 0xff32, nullptr, "R" }, // fullwidth 'R' - { 0xff33, nullptr, "S" }, // fullwidth 'S' - { 0xff34, nullptr, "T" }, // fullwidth 'T' - { 0xff35, nullptr, "U" }, // fullwidth 'U' - { 0xff36, nullptr, "V" }, // fullwidth 'V' - { 0xff37, nullptr, "W" }, // fullwidth 'W' - { 0xff38, nullptr, "X" }, // fullwidth 'X' - { 0xff39, nullptr, "Y" }, // fullwidth 'Y' - { 0xff3a, nullptr, "Z" }, // fullwidth 'Z' - { 0xff3b, nullptr, "[" }, // fullwidth left bracket - { 0xff3c, nullptr, "\\" }, // fullwidth backslash - { 0xff3d, nullptr, "]" }, // fullwidth right bracket - { 0xff3e, nullptr, "^" }, // fullwidth caret - { 0xff3f, nullptr, "_" }, // fullwidth underscore - { 0xff40, nullptr, "`" }, // fullwidth backquote - { 0xff41, nullptr, "a" }, // fullwidth 'a' - { 0xff42, nullptr, "b" }, // fullwidth 'b' - { 0xff43, nullptr, "c" }, // fullwidth 'c' - { 0xff44, nullptr, "d" }, // fullwidth 'd' - { 0xff45, nullptr, "e" }, // fullwidth 'e' - { 0xff46, nullptr, "f" }, // fullwidth 'f' - { 0xff47, nullptr, "g" }, // fullwidth 'g' - { 0xff48, nullptr, "h" }, // fullwidth 'h' - { 0xff49, nullptr, "i" }, // fullwidth 'i' - { 0xff4a, nullptr, "j" }, // fullwidth 'j' - { 0xff4b, nullptr, "k" }, // fullwidth 'k' - { 0xff4c, nullptr, "l" }, // fullwidth 'l' - { 0xff4d, nullptr, "m" }, // fullwidth 'm' - { 0xff4e, nullptr, "n" }, // fullwidth 'n' - { 0xff4f, nullptr, "o" }, // fullwidth 'o' - { 0xff50, nullptr, "p" }, // fullwidth 'p' - { 0xff51, nullptr, "q" }, // fullwidth 'q' - { 0xff52, nullptr, "r" }, // fullwidth 'r' - { 0xff53, nullptr, "s" }, // fullwidth 's' - { 0xff54, nullptr, "t" }, // fullwidth 't' - { 0xff55, nullptr, "u" }, // fullwidth 'u' - { 0xff56, nullptr, "v" }, // fullwidth 'v' - { 0xff57, nullptr, "w" }, // fullwidth 'w' - { 0xff58, nullptr, "x" }, // fullwidth 'x' - { 0xff59, nullptr, "y" }, // fullwidth 'y' - { 0xff5a, nullptr, "z" }, // fullwidth 'z' - { 0xff5b, nullptr, "{" }, // fullwidth left brace - { 0xff5c, nullptr, "|" }, // fullwidth vertical bar - { 0xff5d, nullptr, "}" }, // fullwidth right brace - { 0xff5e, nullptr, "~" }, // fullwidth tilde - { 0xff5f, nullptr, "((" }, // fullwidth double left parenthesis - { 0xff60, nullptr, "))" }, // fullwidth double right parenthesis - { 0xffe0, nullptr, "\xC2\xA2" }, // fullwidth cent sign - { 0xffe1, nullptr, "\xC2\xA3" }, // fullwidth pound sign - { 0xffe4, nullptr, "\xC2\xA4" }, // fullwidth broken bar - { 0xffe5, nullptr, "\xC2\xA5" }, // fullwidth yen sign - { 0xffe6, nullptr, "\xE2\x82\xA9" }, // fullwidth won sign - { 0xffe9, nullptr, "\xE2\x86\x90" }, // fullwidth left arrow - { 0xffea, nullptr, "\xE2\x86\x91" }, // fullwidth up arrow - { 0xffeb, nullptr, "\xE2\x86\x92" }, // fullwidth right arrow - { 0xffec, nullptr, "\xE2\x86\x93" }, // fullwidth down arrow - { 0xffed, nullptr, "\xE2\x96\xAA" }, // fullwidth solid box - { 0xffee, nullptr, "\xE2\x97\xA6" }, // fullwidth open circle - { UCHAR_SHIFT_1, "Shift", nullptr }, // Shift key - { UCHAR_SHIFT_2, "Ctrl", nullptr }, // Ctrl key - { UCHAR_MAMEKEY(F1), "F1", nullptr }, // F1 function key - { UCHAR_MAMEKEY(F2), "F2", nullptr }, // F2 function key - { UCHAR_MAMEKEY(F3), "F3", nullptr }, // F3 function key - { UCHAR_MAMEKEY(F4), "F4", nullptr }, // F4 function key - { UCHAR_MAMEKEY(F5), "F5", nullptr }, // F5 function key - { UCHAR_MAMEKEY(F6), "F6", nullptr }, // F6 function key - { UCHAR_MAMEKEY(F7), "F7", nullptr }, // F7 function key - { UCHAR_MAMEKEY(F8), "F8", nullptr }, // F8 function key - { UCHAR_MAMEKEY(F9), "F9", nullptr }, // F9 function key - { UCHAR_MAMEKEY(F10), "F10", nullptr }, // F10 function key - { UCHAR_MAMEKEY(F11), "F11", nullptr }, // F11 function key - { UCHAR_MAMEKEY(F12), "F12", nullptr }, // F12 function key - { UCHAR_MAMEKEY(F13), "F13", nullptr }, // F13 function key - { UCHAR_MAMEKEY(F14), "F14", nullptr }, // F14 function key - { UCHAR_MAMEKEY(F15), "F15", nullptr }, // F15 function key - { UCHAR_MAMEKEY(F16), "F16", nullptr }, // F16 function key - { UCHAR_MAMEKEY(F17), "F17", nullptr }, // F17 function key - { UCHAR_MAMEKEY(F18), "F18", nullptr }, // F18 function key - { UCHAR_MAMEKEY(F19), "F19", nullptr }, // F19 function key - { UCHAR_MAMEKEY(F20), "F20", nullptr }, // F20 function key - { UCHAR_MAMEKEY(ESC), "Esc", "\033" }, // Esc key - { UCHAR_MAMEKEY(INSERT), "Insert", nullptr }, // Insert key - { UCHAR_MAMEKEY(DEL), "Delete", "\010" }, // Delete key - { UCHAR_MAMEKEY(HOME), "Home", "\014" }, // Home key - { UCHAR_MAMEKEY(END), "End", nullptr }, // End key - { UCHAR_MAMEKEY(PGUP), "Page Up", nullptr }, // Page Up key - { UCHAR_MAMEKEY(PGDN), "Page Down", nullptr }, // Page Down key - { UCHAR_MAMEKEY(LEFT), "Cursor Left", nullptr }, // Cursor Left - { UCHAR_MAMEKEY(RIGHT), "Cursor Right", nullptr }, // Cursor Right - { UCHAR_MAMEKEY(UP), "Cursor Up", nullptr }, // Cursor Up - { UCHAR_MAMEKEY(DOWN), "Cursor Down", nullptr }, // Cursor Down - { UCHAR_MAMEKEY(0_PAD), "Keypad 0", nullptr }, // 0 on the numeric keypad - { UCHAR_MAMEKEY(1_PAD), "Keypad 1", nullptr }, // 1 on the numeric keypad - { UCHAR_MAMEKEY(2_PAD), "Keypad 2", nullptr }, // 2 on the numeric keypad - { UCHAR_MAMEKEY(3_PAD), "Keypad 3", nullptr }, // 3 on the numeric keypad - { UCHAR_MAMEKEY(4_PAD), "Keypad 4", nullptr }, // 4 on the numeric keypad - { UCHAR_MAMEKEY(5_PAD), "Keypad 5", nullptr }, // 5 on the numeric keypad - { UCHAR_MAMEKEY(6_PAD), "Keypad 6", nullptr }, // 6 on the numeric keypad - { UCHAR_MAMEKEY(7_PAD), "Keypad 7", nullptr }, // 7 on the numeric keypad - { UCHAR_MAMEKEY(8_PAD), "Keypad 8", nullptr }, // 8 on the numeric keypad - { UCHAR_MAMEKEY(9_PAD), "Keypad 9", nullptr }, // 9 on the numeric keypad - { UCHAR_MAMEKEY(SLASH_PAD), "Keypad /", nullptr }, // / on the numeric keypad - { UCHAR_MAMEKEY(ASTERISK), "Keypad *", nullptr }, // * on the numeric keypad - { UCHAR_MAMEKEY(MINUS_PAD), "Keypad -", nullptr }, // - on the numeric Keypad - { UCHAR_MAMEKEY(PLUS_PAD), "Keypad +", nullptr }, // + on the numeric Keypad - { UCHAR_MAMEKEY(DEL_PAD), "Keypad .", nullptr }, // . on the numeric keypad - { UCHAR_MAMEKEY(ENTER_PAD), "Keypad Enter", nullptr }, // Enter on the numeric keypad - { UCHAR_MAMEKEY(PRTSCR), "Print Screen", nullptr }, // Print Screen key - { UCHAR_MAMEKEY(PAUSE), "Pause", nullptr }, // Pause key - { UCHAR_MAMEKEY(LSHIFT), "Left Shift", nullptr }, // Left Shift key - { UCHAR_MAMEKEY(RSHIFT), "Right Shift", nullptr }, // Right Shift key - { UCHAR_MAMEKEY(LCONTROL), "Left Ctrl", nullptr }, // Left Control key - { UCHAR_MAMEKEY(RCONTROL), "Right Ctrl", nullptr }, // Right Control key - { UCHAR_MAMEKEY(LALT), "Left Alt", nullptr }, // Left Alt key - { UCHAR_MAMEKEY(RALT), "Right Alt", nullptr }, // Right Alt key - { UCHAR_MAMEKEY(SCRLOCK), "Scroll Lock", nullptr }, // Scroll Lock key - { UCHAR_MAMEKEY(NUMLOCK), "Num Lock", nullptr }, // Num Lock key - { UCHAR_MAMEKEY(CAPSLOCK), "Caps Lock", nullptr }, // Caps Lock key - { UCHAR_MAMEKEY(LWIN), "Left Win", nullptr }, // Left Win key - { UCHAR_MAMEKEY(RWIN), "Right Win", nullptr }, // Right Win key - { UCHAR_MAMEKEY(MENU), "Menu", nullptr }, // Menu key - { UCHAR_MAMEKEY(CANCEL), "Break", nullptr } // Break/Pause key -}; - - - //************************************************************************** // COMMON SHARED STRINGS //************************************************************************** @@ -624,6 +291,10 @@ const struct } // TODO: anonymous namespace +// XML attributes for the different types +const char *const ioport_manager::seqtypestrings[] = { "standard", "increment", "decrement" }; + + std::uint8_t const inp_header::MAGIC[inp_header::OFFS_BASETIME - inp_header::OFFS_MAGIC] = { 'M', 'A', 'M', 'E', 'I', 'N', 'P', 0 }; @@ -833,544 +504,6 @@ void digital_joystick::frame_update() -//************************************************************************** -// NATURAL KEYBOARD -//************************************************************************** - -//------------------------------------------------- -// natural_keyboard - constructor -//------------------------------------------------- - -natural_keyboard::natural_keyboard(running_machine &machine) - : m_machine(machine), - m_bufbegin(0), - m_bufend(0), - m_status_keydown(false), - m_last_cr(false), - m_timer(nullptr), - m_current_rate(attotime::zero) -{ - m_queue_chars = ioport_queue_chars_delegate(); - m_accept_char = ioport_accept_char_delegate(); - m_charqueue_empty = ioport_charqueue_empty_delegate(); -} - -//------------------------------------------------- -// initialize - initialize natural keyboard -// support -//------------------------------------------------- - -void natural_keyboard::initialize() -{ - // posting keys directly only makes sense for a computer - if (machine().ioport().has_keyboard()) - { - m_buffer.resize(KEY_BUFFER_SIZE); - m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(natural_keyboard::timer), this)); - build_codes(machine().ioport()); - } -} -//------------------------------------------------- -// configure - configure callbacks for full- -// featured keyboard support -//------------------------------------------------- - -void natural_keyboard::configure(ioport_queue_chars_delegate queue_chars, ioport_accept_char_delegate accept_char, ioport_charqueue_empty_delegate charqueue_empty) -{ - // set the callbacks - m_queue_chars = queue_chars; - m_accept_char = accept_char; - m_charqueue_empty = charqueue_empty; -} - - -//------------------------------------------------- -// post - post a single character -//------------------------------------------------- - -void natural_keyboard::post(unicode_char ch) -{ - // ignore any \n that are preceded by \r - if (m_last_cr && ch == '\n') - { - m_last_cr = false; - return; - } - - // change all eolns to '\r' - if (ch == '\n') - ch = '\r'; - else - m_last_cr = (ch == '\r'); - - // logging - if (LOG_NATURAL_KEYBOARD) - { - const keycode_map_entry *code = find_code(ch); - machine().logerror("natural_keyboard::post(): code=%i (%s) field.name='%s'\n", int(ch), unicode_to_string(ch).c_str(), (code != nullptr && code->field[0] != nullptr) ? code->field[0]->name() : ""); - } - - // can we post this key in the queue directly? - if (can_post_directly(ch)) - internal_post(ch); - - // can we post this key with an alternate representation? - else if (can_post_alternate(ch)) - { - const char_info *info = char_info::find(ch); - assert(info != nullptr && info->alternate != nullptr); - const char *altstring = info->alternate; - while (*altstring != 0) - { - altstring += uchar_from_utf8(&ch, altstring, strlen(altstring)); - internal_post(ch); - } - } -} - - -//------------------------------------------------- -// post - post a unicode encoded string -//------------------------------------------------- - -void natural_keyboard::post(const unicode_char *text, size_t length, const attotime &rate) -{ - // set the fixed rate - m_current_rate = rate; - - // 0 length means strlen - if (length == 0) - for (const unicode_char *scan = text; *scan != 0; scan++) - length++; - - // iterate over characters or until the buffer is full up - while (length > 0 && !full()) - { - // fetch next character - post(*text++); - length--; - } -} - - -//------------------------------------------------- -// post_utf8 - post a UTF-8 encoded string -//------------------------------------------------- - -void natural_keyboard::post_utf8(const char *text, size_t length, const attotime &rate) -{ - // set the fixed rate - m_current_rate = rate; - - // 0-length means strlen - if (length == 0) - length = strlen(text); - - // iterate until out of characters - while (length > 0) - { - // decode the next character - unicode_char uc; - int count = uchar_from_utf8(&uc, text, length); - if (count < 0) - { - count = 1; - uc = INVALID_CHAR; - } - - // append to the buffer - post(uc); - text += count; - length -= count; - } -} - - -//------------------------------------------------- -// post_coded - post a coded string -//------------------------------------------------- - -void natural_keyboard::post_coded(const char *text, size_t length, const attotime &rate) -{ - static const struct - { - const char *key; - unicode_char code; - } codes[] = - { - { "BACKSPACE", 8 }, - { "BS", 8 }, - { "BKSP", 8 }, - { "DEL", UCHAR_MAMEKEY(DEL) }, - { "DELETE", UCHAR_MAMEKEY(DEL) }, - { "END", UCHAR_MAMEKEY(END) }, - { "ENTER", 13 }, - { "ESC", '\033' }, - { "HOME", UCHAR_MAMEKEY(HOME) }, - { "INS", UCHAR_MAMEKEY(INSERT) }, - { "INSERT", UCHAR_MAMEKEY(INSERT) }, - { "PGDN", UCHAR_MAMEKEY(PGDN) }, - { "PGUP", UCHAR_MAMEKEY(PGUP) }, - { "SPACE", 32 }, - { "TAB", 9 }, - { "F1", UCHAR_MAMEKEY(F1) }, - { "F2", UCHAR_MAMEKEY(F2) }, - { "F3", UCHAR_MAMEKEY(F3) }, - { "F4", UCHAR_MAMEKEY(F4) }, - { "F5", UCHAR_MAMEKEY(F5) }, - { "F6", UCHAR_MAMEKEY(F6) }, - { "F7", UCHAR_MAMEKEY(F7) }, - { "F8", UCHAR_MAMEKEY(F8) }, - { "F9", UCHAR_MAMEKEY(F9) }, - { "F10", UCHAR_MAMEKEY(F10) }, - { "F11", UCHAR_MAMEKEY(F11) }, - { "F12", UCHAR_MAMEKEY(F12) }, - { "QUOTE", '\"' } - }; - - // set the fixed rate - m_current_rate = rate; - - // 0-length means strlen - if (length == 0) - length = strlen(text); - - // iterate through the source string - size_t curpos = 0; - while (curpos < length) - { - // extract next character - unicode_char ch = text[curpos]; - size_t increment = 1; - - // look for escape characters - if (ch == '{') - for (auto & code : codes) - { - size_t keylen = strlen(code.key); - if (curpos + keylen + 2 <= length) - if (core_strnicmp(code.key, &text[curpos + 1], keylen) == 0 && text[curpos + keylen + 1] == '}') - { - ch = code.code; - increment = keylen + 2; - } - } - - // if we got a code, post it - if (ch != 0) - post(ch); - curpos += increment; - } -} - - -//------------------------------------------------- -// build_codes - given an input port table, create -// an input code table useful for mapping unicode -// chars -//------------------------------------------------- - -void natural_keyboard::build_codes(ioport_manager &manager) -{ - // iterate over shift keys - ioport_field *shift[UCHAR_SHIFT_END + 1 - UCHAR_SHIFT_BEGIN] = { nullptr }; - for (int curshift = 0; curshift <= ARRAY_LENGTH(shift); curshift++) - if (curshift == 0 || shift[curshift - 1] != nullptr) - - // iterate over ports and fields - for (auto &port : manager.ports()) - for (ioport_field &field : port.second->fields()) - if (field.type() == IPT_KEYBOARD) - { - // fetch the code, ignoring 0 - unicode_char code = field.keyboard_code(curshift); - if (code == 0) - continue; - - // is this a shifter key? - if (code >= UCHAR_SHIFT_BEGIN && code <= UCHAR_SHIFT_END) - shift[code - UCHAR_SHIFT_BEGIN] = &field; - - // not a shifter key; record normally - else - { - keycode_map_entry newcode; - if (curshift == 0) - { - newcode.field[0] = &field; - newcode.field[1] = nullptr; - } - else - { - newcode.field[0] = shift[curshift - 1]; - newcode.field[1] = &field; - } - newcode.ch = code; - m_keycode_map.push_back(newcode); - - if (LOG_NATURAL_KEYBOARD) - { - machine().logerror("natural_keyboard: code=%i (%s) port=%p field.name='%s'\n", int(code), unicode_to_string(code).c_str(), (void *)&port, field.name()); - } - } - } -} - - -//------------------------------------------------- -// can_post_directly - determine if the given -// unicode character can be directly posted -//------------------------------------------------- - -bool natural_keyboard::can_post_directly(unicode_char ch) -{ - // if we have a queueing callback, then it depends on whether we can accept the character - if (!m_queue_chars.isnull()) - return m_accept_char.isnull() ? true : m_accept_char(ch); - - // otherwise, it depends on the input codes - const keycode_map_entry *code = find_code(ch); - return (code != nullptr && code->field[0] != nullptr); -} - - -//------------------------------------------------- -// can_post_alternate - determine if the given -// unicode character can be posted via translation -//------------------------------------------------- - -bool natural_keyboard::can_post_alternate(unicode_char ch) -{ - const char_info *info = char_info::find(ch); - if (info == nullptr) - return false; - - const char *altstring = info->alternate; - if (altstring == nullptr) - return false; - - while (*altstring != 0) - { - unicode_char uchar; - int count = uchar_from_utf8(&uchar, altstring, strlen(altstring)); - if (count <= 0) - return false; - if (!can_post_directly(uchar)) - return false; - altstring += count; - } - return true; -} - - -//------------------------------------------------- -// choose_delay - determine the delay between -// posting keyboard events -//------------------------------------------------- - -attotime natural_keyboard::choose_delay(unicode_char ch) -{ - // if we have a live rate, just use that - if (m_current_rate != attotime::zero) - return m_current_rate; - - // systems with queue_chars can afford a much smaller delay - if (!m_queue_chars.isnull()) - return attotime::from_msec(10); - - // otherwise, default to constant delay with a longer delay on CR - return attotime::from_msec((ch == '\r') ? 200 : 50); -} - - -//------------------------------------------------- -// internal_post - post a keyboard event -//------------------------------------------------- - -void natural_keyboard::internal_post(unicode_char ch) -{ - // need to start up the timer? - if (empty()) - { - m_timer->adjust(choose_delay(ch)); - m_status_keydown = 0; - } - - // add to the buffer, resizing if necessary - m_buffer[m_bufend++] = ch; - if ((m_bufend + 1) % m_buffer.size() == m_bufbegin) - m_buffer.resize(m_buffer.size() + KEY_BUFFER_SIZE); - m_bufend %= m_buffer.size(); -} - - -//------------------------------------------------- -// timer - timer callback to keep things flowing -// when posting a string of characters -//------------------------------------------------- - -void natural_keyboard::timer(void *ptr, int param) -{ - // the driver has a queue_chars handler - if (!m_queue_chars.isnull()) - { - while (!empty() && m_queue_chars(&m_buffer[m_bufbegin], 1)) - { - m_bufbegin = (m_bufbegin + 1) % m_buffer.size(); - if (m_current_rate != attotime::zero) - break; - } - } - - // the driver does not have a queue_chars handler - else - { - if (m_status_keydown) - m_bufbegin = (m_bufbegin + 1) % m_buffer.size(); - m_status_keydown = !m_status_keydown; - } - - // need to make sure timerproc is called again if buffer not empty - if (!empty()) - m_timer->adjust(choose_delay(m_buffer[m_bufbegin])); -} - - -//------------------------------------------------- -// unicode_to_string - obtain a string -// representation of a given code; used for -// logging and debugging -//------------------------------------------------- - -std::string natural_keyboard::unicode_to_string(unicode_char ch) -{ - std::string buffer; - switch (ch) - { - // check some magic values - case '\0': buffer.assign("\\0"); break; - case '\r': buffer.assign("\\r"); break; - case '\n': buffer.assign("\\n"); break; - case '\t': buffer.assign("\\t"); break; - - default: - // seven bit ASCII is easy - if (ch >= 32 && ch < 128) - { - char temp[2] = { char(ch), 0 }; - buffer.assign(temp); - } - else if (ch >= UCHAR_MAMEKEY_BEGIN) - { - // try to obtain a codename with code_name(); this can result in an empty string - input_code code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, input_item_id(ch - UCHAR_MAMEKEY_BEGIN)); - buffer = machine().input().code_name(code); - } - - // did we fail to resolve? if so, we have a last resort - if (buffer.empty()) - buffer = string_format("U+%04X", unsigned(ch)); - break; - } - return buffer; -} - - -//------------------------------------------------- -// find_code - find a code in our lookup table -//------------------------------------------------- - -const natural_keyboard::keycode_map_entry *natural_keyboard::find_code(unicode_char ch) const -{ - for (auto & elem : m_keycode_map) - { - if (elem.ch == ch) - return &elem; - } - return nullptr; -} - - -//------------------------------------------------- -// frame_update - once per frame update of the -// natural keyboard state -//------------------------------------------------- - -void natural_keyboard::frame_update(ioport_port &port, ioport_value &digital) -{ - // is there currently a key down? - if (m_status_keydown && !empty()) - { - // loop through this character's component codes - const keycode_map_entry *code = find_code(m_buffer[m_bufbegin]); - if (code != nullptr) - for (int fieldnum = 0; fieldnum < ARRAY_LENGTH(code->field) && code->field[fieldnum] != nullptr; fieldnum++) - if (&code->field[fieldnum]->port() == &port) - digital |= code->field[fieldnum]->mask(); - } -} - - -//------------------------------------------------- -// key_name - returns the name of a specific key -//------------------------------------------------- - -std::string natural_keyboard::key_name(unicode_char ch) const -{ - std::string str; - - // attempt to get the string from the character info table - const char_info *ci = char_info::find(ch); - const char *result = (ci != nullptr) ? ci->name : nullptr; - if (result != nullptr) - str.assign(result); - - // if that doesn't work, convert to UTF-8 - else if (ch > 0x7F || isprint(ch)) - { - char buf[10]; - int count = utf8_from_uchar(buf, ARRAY_LENGTH(buf), ch); - buf[count] = 0; - str.assign(buf); - } - - // otherwise, opt for question marks - else - str.assign("???"); - return str; -} - - -//------------------------------------------------- -// dump - dumps info to string -//------------------------------------------------- - -std::string natural_keyboard::dump() -{ - std::ostringstream buffer; - const size_t left_column_width = 24; - - // loop through all codes - for (auto & code : m_keycode_map) - { - // describe the character code - std::string description = string_format("%08X (%s) ", code.ch, unicode_to_string(code.ch).c_str()); - - // pad with spaces - util::stream_format(buffer, "%-*s", left_column_width, description); - - // identify the keys used - for (int field = 0; field < ARRAY_LENGTH(code.field) && code.field[field] != nullptr; field++) - util::stream_format(buffer, "%s'%s'", (field > 0) ? ", " : "", code.field[field]->name()); - - // carriage return - buffer << '\n'; - } - - return buffer.str(); -} - - //************************************************************************** // I/O PORT CONDITION //************************************************************************** @@ -1626,6 +759,82 @@ unicode_char ioport_field::keyboard_code(int which) const } +//------------------------------------------------- +// key_name - returns the name of a specific key +//------------------------------------------------- + +std::string ioport_field::key_name(int which) const +{ + unicode_char ch = keyboard_code(which); + + // attempt to get the string from the character info table + switch (ch) + { + case 8: return "Backspace"; + case 9: return "Tab"; + case 12: return "Clear"; + case 13: return "Enter"; + case 27: return "Esc"; + case 32: return "Space"; + case UCHAR_SHIFT_1: return "Shift"; + case UCHAR_SHIFT_2: return "Ctrl"; + case UCHAR_MAMEKEY(ESC): return "Esc"; + case UCHAR_MAMEKEY(INSERT): return "Insert"; + case UCHAR_MAMEKEY(DEL): return "Delete"; + case UCHAR_MAMEKEY(HOME): return "Home"; + case UCHAR_MAMEKEY(END): return "End"; + case UCHAR_MAMEKEY(PGUP): return "Page Up"; + case UCHAR_MAMEKEY(PGDN): return "Page Down"; + case UCHAR_MAMEKEY(LEFT): return "Cursor Left"; + case UCHAR_MAMEKEY(RIGHT): return "Cursor Right"; + case UCHAR_MAMEKEY(UP): return "Cursor Up"; + case UCHAR_MAMEKEY(DOWN): return "Cursor Down"; + case UCHAR_MAMEKEY(SLASH_PAD): return "Keypad /"; + case UCHAR_MAMEKEY(ASTERISK): return "Keypad *"; + case UCHAR_MAMEKEY(MINUS_PAD): return "Keypad -"; + case UCHAR_MAMEKEY(PLUS_PAD): return "Keypad +"; + case UCHAR_MAMEKEY(DEL_PAD): return "Keypad ."; + case UCHAR_MAMEKEY(ENTER_PAD): return "Keypad Enter"; + case UCHAR_MAMEKEY(PRTSCR): return "Print Screen"; + case UCHAR_MAMEKEY(PAUSE): return "Pause"; + case UCHAR_MAMEKEY(LSHIFT): return "Left Shift"; + case UCHAR_MAMEKEY(RSHIFT): return "Right Shift"; + case UCHAR_MAMEKEY(LCONTROL): return "Left Ctrl"; + case UCHAR_MAMEKEY(RCONTROL): return "Right Ctrl"; + case UCHAR_MAMEKEY(LALT): return "Left Alt"; + case UCHAR_MAMEKEY(RALT): return "Right Alt"; + case UCHAR_MAMEKEY(SCRLOCK): return "Scroll Lock"; + case UCHAR_MAMEKEY(NUMLOCK): return "Num Lock"; + case UCHAR_MAMEKEY(CAPSLOCK): return "Caps Lock"; + case UCHAR_MAMEKEY(LWIN): return "Left Win"; + case UCHAR_MAMEKEY(RWIN): return "Right Win"; + case UCHAR_MAMEKEY(MENU): return "Menu"; + case UCHAR_MAMEKEY(CANCEL): return "Break"; + default: break; + } + + // handle function keys + if (ch >= UCHAR_MAMEKEY(F1) && ch <= UCHAR_MAMEKEY(F20)) + return util::string_format("F%d", ch - UCHAR_MAMEKEY(F1) + 1); + + // handle 0-9 on numeric keypad + if (ch >= UCHAR_MAMEKEY(0_PAD) && ch <= UCHAR_MAMEKEY(9_PAD)) + return util::string_format("Keypad %d", ch - UCHAR_MAMEKEY(0_PAD)); + + // if that doesn't work, convert to UTF-8 + if (ch > 0x7F || isprint(ch)) + { + char buf[10]; + int count = utf8_from_uchar(buf, ARRAY_LENGTH(buf), ch); + buf[count] = 0; + return std::string(buf); + } + + // otherwise, opt for question marks + return "???"; +} + + //------------------------------------------------- // get_user_settings - return the current // settings for the given input field @@ -1862,6 +1071,15 @@ void ioport_field::frame_update(ioport_value &result) if (machine().ui().is_menu_active()) return; + // if we're a keyboard type and using natural keyboard, bail + if (m_type == IPT_KEYBOARD && machine().ui().use_natural_keyboard()) + { + // use just the digital value + if (m_digital_value) + result |= m_mask; + return; + } + // if the state changed, look for switch down/switch up bool curstate = m_digital_value || machine().input().seq_pressed(seq()); if (m_live->autofire && !machine().ioport().get_autofire_toggle()) @@ -1884,10 +1102,6 @@ void ioport_field::frame_update(ioport_value &result) changed = true; } - // if we're a keyboard type and using natural keyboard, bail - if (m_type == IPT_KEYBOARD && machine().ui().use_natural_keyboard()) - return; - // coin impulse option int effective_impulse = m_impulse; int impulse_option_val = machine().options().coin_impulse(); @@ -2153,7 +1367,7 @@ ioport_field_live::ioport_field_live(ioport_field &field, analog_field *analog) unicode_char ch = field.keyboard_code(which); if (ch == 0) break; - name.append(string_format("%-*s ", std::max(SPACE_COUNT - 1, 0), field.manager().natkeyboard().key_name(ch))); + name.append(string_format("%-*s ", std::max(SPACE_COUNT - 1, 0), field.key_name(which))); } // trim extra spaces @@ -2283,9 +1497,6 @@ void ioport_port::frame_update() // now loop back and modify based on the inputs for (ioport_field &field : fields()) field.frame_update(m_live->digital); - - // hook for MESS's natural keyboard support - manager().natkeyboard().frame_update(*this, m_live->digital); } @@ -2440,7 +1651,6 @@ ioport_port_live::ioport_port_live(ioport_port &port) ioport_manager::ioport_manager(running_machine &machine) : m_machine(machine), m_safe_to_read(false), - m_natkeyboard(machine), m_last_frame_time(attotime::zero), m_last_delta_nsec(0), m_record_file(machine.options().input_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS), @@ -2526,7 +1736,9 @@ time_t ioport_manager::initialize() break; } - m_natkeyboard.initialize(); + // initialize natural keyboard + m_natkeyboard = std::make_unique(machine()); + // register callbacks for when we load configurations machine().configuration().config_register("input", config_saveload_delegate(FUNC(ioport_manager::load_config), this), config_saveload_delegate(FUNC(ioport_manager::save_config), this)); @@ -2631,6 +1843,15 @@ void ioport_manager::exit() } +//------------------------------------------------- +// ~ioport_manager - destructor +//------------------------------------------------- + +ioport_manager::~ioport_manager() +{ +} + + //------------------------------------------------- // type_name - return the name for the given // type/player @@ -3937,31 +3158,6 @@ void ioport_configurer::onoff_alloc(const char *name, ioport_value defval, iopor MISCELLANEOUS ***************************************************************************/ -//------------------------------------------------- -// find - look up information about a particular -// character -//------------------------------------------------- - -const char_info *char_info::find(unicode_char target) -{ - // perform a simple binary search to find the proper alternate - int low = 0; - int high = ARRAY_LENGTH(charinfo); - while (high > low) - { - int middle = (high + low) / 2; - unicode_char ch = charinfo[middle].ch; - if (ch < target) - low = middle + 1; - else if (ch > target) - high = middle; - else - return &charinfo[middle]; - } - return nullptr; -} - - //------------------------------------------------- // dynamic_field - constructor //------------------------------------------------- @@ -4538,43 +3734,3 @@ input_seq_type ioport_manager::token_to_seq_type(const char *string) return input_seq_type(seqindex); return SEQ_TYPE_INVALID; } - - - -//------------------------------------------------- -// validate_natural_keyboard_statics - -// validates natural keyboard static data -//------------------------------------------------- - -/* -int validate_natural_keyboard_statics(void) -{ - int i; - int error = FALSE; - unicode_char last_char = 0; - const char_info *ci; - - // check to make sure that charinfo is in order - for (i = 0; i < ARRAY_LENGTH(charinfo); i++) - { - if (last_char >= charinfo[i].ch) - { - osd_printf_error("inputx: charinfo is out of order; 0x%08x should be higher than 0x%08x\n", charinfo[i].ch, last_char); - error = TRUE; - } - last_char = charinfo[i].ch; - } - - // check to make sure that I can look up everything on alternate_charmap - for (i = 0; i < ARRAY_LENGTH(charinfo); i++) - { - ci = char_info::find(charinfo[i].ch); - if (ci != &charinfo[i]) - { - osd_printf_error("ioport: expected char_info::find(0x%08x) to work properly\n", charinfo[i].ch); - error = TRUE; - } - } - return error; -} -*/ diff --git a/src/emu/ioport.h b/src/emu/ioport.h index 419b603b239..faf60068f3e 100644 --- a/src/emu/ioport.h +++ b/src/emu/ioport.h @@ -653,10 +653,6 @@ enum // TYPE DEFINITIONS //************************************************************************** -// opaque types pointing to live state -struct input_port_state; -struct input_field_state; - // forward declarations class ioport_list; class ioport_port; @@ -664,7 +660,7 @@ struct ioport_port_live; class ioport_field; struct ioport_field_live; class ioport_manager; -class emu_timer; +class natural_keyboard; struct xml_data_node; class analog_field; @@ -676,11 +672,6 @@ typedef device_delegate ioport_field_read typedef device_delegate ioport_field_write_delegate; typedef device_delegate ioport_field_crossmap_delegate; -// keyboard helper function delegates -typedef delegate ioport_queue_chars_delegate; -typedef delegate ioport_accept_char_delegate; -typedef delegate ioport_charqueue_empty_delegate; - // ======================> inp_header @@ -897,75 +888,6 @@ private: DECLARE_ENUM_OPERATORS(digital_joystick::direction_t) -// ======================> natural_keyboard - -// buffer to handle copy/paste/insert of keys -class natural_keyboard -{ - DISABLE_COPYING(natural_keyboard); - -public: - // construction/destruction - natural_keyboard(running_machine &machine); - - void initialize(); - - // getters and queries - running_machine &machine() const { return m_machine; } - bool empty() const { return (m_bufbegin == m_bufend); } - bool full() const { return ((m_bufend + 1) % m_buffer.size()) == m_bufbegin; } - bool can_post() const { return (!m_queue_chars.isnull() || !m_keycode_map.empty()); } - bool is_posting() const { return (!empty() || (!m_charqueue_empty.isnull() && !m_charqueue_empty())); } - - // configuration - void configure(ioport_queue_chars_delegate queue_chars, ioport_accept_char_delegate accept_char, ioport_charqueue_empty_delegate charqueue_empty); - - // posting - void post(unicode_char ch); - void post(const unicode_char *text, size_t length = 0, const attotime &rate = attotime::zero); - void post_utf8(const char *text, size_t length = 0, const attotime &rate = attotime::zero); - void post_coded(const char *text, size_t length = 0, const attotime &rate = attotime::zero); - - void frame_update(ioport_port &port, ioport_value &digital); - std::string key_name(unicode_char ch) const; - - // debugging - std::string dump(); - -private: - // internal keyboard code information - struct keycode_map_entry - { - unicode_char ch; - ioport_field * field[UCHAR_SHIFT_END + 1 - UCHAR_SHIFT_BEGIN]; - }; - - // internal helpers - void build_codes(ioport_manager &manager); - bool can_post_directly(unicode_char ch); - bool can_post_alternate(unicode_char ch); - attotime choose_delay(unicode_char ch); - void internal_post(unicode_char ch); - void timer(void *ptr, int param); - std::string unicode_to_string(unicode_char ch); - const keycode_map_entry *find_code(unicode_char ch) const; - - // internal state - running_machine & m_machine; // reference to our machine - UINT32 m_bufbegin; // index of starting character - UINT32 m_bufend; // index of ending character - std::vector m_buffer; // actual buffer - bool m_status_keydown; // current keydown status - bool m_last_cr; // was the last char a CR? - emu_timer * m_timer; // timer for posting characters - attotime m_current_rate; // current rate for posting - ioport_queue_chars_delegate m_queue_chars; // queue characters callback - ioport_accept_char_delegate m_accept_char; // accept character callback - ioport_charqueue_empty_delegate m_charqueue_empty; // character queue empty callback - std::vector m_keycode_map; // keycode map -}; - - // ======================> ioport_condition // encapsulates a condition on a port field or setting @@ -1152,6 +1074,7 @@ public: UINT8 way() const { return m_way; } unicode_char keyboard_code(int which) const; + std::string key_name(int which) const; ioport_field_live &live() const { assert(m_live != nullptr); return *m_live; } // setters @@ -1467,12 +1390,13 @@ public: // construction/destruction ioport_manager(running_machine &machine); time_t initialize(); + ~ioport_manager(); // getters running_machine &machine() const { return m_machine; } const ioport_list &ports() const { return m_portlist; } bool safe_to_read() const { return m_safe_to_read; } - natural_keyboard &natkeyboard() { return m_natkeyboard; } + natural_keyboard &natkeyboard() { assert(m_natkeyboard != nullptr); return *m_natkeyboard; } // type helpers const simple_list &types() const { return m_typelist; } @@ -1510,6 +1434,7 @@ private: ioport_port *port(const char *tag) const { if (tag) { auto search = m_portlist.find(tag); if (search != m_portlist.end()) return search->second.get(); else return nullptr; } else return nullptr; } void exit(); input_seq_type token_to_seq_type(const char *string); + static const char *const seqtypestrings[]; void load_config(config_type cfg_type, xml_data_node *parentnode); void load_remap_table(xml_data_node *parentnode); @@ -1549,7 +1474,7 @@ private: // specific special global input states simple_list m_joystick_list; // list of digital joysticks - natural_keyboard m_natkeyboard; // natural keyboard support + std::unique_ptr m_natkeyboard; // natural keyboard support // frame time tracking attotime m_last_frame_time; // time of the last frame callback diff --git a/src/emu/natkeyboard.cpp b/src/emu/natkeyboard.cpp new file mode 100644 index 00000000000..1301ecd2926 --- /dev/null +++ b/src/emu/natkeyboard.cpp @@ -0,0 +1,842 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + natkeyboard.cpp + + Natural keyboard input support. + +***************************************************************************/ + +#include "emu.h" +#include "natkeyboard.h" + + +//************************************************************************** +// DEBUGGING +//************************************************************************** + +#define LOG_NATURAL_KEYBOARD 0 + + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +const int KEY_BUFFER_SIZE = 4096; +const unicode_char INVALID_CHAR = '?'; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// character information +struct char_info +{ + unicode_char ch; + const char *alternate; // alternative string, in UTF-8 + + static const char_info *find(unicode_char target); +}; + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + + +// master character info table +const char_info charinfo[] = +{ + { 0x0009, " " }, // Tab + { 0x0061, "A" }, // a + { 0x0062, "B" }, // b + { 0x0063, "C" }, // c + { 0x0064, "D" }, // d + { 0x0065, "E" }, // e + { 0x0066, "F" }, // f + { 0x0067, "G" }, // g + { 0x0068, "H" }, // h + { 0x0069, "I" }, // i + { 0x006a, "J" }, // j + { 0x006b, "K" }, // k + { 0x006c, "L" }, // l + { 0x006d, "M" }, // m + { 0x006e, "N" }, // n + { 0x006f, "O" }, // o + { 0x0070, "P" }, // p + { 0x0071, "Q" }, // q + { 0x0072, "R" }, // r + { 0x0073, "S" }, // s + { 0x0074, "T" }, // t + { 0x0075, "U" }, // u + { 0x0076, "V" }, // v + { 0x0077, "W" }, // w + { 0x0078, "X" }, // x + { 0x0079, "Y" }, // y + { 0x007a, "Z" }, // z + { 0x00a0, " " }, // non breaking space + { 0x00a1, "!" }, // inverted exclamation mark + { 0x00a6, "|" }, // broken bar + { 0x00a9, "(c)" }, // copyright sign + { 0x00ab, "<<" }, // left pointing double angle + { 0x00ae, "(r)" }, // registered sign + { 0x00bb, ">>" }, // right pointing double angle + { 0x00bc, "1/4" }, // vulgar fraction one quarter + { 0x00bd, "1/2" }, // vulgar fraction one half + { 0x00be, "3/4" }, // vulgar fraction three quarters + { 0x00bf, "?" }, // inverted question mark + { 0x00c0, "A" }, // 'A' grave + { 0x00c1, "A" }, // 'A' acute + { 0x00c2, "A" }, // 'A' circumflex + { 0x00c3, "A" }, // 'A' tilde + { 0x00c4, "A" }, // 'A' diaeresis + { 0x00c5, "A" }, // 'A' ring above + { 0x00c6, "AE" }, // 'AE' ligature + { 0x00c7, "C" }, // 'C' cedilla + { 0x00c8, "E" }, // 'E' grave + { 0x00c9, "E" }, // 'E' acute + { 0x00ca, "E" }, // 'E' circumflex + { 0x00cb, "E" }, // 'E' diaeresis + { 0x00cc, "I" }, // 'I' grave + { 0x00cd, "I" }, // 'I' acute + { 0x00ce, "I" }, // 'I' circumflex + { 0x00cf, "I" }, // 'I' diaeresis + { 0x00d0, "D" }, // 'ETH' + { 0x00d1, "N" }, // 'N' tilde + { 0x00d2, "O" }, // 'O' grave + { 0x00d3, "O" }, // 'O' acute + { 0x00d4, "O" }, // 'O' circumflex + { 0x00d5, "O" }, // 'O' tilde + { 0x00d6, "O" }, // 'O' diaeresis + { 0x00d7, "X" }, // multiplication sign + { 0x00d8, "O" }, // 'O' stroke + { 0x00d9, "U" }, // 'U' grave + { 0x00da, "U" }, // 'U' acute + { 0x00db, "U" }, // 'U' circumflex + { 0x00dc, "U" }, // 'U' diaeresis + { 0x00dd, "Y" }, // 'Y' acute + { 0x00df, "SS" }, // sharp S + { 0x00e0, "a" }, // 'a' grave + { 0x00e1, "a" }, // 'a' acute + { 0x00e2, "a" }, // 'a' circumflex + { 0x00e3, "a" }, // 'a' tilde + { 0x00e4, "a" }, // 'a' diaeresis + { 0x00e5, "a" }, // 'a' ring above + { 0x00e6, "ae" }, // 'ae' ligature + { 0x00e7, "c" }, // 'c' cedilla + { 0x00e8, "e" }, // 'e' grave + { 0x00e9, "e" }, // 'e' acute + { 0x00ea, "e" }, // 'e' circumflex + { 0x00eb, "e" }, // 'e' diaeresis + { 0x00ec, "i" }, // 'i' grave + { 0x00ed, "i" }, // 'i' acute + { 0x00ee, "i" }, // 'i' circumflex + { 0x00ef, "i" }, // 'i' diaeresis + { 0x00f0, "d" }, // 'eth' + { 0x00f1, "n" }, // 'n' tilde + { 0x00f2, "o" }, // 'o' grave + { 0x00f3, "o" }, // 'o' acute + { 0x00f4, "o" }, // 'o' circumflex + { 0x00f5, "o" }, // 'o' tilde + { 0x00f6, "o" }, // 'o' diaeresis + { 0x00f8, "o" }, // 'o' stroke + { 0x00f9, "u" }, // 'u' grave + { 0x00fa, "u" }, // 'u' acute + { 0x00fb, "u" }, // 'u' circumflex + { 0x00fc, "u" }, // 'u' diaeresis + { 0x00fd, "y" }, // 'y' acute + { 0x00ff, "y" }, // 'y' diaeresis + { 0x2010, "-" }, // hyphen + { 0x2011, "-" }, // non-breaking hyphen + { 0x2012, "-" }, // figure dash + { 0x2013, "-" }, // en dash + { 0x2014, "-" }, // em dash + { 0x2015, "-" }, // horizontal dash + { 0x2018, "\'" }, // left single quotation mark + { 0x2019, "\'" }, // right single quotation mark + { 0x201a, "\'" }, // single low quotation mark + { 0x201b, "\'" }, // single high reversed quotation mark + { 0x201c, "\"" }, // left double quotation mark + { 0x201d, "\"" }, // right double quotation mark + { 0x201e, "\"" }, // double low quotation mark + { 0x201f, "\"" }, // double high reversed quotation mark + { 0x2024, "." }, // one dot leader + { 0x2025, ".." }, // two dot leader + { 0x2026, "..." }, // horizontal ellipsis + { 0x2047, "??" }, // double question mark + { 0x2048, "?!" }, // question exclamation mark + { 0x2049, "!?" }, // exclamation question mark + { 0xff01, "!" }, // fullwidth exclamation point + { 0xff02, "\"" }, // fullwidth quotation mark + { 0xff03, "#" }, // fullwidth number sign + { 0xff04, "$" }, // fullwidth dollar sign + { 0xff05, "%" }, // fullwidth percent sign + { 0xff06, "&" }, // fullwidth ampersand + { 0xff07, "\'" }, // fullwidth apostrophe + { 0xff08, "(" }, // fullwidth left parenthesis + { 0xff09, ")" }, // fullwidth right parenthesis + { 0xff0a, "*" }, // fullwidth asterisk + { 0xff0b, "+" }, // fullwidth plus + { 0xff0c, "," }, // fullwidth comma + { 0xff0d, "-" }, // fullwidth minus + { 0xff0e, "." }, // fullwidth period + { 0xff0f, "/" }, // fullwidth slash + { 0xff10, "0" }, // fullwidth zero + { 0xff11, "1" }, // fullwidth one + { 0xff12, "2" }, // fullwidth two + { 0xff13, "3" }, // fullwidth three + { 0xff14, "4" }, // fullwidth four + { 0xff15, "5" }, // fullwidth five + { 0xff16, "6" }, // fullwidth six + { 0xff17, "7" }, // fullwidth seven + { 0xff18, "8" }, // fullwidth eight + { 0xff19, "9" }, // fullwidth nine + { 0xff1a, ":" }, // fullwidth colon + { 0xff1b, ";" }, // fullwidth semicolon + { 0xff1c, "<" }, // fullwidth less than sign + { 0xff1d, "=" }, // fullwidth equals sign + { 0xff1e, ">" }, // fullwidth greater than sign + { 0xff1f, "?" }, // fullwidth question mark + { 0xff20, "@" }, // fullwidth at sign + { 0xff21, "A" }, // fullwidth 'A' + { 0xff22, "B" }, // fullwidth 'B' + { 0xff23, "C" }, // fullwidth 'C' + { 0xff24, "D" }, // fullwidth 'D' + { 0xff25, "E" }, // fullwidth 'E' + { 0xff26, "F" }, // fullwidth 'F' + { 0xff27, "G" }, // fullwidth 'G' + { 0xff28, "H" }, // fullwidth 'H' + { 0xff29, "I" }, // fullwidth 'I' + { 0xff2a, "J" }, // fullwidth 'J' + { 0xff2b, "K" }, // fullwidth 'K' + { 0xff2c, "L" }, // fullwidth 'L' + { 0xff2d, "M" }, // fullwidth 'M' + { 0xff2e, "N" }, // fullwidth 'N' + { 0xff2f, "O" }, // fullwidth 'O' + { 0xff30, "P" }, // fullwidth 'P' + { 0xff31, "Q" }, // fullwidth 'Q' + { 0xff32, "R" }, // fullwidth 'R' + { 0xff33, "S" }, // fullwidth 'S' + { 0xff34, "T" }, // fullwidth 'T' + { 0xff35, "U" }, // fullwidth 'U' + { 0xff36, "V" }, // fullwidth 'V' + { 0xff37, "W" }, // fullwidth 'W' + { 0xff38, "X" }, // fullwidth 'X' + { 0xff39, "Y" }, // fullwidth 'Y' + { 0xff3a, "Z" }, // fullwidth 'Z' + { 0xff3b, "[" }, // fullwidth left bracket + { 0xff3c, "\\" }, // fullwidth backslash + { 0xff3d, "]" }, // fullwidth right bracket + { 0xff3e, "^" }, // fullwidth caret + { 0xff3f, "_" }, // fullwidth underscore + { 0xff40, "`" }, // fullwidth backquote + { 0xff41, "a" }, // fullwidth 'a' + { 0xff42, "b" }, // fullwidth 'b' + { 0xff43, "c" }, // fullwidth 'c' + { 0xff44, "d" }, // fullwidth 'd' + { 0xff45, "e" }, // fullwidth 'e' + { 0xff46, "f" }, // fullwidth 'f' + { 0xff47, "g" }, // fullwidth 'g' + { 0xff48, "h" }, // fullwidth 'h' + { 0xff49, "i" }, // fullwidth 'i' + { 0xff4a, "j" }, // fullwidth 'j' + { 0xff4b, "k" }, // fullwidth 'k' + { 0xff4c, "l" }, // fullwidth 'l' + { 0xff4d, "m" }, // fullwidth 'm' + { 0xff4e, "n" }, // fullwidth 'n' + { 0xff4f, "o" }, // fullwidth 'o' + { 0xff50, "p" }, // fullwidth 'p' + { 0xff51, "q" }, // fullwidth 'q' + { 0xff52, "r" }, // fullwidth 'r' + { 0xff53, "s" }, // fullwidth 's' + { 0xff54, "t" }, // fullwidth 't' + { 0xff55, "u" }, // fullwidth 'u' + { 0xff56, "v" }, // fullwidth 'v' + { 0xff57, "w" }, // fullwidth 'w' + { 0xff58, "x" }, // fullwidth 'x' + { 0xff59, "y" }, // fullwidth 'y' + { 0xff5a, "z" }, // fullwidth 'z' + { 0xff5b, "{" }, // fullwidth left brace + { 0xff5c, "|" }, // fullwidth vertical bar + { 0xff5d, "}" }, // fullwidth right brace + { 0xff5e, "~" }, // fullwidth tilde + { 0xff5f, "((" }, // fullwidth double left parenthesis + { 0xff60, "))" }, // fullwidth double right parenthesis + { 0xffe0, "\xC2\xA2" }, // fullwidth cent sign + { 0xffe1, "\xC2\xA3" }, // fullwidth pound sign + { 0xffe4, "\xC2\xA4" }, // fullwidth broken bar + { 0xffe5, "\xC2\xA5" }, // fullwidth yen sign + { 0xffe6, "\xE2\x82\xA9" }, // fullwidth won sign + { 0xffe9, "\xE2\x86\x90" }, // fullwidth left arrow + { 0xffea, "\xE2\x86\x91" }, // fullwidth up arrow + { 0xffeb, "\xE2\x86\x92" }, // fullwidth right arrow + { 0xffec, "\xE2\x86\x93" }, // fullwidth down arrow + { 0xffed, "\xE2\x96\xAA" }, // fullwidth solid box + { 0xffee, "\xE2\x97\xA6" }, // fullwidth open circle + { UCHAR_MAMEKEY(ESC), "\033" }, // Esc key + { UCHAR_MAMEKEY(DEL), "\010" }, // Delete key + { UCHAR_MAMEKEY(HOME), "\014" } // Home key +}; + + + +//************************************************************************** +// NATURAL KEYBOARD +//************************************************************************** + +//------------------------------------------------- +// natural_keyboard - constructor +//------------------------------------------------- + +natural_keyboard::natural_keyboard(running_machine &machine) + : m_machine(machine), + m_bufbegin(0), + m_bufend(0), + m_status_keydown(false), + m_last_cr(false), + m_timer(nullptr), + m_current_rate(attotime::zero) +{ + m_queue_chars = ioport_queue_chars_delegate(); + m_accept_char = ioport_accept_char_delegate(); + m_charqueue_empty = ioport_charqueue_empty_delegate(); + + // posting keys directly only makes sense for a computer + if (machine.ioport().has_keyboard()) + { + m_buffer.resize(KEY_BUFFER_SIZE); + m_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(natural_keyboard::timer), this)); + build_codes(machine.ioport()); + } +} + + +//------------------------------------------------- +// configure - configure callbacks for full- +// featured keyboard support +//------------------------------------------------- + +void natural_keyboard::configure(ioport_queue_chars_delegate queue_chars, ioport_accept_char_delegate accept_char, ioport_charqueue_empty_delegate charqueue_empty) +{ + // set the callbacks + m_queue_chars = queue_chars; + m_accept_char = accept_char; + m_charqueue_empty = charqueue_empty; +} + + +//------------------------------------------------- +// post - post a single character +//------------------------------------------------- + +void natural_keyboard::post(unicode_char ch) +{ + // ignore any \n that are preceded by \r + if (m_last_cr && ch == '\n') + { + m_last_cr = false; + return; + } + + // change all eolns to '\r' + if (ch == '\n') + ch = '\r'; + else + m_last_cr = (ch == '\r'); + + // logging + if (LOG_NATURAL_KEYBOARD) + { + const keycode_map_entry *code = find_code(ch); + machine().logerror("natural_keyboard::post(): code=%i (%s) field.name='%s'\n", int(ch), unicode_to_string(ch).c_str(), (code != nullptr && code->field[0] != nullptr) ? code->field[0]->name() : ""); + } + + // can we post this key in the queue directly? + if (can_post_directly(ch)) + internal_post(ch); + + // can we post this key with an alternate representation? + else if (can_post_alternate(ch)) + { + const char_info *info = char_info::find(ch); + assert(info != nullptr && info->alternate != nullptr); + const char *altstring = info->alternate; + while (*altstring != 0) + { + altstring += uchar_from_utf8(&ch, altstring, strlen(altstring)); + internal_post(ch); + } + } +} + + +//------------------------------------------------- +// post - post a unicode encoded string +//------------------------------------------------- + +void natural_keyboard::post(const unicode_char *text, size_t length, const attotime &rate) +{ + // set the fixed rate + m_current_rate = rate; + + // 0 length means strlen + if (length == 0) + for (const unicode_char *scan = text; *scan != 0; scan++) + length++; + + // iterate over characters or until the buffer is full up + while (length > 0 && !full()) + { + // fetch next character + post(*text++); + length--; + } +} + + +//------------------------------------------------- +// post_utf8 - post a UTF-8 encoded string +//------------------------------------------------- + +void natural_keyboard::post_utf8(const char *text, size_t length, const attotime &rate) +{ + // set the fixed rate + m_current_rate = rate; + + // 0-length means strlen + if (length == 0) + length = strlen(text); + + // iterate until out of characters + while (length > 0) + { + // decode the next character + unicode_char uc; + int count = uchar_from_utf8(&uc, text, length); + if (count < 0) + { + count = 1; + uc = INVALID_CHAR; + } + + // append to the buffer + post(uc); + text += count; + length -= count; + } +} + + +//------------------------------------------------- +// post_coded - post a coded string +//------------------------------------------------- + +void natural_keyboard::post_coded(const char *text, size_t length, const attotime &rate) +{ + static const struct + { + const char *key; + unicode_char code; + } codes[] = + { + { "BACKSPACE", 8 }, + { "BS", 8 }, + { "BKSP", 8 }, + { "DEL", UCHAR_MAMEKEY(DEL) }, + { "DELETE", UCHAR_MAMEKEY(DEL) }, + { "END", UCHAR_MAMEKEY(END) }, + { "ENTER", 13 }, + { "ESC", '\033' }, + { "HOME", UCHAR_MAMEKEY(HOME) }, + { "INS", UCHAR_MAMEKEY(INSERT) }, + { "INSERT", UCHAR_MAMEKEY(INSERT) }, + { "PGDN", UCHAR_MAMEKEY(PGDN) }, + { "PGUP", UCHAR_MAMEKEY(PGUP) }, + { "SPACE", 32 }, + { "TAB", 9 }, + { "F1", UCHAR_MAMEKEY(F1) }, + { "F2", UCHAR_MAMEKEY(F2) }, + { "F3", UCHAR_MAMEKEY(F3) }, + { "F4", UCHAR_MAMEKEY(F4) }, + { "F5", UCHAR_MAMEKEY(F5) }, + { "F6", UCHAR_MAMEKEY(F6) }, + { "F7", UCHAR_MAMEKEY(F7) }, + { "F8", UCHAR_MAMEKEY(F8) }, + { "F9", UCHAR_MAMEKEY(F9) }, + { "F10", UCHAR_MAMEKEY(F10) }, + { "F11", UCHAR_MAMEKEY(F11) }, + { "F12", UCHAR_MAMEKEY(F12) }, + { "QUOTE", '\"' } + }; + + // set the fixed rate + m_current_rate = rate; + + // 0-length means strlen + if (length == 0) + length = strlen(text); + + // iterate through the source string + size_t curpos = 0; + while (curpos < length) + { + // extract next character + unicode_char ch = text[curpos]; + size_t increment = 1; + + // look for escape characters + if (ch == '{') + for (auto & code : codes) + { + size_t keylen = strlen(code.key); + if (curpos + keylen + 2 <= length) + if (core_strnicmp(code.key, &text[curpos + 1], keylen) == 0 && text[curpos + keylen + 1] == '}') + { + ch = code.code; + increment = keylen + 2; + } + } + + // if we got a code, post it + if (ch != 0) + post(ch); + curpos += increment; + } +} + + +//------------------------------------------------- +// build_codes - given an input port table, create +// an input code table useful for mapping unicode +// chars +//------------------------------------------------- + +void natural_keyboard::build_codes(ioport_manager &manager) +{ + // iterate over shift keys + ioport_field *shift[UCHAR_SHIFT_END + 1 - UCHAR_SHIFT_BEGIN] = { nullptr }; + for (int curshift = 0; curshift <= ARRAY_LENGTH(shift); curshift++) + if (curshift == 0 || shift[curshift - 1] != nullptr) + + // iterate over ports and fields + for (auto &port : manager.ports()) + for (ioport_field &field : port.second->fields()) + if (field.type() == IPT_KEYBOARD) + { + // fetch the code, ignoring 0 + unicode_char code = field.keyboard_code(curshift); + if (code == 0) + continue; + + // is this a shifter key? + if (code >= UCHAR_SHIFT_BEGIN && code <= UCHAR_SHIFT_END) + shift[code - UCHAR_SHIFT_BEGIN] = &field; + + // not a shifter key; record normally + else + { + keycode_map_entry newcode; + if (curshift == 0) + { + newcode.field[0] = &field; + newcode.field[1] = nullptr; + } + else + { + newcode.field[0] = shift[curshift - 1]; + newcode.field[1] = &field; + } + newcode.ch = code; + m_keycode_map.push_back(newcode); + + if (LOG_NATURAL_KEYBOARD) + { + machine().logerror("natural_keyboard: code=%i (%s) port=%p field.name='%s'\n", int(code), unicode_to_string(code).c_str(), (void *)&port, field.name()); + } + } + } +} + + +//------------------------------------------------- +// can_post_directly - determine if the given +// unicode character can be directly posted +//------------------------------------------------- + +bool natural_keyboard::can_post_directly(unicode_char ch) +{ + // if we have a queueing callback, then it depends on whether we can accept the character + if (!m_queue_chars.isnull()) + return m_accept_char.isnull() ? true : m_accept_char(ch); + + // otherwise, it depends on the input codes + const keycode_map_entry *code = find_code(ch); + return (code != nullptr && code->field[0] != nullptr); +} + + +//------------------------------------------------- +// can_post_alternate - determine if the given +// unicode character can be posted via translation +//------------------------------------------------- + +bool natural_keyboard::can_post_alternate(unicode_char ch) +{ + const char_info *info = char_info::find(ch); + if (info == nullptr) + return false; + + const char *altstring = info->alternate; + if (altstring == nullptr) + return false; + + while (*altstring != 0) + { + unicode_char uchar; + int count = uchar_from_utf8(&uchar, altstring, strlen(altstring)); + if (count <= 0) + return false; + if (!can_post_directly(uchar)) + return false; + altstring += count; + } + return true; +} + + +//------------------------------------------------- +// choose_delay - determine the delay between +// posting keyboard events +//------------------------------------------------- + +attotime natural_keyboard::choose_delay(unicode_char ch) +{ + // if we have a live rate, just use that + if (m_current_rate != attotime::zero) + return m_current_rate; + + // systems with queue_chars can afford a much smaller delay + if (!m_queue_chars.isnull()) + return attotime::from_msec(10); + + // otherwise, default to constant delay with a longer delay on CR + return attotime::from_msec((ch == '\r') ? 200 : 50); +} + + +//------------------------------------------------- +// internal_post - post a keyboard event +//------------------------------------------------- + +void natural_keyboard::internal_post(unicode_char ch) +{ + // need to start up the timer? + if (empty()) + { + m_timer->adjust(choose_delay(ch)); + m_status_keydown = 0; + } + + // add to the buffer, resizing if necessary + m_buffer[m_bufend++] = ch; + if ((m_bufend + 1) % m_buffer.size() == m_bufbegin) + m_buffer.resize(m_buffer.size() + KEY_BUFFER_SIZE); + m_bufend %= m_buffer.size(); +} + + +//------------------------------------------------- +// timer - timer callback to keep things flowing +// when posting a string of characters +//------------------------------------------------- + +void natural_keyboard::timer(void *ptr, int param) +{ + // the driver has a queue_chars handler + if (!m_queue_chars.isnull()) + { + while (!empty() && m_queue_chars(&m_buffer[m_bufbegin], 1)) + { + m_bufbegin = (m_bufbegin + 1) % m_buffer.size(); + if (m_current_rate != attotime::zero) + break; + } + } + + // the driver does not have a queue_chars handler + else + { + // loop through this character's component codes + const bool new_keydown = !m_status_keydown; + const keycode_map_entry *const code = find_code(m_buffer[m_bufbegin]); + if (code != nullptr) + for (int fieldnum = 0; fieldnum < ARRAY_LENGTH(code->field) && code->field[fieldnum] != nullptr; fieldnum++) + code->field[fieldnum]->set_value(new_keydown); + + // proceed to next character when keydown expires + if (!new_keydown) + m_bufbegin = (m_bufbegin + 1) % m_buffer.size(); + m_status_keydown = new_keydown; + } + + // need to make sure timerproc is called again if buffer not empty + if (!empty()) + m_timer->adjust(choose_delay(m_buffer[m_bufbegin])); +} + + +//------------------------------------------------- +// unicode_to_string - obtain a string +// representation of a given code; used for +// logging and debugging +//------------------------------------------------- + +std::string natural_keyboard::unicode_to_string(unicode_char ch) +{ + std::string buffer; + switch (ch) + { + // check some magic values + case '\0': buffer.assign("\\0"); break; + case '\r': buffer.assign("\\r"); break; + case '\n': buffer.assign("\\n"); break; + case '\t': buffer.assign("\\t"); break; + + default: + // seven bit ASCII is easy + if (ch >= 32 && ch < 128) + { + char temp[2] = { char(ch), 0 }; + buffer.assign(temp); + } + else if (ch >= UCHAR_MAMEKEY_BEGIN) + { + // try to obtain a codename with code_name(); this can result in an empty string + input_code code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, input_item_id(ch - UCHAR_MAMEKEY_BEGIN)); + buffer = machine().input().code_name(code); + } + + // did we fail to resolve? if so, we have a last resort + if (buffer.empty()) + buffer = string_format("U+%04X", unsigned(ch)); + break; + } + return buffer; +} + + +//------------------------------------------------- +// find_code - find a code in our lookup table +//------------------------------------------------- + +const natural_keyboard::keycode_map_entry *natural_keyboard::find_code(unicode_char ch) const +{ + for (auto & elem : m_keycode_map) + { + if (elem.ch == ch) + return &elem; + } + return nullptr; +} + + +//------------------------------------------------- +// dump - dumps info to string +//------------------------------------------------- + +std::string natural_keyboard::dump() +{ + std::ostringstream buffer; + const size_t left_column_width = 24; + + // loop through all codes + for (auto & code : m_keycode_map) + { + // describe the character code + std::string description = string_format("%08X (%s) ", code.ch, unicode_to_string(code.ch).c_str()); + + // pad with spaces + util::stream_format(buffer, "%-*s", left_column_width, description); + + // identify the keys used + for (int field = 0; field < ARRAY_LENGTH(code.field) && code.field[field] != nullptr; field++) + util::stream_format(buffer, "%s'%s'", (field > 0) ? ", " : "", code.field[field]->name()); + + // carriage return + buffer << '\n'; + } + + return buffer.str(); +} + + +/*************************************************************************** + MISCELLANEOUS +***************************************************************************/ + +//------------------------------------------------- +// find - look up information about a particular +// character +//------------------------------------------------- + +const char_info *char_info::find(unicode_char target) +{ + // perform a simple binary search to find the proper alternate + int low = 0; + int high = ARRAY_LENGTH(charinfo); + while (high > low) + { + int middle = (high + low) / 2; + unicode_char ch = charinfo[middle].ch; + if (ch < target) + low = middle + 1; + else if (ch > target) + high = middle; + else + return &charinfo[middle]; + } + return nullptr; +} + + +//------------------------------------------------- +// validate_natural_keyboard_statics - +// validates natural keyboard static data +//------------------------------------------------- + +/* +int validate_natural_keyboard_statics(void) +{ + int i; + int error = FALSE; + unicode_char last_char = 0; + const char_info *ci; + + // check to make sure that charinfo is in order + for (i = 0; i < ARRAY_LENGTH(charinfo); i++) + { + if (last_char >= charinfo[i].ch) + { + osd_printf_error("inputx: charinfo is out of order; 0x%08x should be higher than 0x%08x\n", charinfo[i].ch, last_char); + error = TRUE; + } + last_char = charinfo[i].ch; + } + + // check to make sure that I can look up everything on alternate_charmap + for (i = 0; i < ARRAY_LENGTH(charinfo); i++) + { + ci = char_info::find(charinfo[i].ch); + if (ci != &charinfo[i]) + { + osd_printf_error("ioport: expected char_info::find(0x%08x) to work properly\n", charinfo[i].ch); + error = TRUE; + } + } + return error; +} +*/ diff --git a/src/emu/natkeyboard.h b/src/emu/natkeyboard.h new file mode 100644 index 00000000000..519482b8cb9 --- /dev/null +++ b/src/emu/natkeyboard.h @@ -0,0 +1,90 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + natkeyboard.h + + Natural keyboard input support. + +***************************************************************************/ + +#pragma once + +#ifndef EMU_NATKEYBOARD_H +#define EMU_NATKEYBOARD_H + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// keyboard helper function delegates +typedef delegate ioport_queue_chars_delegate; +typedef delegate ioport_accept_char_delegate; +typedef delegate ioport_charqueue_empty_delegate; + + +// ======================> natural_keyboard + +// buffer to handle copy/paste/insert of keys +class natural_keyboard +{ + DISABLE_COPYING(natural_keyboard); + +public: + // construction/destruction + natural_keyboard(running_machine &machine); + + // getters and queries + running_machine &machine() const { return m_machine; } + bool empty() const { return (m_bufbegin == m_bufend); } + bool full() const { return ((m_bufend + 1) % m_buffer.size()) == m_bufbegin; } + bool can_post() const { return (!m_queue_chars.isnull() || !m_keycode_map.empty()); } + bool is_posting() const { return (!empty() || (!m_charqueue_empty.isnull() && !m_charqueue_empty())); } + + // configuration + void configure(ioport_queue_chars_delegate queue_chars, ioport_accept_char_delegate accept_char, ioport_charqueue_empty_delegate charqueue_empty); + + // posting + void post(unicode_char ch); + void post(const unicode_char *text, size_t length = 0, const attotime &rate = attotime::zero); + void post_utf8(const char *text, size_t length = 0, const attotime &rate = attotime::zero); + void post_coded(const char *text, size_t length = 0, const attotime &rate = attotime::zero); + + // debugging + std::string dump(); + +private: + // internal keyboard code information + struct keycode_map_entry + { + unicode_char ch; + ioport_field * field[UCHAR_SHIFT_END + 1 - UCHAR_SHIFT_BEGIN]; + }; + + // internal helpers + void build_codes(ioport_manager &manager); + bool can_post_directly(unicode_char ch); + bool can_post_alternate(unicode_char ch); + attotime choose_delay(unicode_char ch); + void internal_post(unicode_char ch); + void timer(void *ptr, int param); + std::string unicode_to_string(unicode_char ch); + const keycode_map_entry *find_code(unicode_char ch) const; + + // internal state + running_machine & m_machine; // reference to our machine + UINT32 m_bufbegin; // index of starting character + UINT32 m_bufend; // index of ending character + std::vector m_buffer; // actual buffer + bool m_status_keydown; // current keydown status + bool m_last_cr; // was the last char a CR? + emu_timer * m_timer; // timer for posting characters + attotime m_current_rate; // current rate for posting + ioport_queue_chars_delegate m_queue_chars; // queue characters callback + ioport_accept_char_delegate m_accept_char; // accept character callback + ioport_charqueue_empty_delegate m_charqueue_empty; // character queue empty callback + std::vector m_keycode_map; // keycode map +}; + +#endif diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp index 88c271f008e..86b0b71b93f 100644 --- a/src/frontend/mame/luaengine.cpp +++ b/src/frontend/mame/luaengine.cpp @@ -20,6 +20,7 @@ #include "emuopts.h" #include "ui/ui.h" #include "luaengine.h" +#include "natkeyboard.h" #include "uiinput.h" #include diff --git a/src/frontend/mame/ui/mainmenu.cpp b/src/frontend/mame/ui/mainmenu.cpp index b3b54891168..969f9b5b9f0 100644 --- a/src/frontend/mame/ui/mainmenu.cpp +++ b/src/frontend/mame/ui/mainmenu.cpp @@ -11,6 +11,7 @@ #include "emu.h" #include "crsshair.h" #include "emuopts.h" +#include "natkeyboard.h" #include "mame.h" #include "luaengine.h" #include "ui/menu.h" diff --git a/src/frontend/mame/ui/ui.cpp b/src/frontend/mame/ui/ui.cpp index 85374c14c64..ff9a5564b2a 100644 --- a/src/frontend/mame/ui/ui.cpp +++ b/src/frontend/mame/ui/ui.cpp @@ -15,6 +15,7 @@ #include "video/vector.h" #include "machine/laserdsc.h" #include "drivenum.h" +#include "natkeyboard.h" #include "render.h" #include "luaengine.h" #include "cheat.h"