wind+libui: Add support for keyboard events

This commit is contained in:
apio 2023-09-16 11:45:19 +02:00
parent 65b7518e50
commit d3cc4d109b
Signed by: apio
GPG Key ID: B8A7D06E42258954
16 changed files with 419 additions and 2 deletions

View File

@ -24,6 +24,7 @@ namespace ui
Result<EventResult> handle_mouse_leave() override;
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
Result<void> draw(Canvas& canvas) override;
private:

View File

@ -24,6 +24,7 @@ namespace ui
Result<EventResult> handle_mouse_leave() override;
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
Result<void> draw(Canvas& canvas) override;
private:

14
libui/include/ui/Key.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <moon/Keyboard.h>
namespace ui
{
enum Modifier
{
Mod_Shift = (1 << 0),
Mod_Alt = (1 << 1),
Mod_Super = (1 << 2),
Mod_AltGr = (1 << 3),
Mod_Ctrl = (1 << 4)
};
}

View File

@ -34,6 +34,7 @@ namespace ui
Result<EventResult> handle_mouse_leave() override;
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
Result<void> draw(Canvas& canvas) override;
@ -55,6 +56,7 @@ namespace ui
Result<EventResult> handle_mouse_leave() override;
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
Result<void> draw(Canvas& canvas) override;

View File

@ -13,6 +13,7 @@
#include <ui/Canvas.h>
#include <ui/Point.h>
#include <ui/Rect.h>
#include <ui/ipc/Client.h>
namespace ui
{
@ -50,6 +51,12 @@ namespace ui
return EventResult::DidNotHandle;
}
virtual Result<EventResult> handle_key_event(const ui::KeyEventRequest& request)
{
ignore(request);
return EventResult::DidNotHandle;
}
virtual Result<void> draw(Canvas& canvas);
void set_window(Window* window, Rect rect, Badge<Window>)

View File

@ -49,6 +49,7 @@ namespace ui
Result<ui::EventResult> handle_mouse_leave();
Result<ui::EventResult> handle_mouse_move(ui::Point position);
Result<ui::EventResult> handle_mouse_buttons(ui::Point position, int buttons);
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request);
int id() const
{

View File

@ -9,7 +9,9 @@
#pragma once
#include <os/IPC.h>
#include <ui/Key.h>
#include <ui/Point.h>
#include <ui/Rect.h>
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;
};
}

View File

@ -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!");
}
}

View File

@ -61,6 +61,11 @@ namespace ui
return m_child->handle_mouse_up(position, buttons);
}
Result<EventResult> Button::handle_key_event(const ui::KeyEventRequest& request)
{
return m_child->handle_key_event(request);
}
Result<void> Button::draw(Canvas& canvas)
{
return m_child->draw(canvas);

View File

@ -47,6 +47,11 @@ namespace ui
return ui::EventResult::DidNotHandle;
}
Result<EventResult> Container::handle_key_event(const ui::KeyEventRequest& request)
{
return m_widget->handle_key_event(request);
}
Result<void> 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,

View File

@ -64,6 +64,19 @@ namespace ui
return ui::EventResult::DidNotHandle;
}
Result<EventResult> 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<void> HorizontalLayout::draw(Canvas& canvas)
{
for (auto widget : m_widgets)
@ -159,6 +172,19 @@ namespace ui
return ui::EventResult::DidNotHandle;
}
Result<EventResult> 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<void> VerticalLayout::draw(Canvas& canvas)
{
for (auto widget : m_widgets)

View File

@ -114,4 +114,10 @@ namespace ui
m_old_mouse_buttons = buttons;
return result;
}
Result<ui::EventResult> Window::handle_key_event(const ui::KeyEventRequest& request)
{
if (!m_main_widget) return ui::EventResult::DidNotHandle;
return m_main_widget->handle_key_event(request);
}
}

View File

@ -8,6 +8,8 @@ set(SOURCES
Window.cpp
IPC.cpp
IPC.h
Keyboard.cpp
Keyboard.h
Client.h
)

305
wind/Keyboard.cpp Normal file
View File

@ -0,0 +1,305 @@
#include "Keyboard.h"
#include <luna/CType.h>
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;
}
}

10
wind/Keyboard.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <ui/ipc/Client.h>
namespace wind
{
namespace Keyboard
{
ui::KeyEventRequest decode_keyboard_event(moon::KeyCode code, bool released);
}
}

View File

@ -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<int> 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++)
{