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:
parent
df76d57c47
commit
f08d500fd6
@ -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.
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -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"
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user