feat: add keyboard layout support
This commit is contained in:
@@ -352,7 +352,8 @@ void filemanager_on_key(Window* win, const montauk::abi::KeyEvent& key) {
|
||||
fm->pathbar_cursor = 0;
|
||||
} else if (key.scancode == 0x4F) {
|
||||
fm->pathbar_cursor = fm->pathbar_len;
|
||||
} else if (key.ascii >= 32 && key.ascii < 127 && fm->pathbar_len < 254) {
|
||||
} else if ((unsigned char)key.ascii >= 32
|
||||
&& (unsigned char)key.ascii != 127 && fm->pathbar_len < 254) {
|
||||
for (int i = fm->pathbar_len; i > fm->pathbar_cursor; i--)
|
||||
fm->pathbar_buf[i] = fm->pathbar_buf[i - 1];
|
||||
fm->pathbar_buf[fm->pathbar_cursor] = key.ascii;
|
||||
@@ -399,7 +400,8 @@ void filemanager_on_key(Window* win, const montauk::abi::KeyEvent& key) {
|
||||
} else if (key.scancode == 0x4F) {
|
||||
// End
|
||||
fm->rename_cursor = fm->rename_len;
|
||||
} else if (key.ascii >= 32 && key.ascii < 127) {
|
||||
} else if ((unsigned char)key.ascii >= 32
|
||||
&& (unsigned char)key.ascii != 127) {
|
||||
// Printable character (reject / and other FS-unsafe chars)
|
||||
if (key.ascii != '/' && key.ascii != '\\' && fm->rename_len < 62) {
|
||||
for (int i = fm->rename_len; i > fm->rename_cursor; i--)
|
||||
|
||||
@@ -7,6 +7,27 @@
|
||||
#include "desktop_internal.hpp"
|
||||
#include <montauk/user.h>
|
||||
|
||||
bool gui::desktop_refresh_keyboard_layouts(DesktopState* ds, bool force) {
|
||||
if (!ds) return false;
|
||||
uint64_t now = montauk::get_milliseconds();
|
||||
if (!force && now - ds->keyboard_last_poll < 1000) return false;
|
||||
|
||||
// Only the selection can change while we run: the layout tables are
|
||||
// read-only OS data, so there is no reason to re-parse them every second.
|
||||
int prev_active = ds->keyboard.active;
|
||||
bool prev_enabled[montauk::keyboard::MAX_LAYOUTS];
|
||||
for (int i = 0; i < montauk::keyboard::MAX_LAYOUTS; i++)
|
||||
prev_enabled[i] = ds->keyboard.enabled[i];
|
||||
|
||||
montauk::keyboard::refresh_selection(&ds->keyboard, ds->current_user);
|
||||
|
||||
bool changed = ds->keyboard.active != prev_active;
|
||||
for (int i = 0; !changed && i < ds->keyboard.registry.count; i++)
|
||||
changed = ds->keyboard.enabled[i] != prev_enabled[i];
|
||||
ds->keyboard_last_poll = now;
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool try_unlock(DesktopState* ds) {
|
||||
ds->lock_show_error = false;
|
||||
|
||||
@@ -85,7 +106,8 @@ static void handle_lock_keyboard(DesktopState* ds, const montauk::abi::KeyEvent&
|
||||
}
|
||||
|
||||
// Printable characters
|
||||
if (key.ascii >= 0x20 && key.ascii < 0x7F) {
|
||||
if ((unsigned char)key.ascii >= 0x20
|
||||
&& (unsigned char)key.ascii != 0x7F) {
|
||||
if (ds->lock_password_len < 63) {
|
||||
ds->lock_password[ds->lock_password_len] = key.ascii;
|
||||
ds->lock_password_len++;
|
||||
@@ -538,6 +560,22 @@ void gui::desktop_handle_mouse(DesktopState* ds) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Keyboard layout indicator: cycle through the layouts enabled in the
|
||||
// Keyboard Settings applet and persist the new active choice.
|
||||
if (ds->keyboard_layout_rect.w > 0
|
||||
&& ds->keyboard_layout_rect.contains(mx, my)) {
|
||||
ds->keyboard.active = montauk::keyboard::next_enabled(
|
||||
ds->keyboard, ds->keyboard.active);
|
||||
montauk::keyboard::save_user(ds->current_user, ds->keyboard);
|
||||
ds->keyboard_last_poll = montauk::get_milliseconds();
|
||||
ds->app_menu_open = false;
|
||||
ds->net_popup_open = false;
|
||||
ds->wifi_popup_open = false;
|
||||
ds->vol_popup_open = false;
|
||||
gui::mtk::context_menu_close(ds->ctx_menu);
|
||||
return;
|
||||
}
|
||||
|
||||
// Volume icon
|
||||
if (ds->vol_icon_rect.w > 0 && ds->vol_icon_rect.contains(mx, my)) {
|
||||
ds->vol_popup_open = !ds->vol_popup_open;
|
||||
@@ -801,8 +839,12 @@ void gui::desktop_handle_mouse(DesktopState* ds) {
|
||||
}
|
||||
|
||||
void gui::desktop_handle_keyboard(DesktopState* ds, const montauk::abi::KeyEvent& key) {
|
||||
montauk::abi::KeyEvent translated_key = key;
|
||||
montauk::keyboard::translate(ds->keyboard, &translated_key);
|
||||
const montauk::abi::KeyEvent& routed_key = translated_key;
|
||||
|
||||
if (ds->screen_locked) {
|
||||
handle_lock_keyboard(ds, key);
|
||||
handle_lock_keyboard(ds, routed_key);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -816,7 +858,7 @@ void gui::desktop_handle_keyboard(DesktopState* ds, const montauk::abi::KeyEvent
|
||||
montauk::abi::WinEvent ev;
|
||||
montauk::memset(&ev, 0, sizeof(ev));
|
||||
ev.type = 0; // key
|
||||
ev.key = key;
|
||||
ev.key = routed_key;
|
||||
montauk::win_sendevent(win->ext_win_id, &ev);
|
||||
ds->launcher_open = false;
|
||||
ds->app_menu_open = false;
|
||||
@@ -824,37 +866,37 @@ void gui::desktop_handle_keyboard(DesktopState* ds, const montauk::abi::KeyEvent
|
||||
return;
|
||||
}
|
||||
|
||||
if (desktop_handle_launcher_keyboard(ds, key)) {
|
||||
if (desktop_handle_launcher_keyboard(ds, routed_key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Global shortcuts (only on key press)
|
||||
if (key.pressed && key.ctrl && key.alt) {
|
||||
if (key.ascii == 'l' || key.ascii == 'L') {
|
||||
if (routed_key.pressed && routed_key.ctrl && routed_key.alt) {
|
||||
if (routed_key.ascii == 'l' || routed_key.ascii == 'L') {
|
||||
desktop_lock_screen(ds);
|
||||
return;
|
||||
}
|
||||
if (key.ascii == 't' || key.ascii == 'T') {
|
||||
if (routed_key.ascii == 't' || routed_key.ascii == 'T') {
|
||||
open_terminal(ds);
|
||||
return;
|
||||
}
|
||||
if (key.ascii == 'f' || key.ascii == 'F') {
|
||||
if (routed_key.ascii == 'f' || routed_key.ascii == 'F') {
|
||||
open_filemanager(ds);
|
||||
return;
|
||||
}
|
||||
if (key.ascii == 'c' || key.ascii == 'C') {
|
||||
if (routed_key.ascii == 'c' || routed_key.ascii == 'C') {
|
||||
open_calculator(ds);
|
||||
return;
|
||||
}
|
||||
if (key.ascii == 'e' || key.ascii == 'E') {
|
||||
if (routed_key.ascii == 'e' || routed_key.ascii == 'E') {
|
||||
open_texteditor(ds);
|
||||
return;
|
||||
}
|
||||
if (key.ascii == 'w' || key.ascii == 'W') {
|
||||
if (routed_key.ascii == 'w' || routed_key.ascii == 'W') {
|
||||
open_wordprocessor(ds);
|
||||
return;
|
||||
}
|
||||
if (key.ascii == 'k' || key.ascii == 'K') {
|
||||
if (routed_key.ascii == 'k' || routed_key.ascii == 'K') {
|
||||
open_klog(ds);
|
||||
return;
|
||||
}
|
||||
@@ -868,10 +910,10 @@ void gui::desktop_handle_keyboard(DesktopState* ds, const montauk::abi::KeyEvent
|
||||
montauk::abi::WinEvent ev;
|
||||
montauk::memset(&ev, 0, sizeof(ev));
|
||||
ev.type = 0; // key
|
||||
ev.key = key;
|
||||
ev.key = routed_key;
|
||||
montauk::win_sendevent(win->ext_win_id, &ev);
|
||||
} else if (win->on_key) {
|
||||
win->on_key(win, key);
|
||||
win->on_key(win, routed_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,7 +494,8 @@ bool desktop_handle_launcher_keyboard(DesktopState* ds, const montauk::abi::KeyE
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key.ascii >= 0x20 && key.ascii < 0x7F && ds->launcher_query_len < 63) {
|
||||
if ((unsigned char)key.ascii >= 0x20
|
||||
&& (unsigned char)key.ascii != 0x7F && ds->launcher_query_len < 63) {
|
||||
ds->launcher_query[ds->launcher_query_len++] = key.ascii;
|
||||
ds->launcher_query[ds->launcher_query_len] = '\0';
|
||||
desktop_update_launcher_results(ds);
|
||||
|
||||
@@ -405,6 +405,10 @@ void gui::desktop_init(DesktopState* ds) {
|
||||
ds->vol_output = montauk::audio_get_output(-1);
|
||||
ds->vol_bt_status = montauk::audio_bt_status(-1);
|
||||
|
||||
ds->keyboard = montauk::keyboard::load_user(ds->current_user);
|
||||
ds->keyboard_layout_rect = {0, 0, 0, 0};
|
||||
ds->keyboard_last_poll = montauk::get_milliseconds();
|
||||
|
||||
ds->closing_ext_count = 0;
|
||||
|
||||
ds->screen_locked = false;
|
||||
@@ -813,6 +817,7 @@ void gui::desktop_run(DesktopState* ds) {
|
||||
}
|
||||
|
||||
uint64_t now = montauk::get_milliseconds();
|
||||
sceneChanged |= desktop_refresh_keyboard_layouts(ds, false);
|
||||
sceneChanged |= desktop_refresh_panel_state(ds, now);
|
||||
|
||||
// Pick up any wallpaper buffer produced by a background loader.
|
||||
|
||||
@@ -108,8 +108,34 @@ void gui::desktop_draw_panel(DesktopState* ds) {
|
||||
int date_x = clock_x - date_w - 10;
|
||||
draw_text(fb, date_x, clock_y, date_str, colors::PANEL_TEXT);
|
||||
|
||||
// Volume icon (to the left of the date)
|
||||
int vol_icon_x = date_x - 16 - 12;
|
||||
// Only render switcher if more than one keyboard layout is installed
|
||||
int enabled_layouts = 0;
|
||||
for (int i = 0; i < ds->keyboard.registry.count; i++)
|
||||
if (ds->keyboard.enabled[i]) enabled_layouts++;
|
||||
|
||||
// Whatever text ends up leftmost is what the volume icon spaces off.
|
||||
int vol_anchor_x = date_x;
|
||||
|
||||
if (enabled_layouts > 1) {
|
||||
const char* layout_short = "en";
|
||||
if (ds->keyboard.active >= 0
|
||||
&& ds->keyboard.active < ds->keyboard.registry.count) {
|
||||
layout_short =
|
||||
ds->keyboard.registry.items[ds->keyboard.active].short_name;
|
||||
}
|
||||
int layout_tw = text_width(layout_short);
|
||||
int layout_x = date_x - layout_tw - 10;
|
||||
// Padded evenly past the glyphs so the click target is comfortable.
|
||||
ds->keyboard_layout_rect = {layout_x - 5, 4, layout_tw + 10, 24};
|
||||
draw_text(fb, layout_x, clock_y, layout_short, colors::PANEL_TEXT);
|
||||
vol_anchor_x = layout_x;
|
||||
} else {
|
||||
ds->keyboard_layout_rect = {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
// Volume icon keeps its 12px icon-to-text gap whether it sits next to the
|
||||
// layout tag or, with one layout, the date.
|
||||
int vol_icon_x = vol_anchor_x - 12 - 16;
|
||||
int vol_icon_y = (PANEL_HEIGHT - 16) / 2;
|
||||
ds->vol_icon_rect = {vol_icon_x, vol_icon_y, 16, 16};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user