mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 02:16:58 +00:00
feature(input): implement keyboard layouts and utf-8 input subsystem
* Adding keyboard layout (backend) * Update settings.c with new keyboard tab * Fixing keyboard icon && Fixing long loading time in settings.c * Refactor of key handling for a larger compatibility with the keyboard layout * Adding keyboard handler * Udating ps2.c with the new logic * Updating WM/kernel/userland with the new input system * Fixing keycode range && Updating dead keys handling * Add comments for explanation * Update notepad & vm.c to parse utf-8 * Adding utf-8 parsing utils in libc && Update notepad.c * Adding icon for icon settings * Fixing a warning with double definition * Adding new kb kayout: QWERTZ and DVORAK && Update new layout instrauction * Add documentation for keyboard input subsystem This document outlines the architecture and design of the input subsystem, focusing on keyboard input processing, driver responsibilities, keycode representation, and keymap functionality. --------- Co-authored-by: boreddevnl <chris@boreddev.nl>
This commit is contained in:
@@ -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 <stddef.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
115
src/userland/libc/utf-8.c
Normal file
115
src/userland/libc/utf-8.c
Normal file
@@ -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;
|
||||
}
|
||||
25
src/userland/libc/utf-8.h
Normal file
25
src/userland/libc/utf-8.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef UTF_8_H
|
||||
#define UTF_8_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// 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
|
||||
Reference in New Issue
Block a user