mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-22 12:33:29 +00:00
Attempts to factor out the latest keyboard logic and hook it in from SDL also.
This commit is contained in:
parent
df76d57c47
commit
f08d500fd6
@ -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.
|
||||
|
@ -9,6 +9,7 @@
|
||||
#ifndef KeyboardMachine_h
|
||||
#define KeyboardMachine_h
|
||||
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <set>
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -67,7 +67,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
enableASanStackUseAfterReturn = "YES"
|
||||
|
@ -488,55 +488,19 @@ struct ActivityObserver: public Activity::Observer {
|
||||
}
|
||||
#undef BIND
|
||||
|
||||
Inputs::Keyboard &keyboard = keyboard_machine->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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<uint32_t, KeyPress> keypresses;
|
||||
|
||||
@ -797,6 +797,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// Grab the machine lock and process all pending events.
|
||||
std::lock_guard<std::mutex> 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) {
|
||||
|
Loading…
Reference in New Issue
Block a user