diff options
Diffstat (limited to 'src/emu/natkeyboard.cpp')
-rw-r--r-- | src/emu/natkeyboard.cpp | 482 |
1 files changed, 299 insertions, 183 deletions
diff --git a/src/emu/natkeyboard.cpp b/src/emu/natkeyboard.cpp index 7d154787c0c..1abe9a140ff 100644 --- a/src/emu/natkeyboard.cpp +++ b/src/emu/natkeyboard.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles,Vas Crabb /*************************************************************************** natkeyboard.cpp @@ -10,7 +10,16 @@ #include "emu.h" #include "natkeyboard.h" + +#include "corestr.h" #include "emuopts.h" +#include "unicode.h" + +// FIXME: allow OSD module headers to be included in a less ugly way +#include "../osd/modules/lib/osdlib.h" + +#include <algorithm> +#include <cstring> //************************************************************************** @@ -25,7 +34,8 @@ // CONSTANTS //************************************************************************** -const int KEY_BUFFER_SIZE = 4096; +const u32 KEY_BUFFER_CHUNK_SIZE = 0x1000; +const u32 KEY_BUFFER_MAX_SIZE = 0x800000; const char32_t INVALID_CHAR = '?'; @@ -38,7 +48,7 @@ const char32_t INVALID_CHAR = '?'; struct char_info { char32_t ch; - const char *alternate; // alternative string, in UTF-8 + const char *alternate; // alternative string, in UTF-8 static const char_info *find(char32_t target); }; @@ -317,9 +327,11 @@ const char_info charinfo[] = natural_keyboard::natural_keyboard(running_machine &machine) : m_machine(machine) + , m_have_charkeys(false) , m_in_use(false) , m_bufbegin(0) , m_bufend(0) + , m_current_code(nullptr) , m_fieldnum(0) , m_status_keydown(false) , m_last_cr(false) @@ -330,10 +342,10 @@ natural_keyboard::natural_keyboard(running_machine &machine) , m_charqueue_empty() { // try building a list of keycodes; if none are available, don't bother - build_codes(machine.ioport()); - if (!m_keycode_map.empty()) + build_codes(); + if (!m_keyboards.empty()) { - m_buffer.resize(KEY_BUFFER_SIZE); + m_buffer.resize(KEY_BUFFER_CHUNK_SIZE); m_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(natural_keyboard::timer), this)); } @@ -350,9 +362,10 @@ natural_keyboard::natural_keyboard(running_machine &machine) 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; + assert(m_timer != nullptr); + m_queue_chars = std::move(queue_chars); + m_accept_char = std::move(accept_char); + m_charqueue_empty = std::move(charqueue_empty); } @@ -370,59 +383,69 @@ void natural_keyboard::set_in_use(bool usage) machine().options().set_value(OPTION_NATURAL_KEYBOARD, usage, OPTION_PRIORITY_CMDLINE); // lock out (or unlock) all keyboard inputs - for (auto &port : machine().ioport().ports()) - for (ioport_field &field : port.second->fields()) - if (field.type() == IPT_KEYBOARD) - { - field.live().lockout = usage; + for (kbd_dev_info &devinfo : m_keyboards) + { + for (ioport_field &field : devinfo.keyfields) + { + bool const is_keyboard(field.type() == IPT_KEYBOARD); + field.live().lockout = !devinfo.enabled || (is_keyboard && usage); - // clear pressed status when going out of use - if (!usage) - field.set_value(0); - } + // clear pressed status when going out of use + if (is_keyboard && !usage) + field.set_value(0); + } + } } } //------------------------------------------------- -// post - post a single character +// post_char - post a single character //------------------------------------------------- -void natural_keyboard::post(char32_t ch) +void natural_keyboard::post_char(char32_t ch, bool normalize_crlf) { - // ignore any \n that are preceded by \r - if (m_last_cr && ch == '\n') + if (normalize_crlf) { - m_last_cr = false; - return; - } + // 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'); + // 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>"); + machine().logerror("natural_keyboard::post_char(): 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)) + { + // can post this key in the queue directly 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); + // can post this key with an alternate representation + char_info const *const info = char_info::find(ch); assert(info != nullptr && info->alternate != nullptr); - const char *altstring = info->alternate; - while (*altstring != 0) + char const *altstring = info->alternate; + unsigned remain(std::strlen(altstring)); + while (remain) { - altstring += uchar_from_utf8(&ch, altstring, strlen(altstring)); + int const used(uchar_from_utf8(&ch, altstring, remain)); + assert(0 < used); + altstring += used; + remain -= used; internal_post(ch); } } @@ -433,23 +456,14 @@ void natural_keyboard::post(char32_t ch) // post - post a unicode encoded string //------------------------------------------------- -void natural_keyboard::post(const char32_t *text, size_t length, const attotime &rate) +void natural_keyboard::post(std::u32string_view text, const attotime &rate) { // set the fixed rate m_current_rate = rate; - // 0 length means strlen - if (length == 0) - for (const char32_t *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--; - } + // iterate over characters + for (char32_t ch : text) + post_char(ch, true); } @@ -457,21 +471,17 @@ void natural_keyboard::post(const char32_t *text, size_t length, const attotime // post_utf8 - post a UTF-8 encoded string //------------------------------------------------- -void natural_keyboard::post_utf8(const char *text, size_t length, const attotime &rate) +void natural_keyboard::post_utf8(std::string_view text, 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) + while (!text.empty()) { // decode the next character char32_t uc; - int count = uchar_from_utf8(&uc, text, length); + int count = uchar_from_utf8(&uc, text); if (count < 0) { count = 1; @@ -479,84 +489,75 @@ void natural_keyboard::post_utf8(const char *text, size_t length, const attotime } // append to the buffer - post(uc); - text += count; - length -= count; + post_char(uc, true); + text.remove_prefix(count); } } -void natural_keyboard::post_utf8(const std::string &text, const attotime &rate) -{ - if (!text.empty()) - post_utf8(text.c_str(), text.size(), rate); -} - - //------------------------------------------------- // post_coded - post a coded string //------------------------------------------------- -void natural_keyboard::post_coded(const char *text, size_t length, const attotime &rate) +void natural_keyboard::post_coded(std::string_view text, const attotime &rate) { + using namespace std::literals; static const struct { - const char *key; + std::string_view key; char32_t 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", '\"' } + { "BACKSPACE"sv, 8 }, + { "BS"sv, 8 }, + { "BKSP"sv, 8 }, + { "CAPSLOCK"sv, UCHAR_MAMEKEY(CAPSLOCK) }, + { "CR"sv, 13 }, + { "DEL"sv, UCHAR_MAMEKEY(DEL) }, + { "DELETE"sv, UCHAR_MAMEKEY(DEL) }, + { "END"sv, UCHAR_MAMEKEY(END) }, + { "ENTER"sv, 13 }, + { "ESC"sv, '\033' }, + { "HOME"sv, UCHAR_MAMEKEY(HOME) }, + { "INS"sv, UCHAR_MAMEKEY(INSERT) }, + { "INSERT"sv, UCHAR_MAMEKEY(INSERT) }, + { "LF"sv, 10 }, + { "PGDN"sv, UCHAR_MAMEKEY(PGDN) }, + { "PGUP"sv, UCHAR_MAMEKEY(PGUP) }, + { "SPACE"sv, 32 }, + { "TAB"sv, 9 }, + { "F1"sv, UCHAR_MAMEKEY(F1) }, + { "F2"sv, UCHAR_MAMEKEY(F2) }, + { "F3"sv, UCHAR_MAMEKEY(F3) }, + { "F4"sv, UCHAR_MAMEKEY(F4) }, + { "F5"sv, UCHAR_MAMEKEY(F5) }, + { "F6"sv, UCHAR_MAMEKEY(F6) }, + { "F7"sv, UCHAR_MAMEKEY(F7) }, + { "F8"sv, UCHAR_MAMEKEY(F8) }, + { "F9"sv, UCHAR_MAMEKEY(F9) }, + { "F10"sv, UCHAR_MAMEKEY(F10) }, + { "F11"sv, UCHAR_MAMEKEY(F11) }, + { "F12"sv, UCHAR_MAMEKEY(F12) }, + { "QUOTE"sv, '\"' } }; // 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) + while (!text.empty()) { // extract next character - char32_t ch = text[curpos]; - size_t increment = 1; + char32_t ch = text.front(); + std::string_view::size_type 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] == '}') + std::string_view::size_type keylen = code.key.length(); + if (keylen + 2 <= text.length()) + if (util::strequpper(text.substr(1, keylen), code.key) && text[keylen + 1] == '}') { ch = code.code; increment = keylen + 2; @@ -565,19 +566,12 @@ void natural_keyboard::post_coded(const char *text, size_t length, const attotim // if we got a code, post it if (ch != 0) - post(ch); - curpos += increment; + post_char(ch); + text.remove_prefix(increment); } } -void natural_keyboard::post_coded(const std::string &text, const attotime &rate) -{ - if (!text.empty()) - post_coded(text.c_str(), text.size(), rate); -} - - //------------------------------------------------- // paste - does a paste from the keyboard //------------------------------------------------- @@ -598,16 +592,63 @@ void natural_keyboard::paste() // chars //------------------------------------------------- -void natural_keyboard::build_codes(ioport_manager &manager) +void natural_keyboard::build_codes() { - // find all shift keys - unsigned mask = 0; - std::array<ioport_field *, SHIFT_COUNT> shift; - std::fill(std::begin(shift), std::end(shift), nullptr); + ioport_manager &manager(machine().ioport()); + + // find all the devices with keyboard or keypad inputs for (auto const &port : manager.ports()) { + auto devinfo( + std::find_if( + m_keyboards.begin(), + m_keyboards.end(), + [&port] (kbd_dev_info const &info) + { + return &port.second->device() == &info.device.get(); + })); for (ioport_field &field : port.second->fields()) { + bool const is_keyboard(field.type() == IPT_KEYBOARD); + if (is_keyboard || (field.type() == IPT_KEYPAD)) + { + if (m_keyboards.end() == devinfo) + devinfo = m_keyboards.emplace(devinfo, port.second->device()); + devinfo->keyfields.emplace_back(field); + if (is_keyboard) + devinfo->keyboard = true; + else + devinfo->keypad = true; + } + } + } + std::sort( + std::begin(m_keyboards), + std::end(m_keyboards), + [] (kbd_dev_info const &l, kbd_dev_info const &r) + { + return 0 > std::strcmp(l.device.get().tag(), r.device.get().tag()); + }); + + // set up key mappings for each keyboard + std::array<ioport_field *, SHIFT_COUNT> shift; + unsigned mask; + bool have_keyboard(false); + for (kbd_dev_info &devinfo : m_keyboards) + { + if (LOG_NATURAL_KEYBOARD) + machine().logerror("natural_keyboard: building codes for %s... (%u fields)\n", devinfo.device.get().tag(), devinfo.keyfields.size()); + + // enable all pure keypads and the first keyboard + if (!devinfo.keyboard || !have_keyboard) + devinfo.enabled = true; + have_keyboard = have_keyboard || devinfo.keyboard; + + // find all shift keys + std::fill(std::begin(shift), std::end(shift), nullptr); + mask = 0; + for (ioport_field &field : devinfo.keyfields) + { if (field.type() == IPT_KEYBOARD) { std::vector<char32_t> const codes = field.keyboard_codes(0); @@ -617,17 +658,17 @@ void natural_keyboard::build_codes(ioport_manager &manager) { mask |= 1U << (code - UCHAR_SHIFT_BEGIN); shift[code - UCHAR_SHIFT_BEGIN] = &field; + if (LOG_NATURAL_KEYBOARD) + machine().logerror("natural_keyboard: UCHAR_SHIFT_%d found\n", code - UCHAR_SHIFT_BEGIN + 1); } } } } - } - // iterate over ports and fields - for (auto const &port : manager.ports()) - { - for (ioport_field &field : port.second->fields()) + // iterate over keyboard/keypad fields + for (ioport_field &field : devinfo.keyfields) { + field.live().lockout = !devinfo.enabled; if (field.type() == IPT_KEYBOARD) { // iterate over all shift states @@ -635,38 +676,40 @@ void natural_keyboard::build_codes(ioport_manager &manager) { if (!(curshift & ~mask)) { - // fetch the code, ignoring 0 and shiters + // fetch the code, ignoring shifters std::vector<char32_t> const codes = field.keyboard_codes(curshift); for (char32_t code : codes) { - if (((code < UCHAR_SHIFT_BEGIN) || (code > UCHAR_SHIFT_END)) && (code != 0)) + if ((code < UCHAR_SHIFT_BEGIN) || (code > UCHAR_SHIFT_END)) { - // prefer lowest shift state - keycode_map::iterator const found(m_keycode_map.find(code)); - if ((m_keycode_map.end() == found) || (found->second.shift > curshift)) + m_have_charkeys = true; + keycode_map::iterator const found(devinfo.codemap.find(code)); + keycode_map_entry newcode; + std::fill(std::begin(newcode.field), std::end(newcode.field), nullptr); + newcode.shift = curshift; + newcode.condition = field.condition(); + + unsigned fieldnum(0); + for (unsigned i = 0, bits = curshift; (i < SHIFT_COUNT) && bits; ++i, bits >>= 1) { - keycode_map_entry newcode; - std::fill(std::begin(newcode.field), std::end(newcode.field), nullptr); - newcode.shift = curshift; - - unsigned fieldnum = 0; - for (unsigned i = 0, bits = curshift; (i < SHIFT_COUNT) && bits; ++i, bits >>= 1) - { - if (BIT(bits, 0)) - newcode.field[fieldnum++] = shift[i]; - } - - newcode.field[fieldnum] = &field; - if (m_keycode_map.end() == found) - m_keycode_map.emplace(code, newcode); - else - found->second = newcode; - - if (LOG_NATURAL_KEYBOARD) - { - machine().logerror("natural_keyboard: code=%u (%s) port=%p field.name='%s'\n", - code, unicode_to_string(code), (void *)&port, field.name()); - } + if (BIT(bits, 0)) + newcode.field[fieldnum++] = shift[i]; + } + + newcode.field[fieldnum] = &field; + if (devinfo.codemap.end() == found) + { + keycode_map_entries entries; + entries.emplace_back(newcode); + devinfo.codemap.emplace(code, std::move(entries)); + } + else + found->second.emplace_back(newcode); + + if (LOG_NATURAL_KEYBOARD) + { + machine().logerror("natural_keyboard: code=%u (%s) port=%p field.name='%s'\n", + code, unicode_to_string(code), (void *)&field.port(), field.name()); } } } @@ -674,6 +717,31 @@ void natural_keyboard::build_codes(ioport_manager &manager) } } } + + // sort mapping entries by shift state + for (auto &mapping : devinfo.codemap) + { + std::sort( + mapping.second.begin(), + mapping.second.end(), + [] (keycode_map_entry const &x, keycode_map_entry const &y) { return x.shift < y.shift; }); + } + } +} + + +//------------------------------------------------- +// set_keyboard_enabled - enable or disable a +// device with key inputs +//------------------------------------------------- + +void natural_keyboard::set_keyboard_enabled(size_t n, bool enable) +{ + if (enable != m_keyboards[n].enabled) + { + m_keyboards[n].enabled = enable; + for (ioport_field &field : m_keyboards[n].keyfields) + field.live().lockout = !enable || ((field.type() == IPT_KEYBOARD) && in_use()); } } @@ -759,10 +827,19 @@ void natural_keyboard::internal_post(char32_t ch) } // 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(); + m_buffer[m_bufend] = ch; + size_t size = m_buffer.size(); + + if ((m_bufend + 1) % size == m_bufbegin) + { + if (size >= KEY_BUFFER_MAX_SIZE) + return; + + m_buffer.insert(m_buffer.begin() + m_bufbegin, KEY_BUFFER_CHUNK_SIZE, INVALID_CHAR); + m_bufbegin += KEY_BUFFER_CHUNK_SIZE; + } + + m_bufend = (m_bufend + 1) % size; } @@ -771,7 +848,7 @@ void natural_keyboard::internal_post(char32_t ch) // when posting a string of characters //------------------------------------------------- -void natural_keyboard::timer(void *ptr, int param) +void natural_keyboard::timer(s32 param) { if (!m_queue_chars.isnull()) { @@ -788,13 +865,15 @@ void natural_keyboard::timer(void *ptr, int param) // the driver does not have a queue_chars handler // loop through this character's component codes - const keycode_map_entry *const code = find_code(m_buffer[m_bufbegin]); + if (!m_fieldnum) + m_current_code = find_code(m_buffer[m_bufbegin]); bool advance; - if (code) + if (m_current_code) { + keycode_map_entry const code(*m_current_code); do { - ioport_field *const field = code->field[m_fieldnum]; + ioport_field *const field = code.field[m_fieldnum]; if (field) { // special handling for toggle fields @@ -804,8 +883,8 @@ void natural_keyboard::timer(void *ptr, int param) field->set_value(!field->digital_value()); } } - while (code->field[m_fieldnum] && (++m_fieldnum < code->field.size()) && m_status_keydown); - advance = (m_fieldnum >= code->field.size()) || !code->field[m_fieldnum]; + while (code.field[m_fieldnum] && (++m_fieldnum < code.field.size()) && m_status_keydown); + advance = (m_fieldnum >= code.field.size()) || !code.field[m_fieldnum]; } else { @@ -875,8 +954,22 @@ std::string natural_keyboard::unicode_to_string(char32_t ch) const const natural_keyboard::keycode_map_entry *natural_keyboard::find_code(char32_t ch) const { - keycode_map::const_iterator const found(m_keycode_map.find(ch)); - return (m_keycode_map.end() != found) ? &found->second : nullptr; + for (kbd_dev_info const &devinfo : m_keyboards) + { + if (devinfo.enabled) + { + keycode_map::const_iterator const found(devinfo.codemap.find(ch)); + if (devinfo.codemap.end() != found) + { + for (keycode_map_entry const &entry : found->second) + { + if (entry.condition.eval()) + return &entry; + } + } + } + } + return nullptr; } @@ -888,23 +981,46 @@ void natural_keyboard::dump(std::ostream &str) const { constexpr size_t left_column_width = 24; - // loop through all codes - bool first(true); - for (auto &code : m_keycode_map) + // loop through all devices + bool firstdev(true); + for (kbd_dev_info const &devinfo : m_keyboards) { - // describe the character code - std::string const description(string_format("%08X (%s) ", code.first, unicode_to_string(code.first))); + if (!firstdev) + str << '\n'; + util::stream_format( + str, + "%s(%s) - %s%s%s %sabled\n", + devinfo.device.get().type().shortname(), + devinfo.device.get().tag(), + devinfo.keyboard ? "keyboard" : "", + (devinfo.keyboard && devinfo.keypad) ? "/" : "", + devinfo.keypad ? "keypad" : "", + devinfo.enabled ? "en" : "dis"); + firstdev = false; + + // loop through all codes + for (auto &code : devinfo.codemap) + { + // describe the character code + std::string const description(string_format("%08X (%s) ", code.first, unicode_to_string(code.first))); - // pad with spaces - util::stream_format(str, "%-*s", left_column_width, description); + // pad with spaces + util::stream_format(str, "%-*s", left_column_width, description); - // identify the keys used - for (std::size_t field = 0; (code.second.field.size() > field) && code.second.field[field]; ++field) - util::stream_format(str, "%s'%s'", first ? "" : ", ", code.second.field[field]->name()); + for (auto &entry : code.second) + { + // identify the keys used + bool firstkey(true); + for (std::size_t field = 0; (entry.field.size() > field) && entry.field[field]; ++field) + { + util::stream_format(str, "%s'%s'", firstkey ? "" : ", ", entry.field[field]->name()); + firstkey = false; + } - // carriage return - str << '\n'; - first = false; + // carriage return + str << '\n'; + } + } } } @@ -934,7 +1050,7 @@ const char_info *char_info::find(char32_t target) { // perform a simple binary search to find the proper alternate int low = 0; - int high = ARRAY_LENGTH(charinfo); + int high = std::size(charinfo); while (high > low) { int middle = (high + low) / 2; @@ -964,7 +1080,7 @@ bool validate_natural_keyboard_statics(void) const char_info *ci; // check to make sure that charinfo is in order - for (i = 0; i < ARRAY_LENGTH(charinfo); i++) + for (i = 0; i < std::size(charinfo); i++) { if (last_char >= charinfo[i].ch) { @@ -975,7 +1091,7 @@ bool validate_natural_keyboard_statics(void) } // check to make sure that I can look up everything on alternate_charmap - for (i = 0; i < ARRAY_LENGTH(charinfo); i++) + for (i = 0; i < std::size(charinfo); i++) { ci = char_info::find(charinfo[i].ch); if (ci != &charinfo[i]) |