diff --git a/src/sys/syscall.c b/src/sys/syscall.c index d052058..538dbdf 100644 --- a/src/sys/syscall.c +++ b/src/sys/syscall.c @@ -1476,6 +1476,11 @@ static uint64_t sys_cmd_set_mouse_speed(const syscall_args_t *args) { return 0; } +static uint64_t sys_cmd_set_mouse_cursor_scale(const syscall_args_t *args) { + wm_set_cursor_scale_tenths((int)args->arg2); + return 0; +} + static uint64_t sys_cmd_network_init(const syscall_args_t *args) { (void)args; extern int network_init(void); @@ -1501,6 +1506,11 @@ static uint64_t sys_cmd_get_mouse_speed(const syscall_args_t *args) { return mouse_speed; } +static uint64_t sys_cmd_get_mouse_cursor_scale(const syscall_args_t *args) { + (void)args; + return (uint64_t)wm_get_cursor_scale_tenths(); +} + static uint64_t sys_cmd_get_wallpaper_thumb(const syscall_args_t *args) { (void)args; return -1; @@ -2415,6 +2425,8 @@ static const syscall_handler_fn sys_cmd_table[SYS_CMD_TABLE_SIZE] = { [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_SET_MOUSE_CURSOR_SCALE] = sys_cmd_set_mouse_cursor_scale, + [SYSTEM_CMD_GET_MOUSE_CURSOR_SCALE] = sys_cmd_get_mouse_cursor_scale, [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 10db73e..5ef981b 100644 --- a/src/sys/syscall.h +++ b/src/sys/syscall.h @@ -103,6 +103,8 @@ typedef struct { #define SYSTEM_CMD_SET_KEYBOARD_LAYOUT 49 #define SYSTEM_CMD_PARALLEL_RUN 50 #define SYSTEM_CMD_GET_KEYBOARD_LAYOUT 51 +#define SYSTEM_CMD_SET_MOUSE_CURSOR_SCALE 52 +#define SYSTEM_CMD_GET_MOUSE_CURSOR_SCALE 53 #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/settings.c b/src/userland/gui/settings.c index 99a6491..04715de 100644 --- a/src/userland/gui/settings.c +++ b/src/userland/gui/settings.c @@ -62,6 +62,8 @@ 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; +static widget_slider_t slider_mouse; +static widget_slider_t slider_cursor_size; #define MAX_WALLPAPERS 10 @@ -199,16 +201,20 @@ typedef struct { char name[64]; uint32_t thumb[WALLPAPER_THUMB_W * WALLPAPER_THUMB_H]; _Bool valid; + _Bool thumb_loaded; } wallpaper_entry_t; static wallpaper_entry_t wallpapers[MAX_WALLPAPERS]; static int wallpaper_count = 0; +static _Bool wallpapers_scanned = 0; +static int next_wallpaper_thumb = 0; static _Bool desktop_snap_to_grid = 1; static _Bool desktop_auto_align = 1; static int desktop_max_rows_per_col = 10; static int desktop_max_cols = 10; static int mouse_speed = 10; +static int mouse_cursor_scale_tenths = 10; static int font_count = 0; static int selected_font = -1; @@ -229,6 +235,22 @@ static void cli_itoa(int num, char *str) { } } +static void format_scale_tenths(int scale_tenths, char *str) { + if (scale_tenths < 10) scale_tenths = 10; + if (scale_tenths > 40) scale_tenths = 40; + + char whole[4]; + cli_itoa(scale_tenths / 10, whole); + strcpy(str, whole); + + int len = 0; + while (str[len]) len++; + str[len++] = '.'; + str[len++] = (char)('0' + (scale_tenths % 10)); + str[len++] = 'x'; + str[len] = 0; +} + static void generate_lumberjack_pattern(void) { uint32_t red = 0xFFDC143C; uint32_t dark_grey = 0xFF404040; @@ -388,9 +410,15 @@ static void load_settings_icons(void) { static void decode_wallpapers_task(void *arg) { (void)arg; wallpaper_count = 0; + next_wallpaper_thumb = 0; + wallpapers_scanned = 0; + FAT32_FileInfo info[MAX_WALLPAPERS]; int count = sys_list("/Library/images/Wallpapers", info, MAX_WALLPAPERS); - if (count < 0) return; + if (count < 0) { + wallpapers_scanned = 1; + return; + } for (int i = 0; i < count && wallpaper_count < MAX_WALLPAPERS; i++) { if (info[i].is_directory) continue; // Skip directories @@ -404,6 +432,9 @@ static void decode_wallpapers_task(void *arg) { if (c1 != 'g' || c2 != 'p' || c3 != 'j') continue; wallpaper_entry_t *wp = &wallpapers[wallpaper_count]; + wp->valid = 0; + wp->thumb_loaded = 0; + // Set path char *pref = "/Library/images/Wallpapers/"; int pl = 0; while (pref[pl]) { wp->path[pl] = pref[pl]; pl++; } @@ -414,68 +445,87 @@ static void decode_wallpapers_task(void *arg) { for (int j = 0; j < nl - 4 && j < 63; j++) wp->name[j] = info[i].name[j]; wp->name[(nl-4 < 63) ? nl-4 : 63] = 0; - // Load and generate thumbnail + int tx = (wallpaper_count % 3) * (WALLPAPER_THUMB_W + 15); + int ty = (wallpaper_count / 3) * (WALLPAPER_THUMB_H + 30); + widget_button_init(&btn_wp_thumbs[wallpaper_count], 8 + tx, 306 + ty, WALLPAPER_THUMB_W + 8, WALLPAPER_THUMB_H + 26, ""); + wallpaper_count++; + } + + wallpapers_scanned = 1; +} + +static void load_wallpapers(void) { + void *job_args[1] = { NULL }; + sys_parallel_run(decode_wallpapers_task, job_args, 1); +} + +static bool load_next_wallpaper_thumb(void) { + if (!wallpapers_scanned) return false; + + while (next_wallpaper_thumb < wallpaper_count) { + wallpaper_entry_t *wp = &wallpapers[next_wallpaper_thumb]; + next_wallpaper_thumb++; + + if (wp->thumb_loaded) continue; + wp->thumb_loaded = 1; + char cache_path[256]; int cp = 0; char *cpref = "/Library/Caches/Thumbnails/"; while (cpref[cp]) { cache_path[cp] = cpref[cp]; cp++; } + + int prefix_len = 0; + char *pref = "/Library/images/Wallpapers/"; + while (pref[prefix_len] && wp->path[prefix_len] == pref[prefix_len]) prefix_len++; + int cn = 0; - while (info[i].name[cn]) { cache_path[cp+cn] = info[i].name[cn]; cn++; } + while (wp->path[prefix_len + cn]) { cache_path[cp + cn] = wp->path[prefix_len + cn]; cn++; } char *csuf = ".bin"; int cs = 0; - while (csuf[cs]) { cache_path[cp+cn+cs] = csuf[cs]; cs++; } - cache_path[cp+cn+cs] = 0; + while (csuf[cs]) { cache_path[cp + cn + cs] = csuf[cs]; cs++; } + cache_path[cp + cn + cs] = 0; int cfd = sys_open(cache_path, "r"); if (cfd >= 0) { sys_read(cfd, wp->thumb, WALLPAPER_THUMB_W * WALLPAPER_THUMB_H * 4); sys_close(cfd); wp->valid = 1; - } else { - int fd = sys_open(wp->path, "r"); - if (fd >= 0) { - int size = sys_seek(fd, 0, 2); // SEEK_END - sys_seek(fd, 0, 0); // SEEK_SET - if (size > 0 && size < 8 * 1024 * 1024) { - unsigned char *buf = (unsigned char *)malloc(size); - if (buf) { - sys_read(fd, buf, size); - int img_w, img_h, channels; - unsigned char *img = stbi_load_from_memory(buf, size, &img_w, &img_h, &channels, 4); - if (img && img_w > 0 && img_h > 0) { - scale_rgba_to_argb(img, img_w, img_h, wp->thumb, WALLPAPER_THUMB_W, WALLPAPER_THUMB_H); - wp->valid = 1; - stbi_image_free(img); - - // Save to cache - sys_mkdir("/Library/Caches"); - sys_mkdir("/Library/Caches/Thumbnails"); - int swfd = sys_open(cache_path, "w"); - if (swfd >= 0) { - sys_write_fs(swfd, wp->thumb, WALLPAPER_THUMB_W * WALLPAPER_THUMB_H * 4); - sys_close(swfd); - } - } - free(buf); - } - } - sys_close(fd); - } + return true; } - wallpaper_count++; - } -} + int fd = sys_open(wp->path, "r"); + if (fd >= 0) { + int size = sys_seek(fd, 0, 2); // SEEK_END + sys_seek(fd, 0, 0); // SEEK_SET + if (size > 0 && size < 8 * 1024 * 1024) { + unsigned char *buf = (unsigned char *)malloc(size); + if (buf) { + sys_read(fd, buf, size); + int img_w, img_h, channels; + unsigned char *img = stbi_load_from_memory(buf, size, &img_w, &img_h, &channels, 4); + if (img && img_w > 0 && img_h > 0) { + scale_rgba_to_argb(img, img_w, img_h, wp->thumb, WALLPAPER_THUMB_W, WALLPAPER_THUMB_H); + wp->valid = 1; -static void load_wallpapers(void) { - void *job_args[1] = { NULL }; - sys_parallel_run(decode_wallpapers_task, job_args, 1); + sys_mkdir("/Library/Caches"); + sys_mkdir("/Library/Caches/Thumbnails"); + int swfd = sys_open(cache_path, "w"); + if (swfd >= 0) { + sys_write_fs(swfd, wp->thumb, WALLPAPER_THUMB_W * WALLPAPER_THUMB_H * 4); + sys_close(swfd); + } + } + if (img) stbi_image_free(img); + free(buf); + } + } + sys_close(fd); + } - for (int i = 0; i < wallpaper_count; i++) { - int tx = (i % 3) * (WALLPAPER_THUMB_W + 15); - int ty = (i / 3) * (WALLPAPER_THUMB_H + 30); - widget_button_init(&btn_wp_thumbs[i], 8 + tx, 306 + ty, WALLPAPER_THUMB_W + 8, WALLPAPER_THUMB_H + 20, ""); + return true; } + + return false; } static uint32_t parse_rgb_separate(const char *r, const char *g, const char *b) { @@ -498,7 +548,6 @@ static uint32_t parse_rgb_separate(const char *r, const char *g, const char *b) static void control_panel_paint_main(ui_window_t win) { int offset_x = 8; int offset_y = 6; - int win_w = 350; int item_y = 0; int item_h = 60; @@ -815,15 +864,23 @@ static void control_panel_paint_mouse(ui_window_t win) { int section_y = offset_y + 65; ui_draw_string(win, offset_x, section_y, "Speed:", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, offset_x + 60, section_y + 8, 200, 8, 4, COLOR_DARK_PANEL); - - int knob_x = offset_x + 60 + (mouse_speed - 1) * 190 / 49; - ui_draw_rounded_rect_filled(win, knob_x, section_y + 2, 10, 14, 3, 0xFF4A90E2); + slider_mouse.value = (float)mouse_speed; + widget_slider_draw(&settings_ctx, &slider_mouse); ui_draw_string(win, offset_x + 270, section_y + 4, "x", COLOR_DARK_TEXT); char speed_str[4]; cli_itoa(mouse_speed, speed_str); ui_draw_string(win, offset_x + 280, section_y + 4, speed_str, COLOR_DARK_TEXT); + + section_y += 40; + ui_draw_string(win, offset_x, section_y, "Size:", COLOR_DARK_TEXT); + + slider_cursor_size.value = (float)mouse_cursor_scale_tenths; + widget_slider_draw(&settings_ctx, &slider_cursor_size); + + char scale_str[8]; + format_scale_tenths(mouse_cursor_scale_tenths, scale_str); + ui_draw_string(win, offset_x + 270, section_y + 4, scale_str, COLOR_DARK_TEXT); } static void on_font_scroll(void *user_data, int new_scroll_y) { @@ -982,6 +1039,7 @@ static void save_desktop_config(void) { static void save_mouse_config(void) { sys_system(SYSTEM_CMD_SET_MOUSE_SPEED, mouse_speed, 0, 0, 0); + sys_system(SYSTEM_CMD_SET_MOUSE_CURSOR_SCALE, mouse_cursor_scale_tenths, 0, 0, 0); } static int parse_ip(const char* str, net_ipv4_address_t* ip) { @@ -1006,41 +1064,7 @@ static int parse_ip(const char* str, net_ipv4_address_t* ip) { return 0; } -static void fetch_kernel_state(void) { - 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); - - net_ipv4_address_t kip; - if (sys_network_get_ip(&kip) == 0) { - char bp[4]; - net_ip[0] = 0; - for (int i=0; i<4; i++) { - cli_itoa(kip.bytes[i], bp); - strcat(net_ip, bp); - if (i < 3) strcat(net_ip, "."); - } - } - - if (sys_get_dns_server(&kip) == 0) { - char bp[4]; - net_dns[0] = 0; - for (int i=0; i<4; i++) { - cli_itoa(kip.bytes[i], bp); - strcat(net_dns, bp); - if (i < 3) strcat(net_dns, "."); - } - } - - init_dynamic_resolutions(); - load_wallpapers(); -} - static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click) { - int win_w = 350; - if (current_view != VIEW_MAIN && widget_button_handle_mouse(&btn_back, x, y, is_down, is_click, NULL)) { if (is_click) { current_view = VIEW_MAIN; @@ -1218,7 +1242,12 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click if (current_view == VIEW_MAIN) { if (widget_button_handle_mouse(&btn_main_wallpaper, x, y, is_down, is_click, NULL)) { - if (is_click) { current_view = VIEW_WALLPAPER; focused_field = -1; btn_main_wallpaper.pressed = false; } + if (is_click) { + current_view = VIEW_WALLPAPER; + focused_field = -1; + btn_main_wallpaper.pressed = false; + load_wallpapers(); + } return; } if (widget_button_handle_mouse(&btn_main_network, x, y, is_down, is_click, NULL)) { @@ -1255,18 +1284,26 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click } if (current_view == VIEW_MOUSE) { - if (is_down || is_click) { - int offset_x = 8; - int offset_y = 6; - int section_y = offset_y + 65; - if (x >= offset_x + 60 && x <= offset_x + 260 && y >= section_y && y <= section_y + 20) { - int new_speed = 1 + (x - (offset_x + 60)) * 49 / 200; - if (new_speed < 1) new_speed = 1; - if (new_speed > 50) new_speed = 50; + if (widget_slider_handle_mouse(&slider_mouse, x, y, is_down, is_click, NULL)) { + int new_speed = (int)(slider_mouse.value); + focused_field = -1; // slider doesn't use textbox, so clear focus on click + + if (new_speed != mouse_speed) { mouse_speed = new_speed; save_mouse_config(); - return; } + return; + } + + if (widget_slider_handle_mouse(&slider_cursor_size, x, y, is_down, is_click, NULL)) { + int new_scale = (int)(slider_cursor_size.value); + focused_field = -1; + + if (new_scale != mouse_cursor_scale_tenths) { + mouse_cursor_scale_tenths = new_scale; + save_mouse_config(); + } + return; } } @@ -1385,6 +1422,12 @@ static void init_settings_widgets(void) { // Display View Textboxes widget_textbox_init(&tb_custom_w, 8, 276, 60, 25, custom_res_w, 5); widget_textbox_init(&tb_custom_h, 88, 276, 60, 25, custom_res_h, 5); + + widget_slider_init(&slider_mouse, 68, 71, 200, 20, 1.0f, 50.0f, (float)mouse_speed); + slider_mouse.step = 1.0f; + + widget_slider_init(&slider_cursor_size, 68, 111, 200, 20, 10.0f, 40.0f, (float)mouse_cursor_scale_tenths); + slider_cursor_size.step = 1.0f; } int main(int argc, char **argv) { @@ -1404,6 +1447,9 @@ int main(int argc, char **argv) { 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); + mouse_cursor_scale_tenths = sys_system(SYSTEM_CMD_GET_MOUSE_CURSOR_SCALE, 0, 0, 0, 0); + if (mouse_cursor_scale_tenths < 10) mouse_cursor_scale_tenths = 10; + if (mouse_cursor_scale_tenths > 40) mouse_cursor_scale_tenths = 40; load_settings_icons(); // Set initial widget states @@ -1418,8 +1464,6 @@ int main(int argc, char **argv) { 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; @@ -1429,12 +1473,11 @@ int main(int argc, char **argv) { 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 = false; - if (ev.type == GUI_EVENT_MOUSE_DOWN || ev.type == GUI_EVENT_CLICK) { + if (ev.type == GUI_EVENT_CLICK) { down = true; } else if (ev.type == GUI_EVENT_MOUSE_MOVE) { down = (ev.arg3 & 1); @@ -1442,13 +1485,15 @@ int main(int argc, char **argv) { down = false; } - control_panel_handle_mouse( - ev.arg1, - ev.arg2, - down, - ev.type == GUI_EVENT_CLICK - ); - dirty = true; + if (ev.type != GUI_EVENT_MOUSE_MOVE || down) { + 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) { @@ -1482,10 +1527,14 @@ int main(int argc, char **argv) { ui_mark_dirty(win, 0, 0, 350, 500); } } else { - sleep(10); + if (current_view == VIEW_WALLPAPER && load_next_wallpaper_thumb()) { + control_panel_paint(win); + ui_mark_dirty(win, 0, 0, 350, 500); + } else { + sleep(10); + } } } return 0; } - diff --git a/src/userland/libc/syscall.h b/src/userland/libc/syscall.h index 135f2f0..0bce2b7 100644 --- a/src/userland/libc/syscall.h +++ b/src/userland/libc/syscall.h @@ -67,6 +67,8 @@ #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_MOUSE_CURSOR_SCALE 52 +#define SYSTEM_CMD_GET_MOUSE_CURSOR_SCALE 53 #define SYSTEM_CMD_SET_TEXT_COLOR 29 #define SYSTEM_CMD_SET_WALLPAPER_PATH 31 #define SYSTEM_CMD_RTC_SET 32 diff --git a/src/wm/libwidget.c b/src/wm/libwidget.c index 3f128c9..a82c825 100644 --- a/src/wm/libwidget.c +++ b/src/wm/libwidget.c @@ -497,3 +497,149 @@ bool widget_checkbox_handle_mouse(widget_checkbox_t *cb, int mx, int my, bool mo } return false; } + +// --- Slider --- +void widget_slider_init(widget_slider_t *sl, int x, int y, int w, int h, float min, float max, float value) { + sl->x = x; + sl->y = y; + sl->w = w; + sl->h = h; + + sl->min = min; + sl->max = max; + sl->value = value; + sl->step = 0.0f; + + sl->dragging = false; + sl->hovered = false; + sl->on_change = NULL; +} + +void widget_slider_draw(widget_context_t *ctx, widget_slider_t *sl) { + uint32_t track_bg = ctx->use_light_theme ? 0xFFD0D0D0 : 0xFF404040; + uint32_t fill_color = ctx->use_light_theme ? 0xFF007AFF : 0xFF4A90E2; + uint32_t thumb_color = ctx->use_light_theme ? 0xFF606060 : 0xFFDDDDDD; + + if (sl->dragging) { + thumb_color = ctx->use_light_theme ? 0xFF404040 : 0xFFFFFFFF; + } else if (sl->hovered) { + thumb_color = ctx->use_light_theme ? 0xFF505050 : 0xFFF0F0F0; + } + + int track_h = 4; + int track_y = sl->y + (sl->h - track_h) / 2; + + float t = (sl->value - sl->min) / (sl->max - sl->min); + if (t < 0) t = 0; + if (t > 1) t = 1; + + int thumb_x = sl->x + (int)(t * sl->w); + + if (ctx->draw_rounded_rect_filled) { + ctx->draw_rounded_rect_filled(ctx->user_data, sl->x, track_y, sl->w, track_h, 2, track_bg); + } else { + ctx->draw_rect(ctx->user_data, sl->x, track_y, sl->w, track_h, track_bg); + } + + if (ctx->draw_rounded_rect_filled) { + ctx->draw_rounded_rect_filled(ctx->user_data, sl->x, track_y, thumb_x - sl->x, track_h, 2, fill_color); + } else { + ctx->draw_rect(ctx->user_data, sl->x, track_y, thumb_x - sl->x, track_h, fill_color); + } + + int radius = sl->h / 2; + int cx = thumb_x - radius; + + if (ctx->draw_rounded_rect_filled) { + ctx->draw_rounded_rect_filled(ctx->user_data, cx, sl->y, sl->h, sl->h, radius, thumb_color); + } else { + ctx->draw_rect(ctx->user_data, cx, sl->y, sl->h, sl->h, thumb_color); + } +} + +static float widget_roundf(float x) { + return (x >= 0.0f) ? (float)((int)(x + 0.5f)) : (float)((int)(x - 0.5f)); +} + +bool widget_slider_handle_mouse(widget_slider_t *sl, int mx, int my, bool mouse_down, bool mouse_clicked, void *user_data) { + bool in_bounds = (mx >= sl->x && mx < sl->x + sl->w && + my >= sl->y && my < sl->y + sl->h); + + sl->hovered = in_bounds; + + if (mouse_clicked && in_bounds) { + sl->dragging = true; + } + + if (!mouse_down) { + sl->dragging = false; + } + + if (sl->dragging) { + float t = (float)(mx - sl->x) / (float)sl->w; + + if (t < 0) t = 0; + if (t > 1) t = 1; + + float new_value = sl->min + t * (sl->max - sl->min); + + if (sl->step > 0.0f) { + new_value = sl->min + widget_roundf((new_value - sl->min) / sl->step) * sl->step; + } + + if (new_value != sl->value) { + sl->value = new_value; + if (sl->on_change) sl->on_change(user_data, sl->value); + } + + return true; + } + + return in_bounds; +} + +static void widget_itoa(int value, char *buf) { + int i = 0; + int neg = 0; + + if (value == 0) { + buf[0] = '0'; + buf[1] = 0; + return; + } + + if (value < 0) { + neg = 1; + value = -value; + } + + while (value > 0) { + buf[i++] = (char)('0' + (value % 10)); + value /= 10; + } + + if (neg) { + buf[i++] = '-'; + } + + buf[i] = 0; + + for (int a = 0, b = i - 1; a < b; a++, b--) { + char t = buf[a]; + buf[a] = buf[b]; + buf[b] = t; + } +} + +void widget_slider_draw_value(widget_context_t *ctx, widget_slider_t *sl) { + if (!ctx->draw_string) return; + + char buf[16]; + widget_itoa((int)sl->value, buf); + + ctx->draw_string(ctx->user_data, + sl->x + sl->w + 8, + sl->y + (sl->h - 8) / 2, + buf, + ctx->use_light_theme ? 0xFF000000 : 0xFFFFFFFF); +} \ No newline at end of file diff --git a/src/wm/libwidget.h b/src/wm/libwidget.h index a0c3d25..fb0a70b 100644 --- a/src/wm/libwidget.h +++ b/src/wm/libwidget.h @@ -86,6 +86,29 @@ typedef struct { void (*on_toggle)(void *user_data, bool new_state); } widget_checkbox_t; +// --- Slider --- +typedef struct { + int x, y, w, h; + + float min; + float max; + float value; + float step; // 0 = not steps + + bool dragging; + bool hovered; + + void (*on_change)(void *user_data, float value); +} widget_slider_t; +void widget_slider_init(widget_slider_t *sl, int x, int y, int w, int h, + float min, float max, float value); + +void widget_slider_draw(widget_context_t *ctx, widget_slider_t *sl); + +bool widget_slider_handle_mouse(widget_slider_t *sl, int mx, int my, + bool mouse_down, bool mouse_clicked, + void *user_data); + void widget_checkbox_init(widget_checkbox_t *cb, int x, int y, int w, int h, const char *text, bool is_radio); void widget_checkbox_draw(widget_context_t *ctx, widget_checkbox_t *cb); bool widget_checkbox_handle_mouse(widget_checkbox_t *cb, int mx, int my, bool mouse_clicked, void *user_data); diff --git a/src/wm/wm.c b/src/wm/wm.c index 67bbf8a..9206a40 100644 --- a/src/wm/wm.c +++ b/src/wm/wm.c @@ -408,9 +408,14 @@ static uint32_t timer_ticks = 0; static int last_cursor_x = 400; static int last_cursor_y = 300; +static int last_cursor_w = 18; +static int last_cursor_h = 18; -#define CURSOR_W 18 -#define CURSOR_H 18 +#define CURSOR_BASE_W 18 +#define CURSOR_BASE_H 18 +#define CURSOR_SCALE_MIN_TENTHS 10 +#define CURSOR_SCALE_MAX_TENTHS 40 +#define CURSOR_SCALE_DEFAULT_TENTHS 10 static bool periodic_refresh_pending = false; @@ -433,10 +438,62 @@ int desktop_max_cols = 23; // Mouse Settings int mouse_speed = 10; +static int mouse_cursor_scale_tenths = CURSOR_SCALE_DEFAULT_TENTHS; static int mouse_accum_x = 0; static int mouse_accum_y = 0; Window *active_mouse_capture_win = NULL; +static int cursor_clamp_scale_tenths(int scale) { + if (scale < CURSOR_SCALE_MIN_TENTHS) return CURSOR_SCALE_MIN_TENTHS; + if (scale > CURSOR_SCALE_MAX_TENTHS) return CURSOR_SCALE_MAX_TENTHS; + return scale; +} + +static int cursor_scaled_size(int base, int scale_tenths) { + scale_tenths = cursor_clamp_scale_tenths(scale_tenths); + return (base * scale_tenths + 9) / 10; +} + +static int cursor_width_for_scale(int scale_tenths) { + return cursor_scaled_size(CURSOR_BASE_W, scale_tenths); +} + +static int cursor_height_for_scale(int scale_tenths) { + return cursor_scaled_size(CURSOR_BASE_H, scale_tenths); +} + +static int cursor_current_width(void) { + return cursor_width_for_scale(mouse_cursor_scale_tenths); +} + +static int cursor_current_height(void) { + return cursor_height_for_scale(mouse_cursor_scale_tenths); +} + +void wm_set_cursor_scale_tenths(int scale) { + scale = cursor_clamp_scale_tenths(scale); + + uint64_t rflags = wm_lock_acquire(); + if (scale != mouse_cursor_scale_tenths) { + int old_w = cursor_current_width(); + int old_h = cursor_current_height(); + + mouse_cursor_scale_tenths = scale; + + wm_mark_dirty(last_cursor_x, last_cursor_y, last_cursor_w, last_cursor_h); + wm_mark_dirty(mx, my, old_w, old_h); + wm_mark_dirty(mx, my, cursor_current_width(), cursor_current_height()); + } + wm_lock_release(rflags); +} + +int wm_get_cursor_scale_tenths(void) { + uint64_t rflags = wm_lock_acquire(); + int scale = mouse_cursor_scale_tenths; + wm_lock_release(rflags); + return scale; +} + // Helper to check if string ends with suffix static bool str_ends_with(const char *str, const char *suffix) { int str_len = 0; while(str[str_len]) str_len++; @@ -2198,7 +2255,7 @@ void draw_window(Window *win) { void draw_cursor(int x, int y) { // '.' transparent, 'w' white outline, 'b' black fill - static const char cursor_bitmap[CURSOR_H][CURSOR_W + 1] = { + static const char cursor_bitmap[CURSOR_BASE_H][CURSOR_BASE_W + 1] = { "w.................", "ww................", "wbw...............", @@ -2216,16 +2273,21 @@ void draw_cursor(int x, int y) { "wwwwbbbw..........", "....wbbw..........", ".....wbw..........", - "......ww..........." + "......ww.........." }; - for (int r = 0; r < CURSOR_H; r++) { - for (int c = 0; c < CURSOR_W; c++) { + int draw_w = cursor_current_width(); + int draw_h = cursor_current_height(); + + for (int y_off = 0; y_off < draw_h; y_off++) { + int r = (y_off * CURSOR_BASE_H) / draw_h; + for (int x_off = 0; x_off < draw_w; x_off++) { + int c = (x_off * CURSOR_BASE_W) / draw_w; char p = cursor_bitmap[r][c]; if (p == 'w') { - put_pixel(x + c, y + r, COLOR_WHITE); + put_pixel(x + x_off, y + y_off, COLOR_WHITE); } else if (p == 'b') { - put_pixel(x + c, y + r, COLOR_BLACK); + put_pixel(x + x_off, y + y_off, COLOR_BLACK); } } } @@ -2239,8 +2301,8 @@ static void erase_cursor(int x, int y) { // Clamp to screen int x1 = x < 0 ? 0 : x; int y1 = y < 0 ? 0 : y; - int x2 = x + CURSOR_W > sw ? sw : x + CURSOR_W; - int y2 = y + CURSOR_H > sh ? sh : y + CURSOR_H; + int x2 = x + cursor_current_width() > sw ? sw : x + cursor_current_width(); + int y2 = y + cursor_current_height() > sh ? sh : y + cursor_current_height(); int w = x2 - x1; int h = y2 - y1; @@ -2644,8 +2706,10 @@ void wm_paint(void) { int sh = get_screen_height(); uint64_t rflags; rflags = wm_lock_acquire(); - wm_mark_dirty(last_cursor_x, last_cursor_y, CURSOR_W, CURSOR_H); - wm_mark_dirty(mx, my, CURSOR_W, CURSOR_H); + int cursor_w = cursor_current_width(); + int cursor_h = cursor_current_height(); + wm_mark_dirty(last_cursor_x, last_cursor_y, last_cursor_w, last_cursor_h); + wm_mark_dirty(mx, my, cursor_w, cursor_h); DirtyRect dirty = graphics_get_dirty_rect(); if (menubar_dirty_pending) { @@ -2719,6 +2783,8 @@ void wm_paint(void) { draw_cursor(mx, my); last_cursor_x = mx; last_cursor_y = my; + last_cursor_w = cursor_w; + last_cursor_h = cursor_h; graphics_flip_buffer(); graphics_clear_dirty_no_lock(); wm_lock_release(rflags); @@ -3230,8 +3296,10 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) { if (my >= sh) my = sh - 1; if (move_x != 0 || move_y != 0) { - wm_mark_dirty(prev_mx, prev_my, CURSOR_W, CURSOR_H); - wm_mark_dirty(mx, my, CURSOR_W, CURSOR_H); + int cursor_w = cursor_current_width(); + int cursor_h = cursor_current_height(); + wm_mark_dirty(prev_mx, prev_my, cursor_w, cursor_h); + wm_mark_dirty(mx, my, cursor_w, cursor_h); } if (dz != 0) { @@ -3792,8 +3860,10 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) { if (prev_mx != mx || prev_my != my) { // Cursor moved - just mark dirty cursor areas - wm_mark_dirty(prev_mx, prev_my, CURSOR_W, CURSOR_H); - wm_mark_dirty(mx, my, CURSOR_W, CURSOR_H); + int cursor_w = cursor_current_width(); + int cursor_h = cursor_current_height(); + wm_mark_dirty(prev_mx, prev_my, cursor_w, cursor_h); + wm_mark_dirty(mx, my, cursor_w, cursor_h); } } diff --git a/src/wm/wm.h b/src/wm/wm.h index e1b4d98..53f367c 100644 --- a/src/wm/wm.h +++ b/src/wm/wm.h @@ -152,5 +152,7 @@ extern int desktop_max_cols; // Mouse Settings extern int mouse_speed; +void wm_set_cursor_scale_tenths(int scale); +int wm_get_cursor_scale_tenths(void); #endif