Separate natural keyboard support from ioport.cpp (nw)

This commit is contained in:
AJR 2016-10-01 23:25:53 -04:00
parent 53ae060484
commit f1b0dfe64a
10 changed files with 1048 additions and 1028 deletions

View File

@ -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",

View File

@ -20,6 +20,7 @@
#include "emu.h"
#include "pckeybrd.h"
#include "natkeyboard.h"
/* AT keyboard documentation comes from www.beyondlogic.org and HelpPC documentation */

View File

@ -17,6 +17,7 @@
#include "express.h"
#include "debughlp.h"
#include "debugvw.h"
#include "natkeyboard.h"
#include "render.h"
#include <ctype.h>

File diff suppressed because it is too large Load Diff

View File

@ -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_value (ioport_field &, void *)> ioport_field_read
typedef device_delegate<void (ioport_field &, void *, ioport_value, ioport_value)> ioport_field_write_delegate;
typedef device_delegate<float (ioport_field &, float)> ioport_field_crossmap_delegate;
// keyboard helper function delegates
typedef delegate<int (const unicode_char *, size_t)> ioport_queue_chars_delegate;
typedef delegate<bool (unicode_char)> ioport_accept_char_delegate;
typedef delegate<bool ()> 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<unicode_char> 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<keycode_map_entry> 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<input_type_entry> &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<digital_joystick> m_joystick_list; // list of digital joysticks
natural_keyboard m_natkeyboard; // natural keyboard support
std::unique_ptr<natural_keyboard> m_natkeyboard; // natural keyboard support
// frame time tracking
attotime m_last_frame_time; // time of the last frame callback

842
src/emu/natkeyboard.cpp Normal file
View File

@ -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() : "<null>");
}
// 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;
}
*/

90
src/emu/natkeyboard.h Normal file
View File

@ -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<int (const unicode_char *, size_t)> ioport_queue_chars_delegate;
typedef delegate<bool (unicode_char)> ioport_accept_char_delegate;
typedef delegate<bool ()> 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<unicode_char> 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<keycode_map_entry> m_keycode_map; // keycode map
};
#endif

View File

@ -20,6 +20,7 @@
#include "emuopts.h"
#include "ui/ui.h"
#include "luaengine.h"
#include "natkeyboard.h"
#include "uiinput.h"
#include <mutex>

View File

@ -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"

View File

@ -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"