diff --git a/Makefile b/Makefile index 0dc9917..c51352a 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ C_SOURCES = $(wildcard $(SRC_DIR)/core/*.c) \ $(wildcard $(SRC_DIR)/sys/*.c) \ $(wildcard $(SRC_DIR)/mem/*.c) \ $(wildcard $(SRC_DIR)/dev/*.c) \ + $(wildcard $(SRC_DIR)/input/*.c) \ $(wildcard $(SRC_DIR)/net/*.c) \ $(wildcard $(SRC_DIR)/net/nic/*.c) \ $(wildcard $(SRC_DIR)/fs/*.c) \ @@ -45,6 +46,7 @@ OBJ_FILES = $(patsubst $(SRC_DIR)/core/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_D $(patsubst $(SRC_DIR)/sys/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/sys/*.c)) \ $(patsubst $(SRC_DIR)/mem/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/mem/*.c)) \ $(patsubst $(SRC_DIR)/dev/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/dev/*.c)) \ + $(patsubst $(SRC_DIR)/input/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/input/*.c)) \ $(patsubst $(SRC_DIR)/net/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/net/*.c)) \ $(patsubst $(SRC_DIR)/net/nic/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/net/nic/*.c)) \ $(patsubst $(SRC_DIR)/fs/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/fs/*.c)) \ @@ -55,7 +57,7 @@ OBJ_FILES = $(patsubst $(SRC_DIR)/core/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_D CFLAGS = -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding \ -fno-stack-protector -fno-stack-check -fno-lto -fPIE \ -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone \ - -I$(SRC_DIR) -I$(SRC_DIR)/net/third_party/lwip -I$(SRC_DIR)/core -I$(SRC_DIR)/sys -I$(SRC_DIR)/mem -I$(SRC_DIR)/dev -I$(SRC_DIR)/net -I$(SRC_DIR)/net/nic -I$(SRC_DIR)/fs -I$(SRC_DIR)/wm + -I$(SRC_DIR) -I$(SRC_DIR)/net/third_party/lwip -I$(SRC_DIR)/core -I$(SRC_DIR)/sys -I$(SRC_DIR)/mem -I$(SRC_DIR)/dev -I$(SRC_DIR)/net -I$(SRC_DIR)/net/nic -I$(SRC_DIR)/fs -I$(SRC_DIR)/wm -I$(SRC_DIR)/input LDFLAGS = -m elf_x86_64 -nostdlib -static -pie --no-dynamic-linker \ -z text -z max-page-size=0x1000 -T linker.ld @@ -105,6 +107,10 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/mem/%.c | $(BUILD_DIR) limine-setup $(BUILD_DIR)/%.o: $(SRC_DIR)/dev/%.c | $(BUILD_DIR) limine-setup mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/%.o: $(SRC_DIR)/input/%.c | $(BUILD_DIR) limine-setup + mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.o: $(SRC_DIR)/net/%.c | $(BUILD_DIR) limine-setup mkdir -p $(dir $@) diff --git a/docs/architecture/input/keyboard.md b/docs/architecture/input/keyboard.md new file mode 100644 index 0000000..3cdc59b --- /dev/null +++ b/docs/architecture/input/keyboard.md @@ -0,0 +1,273 @@ +# Input Subsystem + +## Overview +The input subsystem is responsible for handling user input, primarily from the keyboard. + +It provides a structured pipeline that transforms low-level hardware signals into usable data for the kernel and higher-level components. This subsystem abstracts hardware-specific behavior and exposes a consistent interface to the rest of the operating system. + +--- + +## Scope +The `/input` directory focuses on keyboard input. It includes: + +- A keyboard driver responsible for handling hardware events +- A keycode layer used as an intermediate representation +- A keymap system that translates keycodes into characters + +--- + +## Design Principles + +- **Hardware abstraction** + Hardware-specific logic is isolated from higher-level components. + +- **Simplicity** + The input path is kept minimal and efficient, especially in interrupt context. + +- **Modularity** + Each stage of input processing is handled by a dedicated component. + +- **Extensibility** + The system is designed to support additional input devices and layouts in the future. + +--- + +## Directory Structure +``` +input/ +├── keyboard.c +├── keyboard.h +├── keycodes.h +├── keymap.c +├── keymap.h +``` + +--- + +## Input Processing Model + +Keyboard input is processed in three distinct stages: + +1. Raw scancodes are received from the hardware +2. Scancodes are converted into keycodes +3. Keycodes are translated into characters or control signals + +Each stage is handled independently to ensure clarity and maintainability. + +--- + +## Components + +### Keyboard Driver + +#### Overview +The keyboard driver interfaces directly with the keyboard hardware. It handles interrupts and processes raw input data from the controller. + +#### Responsibilities + +- Handle keyboard interrupts +- Read scancodes from the PS/2 controller +- Convert scancodes into keycodes +- Forward processed data to higher layers + +#### Behavior + +The driver operates in an interrupt-driven context. When a key event occurs, the hardware triggers an interrupt. The driver reads the corresponding scancode and processes it immediately. + +Because this code runs at a low level, it must be fast, predictable, and minimal. + +#### Integration + +The keyboard driver depends on: + +- The PS/2 controller driver for hardware communication +- The interrupt subsystem for event handling + +It provides output to: + +- The keycode system +- The keymap system + +#### Constraints + +- Must not block execution +- Must minimize processing time per interrupt +- Must correctly handle key press and key release events + +--- + +### Keycodes + +#### Overview +Keycodes define a hardware-independent representation of keyboard keys. + +They serve as an abstraction layer between raw scancodes and higher-level logic. + +#### Purpose + +The keycode system standardizes keyboard input by mapping all physical key events to a consistent set of identifiers. + +This allows the system to: + +- Remain independent from specific hardware implementations +- Simplify input handling logic +- Support multiple layouts and configurations + +#### Design + +Each key is represented by a unique constant, such as: + +- KEY_A +- KEY_ENTER +- KEY_SHIFT + +#### Role in the System + +Keycodes act as the intermediate layer between: + +- Hardware-level scancodes +- Character-level or command-level input + +#### Usage + +- Generated by the keyboard driver +- Consumed by the keymap system + +#### Extensibility + +The keycode system can be extended to support: + +- Additional keys (function keys, multimedia keys) +- Non-standard input devices +- Custom mappings + +--- + +### Keymap + +#### Overview +The keymap system translates keycodes into characters or control signals. + +It defines how physical key presses are interpreted based on layout and modifier state. + +#### Responsibilities + +- Convert keycodes into ASCII or equivalent representations +- Apply modifier logic such as Shift and Control +- Provide consistent character output + +#### Behavior + +The keymap takes a keycode as input and produces an output depending on: + +- The current keyboard layout +- Active modifier keys + +The same keycode may produce different results depending on modifier state. + + +#### Integration + +- Receives keycodes from the keyboard driver +- Outputs characters to the kernel or userland + +--- + +## Control Signals + +In addition to character generation, the input subsystem produces **control signals** representing non-printable keys and command-oriented input. + +These signals are derived from keycodes that do not map directly to ASCII characters. + +--- + +### Definition + +A control signal is an abstract representation of a key event used to trigger system-level behavior rather than text output. + +Typical control signals include: + +- Enter +- Backspace +- Escape +- Tab +- Arrow keys +- Function keys + +--- + +### Encoding + +Control signals may be represented in different ways depending on the layer: + +#### ASCII Control Characters (when applicable) + +Some keys map to standard ASCII control codes: + +- `ENTER` → `0x0A` (Line Feed) or `0x0D` (Carriage Return) +- `BACKSPACE` → `0x08` +- `TAB` → `0x09` +- `ESC` → `0x1B` + +These values are part of the ASCII control range (`0x00`–`0x1F`). + +--- + +#### Non-ASCII Keys + +Keys that do not belong to the ASCII set are typically handled as **extended keycodes** or **internal constants**: + +Examples: + +- Arrow keys +- Insert / Delete +- Home / End +- Function keys (F1–F12) + +------ +## Non-ASCII Characters + +Non-ASCII characters include any character outside the standard 7-bit ASCII range (`0x00`–`0x7F`). + +Examples: + +- Accented characters: `é`, `à`, `ç` +- Symbols: `€`, `£` +- Unicode characters from non-Latin scripts + +--- + +### Encoding Considerations + +The current system typically assumes ASCII output. However, supporting non-ASCII characters requires: + +- A wider character encoding (e.g. UTF-8) +- Extended keymaps capable of mapping key combinations to multi-byte sequences + +Example: + +- `'é'` in UTF-8 → `0xC3 0xA9` + +--- + +### Modifier and Layout Impact + +Non-ASCII characters are often produced through: + +- Keyboard layout differences (AZERTY vs QWERTY) +- Modifier combinations (Shift, AltGr) + +Example: + +- `AltGr + E` → `'€'` (depending on layout) +- `KEY_E` → `'e'` +- `KEY_E + SHIFT` → `'E'` + +--- + +### Usage + +- Control signals are used for command handling and system interaction +- Non-ASCII characters are used for text input and require proper encoding support + + diff --git a/src/core/main.c b/src/core/main.c index 2cc7d20..578f7e2 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -32,6 +32,7 @@ #include "sys/kernel_subsystem.h" #include "sys/module_manager.h" #include "sys/bootfs_state.h" +#include "input/keymap.h" extern void sysfs_init_subsystems(void); @@ -314,6 +315,7 @@ void kmain(void) { fat32_mkdir("/Library/images/gif"); fat32_mkdir("/Library/Fonts"); fat32_mkdir("/Library/DOOM"); + fat32_mkdir("/Library/conf"); fat32_mkdir("/Library/bsh"); fat32_mkdir("/docs"); fat32_mkdir("/root"); @@ -430,6 +432,9 @@ void kmain(void) { ps2_init(); asm("sti"); + keymap_init(); + serial_write("[INIT] Keymap initialized"); + lapic_init(); if (smp_request.response != NULL) { diff --git a/src/dev/ps2.c b/src/dev/ps2.c index 660ec37..ce575be 100644 --- a/src/dev/ps2.c +++ b/src/dev/ps2.c @@ -8,6 +8,8 @@ #include "lapic.h" #include "smp.h" #include +#include "input/keyboard.h" +#include "input/keymap.h" extern void serial_print(const char *s); extern void serial_print_hex(uint64_t n); @@ -20,28 +22,23 @@ uint64_t timer_handler(registers_t *regs) { kernel_ticks++; wm_timer_tick(); network_process_frames(); - + extern void k_beep_process(void); k_beep_process(); } - - outb(0x20, 0x20); + + outb(0x20, 0x20); extern uint64_t process_schedule(uint64_t current_rsp); uint64_t new_rsp = process_schedule((uint64_t)regs); - + if (smp_cpu_count() > 1) { lapic_send_ipi_all(); } - + return new_rsp; } // --- Keyboard --- -static bool shift_pressed = false; -static bool caps_lock_on = false; -bool ps2_ctrl_pressed = false; -static bool extended_scancode = false; - static void ps2_kbd_wait_write(void) { uint32_t timeout = 100000; while (timeout--) { @@ -51,95 +48,29 @@ static void ps2_kbd_wait_write(void) { static void ps2_update_leds(void) { uint8_t led_status = 0; - if (caps_lock_on) led_status |= 4; - + uint32_t mods = keyboard_get_modifiers(); + + if (mods & KB_MOD_CAPS) led_status |= 4; + if (mods & KB_MOD_NUM) led_status |= 2; + if (mods & KB_MOD_SCROLL) led_status |= 1; + ps2_kbd_wait_write(); outb(0x60, 0xED); ps2_kbd_wait_write(); outb(0x60, led_status); } -static char scancode_map[128] = { - 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', - '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', - 21, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, - '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', - 22, ' ', 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -static char scancode_map_shift[128] = { - 0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', - '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', - 21, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, - '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*', - 22, ' ', 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - uint64_t keyboard_handler(registers_t *regs) { uint8_t scancode = inb(0x60); - if (scancode == 0xE0) { - extended_scancode = true; - outb(0x20, 0x20); - return (uint64_t)regs; - } - - if (scancode == 0x1D) { - ps2_ctrl_pressed = true; - extended_scancode = false; - } else if (scancode == 0x9D) { - ps2_ctrl_pressed = false; - extended_scancode = false; - } - - - - if (scancode == 0x2A || scancode == 0x36) { // Shift Down - shift_pressed = true; - } else if (scancode == 0xAA || scancode == 0xB6) { // Shift Up - shift_pressed = false; - } else if (scancode == 0x3A) { // Caps Lock Down - caps_lock_on = !caps_lock_on; - ps2_update_leds(); - } else if (!(scancode & 0x80)) { // Key Press - if (extended_scancode) { - extended_scancode = false; - switch (scancode) { - case 0x48: wm_handle_key(17, true); break; // Up arrow - case 0x50: wm_handle_key(18, true); break; // Down arrow - case 0x4B: wm_handle_key(19, true); break; // Left arrow - case 0x4D: wm_handle_key(20, true); break; // Right arrow - } - } else { - char c = shift_pressed ? scancode_map_shift[scancode] : scancode_map[scancode]; - if (c) { - if (caps_lock_on) { - if (c >= 'a' && c <= 'z') c -= 32; - else if (c >= 'A' && c <= 'Z') c += 32; - } - wm_handle_key(c, true); - } - } - } else if (scancode & 0x80) { // Key release - if (extended_scancode) { - extended_scancode = false; - switch (scancode & 0x7F) { // Strip the release bit - case 0x48: wm_handle_key(17, false); break; // Up arrow - case 0x50: wm_handle_key(18, false); break; // Down arrow - case 0x4B: wm_handle_key(19, false); break; // Left arrow - case 0x4D: wm_handle_key(20, false); break; // Right arrow - } - } else { - uint8_t base_scancode = scancode & 0x7F; - char c = shift_pressed ? scancode_map_shift[base_scancode] : scancode_map[base_scancode]; - if (c) { - if (caps_lock_on) { - if (c >= 'a' && c <= 'z') c -= 32; - else if (c >= 'A' && c <= 'Z') c += 32; - } - wm_handle_key(c, false); - } + keyboard_event_t ev; + if (keyboard_handle_set1_scancode(scancode, &ev)) { + // Update LEDs if a lock key state changed + if (ev.keycode == KEY_CAPS_LOCK || ev.keycode == KEY_NUM_LOCK || ev.keycode == KEY_SCROLL_LOCK) { + if (ev.pressed) ps2_update_leds(); } + + wm_handle_key_event(ev.keycode, ev.codepoint, ev.mods, ev.pressed); } outb(0x20, 0x20); // EOI @@ -178,11 +109,11 @@ uint8_t mouse_read(void) { void mouse_init(void) { uint8_t status; - + // Enable Aux Device mouse_wait(0); outb(0x64, 0xA8); - + // Enable Interrupts mouse_wait(0); outb(0x64, 0x20); @@ -192,7 +123,7 @@ void mouse_init(void) { outb(0x64, 0x60); mouse_wait(0); outb(0x60, status); - + // Set Defaults mouse_write(0xF6); mouse_read(); @@ -201,12 +132,12 @@ void mouse_init(void) { mouse_write(0xF3); mouse_read(); mouse_write(200); mouse_read(); mouse_write(0xF3); mouse_read(); mouse_write(100); mouse_read(); mouse_write(0xF3); mouse_read(); mouse_write(80); mouse_read(); - + mouse_write(0xF2); mouse_read(); uint8_t id = mouse_read(); if (id == 3) mouse_has_wheel = true; - + // Enable Streaming mouse_write(0xF4); mouse_read(); @@ -217,11 +148,11 @@ uint64_t mouse_handler(registers_t *regs) { if (!(status & 0x20)) { outb(0x20, 0x20); outb(0xA0, 0x20); - return (uint64_t)regs; + return (uint64_t)regs; } uint8_t b = inb(0x60); - + if (mouse_cycle == 0) { if ((b & 0x08) == 0) { // Out of sync @@ -257,9 +188,12 @@ uint64_t mouse_handler(registers_t *regs) { } void ps2_init(void) { + keymap_init(); + keyboard_init(); mouse_init(); + ps2_update_leds(); } bool ps2_shift_pressed(void) { - return shift_pressed; -} + return keyboard_shift_pressed(); +} \ No newline at end of file diff --git a/src/input/keyboard.c b/src/input/keyboard.c new file mode 100644 index 0000000..a26d60f --- /dev/null +++ b/src/input/keyboard.c @@ -0,0 +1,214 @@ +#include "keyboard.h" +#include "keymap.h" + +typedef struct { + bool e0_prefix; + bool left_shift; + bool right_shift; + bool left_ctrl; + bool right_ctrl; + bool left_alt; + bool right_alt; + bool caps_lock; + bool num_lock; + bool scroll_lock; + uint32_t dead_key; +} keyboard_state_t; + +static keyboard_state_t g_kb; + +// table of scancode to keycode for set 1 (without E0 prefix) +static const uint16_t set1_base[128] = { + [0x01] = KEY_ESC, + [0x02] = KEY_1, [0x03] = KEY_2, [0x04] = KEY_3, [0x05] = KEY_4, + [0x06] = KEY_5, [0x07] = KEY_6, [0x08] = KEY_7, [0x09] = KEY_8, + [0x0A] = KEY_9, [0x0B] = KEY_0, + [0x0C] = KEY_MINUS, [0x0D] = KEY_EQUAL, + [0x0E] = KEY_BACKSPACE, + [0x0F] = KEY_TAB, + + [0x10] = KEY_Q, [0x11] = KEY_W, [0x12] = KEY_E, [0x13] = KEY_R, + [0x14] = KEY_T, [0x15] = KEY_Y, [0x16] = KEY_U, [0x17] = KEY_I, + [0x18] = KEY_O, [0x19] = KEY_P, + [0x1A] = KEY_LBRACKET, [0x1B] = KEY_RBRACKET, + [0x1C] = KEY_ENTER, + [0x1D] = KEY_LEFT_CTRL, + + [0x1E] = KEY_A, [0x1F] = KEY_S, [0x20] = KEY_D, [0x21] = KEY_F, + [0x22] = KEY_G, [0x23] = KEY_H, [0x24] = KEY_J, [0x25] = KEY_K, + [0x26] = KEY_L, + [0x27] = KEY_SEMICOLON, [0x28] = KEY_APOSTROPHE, [0x29] = KEY_GRAVE, + + [0x2A] = KEY_LEFT_SHIFT, + [0x2B] = KEY_BACKSLASH, + [0x2C] = KEY_Z, [0x2D] = KEY_X, [0x2E] = KEY_C, [0x2F] = KEY_V, + [0x30] = KEY_B, [0x31] = KEY_N, [0x32] = KEY_M, + [0x33] = KEY_COMMA, [0x34] = KEY_DOT, [0x35] = KEY_SLASH, + [0x36] = KEY_RIGHT_SHIFT, + + [0x37] = KEY_KP_STAR, + [0x38] = KEY_LEFT_ALT, + [0x39] = KEY_SPACE, + [0x3A] = KEY_CAPS_LOCK, + + [0x3B] = KEY_F1, [0x3C] = KEY_F2, [0x3D] = KEY_F3, [0x3E] = KEY_F4, + [0x3F] = KEY_F5, [0x40] = KEY_F6, [0x41] = KEY_F7, [0x42] = KEY_F8, + [0x43] = KEY_F9, [0x44] = KEY_F10, + [0x45] = KEY_NUM_LOCK, + [0x46] = KEY_SCROLL_LOCK, + + [0x47] = KEY_KP_7, [0x48] = KEY_KP_8, [0x49] = KEY_KP_9, + [0x4A] = KEY_KP_MINUS, + [0x4B] = KEY_KP_4, [0x4C] = KEY_KP_5, [0x4D] = KEY_KP_6, + [0x4E] = KEY_KP_PLUS, + [0x4F] = KEY_KP_1, [0x50] = KEY_KP_2, [0x51] = KEY_KP_3, + [0x52] = KEY_KP_0, [0x53] = KEY_KP_DOT, + + [0x57] = KEY_F11, + [0x58] = KEY_F12, +}; + +// table of scancode to keycode for set 1 with E0 prefix +static const uint16_t set1_ext[128] = { + [0x1C] = KEY_KP_ENTER, + [0x1D] = KEY_RIGHT_CTRL, + [0x35] = KEY_KP_SLASH, + [0x38] = KEY_RIGHT_ALT, + + [0x47] = KEY_HOME, + [0x48] = KEY_ARROW_UP, + [0x49] = KEY_PAGE_UP, + [0x4B] = KEY_ARROW_LEFT, + [0x4D] = KEY_ARROW_RIGHT, + [0x4F] = KEY_END, + [0x50] = KEY_ARROW_DOWN, + [0x51] = KEY_PAGE_DOWN, + [0x52] = KEY_INSERT, + [0x53] = KEY_DELETE, +}; + +void keyboard_init(void) { + g_kb.e0_prefix = false; + g_kb.left_shift = false; + g_kb.right_shift = false; + g_kb.left_ctrl = false; + g_kb.right_ctrl = false; + g_kb.left_alt = false; + g_kb.right_alt = false; + g_kb.caps_lock = false; + g_kb.num_lock = false; + g_kb.scroll_lock = false; + g_kb.dead_key = 0; +} + +// Convert a set 1 scancode (with optional E0 prefix) to a keycode. Returns KEY_NONE if the scancode is invalid or unmapped. +uint16_t keyboard_keycode_from_set1(uint8_t scancode, bool extended) { + if (scancode >= 128) return KEY_NONE; + return extended ? set1_ext[scancode] : set1_base[scancode]; +} + +// Update the state of modifier keys based on the keycode and press/release event. +static void update_mod_state(uint16_t keycode, bool pressed) { + switch (keycode) { + case KEY_LEFT_SHIFT: g_kb.left_shift = pressed; break; + case KEY_RIGHT_SHIFT: g_kb.right_shift = pressed; break; + case KEY_LEFT_CTRL: g_kb.left_ctrl = pressed; break; + case KEY_RIGHT_CTRL: g_kb.right_ctrl = pressed; break; + case KEY_LEFT_ALT: g_kb.left_alt = pressed; break; + case KEY_RIGHT_ALT: g_kb.right_alt = pressed; break; + + case KEY_CAPS_LOCK: + if (pressed) g_kb.caps_lock = !g_kb.caps_lock; + break; + case KEY_NUM_LOCK: + if (pressed) g_kb.num_lock = !g_kb.num_lock; + break; + case KEY_SCROLL_LOCK: + if (pressed) g_kb.scroll_lock = !g_kb.scroll_lock; + break; + default: + break; + } +} + +// Get the current state of modifier keys as a bitmask. +uint32_t keyboard_get_modifiers(void) { + uint32_t mods = 0; + if (g_kb.left_shift || g_kb.right_shift) mods |= KB_MOD_SHIFT; + if (g_kb.left_ctrl || g_kb.right_ctrl) mods |= KB_MOD_CTRL; + if (g_kb.left_alt) mods |= KB_MOD_ALT; + if (g_kb.right_alt) mods |= KB_MOD_ALTGR; + if (g_kb.caps_lock) mods |= KB_MOD_CAPS; + if (g_kb.num_lock) mods |= KB_MOD_NUM; + if (g_kb.scroll_lock) mods |= KB_MOD_SCROLL; + return mods; +} + +// Helper functions to check specific modifiers +bool keyboard_shift_pressed(void) { + return (keyboard_get_modifiers() & KB_MOD_SHIFT) != 0; +} + +bool keyboard_ctrl_pressed(void) { + return (keyboard_get_modifiers() & KB_MOD_CTRL) != 0; +} + +bool keyboard_handle_set1_scancode(uint8_t scancode, keyboard_event_t *ev) { + if (!ev) return false; + + ev->keycode = KEY_NONE; + ev->codepoint = 0; + ev->mods = keyboard_get_modifiers(); + ev->pressed = false; + ev->repeat = false; + ev->is_text = false; + + if (scancode == 0xE0) { + g_kb.e0_prefix = true; + return false; + } + + if (scancode == 0xE1) { + g_kb.e0_prefix = false; + return false; // ignore Pause/Break's multi-byte sequence for simplicity + } + + bool release = (scancode & 0x80) != 0; + uint8_t base = (uint8_t)(scancode & 0x7F); + + uint16_t keycode = keyboard_keycode_from_set1(base, g_kb.e0_prefix); + g_kb.e0_prefix = false; + + if (keycode == KEY_NONE) return false; + + bool pressed = !release; + update_mod_state(keycode, pressed); + + ev->keycode = keycode; + ev->pressed = pressed; + ev->mods = keyboard_get_modifiers(); + + if (!pressed) { + return true; + } + + keymap_result_t r = keymap_translate_keycode(keycode, ev->mods); + + if (r.is_dead) { + g_kb.dead_key = r.codepoint; + return true; + } + + if (r.is_text) { + uint32_t cp = r.codepoint; + if (g_kb.dead_key != 0) { + uint32_t composed = keymap_compose(g_kb.dead_key, cp); + if (composed != 0) cp = composed; + g_kb.dead_key = 0; + } + ev->codepoint = cp; + ev->is_text = true; + } + + return true; +} \ No newline at end of file diff --git a/src/input/keyboard.h b/src/input/keyboard.h new file mode 100644 index 0000000..d9ecd79 --- /dev/null +++ b/src/input/keyboard.h @@ -0,0 +1,26 @@ +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include +#include +#include "keycodes.h" + +typedef struct { + uint16_t keycode; + uint32_t codepoint; + uint32_t mods; + bool pressed; + bool repeat; + bool is_text; +} keyboard_event_t; + +void keyboard_init(void); +bool keyboard_handle_set1_scancode(uint8_t scancode, keyboard_event_t *ev); + +uint16_t keyboard_keycode_from_set1(uint8_t scancode, bool extended); + +bool keyboard_shift_pressed(void); +bool keyboard_ctrl_pressed(void); +uint32_t keyboard_get_modifiers(void); + +#endif \ No newline at end of file diff --git a/src/input/keycodes.h b/src/input/keycodes.h new file mode 100644 index 0000000..2969c5f --- /dev/null +++ b/src/input/keycodes.h @@ -0,0 +1,84 @@ +#ifndef KEYCODES_H +#define KEYCODES_H + +#include + +typedef enum { + KEY_NONE = 0, + + KEY_ESC, + + KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, + KEY_MINUS, + KEY_EQUAL, + KEY_BACKSPACE, + KEY_TAB, + + KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, + KEY_LBRACKET, + KEY_RBRACKET, + KEY_ENTER, + + KEY_LEFT_CTRL, + KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, + KEY_SEMICOLON, + KEY_APOSTROPHE, + KEY_GRAVE, + + KEY_LEFT_SHIFT, + KEY_BACKSLASH, + KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, + KEY_COMMA, + KEY_DOT, + KEY_SLASH, + KEY_RIGHT_SHIFT, + + KEY_KP_STAR, + KEY_LEFT_ALT, + KEY_SPACE, + KEY_CAPS_LOCK, + + KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, + KEY_F7, KEY_F8, KEY_F9, KEY_F10, + KEY_NUM_LOCK, + KEY_SCROLL_LOCK, + + KEY_KP_7, KEY_KP_8, KEY_KP_9, + KEY_KP_MINUS, + KEY_KP_4, KEY_KP_5, KEY_KP_6, + KEY_KP_PLUS, + KEY_KP_1, KEY_KP_2, KEY_KP_3, + KEY_KP_0, + KEY_KP_DOT, + + KEY_F11, + KEY_F12, + + KEY_KP_ENTER, + KEY_RIGHT_CTRL, + KEY_KP_SLASH, + KEY_RIGHT_ALT, + + KEY_HOME, + KEY_ARROW_UP, + KEY_PAGE_UP, + KEY_ARROW_LEFT, + KEY_ARROW_RIGHT, + KEY_END, + KEY_ARROW_DOWN, + KEY_PAGE_DOWN, + KEY_INSERT, + KEY_DELETE, + + KEY_MAX +} keycode_t; + +#define KB_MOD_SHIFT 0x0001u +#define KB_MOD_CTRL 0x0002u +#define KB_MOD_ALT 0x0004u +#define KB_MOD_ALTGR 0x0008u +#define KB_MOD_CAPS 0x0010u +#define KB_MOD_NUM 0x0020u +#define KB_MOD_SCROLL 0x0040u + +#endif \ No newline at end of file diff --git a/src/input/keymap.c b/src/input/keymap.c new file mode 100644 index 0000000..d5081ac --- /dev/null +++ b/src/input/keymap.c @@ -0,0 +1,447 @@ +#include "keymap.h" + +#define DEAD_NORMAL 0x01 +#define DEAD_SHIFT 0x02 +#define DEAD_ALTGR 0x04 +#define DEAD_SHIFT_ALTGR 0x08 + +typedef struct { + const char *name; + keymap_entry_t entries[KEY_MAX]; +} keyboard_layout_t; + +typedef struct { + uint32_t dead; + uint32_t base; + uint32_t result; +} compose_entry_t; + +/* +HOW TO ADD A NEW LAYOUT: +1. Add a new entry to the keymap_id_t enum in keymap.h +2. Create a new keyboard_layout_t instance in keymap.c, filling the entries array with the appropriate codepoints for each keycode and modifier combination. Use 0 for unused combinations. +3. Add the new layout to the g_layouts array in keymap.c +4. (Optional) If your layout has dead keys, add the appropriate entries to the g_compose_table array in keymap.c, defining how dead keys combine with base characters to produce composed characters. +5. Add the layout to /src/userland/gui/settings.c in init_settings_widgets() in the *keyboard_opts[] array and increment the count in widget_dropdown_init for drop_keyboard. +*/ + +// QWERTY LAYOUT US (DEFAULT) +static const keyboard_layout_t layout_qwerty = { + "QWERTY (US)", + .entries = { + // [KEYCODE] = {normal, shift, altgr, shift_altgr, dead_mask, alpha} + [KEY_1] = {'1', '!', 0, 0, 0, false}, + [KEY_2] = {'2', '@', 0, 0, 0, false}, + [KEY_3] = {'3', '#', 0, 0, 0, false}, + [KEY_4] = {'4', '$', 0, 0, 0, false}, + [KEY_5] = {'5', '%', 0, 0, 0, false}, + [KEY_6] = {'6', '^', 0, 0, 0, false}, + [KEY_7] = {'7', '&', 0, 0, 0, false}, + [KEY_8] = {'8', '*', 0, 0, 0, false}, + [KEY_9] = {'9', '(', 0, 0, 0, false}, + [KEY_0] = {'0', ')', 0, 0, 0, false}, + [KEY_MINUS] = {'-', '_', 0, 0, 0, false}, + [KEY_EQUAL] = {'=', '+', 0, 0, 0, false}, + + [KEY_Q] = {'q', 'Q', 0, 0, 0, true}, + [KEY_W] = {'w', 'W', 0, 0, 0, true}, + [KEY_E] = {'e', 'E', 0, 0, 0, true}, + [KEY_R] = {'r', 'R', 0, 0, 0, true}, + [KEY_T] = {'t', 'T', 0, 0, 0, true}, + [KEY_Y] = {'y', 'Y', 0, 0, 0, true}, + [KEY_U] = {'u', 'U', 0, 0, 0, true}, + [KEY_I] = {'i', 'I', 0, 0, 0, true}, + [KEY_O] = {'o', 'O', 0, 0, 0, true}, + [KEY_P] = {'p', 'P', 0, 0, 0, true}, + [KEY_LBRACKET] = {'[', '{', 0, 0, 0, false}, + [KEY_RBRACKET] = {']', '}', 0, 0, 0, false}, + + [KEY_A] = {'a', 'A', 0, 0, 0, true}, + [KEY_S] = {'s', 'S', 0, 0, 0, true}, + [KEY_D] = {'d', 'D', 0, 0, 0, true}, + [KEY_F] = {'f', 'F', 0, 0, 0, true}, + [KEY_G] = {'g', 'G', 0, 0, 0, true}, + [KEY_H] = {'h', 'H', 0, 0, 0, true}, + [KEY_J] = {'j', 'J', 0, 0, 0, true}, + [KEY_K] = {'k', 'K', 0, 0, 0, true}, + [KEY_L] = {'l', 'L', 0, 0, 0, true}, + [KEY_SEMICOLON] = {';', ':', 0, 0, 0, false}, + [KEY_APOSTROPHE] = {'\'', '"', 0, 0, 0, false}, + [KEY_GRAVE] = {'`', '~', 0, 0, 0, false}, + + [KEY_BACKSLASH] = {'\\', '|', 0, 0, 0, false}, + [KEY_Z] = {'z', 'Z', 0, 0, 0, true}, + [KEY_X] = {'x', 'X', 0, 0, 0, true}, + [KEY_C] = {'c', 'C', 0, 0, 0, true}, + [KEY_V] = {'v', 'V', 0, 0, 0, true}, + [KEY_B] = {'b', 'B', 0, 0, 0, true}, + [KEY_N] = {'n', 'N', 0, 0, 0, true}, + [KEY_M] = {'m', 'M', 0, 0, 0, true}, + [KEY_COMMA] = {',', '<', 0, 0, 0, false}, + [KEY_DOT] = {'.', '>', 0, 0, 0, false}, + [KEY_SLASH] = {'/', '?', 0, 0, 0, false}, + + [KEY_SPACE] = {' ', ' ', 0, 0, 0, false}, + [KEY_KP_SLASH] = {'/', '/', 0, 0, 0, false}, + [KEY_KP_STAR] = {'*', '*', 0, 0, 0, false}, + [KEY_KP_MINUS] = {'-', '-', 0, 0, 0, false}, + [KEY_KP_PLUS] = {'+', '+', 0, 0, 0, false}, + [KEY_KP_DOT] = {'.', '.', 0, 0, 0, false}, + [KEY_KP_0] = {'0', '0', 0, 0, 0, false}, + [KEY_KP_1] = {'1', '1', 0, 0, 0, false}, + [KEY_KP_2] = {'2', '2', 0, 0, 0, false}, + [KEY_KP_3] = {'3', '3', 0, 0, 0, false}, + [KEY_KP_4] = {'4', '4', 0, 0, 0, false}, + [KEY_KP_5] = {'5', '5', 0, 0, 0, false}, + [KEY_KP_6] = {'6', '6', 0, 0, 0, false}, + [KEY_KP_7] = {'7', '7', 0, 0, 0, false}, + [KEY_KP_8] = {'8', '8', 0, 0, 0, false}, + [KEY_KP_9] = {'9', '9', 0, 0, 0, false}, + } +}; + +// AZERTY LAYOUT FR +static const keyboard_layout_t layout_azerty = { + "AZERTY (FR)", + .entries = { + [KEY_1] = { '&', '1', 0, 0, 0, false }, + [KEY_2] = { 0x00E9, '2', '~', 0, 0, false }, // é / 2 / ~ + [KEY_3] = { '"', '3', '#', 0, 0, false }, + [KEY_4] = { '\'', '4', '{', 0, 0, false }, + [KEY_5] = { '(', '5', '[', 0, 0, false }, + [KEY_6] = { '-', '6', '|', 0, 0, false }, + [KEY_7] = { 0x00E8, '7', '`', 0, 0, false }, // è / 7 / ` + [KEY_8] = { '_', '8', '\\', 0, 0, false }, + [KEY_9] = { 0x00E7, '9', '^', 0, 0, false }, // ç / 9 / ^ + [KEY_0] = { 0x00E0, '0', '@', 0, 0, false }, // à / 0 / @ + [KEY_MINUS] = { ')', 0x00B0, ']', 0, 0, false }, // ) / ° + [KEY_EQUAL] = { '=', '+', '}', 0, 0, false }, + + [KEY_Q] = { 'a', 'A', 0, 0, 0, true }, + [KEY_W] = { 'z', 'Z', 0, 0, 0, true }, + [KEY_E] = { 'e', 'E', 0x20AC, 0, 0, true }, // € + [KEY_R] = { 'r', 'R', 0, 0, 0, true }, + [KEY_T] = { 't', 'T', 0, 0, 0, true }, + [KEY_Y] = { 'y', 'Y', 0, 0, 0, true }, + [KEY_U] = { 'u', 'U', 0, 0, 0, true }, + [KEY_I] = { 'i', 'I', 0, 0, 0, true }, + [KEY_O] = { 'o', 'O', 0, 0, 0, true }, + [KEY_P] = { 'p', 'P', 0, 0, 0, true }, + [KEY_LBRACKET] = { '^', 0x00A8, 0, 0, DEAD_NORMAL | DEAD_SHIFT, false }, // ^ / ¨ + [KEY_RBRACKET] = { '$', 0x00A3, 0, 0, 0, false }, // £ + + [KEY_A] = { 'q', 'Q', 0, 0, 0, true }, + [KEY_S] = { 's', 'S', 0, 0, 0, true }, + [KEY_D] = { 'd', 'D', 0, 0, 0, true }, + [KEY_F] = { 'f', 'F', 0, 0, 0, true }, + [KEY_G] = { 'g', 'G', 0, 0, 0, true }, + [KEY_H] = { 'h', 'H', 0, 0, 0, true }, + [KEY_J] = { 'j', 'J', 0, 0, 0, true }, + [KEY_K] = { 'k', 'K', 0, 0, 0, true }, + [KEY_L] = { 'l', 'L', 0, 0, 0, true }, + [KEY_SEMICOLON] = { 'm', 'M', 0, 0, 0, true }, + [KEY_APOSTROPHE] = { 0x00F9, '%', 0, 0, 0, false }, // ù / % + [KEY_GRAVE] = { 0x00B2, 0, 0, 0, 0, false }, // ² + + [KEY_BACKSLASH] = { '*', 0x00B5, 0, 0, 0, false }, // * / µ + [KEY_Z] = { 'w', 'W', 0, 0, 0, true }, + [KEY_X] = { 'x', 'X', 0, 0, 0, true }, + [KEY_C] = { 'c', 'C', 0, 0, 0, true }, + [KEY_V] = { 'v', 'V', 0, 0, 0, true }, + [KEY_B] = { 'b', 'B', 0, 0, 0, true }, + [KEY_N] = { 'n', 'N', 0, 0, 0, true }, + [KEY_M] = { ',', '?', 0, 0, 0, false }, + [KEY_COMMA] = { ';', '.', 0, 0, 0, false }, + [KEY_DOT] = { ':', '/', 0, 0, 0, false }, + [KEY_SLASH] = { '!', 0x00A7, 0, 0, 0, false }, + + [KEY_SPACE] = { ' ', ' ', 0, 0, 0, false }, + + [KEY_KP_SLASH] = {'/', '/', 0, 0, 0, false}, + [KEY_KP_STAR] = {'*', '*', 0, 0, 0, false}, + [KEY_KP_MINUS] = {'-', '-', 0, 0, 0, false}, + [KEY_KP_PLUS] = {'+', '+', 0, 0, 0, false}, + [KEY_KP_DOT] = {'.', '.', 0, 0, 0, false}, + [KEY_KP_0] = {'0', '0', 0, 0, 0, false}, + [KEY_KP_1] = {'1', '1', 0, 0, 0, false}, + [KEY_KP_2] = {'2', '2', 0, 0, 0, false}, + [KEY_KP_3] = {'3', '3', 0, 0, 0, false}, + [KEY_KP_4] = {'4', '4', 0, 0, 0, false}, + [KEY_KP_5] = {'5', '5', 0, 0, 0, false}, + [KEY_KP_6] = {'6', '6', 0, 0, 0, false}, + [KEY_KP_7] = {'7', '7', 0, 0, 0, false}, + [KEY_KP_8] = {'8', '8', 0, 0, 0, false}, + [KEY_KP_9] = {'9', '9', 0, 0, 0, false}, + } +}; + +static const keyboard_layout_t layout_qwertz = { + "QWERTZ (DE)", + .entries = { + [KEY_1] = { '1', '!', 0, 0, 0, false }, + [KEY_2] = { '2', '"', 0x00B2, 0, 0, false }, // ² + [KEY_3] = { '3', 0x00A7, 0x00B3, 0, 0, false }, // § ³ + [KEY_4] = { '4', '$', 0, 0, 0, false }, + [KEY_5] = { '5', '%', 0, 0, 0, false }, + [KEY_6] = { '6', '&', 0, 0, 0, false }, + [KEY_7] = { '7', '/', '{', 0, 0, false }, + [KEY_8] = { '8', '(', '[', 0, 0, false }, + [KEY_9] = { '9', ')', ']', 0, 0, false }, + [KEY_0] = { '0', '=', '}', 0, 0, false }, + [KEY_MINUS] = { 0x00DF, '?', '\\', 0, 0, false }, // ß + [KEY_EQUAL] = { 0x00B4, '`', 0, 0, DEAD_NORMAL, false }, // ´ dead + + [KEY_Q] = { 'q', 'Q', '@', 0, 0, true }, + [KEY_W] = { 'w', 'W', 0, 0, 0, true }, + [KEY_E] = { 'e', 'E', 0x20AC, 0, 0, true }, // € + [KEY_R] = { 'r', 'R', 0, 0, 0, true }, + [KEY_T] = { 't', 'T', 0, 0, 0, true }, + [KEY_Y] = { 'z', 'Z', 0, 0, 0, true }, + [KEY_U] = { 'u', 'U', 0, 0, 0, true }, + [KEY_I] = { 'i', 'I', 0, 0, 0, true }, + [KEY_O] = { 'o', 'O', 0, 0, 0, true }, + [KEY_P] = { 'p', 'P', 0, 0, 0, true }, + [KEY_LBRACKET] = { 0x00FC, 0x00DC, 0, 0, 0, true }, // ü + [KEY_RBRACKET] = { '+', '*', '~', 0, 0, false }, + + [KEY_A] = { 'a', 'A', 0, 0, 0, true }, + [KEY_S] = { 's', 'S', 0, 0, 0, true }, + [KEY_D] = { 'd', 'D', 0, 0, 0, true }, + [KEY_F] = { 'f', 'F', 0, 0, 0, true }, + [KEY_G] = { 'g', 'G', 0, 0, 0, true }, + [KEY_H] = { 'h', 'H', 0, 0, 0, true }, + [KEY_J] = { 'j', 'J', 0, 0, 0, true }, + [KEY_K] = { 'k', 'K', 0, 0, 0, true }, + [KEY_L] = { 'l', 'L', 0, 0, 0, true }, + [KEY_SEMICOLON] = { 0x00F6, 0x00D6, 0, 0, 0, true }, // ö + [KEY_APOSTROPHE] = { 0x00E4, 0x00C4, 0, 0, 0, true }, // ä + [KEY_GRAVE] = { '^', 0x00B0, 0, 0, DEAD_NORMAL, false }, // ^ dead + + [KEY_BACKSLASH] = { '#', '\'', 0, 0, 0, false }, + [KEY_Z] = { 'y', 'Y', 0, 0, 0, true }, + [KEY_X] = { 'x', 'X', 0, 0, 0, true }, + [KEY_C] = { 'c', 'C', 0, 0, 0, true }, + [KEY_V] = { 'v', 'V', 0, 0, 0, true }, + [KEY_B] = { 'b', 'B', 0, 0, 0, true }, + [KEY_N] = { 'n', 'N', 0, 0, 0, true }, + [KEY_M] = { 'm', 'M', 0, 0, 0, true }, + [KEY_COMMA] = { ',', ';', 0, 0, 0, false }, + [KEY_DOT] = { '.', ':', 0, 0, 0, false }, + [KEY_SLASH] = { '-', '_', 0, 0, 0, false }, + + [KEY_SPACE] = { ' ', ' ', 0, 0, 0, false }, + + [KEY_KP_SLASH] = {'/', '/', 0, 0, 0, false}, + [KEY_KP_STAR] = {'*', '*', 0, 0, 0, false}, + [KEY_KP_MINUS] = {'-', '-', 0, 0, 0, false}, + [KEY_KP_PLUS] = {'+', '+', 0, 0, 0, false}, + [KEY_KP_DOT] = {'.', '.', 0, 0, 0, false}, + [KEY_KP_0] = {'0', '0', 0, 0, 0, false}, + [KEY_KP_1] = {'1', '1', 0, 0, 0, false}, + [KEY_KP_2] = {'2', '2', 0, 0, 0, false}, + [KEY_KP_3] = {'3', '3', 0, 0, 0, false}, + [KEY_KP_4] = {'4', '4', 0, 0, 0, false}, + [KEY_KP_5] = {'5', '5', 0, 0, 0, false}, + [KEY_KP_6] = {'6', '6', 0, 0, 0, false}, + [KEY_KP_7] = {'7', '7', 0, 0, 0, false}, + [KEY_KP_8] = {'8', '8', 0, 0, 0, false}, + [KEY_KP_9] = {'9', '9', 0, 0, 0, false}, + } +}; + +static const keyboard_layout_t layout_dvorak = { + "DVORAK", + .entries = { + [KEY_1] = { '1', '!', 0, 0, 0, false }, + [KEY_2] = { '2', '@', 0, 0, 0, false }, + [KEY_3] = { '3', '#', 0, 0, 0, false }, + [KEY_4] = { '4', '$', 0, 0, 0, false }, + [KEY_5] = { '5', '%', 0, 0, 0, false }, + [KEY_6] = { '6', '^', 0, 0, 0, false }, + [KEY_7] = { '7', '&', 0, 0, 0, false }, + [KEY_8] = { '8', '*', 0, 0, 0, false }, + [KEY_9] = { '9', '(', 0, 0, 0, false }, + [KEY_0] = { '0', ')', 0, 0, 0, false }, + [KEY_MINUS] = { '[', '{', 0, 0, 0, false }, + [KEY_EQUAL] = { ']', '}', 0, 0, 0, false }, + + [KEY_Q] = { '\'', '"', 0, 0, 0, false }, + [KEY_W] = { ',', '<', 0, 0, 0, false }, + [KEY_E] = { '.', '>', 0, 0, 0, false }, + [KEY_R] = { 'p', 'P', 0, 0, 0, true }, + [KEY_T] = { 'y', 'Y', 0, 0, 0, true }, + [KEY_Y] = { 'f', 'F', 0, 0, 0, true }, + [KEY_U] = { 'g', 'G', 0, 0, 0, true }, + [KEY_I] = { 'c', 'C', 0, 0, 0, true }, + [KEY_O] = { 'r', 'R', 0, 0, 0, true }, + [KEY_P] = { 'l', 'L', 0, 0, 0, true }, + [KEY_LBRACKET] = { '/', '?', 0, 0, 0, false }, + [KEY_RBRACKET] = { '=', '+', 0, 0, 0, false }, + + [KEY_A] = { 'a', 'A', 0, 0, 0, true }, + [KEY_S] = { 'o', 'O', 0, 0, 0, true }, + [KEY_D] = { 'e', 'E', 0, 0, 0, true }, + [KEY_F] = { 'u', 'U', 0, 0, 0, true }, + [KEY_G] = { 'i', 'I', 0, 0, 0, true }, + [KEY_H] = { 'd', 'D', 0, 0, 0, true }, + [KEY_J] = { 'h', 'H', 0, 0, 0, true }, + [KEY_K] = { 't', 'T', 0, 0, 0, true }, + [KEY_L] = { 'n', 'N', 0, 0, 0, true }, + [KEY_SEMICOLON] = { 's', 'S', 0, 0, 0, true }, + [KEY_APOSTROPHE] = { '-', '_', 0, 0, 0, false }, + [KEY_GRAVE] = { '`', '~', 0, 0, 0, false }, + + [KEY_BACKSLASH] = { '\\', '|', 0, 0, 0, false }, + [KEY_Z] = { ';', ':', 0, 0, 0, false }, + [KEY_X] = { 'q', 'Q', 0, 0, 0, true }, + [KEY_C] = { 'j', 'J', 0, 0, 0, true }, + [KEY_V] = { 'k', 'K', 0, 0, 0, true }, + [KEY_B] = { 'x', 'X', 0, 0, 0, true }, + [KEY_N] = { 'b', 'B', 0, 0, 0, true }, + [KEY_M] = { 'm', 'M', 0, 0, 0, true }, + [KEY_COMMA] = { 'w', 'W', 0, 0, 0, true }, + [KEY_DOT] = { 'v', 'V', 0, 0, 0, true }, + [KEY_SLASH] = { 'z', 'Z', 0, 0, 0, true }, + + [KEY_SPACE] = { ' ', ' ', 0, 0, 0, false }, + } +}; + +static const keyboard_layout_t *g_layouts[] = { + &layout_qwerty, + &layout_azerty, + &layout_qwertz, + &layout_dvorak +}; + +static keymap_id_t g_current = KEYMAP_QWERTY; +static uint32_t g_active_dead = 0; + +static const compose_entry_t g_compose_table[] = { + { '^', 'a', 0x00E2 }, { '^', 'e', 0x00EA }, { '^', 'i', 0x00EE }, { '^', 'o', 0x00F4 }, { '^', 'u', 0x00FB }, + { '^', 'A', 0x00C2 }, { '^', 'E', 0x00CA }, { '^', 'I', 0x00CE }, { '^', 'O', 0x00D4 }, { '^', 'U', 0x00DB }, + + { 0x00A8, 'a', 0x00E4 }, { 0x00A8, 'e', 0x00EB }, { 0x00A8, 'i', 0x00EF }, { 0x00A8, 'o', 0x00F6 }, { 0x00A8, 'u', 0x00FC }, { 0x00A8, 'y', 0x00FF }, + { 0x00A8, 'A', 0x00C4 }, { 0x00A8, 'E', 0x00CB }, { 0x00A8, 'I', 0x00CF }, { 0x00A8, 'O', 0x00D6 }, { 0x00A8, 'U', 0x00DC }, + + { 0x00B4, 'a', 0x00E1 }, { 0x00B4, 'e', 0x00E9 }, { 0x00B4, 'i', 0x00ED }, { 0x00B4, 'o', 0x00F3 }, { 0x00B4, 'u', 0x00FA }, + { 0x00B4, 'A', 0x00C1 }, { 0x00B4, 'E', 0x00C9 }, { 0x00B4, 'I', 0x00CD }, { 0x00B4, 'O', 0x00D3 }, { 0x00B4, 'U', 0x00DA }, + + { '`', 'a', 0x00E0 }, { '`', 'e', 0x00E8 }, { '`', 'i', 0x00EC }, { '`', 'o', 0x00F2 }, { '`', 'u', 0x00F9 }, + { 0, 0, 0 } +}; + +void keymap_init(void) { + g_current = KEYMAP_QWERTY; +} + +void keymap_set_current(keymap_id_t id) { + if ((int)id < 0 || (int)id >= keymap_get_count()) return; + g_current = id; +} + +keymap_id_t keymap_get_current(void) { + return g_current; +} + +const char *keymap_get_name(keymap_id_t id) { + if ((int)id < 0 || (int)id >= keymap_get_count()) return "Unknown"; + return g_layouts[id]->name; +} + +int keymap_get_count(void) { + return (int)(sizeof(g_layouts) / sizeof(g_layouts[0])); +} + +static keymap_result_t make_result(uint32_t cp, bool dead) { + keymap_result_t r; + r.codepoint = cp; + r.is_text = (cp != 0); + r.is_dead = dead; + return r; +} + +keymap_result_t keymap_translate_keycode(uint16_t keycode, uint32_t mods) { + if (keycode <= 0 || keycode >= KEY_MAX) + return make_result(0, false); + + const keymap_entry_t *e = &g_layouts[g_current]->entries[keycode]; + if (!e->normal && !e->shift && !e->altgr && !e->shift_altgr) + return make_result(0, false); + + bool shift = (mods & KB_MOD_SHIFT) != 0; + bool caps = (mods & KB_MOD_CAPS) != 0; + bool altgr = (mods & KB_MOD_ALTGR) != 0; + + uint32_t cp = 0; + uint8_t dead_mask_bit = 0; + + if (altgr) { + if (shift) { + cp = e->shift_altgr; + dead_mask_bit = DEAD_SHIFT_ALTGR; + } else { + cp = e->altgr; + dead_mask_bit = DEAD_ALTGR; + } + } else if (e->alpha) { + bool uppercase = shift ^ caps; + cp = uppercase ? e->shift : e->normal; + dead_mask_bit = uppercase ? DEAD_SHIFT : DEAD_NORMAL; + } else { + cp = shift ? e->shift : e->normal; + dead_mask_bit = shift ? DEAD_SHIFT : DEAD_NORMAL; + } + + bool is_dead = (e->dead_mask & dead_mask_bit) != 0; + + if (is_dead && cp != 0) { + g_active_dead = cp; + return make_result(0, true); + } + + if (g_active_dead && cp != 0) { + uint32_t combined = keymap_compose(g_active_dead, cp); + g_active_dead = 0; + + if (combined != 0) { + return make_result(combined, false); + } + + return make_result(cp, false); + } + + return make_result(cp, false); +} + +uint32_t keymap_compose(uint32_t dead_codepoint, uint32_t base_codepoint) { + for (int i = 0; g_compose_table[i].dead != 0; i++) { + if (g_compose_table[i].dead == dead_codepoint && + g_compose_table[i].base == base_codepoint) { + return g_compose_table[i].result; + } + } + return 0; +} + +int keymap_legacy_key(uint16_t keycode, uint32_t codepoint) { + if (codepoint != 0 && codepoint < 128) return (int)codepoint; + + switch (keycode) { + case KEY_ESC: return 27; + case KEY_BACKSPACE: return '\b'; + case KEY_TAB: return '\t'; + case KEY_ENTER: + case KEY_KP_ENTER: return '\n'; + + case KEY_ARROW_UP: return 17; + case KEY_ARROW_DOWN: return 18; + case KEY_ARROW_LEFT: return 19; + case KEY_ARROW_RIGHT: return 20; + + case KEY_RIGHT_ALT: return 22; // for compat w/ doom + case KEY_CAPS_LOCK: return 23; // same here + case KEY_DELETE: return 127; + default: return 0; + } +} \ No newline at end of file diff --git a/src/input/keymap.h b/src/input/keymap.h new file mode 100644 index 0000000..809be99 --- /dev/null +++ b/src/input/keymap.h @@ -0,0 +1,42 @@ +#ifndef KEYMAP_H +#define KEYMAP_H + +#include +#include +#include "keycodes.h" + +typedef enum { + KEYMAP_QWERTY = 0, + KEYMAP_AZERTY = 1, + KEYMAP_QWERTZ = 2, + KEYMAP_DVORAK = 3, +} keymap_id_t; + +typedef struct { + uint32_t normal; + uint32_t shift; + uint32_t altgr; + uint32_t shift_altgr; + uint8_t dead_mask; // bit0 normal, bit1 shift, bit2 altgr, bit3 shift_altgr + bool alpha; +} keymap_entry_t; + +typedef struct { + uint32_t codepoint; + bool is_text; + bool is_dead; +} keymap_result_t; + +void keymap_init(void); +void keymap_set_current(keymap_id_t id); +keymap_id_t keymap_get_current(void); +const char *keymap_get_name(keymap_id_t id); +int keymap_get_count(void); + +keymap_result_t keymap_translate_keycode(uint16_t keycode, uint32_t mods); +uint32_t keymap_compose(uint32_t dead_codepoint, uint32_t base_codepoint); + +// compat legacy for existing apps +int keymap_legacy_key(uint16_t keycode, uint32_t codepoint); + +#endif \ No newline at end of file diff --git a/src/mem/vm.c b/src/mem/vm.c index 6554791..dca6ddb 100644 --- a/src/mem/vm.c +++ b/src/mem/vm.c @@ -11,15 +11,8 @@ #include "ps2.h" #include "kutils.h" #include "io.h" - -// --- Scancode Map (Set 1) --- -static char vm_scancode_map[128] = { - 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', - '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', - 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, - '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', - 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; +#include "input/keymap.h" +#include "input/keyboard.h" // VM State static int stack[VM_STACK_SIZE]; @@ -97,8 +90,22 @@ static void vm_syscall(int id) { push(0); break; case VM_SYS_PRINT_CHAR: { - char c = (char)pop(); - char s[2] = {c, 0}; + uint32_t cp = (uint32_t)pop(); + char s[5] = {0}; + + if (cp < 0x80) { + s[0] = cp; + } else if (cp < 0x800) { + s[0] = 0xC0 | (cp >> 6); + s[1] = 0x80 | (cp & 0x3F); + } else if (cp < 0x10000) { + s[0] = 0xE0 | (cp >> 12); + s[1] = 0x80 | ((cp >> 6) & 0x3F); + s[2] = 0x80 | (cp & 0x3F); + } else { + s[0] = '?'; + } + cmd_write(s); push(0); break; @@ -121,15 +128,32 @@ static void vm_syscall(int id) { break; case VM_SYS_GETCHAR: { int c = 0; - // Blocking read for a valid key press + bool ext = false; + // Wait for a key press and return the ASCII code while (1) { - if ((inb(0x64) & 1)) { // Data available + if ((inb(0x64) & 1)) { // Data available in keyboard controller uint8_t sc = inb(0x60); - if (!(sc & 0x80)) { // Key press - if (sc < 128) { - c = vm_scancode_map[sc]; - if (c) break; + + if (sc == 0xE0) { // Extended scancode prefix + ext = true; + continue; + } + + if (!(sc & 0x80)) { // Key press (not release) + uint16_t keycode = keyboard_keycode_from_set1((uint8_t)(sc & 0x7F), ext); // Get keycode and reset + ext = false; + + if (keycode != KEY_NONE) { + uint32_t mods = keyboard_get_modifiers(); + keymap_result_t r = keymap_translate_keycode(keycode, mods); + // Only return valid text characters, ignore modifiers and dead keys + if (r.is_text && !r.is_dead && r.codepoint >= 32 && r.codepoint != 127) { + c = (int)r.codepoint; + break; + } } + } else { + ext = false; } } } @@ -545,7 +569,26 @@ int vm_exec(const uint8_t *code, int code_size) { case OP_MUL: push(pop() * pop()); break; case OP_DIV: { int b=pop(); int a=pop(); push(b==0?0:a/b); } break; case OP_PRINT: cmd_write_int(pop()); cmd_write("\n"); break; - case OP_PRITC: { char c=(char)pop(); char s[2]={c,0}; cmd_write(s); } break; + case OP_PRITC: { + uint32_t cp = (uint32_t)pop(); + char s[5] = {0}; + + if (cp < 0x80) { + s[0] = (char)cp; + } else if (cp < 0x800) { + s[0] = (char)(0xC0 | (cp >> 6)); + s[1] = (char)(0x80 | (cp & 0x3F)); + } else if (cp < 0x10000) { + s[0] = (char)(0xE0 | (cp >> 12)); + s[1] = (char)(0x80 | ((cp >> 6) & 0x3F)); + s[2] = (char)(0x80 | (cp & 0x3F)); + } else { + s[0] = '?'; + } + + cmd_write(s); + break; + } case OP_JMP: { int addr = 0; addr |= memory[pc++]; @@ -601,4 +644,4 @@ int vm_exec(const uint8_t *code, int code_size) { } wm_custom_paint_hook = NULL; return 0; -} \ No newline at end of file +} diff --git a/src/sys/syscall.c b/src/sys/syscall.c index a25a3d4..7566d28 100644 --- a/src/sys/syscall.c +++ b/src/sys/syscall.c @@ -22,14 +22,17 @@ #include "tty.h" #include "font_manager.h" #include "graphics.h" +#include "input/keycodes.h" +#include "input/keymap.h" #include "app_metadata.h" -extern bool ps2_ctrl_pressed; - #define SPAWN_FLAG_TERMINAL 0x1 #define SPAWN_FLAG_INHERIT_TTY 0x2 #define SPAWN_FLAG_TTY_ID 0x4 +#define SYSTEM_CMD_SET_KEYBOARD_LAYOUT 49 +#define SYSTEM_CMD_GET_KEYBOARD_LAYOUT 51 + // Read MSR static inline uint64_t rdmsr(uint32_t msr) { uint32_t low, high; @@ -158,10 +161,17 @@ void syscall_send_mouse_up_event(Window *win, int x, int y) { user_window_mouse_up(win, x, y); } -static void user_window_key(Window *win, char c, bool pressed) { +static void user_window_key(Window *win, int legacy, uint16_t keycode, uint32_t codepoint, uint32_t mods, bool pressed) { process_t *proc = process_get_by_ui_window(win); if (!proc) return; - gui_event_t ev = { .type = pressed ? GUI_EVENT_KEY : GUI_EVENT_KEYUP, .arg1 = (int)c, .arg3 = (int)ps2_ctrl_pressed }; + + gui_event_t ev = { + .type = pressed ? GUI_EVENT_KEY : GUI_EVENT_KEYUP, + .arg1 = legacy, + .arg2 = (int)keycode, + .arg3 = (int)mods, + .arg4 = (int)codepoint + }; process_push_gui_event(proc, &ev); } @@ -2105,6 +2115,16 @@ static uint64_t sys_cmd_get_elf_primary_image(const syscall_args_t *args) { return app_metadata_get_primary_image(path, out_path, out_size) ? 1 : 0; } +static uint64_t sys_cmd_set_keyboard_layout(const syscall_args_t *args) { + keymap_set_current((keymap_id_t)args->arg2); + return 0; +} + +static uint64_t sys_cmd_get_keyboard_layout(const syscall_args_t *args) { + (void)args; + return (uint64_t)keymap_get_current(); +} + #define SYS_CMD_TABLE_SIZE 78 static const syscall_handler_fn sys_cmd_table[SYS_CMD_TABLE_SIZE] = { [SYSTEM_CMD_SET_BG_COLOR] = sys_cmd_set_bg_color, @@ -2152,6 +2172,8 @@ static const syscall_handler_fn sys_cmd_table[SYS_CMD_TABLE_SIZE] = { [SYSTEM_CMD_SET_RESOLUTION] = sys_cmd_set_resolution, [SYSTEM_CMD_NETWORK_GET_NIC_NAME] = sys_cmd_network_get_nic_name, [SYSTEM_CMD_PARALLEL_RUN] = sys_cmd_parallel_run, + [SYSTEM_CMD_SET_KEYBOARD_LAYOUT] = sys_cmd_set_keyboard_layout, + [SYSTEM_CMD_GET_KEYBOARD_LAYOUT] = sys_cmd_get_keyboard_layout, [SYSTEM_CMD_TTY_CREATE] = sys_cmd_tty_create, [SYSTEM_CMD_TTY_READ_OUT] = sys_cmd_tty_read_out, [SYSTEM_CMD_TTY_WRITE_IN] = sys_cmd_tty_write_in, diff --git a/src/sys/syscall.h b/src/sys/syscall.h index 5c1ddb3..10db73e 100644 --- a/src/sys/syscall.h +++ b/src/sys/syscall.h @@ -100,7 +100,9 @@ typedef struct { #define SYSTEM_CMD_SLEEP 46 #define SYSTEM_CMD_SET_RESOLUTION 47 #define SYSTEM_CMD_NETWORK_GET_NIC_NAME 48 +#define SYSTEM_CMD_SET_KEYBOARD_LAYOUT 49 #define SYSTEM_CMD_PARALLEL_RUN 50 +#define SYSTEM_CMD_GET_KEYBOARD_LAYOUT 51 #define SYSTEM_CMD_TTY_CREATE 60 #define SYSTEM_CMD_TTY_READ_OUT 61 #define SYSTEM_CMD_TTY_WRITE_IN 62 diff --git a/src/userland/gui/notepad.c b/src/userland/gui/notepad.c index c6b118b..f91986a 100644 --- a/src/userland/gui/notepad.c +++ b/src/userland/gui/notepad.c @@ -7,6 +7,8 @@ #include "libc/libui.h" #include "libc/stdlib.h" #include "libc/syscall_user.h" +#include "libc/utf-8.h" +#include "libc/input.h" #include #define COLOR_NOTEPAD_BG 0xFFFFFFFF @@ -23,16 +25,16 @@ static void notepad_ensure_cursor_visible(int h) { if (fh < 8) fh = 8; int visible_lines = (h - 10) / fh; if (visible_lines < 1) visible_lines = 1; - + int cursor_line = 0; for (int i = 0; i < cursor_pos && i < buf_len; i++) { if (buffer[i] == '\n') cursor_line++; } - + if (cursor_line < notepad_scroll_line) { notepad_scroll_line = cursor_line; } - + if (cursor_line >= notepad_scroll_line + visible_lines) { notepad_scroll_line = cursor_line - visible_lines + 1; } @@ -51,7 +53,7 @@ static void notepad_load_state() { } static void notepad_save_state() { - // Ensure dir exists + // Ensure dir exist sys_mkdir("/tmp"); int fd = sys_open("/tmp/notepad_state.txt", "w"); if (fd >= 0) { @@ -66,21 +68,28 @@ static void notepad_paint(ui_window_t win, int w, int h) { int fh = (int)ui_get_font_height(); if (fh < 8) fh = 8; - int visual_line = 0; + int visual_line = 0; int current_x = 4; int current_y = 4; - int window_right = w - 8; - - for (int i = 0; i < buf_len; i++) { + int window_right = w - 8; + + for (int i = 0; i < buf_len; ) { + int adv; + uint32_t cp = text_decode_utf8(&buffer[i], &adv); + if (visual_line < notepad_scroll_line) { - if (buffer[i] == '\n') { + if (cp == '\n') { visual_line++; current_x = 4; current_y = 4; } else { - char ch[2] = {buffer[i], 0}; - int cw = (int)ui_get_string_width(ch); + char out[5]; + int len = text_encode_utf8(cp, out); + out[len] = 0; + + int cw = (int)ui_get_string_width(out); if (cw < 1) cw = 8; + if (current_x + cw >= window_right) { visual_line++; current_x = 4; @@ -88,50 +97,61 @@ static void notepad_paint(ui_window_t win, int w, int h) { } current_x += cw; } + + i += adv; continue; } - - if (visual_line >= notepad_scroll_line + (h - 8) / fh) { - break; - } - - if (buffer[i] == '\n') { + + if (visual_line >= notepad_scroll_line + (h - 8) / fh) break; + + if (cp == '\n') { current_x = 4; current_y += fh; visual_line++; } else { - char ch[2] = {buffer[i], 0}; - int cw = (int)ui_get_string_width(ch); + char out[5]; + int len = text_encode_utf8(cp, out); + out[len] = 0; + + int cw = (int)ui_get_string_width(out); if (cw < 1) cw = 8; + if (current_x + cw >= window_right) { current_x = 4; current_y += fh; visual_line++; - - if (visual_line >= notepad_scroll_line + (h - 8) / fh) { - break; - } + + if (visual_line >= notepad_scroll_line + (h - 8) / fh) break; } - - ui_draw_string(win, current_x, current_y, ch, COLOR_BLACK); + + ui_draw_string(win, current_x, current_y, out, COLOR_BLACK); current_x += cw; } + + i += adv; } - - // Cursor + + // --- CURSOR --- int cx = 4; int cy = 4; int c_visual_line = 0; - - for (int i = 0; i < cursor_pos; i++) { - if (buffer[i] == '\n') { + + for (int i = 0; i < cursor_pos; ) { + int adv; + uint32_t cp = text_decode_utf8(&buffer[i], &adv); + + if (cp == '\n') { cx = 4; cy += fh; c_visual_line++; } else { - char ch[2] = {buffer[i], 0}; - int cw = (int)ui_get_string_width(ch); + char out[5]; + int len = text_encode_utf8(cp, out); + out[len] = 0; + + int cw = (int)ui_get_string_width(out); if (cw < 1) cw = 8; + if (cx + cw >= window_right) { cx = 4; cy += fh; @@ -139,120 +159,96 @@ static void notepad_paint(ui_window_t win, int w, int h) { } cx += cw; } + + i += adv; } - - if (c_visual_line >= notepad_scroll_line && + + if (c_visual_line >= notepad_scroll_line && c_visual_line < notepad_scroll_line + (h - 8) / fh) { ui_draw_rect(win, cx, cy, 2, fh - 2, COLOR_BLACK); } - + ui_mark_dirty(win, 0, 0, w, h); } -static void notepad_key(ui_window_t win, int h, char c) { - if (c == 17) { // UP - if (cursor_pos > 0) { - int curr = cursor_pos; - int line_start = curr; - while (line_start > 0 && buffer[line_start - 1] != '\n') { - line_start--; - } - int col = curr - line_start; +static void notepad_key(ui_window_t win, int h, int legacy, uint32_t codepoint) { + (void)win; - if (line_start > 0) { - int prev_line_end = line_start - 1; - int prev_line_start = prev_line_end; - while (prev_line_start > 0 && buffer[prev_line_start - 1] != '\n') { - prev_line_start--; - } - int prev_line_len = prev_line_end - prev_line_start; - if (col > prev_line_len) col = prev_line_len; - cursor_pos = prev_line_start + col; - } - } - } else if (c == 18) { // DOWN - if (cursor_pos < buf_len) { - int curr = cursor_pos; - int line_start = curr; - while (line_start > 0 && buffer[line_start - 1] != '\n') { - line_start--; - } - int col = curr - line_start; - - int next_line_start = curr; - while (next_line_start < buf_len && buffer[next_line_start] != '\n') { - next_line_start++; - } - - if (next_line_start < buf_len) { - next_line_start++; // Skip newline - int next_line_end = next_line_start; - while (next_line_end < buf_len && buffer[next_line_end] != '\n') { - next_line_end++; - } - int next_line_len = next_line_end - next_line_start; - if (col > next_line_len) col = next_line_len; - cursor_pos = next_line_start + col; - } else { - cursor_pos = buf_len; - } - } - } else if (c == 19) { // LEFT + if (legacy == KEY_UP) { // UP if (cursor_pos > 0) cursor_pos--; - } else if (c == 20) { // RIGHT + } else if (legacy == KEY_DOWN) { // DOWN if (cursor_pos < buf_len) cursor_pos++; - } else if (c == '\b') { // Backspace + } else if (legacy == KEY_LEFT) { // LEFT + if (cursor_pos > 0) + cursor_pos = (int)(text_prev_utf8(buffer, &buffer[cursor_pos]) - buffer); + } else if (legacy == KEY_RIGHT) { // RIGHT + if (cursor_pos < buf_len) + cursor_pos = (int)(text_next_utf8(&buffer[cursor_pos]) - buffer); + } else if (legacy == '\b') { if (cursor_pos > 0) { - for (int i = cursor_pos; i < buf_len; i++) { - buffer[i - 1] = buffer[i]; - } - buf_len--; - cursor_pos--; + int prev = (int)(text_prev_utf8(buffer, &buffer[cursor_pos]) - buffer); + int len = cursor_pos - prev; + + for (int i = cursor_pos; i < buf_len; i++) + buffer[i - len] = buffer[i]; + + buf_len -= len; + cursor_pos = prev; buffer[buf_len] = 0; } - } else if (c == '\n') { // Enter - if (buf_len < 1023) { - for (int i = buf_len; i > cursor_pos; i--) { - buffer[i] = buffer[i - 1]; - } - buffer[cursor_pos] = c; - buf_len++; - cursor_pos++; - buffer[buf_len] = 0; - } - } else { + } else if (legacy == '\n') { if (buf_len < NOTEPAD_BUF_SIZE - 1) { - for (int i = buf_len; i > cursor_pos; i--) { + for (int i = buf_len; i > cursor_pos; i--) buffer[i] = buffer[i - 1]; - } - buffer[cursor_pos] = c; + + buffer[cursor_pos++] = '\n'; buf_len++; - cursor_pos++; + buffer[buf_len] = 0; + } + } + // Only insert printable characters (excluding DEL) + else if (codepoint >= 32 && codepoint != 127) { + char utf8[4]; + int len = text_encode_utf8(codepoint, utf8); + + if (len > 0 && buf_len + len < NOTEPAD_BUF_SIZE) { + for (int i = buf_len - 1; i >= cursor_pos; i--) + buffer[i + len] = buffer[i]; + + for (int i = 0; i < len; i++) + buffer[cursor_pos + i] = utf8[i]; + + buf_len += len; + cursor_pos += len; buffer[buf_len] = 0; } } + notepad_ensure_cursor_visible(h); } int main(int argc, char **argv) { sys_serial_write("Notepad: Starting userspace main...\n"); ui_window_t win = ui_window_create("Notepad", 100, 100, 400, 300); + if (win == 0) { sys_serial_write("Notepad: Failed to create window!\n"); return 1; } + sys_serial_write("Notepad: Window created successfully.\n"); notepad_load_state(); gui_event_t ev; sys_serial_write("Notepad: Entering event loop...\n"); + while (1) { if (ui_get_event(win, &ev)) { if (ev.type == GUI_EVENT_PAINT) { notepad_paint(win, 400, 300); } else if (ev.type == GUI_EVENT_KEY) { - notepad_key(win, 300, (char)ev.arg1); + notepad_key(win, 300, ev.arg1, (uint32_t)ev.arg4); notepad_paint(win, 400, 300); } else if (ev.type == GUI_EVENT_CLOSE) { sys_serial_write("Notepad: CLOSE\n"); @@ -265,4 +261,4 @@ int main(int argc, char **argv) { } return 0; -} +} \ No newline at end of file diff --git a/src/userland/gui/settings.c b/src/userland/gui/settings.c index f625dd1..c45b8ab 100644 --- a/src/userland/gui/settings.c +++ b/src/userland/gui/settings.c @@ -55,13 +55,14 @@ static widget_checkbox_t chk_snap; static widget_checkbox_t chk_align; static widget_dropdown_t drop_res; static widget_dropdown_t drop_color; +static widget_dropdown_t drop_keyboard; static widget_textbox_t tb_r, tb_g, tb_b; static widget_textbox_t tb_ip, tb_dns; static widget_button_t btn_apply, btn_back; #define MAX_WALLPAPERS 10 -static widget_button_t btn_main_wallpaper, btn_main_network, btn_main_desktop, btn_main_mouse, btn_main_fonts, btn_main_display; +static widget_button_t btn_main_wallpaper, btn_main_network, btn_main_desktop, btn_main_mouse, btn_main_fonts, btn_main_display, btn_main_keyboard; static widget_button_t btn_wp_colors[6]; static widget_button_t btn_wp_patterns[2]; static widget_button_t btn_wp_apply; @@ -99,7 +100,8 @@ enum settings_icon_id { SETTINGS_ICON_MOUSE, SETTINGS_ICON_FONTS, SETTINGS_ICON_DISPLAY, - SETTINGS_ICON_COUNT + SETTINGS_ICON_KEYBOARD, + SETTINGS_ICON_COUNT,// must be last }; static const char *settings_icon_names[SETTINGS_ICON_COUNT] = { @@ -109,6 +111,7 @@ static const char *settings_icon_names[SETTINGS_ICON_COUNT] = { "input-mouse.png", "fonts.png", "preferences-desktop-display.png", + "input-keyboard.png" }; static int settings_icon_main_state[SETTINGS_ICON_COUNT]; @@ -134,6 +137,7 @@ static uint32_t settings_icon_list_pixels[SETTINGS_ICON_COUNT][SETTINGS_ICON_LIS #define VIEW_MOUSE 4 #define VIEW_FONTS 5 #define VIEW_DISPLAY 6 +#define VIEW_KEYBOARD 7 static int disp_sel_res = 2; static int disp_sel_color = 0; @@ -148,6 +152,7 @@ static char net_ip[16] = ""; static char net_dns[16] = ""; static int focused_field = -1; static int input_cursor = 0; +static int keyboard_layout = 0; static int dyn_res_w[3]; static int dyn_res_h[3]; @@ -503,6 +508,13 @@ static void control_panel_paint_main(ui_window_t win) { settings_draw_icon(win, SETTINGS_ICON_DISPLAY, offset_x + 16, offset_y + item_y + 14, false); ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Display", COLOR_DARK_TEXT); ui_draw_string(win, offset_x + 60, offset_y + item_y + 35, "Screen resolution & color", COLOR_DKGRAY); + + // Keyboard + item_y += item_h + item_spacing; + widget_button_draw(&settings_ctx, &btn_main_keyboard); + settings_draw_icon(win, SETTINGS_ICON_KEYBOARD, offset_x + 16, offset_y + item_y + 14, false); + ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Keyboard", COLOR_DARK_TEXT); + ui_draw_string(win, offset_x + 60, offset_y + item_y + 35, "Keyboard layout", COLOR_DKGRAY); } static void control_panel_paint_wallpaper(ui_window_t win) { @@ -891,6 +903,19 @@ static void control_panel_paint_display(ui_window_t win) { widget_dropdown_draw(&settings_ctx, &drop_color); } +static void control_panel_paint_keyboard(ui_window_t win) { + int offset_x = 8; + int offset_y = 6; + + widget_button_draw(&settings_ctx, &btn_back); + + ui_draw_string(win, offset_x, offset_y + 40, "Keyboard Layout:", COLOR_DARK_TEXT); + + widget_dropdown_draw(&settings_ctx, &drop_keyboard); + + widget_button_draw(&settings_ctx, &btn_apply); +} + static void control_panel_paint(ui_window_t win) { // Fill background ui_draw_rect(win, 0, 0, 350, 500, COLOR_DARK_BG); @@ -911,6 +936,8 @@ static void control_panel_paint(ui_window_t win) { control_panel_paint_fonts(win); } else if (current_view == VIEW_DISPLAY) { control_panel_paint_display(win); + } else if (current_view == VIEW_KEYBOARD) { + control_panel_paint_keyboard(win); } } @@ -1185,6 +1212,14 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click if (is_click) { current_view = VIEW_DISPLAY; focused_field = -1; btn_main_display.pressed = false; } return; } + if (widget_button_handle_mouse(&btn_main_keyboard, x, y, is_down, is_click, NULL)) { + if (is_click) { + current_view = VIEW_KEYBOARD; + focused_field = -1; + btn_main_keyboard.pressed = false; + } + return; + } } if (current_view == VIEW_MOUSE) { @@ -1211,6 +1246,22 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click focused_field = 4; disp_sel_res = 5; input_cursor = tb_custom_h.cursor_pos; return; } } + + if (current_view == VIEW_KEYBOARD) { + + if (widget_dropdown_handle_mouse(&drop_keyboard, x, y, is_click, NULL)) { + keyboard_layout = drop_keyboard.selected_idx; + return; + } + + if (widget_button_handle_mouse(&btn_apply, x, y, is_down, is_click, NULL)) { + if (is_click) { + sys_system(SYSTEM_CMD_SET_KEYBOARD_LAYOUT, keyboard_layout, 0,0,0); + btn_apply.pressed = false; + } + return; + } + } } static void control_panel_handle_key(char c, bool pressed) { @@ -1253,6 +1304,9 @@ static void init_settings_widgets(void) { static const char *color_opts[] = {"32-bit", "16-bit", "256 Colors", "Grayscale", "Monochrome"}; widget_dropdown_init(&drop_color, 168, 66, 140, 30, color_opts, 5); + static const char *keyboard_opts[] = {"QWERTY", "AZERTY", "QWERTZ", "DVORAK"}; // add more layouts here + widget_dropdown_init(&drop_keyboard, 8, 80, 200, 30, keyboard_opts, 4); // increment the last number when adding more layouts + widget_textbox_init(&tb_r, 33, 226, 50, 18, rgb_r, 4); widget_textbox_init(&tb_g, 123, 226, 50, 18, rgb_g, 4); widget_textbox_init(&tb_b, 213, 226, 50, 18, rgb_b, 4); @@ -1270,7 +1324,8 @@ static void init_settings_widgets(void) { widget_button_init(&btn_main_desktop, 8, 6 + item_y, 334, 60, ""); item_y += 70; widget_button_init(&btn_main_mouse, 8, 6 + item_y, 334, 60, ""); item_y += 70; widget_button_init(&btn_main_fonts, 8, 6 + item_y, 334, 60, ""); item_y += 70; - widget_button_init(&btn_main_display, 8, 6 + item_y, 334, 60, ""); + widget_button_init(&btn_main_display, 8, 6 + item_y, 334, 60, ""); item_y += 70; + widget_button_init(&btn_main_keyboard, 8, 6 + item_y, 334, 60, ""); // Wallpaper View Buttons widget_button_init(&btn_wp_colors[0], 8, 71, 91, 25, ""); @@ -1302,54 +1357,93 @@ static void init_settings_widgets(void) { int main(int argc, char **argv) { (void)argc; (void)argv; + ui_window_t win = ui_window_create("Settings", 200, 150, 350, 500); if (!win) return 1; generate_lumberjack_pattern(); - - fetch_kernel_state(); - + init_dynamic_resolutions(); + init_settings_widgets(); + + desktop_snap_to_grid = sys_system(SYSTEM_CMD_GET_DESKTOP_PROP, 1, 0, 0, 0); + desktop_auto_align = sys_system(SYSTEM_CMD_GET_DESKTOP_PROP, 2, 0, 0, 0); + desktop_max_rows_per_col = sys_system(SYSTEM_CMD_GET_DESKTOP_PROP, 3, 0, 0, 0); + desktop_max_cols = sys_system(SYSTEM_CMD_GET_DESKTOP_PROP, 4, 0, 0, 0); + mouse_speed = sys_system(SYSTEM_CMD_GET_MOUSE_SPEED, 0, 0, 0, 0); load_settings_icons(); - - // Set initial toggle states - chk_snap.checked = desktop_snap_to_grid; - chk_align.checked = desktop_auto_align; - drop_res.selected_idx = disp_sel_res; + + // Set initial widget states + chk_snap.checked = desktop_snap_to_grid; + chk_align.checked = desktop_auto_align; + drop_res.selected_idx = disp_sel_res; drop_color.selected_idx = disp_sel_color; - + + keyboard_layout = sys_system(SYSTEM_CMD_GET_KEYBOARD_LAYOUT, 0, 0, 0, 0); + drop_keyboard.selected_idx = keyboard_layout; + + control_panel_paint(win); + ui_mark_dirty(win, 0, 0, 350, 500); + + load_wallpapers(); // load after first paint to avoid startup delay + gui_event_t ev; while (1) { bool dirty = false; + if (ui_get_event(win, &ev)) { if (ev.type == GUI_EVENT_PAINT) { dirty = true; - } else if (ev.type == GUI_EVENT_CLICK || ev.type == GUI_EVENT_MOUSE_DOWN || - ev.type == GUI_EVENT_MOUSE_MOVE || ev.type == GUI_EVENT_MOUSE_UP) { - bool down = (ev.type == GUI_EVENT_MOUSE_DOWN || ev.type == GUI_EVENT_CLICK); - if (ev.type == GUI_EVENT_MOUSE_MOVE) down = (ev.arg3 & 1); - if (ev.type == GUI_EVENT_MOUSE_UP) down = false; - - control_panel_handle_mouse(ev.arg1, ev.arg2, down, ev.type == GUI_EVENT_CLICK); + + } else if (ev.type == GUI_EVENT_CLICK || + ev.type == GUI_EVENT_MOUSE_DOWN || + ev.type == GUI_EVENT_MOUSE_MOVE || + ev.type == GUI_EVENT_MOUSE_UP) { + bool down = false; + + if (ev.type == GUI_EVENT_MOUSE_DOWN || ev.type == GUI_EVENT_CLICK) { + down = true; + } else if (ev.type == GUI_EVENT_MOUSE_MOVE) { + down = (ev.arg3 & 1); + } else if (ev.type == GUI_EVENT_MOUSE_UP) { + down = false; + } + + control_panel_handle_mouse( + ev.arg1, + ev.arg2, + down, + ev.type == GUI_EVENT_CLICK + ); dirty = true; + } else if (ev.type == GUI_EVENT_MOUSE_WHEEL) { if (current_view == VIEW_FONTS) { font_scroll_y -= ev.arg1 * 20; int max_scroll = font_scrollbar.content_height - font_scrollbar.h; + if (max_scroll < 0) max_scroll = 0; if (font_scroll_y < 0) font_scroll_y = 0; if (font_scroll_y > max_scroll) font_scroll_y = max_scroll; - widget_scrollbar_update(&font_scrollbar, font_scrollbar.content_height, font_scroll_y); + + widget_scrollbar_update( + &font_scrollbar, + font_scrollbar.content_height, + font_scroll_y + ); dirty = true; } + } else if (ev.type == GUI_EVENT_KEY) { control_panel_handle_key((char)ev.arg1, true); dirty = true; + } else if (ev.type == GUI_EVENT_KEYUP) { control_panel_handle_key((char)ev.arg1, false); + } else if (ev.type == GUI_EVENT_CLOSE) { sys_exit(0); } - + if (dirty) { control_panel_paint(win); ui_mark_dirty(win, 0, 0, 350, 500); @@ -1358,6 +1452,7 @@ int main(int argc, char **argv) { sleep(10); } } + return 0; } diff --git a/src/userland/libc/libui.h b/src/userland/libc/libui.h index 6d98091..38d92e4 100644 --- a/src/userland/libc/libui.h +++ b/src/userland/libc/libui.h @@ -44,9 +44,10 @@ typedef struct { int type; - int arg1; // For click: x - int arg2; // For click: y - int arg3; // For click: button state + int arg1; // CLICK: x / KEY: legacy char-or-compat key + int arg2; // CLICK: y / KEY: keycode + int arg3; // CLICK: button state / KEY: modifier mask + int arg4; // KEY: Unicode codepoint (0 if none) } gui_event_t; // Window Handle diff --git a/src/userland/libc/syscall.h b/src/userland/libc/syscall.h index a923c66..4250f83 100644 --- a/src/userland/libc/syscall.h +++ b/src/userland/libc/syscall.h @@ -64,6 +64,8 @@ #define SYSTEM_CMD_NETWORK_HAS_IP 30 #define SYSTEM_CMD_GET_SHELL_CONFIG 28 #define SYSTEM_CMD_NETWORK_GET_NIC_NAME 48 +#define SYSTEM_CMD_SET_KEYBOARD_LAYOUT 49 +#define SYSTEM_CMD_GET_KEYBOARD_LAYOUT 51 #define SYSTEM_CMD_SET_TEXT_COLOR 29 #define SYSTEM_CMD_SET_WALLPAPER_PATH 31 #define SYSTEM_CMD_RTC_SET 32 diff --git a/src/userland/libc/utf-8.c b/src/userland/libc/utf-8.c new file mode 100644 index 0000000..edc5af6 --- /dev/null +++ b/src/userland/libc/utf-8.c @@ -0,0 +1,115 @@ +#include "utf-8.h" + +static int utf8_write_replacement(char *out) { + out[0] = (char)0xEF; + out[1] = (char)0xBF; + out[2] = (char)0xBD; + return 3; +} + +uint32_t text_decode_utf8(const char *s, int *advance) { + const unsigned char *u = (const unsigned char *)s; + + if (!u || u[0] == 0) { + if (advance) *advance = 0; + return 0; + } + + if ((u[0] & 0x80) == 0) { + if (advance) *advance = 1; + return u[0]; + } + + if ((u[0] & 0xE0) == 0xC0 && + (u[1] & 0xC0) == 0x80) { + if (advance) *advance = 2; + return ((u[0] & 0x1F) << 6) | + (u[1] & 0x3F); + } + + if ((u[0] & 0xF0) == 0xE0 && + (u[1] & 0xC0) == 0x80 && + (u[2] & 0xC0) == 0x80) { + if (advance) *advance = 3; + return ((u[0] & 0x0F) << 12) | + ((u[1] & 0x3F) << 6) | + (u[2] & 0x3F); + } + + if ((u[0] & 0xF8) == 0xF0 && + (u[1] & 0xC0) == 0x80 && + (u[2] & 0xC0) == 0x80 && + (u[3] & 0xC0) == 0x80) { + if (advance) *advance = 4; + return ((u[0] & 0x07) << 18) | + ((u[1] & 0x3F) << 12) | + ((u[2] & 0x3F) << 6) | + (u[3] & 0x3F); + } + + if (advance) *advance = 1; + return 0xFFFD; +} + +int text_encode_utf8(uint32_t cp, char *out) { + if (cp <= 0x7F) { + out[0] = (char)cp; + return 1; + } + + if (cp <= 0x7FF) { + out[0] = 0xC0 | (cp >> 6); + out[1] = 0x80 | (cp & 0x3F); + return 2; + } + + if (cp <= 0xFFFF) { + out[0] = 0xE0 | (cp >> 12); + out[1] = 0x80 | ((cp >> 6) & 0x3F); + out[2] = 0x80 | (cp & 0x3F); + return 3; + } + + if (cp <= 0x10FFFF) { + out[0] = 0xF0 | (cp >> 18); + out[1] = 0x80 | ((cp >> 12) & 0x3F); + out[2] = 0x80 | ((cp >> 6) & 0x3F); + out[3] = 0x80 | (cp & 0x3F); + return 4; + } + + return utf8_write_replacement(out); +} + +const char* text_next_utf8(const char *s) { + if (!s || *s == 0) return s; + + int adv; + text_decode_utf8(s, &adv); + return s + adv; +} + +const char* text_prev_utf8(const char *start, const char *s) { + if (!s || s <= start) return start; + + s--; + while (s > start && ((*s & 0xC0) == 0x80)) { + s--; + } + return s; +} + +int text_strlen_utf8(const char *s) { + if (!s) return 0; + + int count = 0; + int adv; + + while (*s) { + text_decode_utf8(s, &adv); + s += adv; + count++; + } + + return count; +} \ No newline at end of file diff --git a/src/userland/libc/utf-8.h b/src/userland/libc/utf-8.h new file mode 100644 index 0000000..1af7b36 --- /dev/null +++ b/src/userland/libc/utf-8.h @@ -0,0 +1,25 @@ +#ifndef UTF_8_H +#define UTF_8_H + +#include + +// Decode one UTF-8 codepoint +// s: input string +// advance: number of bytes consumed +uint32_t text_decode_utf8(const char *s, int *advance); + +// Encode one codepoint into UTF-8 +// out must be at least 4 bytes +// return: number of bytes written +int text_encode_utf8(uint32_t cp, char *out); + +// Move to next UTF-8 character +const char* text_next_utf8(const char *s); + +// Move to previous UTF-8 character +const char* text_prev_utf8(const char *start, const char *s); + +// Count characters (not bytes) +int text_strlen_utf8(const char *s); + +#endif \ No newline at end of file diff --git a/src/wm/explorer.c b/src/wm/explorer.c index cab04c3..95ceaa2 100644 --- a/src/wm/explorer.c +++ b/src/wm/explorer.c @@ -1414,7 +1414,12 @@ static void explorer_handle_click(Window *win, int x, int y) { } } -static void explorer_handle_key(Window *win, char c, bool pressed) { +static void explorer_handle_key(Window *win, int legacy, uint16_t keycode, uint32_t codepoint, uint32_t mods, bool pressed) { + (void)keycode; + (void)codepoint; + (void)mods; + char c = (char)legacy; + if (!pressed) return; ExplorerState *state = (ExplorerState*)win->data; diff --git a/src/wm/gui_ipc.h b/src/wm/gui_ipc.h index 4e3c1f3..3d6cf77 100644 --- a/src/wm/gui_ipc.h +++ b/src/wm/gui_ipc.h @@ -34,9 +34,10 @@ typedef struct { int type; - int arg1; // For click: x - int arg2; // For click: y - int arg3; // For click: button state + int arg1; // CLICK: x / KEY: legacy char-or-compat key + int arg2; // CLICK: y / KEY: keycode + int arg3; // CLICK: button state / KEY: modifier mask + int arg4; // KEY: Unicode codepoint (0 if none) } gui_event_t; #endif diff --git a/src/wm/wm.c b/src/wm/wm.c index e6613ad..990c241 100644 --- a/src/wm/wm.c +++ b/src/wm/wm.c @@ -22,6 +22,8 @@ #include "../sys/work_queue.h" #include "../sys/smp.h" #include "../core/kconsole.h" +#include "../input/keycodes.h" +#include "../input/keymap.h" // Hello developer, @@ -30,7 +32,7 @@ // TRUST ME. // If you do decide to hate yourself for some dumb reason, // add a few hours to the counter of despair: -// hours wasted: 57 +// hours wasted: 61 // send help #include "../sys/spinlock.h" @@ -98,7 +100,7 @@ static char notif_text[256] = {0}; static int notif_timer = 0; static int notif_x_offset = 420; // Starts offscreen static bool notif_active = false; -extern bool ps2_ctrl_pressed; + static lumos_state_t lumos_state = {0}; static bool lumos_index_built = false; @@ -3652,14 +3654,18 @@ void wm_handle_mouse(int dx, int dy, uint8_t buttons, int dz) { // Input Queue #define INPUT_QUEUE_SIZE 128 typedef struct { - char c; + int legacy; + uint16_t keycode; + uint32_t codepoint; + uint32_t mods; bool pressed; } key_event_t; static key_event_t key_queue[INPUT_QUEUE_SIZE]; static volatile int key_head = 0; static volatile int key_tail = 0; -static void wm_dispatch_key(char c, bool pressed) { +static void wm_dispatch_key(int legacy, uint16_t keycode, uint32_t codepoint, uint32_t mods, bool pressed) { + char c = (char)legacy; if (desktop_dialog_state != 0) { int len = 0; while(desktop_dialog_input[len]) len++; if (c == '\n') { @@ -3721,7 +3727,7 @@ static void wm_dispatch_key(char c, bool pressed) { if (target->handle_key) { - target->handle_key(target, c, pressed); + target->handle_key(target, legacy, keycode, codepoint, mods, pressed); } // Mark window as needing redraw on next timer tick @@ -3748,18 +3754,20 @@ static void build_file_index_async(void *arg) { file_index_build(); } -void wm_handle_key(char c, bool pressed) { - if (pressed && c == 'p' && ps2_ctrl_pressed) { +// Called by keyboard interrupt handler +void wm_handle_key_event(uint16_t keycode, uint32_t codepoint, uint32_t mods, bool pressed) { + int legacy = keymap_legacy_key(keycode, codepoint); + char c = (char)legacy; + + if (pressed && keycode == KEY_P && (mods & KB_MOD_CTRL)) { process_create_elf("/bin/screenshot.elf", NULL, false, -1); return; } - - if (pressed && c == ' ' && ps2_ctrl_pressed && ps2_shift_pressed()) { + + if (pressed && keycode == KEY_SPACE && (mods & KB_MOD_CTRL) && (mods & KB_MOD_SHIFT)) { lumos_state.visible = !lumos_state.visible; if (lumos_state.visible) { - // Check current index status - it may still be building in background lumos_index_built = file_index_is_valid(); - // Clear search state when opening lumos_state.search_len = 0; lumos_state.search_query[0] = 0; lumos_state.cursor_pos = 0; @@ -3772,15 +3780,18 @@ void wm_handle_key(char c, bool pressed) { force_redraw = true; return; } - - if (lumos_state.visible && pressed) { + + if (lumos_state.visible && pressed && legacy != 0) { wm_lumos_handle_key(c); return; } - + int next = (key_head + 1) % INPUT_QUEUE_SIZE; if (next != key_tail) { - key_queue[key_head].c = c; + key_queue[key_head].legacy = legacy; + key_queue[key_head].keycode = keycode; + key_queue[key_head].codepoint = codepoint; + key_queue[key_head].mods = mods; key_queue[key_head].pressed = pressed; key_head = next; } @@ -3801,7 +3812,7 @@ void wm_process_input(void) { while (key_head != key_tail) { key_event_t ev = key_queue[key_tail]; key_tail = (key_tail + 1) % INPUT_QUEUE_SIZE; - wm_dispatch_key(ev.c, ev.pressed); + wm_dispatch_key(ev.legacy, ev.keycode, ev.codepoint, ev.mods, ev.pressed); } wm_lock_release(rflags); diff --git a/src/wm/wm.h b/src/wm/wm.h index 1b8ada3..f33cc77 100644 --- a/src/wm/wm.h +++ b/src/wm/wm.h @@ -60,7 +60,7 @@ struct Window { // Callbacks void (*paint)(Window *win); - void (*handle_key)(Window *win, char c, bool pressed); + void (*handle_key)(Window *win, int legacy, uint16_t keycode, uint32_t codepoint, uint32_t mods, bool pressed); void (*handle_click)(Window *win, int x, int y); void (*handle_right_click)(Window *win, int x, int y); void (*handle_mouse_down)(Window *win, int x, int y); @@ -91,7 +91,7 @@ typedef struct { void wm_init(void); void wm_handle_mouse(int dx, int dy, uint8_t buttons, int dz); -void wm_handle_key(char c, bool pressed); +void wm_handle_key_event(uint16_t keycode, uint32_t codepoint, uint32_t mods, bool pressed); void wm_handle_click(int x, int y); void wm_handle_right_click(int x, int y); void wm_process_input(void);