client: move scan code translation to InputsChannel

This commit is contained in:
Yaniv Kamay 2009-11-22 18:54:24 +02:00
parent 4f2e36f08a
commit 81241dd825
5 changed files with 234 additions and 218 deletions

View File

@ -234,7 +234,7 @@ void StickyKeyTimer::response(AbstractProcessLoop& events_loop)
Application* app = (Application*)events_loop.get_owner();
StickyInfo* sticky_info = &app->_sticky_info;
ASSERT(app->is_sticky_trace_key(sticky_info->key));
ASSERT(app->_key_table[sticky_info->key ].press);
ASSERT(app->_keyboard_state[sticky_info->key]);
ASSERT(sticky_info->key_first_down);
ASSERT(sticky_info->key_down);
sticky_info->sticky_mode = true;
@ -270,6 +270,7 @@ Application::Application()
, _changing_screens (false)
, _exit_code (0)
, _active_screen (NULL)
, _num_keys_pressed (0)
, _gui_layer (new GUILayer())
, _key_handler (&default_key_handler)
, _mouse_handler (&default_mouse_handler)
@ -281,7 +282,7 @@ Application::Application()
DBG(0, "");
Platform::set_process_loop(*this);
init_monitors();
init_key_table();
memset(_keyboard_state, 0, sizeof(_keyboard_state));
init_menu();
_main_screen = get_screen(0);
_main_screen->attach_layer(*_gui_layer);
@ -573,177 +574,12 @@ void Application::on_mouse_up(int button, int buttons_state)
_mouse_handler->on_mouse_up(button, buttons_state);
}
void Application::init_scan_code(int index)
{
ASSERT((index & 0x80) == 0);
_key_table[index]._make = index;
_key_table[index]._break = index | 0x80;
}
void Application::init_korean_scan_code(int index)
{
_key_table[index]._make = index;
_key_table[index]._break = index;
}
void Application::init_escape_scan_code(int index)
{
ASSERT(((index - REDKEY_ESCAPE_BASE) & 0x80) == 0);
_key_table[index]._make = 0xe0 | ((index - REDKEY_ESCAPE_BASE) << 8);
_key_table[index]._break = _key_table[index]._make | 0x8000;
}
void Application::init_pause_scan_code()
{
_key_table[REDKEY_PAUSE]._make = 0x451de1;
_key_table[REDKEY_PAUSE]._break = 0xc59de1;
}
void Application::init_key_table()
{
memset(_key_table, 0, sizeof(_key_table));
_num_keys_pressed = 0;
init_scan_code(REDKEY_ESCAPE);
init_scan_code(REDKEY_1);
init_scan_code(REDKEY_2);
init_scan_code(REDKEY_3);
init_scan_code(REDKEY_4);
init_scan_code(REDKEY_5);
init_scan_code(REDKEY_6);
init_scan_code(REDKEY_7);
init_scan_code(REDKEY_8);
init_scan_code(REDKEY_9);
init_scan_code(REDKEY_0);
init_scan_code(REDKEY_MINUS);
init_scan_code(REDKEY_EQUALS);
init_scan_code(REDKEY_BACKSPACE);
init_scan_code(REDKEY_TAB);
init_scan_code(REDKEY_Q);
init_scan_code(REDKEY_W);
init_scan_code(REDKEY_E);
init_scan_code(REDKEY_R);
init_scan_code(REDKEY_T);
init_scan_code(REDKEY_Y);
init_scan_code(REDKEY_U);
init_scan_code(REDKEY_I);
init_scan_code(REDKEY_O);
init_scan_code(REDKEY_P);
init_scan_code(REDKEY_L_BRACKET);
init_scan_code(REDKEY_R_BRACKET);
init_scan_code(REDKEY_ENTER);
init_scan_code(REDKEY_L_CTRL);
init_scan_code(REDKEY_A);
init_scan_code(REDKEY_S);
init_scan_code(REDKEY_D);
init_scan_code(REDKEY_F);
init_scan_code(REDKEY_G);
init_scan_code(REDKEY_H);
init_scan_code(REDKEY_J);
init_scan_code(REDKEY_K);
init_scan_code(REDKEY_L);
init_scan_code(REDKEY_SEMICOLON);
init_scan_code(REDKEY_QUOTE);
init_scan_code(REDKEY_BACK_QUOTE);
init_scan_code(REDKEY_L_SHIFT);
init_scan_code(REDKEY_BACK_SLASH);
init_scan_code(REDKEY_Z);
init_scan_code(REDKEY_X);
init_scan_code(REDKEY_C);
init_scan_code(REDKEY_V);
init_scan_code(REDKEY_B);
init_scan_code(REDKEY_N);
init_scan_code(REDKEY_M);
init_scan_code(REDKEY_COMMA);
init_scan_code(REDKEY_PERIOD);
init_scan_code(REDKEY_SLASH);
init_scan_code(REDKEY_R_SHIFT);
init_scan_code(REDKEY_PAD_MULTIPLY);
init_scan_code(REDKEY_L_ALT);
init_scan_code(REDKEY_SPACE);
init_scan_code(REDKEY_CAPS_LOCK);
init_scan_code(REDKEY_F1);
init_scan_code(REDKEY_F2);
init_scan_code(REDKEY_F3);
init_scan_code(REDKEY_F4);
init_scan_code(REDKEY_F5);
init_scan_code(REDKEY_F6);
init_scan_code(REDKEY_F7);
init_scan_code(REDKEY_F8);
init_scan_code(REDKEY_F9);
init_scan_code(REDKEY_F10);
init_scan_code(REDKEY_NUM_LOCK);
init_scan_code(REDKEY_SCROLL_LOCK);
init_scan_code(REDKEY_PAD_7);
init_scan_code(REDKEY_PAD_8);
init_scan_code(REDKEY_PAD_9);
init_scan_code(REDKEY_PAD_MINUS);
init_scan_code(REDKEY_PAD_4);
init_scan_code(REDKEY_PAD_5);
init_scan_code(REDKEY_PAD_6);
init_scan_code(REDKEY_PAD_PLUS);
init_scan_code(REDKEY_PAD_1);
init_scan_code(REDKEY_PAD_2);
init_scan_code(REDKEY_PAD_3);
init_scan_code(REDKEY_PAD_0);
init_scan_code(REDKEY_PAD_POINT);
init_scan_code(REDKEY_EUROPEAN);
init_scan_code(REDKEY_F11);
init_scan_code(REDKEY_F12);
init_scan_code(REDKEY_JAPANESE_HIRAGANA_KATAKANA);
init_scan_code(REDKEY_JAPANESE_BACKSLASH);
init_scan_code(REDKEY_JAPANESE_HENKAN);
init_scan_code(REDKEY_JAPANESE_MUHENKAN);
init_scan_code(REDKEY_JAPANESE_YEN);
init_korean_scan_code(REDKEY_KOREAN_HANGUL);
init_korean_scan_code(REDKEY_KOREAN_HANGUL_HANJA);
init_escape_scan_code(REDKEY_ESCAPE_BASE);
init_escape_scan_code(REDKEY_PAD_ENTER);
init_escape_scan_code(REDKEY_R_CTRL);
init_escape_scan_code(REDKEY_FAKE_L_SHIFT);
init_escape_scan_code(REDKEY_PAD_DIVIDE);
init_escape_scan_code(REDKEY_FAKE_R_SHIFT);
init_escape_scan_code(REDKEY_CTRL_PRINT_SCREEN);
init_escape_scan_code(REDKEY_R_ALT);
init_escape_scan_code(REDKEY_CTRL_BREAK);
init_escape_scan_code(REDKEY_HOME);
init_escape_scan_code(REDKEY_UP);
init_escape_scan_code(REDKEY_PAGEUP);
init_escape_scan_code(REDKEY_LEFT);
init_escape_scan_code(REDKEY_RIGHT);
init_escape_scan_code(REDKEY_END);
init_escape_scan_code(REDKEY_DOWN);
init_escape_scan_code(REDKEY_PAGEDOWN);
init_escape_scan_code(REDKEY_INSERT);
init_escape_scan_code(REDKEY_DELETE);
init_escape_scan_code(REDKEY_LEFT_CMD);
init_escape_scan_code(REDKEY_RIGHT_CMD);
init_escape_scan_code(REDKEY_MENU);
init_pause_scan_code();
}
inline uint32_t Application::get_make_scan_code(RedKey key)
{
return _key_table[key]._make;
}
inline uint32_t Application::get_break_scan_code(RedKey key)
{
return _key_table[key]._break;
}
void Application::unpress_all()
{
reset_sticky();
for (int i = 0; i < REDKEY_NUM_KEYS; i++) {
if (_key_table[i].press) {
uint32_t scan_code = get_break_scan_code((RedKey)i);
ASSERT(scan_code);
_key_handler->on_key_up(scan_code);
if (_keyboard_state[i]) {
_key_handler->on_key_up((RedKey)i);
unpress_key((RedKey)i);
}
}
@ -925,10 +761,10 @@ static void show_red_key(RedKey key)
bool Application::press_key(RedKey key)
{
if (_key_table[key].press) {
if (_keyboard_state[key]) {
return true;
} else {
_key_table[key].press = true;
_keyboard_state[key] = true;
_num_keys_pressed++;
return false;
}
@ -938,8 +774,8 @@ bool Application::unpress_key(RedKey key)
{
ASSERT(!_sticky_info.key_down || !is_sticky_trace_key(key));
if (_key_table[key].press) {
_key_table[key].press = false;
if (_keyboard_state[key]) {
_keyboard_state[key] = false;
_num_keys_pressed--;
ASSERT(_num_keys_pressed >= 0);
return true;
@ -959,7 +795,7 @@ void Application::reset_sticky()
_sticky_info.key_first_down = false;
deactivate_interval_timer(*_sticky_info.timer);
if (_sticky_info.sticky_mode) {
ASSERT(_key_table[_sticky_info.key].press);
ASSERT(_keyboard_state[_sticky_info.key]);
// if it is physically down, we shouldn't unpress it
if (!_sticky_info.key_down) {
do_on_key_up(_sticky_info.key);
@ -980,12 +816,6 @@ void Application::on_key_down(RedKey key)
return;
}
uint32_t scan_code = get_make_scan_code(key);
if (!scan_code) {
LOG_WARN("no make code for %d", key);
return;
}
bool was_pressed = press_key(key);
if (_sticky_info.trace_is_on) {
if (key == _sticky_info.key) {
@ -1019,17 +849,17 @@ void Application::on_key_down(RedKey key)
#ifdef WIN32
if (!_active_screen->intercepts_sys_key() &&
(key == REDKEY_LEFT_CMD || key == REDKEY_RIGHT_CMD ||
key == REDKEY_MENU || _key_table[REDKEY_L_ALT].press)) {
key == REDKEY_MENU || _keyboard_state[REDKEY_L_ALT])) {
unpress_key(key);
return;
}
if (!_sticky_info.sticky_mode &&
((_key_table[REDKEY_L_CTRL].press || _key_table[REDKEY_R_CTRL].press) &&
(_key_table[REDKEY_L_ALT].press || _key_table[REDKEY_R_ALT].press))) {
((_keyboard_state[REDKEY_L_CTRL] || _keyboard_state[REDKEY_R_CTRL]) &&
(_keyboard_state[REDKEY_L_ALT] || _keyboard_state[REDKEY_R_ALT]))) {
if (key == REDKEY_END || key == REDKEY_PAD_1) {
unpress_key(key);
_key_handler->on_key_down(get_make_scan_code(REDKEY_DELETE));
_key_handler->on_key_up(get_break_scan_code(REDKEY_DELETE));
_key_handler->on_key_down(REDKEY_DELETE);
_key_handler->on_key_up(REDKEY_DELETE);
} else if (key == REDKEY_DELETE || key == REDKEY_PAD_POINT) {
unpress_key(key);
return;
@ -1037,23 +867,18 @@ void Application::on_key_down(RedKey key)
}
#endif
_key_handler->on_key_down(scan_code);
_key_handler->on_key_down(key);
}
void Application::do_on_key_up(RedKey key)
{
unpress_key(key);
uint32_t scan_code = get_break_scan_code(key);
if (!scan_code) {
LOG_WARN("no break code for %d", key);
return;
}
_key_handler->on_key_up(scan_code);
_key_handler->on_key_up(key);
}
void Application::on_key_up(RedKey key)
{
if(key < 0 || key >= REDKEY_NUM_KEYS || !_key_table[key].press) {
if(key < 0 || key >= REDKEY_NUM_KEYS || !_keyboard_state[key]) {
return;
}
@ -1294,9 +1119,9 @@ bool Application::toggle_full_screen()
{
RedKey shift_pressed = REDKEY_INVALID;
if (_key_table[REDKEY_L_SHIFT].press) {
if (_keyboard_state[REDKEY_L_SHIFT]) {
shift_pressed = REDKEY_L_SHIFT;
} else if (_key_table[REDKEY_R_SHIFT].press) {
} else if (_keyboard_state[REDKEY_R_SHIFT]) {
shift_pressed = REDKEY_R_SHIFT;
}
if (_full_screen) {
@ -1395,7 +1220,7 @@ bool Application::is_key_set_pressed(const HotkeySet& key_set)
HotkeySet::const_iterator iter = key_set.begin();
while (iter != key_set.end()) {
if (!(_key_table[iter->main].press || _key_table[iter->alter].press)) {
if (!(_keyboard_state[iter->main] || _keyboard_state[iter->alter])) {
break;
}
++iter;
@ -1420,12 +1245,12 @@ int Application::get_hotkeys_commnad()
void Application::send_key_down(RedKey key)
{
_key_handler->on_key_down(get_make_scan_code(key));
_key_handler->on_key_down(key);
}
void Application::send_key_up(RedKey key)
{
_key_handler->on_key_up(get_break_scan_code(key));
_key_handler->on_key_up(key);
}
void Application::send_alt_ctl_del()

View File

@ -72,12 +72,6 @@ private:
std::vector<MonitorInfo> _monitors;
};
struct KeyInfo {
uint32_t _make;
uint32_t _break;
bool press;
};
enum CanvasOption {
CANVAS_OPTION_INVALID,
CANVAS_OPTION_CAIRO,
@ -175,14 +169,7 @@ private:
bool set_canvas_option(CmdLineParser& parser, char *val);
bool process_cmd_line(int argc, char** argv);
void abort();
void init_scan_code(int index);
void init_korean_scan_code(int index);
void init_escape_scan_code(int index);
void init_pause_scan_code();
void init_key_table();
void init_menu();
uint32_t get_make_scan_code(RedKey key);
uint32_t get_break_scan_code(RedKey key);
void unpress_all();
bool release_capture();
bool do_connect();
@ -235,7 +222,7 @@ private:
bool _changing_screens;
int _exit_code;
RedScreen* _active_screen;
KeyInfo _key_table[REDKEY_NUM_KEYS];
bool _keyboard_state[REDKEY_NUM_KEYS];
int _num_keys_pressed;
HotKeys _hot_keys;
CommandsMap _commands_map;

View File

@ -332,16 +332,40 @@ void InputsChannel::on_mouse_up(int button, int buttons_state)
post_message(message);
}
void InputsChannel::on_key_down(uint32_t scan_code)
InputsChannel::KeyInfo InputsChannel::_scan_table[REDKEY_NUM_KEYS];
uint32_t InputsChannel::get_make_scan_code(RedKey key)
{
return _scan_table[key].make_scan;
}
uint32_t InputsChannel::get_break_scan_code(RedKey key)
{
return _scan_table[key].break_scan;
}
void InputsChannel::on_key_down(RedKey key)
{
uint32_t scan_code = get_make_scan_code(key);
if (!scan_code) {
LOG_WARN("no make code for %d", key);
return;
}
Message* message = new Message(REDC_INPUTS_KEY_DOWN, sizeof(RedcKeyDown));
RedcKeyDown* event = (RedcKeyDown*)message->data();
event->code = scan_code;
post_message(message);
}
void InputsChannel::on_key_up(uint32_t scan_code)
void InputsChannel::on_key_up(RedKey key)
{
uint32_t scan_code = get_break_scan_code(key);
if (!scan_code) {
LOG_WARN("no break code for %d", key);
return;
}
Message* message = new Message(REDC_INPUTS_KEY_UP, sizeof(RedcKeyUp));
RedcKeyUp* event = (RedcKeyUp*)message->data();
event->code = scan_code;
@ -379,6 +403,168 @@ void InputsChannel::on_focus_in()
#endif
}
void InputsChannel::init_scan_code(int index)
{
ASSERT((index & 0x80) == 0);
_scan_table[index].make_scan = index;
_scan_table[index].break_scan = index | 0x80;
}
void InputsChannel::init_korean_scan_code(int index)
{
_scan_table[index].make_scan = index;
_scan_table[index].break_scan = index;
}
void InputsChannel::init_escape_scan_code(int index)
{
ASSERT(((index - REDKEY_ESCAPE_BASE) & 0x80) == 0);
_scan_table[index].make_scan = 0xe0 | ((index - REDKEY_ESCAPE_BASE) << 8);
_scan_table[index].break_scan = _scan_table[index].make_scan | 0x8000;
}
void InputsChannel::init_pause_scan_code()
{
_scan_table[REDKEY_PAUSE].make_scan = 0x451de1;
_scan_table[REDKEY_PAUSE].break_scan = 0xc59de1;
}
void InputsChannel::init_scan_table()
{
memset(_scan_table, 0, sizeof(_scan_table));
init_scan_code(REDKEY_ESCAPE);
init_scan_code(REDKEY_1);
init_scan_code(REDKEY_2);
init_scan_code(REDKEY_3);
init_scan_code(REDKEY_4);
init_scan_code(REDKEY_5);
init_scan_code(REDKEY_6);
init_scan_code(REDKEY_7);
init_scan_code(REDKEY_8);
init_scan_code(REDKEY_9);
init_scan_code(REDKEY_0);
init_scan_code(REDKEY_MINUS);
init_scan_code(REDKEY_EQUALS);
init_scan_code(REDKEY_BACKSPACE);
init_scan_code(REDKEY_TAB);
init_scan_code(REDKEY_Q);
init_scan_code(REDKEY_W);
init_scan_code(REDKEY_E);
init_scan_code(REDKEY_R);
init_scan_code(REDKEY_T);
init_scan_code(REDKEY_Y);
init_scan_code(REDKEY_U);
init_scan_code(REDKEY_I);
init_scan_code(REDKEY_O);
init_scan_code(REDKEY_P);
init_scan_code(REDKEY_L_BRACKET);
init_scan_code(REDKEY_R_BRACKET);
init_scan_code(REDKEY_ENTER);
init_scan_code(REDKEY_L_CTRL);
init_scan_code(REDKEY_A);
init_scan_code(REDKEY_S);
init_scan_code(REDKEY_D);
init_scan_code(REDKEY_F);
init_scan_code(REDKEY_G);
init_scan_code(REDKEY_H);
init_scan_code(REDKEY_J);
init_scan_code(REDKEY_K);
init_scan_code(REDKEY_L);
init_scan_code(REDKEY_SEMICOLON);
init_scan_code(REDKEY_QUOTE);
init_scan_code(REDKEY_BACK_QUOTE);
init_scan_code(REDKEY_L_SHIFT);
init_scan_code(REDKEY_BACK_SLASH);
init_scan_code(REDKEY_Z);
init_scan_code(REDKEY_X);
init_scan_code(REDKEY_C);
init_scan_code(REDKEY_V);
init_scan_code(REDKEY_B);
init_scan_code(REDKEY_N);
init_scan_code(REDKEY_M);
init_scan_code(REDKEY_COMMA);
init_scan_code(REDKEY_PERIOD);
init_scan_code(REDKEY_SLASH);
init_scan_code(REDKEY_R_SHIFT);
init_scan_code(REDKEY_PAD_MULTIPLY);
init_scan_code(REDKEY_L_ALT);
init_scan_code(REDKEY_SPACE);
init_scan_code(REDKEY_CAPS_LOCK);
init_scan_code(REDKEY_F1);
init_scan_code(REDKEY_F2);
init_scan_code(REDKEY_F3);
init_scan_code(REDKEY_F4);
init_scan_code(REDKEY_F5);
init_scan_code(REDKEY_F6);
init_scan_code(REDKEY_F7);
init_scan_code(REDKEY_F8);
init_scan_code(REDKEY_F9);
init_scan_code(REDKEY_F10);
init_scan_code(REDKEY_NUM_LOCK);
init_scan_code(REDKEY_SCROLL_LOCK);
init_scan_code(REDKEY_PAD_7);
init_scan_code(REDKEY_PAD_8);
init_scan_code(REDKEY_PAD_9);
init_scan_code(REDKEY_PAD_MINUS);
init_scan_code(REDKEY_PAD_4);
init_scan_code(REDKEY_PAD_5);
init_scan_code(REDKEY_PAD_6);
init_scan_code(REDKEY_PAD_PLUS);
init_scan_code(REDKEY_PAD_1);
init_scan_code(REDKEY_PAD_2);
init_scan_code(REDKEY_PAD_3);
init_scan_code(REDKEY_PAD_0);
init_scan_code(REDKEY_PAD_POINT);
init_scan_code(REDKEY_EUROPEAN);
init_scan_code(REDKEY_F11);
init_scan_code(REDKEY_F12);
init_scan_code(REDKEY_JAPANESE_HIRAGANA_KATAKANA);
init_scan_code(REDKEY_JAPANESE_BACKSLASH);
init_scan_code(REDKEY_JAPANESE_HENKAN);
init_scan_code(REDKEY_JAPANESE_MUHENKAN);
init_scan_code(REDKEY_JAPANESE_YEN);
init_korean_scan_code(REDKEY_KOREAN_HANGUL);
init_korean_scan_code(REDKEY_KOREAN_HANGUL_HANJA);
init_escape_scan_code(REDKEY_ESCAPE_BASE);
init_escape_scan_code(REDKEY_PAD_ENTER);
init_escape_scan_code(REDKEY_R_CTRL);
init_escape_scan_code(REDKEY_FAKE_L_SHIFT);
init_escape_scan_code(REDKEY_PAD_DIVIDE);
init_escape_scan_code(REDKEY_FAKE_R_SHIFT);
init_escape_scan_code(REDKEY_CTRL_PRINT_SCREEN);
init_escape_scan_code(REDKEY_R_ALT);
init_escape_scan_code(REDKEY_CTRL_BREAK);
init_escape_scan_code(REDKEY_HOME);
init_escape_scan_code(REDKEY_UP);
init_escape_scan_code(REDKEY_PAGEUP);
init_escape_scan_code(REDKEY_LEFT);
init_escape_scan_code(REDKEY_RIGHT);
init_escape_scan_code(REDKEY_END);
init_escape_scan_code(REDKEY_DOWN);
init_escape_scan_code(REDKEY_PAGEDOWN);
init_escape_scan_code(REDKEY_INSERT);
init_escape_scan_code(REDKEY_DELETE);
init_escape_scan_code(REDKEY_LEFT_CMD);
init_escape_scan_code(REDKEY_RIGHT_CMD);
init_escape_scan_code(REDKEY_MENU);
init_pause_scan_code();
}
class InitGlobals {
public:
InitGlobals()
{
InputsChannel::init_scan_table();
}
};
static InitGlobals init_globals;
class InputsFactory: public ChannelFactory {
public:
InputsFactory() : ChannelFactory(RED_CHANNEL_INPUTS) {}

View File

@ -31,8 +31,8 @@ public:
virtual void on_mouse_motion(int dx, int dy, int buttons_state);
virtual void on_mouse_down(int button, int buttons_state);
virtual void on_mouse_up(int button, int buttons_state);
virtual void on_key_down(uint32_t scan_code);
virtual void on_key_up(uint32_t scan_code);
virtual void on_key_down(RedKey key);
virtual void on_key_up(RedKey key);
virtual void on_focus_in();
void on_mouse_position(int x, int y, int buttons_state, int display_id);
@ -53,6 +53,14 @@ private:
void handle_modifaiers(RedPeer::InMessage* message);
void handle_motion_ack(RedPeer::InMessage* message);
static uint32_t get_make_scan_code(RedKey key);
static uint32_t get_break_scan_code(RedKey key);
static void init_scan_code(int index);
static void init_korean_scan_code(int index);
static void init_escape_scan_code(int index);
static void init_pause_scan_code();
static void init_scan_table();
private:
Mutex _motion_lock;
int _mouse_buttons_state;
@ -67,6 +75,14 @@ private:
Mutex _update_modifiers_lock;
bool _active_modifiers_event;
struct KeyInfo {
uint32_t make_scan;
uint32_t break_scan;
};
static KeyInfo _scan_table[REDKEY_NUM_KEYS];
friend class InitGlobals;
friend class MotionMessage;
friend class PositionMessage;
friend class KeyModifiersEvent;

View File

@ -18,11 +18,13 @@
#ifndef _H_INPUTS_HANDLER
#define _H_INPUTS_HANDLER
#include "red_key.h"
class KeyHandler {
public:
virtual ~KeyHandler() {}
virtual void on_key_down(uint32_t scan_code) {}
virtual void on_key_up(uint32_t scan_code) {}
virtual void on_key_down(RedKey key) {}
virtual void on_key_up(RedKey key) {}
virtual void on_focus_in() {}
virtual void on_focus_out() {}
virtual bool permit_focus_loss() { return true;}