1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +00:00

Attempts to factor out the latest keyboard logic and hook it in from SDL also.

This commit is contained in:
Thomas Harte 2020-03-03 22:58:15 -05:00
parent df76d57c47
commit f08d500fd6
5 changed files with 102 additions and 108 deletions

View File

@ -35,7 +35,9 @@ class Keyboard {
Keypad4, Keypad5, Keypad6, KeypadMinus, Keypad4, Keypad5, Keypad6, KeypadMinus,
Keypad1, Keypad2, Keypad3, KeypadEnter, Keypad1, Keypad2, Keypad3, KeypadEnter,
Keypad0, KeypadDecimalPoint, KeypadEquals, Keypad0, KeypadDecimalPoint, KeypadEquals,
Help Help,
Max = Help
}; };
/// Constructs a Keyboard that declares itself to observe all keys. /// Constructs a Keyboard that declares itself to observe all keys.

View File

@ -9,6 +9,7 @@
#ifndef KeyboardMachine_h #ifndef KeyboardMachine_h
#define KeyboardMachine_h #define KeyboardMachine_h
#include <bitset>
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <set> #include <set>
@ -54,6 +55,52 @@ class Machine: public KeyActions {
Provides a destination for keyboard input. Provides a destination for keyboard input.
*/ */
virtual Inputs::Keyboard &get_keyboard() = 0; 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);
}
}
}; };
/*! /*!

View File

@ -67,7 +67,7 @@
</Testables> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Release" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableASanStackUseAfterReturn = "YES" enableASanStackUseAfterReturn = "YES"

View File

@ -488,55 +488,19 @@ struct ActivityObserver: public Activity::Observer {
} }
#undef BIND #undef BIND
Inputs::Keyboard &keyboard = keyboard_machine->get_keyboard(); // Pick an ASCII code, if any.
char pressedKey = '\0';
if(keyboard.observed_keys().find(mapped_key) != keyboard.observed_keys().end()) { if(characters.length) {
// Don't pass anything on if this is not new information. unichar firstCharacter = [characters characterAtIndex:0];
if(_depressedKeys[key] == !!isPressed) return; if(firstCharacter < 128) {
_depressedKeys[key] = !!isPressed; 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. @synchronized(self) {
bool shouldTryToType = self.inputMode == CSMachineKeyboardInputModeKeyboardLogical; if(keyboard_machine->apply_key(mapped_key, pressedKey, isPressed, self.inputMode == CSMachineKeyboardInputModeKeyboardLogical)) {
return;
// 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);
}
} }
// 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;
} }
} }

View File

@ -295,7 +295,7 @@ class ActivityObserver: public Activity::Observer {
std::mutex mutex; 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; #define BIND(x, y) case SDL_SCANCODE_##x: key = Inputs::Keyboard::Key::y; break;
switch(scancode) { switch(scancode) {
default: return false; default: return false;
@ -771,7 +771,7 @@ int main(int argc, char *argv[]) {
struct KeyPress { struct KeyPress {
bool is_down = true; bool is_down = true;
std::string input; std::string input;
SDL_Keycode keycode = SDLK_UNKNOWN; SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
}; };
std::unordered_map<uint32_t, KeyPress> keypresses; std::unordered_map<uint32_t, KeyPress> keypresses;
@ -797,6 +797,7 @@ int main(int argc, char *argv[]) {
// Grab the machine lock and process all pending events. // Grab the machine lock and process all pending events.
std::lock_guard<std::mutex> lock_guard(machine_mutex); std::lock_guard<std::mutex> lock_guard(machine_mutex);
const auto keyboard_machine = machine->keyboard_machine();
SDL_Event event; SDL_Event event;
while(SDL_PollEvent(&event)) { while(SDL_PollEvent(&event)) {
switch(event.type) { switch(event.type) {
@ -821,17 +822,12 @@ int main(int argc, char *argv[]) {
machine->media_target()->insert_media(media); machine->media_target()->insert_media(media);
} break; } break;
case SDL_TEXTINPUT: { case SDL_TEXTINPUT:
const auto keyboard_machine = machine->keyboard_machine(); keypresses[event.text.timestamp].input = event.text.text;
if(keyboard_machine) { break;
keypresses[event.text.timestamp].input = event.text.text;
}
} break;
case SDL_KEYDOWN: case SDL_KEYDOWN:
case SDL_KEYUP: { case SDL_KEYUP: {
const auto keyboard_machine = machine->keyboard_machine();
if(event.type == SDL_KEYDOWN) { if(event.type == SDL_KEYDOWN) {
// Syphon off the key-press if it's control+shift+V (paste). // 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)) { 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; const bool is_pressed = event.type == SDL_KEYDOWN;
keypresses[event.text.timestamp].scancode = event.key.keysym.scancode;
if(keyboard_machine) { keypresses[event.text.timestamp].is_down = is_pressed;
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;
}
}
}
} break; } break;
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
@ -966,36 +939,44 @@ int main(int argc, char *argv[]) {
} }
// Handle accumulated key states. // Handle accumulated key states.
JoystickMachine::Machine *const joystick_machine = machine->joystick_machine();
for (const auto &keypress: keypresses) { 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(); 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. // Push new joystick state, if any.
JoystickMachine::Machine *const joystick_machine = machine->joystick_machine();
if(joystick_machine) { if(joystick_machine) {
auto &machine_joysticks = joystick_machine->get_joysticks(); auto &machine_joysticks = joystick_machine->get_joysticks();
for(size_t c = 0; c < joysticks.size(); ++c) { for(size_t c = 0; c < joysticks.size(); ++c) {