From df76d57c4752b9771f78a1b62a9d57341720bc1f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Mar 2020 21:30:30 -0500 Subject: [PATCH 1/6] Experimentally attempts to tie together input and keypresses by timestamp. --- OSBindings/SDL/main.cpp | 75 +++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 2ffbeb13e..cd4e1fc6a 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -611,10 +612,8 @@ int main(int argc, char *argv[]) { } // Check whether a 'logical' keyboard has been requested. - constexpr bool logical_keyboard = false; //arguments.selections.find("logical-keyboard") != arguments.selections.end(); - /* Logical keyboard entry is currently disabled; the attempt below to get logical input via SDL_GetKeyName is - too flawed — all letters are always capitals, shifted symbols are reported correctly on their first - press only, etc. I need to see whether other options are available. If not then SDL may not allow this feature.*/ + const bool logical_keyboard = arguments.selections.find("logical-keyboard") != arguments.selections.end(); + SDL_StartTextInput(); // Wire up the best-effort updater, its delegate, and the speaker delegate. machine_runner.machine = machine.get(); @@ -767,6 +766,15 @@ int main(int argc, char *argv[]) { activity_observer = std::make_unique(activity_source, 4.0f / 3.0f); } + // SDL 2.x delivers key up/down events and text inputs separately even when they're correlated; + // this struct and map is used to correlate them by time. + struct KeyPress { + bool is_down = true; + std::string input; + SDL_Keycode keycode = SDLK_UNKNOWN; + }; + std::unordered_map keypresses; + // Run the main event loop until the OS tells us to quit. const bool uses_mouse = !!machine->mouse_machine(); bool should_quit = false; @@ -813,6 +821,13 @@ int main(int argc, char *argv[]) { machine->media_target()->insert_media(media); } break; + case SDL_TEXTINPUT: { + const auto keyboard_machine = machine->keyboard_machine(); + if(keyboard_machine) { + keypresses[event.text.timestamp].input = event.text.text; + } + } break; + case SDL_KEYDOWN: case SDL_KEYUP: { const auto keyboard_machine = machine->keyboard_machine(); @@ -896,27 +911,8 @@ int main(int argc, char *argv[]) { const bool is_pressed = event.type == SDL_KEYDOWN; if(keyboard_machine) { - // Grab the key's symbol. - char key_value = '\0'; - const char *key_name = SDL_GetKeyName(event.key.keysym.sym); - if(key_name[0] >= 0 && key_name[1] == 0) key_value = key_name[0]; - - // If a logical mapping was selected and a symbol was found, type it. - if(logical_keyboard && key_value != '\0' && keyboard_machine->can_type(key_value)) { - if(is_pressed) { - char string[] = { key_value, 0 }; - keyboard_machine->type_string(string); - } - break; - } - - // Otherwise, supply as a normal keypress. - Inputs::Keyboard::Key key = Inputs::Keyboard::Key::Space; - if(!KeyboardKeyForSDLScancode(event.key.keysym.scancode, key)) break; - if(keyboard_machine->get_keyboard().observed_keys().find(key) != keyboard_machine->get_keyboard().observed_keys().end()) { - keyboard_machine->get_keyboard().set_key_pressed(key, key_value, is_pressed); - break; - } + keypresses[event.text.timestamp].keycode = event.key.keysym.sym; + keypresses[event.text.timestamp].is_down = is_pressed; } JoystickMachine::Machine *const joystick_machine = machine->joystick_machine(); @@ -969,6 +965,35 @@ int main(int argc, char *argv[]) { } } + // Handle accumulated key states. + for (const auto &keypress: keypresses) { + printf("Key: %d %d %s\n", keypress.second.keycode, keypress.second.is_down, keypress.second.input.c_str()); + } + keypresses.clear(); +// printf("Key down: %d", event.text.timestamp); +// +// // Grab the key's symbol. +// char key_value = '\0'; +// const char *key_name = SDL_GetKeyName(event.key.keysym.sym); +// if(key_name[0] >= 0 && key_name[1] == 0) key_value = key_name[0]; +// +// // If a logical mapping was selected and a symbol was found, type it. +// if(logical_keyboard && key_value != '\0' && keyboard_machine->can_type(key_value)) { +// if(is_pressed) { +// char string[] = { key_value, 0 }; +// keyboard_machine->type_string(string); +// } +// break; +// } +// +// // Otherwise, supply as a normal keypress. +// Inputs::Keyboard::Key key = Inputs::Keyboard::Key::Space; +// if(!KeyboardKeyForSDLScancode(event.key.keysym.scancode, key)) break; +// if(keyboard_machine->get_keyboard().observed_keys().find(key) != keyboard_machine->get_keyboard().observed_keys().end()) { +// keyboard_machine->get_keyboard().set_key_pressed(key, key_value, is_pressed); +// break; +// } + // Push new joystick state, if any. JoystickMachine::Machine *const joystick_machine = machine->joystick_machine(); if(joystick_machine) { From f08d500fd60571f638a98fbd04f9ee2edd33a20e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Mar 2020 22:58:15 -0500 Subject: [PATCH 2/6] Attempts to factor out the latest keyboard logic and hook it in from SDL also. --- Inputs/Keyboard.hpp | 4 +- Machines/KeyboardMachine.hpp | 47 ++++++++ .../xcschemes/Clock Signal.xcscheme | 2 +- .../Mac/Clock Signal/Machine/CSMachine.mm | 56 ++-------- OSBindings/SDL/main.cpp | 101 +++++++----------- 5 files changed, 102 insertions(+), 108 deletions(-) diff --git a/Inputs/Keyboard.hpp b/Inputs/Keyboard.hpp index 3011ed826..30bd84191 100644 --- a/Inputs/Keyboard.hpp +++ b/Inputs/Keyboard.hpp @@ -35,7 +35,9 @@ class Keyboard { Keypad4, Keypad5, Keypad6, KeypadMinus, Keypad1, Keypad2, Keypad3, KeypadEnter, Keypad0, KeypadDecimalPoint, KeypadEquals, - Help + Help, + + Max = Help }; /// Constructs a Keyboard that declares itself to observe all keys. diff --git a/Machines/KeyboardMachine.hpp b/Machines/KeyboardMachine.hpp index a84f53fb0..02dcaff74 100644 --- a/Machines/KeyboardMachine.hpp +++ b/Machines/KeyboardMachine.hpp @@ -9,6 +9,7 @@ #ifndef KeyboardMachine_h #define KeyboardMachine_h +#include #include #include #include @@ -54,6 +55,52 @@ class Machine: public KeyActions { Provides a destination for keyboard input. */ virtual Inputs::Keyboard &get_keyboard() = 0; + + /*! + Provides a standard bundle of logic for hosts that are able to correlate typed symbols + with keypresses. Specifically: + + If map_logically is false: + + (i) initially try to set @c key as @c is_pressed; + (ii) if this machine doesn't map @c key to anything but @c symbol is a printable ASCII character, attempt to @c type_string it. + + If map_logically is true: + + (i) if @c symbol can be typed and this is a key down, @c type_string it; + (ii) if @c symbol cannot be typed, set @c key as @c is_pressed + */ + bool apply_key(Inputs::Keyboard::Key key, char symbol, bool is_pressed, bool map_logically) { + Inputs::Keyboard &keyboard = get_keyboard(); + + if(!map_logically) { + // Try a regular keypress first, and stop if that works. + if(keyboard.set_key_pressed(key, symbol, is_pressed)) { + return true; + } + + // That having failed, if a symbol has been supplied then try typing it. + if(symbol && can_type(symbol)) { + char string[2] = { symbol, 0 }; + type_string(string); + return true; + } + + return false; + } else { + // Try to type first. + if(is_pressed && symbol && can_type(symbol)) { + char string[2] = { symbol, 0 }; + type_string(string); + return true; + } + + // That didn't work. Forward as a keypress. As, either: + // (i) this is a key down, but doesn't have a symbol, or is an untypeable symbol; or + // (ii) this is a key up, which it won't be an issue to miscommunicate. + return keyboard.set_key_pressed(key, symbol, is_pressed); + } + } }; /*! diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 7f370216e..f049730f4 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -67,7 +67,7 @@ get_keyboard(); - - if(keyboard.observed_keys().find(mapped_key) != keyboard.observed_keys().end()) { - // Don't pass anything on if this is not new information. - if(_depressedKeys[key] == !!isPressed) return; - _depressedKeys[key] = !!isPressed; - - // Pick an ASCII code, if any. - char pressedKey = '\0'; - if(characters.length) { - unichar firstCharacter = [characters characterAtIndex:0]; - if(firstCharacter < 128) { - pressedKey = (char)firstCharacter; - } + // Pick an ASCII code, if any. + char pressedKey = '\0'; + if(characters.length) { + unichar firstCharacter = [characters characterAtIndex:0]; + if(firstCharacter < 128) { + pressedKey = (char)firstCharacter; } + } - // Decide whether to try to 'type' (in the logical mapping sense) in the first instance. - bool shouldTryToType = self.inputMode == CSMachineKeyboardInputModeKeyboardLogical; - - // Even if the default wasn't to try to type, have a go anyway if the key wasn't - // recognised directly. E.g. if the user hits their square bracket key on a machine that - // doesn't have a correspondingly-placed key, then try to type a square bracket. - if(!shouldTryToType) { - @synchronized(self) { - shouldTryToType = !keyboard.set_key_pressed(mapped_key, pressedKey, isPressed); - } + @synchronized(self) { + if(keyboard_machine->apply_key(mapped_key, pressedKey, isPressed, self.inputMode == CSMachineKeyboardInputModeKeyboardLogical)) { + return; } - - // If this should try to type, give that a go. But typing may fail, e.g. because the user - // has pressed something like the cursor keys, which don't actually map to a typeable symbol. - if(shouldTryToType) { - @synchronized(self) { - if(pressedKey && keyboard_machine->can_type(pressedKey)) { - if(isPressed) { - char string[2] = { pressedKey, 0 }; - keyboard_machine->type_string(string); - } - return; - } - } - - // Okay, so at this point either: set_key_pressed was already tried but will fail anyway, - // or else it hasn't been tried yet and is worth a go. - @synchronized(self) { - shouldTryToType = !keyboard.set_key_pressed(mapped_key, pressedKey, isPressed); - } - } - - return; } } diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index cd4e1fc6a..0f271ef8a 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -295,7 +295,7 @@ class ActivityObserver: public Activity::Observer { std::mutex mutex; }; -bool KeyboardKeyForSDLScancode(SDL_Keycode scancode, Inputs::Keyboard::Key &key) { +bool KeyboardKeyForSDLScancode(SDL_Scancode scancode, Inputs::Keyboard::Key &key) { #define BIND(x, y) case SDL_SCANCODE_##x: key = Inputs::Keyboard::Key::y; break; switch(scancode) { default: return false; @@ -771,7 +771,7 @@ int main(int argc, char *argv[]) { struct KeyPress { bool is_down = true; std::string input; - SDL_Keycode keycode = SDLK_UNKNOWN; + SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; }; std::unordered_map keypresses; @@ -797,6 +797,7 @@ int main(int argc, char *argv[]) { // Grab the machine lock and process all pending events. std::lock_guard lock_guard(machine_mutex); + const auto keyboard_machine = machine->keyboard_machine(); SDL_Event event; while(SDL_PollEvent(&event)) { switch(event.type) { @@ -821,17 +822,12 @@ int main(int argc, char *argv[]) { machine->media_target()->insert_media(media); } break; - case SDL_TEXTINPUT: { - const auto keyboard_machine = machine->keyboard_machine(); - if(keyboard_machine) { - keypresses[event.text.timestamp].input = event.text.text; - } - } break; + case SDL_TEXTINPUT: + keypresses[event.text.timestamp].input = event.text.text; + break; case SDL_KEYDOWN: case SDL_KEYUP: { - const auto keyboard_machine = machine->keyboard_machine(); - if(event.type == SDL_KEYDOWN) { // Syphon off the key-press if it's control+shift+V (paste). if(event.key.keysym.sym == SDLK_v && (SDL_GetModState()&KMOD_CTRL) && (SDL_GetModState()&KMOD_SHIFT)) { @@ -909,31 +905,8 @@ int main(int argc, char *argv[]) { } const bool is_pressed = event.type == SDL_KEYDOWN; - - if(keyboard_machine) { - keypresses[event.text.timestamp].keycode = event.key.keysym.sym; - keypresses[event.text.timestamp].is_down = is_pressed; - } - - JoystickMachine::Machine *const joystick_machine = machine->joystick_machine(); - if(joystick_machine) { - auto &joysticks = joystick_machine->get_joysticks(); - if(!joysticks.empty()) { - switch(event.key.keysym.scancode) { - case SDL_SCANCODE_LEFT: joysticks[0]->set_input(Inputs::Joystick::Input::Left, is_pressed); break; - case SDL_SCANCODE_RIGHT: joysticks[0]->set_input(Inputs::Joystick::Input::Right, is_pressed); break; - case SDL_SCANCODE_UP: joysticks[0]->set_input(Inputs::Joystick::Input::Up, is_pressed); break; - case SDL_SCANCODE_DOWN: joysticks[0]->set_input(Inputs::Joystick::Input::Down, is_pressed); break; - case SDL_SCANCODE_SPACE: joysticks[0]->set_input(Inputs::Joystick::Input::Fire, is_pressed); break; - case SDL_SCANCODE_A: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 0), is_pressed); break; - case SDL_SCANCODE_S: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 1), is_pressed); break; - default: { - const char *key_name = SDL_GetKeyName(event.key.keysym.sym); - joysticks[0]->set_input(Inputs::Joystick::Input(key_name[0]), is_pressed); - } break; - } - } - } + keypresses[event.text.timestamp].scancode = event.key.keysym.scancode; + keypresses[event.text.timestamp].is_down = is_pressed; } break; case SDL_MOUSEBUTTONDOWN: @@ -966,36 +939,44 @@ int main(int argc, char *argv[]) { } // Handle accumulated key states. + JoystickMachine::Machine *const joystick_machine = machine->joystick_machine(); for (const auto &keypress: keypresses) { - printf("Key: %d %d %s\n", keypress.second.keycode, keypress.second.is_down, keypress.second.input.c_str()); + // Try to set this key on the keyboard first, if there is one. + if(keyboard_machine) { + Inputs::Keyboard::Key key = Inputs::Keyboard::Key::Space; + if( KeyboardKeyForSDLScancode(keypress.second.scancode, key) && + keyboard_machine->apply_key(key, keypress.second.input.size() ? keypress.second.input[0] : 0, keypress.second.is_down, logical_keyboard)) { + continue; + } + } + + // Having failed that, try converting it into a joystick action. + if(joystick_machine) { + auto &joysticks = joystick_machine->get_joysticks(); + if(!joysticks.empty()) { + const bool is_pressed = keypress.second.is_down; + switch(keypress.second.scancode) { + case SDL_SCANCODE_LEFT: joysticks[0]->set_input(Inputs::Joystick::Input::Left, is_pressed); break; + case SDL_SCANCODE_RIGHT: joysticks[0]->set_input(Inputs::Joystick::Input::Right, is_pressed); break; + case SDL_SCANCODE_UP: joysticks[0]->set_input(Inputs::Joystick::Input::Up, is_pressed); break; + case SDL_SCANCODE_DOWN: joysticks[0]->set_input(Inputs::Joystick::Input::Down, is_pressed); break; + case SDL_SCANCODE_SPACE: joysticks[0]->set_input(Inputs::Joystick::Input::Fire, is_pressed); break; + case SDL_SCANCODE_A: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 0), is_pressed); break; + case SDL_SCANCODE_S: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 1), is_pressed); break; + case SDL_SCANCODE_D: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 2), is_pressed); break; + case SDL_SCANCODE_F: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 3), is_pressed); break; + default: { + if(keypress.second.input.size()) { + joysticks[0]->set_input(Inputs::Joystick::Input(keypress.second.input[0]), is_pressed); + } + } break; + } + } + } } keypresses.clear(); -// printf("Key down: %d", event.text.timestamp); -// -// // Grab the key's symbol. -// char key_value = '\0'; -// const char *key_name = SDL_GetKeyName(event.key.keysym.sym); -// if(key_name[0] >= 0 && key_name[1] == 0) key_value = key_name[0]; -// -// // If a logical mapping was selected and a symbol was found, type it. -// if(logical_keyboard && key_value != '\0' && keyboard_machine->can_type(key_value)) { -// if(is_pressed) { -// char string[] = { key_value, 0 }; -// keyboard_machine->type_string(string); -// } -// break; -// } -// -// // Otherwise, supply as a normal keypress. -// Inputs::Keyboard::Key key = Inputs::Keyboard::Key::Space; -// if(!KeyboardKeyForSDLScancode(event.key.keysym.scancode, key)) break; -// if(keyboard_machine->get_keyboard().observed_keys().find(key) != keyboard_machine->get_keyboard().observed_keys().end()) { -// keyboard_machine->get_keyboard().set_key_pressed(key, key_value, is_pressed); -// break; -// } // Push new joystick state, if any. - JoystickMachine::Machine *const joystick_machine = machine->joystick_machine(); if(joystick_machine) { auto &machine_joysticks = joystick_machine->get_joysticks(); for(size_t c = 0; c < joysticks.size(); ++c) { From a0311858f9535e2ed0468305d09815dc5a3d4afd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Mar 2020 23:04:10 -0500 Subject: [PATCH 3/6] Adds mappings for curly brackets. --- Machines/AmstradCPC/Keyboard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Machines/AmstradCPC/Keyboard.cpp b/Machines/AmstradCPC/Keyboard.cpp index 8850f52ed..5f00ea55b 100644 --- a/Machines/AmstradCPC/Keyboard.cpp +++ b/Machines/AmstradCPC/Keyboard.cpp @@ -143,8 +143,8 @@ uint16_t *CharacterMapper::sequence_for_character(char character) { /* t */ KEYS(KeyT), /* u */ KEYS(KeyU), /* v */ KEYS(KeyV), /* w */ KEYS(KeyW), /* x */ KEYS(KeyX), /* y */ KEYS(KeyY), - /* z */ KEYS(KeyZ), /* { */ X, - /* | */ SHIFT(KeyAt), /* } */ X, + /* z */ KEYS(KeyZ), /* { */ SHIFT(KeyLeftSquareBracket), + /* | */ SHIFT(KeyAt), /* } */ SHIFT(KeyRightSquareBracket), /* ~ */ X }; #undef KEYS From 3758ec79ac21bbf21c5f8718dbeec6d8e45feb87 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Mar 2020 23:09:22 -0500 Subject: [PATCH 4/6] Adds potential test argument. --- .../xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 4cd4a9880..41c946476 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -56,17 +56,21 @@ argument = "/Users/thomasharte/Downloads/test-dsk-for-rw-and-50-60-hz/TEST-RW-60Hz.DSK" isEnabled = "NO"> + + + isEnabled = "NO"> + isEnabled = "NO"> Date: Thu, 5 Mar 2020 21:01:30 -0500 Subject: [PATCH 5/6] Adds virtual keys for F1, F2, etc. --- Machines/Electron/Electron.cpp | 37 +++++++++++++++++++++++++++------- Machines/Electron/Keyboard.cpp | 12 +++++++++++ Machines/Electron/Keyboard.hpp | 3 +++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index a9f91d1c0..ba58a5f76 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -115,13 +115,36 @@ class ConcreteMachine: } void set_key_state(uint16_t key, bool isPressed) final { - if(key == KeyBreak) { - m6502_.set_reset_line(isPressed); - } else { - if(isPressed) - key_states_[key >> 4] |= key&0xf; - else - key_states_[key >> 4] &= ~(key&0xf); + switch(key) { + default: + if(isPressed) + key_states_[key >> 4] |= key&0xf; + else + key_states_[key >> 4] &= ~(key&0xf); + break; + + case KeyBreak: + m6502_.set_reset_line(isPressed); + break; + +#define ShiftedKey(source, dest) \ + case source: \ + set_key_state(KeyShift, isPressed); \ + set_key_state(dest, isPressed); \ + break; + + ShiftedKey(KeyF1, Key1); + ShiftedKey(KeyF2, Key2); + ShiftedKey(KeyF3, Key3); + ShiftedKey(KeyF4, Key4); + ShiftedKey(KeyF5, Key5); + ShiftedKey(KeyF6, Key6); + ShiftedKey(KeyF7, Key7); + ShiftedKey(KeyF8, Key8); + ShiftedKey(KeyF9, Key9); + ShiftedKey(KeyF0, Key0); + +#undef ShiftedKey } } diff --git a/Machines/Electron/Keyboard.cpp b/Machines/Electron/Keyboard.cpp index 3ed607ead..26611d81b 100644 --- a/Machines/Electron/Keyboard.cpp +++ b/Machines/Electron/Keyboard.cpp @@ -52,6 +52,18 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(KeypadMinus, KeyMinus); BIND(KeypadPlus, KeyColon); BIND(Space, KeySpace); + + // Virtual mappings. + BIND(F1, KeyF1); + BIND(F2, KeyF2); + BIND(F3, KeyF3); + BIND(F4, KeyF4); + BIND(F5, KeyF5); + BIND(F6, KeyF6); + BIND(F7, KeyF7); + BIND(F8, KeyF8); + BIND(F9, KeyF9); + BIND(F10, KeyF0); } #undef BIND return KeyboardMachine::MappedMachine::KeyNotMapped; diff --git a/Machines/Electron/Keyboard.hpp b/Machines/Electron/Keyboard.hpp index 39c36d3ac..38a5baa4f 100644 --- a/Machines/Electron/Keyboard.hpp +++ b/Machines/Electron/Keyboard.hpp @@ -30,6 +30,9 @@ enum Key: uint16_t { KeyZ = 0x00c0 | 0x08, KeyA = 0x00c0 | 0x04, KeyQ = 0x00c0 | 0x02, Key1 = 0x00c0 | 0x01, KeyShift = 0x00d0 | 0x08, KeyControl = 0x00d0 | 0x04, KeyFunc = 0x00d0 | 0x02, KeyEscape = 0x00d0 | 0x01, + // Virtual keys. + KeyF1 = 0xfff0, KeyF2, KeyF3, KeyF4, KeyF5, KeyF6, KeyF7, KeyF8, KeyF9, KeyF0, + KeyBreak = 0xfffd, }; From 58b8dfb9299696e17d8f18e08fbb8b2fe65f6114 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 5 Mar 2020 21:56:26 -0500 Subject: [PATCH 6/6] Attempts to improve SDL key merging. --- Machines/KeyboardMachine.hpp | 2 +- .../xcschemes/Clock Signal Kiosk.xcscheme | 8 +-- OSBindings/SDL/main.cpp | 57 +++++++++++++++---- 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/Machines/KeyboardMachine.hpp b/Machines/KeyboardMachine.hpp index 02dcaff74..c5ee1f0e1 100644 --- a/Machines/KeyboardMachine.hpp +++ b/Machines/KeyboardMachine.hpp @@ -80,7 +80,7 @@ class Machine: public KeyActions { } // That having failed, if a symbol has been supplied then try typing it. - if(symbol && can_type(symbol)) { + if(is_pressed && symbol && can_type(symbol)) { char string[2] = { symbol, 0 }; type_string(string); return true; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 41c946476..073c568e9 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -31,7 +31,7 @@ + isEnabled = "NO"> + isEnabled = "YES"> + isEnabled = "YES"> #include #include -#include +#include #include @@ -773,7 +773,7 @@ int main(int argc, char *argv[]) { std::string input; SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; }; - std::unordered_map keypresses; + std::map keypresses; // Run the main event loop until the OS tells us to quit. const bool uses_mouse = !!machine->mouse_machine(); @@ -904,9 +904,8 @@ int main(int argc, char *argv[]) { break; } - const bool is_pressed = event.type == SDL_KEYDOWN; keypresses[event.text.timestamp].scancode = event.key.keysym.scancode; - keypresses[event.text.timestamp].is_down = is_pressed; + keypresses[event.text.timestamp].is_down = event.type == SDL_KEYDOWN; } break; case SDL_MOUSEBUTTONDOWN: @@ -938,14 +937,50 @@ int main(int argc, char *argv[]) { } } + // Look for potential keypress merges; SDL doesn't in any capacity guarantee that keypresses that produce + // symbols will be delivered with the same timestamp. So look for any pairs of recorded kepresses that are + // close together temporally and otherwise seem to match. + std::vector matched_keypresses; + if(keypresses.size()) { + auto next_keypress = keypresses.begin(); + + while(next_keypress != keypresses.end()) { + auto keypress = next_keypress; + ++next_keypress; + + // If the two appear to pair off, push a combination and advance twice. + // Otherwise, keep just the first and advance once. + if( + next_keypress != keypresses.end() && + keypress->first >= next_keypress->first - 5 && + keypress->second.is_down && next_keypress->second.is_down && + !keypress->second.input.size() != !next_keypress->second.input.size() && + (keypress->second.scancode != SDL_SCANCODE_UNKNOWN) != (next_keypress->second.scancode != SDL_SCANCODE_UNKNOWN)) { + + KeyPress combined_keypress; + + if(keypress->second.scancode != SDL_SCANCODE_UNKNOWN) { + combined_keypress.scancode = keypress->second.scancode; + combined_keypress.input = std::move(next_keypress->second.input); + } else { + combined_keypress.scancode = next_keypress->second.scancode; + combined_keypress.input = std::move(keypress->second.input); + }; + ++next_keypress; + } else { + matched_keypresses.push_back(keypress->second); + } + } + } + // Handle accumulated key states. JoystickMachine::Machine *const joystick_machine = machine->joystick_machine(); - for (const auto &keypress: keypresses) { + for (const auto &keypress: matched_keypresses) { // Try to set this key on the keyboard first, if there is one. if(keyboard_machine) { Inputs::Keyboard::Key key = Inputs::Keyboard::Key::Space; - if( KeyboardKeyForSDLScancode(keypress.second.scancode, key) && - keyboard_machine->apply_key(key, keypress.second.input.size() ? keypress.second.input[0] : 0, keypress.second.is_down, logical_keyboard)) { + if( KeyboardKeyForSDLScancode(keypress.scancode, key) && + keyboard_machine->apply_key(key, keypress.input.size() ? keypress.input[0] : 0, keypress.is_down, logical_keyboard)) { continue; } } @@ -954,8 +989,8 @@ int main(int argc, char *argv[]) { if(joystick_machine) { auto &joysticks = joystick_machine->get_joysticks(); if(!joysticks.empty()) { - const bool is_pressed = keypress.second.is_down; - switch(keypress.second.scancode) { + const bool is_pressed = keypress.is_down; + switch(keypress.scancode) { case SDL_SCANCODE_LEFT: joysticks[0]->set_input(Inputs::Joystick::Input::Left, is_pressed); break; case SDL_SCANCODE_RIGHT: joysticks[0]->set_input(Inputs::Joystick::Input::Right, is_pressed); break; case SDL_SCANCODE_UP: joysticks[0]->set_input(Inputs::Joystick::Input::Up, is_pressed); break; @@ -966,8 +1001,8 @@ int main(int argc, char *argv[]) { case SDL_SCANCODE_D: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 2), is_pressed); break; case SDL_SCANCODE_F: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 3), is_pressed); break; default: { - if(keypress.second.input.size()) { - joysticks[0]->set_input(Inputs::Joystick::Input(keypress.second.input[0]), is_pressed); + if(keypress.input.size()) { + joysticks[0]->set_input(Inputs::Joystick::Input(keypress.input[0]), is_pressed); } } break; }