mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-15 14:27:29 +00:00
Merge pull request #764 from TomHarte/ZX80Cursors
Adds better guesses for unmapped physical keys.
This commit is contained in:
@@ -56,10 +56,12 @@ MultiKeyboardMachine::MultiKeyboard::MultiKeyboard(const std::vector<::KeyboardM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiKeyboardMachine::MultiKeyboard::set_key_pressed(Key key, char value, bool is_pressed) {
|
bool MultiKeyboardMachine::MultiKeyboard::set_key_pressed(Key key, char value, bool is_pressed) {
|
||||||
|
bool was_consumed = false;
|
||||||
for(const auto &machine: machines_) {
|
for(const auto &machine: machines_) {
|
||||||
machine->get_keyboard().set_key_pressed(key, value, is_pressed);
|
was_consumed |= machine->get_keyboard().set_key_pressed(key, value, is_pressed);
|
||||||
}
|
}
|
||||||
|
return was_consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiKeyboardMachine::MultiKeyboard::reset_all_keys() {
|
void MultiKeyboardMachine::MultiKeyboard::reset_all_keys() {
|
||||||
|
@@ -32,7 +32,7 @@ class MultiKeyboardMachine: public KeyboardMachine::Machine {
|
|||||||
public:
|
public:
|
||||||
MultiKeyboard(const std::vector<::KeyboardMachine::Machine *> &machines);
|
MultiKeyboard(const std::vector<::KeyboardMachine::Machine *> &machines);
|
||||||
|
|
||||||
void set_key_pressed(Key key, char value, bool is_pressed) final;
|
bool set_key_pressed(Key key, char value, bool is_pressed) final;
|
||||||
void reset_all_keys() final;
|
void reset_all_keys() final;
|
||||||
const std::set<Key> &observed_keys() final;
|
const std::set<Key> &observed_keys() final;
|
||||||
bool is_exclusive() final;
|
bool is_exclusive() final;
|
||||||
|
@@ -19,14 +19,15 @@ Keyboard::Keyboard(const std::set<Key> &essential_modifiers) : essential_modifie
|
|||||||
Keyboard::Keyboard(const std::set<Key> &observed_keys, const std::set<Key> &essential_modifiers) :
|
Keyboard::Keyboard(const std::set<Key> &observed_keys, const std::set<Key> &essential_modifiers) :
|
||||||
observed_keys_(observed_keys), essential_modifiers_(essential_modifiers), is_exclusive_(false) {}
|
observed_keys_(observed_keys), essential_modifiers_(essential_modifiers), is_exclusive_(false) {}
|
||||||
|
|
||||||
void Keyboard::set_key_pressed(Key key, char value, bool is_pressed) {
|
bool Keyboard::set_key_pressed(Key key, char value, bool is_pressed) {
|
||||||
std::size_t key_offset = static_cast<std::size_t>(key);
|
std::size_t key_offset = static_cast<std::size_t>(key);
|
||||||
if(key_offset >= key_states_.size()) {
|
if(key_offset >= key_states_.size()) {
|
||||||
key_states_.resize(key_offset+1, false);
|
key_states_.resize(key_offset+1, false);
|
||||||
}
|
}
|
||||||
key_states_[key_offset] = is_pressed;
|
key_states_[key_offset] = is_pressed;
|
||||||
|
|
||||||
if(delegate_) delegate_->keyboard_did_change_key(this, key, is_pressed);
|
if(delegate_) return delegate_->keyboard_did_change_key(this, key, is_pressed);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::set<Inputs::Keyboard::Key> &Keyboard::get_essential_modifiers() {
|
const std::set<Inputs::Keyboard::Key> &Keyboard::get_essential_modifiers() {
|
||||||
|
@@ -45,7 +45,9 @@ class Keyboard {
|
|||||||
Keyboard(const std::set<Key> &observed_keys, const std::set<Key> &essential_modifiers);
|
Keyboard(const std::set<Key> &observed_keys, const std::set<Key> &essential_modifiers);
|
||||||
|
|
||||||
// Host interface.
|
// Host interface.
|
||||||
virtual void set_key_pressed(Key key, char value, bool is_pressed);
|
|
||||||
|
/// @returns @c true if the key press affects the machine; @c false otherwise.
|
||||||
|
virtual bool set_key_pressed(Key key, char value, bool is_pressed);
|
||||||
virtual void reset_all_keys();
|
virtual void reset_all_keys();
|
||||||
|
|
||||||
/// @returns a set of all Keys that this keyboard responds to.
|
/// @returns a set of all Keys that this keyboard responds to.
|
||||||
@@ -68,7 +70,7 @@ class Keyboard {
|
|||||||
|
|
||||||
// Delegate interface.
|
// Delegate interface.
|
||||||
struct Delegate {
|
struct Delegate {
|
||||||
virtual void keyboard_did_change_key(Keyboard *keyboard, Key key, bool is_pressed) = 0;
|
virtual bool keyboard_did_change_key(Keyboard *keyboard, Key key, bool is_pressed) = 0;
|
||||||
virtual void reset_all_keys(Keyboard *keyboard) = 0;
|
virtual void reset_all_keys(Keyboard *keyboard) = 0;
|
||||||
};
|
};
|
||||||
void set_delegate(Delegate *delegate);
|
void set_delegate(Delegate *delegate);
|
||||||
|
@@ -13,7 +13,9 @@ using namespace AmstradCPC;
|
|||||||
uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
||||||
#define BIND(source, dest) case Inputs::Keyboard::Key::source: return dest
|
#define BIND(source, dest) case Inputs::Keyboard::Key::source: return dest
|
||||||
switch(key) {
|
switch(key) {
|
||||||
default: return KeyCopy;
|
default: break;
|
||||||
|
|
||||||
|
BIND(BackTick, KeyCopy);
|
||||||
|
|
||||||
BIND(k0, Key0); BIND(k1, Key1); BIND(k2, Key2); BIND(k3, Key3); BIND(k4, Key4);
|
BIND(k0, Key0); BIND(k1, Key1); BIND(k2, Key2); BIND(k3, Key3); BIND(k4, Key4);
|
||||||
BIND(k5, Key5); BIND(k6, Key6); BIND(k7, Key7); BIND(k8, Key8); BIND(k9, Key9);
|
BIND(k5, Key5); BIND(k6, Key6); BIND(k7, Key7); BIND(k8, Key8); BIND(k9, Key9);
|
||||||
@@ -72,6 +74,7 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
BIND(KeypadDelete, KeyDelete);
|
BIND(KeypadDelete, KeyDelete);
|
||||||
}
|
}
|
||||||
#undef BIND
|
#undef BIND
|
||||||
|
return KeyboardMachine::MappedMachine::KeyNotMapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
||||||
@@ -142,7 +145,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) {
|
|||||||
/* x */ KEYS(KeyX), /* y */ KEYS(KeyY),
|
/* x */ KEYS(KeyX), /* y */ KEYS(KeyY),
|
||||||
/* z */ KEYS(KeyZ), /* { */ X,
|
/* z */ KEYS(KeyZ), /* { */ X,
|
||||||
/* | */ SHIFT(KeyAt), /* } */ X,
|
/* | */ SHIFT(KeyAt), /* } */ X,
|
||||||
/* ~ */ X, /* DEL */ KEYS(KeyDelete),
|
/* ~ */ X
|
||||||
};
|
};
|
||||||
#undef KEYS
|
#undef KEYS
|
||||||
#undef SHIFT
|
#undef SHIFT
|
||||||
|
@@ -811,18 +811,18 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
open_apple_is_pressed_ = closed_apple_is_pressed_ = key_is_down_ = false;
|
open_apple_is_pressed_ = closed_apple_is_pressed_ = key_is_down_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_key_pressed(Key key, char value, bool is_pressed) final {
|
bool set_key_pressed(Key key, char value, bool is_pressed) final {
|
||||||
switch(key) {
|
switch(key) {
|
||||||
default: break;
|
default: break;
|
||||||
case Key::F12:
|
case Key::F12:
|
||||||
m6502_.set_reset_line(is_pressed);
|
m6502_.set_reset_line(is_pressed);
|
||||||
return;
|
return true;
|
||||||
case Key::LeftOption:
|
case Key::LeftOption:
|
||||||
open_apple_is_pressed_ = is_pressed;
|
open_apple_is_pressed_ = is_pressed;
|
||||||
return;
|
return true;
|
||||||
case Key::RightOption:
|
case Key::RightOption:
|
||||||
closed_apple_is_pressed_ = is_pressed;
|
closed_apple_is_pressed_ = is_pressed;
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no ASCII value is supplied, look for a few special cases.
|
// If no ASCII value is supplied, look for a few special cases.
|
||||||
@@ -833,7 +833,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
case Key::Down: value = 0x0a; break;
|
case Key::Down: value = 0x0a; break;
|
||||||
case Key::Up: value = 0x0b; break;
|
case Key::Up: value = 0x0b; break;
|
||||||
case Key::Backspace: value = 0x7f; break;
|
case Key::Backspace: value = 0x7f; break;
|
||||||
default: return;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,6 +848,8 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
key_is_down_ = false;
|
key_is_down_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Inputs::Keyboard &get_keyboard() final {
|
Inputs::Keyboard &get_keyboard() final {
|
||||||
|
@@ -62,7 +62,7 @@ class ConcreteMachine:
|
|||||||
set_clock_rate(2000000);
|
set_clock_rate(2000000);
|
||||||
|
|
||||||
speaker_.set_input_rate(2000000 / SoundGenerator::clock_rate_divider);
|
speaker_.set_input_rate(2000000 / SoundGenerator::clock_rate_divider);
|
||||||
speaker_.set_high_frequency_cutoff(7000);
|
speaker_.set_high_frequency_cutoff(6000);
|
||||||
|
|
||||||
const std::string machine_name = "Electron";
|
const std::string machine_name = "Electron";
|
||||||
std::vector<ROMMachine::ROM> required_roms = {
|
std::vector<ROMMachine::ROM> required_roms = {
|
||||||
|
@@ -13,7 +13,9 @@ using namespace Electron;
|
|||||||
uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
||||||
#define BIND(source, dest) case Inputs::Keyboard::Key::source: return Electron::Key::dest
|
#define BIND(source, dest) case Inputs::Keyboard::Key::source: return Electron::Key::dest
|
||||||
switch(key) {
|
switch(key) {
|
||||||
default: return KeyCopy;
|
default: break;
|
||||||
|
|
||||||
|
BIND(BackTick, KeyCopy);
|
||||||
|
|
||||||
BIND(k0, Key0); BIND(k1, Key1); BIND(k2, Key2); BIND(k3, Key3); BIND(k4, Key4);
|
BIND(k0, Key0); BIND(k1, Key1); BIND(k2, Key2); BIND(k3, Key3); BIND(k4, Key4);
|
||||||
BIND(k5, Key5); BIND(k6, Key6); BIND(k7, Key7); BIND(k8, Key8); BIND(k9, Key9);
|
BIND(k5, Key5); BIND(k6, Key6); BIND(k7, Key7); BIND(k8, Key8); BIND(k9, Key9);
|
||||||
@@ -52,6 +54,7 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
BIND(Space, KeySpace);
|
BIND(Space, KeySpace);
|
||||||
}
|
}
|
||||||
#undef BIND
|
#undef BIND
|
||||||
|
return KeyboardMachine::MappedMachine::KeyNotMapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
||||||
@@ -123,7 +126,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) {
|
|||||||
/* x */ SHIFT(KeyX), /* y */ SHIFT(KeyY),
|
/* x */ SHIFT(KeyX), /* y */ SHIFT(KeyY),
|
||||||
/* z */ SHIFT(KeyZ), /* { */ CTRL(KeyUp),
|
/* z */ SHIFT(KeyZ), /* { */ CTRL(KeyUp),
|
||||||
/* | */ SHIFT(KeyRight), /* } */ CTRL(KeyDown),
|
/* | */ SHIFT(KeyRight), /* } */ CTRL(KeyDown),
|
||||||
/* ~ */ CTRL(KeyLeft), /* DEL */ KEYS(KeyDelete),
|
/* ~ */ CTRL(KeyLeft)
|
||||||
};
|
};
|
||||||
#undef KEYS
|
#undef KEYS
|
||||||
#undef SHIFT
|
#undef SHIFT
|
||||||
|
@@ -14,9 +14,11 @@ MappedMachine::MappedMachine(const std::set<Inputs::Keyboard::Key> &essential_mo
|
|||||||
keyboard_.set_delegate(this);
|
keyboard_.set_delegate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MappedMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) {
|
bool MappedMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) {
|
||||||
uint16_t mapped_key = get_keyboard_mapper()->mapped_key_for_key(key);
|
uint16_t mapped_key = get_keyboard_mapper()->mapped_key_for_key(key);
|
||||||
if(mapped_key != KeyNotMapped) set_key_state(mapped_key, is_pressed);
|
if(mapped_key == KeyNotMapped) return false;
|
||||||
|
set_key_state(mapped_key, is_pressed);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MappedMachine::reset_all_keys(Inputs::Keyboard *keyboard) {
|
void MappedMachine::reset_all_keys(Inputs::Keyboard *keyboard) {
|
||||||
|
@@ -95,7 +95,7 @@ class MappedMachine: public Inputs::Keyboard::Delegate, public Machine {
|
|||||||
virtual Inputs::Keyboard &get_keyboard() override;
|
virtual Inputs::Keyboard &get_keyboard() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) override;
|
bool keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) override;
|
||||||
void reset_all_keys(Inputs::Keyboard *keyboard) override;
|
void reset_all_keys(Inputs::Keyboard *keyboard) override;
|
||||||
Inputs::Keyboard keyboard_;
|
Inputs::Keyboard keyboard_;
|
||||||
};
|
};
|
||||||
|
@@ -355,12 +355,18 @@ class ConcreteMachine:
|
|||||||
return keyboard_;
|
return keyboard_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_did_change_key(Inputs::Keyboard *, Inputs::Keyboard::Key key, bool is_pressed) final {
|
bool keyboard_did_change_key(Inputs::Keyboard *, Inputs::Keyboard::Key key, bool is_pressed) final {
|
||||||
if(key == Inputs::Keyboard::Key::Enter) {
|
if(key == Inputs::Keyboard::Key::Enter) {
|
||||||
pause_is_pressed_ = is_pressed;
|
pause_is_pressed_ = is_pressed;
|
||||||
} else if(key == Inputs::Keyboard::Key::Escape) {
|
return true;
|
||||||
reset_is_pressed_ = is_pressed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(key == Inputs::Keyboard::Key::Escape) {
|
||||||
|
reset_is_pressed_ = is_pressed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_all_keys(Inputs::Keyboard *) final {
|
void reset_all_keys(Inputs::Keyboard *) final {
|
||||||
|
@@ -32,6 +32,11 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
// Virtual keys follow.
|
// Virtual keys follow.
|
||||||
BIND(Backspace, KeyDelete);
|
BIND(Backspace, KeyDelete);
|
||||||
BIND(Escape, KeyBreak);
|
BIND(Escape, KeyBreak);
|
||||||
|
BIND(Up, KeyUp);
|
||||||
|
BIND(Down, KeyDown);
|
||||||
|
BIND(Left, KeyLeft);
|
||||||
|
BIND(Right, KeyRight);
|
||||||
|
BIND(BackTick, KeyEdit); BIND(F1, KeyEdit);
|
||||||
}
|
}
|
||||||
#undef BIND
|
#undef BIND
|
||||||
return KeyboardMachine::MappedMachine::KeyNotMapped;
|
return KeyboardMachine::MappedMachine::KeyNotMapped;
|
||||||
|
@@ -26,7 +26,7 @@ enum Key: uint16_t {
|
|||||||
|
|
||||||
// Add some virtual keys; these do not exist on a real ZX80 or ZX81. They're just a convenience.
|
// Add some virtual keys; these do not exist on a real ZX80 or ZX81. They're just a convenience.
|
||||||
KeyDelete = 0x0801,
|
KeyDelete = 0x0801,
|
||||||
KeyBreak = 0x0802,
|
KeyBreak, KeyLeft, KeyRight, KeyUp, KeyDown, KeyEdit
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
||||||
|
@@ -353,19 +353,22 @@ template<bool is_zx81> class ConcreteMachine:
|
|||||||
const auto line = key >> 8;
|
const auto line = key >> 8;
|
||||||
|
|
||||||
// Check for special cases.
|
// Check for special cases.
|
||||||
if(line == 8) {
|
if(line > 7) {
|
||||||
switch(key) {
|
switch(key) {
|
||||||
case KeyDelete:
|
#define ShiftedKey(source, base) \
|
||||||
// Map delete to shift+0.
|
case source: \
|
||||||
set_key_state(KeyShift, is_pressed);
|
set_key_state(KeyShift, is_pressed); \
|
||||||
set_key_state(Key0, is_pressed);
|
set_key_state(base, is_pressed); \
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyBreak:
|
ShiftedKey(KeyDelete, Key0);
|
||||||
// Map break to shift+space.
|
ShiftedKey(KeyBreak, KeySpace);
|
||||||
set_key_state(KeyShift, is_pressed);
|
ShiftedKey(KeyUp, Key7);
|
||||||
set_key_state(KeySpace, is_pressed);
|
ShiftedKey(KeyDown, Key6);
|
||||||
break;
|
ShiftedKey(KeyLeft, Key5);
|
||||||
|
ShiftedKey(KeyRight, Key8);
|
||||||
|
ShiftedKey(KeyEdit, Key1);
|
||||||
|
#undef ShiftedKey
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(is_pressed)
|
if(is_pressed)
|
||||||
|
@@ -504,10 +504,21 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is logical mode and this key maps to a symbol, supply it
|
// Decide whether to try to 'type' (in the logical mapping sense) in the first instance.
|
||||||
// as something to type. If this isn't logical mode, or this key doesn't
|
bool shouldTryToType = self.inputMode == CSMachineKeyboardInputModeKeyboardLogical;
|
||||||
// map to a symbol, pass it along as a standard press.
|
|
||||||
if(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
@synchronized(self) {
|
||||||
if(pressedKey && keyboard_machine->can_type(pressedKey)) {
|
if(pressedKey && keyboard_machine->can_type(pressedKey)) {
|
||||||
if(isPressed) {
|
if(isPressed) {
|
||||||
@@ -517,10 +528,12 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@synchronized(self) {
|
// Okay, so at this point either: set_key_pressed was already tried but will fail anyway,
|
||||||
keyboard.set_key_pressed(mapped_key, pressedKey, isPressed);
|
// 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;
|
return;
|
||||||
|
Reference in New Issue
Block a user