ioport_charqueue_empty_delegate m_charqueue_empty; // character queue empty callback // construction/destruction //------------------------------------------------- // natural_keyboard - constructor //------------------------------------------------- public 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 = null; m_fieldnum = 0; m_status_keydown = false; m_last_cr = false; m_timer = null; m_current_rate = attotime.zero; m_queue_chars = null; m_accept_char = null; m_charqueue_empty = null; // try building a list of keycodes; if none are available, don't bother build_codes(); if (!m_keyboards.empty()) { m_buffer.resize(KEY_BUFFER_SIZE); m_timer = machine.scheduler().timer_alloc(timer); } // retrieve option setting set_in_use(machine.options().natural_keyboard()); }
//void internal_post(char32_t ch); //------------------------------------------------- // timer - timer callback to keep things flowing // when posting a string of characters //------------------------------------------------- void timer(object ptr, int param) { if (m_queue_chars != null) { // the driver has a queue_chars handler while (!empty() && m_queue_chars(m_buffer, m_bufbegin, 1) != 0) { m_bufbegin = (m_bufbegin + 1) % (UInt32)m_buffer.size(); if (m_current_rate != attotime.zero) { break; } } } else { // the driver does not have a queue_chars handler // loop through this character's component codes keycode_map_entry code = find_code(m_buffer[(int)m_bufbegin]); bool advance; if (code != null) { do { assert(m_fieldnum < code.field.Length); ioport_field field = code.field[m_fieldnum]; if (field != null) { // special handling for toggle fields if (!field.live().toggle) { field.set_value(!m_status_keydown ? (UInt32)1 : 0); } else if (!m_status_keydown) { field.set_value(!field.digital_value() ? (UInt32)1 : 0); } } }while (code.field[m_fieldnum] != null && (++m_fieldnum < code.field.Length) && m_status_keydown); advance = (m_fieldnum >= code.field.Length) || code.field[m_fieldnum] == null; } else { advance = true; } if (advance) { m_fieldnum = 0; m_status_keydown = !m_status_keydown; // proceed to next character when keydown expires if (!m_status_keydown) { m_bufbegin = (m_bufbegin + 1) % (UInt32)m_buffer.size(); } } } // need to make sure timerproc is called again if buffer not empty if (!empty()) { m_timer.adjust(choose_delay(m_buffer[(int)m_bufbegin])); } }
// posting //void post(char32_t ch); //void post(const char32_t *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 //void dump(std::ostream &str) const; //std::string dump(); // internal helpers //------------------------------------------------- // build_codes - given an input port table, create // an input code table useful for mapping unicode // chars //------------------------------------------------- void build_codes(ioport_manager manager) { // find all shift keys UInt32 mask = 0; ioport_field [] shift = new ioport_field[SHIFT_COUNT]; for (int i = 0; i < shift.Length; i++) { shift[i] = null; //std::fill(std::begin(shift), std::end(shift), nullptr); } foreach (var port in manager.ports()) { foreach (ioport_field field in port.Value.fields()) { if (field.type() == ioport_type.IPT_KEYBOARD) { ListBase <char32_t> codes = field.keyboard_codes(0); foreach (char32_t code in codes) { if ((code >= ioport_global.UCHAR_SHIFT_BEGIN) && (code <= ioport_global.UCHAR_SHIFT_END)) { mask |= 1U << (int)(code - ioport_global.UCHAR_SHIFT_BEGIN); shift[code - ioport_global.UCHAR_SHIFT_BEGIN] = field; } } } } } // iterate over ports and fields foreach (var port in manager.ports()) { foreach (ioport_field field in port.Value.fields()) { if (field.type() == ioport_type.IPT_KEYBOARD) { // iterate over all shift states for (UInt32 curshift = 0; curshift < SHIFT_STATES; ++curshift) { if ((curshift & ~mask) == 0) { // fetch the code, ignoring 0 and shiters ListBase <char32_t> codes = field.keyboard_codes((int)curshift); foreach (char32_t code in codes) { if (((code < ioport_global.UCHAR_SHIFT_BEGIN) || (code > ioport_global.UCHAR_SHIFT_END)) && (code != 0)) { // prefer lowest shift state var found = m_keycode_map.find(code); if ((null == found) || (found.shift > curshift)) { keycode_map_entry newcode = new keycode_map_entry(); //std::fill(std::begin(newcode.field), std::end(newcode.field), nullptr); for (int i = 0; i < newcode.field.Length; i++) { newcode.field[i] = null; } newcode.shift = curshift; UInt32 fieldnum = 0; for (UInt32 i = 0, bits = curshift; (i < SHIFT_COUNT) && bits != 0; ++i, bits >>= 1) { if (BIT(bits, 0) != 0) { newcode.field[fieldnum++] = shift[i]; } } assert(fieldnum < newcode.field.Length); newcode.field[fieldnum] = field; if (null == found) { m_keycode_map.emplace(code, newcode); } else { found = newcode; } if (LOG_NATURAL_KEYBOARD) { machine().logerror("natural_keyboard: code={0} ({1}) port={2} field.name='{3}'\n", // code=%u (%s) port=%p field.name='%s'\n code, unicode_to_string(code), port, field.name()); } } } } } } } } } }
// debugging //void dump(std::ostream &str) const; //std::string dump(); // internal helpers //------------------------------------------------- // build_codes - given an input port table, create // an input code table useful for mapping unicode // chars //------------------------------------------------- void build_codes() { ioport_manager manager = machine().ioport(); // find all the devices with keyboard or keypad inputs foreach (var port in manager.ports()) { var devinfo = std.find_if( m_keyboards, (kbd_dev_info info) => { return(port.second().device() == info.device); }); foreach (ioport_field field in port.second().fields()) { bool is_keyboard = field.type() == ioport_type.IPT_KEYBOARD; if (is_keyboard || (field.type() == ioport_type.IPT_KEYPAD)) { if (default == devinfo) { //devinfo = m_keyboards.emplace(devinfo, port.second->device()); devinfo = new kbd_dev_info(port.second().device()); m_keyboards.push_back(devinfo); } devinfo.keyfields.emplace_back(field); if (is_keyboard) { devinfo.keyboard = true; } else { devinfo.keypad = true; } } } } std.sort( m_keyboards, (kbd_dev_info l, kbd_dev_info r) => { return(std.strcmp(l.device.tag(), r.device.tag())); }); // set up key mappings for each keyboard std.array <ioport_field, size_t_const_SHIFT_COUNT> shift = new std.array <ioport_field, size_t_const_SHIFT_COUNT>(); unsigned mask; bool have_keyboard = false; foreach (kbd_dev_info devinfo in m_keyboards) { if (LOG_NATURAL_KEYBOARD) { machine().logerror("natural_keyboard: building codes for {0}... ({1} fields)\n", devinfo.device.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(shift, (ioport_field)null); //std::fill(std::begin(shift), std::end(shift), nullptr); mask = 0; foreach (ioport_field field in devinfo.keyfields) { if (field.type() == ioport_type.IPT_KEYBOARD) { std.vector <char32_t> codes = field.keyboard_codes(0); foreach (char32_t code in codes) { if ((code >= UCHAR_SHIFT_BEGIN) && (code <= UCHAR_SHIFT_END)) { mask |= 1U << (int)(code - UCHAR_SHIFT_BEGIN); shift[code - UCHAR_SHIFT_BEGIN] = field; if (LOG_NATURAL_KEYBOARD) { machine().logerror("natural_keyboard: UCHAR_SHIFT_{0} found\n", code - UCHAR_SHIFT_BEGIN + 1); } } } } } // iterate over keyboard/keypad fields foreach (ioport_field field in devinfo.keyfields) { field.live().lockout = !devinfo.enabled; if (field.type() == ioport_type.IPT_KEYBOARD) { // iterate over all shift states for (unsigned curshift = 0; curshift < SHIFT_STATES; ++curshift) { if ((curshift & ~mask) == 0) { // fetch the code, ignoring 0 and shifters std.vector <char32_t> codes = field.keyboard_codes((int)curshift); foreach (char32_t code in codes) { if (((code < UCHAR_SHIFT_BEGIN) || (code > UCHAR_SHIFT_END)) && (code != 0)) { m_have_charkeys = true; var found = devinfo.codemap.find(code); //keycode_map::iterator const found(devinfo.codemap.find(code)); keycode_map_entry newcode = new keycode_map_entry(); std.fill(newcode.field, (ioport_field)null); //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 != 0); ++i, bits >>= 1) { if (BIT(bits, 0) != 0) { newcode.field[fieldnum++] = shift[i]; } } newcode.field[fieldnum] = field; if (default == found) { natural_keyboard_keycode_map_entries entries = new natural_keyboard_keycode_map_entries(); entries.emplace_back(newcode); devinfo.codemap.emplace(code, entries); } else { found.emplace_back(newcode); } if (LOG_NATURAL_KEYBOARD) { machine().logerror("natural_keyboard: code={0} ({1}) port={2} field.name='{3}'\n", code, unicode_to_string(code), field.port(), field.name()); } } } } } } } // sort mapping entries by shift state foreach (var mapping in devinfo.codemap) { std.sort( mapping.second(), (keycode_map_entry x, keycode_map_entry y) => { return(x.shift.CompareTo(y.shift)); }); //[] (keycode_map_entry const &x, keycode_map_entry const &y) { return x.shift < y.shift; }); } }