diff --git a/libui/include/ui/Button.h b/libui/include/ui/Button.h index aa01dab1..0fbcd2df 100644 --- a/libui/include/ui/Button.h +++ b/libui/include/ui/Button.h @@ -24,6 +24,7 @@ namespace ui Result handle_mouse_leave() override; Result handle_mouse_down(Point position, int buttons) override; Result handle_mouse_up(Point position, int buttons) override; + Result handle_key_event(const ui::KeyEventRequest& request) override; Result draw(Canvas& canvas) override; private: diff --git a/libui/include/ui/Container.h b/libui/include/ui/Container.h index aec537d7..36337d67 100644 --- a/libui/include/ui/Container.h +++ b/libui/include/ui/Container.h @@ -24,6 +24,7 @@ namespace ui Result handle_mouse_leave() override; Result handle_mouse_down(Point position, int buttons) override; Result handle_mouse_up(Point position, int buttons) override; + Result handle_key_event(const ui::KeyEventRequest& request) override; Result draw(Canvas& canvas) override; private: diff --git a/libui/include/ui/Key.h b/libui/include/ui/Key.h new file mode 100644 index 00000000..52aca98d --- /dev/null +++ b/libui/include/ui/Key.h @@ -0,0 +1,14 @@ +#pragma once +#include + +namespace ui +{ + enum Modifier + { + Mod_Shift = (1 << 0), + Mod_Alt = (1 << 1), + Mod_Super = (1 << 2), + Mod_AltGr = (1 << 3), + Mod_Ctrl = (1 << 4) + }; +} diff --git a/libui/include/ui/Layout.h b/libui/include/ui/Layout.h index 66e4976b..3b0e8ad7 100644 --- a/libui/include/ui/Layout.h +++ b/libui/include/ui/Layout.h @@ -34,6 +34,7 @@ namespace ui Result handle_mouse_leave() override; Result handle_mouse_down(Point position, int buttons) override; Result handle_mouse_up(Point position, int buttons) override; + Result handle_key_event(const ui::KeyEventRequest& request) override; Result draw(Canvas& canvas) override; @@ -55,6 +56,7 @@ namespace ui Result handle_mouse_leave() override; Result handle_mouse_down(Point position, int buttons) override; Result handle_mouse_up(Point position, int buttons) override; + Result handle_key_event(const ui::KeyEventRequest& request) override; Result draw(Canvas& canvas) override; diff --git a/libui/include/ui/Widget.h b/libui/include/ui/Widget.h index f93e832b..ea690f6e 100644 --- a/libui/include/ui/Widget.h +++ b/libui/include/ui/Widget.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace ui { @@ -50,6 +51,12 @@ namespace ui return EventResult::DidNotHandle; } + virtual Result handle_key_event(const ui::KeyEventRequest& request) + { + ignore(request); + return EventResult::DidNotHandle; + } + virtual Result draw(Canvas& canvas); void set_window(Window* window, Rect rect, Badge) diff --git a/libui/include/ui/Window.h b/libui/include/ui/Window.h index 99058cba..fd914b0a 100644 --- a/libui/include/ui/Window.h +++ b/libui/include/ui/Window.h @@ -49,6 +49,7 @@ namespace ui Result handle_mouse_leave(); Result handle_mouse_move(ui::Point position); Result handle_mouse_buttons(ui::Point position, int buttons); + Result handle_key_event(const ui::KeyEventRequest& request); int id() const { diff --git a/libui/include/ui/ipc/Client.h b/libui/include/ui/ipc/Client.h index 6e277066..f042d3f1 100644 --- a/libui/include/ui/ipc/Client.h +++ b/libui/include/ui/ipc/Client.h @@ -9,7 +9,9 @@ #pragma once #include +#include #include +#include namespace ui { @@ -20,7 +22,8 @@ namespace ui WINDOW_CLOSE_REQUEST_ID, MOUSE_EVENT_REQUEST_ID, MOUSE_LEAVE_REQUEST_ID, - GET_SCREEN_RECT_RESPONSE_ID + GET_SCREEN_RECT_RESPONSE_ID, + KEY_EVENT_REQUEST_ID, }; struct CreateWindowResponse @@ -60,4 +63,18 @@ namespace ui Rect rect; }; + + struct KeyEventRequest + { + static constexpr u8 ID = KEY_EVENT_REQUEST_ID; + + int window; + + bool pressed; + + char letter; + char key; + moon::KeyCode code; + int modifiers; + }; } diff --git a/libui/src/App.cpp b/libui/src/App.cpp index 43fe9b9b..7f4c9c98 100644 --- a/libui/src/App.cpp +++ b/libui/src/App.cpp @@ -132,6 +132,14 @@ namespace ui window->draw(); return {}; } + case KEY_EVENT_REQUEST_ID: { + KeyEventRequest request; + READ_MESSAGE(request); + auto* window = find_window(request.window); + if (window->handle_key_event(request).value_or(ui::EventResult::DidNotHandle) == ui::EventResult::DidHandle) + window->draw(); + return {}; + } default: fail("Unexpected IPC request from server!"); } } diff --git a/libui/src/Button.cpp b/libui/src/Button.cpp index 08c50d15..dd99d65b 100644 --- a/libui/src/Button.cpp +++ b/libui/src/Button.cpp @@ -61,6 +61,11 @@ namespace ui return m_child->handle_mouse_up(position, buttons); } + Result Button::handle_key_event(const ui::KeyEventRequest& request) + { + return m_child->handle_key_event(request); + } + Result Button::draw(Canvas& canvas) { return m_child->draw(canvas); diff --git a/libui/src/Container.cpp b/libui/src/Container.cpp index d7f320db..9b4fb07f 100644 --- a/libui/src/Container.cpp +++ b/libui/src/Container.cpp @@ -47,6 +47,11 @@ namespace ui return ui::EventResult::DidNotHandle; } + Result Container::handle_key_event(const ui::KeyEventRequest& request) + { + return m_widget->handle_key_event(request); + } + Result Container::draw(Canvas& canvas) { auto rect = ui::Rect { m_widget->rect().pos.x - m_rect.pos.x, m_widget->rect().pos.y - m_rect.pos.y, diff --git a/libui/src/Layout.cpp b/libui/src/Layout.cpp index 2b16c885..300739ca 100644 --- a/libui/src/Layout.cpp +++ b/libui/src/Layout.cpp @@ -64,6 +64,19 @@ namespace ui return ui::EventResult::DidNotHandle; } + Result HorizontalLayout::handle_key_event(const ui::KeyEventRequest& request) + { + EventResult result = ui::EventResult::DidNotHandle; + + for (auto widget : m_widgets) + { + auto rc = TRY(widget->handle_key_event(request)); + if (rc == ui::EventResult::DidHandle) result = rc; + } + + return result; + } + Result HorizontalLayout::draw(Canvas& canvas) { for (auto widget : m_widgets) @@ -159,6 +172,19 @@ namespace ui return ui::EventResult::DidNotHandle; } + Result VerticalLayout::handle_key_event(const ui::KeyEventRequest& request) + { + EventResult result = ui::EventResult::DidNotHandle; + + for (auto widget : m_widgets) + { + auto rc = TRY(widget->handle_key_event(request)); + if (rc == ui::EventResult::DidHandle) result = rc; + } + + return result; + } + Result VerticalLayout::draw(Canvas& canvas) { for (auto widget : m_widgets) diff --git a/libui/src/Window.cpp b/libui/src/Window.cpp index da6541a2..441e47db 100644 --- a/libui/src/Window.cpp +++ b/libui/src/Window.cpp @@ -114,4 +114,10 @@ namespace ui m_old_mouse_buttons = buttons; return result; } + + Result Window::handle_key_event(const ui::KeyEventRequest& request) + { + if (!m_main_widget) return ui::EventResult::DidNotHandle; + return m_main_widget->handle_key_event(request); + } } diff --git a/wind/CMakeLists.txt b/wind/CMakeLists.txt index 5d79b2c8..2999afb8 100644 --- a/wind/CMakeLists.txt +++ b/wind/CMakeLists.txt @@ -8,6 +8,8 @@ set(SOURCES Window.cpp IPC.cpp IPC.h + Keyboard.cpp + Keyboard.h Client.h ) diff --git a/wind/Keyboard.cpp b/wind/Keyboard.cpp new file mode 100644 index 00000000..e0941b49 --- /dev/null +++ b/wind/Keyboard.cpp @@ -0,0 +1,305 @@ +#include "Keyboard.h" +#include + +static const char table[] = { + // Function keys + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + // System keys + '\x1b', + '\0', + '\0', + '\0', + '\0', + // Modifier keys + '\0', + '\0', + '\0', + '\0', // or AltGr on some keyboards + '\0', + '\0', + // Navigation keys + '\t', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + // Editing keys + '\b', + '\n', + '\0', + '\x7f', + '\n', + // Lock keys + '\0', + '\0', + '\0', + // Keypad keys + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '.', + '+', + '-', + '*', + '/', + // Character keys (depending on keyboard layout), examples in US QWERTY + '`', // ` + '1', // 1 + '2', // 2 + '3', // 3 + '4', // 4 + '5', // 5 + '6', // 6 + '7', // 7 + '8', // 8 + '9', // 9 + '0', // 0 + '-', // - + '=', // = + 'q', // Q + 'w', // W + 'e', // E + 'r', // R + 't', // T + 'y', // Y + 'u', // U + 'i', // I + 'o', // O + 'p', // P + '[', // [ + ']', // ] + 'a', // A + 's', // S + 'd', // D + 'f', // F + 'g', // G + 'h', // H + 'j', // J + 'k', // K + 'l', // L + ';', // ; + '\'', // ' + '#', // # + '\\', // Backslash + 'z', // Z + 'x', // X + 'c', // C + 'v', // V + 'b', // B + 'n', // N + 'm', // M + ',', // , + '.', // . + '/', // / + ' ', // Space + // Unknown key + '\0', +}; + +static const char shift_table[] = { + // Function keys + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + // System keys + '\x1b', + '\0', + '\0', + '\0', + '\0', + // Modifier keys + '\0', + '\0', + '\0', + '\0', // or AltGr on some keyboards + '\0', + '\0', + // Navigation keys + '\t', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + '\0', + // Editing keys + '\b', + '\n', + '\0', + '\x7f', + '\n', + // Lock keys + '\0', + '\0', + '\0', + // Keypad keys + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '.', + '+', + '-', + '*', + '/', + // Character keys (depending on keyboard layout), examples in US QWERTY + '~', // ` + '!', + '@', + '#', + '$', + '%', + '^', + '&', + '*', + '(', + ')', + '_', + '+', + 'Q', + 'W', + 'E', + 'R', + 'T', + 'Y', + 'U', + 'I', + 'O', + 'P', + '{', + '}', + 'A', + 'S', + 'D', + 'F', + 'G', + 'H', + 'J', + 'K', + 'L', + ':', + '"', + ' ', // # + '|', // Backslash + 'Z', + 'X', + 'C', + 'V', + 'B', + 'N', + 'M', + '<', + '>', + '?', + ' ', // Space + // Unknown key + '\0', +}; + +namespace wind::Keyboard +{ + static bool g_caps_lock = false; + static bool g_right_shift = false; + static bool g_left_shift = false; + static bool g_right_control = false; + static bool g_left_control = false; + static bool g_altgr = false; + static bool g_alt = false; + static bool g_super = false; + + ui::KeyEventRequest decode_keyboard_event(moon::KeyCode code, bool released) + { + ui::KeyEventRequest request; + request.code = code; + request.pressed = !released; + request.modifiers = 0; + + switch (code) + { + case moon::K_CapsLock: + if (!released) { g_caps_lock = !g_caps_lock; } + break; + case moon::K_RightShift: g_right_shift = !released; break; + case moon::K_LeftShift: g_left_shift = !released; break; + case moon::K_RightControl: g_right_control = !released; break; + case moon::K_LeftControl: g_left_control = !released; break; + case moon::K_RightAlt: g_altgr = !released; break; + case moon::K_LeftAlt: g_alt = !released; break; + case moon::K_Super: g_super = !released; break; + default: break; + } + + if ((g_caps_lock && !(g_left_shift || g_right_shift)) || (g_left_shift || g_right_shift)) + { + request.modifiers |= ui::Mod_Shift; + } + + if (g_right_control || g_left_control) request.modifiers |= ui::Mod_Ctrl; + + if (g_alt) request.modifiers |= ui::Mod_Alt; + if (g_altgr) request.modifiers |= ui::Mod_AltGr; + if (g_super) request.modifiers |= ui::Mod_Super; + + request.key = table[code]; + + if (request.modifiers & ui::Mod_Ctrl) + { + char letter; + if (request.modifiers & ui::Mod_Shift) letter = shift_table[code]; + else + letter = table[code]; + if (_islower(letter)) letter = (char)_toupper(letter); + if (_isupper(letter)) letter = 0x40; + if (letter == '@') letter = 0x40; + if (letter > 'Z' && letter < '`') letter = 0x40; + if (letter == '?') letter = 0x7f; + request.letter = letter; + return request; + } + + if (request.modifiers & ui::Mod_Shift) request.letter = shift_table[code]; + else + request.letter = table[code]; + + return request; + } +} diff --git a/wind/Keyboard.h b/wind/Keyboard.h new file mode 100644 index 00000000..3259b669 --- /dev/null +++ b/wind/Keyboard.h @@ -0,0 +1,10 @@ +#pragma once +#include + +namespace wind +{ + namespace Keyboard + { + ui::KeyEventRequest decode_keyboard_event(moon::KeyCode code, bool released); + } +} diff --git a/wind/main.cpp b/wind/main.cpp index 4354f7f5..d65dd551 100644 --- a/wind/main.cpp +++ b/wind/main.cpp @@ -1,6 +1,7 @@ #define CLIENT_IMPLEMENTATION #include "Client.h" #include "IPC.h" +#include "Keyboard.h" #include "Mouse.h" #include "Screen.h" #include "Window.h" @@ -155,8 +156,14 @@ Result luna_main(int argc, char** argv) { moon::KeyboardPacket packet; TRY(keyboard->read_typed(packet)); - os::println("%s key %d", packet.released ? "Released" : "Pressed", packet.key); if (!packet.released && packet.key == moon::K_Tab) debug(clients); + auto request = wind::Keyboard::decode_keyboard_event((moon::KeyCode)packet.key, packet.released); + if (g_windows.last().has_value()) + { + auto* window = g_windows.last().value(); + request.window = window->id; + os::IPC::send_async(window->client->conn, request); + } } for (usize i = 0; i < clients.size(); i++) {