diff --git a/CMakeLists.txt b/CMakeLists.txt index 79df1c6..c602dec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ set(CLEONOS_QEMU_ACCEL_ARGS "") if(CLEONOS_QEMU_ENABLE_KVM) list(APPEND CLEONOS_QEMU_ACCEL_ARGS -enable-kvm -cpu host) endif() -set(CLEONOS_QEMU_ENABLE_USB_TABLET ON CACHE BOOL "Enable QEMU USB tablet for absolute mouse pointer") +set(CLEONOS_QEMU_ENABLE_USB_TABLET OFF CACHE BOOL "Enable QEMU USB tablet for absolute mouse pointer (requires USB HID support)") set(CLEONOS_QEMU_INPUT_ARGS "") if(CLEONOS_QEMU_ENABLE_USB_TABLET) list(APPEND CLEONOS_QEMU_INPUT_ARGS -usb -device usb-tablet) diff --git a/Makefile b/Makefile index 6ffe99f..406da71 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ MENUCONFIG_PRESET ?= DISK_IMAGE_MB ?= CLEONOS_ENABLE ?= auto QEMU_DRIVE_IMAGE ?= build/x86_64/cleonos_disk.img +CLEONOS_QEMU_ENABLE_USB_TABLET ?= OFF SHOW_COMMANDS ?= 0 V ?= 0 @@ -68,6 +69,7 @@ MENUCONFIG_SCOPE_ARG += --clks-only endif CMAKE_PASSTHROUGH_ARGS += -DCLEONOS_ENABLE=$(CLEONOS_ENABLE_EFFECTIVE) +CMAKE_PASSTHROUGH_ARGS += -DCLEONOS_QEMU_ENABLE_USB_TABLET=$(CLEONOS_QEMU_ENABLE_USB_TABLET) ifneq ($(strip $(LIMINE_SKIP_CONFIGURE)),) CMAKE_PASSTHROUGH_ARGS += -DLIMINE_SKIP_CONFIGURE=$(LIMINE_SKIP_CONFIGURE) @@ -240,8 +242,8 @@ help: > $(Q)echo " make run CMAKE_EXTRA_ARGS='-DCLEONOS_QEMU_ENABLE_KVM=ON' # force on" > $(Q)echo " make run CMAKE_EXTRA_ARGS='-DCLEONOS_QEMU_ENABLE_KVM=OFF' # force off" > $(Q)echo "QEMU USB tablet toggle:" -> $(Q)echo " make run CMAKE_EXTRA_ARGS='-DCLEONOS_QEMU_ENABLE_USB_TABLET=ON' # absolute pointer" -> $(Q)echo " make run CMAKE_EXTRA_ARGS='-DCLEONOS_QEMU_ENABLE_USB_TABLET=OFF' # disable tablet" +> $(Q)echo " make run CLEONOS_QEMU_ENABLE_USB_TABLET=OFF # default: PS/2 mouse path" +> $(Q)echo " make run CLEONOS_QEMU_ENABLE_USB_TABLET=ON # USB tablet, requires USB HID support" > $(Q)echo "" > $(Q)echo "Pass custom CMake cache args via:" > $(Q)echo " make configure CMAKE_EXTRA_ARGS='-DLIMINE_SKIP_CONFIGURE=1 -DOBJCOPY_FOR_TARGET=objcopy'" diff --git a/cleonos/CMakeLists.txt b/cleonos/CMakeLists.txt index aea5320..34f0097 100644 --- a/cleonos/CMakeLists.txt +++ b/cleonos/CMakeLists.txt @@ -1,5 +1,9 @@ file(GLOB_RECURSE USER_INC_SOURCES_ABS CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/cleonos/**/*.inc" + "${CMAKE_SOURCE_DIR}/cleonos/c/include/*.h" + "${CMAKE_SOURCE_DIR}/cleonos/c/include/**/*.h" + "${CMAKE_SOURCE_DIR}/cleonos/c/apps/*.h" + "${CMAKE_SOURCE_DIR}/cleonos/c/apps/**/*.h" ) list(SORT USER_INC_SOURCES_ABS) diff --git a/cleonos/c/apps/browser_main.c b/cleonos/c/apps/browser_main.c index d6d4aea..91ab045 100644 --- a/cleonos/c/apps/browser_main.c +++ b/cleonos/c/apps/browser_main.c @@ -1,6 +1,7 @@ #include "cmd_runtime.h" #include +#include #include #include "gumbo.h" @@ -8,15 +9,19 @@ #define USH_BROWSER_SOURCE_MAX 256U #define USH_BROWSER_HOST_MAX 128U #define USH_BROWSER_PATH_MAX 256U -#define USH_BROWSER_HTML_MAX (512U * 1024U) -#define USH_BROWSER_TEXT_MAX (160U * 1024U) +#define USH_BROWSER_HTML_MAX (256U * 1024U) +#define USH_BROWSER_TEXT_MAX (96U * 1024U) +#define USH_BROWSER_GUMBO_PARSE_MAX (96U * 1024U) +#define USH_BROWSER_GUMBO_MAX_ERRORS 0 +#define USH_BROWSER_HTML_BUF_CAP (USH_BROWSER_HTML_MAX + 1U) +#define USH_BROWSER_TEXT_BUF_CAP (USH_BROWSER_TEXT_MAX + 1U) #define USH_BROWSER_TITLE_MAX 128U #define USH_BROWSER_DNS_PACKET_MAX 512U #define USH_BROWSER_HTTP_RECV_CHUNK 2048U #define USH_BROWSER_TCP_POLL_BUDGET 200000000ULL #define USH_BROWSER_TCP_RECV_IDLE_LOOPS 40ULL -#define USH_BROWSER_LINK_MAX 128U +#define USH_BROWSER_LINK_MAX 96U #define USH_BROWSER_LINK_TEXT_MAX 96U #define USH_BROWSER_LINK_HREF_MAX 192U #define USH_BROWSER_HISTORY_MAX 16U @@ -24,7 +29,7 @@ #define USH_BROWSER_SEG_MAX 32U #define USH_BROWSER_SEG_LEN_MAX 63U #define USH_BROWSER_CSS_TEXT_MAX 4096U -#define USH_BROWSER_CSS_RULE_MAX 160U +#define USH_BROWSER_CSS_RULE_MAX 96U #define USH_BROWSER_CSS_IDENT_MAX 48U #define USH_BROWSER_ANSI_RESET "\x1B[0m" #define USH_BROWSER_ANSI_BLUE "\x1B[34m" @@ -76,9 +81,9 @@ typedef struct ush_browser_css_rule { ush_browser_style_delta delta; } ush_browser_css_rule; -static char ush_browser_html_buf[USH_BROWSER_HTML_MAX + 1U]; -static char ush_browser_http_raw_buf[USH_BROWSER_HTML_MAX + 1U]; -static char ush_browser_text_buf[USH_BROWSER_TEXT_MAX + 1U]; +static char *ush_browser_html_buf; +static char *ush_browser_http_raw_buf; +static char *ush_browser_text_buf; static char ush_browser_title[USH_BROWSER_TITLE_MAX]; static ush_browser_link ush_browser_links[USH_BROWSER_LINK_MAX]; @@ -88,6 +93,26 @@ static u64 ush_browser_link_count = 0ULL; static ush_browser_css_rule ush_browser_css_rules[USH_BROWSER_CSS_RULE_MAX]; static u64 ush_browser_css_rule_count = 0ULL; +static int ush_browser_ensure_buffers(void) { + if (ush_browser_html_buf != (char *)0 && ush_browser_http_raw_buf != (char *)0 && + ush_browser_text_buf != (char *)0) { + return 1; + } + + ush_browser_html_buf = (char *)malloc((size_t)USH_BROWSER_HTML_BUF_CAP); + ush_browser_http_raw_buf = (char *)malloc((size_t)USH_BROWSER_HTML_BUF_CAP); + ush_browser_text_buf = (char *)malloc((size_t)USH_BROWSER_TEXT_BUF_CAP); + if (ush_browser_html_buf == (char *)0 || ush_browser_http_raw_buf == (char *)0 || + ush_browser_text_buf == (char *)0) { + return 0; + } + + ush_browser_html_buf[0] = '\0'; + ush_browser_http_raw_buf[0] = '\0'; + ush_browser_text_buf[0] = '\0'; + return 1; +} + static int ush_browser_is_http_url(const char *text) { if (text == (const char *)0) { return 0; @@ -917,7 +942,7 @@ static int ush_browser_fetch_http(const char *url_text, char *out_html, u64 out_ *out_size = 0ULL; out_html[0] = '\0'; - ush_zero(ush_browser_http_raw_buf, (u64)sizeof(ush_browser_http_raw_buf)); + ush_zero(ush_browser_http_raw_buf, (u64)USH_BROWSER_HTML_BUF_CAP); if (cleonos_sys_net_available() == 0ULL) { return 0; @@ -969,11 +994,11 @@ static int ush_browser_fetch_http(const char *url_text, char *out_html, u64 out_ goto cleanup; } - while (raw_len + 1ULL < (u64)sizeof(ush_browser_http_raw_buf)) { + while (raw_len + 1ULL < (u64)USH_BROWSER_HTML_BUF_CAP) { cleonos_net_tcp_recv_req recv_req; u8 chunk[USH_BROWSER_HTTP_RECV_CHUNK]; u64 got; - u64 cap_left = (u64)sizeof(ush_browser_http_raw_buf) - 1ULL - raw_len; + u64 cap_left = (u64)USH_BROWSER_HTML_BUF_CAP - 1ULL - raw_len; recv_req.out_payload_ptr = (u64)(usize)chunk; recv_req.payload_capacity = (u64)sizeof(chunk); @@ -1124,7 +1149,7 @@ static void ush_browser_text_newline(void) { } } - if (ush_browser_text_len + 1ULL >= (u64)sizeof(ush_browser_text_buf)) { + if (ush_browser_text_len + 1ULL >= (u64)USH_BROWSER_TEXT_BUF_CAP) { return; } @@ -1134,7 +1159,7 @@ static void ush_browser_text_newline(void) { } static void ush_browser_text_append_char(char ch) { - if (ush_browser_text_len + 1ULL >= (u64)sizeof(ush_browser_text_buf)) { + if (ush_browser_text_len + 1ULL >= (u64)USH_BROWSER_TEXT_BUF_CAP) { return; } @@ -1180,7 +1205,7 @@ static void ush_browser_text_append_raw(const char *text) { } while (text[i] != '\0') { - if (ush_browser_text_len + 1ULL >= (u64)sizeof(ush_browser_text_buf)) { + if (ush_browser_text_len + 1ULL >= (u64)USH_BROWSER_TEXT_BUF_CAP) { return; } @@ -2371,19 +2396,89 @@ static int ush_browser_read_file(const ush_state *sh, const char *arg, char *out return (total > 0ULL) ? 1 : 0; } -static int ush_browser_render_html(const char *html, u64 html_size) { - GumboOutput *output; - ush_browser_style root_style; +static int ush_browser_render_html_fallback(const char *html, u64 html_size) { + u64 i = 0ULL; + int in_tag = 0; + int pending_space = 0; if (html == (const char *)0 || html_size == 0ULL) { return 0; } - output = gumbo_parse_with_options(&kGumboDefaultOptions, html, (size_t)html_size); - if (output == (GumboOutput *)0 || output->root == (GumboNode *)0) { + ush_browser_link_count = 0ULL; + ush_browser_css_rule_count = 0ULL; + ush_browser_text_reset(); + ush_zero(ush_browser_title, (u64)sizeof(ush_browser_title)); + ush_copy(ush_browser_title, (u64)sizeof(ush_browser_title), "HTML fallback"); + + while (i < html_size && html[i] != '\0') { + char ch = html[i]; + + if (ch == '<') { + in_tag = 1; + if (pending_space == 0) { + ush_browser_text_append_char(' '); + pending_space = 1; + } + i++; + continue; + } + + if (in_tag != 0) { + if (ch == '>') { + in_tag = 0; + } + i++; + continue; + } + + if (ch == '&') { + ush_browser_text_append_char(' '); + pending_space = 1; + i++; + continue; + } + + ush_browser_text_append_char(ch); + pending_space = (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') ? 1 : 0; + i++; + } + + ush_browser_text_trim_trailing_spaces(); + if (ush_browser_text_len == 0ULL) { + ush_browser_text_append("[browser] no renderable text"); + } + + return 1; +} + +static int ush_browser_render_html(const char *html, u64 html_size) { + GumboOutput *output; + GumboOptions options; + ush_browser_style root_style; + u64 parse_size; + + if (html == (const char *)0 || html_size == 0ULL) { return 0; } + parse_size = html_size; + if (parse_size > (u64)USH_BROWSER_GUMBO_PARSE_MAX) { + parse_size = (u64)USH_BROWSER_GUMBO_PARSE_MAX; + } + + options = kGumboDefaultOptions; + options.max_errors = USH_BROWSER_GUMBO_MAX_ERRORS; + options.stop_on_first_error = false; + + output = gumbo_parse_with_options(&options, html, (size_t)parse_size); + if (output == (GumboOutput *)0 || output->root == (GumboNode *)0) { + if (output != (GumboOutput *)0) { + gumbo_destroy_output(&options, output); + } + return ush_browser_render_html_fallback(html, parse_size); + } + ush_browser_link_count = 0ULL; ush_browser_css_rule_count = 0ULL; ush_browser_text_reset(); @@ -2395,7 +2490,7 @@ static int ush_browser_render_html(const char *html, u64 html_size) { ush_browser_walk_dom_styled(output->root, &root_style); ush_browser_text_trim_trailing_spaces(); - gumbo_destroy_output(&kGumboDefaultOptions, output); + gumbo_destroy_output(&options, output); return 1; } @@ -2929,6 +3024,11 @@ static int ush_cmd_browser(const ush_state *sh, const char *arg) { return 0; } + if (ush_browser_ensure_buffers() == 0) { + ush_writeln("browser: memory allocation failed"); + return 0; + } + for (i = 0ULL; i < (u64)USH_BROWSER_HISTORY_MAX; i++) { history[i][0] = '\0'; } @@ -2953,7 +3053,7 @@ static int ush_cmd_browser(const ush_state *sh, const char *arg) { } for (;;) { - if (ush_browser_load_source(sh, current_source, ush_browser_html_buf, (u64)sizeof(ush_browser_html_buf), + if (ush_browser_load_source(sh, current_source, ush_browser_html_buf, (u64)USH_BROWSER_HTML_BUF_CAP, &html_size) == 0) { if (ush_browser_is_https_url(current_source) != 0) { ush_writeln("browser: https:// is not supported yet"); diff --git a/cleonos/c/apps/help_main.c b/cleonos/c/apps/help_main.c index cf479b0..cd34334 100644 --- a/cleonos/c/apps/help_main.c +++ b/cleonos/c/apps/help_main.c @@ -16,7 +16,7 @@ static int ush_cmd_help(void) { ush_writeln(" bmpview [cols]"); ush_writeln(" qrcode [--ecc ] "); ush_writeln(" vim [file] (vim-like editor: normal/insert/:w/:q/:wq)"); - ush_writeln(" uwm (user-space window manager demo)"); + ush_writeln(" uwm (user-space window manager)"); ush_writeln(" wavplay [steps] [ticks] / wavplay --stop"); ush_writeln(" fastfetch [--plain]"); ush_writeln(" doom [wad_path] (framebuffer bootstrap renderer)"); diff --git a/cleonos/c/apps/shell/shell_cmd.c b/cleonos/c/apps/shell/shell_cmd.c index 810f046..be82dad 100644 --- a/cleonos/c/apps/shell/shell_cmd.c +++ b/cleonos/c/apps/shell/shell_cmd.c @@ -138,7 +138,7 @@ static int ush_cmd_help(void) { ush_writeln(" bmpview [cols]"); ush_writeln(" qrcode [--ecc ] "); ush_writeln(" vim [file] (vim-like editor)"); - ush_writeln(" uwm (user-space window manager demo)"); + ush_writeln(" uwm (user-space window manager)"); ush_writeln(" wavplay [steps] [ticks] / wavplay --stop"); ush_writeln(" fastfetch [--plain]"); ush_writeln(" doom [wad_path] (framebuffer bootstrap renderer)"); diff --git a/cleonos/c/apps/uwm/uwm.h b/cleonos/c/apps/uwm/uwm.h index b94c2bf..974f74d 100644 --- a/cleonos/c/apps/uwm/uwm.h +++ b/cleonos/c/apps/uwm/uwm.h @@ -3,33 +3,65 @@ #include "../cmd_runtime.h" -#define USH_UWM_WINDOW_COUNT 3U +#define USH_UWM_APP_COUNT 3U +#define USH_UWM_TASKBAR_INDEX 3 +#define USH_UWM_START_INDEX 4 +#define USH_UWM_WINDOW_COUNT 5U #define USH_UWM_TTY_DISPLAY 1ULL -#define USH_UWM_TITLE_DRAG_HEIGHT 18 -#define USH_UWM_MOVE_STEP 12 + +#define USH_UWM_TOP_CLAMP_Y 24 +#define USH_UWM_TITLE_H 32 +#define USH_UWM_TASKBAR_H 48 +#define USH_UWM_START_W 320 +#define USH_UWM_START_H 360 +#define USH_UWM_APP_START_W 360 +#define USH_UWM_APP_START_H 240 +#define USH_UWM_MIN_WINDOW_W 220 +#define USH_UWM_MIN_WINDOW_H 150 +#define USH_UWM_MOVE_STEP 16 +#define USH_UWM_RESIZE_GRIP 18 +#define USH_UWM_CONTROL_W 46 #define USH_UWM_EVENT_BUDGET 128ULL #define USH_UWM_STARTUP_KEY_DRAIN_MAX 256ULL -#define USH_UWM_MIN_WINDOW_W 180 -#define USH_UWM_MIN_WINDOW_H 120 -#define USH_UWM_TOP_CLAMP_Y 24 +#define USH_UWM_IDLE_SPINS 32 + +#define USH_UWM_TASKBAR_START_W 48 +#define USH_UWM_TASKBAR_SEARCH_W 220 +#define USH_UWM_TASKBAR_BUTTON_W 132 +#define USH_UWM_TASKBAR_BUTTON_GAP 6 #define USH_UWM_KEY_LEFT 1ULL #define USH_UWM_KEY_RIGHT 2ULL #define USH_UWM_KEY_UP 3ULL #define USH_UWM_KEY_DOWN 4ULL +#define USH_UWM_WM_FLAG_TOPMOST 0x1ULL + typedef unsigned int ush_uwm_u32; +typedef enum ush_uwm_window_kind { + USH_UWM_KIND_APP = 0, + USH_UWM_KIND_TASKBAR = 1, + USH_UWM_KIND_START = 2 +} ush_uwm_window_kind; + typedef struct ush_uwm_window { u64 id; int x; int y; int w; int h; - ush_uwm_u32 color; ush_uwm_u32 *pixels; u64 pixel_count; int alive; + int minimized; + int closed; + int topmost; + int dirty; + ush_uwm_window_kind kind; + ush_uwm_u32 accent; + char title[32]; + char subtitle[64]; } ush_uwm_window; typedef struct ush_uwm_session { @@ -40,24 +72,47 @@ typedef struct ush_uwm_session { int drag_window; int drag_offset_x; int drag_offset_y; + int resizing; + int resize_window; + int resize_start_x; + int resize_start_y; + int resize_start_w; + int resize_start_h; + int resize_pending_w; + int resize_pending_h; + int start_open; + u64 mouse_packet_seen; u64 tty_before; int tty_switched; + char last_error[96]; ush_uwm_window windows[USH_UWM_WINDOW_COUNT]; } ush_uwm_session; int ush_uwm_window_index_valid(int index); +int ush_uwm_app_index_valid(int index); int ush_uwm_clampi(int value, int min_value, int max_value); int ush_uwm_u64_as_i32(u64 raw); void ush_uwm_drain_startup_keys(void); -void ush_uwm_render_content(ush_uwm_window *win); int ush_uwm_alloc_pixels(ush_uwm_window *win); +int ush_uwm_replace_pixels(ush_uwm_window *win, int width, int height); +void ush_uwm_render_window(ush_uwm_session *sess, int index); +void ush_uwm_refresh_window(ush_uwm_session *sess, int index); int ush_uwm_create_window(ush_uwm_window *win); int ush_uwm_present_window(const ush_uwm_window *win); +void ush_uwm_destroy_kernel_window(ush_uwm_window *win); void ush_uwm_destroy_window(ush_uwm_window *win); int ush_uwm_window_move_clamped(ush_uwm_session *sess, int index, int target_x, int target_y); +int ush_uwm_window_resize(ush_uwm_session *sess, int index, int target_w, int target_h); void ush_uwm_set_active(ush_uwm_session *sess, int index); void ush_uwm_focus_next(ush_uwm_session *sess); +void ush_uwm_minimize_window(ush_uwm_session *sess, int index); +void ush_uwm_close_window(ush_uwm_session *sess, int index); +void ush_uwm_restore_window(ush_uwm_session *sess, int index); +void ush_uwm_toggle_topmost(ush_uwm_session *sess, int index); +void ush_uwm_toggle_start(ush_uwm_session *sess); +void ush_uwm_close_start(ush_uwm_session *sess); +void ush_uwm_refresh_taskbar(ush_uwm_session *sess); void ush_uwm_handle_event(ush_uwm_session *sess, int window_index, const cleonos_wm_event *event, int *running); int ush_uwm_loop(ush_uwm_session *sess); diff --git a/cleonos/c/apps/uwm/uwm_app.c b/cleonos/c/apps/uwm/uwm_app.c index a06b959..749127e 100644 --- a/cleonos/c/apps/uwm/uwm_app.c +++ b/cleonos/c/apps/uwm/uwm_app.c @@ -2,16 +2,16 @@ static void ush_uwm_usage(void) { ush_writeln("usage: uwm"); - ush_writeln("wm mode: kernel compositor + user window manager"); - ush_writeln("keys: q quit, tab focus next, 1/2/3 focus, wasd/arrow move"); - ush_writeln("mouse: drag focused window by title bar"); + ush_writeln("keys: q quit, tab focus, 1/2/3 restore, wasd/arrow move"); + ush_writeln("keys: m minimize, x close, t pin top, +/- resize"); + ush_writeln("mouse: drag titlebar, resize bottom-right, use taskbar/start"); } static int ush_uwm_parse_args(const char *arg) { char first[USH_PATH_MAX]; const char *rest = ""; - if (arg == (const char *)0 || arg[0] == '\0') { + if (arg == (const char *)0 || arg[0] == 0) { return 1; } @@ -19,7 +19,7 @@ static int ush_uwm_parse_args(const char *arg) { return 0; } - if (rest != (const char *)0 && rest[0] != '\0') { + if (rest != (const char *)0 && rest[0] != 0) { return 0; } @@ -30,74 +30,170 @@ static int ush_uwm_parse_args(const char *arg) { return 0; } +static int ush_uwm_fail(ush_uwm_session *sess, const char *message) { + if (sess != (ush_uwm_session *)0 && message != (const char *)0) { + ush_copy(sess->last_error, (u64)sizeof(sess->last_error), message); + } + + return 0; +} + +static int ush_uwm_work_bottom(const ush_uwm_session *sess) { + int bottom; + + if (sess == (const ush_uwm_session *)0) { + return USH_UWM_TOP_CLAMP_Y; + } + + bottom = sess->screen_h - USH_UWM_TASKBAR_H; + if (bottom < USH_UWM_TOP_CLAMP_Y) { + bottom = USH_UWM_TOP_CLAMP_Y; + } + + return bottom; +} + +static int ush_uwm_fit_dimension(int wanted, int min_value, int max_value) { + if (max_value < 64) { + return 64; + } + + if (max_value < min_value) { + return max_value; + } + + return ush_uwm_clampi(wanted, min_value, max_value); +} + +static void ush_uwm_init_window(ush_uwm_window *win, ush_uwm_window_kind kind, const char *title, const char *subtitle, + int x, int y, int w, int h, ush_uwm_u32 accent, int topmost, int closed) { + if (win == (ush_uwm_window *)0) { + return; + } + + win->id = 0ULL; + win->x = x; + win->y = y; + win->w = w; + win->h = h; + win->pixels = (ush_uwm_u32 *)0; + win->pixel_count = 0ULL; + win->alive = 0; + win->minimized = 0; + win->closed = closed; + win->topmost = topmost; + win->dirty = 1; + win->kind = kind; + win->accent = accent; + ush_copy(win->title, (u64)sizeof(win->title), title); + ush_copy(win->subtitle, (u64)sizeof(win->subtitle), subtitle); +} + +static int ush_uwm_boot_window(ush_uwm_session *sess, int index) { + ush_uwm_window *win; + + if (sess == (ush_uwm_session *)0 || ush_uwm_window_index_valid(index) == 0) { + return 0; + } + + win = &sess->windows[index]; + if (win->closed != 0) { + return 0; + } + + if (win->pixels == (ush_uwm_u32 *)0 && ush_uwm_alloc_pixels(win) == 0) { + return 0; + } + + ush_uwm_render_window(sess, index); + if (ush_uwm_create_window(win) == 0) { + return 0; + } + + if (ush_uwm_present_window(win) == 0) { + ush_uwm_destroy_kernel_window(win); + return 0; + } + + return 1; +} + int ush_uwm_prepare_session(ush_uwm_session *sess) { cleonos_fb_info fb; - int i; + int work_bottom; + int work_h; int base_w; int base_h; - const int x_offsets[USH_UWM_WINDOW_COUNT] = {64, 220, 380}; - const int y_offsets[USH_UWM_WINDOW_COUNT] = {80, 140, 220}; - const ush_uwm_u32 colors[USH_UWM_WINDOW_COUNT] = {0x002B3C66UL, 0x00385C7AUL, 0x004A7288UL}; + int start_w; + int start_h; + int app_gap; + int i; + const char *titles[USH_UWM_APP_COUNT] = {"FILE EXPLORER", "NOTEPAD", "EDGE"}; + const char *subtitles[USH_UWM_APP_COUNT] = {"LOCAL DISK AND SYSTEM FILES", "EDIT TEXT INSIDE CLEONOS", + "WEB PREVIEW AND HTTP TOOLS"}; + const ush_uwm_u32 accents[USH_UWM_APP_COUNT] = {0x000078D7U, 0x0000A300U, 0x00007ACCU}; if (sess == (ush_uwm_session *)0) { return 0; } ush_zero(sess, (u64)sizeof(*sess)); + ush_copy(sess->last_error, (u64)sizeof(sess->last_error), "uwm: init failed"); + if (cleonos_sys_fb_info(&fb) == 0ULL || fb.width == 0ULL || fb.height == 0ULL || fb.bpp != 32ULL) { - return 0; + return ush_uwm_fail(sess, "uwm: framebuffer unavailable"); } if (fb.width > 4096ULL || fb.height > 4096ULL) { - return 0; + return ush_uwm_fail(sess, "uwm: framebuffer is larger than 4096x4096"); } sess->screen_w = (int)fb.width; sess->screen_h = (int)fb.height; sess->active_window = 0; - sess->dragging = 0; sess->drag_window = -1; - sess->drag_offset_x = 0; - sess->drag_offset_y = 0; + sess->resize_window = -1; sess->tty_before = cleonos_sys_tty_active(); - sess->tty_switched = 0; - base_w = sess->screen_w / 3; - base_h = sess->screen_h / 3; - if (base_w < USH_UWM_MIN_WINDOW_W) { - base_w = USH_UWM_MIN_WINDOW_W; - } - if (base_h < USH_UWM_MIN_WINDOW_H) { - base_h = USH_UWM_MIN_WINDOW_H; - } - if (base_w > sess->screen_w - 40) { - base_w = sess->screen_w - 40; - } - if (base_h > sess->screen_h - 40) { - base_h = sess->screen_h - 40; + work_bottom = ush_uwm_work_bottom(sess); + work_h = work_bottom - USH_UWM_TOP_CLAMP_Y; + if (work_h < 80) { + work_h = 80; } - for (i = 0; i < (int)USH_UWM_WINDOW_COUNT; i++) { - ush_uwm_window *win = &sess->windows[i]; + base_w = ush_uwm_fit_dimension(USH_UWM_APP_START_W, USH_UWM_MIN_WINDOW_W, sess->screen_w - 48); + base_h = ush_uwm_fit_dimension(USH_UWM_APP_START_H, USH_UWM_MIN_WINDOW_H, work_h - 32); + app_gap = (sess->screen_w > 900) ? 120 : 58; + + for (i = 0; i < (int)USH_UWM_APP_COUNT; i++) { int max_x = sess->screen_w - base_w; - int max_y = sess->screen_h - base_h; + int max_y = work_bottom - base_h; + int x = 56 + (i * app_gap); + int y = 64 + (i * 46); if (max_x < 0) { max_x = 0; } - if (max_y < 0) { - max_y = 0; + if (max_y < USH_UWM_TOP_CLAMP_Y) { + max_y = USH_UWM_TOP_CLAMP_Y; } - win->id = 0ULL; - win->x = ush_uwm_clampi(x_offsets[i], 0, max_x); - win->y = ush_uwm_clampi(y_offsets[i], USH_UWM_TOP_CLAMP_Y, max_y); - win->w = base_w; - win->h = base_h; - win->color = colors[i]; - win->pixels = (ush_uwm_u32 *)0; - win->pixel_count = 0ULL; - win->alive = 0; + ush_uwm_init_window(&sess->windows[i], USH_UWM_KIND_APP, titles[i], subtitles[i], ush_uwm_clampi(x, 0, max_x), + ush_uwm_clampi(y, USH_UWM_TOP_CLAMP_Y, max_y), base_w, base_h, accents[i], 0, 0); + } + + ush_uwm_init_window(&sess->windows[USH_UWM_TASKBAR_INDEX], USH_UWM_KIND_TASKBAR, "TASKBAR", "", 0, + sess->screen_h - USH_UWM_TASKBAR_H, sess->screen_w, USH_UWM_TASKBAR_H, 0x000078D7U, 1, 0); + if (sess->windows[USH_UWM_TASKBAR_INDEX].y < USH_UWM_TOP_CLAMP_Y) { + sess->windows[USH_UWM_TASKBAR_INDEX].y = USH_UWM_TOP_CLAMP_Y; + } + + start_w = ush_uwm_fit_dimension(USH_UWM_START_W, 180, sess->screen_w - 16); + start_h = ush_uwm_fit_dimension(USH_UWM_START_H, 160, work_h - 8); + ush_uwm_init_window(&sess->windows[USH_UWM_START_INDEX], USH_UWM_KIND_START, "START", "", 0, + sess->screen_h - USH_UWM_TASKBAR_H - start_h, start_w, start_h, 0x000078D7U, 1, 1); + if (sess->windows[USH_UWM_START_INDEX].y < USH_UWM_TOP_CLAMP_Y) { + sess->windows[USH_UWM_START_INDEX].y = USH_UWM_TOP_CLAMP_Y; } return 1; @@ -105,30 +201,34 @@ int ush_uwm_prepare_session(ush_uwm_session *sess) { int ush_uwm_start(ush_uwm_session *sess) { int i; + int started = 0; if (sess == (ush_uwm_session *)0) { return 0; } - for (i = 0; i < (int)USH_UWM_WINDOW_COUNT; i++) { - ush_uwm_window *win = &sess->windows[i]; + if (ush_uwm_boot_window(sess, USH_UWM_TASKBAR_INDEX) == 0) { + return ush_uwm_fail(sess, "uwm: taskbar create failed"); + } - if (ush_uwm_alloc_pixels(win) == 0) { - return 0; - } - - if (ush_uwm_create_window(win) == 0) { - return 0; - } - - ush_uwm_render_content(win); - if (ush_uwm_present_window(win) == 0) { - return 0; + for (i = 0; i < (int)USH_UWM_APP_COUNT; i++) { + if (ush_uwm_boot_window(sess, i) != 0) { + started++; } } - sess->active_window = (int)USH_UWM_WINDOW_COUNT - 1; - ush_uwm_set_active(sess, sess->active_window); + if (started == 0) { + return ush_uwm_fail(sess, "uwm: app window create failed"); + } + + for (i = (int)USH_UWM_APP_COUNT - 1; i >= 0; i--) { + if (sess->windows[i].alive != 0) { + ush_uwm_set_active(sess, i); + break; + } + } + + ush_uwm_refresh_taskbar(sess); return 1; } @@ -185,7 +285,7 @@ int ush_cmd_uwm(const char *arg) { } if (ush_uwm_prepare_session(&sess) == 0) { - ush_writeln("uwm: framebuffer unavailable"); + ush_writeln(sess.last_error); return 0; } @@ -195,13 +295,10 @@ int ush_cmd_uwm(const char *arg) { if (ush_uwm_start(&sess) == 0) { ush_uwm_stop(&sess); ush_uwm_restore_tty(&sess); - ush_writeln("uwm: kernel wm unavailable or init failed"); + ush_writeln(sess.last_error); return 0; } - ush_writeln("uwm: kernel window framework online"); - ush_writeln("uwm: q quit | tab focus | 1/2/3 focus | wasd/arrow move"); - if (ush_uwm_loop(&sess) != 0) { success = 1; } diff --git a/cleonos/c/apps/uwm/uwm_events.c b/cleonos/c/apps/uwm/uwm_events.c index 6a0eb18..53307e1 100644 --- a/cleonos/c/apps/uwm/uwm_events.c +++ b/cleonos/c/apps/uwm/uwm_events.c @@ -1,7 +1,67 @@ #include "uwm.h" +static int ush_uwm_hit_close(const ush_uwm_window *win, int x, int y) { + return (win != (const ush_uwm_window *)0 && x >= win->w - USH_UWM_CONTROL_W && y >= 0 && + y < USH_UWM_TITLE_H) + ? 1 + : 0; +} + +static int ush_uwm_hit_minimize(const ush_uwm_window *win, int x, int y) { + return (win != (const ush_uwm_window *)0 && x >= win->w - (USH_UWM_CONTROL_W * 2) && + x < win->w - USH_UWM_CONTROL_W && y >= 0 && y < USH_UWM_TITLE_H) + ? 1 + : 0; +} + +static int ush_uwm_hit_topmost(const ush_uwm_window *win, int x, int y) { + return (win != (const ush_uwm_window *)0 && x >= win->w - (USH_UWM_CONTROL_W * 3) && + x < win->w - (USH_UWM_CONTROL_W * 2) && y >= 0 && y < USH_UWM_TITLE_H) + ? 1 + : 0; +} + +static int ush_uwm_hit_resize(const ush_uwm_window *win, int x, int y) { + return (win != (const ush_uwm_window *)0 && x >= win->w - USH_UWM_RESIZE_GRIP && + y >= win->h - USH_UWM_RESIZE_GRIP) + ? 1 + : 0; +} + +static int ush_uwm_taskbar_app_x(const ush_uwm_window *taskbar) { + int search_w = USH_UWM_TASKBAR_SEARCH_W; + + if (taskbar == (const ush_uwm_window *)0) { + return USH_UWM_TASKBAR_START_W + 8; + } + + if (taskbar->w < 720) { + search_w = 128; + } + if (taskbar->w < 520) { + search_w = 0; + } + + return USH_UWM_TASKBAR_START_W + 8 + ((search_w > 0) ? (search_w + 10) : 0); +} + +static void ush_uwm_finish_resize(ush_uwm_session *sess) { + int index; + + if (sess == (ush_uwm_session *)0 || sess->resizing == 0) { + return; + } + + index = sess->resize_window; + sess->resizing = 0; + sess->resize_window = -1; + if (ush_uwm_app_index_valid(index) != 0) { + (void)ush_uwm_window_resize(sess, index, sess->resize_pending_w, sess->resize_pending_h); + } +} + static void ush_uwm_handle_key_event(ush_uwm_session *sess, u64 key, int *running) { - int idx = 0; + int idx; int dx = 0; int dy = 0; @@ -14,21 +74,43 @@ static void ush_uwm_handle_key_event(ush_uwm_session *sess, u64 key, int *runnin return; } - if (key == (u64)'\t') { + if (key == (u64)' ') { ush_uwm_focus_next(sess); return; } - if (key >= (u64)'1' && key <= (u64)'3') { - int candidate = (int)(key - (u64)'1'); - if (ush_uwm_window_index_valid(candidate) != 0) { - ush_uwm_set_active(sess, candidate); - } + if (key == (u64)'1' || key == (u64)'2' || key == (u64)'3') { + ush_uwm_restore_window(sess, (int)(key - (u64)'1')); return; } idx = sess->active_window; - if (ush_uwm_window_index_valid(idx) == 0 || sess->windows[idx].alive == 0) { + if (ush_uwm_app_index_valid(idx) == 0 || sess->windows[idx].alive == 0) { + return; + } + + if (key == (u64)'m' || key == (u64)'M') { + ush_uwm_minimize_window(sess, idx); + return; + } + + if (key == (u64)'x' || key == (u64)'X') { + ush_uwm_close_window(sess, idx); + return; + } + + if (key == (u64)'t' || key == (u64)'T') { + ush_uwm_toggle_topmost(sess, idx); + return; + } + + if (key == (u64)'+' || key == (u64)'=') { + (void)ush_uwm_window_resize(sess, idx, sess->windows[idx].w + 32, sess->windows[idx].h + 24); + return; + } + + if (key == (u64)'-') { + (void)ush_uwm_window_resize(sess, idx, sess->windows[idx].w - 32, sess->windows[idx].h - 24); return; } @@ -47,7 +129,66 @@ static void ush_uwm_handle_key_event(ush_uwm_session *sess, u64 key, int *runnin } } +static void ush_uwm_handle_taskbar_click(ush_uwm_session *sess, int local_x, int local_y) { + const ush_uwm_window *taskbar; + int app_x; + int i; + + if (sess == (ush_uwm_session *)0) { + return; + } + + taskbar = &sess->windows[USH_UWM_TASKBAR_INDEX]; + if (local_y < 0 || local_y >= taskbar->h) { + return; + } + + if (local_x >= 0 && local_x < USH_UWM_TASKBAR_START_W) { + ush_uwm_toggle_start(sess); + return; + } + + app_x = ush_uwm_taskbar_app_x(taskbar); + for (i = 0; i < (int)USH_UWM_APP_COUNT; i++) { + ush_uwm_window *app = &sess->windows[i]; + + if (local_x >= app_x && local_x < app_x + USH_UWM_TASKBAR_BUTTON_W && local_y >= 5 && local_y < taskbar->h - 5) { + if (app->alive != 0 && app->minimized == 0 && sess->active_window == i) { + ush_uwm_minimize_window(sess, i); + } else { + ush_uwm_restore_window(sess, i); + } + return; + } + app_x += USH_UWM_TASKBAR_BUTTON_W + USH_UWM_TASKBAR_BUTTON_GAP; + } +} + +static void ush_uwm_handle_start_click(ush_uwm_session *sess, int local_x, int local_y) { + int i; + + if (sess == (ush_uwm_session *)0) { + return; + } + + if (local_x < 52 && local_y >= sess->windows[USH_UWM_START_INDEX].h - 52) { + ush_uwm_close_start(sess); + return; + } + + for (i = 0; i < (int)USH_UWM_APP_COUNT; i++) { + int y = 78 + (i * 44); + + if (local_x >= 66 && local_x < sess->windows[USH_UWM_START_INDEX].w - 16 && local_y >= y && local_y < y + 34) { + ush_uwm_restore_window(sess, i); + ush_uwm_close_start(sess); + return; + } + } +} + static void ush_uwm_handle_mouse_button(ush_uwm_session *sess, int window_index, const cleonos_wm_event *event) { + ush_uwm_window *win; u64 buttons; u64 changed; int local_x; @@ -60,6 +201,7 @@ static void ush_uwm_handle_mouse_button(ush_uwm_session *sess, int window_index, return; } + win = &sess->windows[window_index]; buttons = event->arg0; changed = event->arg1; local_x = ush_uwm_u64_as_i32(event->arg2); @@ -71,39 +213,144 @@ static void ush_uwm_handle_mouse_button(ush_uwm_session *sess, int window_index, return; } - if (left_down != 0) { - if (local_y >= 0 && local_y < USH_UWM_TITLE_DRAG_HEIGHT) { - sess->dragging = 1; - sess->drag_window = window_index; - sess->drag_offset_x = local_x; - sess->drag_offset_y = local_y; - sess->active_window = window_index; + if (left_down == 0) { + if (sess->dragging != 0 && sess->drag_window == window_index) { + sess->dragging = 0; + sess->drag_window = -1; } - } else if (sess->dragging != 0 && sess->drag_window == window_index) { - sess->dragging = 0; + if (sess->resizing != 0 && sess->resize_window == window_index) { + ush_uwm_finish_resize(sess); + } + return; + } + + if (win->kind == USH_UWM_KIND_TASKBAR) { + ush_uwm_handle_taskbar_click(sess, local_x, local_y); + return; + } + + if (win->kind == USH_UWM_KIND_START) { + ush_uwm_handle_start_click(sess, local_x, local_y); + return; + } + + if (ush_uwm_app_index_valid(window_index) == 0) { + return; + } + + if (sess->start_open != 0) { + ush_uwm_close_start(sess); + } + + ush_uwm_set_active(sess, window_index); + + if (ush_uwm_hit_close(win, local_x, local_y) != 0) { + ush_uwm_close_window(sess, window_index); + return; + } + + if (ush_uwm_hit_minimize(win, local_x, local_y) != 0) { + ush_uwm_minimize_window(sess, window_index); + return; + } + + if (ush_uwm_hit_topmost(win, local_x, local_y) != 0) { + ush_uwm_toggle_topmost(sess, window_index); + return; + } + + if (ush_uwm_hit_resize(win, local_x, local_y) != 0) { + sess->resizing = 1; + sess->resize_window = window_index; + sess->mouse_packet_seen = 0ULL; + sess->resize_start_x = win->x + local_x; + sess->resize_start_y = win->y + local_y; + sess->resize_start_w = win->w; + sess->resize_start_h = win->h; + sess->resize_pending_w = win->w; + sess->resize_pending_h = win->h; + return; + } + + if (local_y >= 0 && local_y < USH_UWM_TITLE_H) { + sess->dragging = 1; + sess->drag_window = window_index; + sess->mouse_packet_seen = 0ULL; + sess->drag_offset_x = local_x; + sess->drag_offset_y = local_y; } } static void ush_uwm_handle_mouse_move(ush_uwm_session *sess, int window_index, const cleonos_wm_event *event) { int global_x; int global_y; - int new_x; - int new_y; if (sess == (ush_uwm_session *)0 || event == (const cleonos_wm_event *)0 || ush_uwm_window_index_valid(window_index) == 0) { return; } - if (sess->dragging == 0 || sess->drag_window != window_index) { + global_x = ush_uwm_u64_as_i32(event->arg0); + global_y = ush_uwm_u64_as_i32(event->arg1); + + if (sess->resizing != 0 && sess->resize_window == window_index && ush_uwm_app_index_valid(window_index) != 0) { + sess->resize_pending_w = sess->resize_start_w + (global_x - sess->resize_start_x); + sess->resize_pending_h = sess->resize_start_h + (global_y - sess->resize_start_y); return; } - global_x = ush_uwm_u64_as_i32(event->arg0); - global_y = ush_uwm_u64_as_i32(event->arg1); - new_x = global_x - sess->drag_offset_x; - new_y = global_y - sess->drag_offset_y; - (void)ush_uwm_window_move_clamped(sess, window_index, new_x, new_y); + if (sess->dragging != 0 && sess->drag_window == window_index && ush_uwm_app_index_valid(window_index) != 0) { + (void)ush_uwm_window_move_clamped(sess, window_index, global_x - sess->drag_offset_x, + global_y - sess->drag_offset_y); + } +} + +static int ush_uwm_drive_direct_pointer(ush_uwm_session *sess) { + cleonos_mouse_state mouse; + int global_x; + int global_y; + + if (sess == (ush_uwm_session *)0 || (sess->dragging == 0 && sess->resizing == 0)) { + return 0; + } + + ush_zero(&mouse, (u64)sizeof(mouse)); + if (cleonos_sys_mouse_state(&mouse) == 0ULL || mouse.ready == 0ULL) { + return 0; + } + + if (mouse.packet_count == sess->mouse_packet_seen) { + return 0; + } + sess->mouse_packet_seen = mouse.packet_count; + + if ((mouse.buttons & 0x1ULL) == 0ULL) { + if (sess->dragging != 0) { + sess->dragging = 0; + sess->drag_window = -1; + } + if (sess->resizing != 0) { + ush_uwm_finish_resize(sess); + } + return 1; + } + + global_x = ush_uwm_u64_as_i32(mouse.x); + global_y = ush_uwm_u64_as_i32(mouse.y); + + if (sess->resizing != 0 && ush_uwm_app_index_valid(sess->resize_window) != 0) { + sess->resize_pending_w = sess->resize_start_w + (global_x - sess->resize_start_x); + sess->resize_pending_h = sess->resize_start_h + (global_y - sess->resize_start_y); + return 1; + } + + if (sess->dragging != 0 && ush_uwm_app_index_valid(sess->drag_window) != 0) { + (void)ush_uwm_window_move_clamped(sess, sess->drag_window, global_x - sess->drag_offset_x, + global_y - sess->drag_offset_y); + return 1; + } + + return 0; } void ush_uwm_handle_event(ush_uwm_session *sess, int window_index, const cleonos_wm_event *event, int *running) { @@ -114,11 +361,23 @@ void ush_uwm_handle_event(ush_uwm_session *sess, int window_index, const cleonos switch (event->type) { case CLEONOS_WM_EVENT_FOCUS_GAINED: - sess->active_window = window_index; + if (ush_uwm_app_index_valid(window_index) != 0) { + int old_active = sess->active_window; + sess->active_window = window_index; + if (ush_uwm_app_index_valid(old_active) != 0 && old_active != window_index) { + ush_uwm_refresh_window(sess, old_active); + } + ush_uwm_refresh_window(sess, window_index); + ush_uwm_refresh_taskbar(sess); + } break; case CLEONOS_WM_EVENT_FOCUS_LOST: if (sess->drag_window == window_index) { sess->dragging = 0; + sess->drag_window = -1; + } + if (sess->resize_window == window_index) { + ush_uwm_finish_resize(sess); } break; case CLEONOS_WM_EVENT_KEY: @@ -135,8 +394,41 @@ void ush_uwm_handle_event(ush_uwm_session *sess, int window_index, const cleonos } } +static int ush_uwm_poll_window_events(ush_uwm_session *sess, int window_index, int *running) { + ush_uwm_window *win; + u64 budget = 0ULL; + int handled = 0; + + if (sess == (ush_uwm_session *)0 || running == (int *)0 || ush_uwm_window_index_valid(window_index) == 0) { + return 0; + } + + win = &sess->windows[window_index]; + if (win->alive == 0 || win->id == 0ULL) { + return 0; + } + + while (budget < USH_UWM_EVENT_BUDGET) { + cleonos_wm_event event; + ush_zero(&event, (u64)sizeof(event)); + if (cleonos_sys_wm_poll_event(win->id, &event) == 0ULL) { + break; + } + + ush_uwm_handle_event(sess, window_index, &event, running); + handled++; + if (*running == 0) { + break; + } + budget++; + } + + return handled; +} + int ush_uwm_loop(ush_uwm_session *sess) { int running = 1; + int idle_spins = 0; if (sess == (ush_uwm_session *)0) { return 0; @@ -144,30 +436,33 @@ int ush_uwm_loop(ush_uwm_session *sess) { while (running != 0) { int i; + int handled_events = 0; + int preferred_window = -1; + + if (sess->dragging != 0 && ush_uwm_window_index_valid(sess->drag_window) != 0) { + preferred_window = sess->drag_window; + } else if (sess->resizing != 0 && ush_uwm_window_index_valid(sess->resize_window) != 0) { + preferred_window = sess->resize_window; + } else if (sess->start_open != 0 && sess->windows[USH_UWM_START_INDEX].alive != 0) { + preferred_window = USH_UWM_START_INDEX; + } else if (ush_uwm_window_index_valid(sess->active_window) != 0) { + preferred_window = sess->active_window; + } + + handled_events += ush_uwm_drive_direct_pointer(sess); + if (preferred_window >= 0) { + handled_events += ush_uwm_poll_window_events(sess, preferred_window, &running); + } + + if (running == 0) { + break; + } for (i = 0; i < (int)USH_UWM_WINDOW_COUNT; i++) { - ush_uwm_window *win = &sess->windows[i]; - u64 budget = 0ULL; - - if (win->alive == 0 || win->id == 0ULL) { + if (i == preferred_window) { continue; } - - while (budget < USH_UWM_EVENT_BUDGET) { - cleonos_wm_event event; - ush_zero(&event, (u64)sizeof(event)); - if (cleonos_sys_wm_poll_event(win->id, &event) == 0ULL) { - break; - } - - ush_uwm_handle_event(sess, i, &event, &running); - if (running == 0) { - break; - } - - budget++; - } - + handled_events += ush_uwm_poll_window_events(sess, i, &running); if (running == 0) { break; } @@ -177,7 +472,20 @@ int ush_uwm_loop(ush_uwm_session *sess) { break; } - (void)cleonos_sys_yield(); + if (handled_events != 0 || sess->dragging != 0 || sess->resizing != 0) { + idle_spins = 0; + (void)cleonos_sys_yield(); + continue; + } + + if (idle_spins < USH_UWM_IDLE_SPINS) { + idle_spins++; + (void)cleonos_sys_yield(); + continue; + } + + idle_spins = 0; + (void)cleonos_sys_sleep_ticks(1ULL); } return 1; diff --git a/cleonos/c/apps/uwm/uwm_util.c b/cleonos/c/apps/uwm/uwm_util.c index efa14fe..fc4fd15 100644 --- a/cleonos/c/apps/uwm/uwm_util.c +++ b/cleonos/c/apps/uwm/uwm_util.c @@ -4,7 +4,15 @@ int ush_uwm_window_index_valid(int index) { return (index >= 0 && index < (int)USH_UWM_WINDOW_COUNT) ? 1 : 0; } +int ush_uwm_app_index_valid(int index) { + return (index >= 0 && index < (int)USH_UWM_APP_COUNT) ? 1 : 0; +} + int ush_uwm_clampi(int value, int min_value, int max_value) { + if (max_value < min_value) { + return min_value; + } + if (value < min_value) { return min_value; } diff --git a/cleonos/c/apps/uwm/uwm_window.c b/cleonos/c/apps/uwm/uwm_window.c index 6d0ec93..33ad400 100644 --- a/cleonos/c/apps/uwm/uwm_window.c +++ b/cleonos/c/apps/uwm/uwm_window.c @@ -1,5 +1,65 @@ #include "uwm.h" +#define UWM_GLYPH7(r0, r1, r2, r3, r4, r5, r6) \ + (((u64)(r0) << 30U) | ((u64)(r1) << 25U) | ((u64)(r2) << 20U) | ((u64)(r3) << 15U) | \ + ((u64)(r4) << 10U) | ((u64)(r5) << 5U) | (u64)(r6)) + +#define UWM_COLOR_WHITE 0x00FFFFFFU +#define UWM_COLOR_WIN_BLUE 0x000078D7U +#define UWM_COLOR_DARKER 0x00181818U +#define UWM_COLOR_LIGHT_BG 0x00F3F3F3U +#define UWM_COLOR_PANEL 0x00FFFFFFU +#define UWM_COLOR_TEXT 0x00232323U +#define UWM_COLOR_MUTED 0x00666666U +#define UWM_COLOR_BORDER 0x00D0D0D0U + +static int ush_uwm_work_bottom(const ush_uwm_session *sess) { + int bottom; + + if (sess == (const ush_uwm_session *)0) { + return USH_UWM_TOP_CLAMP_Y; + } + + bottom = sess->screen_h - USH_UWM_TASKBAR_H; + if (bottom < USH_UWM_TOP_CLAMP_Y) { + bottom = USH_UWM_TOP_CLAMP_Y; + } + + return bottom; +} + +static ush_uwm_u32 ush_uwm_mix(ush_uwm_u32 a, ush_uwm_u32 b, int pos, int max_pos) { + unsigned int ar; + unsigned int ag; + unsigned int ab; + unsigned int br; + unsigned int bg; + unsigned int bb; + unsigned int r; + unsigned int g; + unsigned int blue; + unsigned int left; + unsigned int right; + + if (max_pos <= 0) { + return b; + } + + pos = ush_uwm_clampi(pos, 0, max_pos); + left = (unsigned int)(max_pos - pos); + right = (unsigned int)pos; + ar = (a >> 16U) & 0xFFU; + ag = (a >> 8U) & 0xFFU; + ab = a & 0xFFU; + br = (b >> 16U) & 0xFFU; + bg = (b >> 8U) & 0xFFU; + bb = b & 0xFFU; + r = ((ar * left) + (br * right)) / (unsigned int)max_pos; + g = ((ag * left) + (bg * right)) / (unsigned int)max_pos; + blue = ((ab * left) + (bb * right)) / (unsigned int)max_pos; + return (ush_uwm_u32)((r << 16U) | (g << 8U) | blue); +} + static void ush_uwm_fill_rect(ush_uwm_window *win, int x, int y, int w, int h, ush_uwm_u32 color) { int left; int top; @@ -38,54 +98,517 @@ static void ush_uwm_fill_rect(ush_uwm_window *win, int x, int y, int w, int h, u u64 base = (u64)(unsigned int)row * (u64)(unsigned int)win->w; int col; + if (base + (u64)(unsigned int)right > win->pixel_count) { + return; + } + for (col = left; col < right; col++) { - u64 idx = base + (u64)(unsigned int)col; - if (idx >= win->pixel_count) { - return; - } - win->pixels[idx] = color; + win->pixels[base + (u64)(unsigned int)col] = color; } } } -void ush_uwm_render_content(ush_uwm_window *win) { - int y; +static void ush_uwm_gradient_rect(ush_uwm_window *win, int x, int y, int w, int h, ush_uwm_u32 top, + ush_uwm_u32 bottom) { + int row; - if (win == (ush_uwm_window *)0 || win->pixels == (ush_uwm_u32 *)0) { - return; - } - - ush_uwm_fill_rect(win, 0, 0, win->w, win->h, win->color); - ush_uwm_fill_rect(win, 0, 0, win->w, 18, 0x001A1F2BUL); - ush_uwm_fill_rect(win, 8, 5, 8, 8, 0x00FFD166UL); - ush_uwm_fill_rect(win, win->w - 18, 5, 10, 8, 0x00E76F51UL); - - for (y = 30; y < win->h; y += 22) { - ush_uwm_fill_rect(win, 10, y, win->w - 20, 1, 0x003F4D62UL); + for (row = 0; row < h; row++) { + ush_uwm_fill_rect(win, x, y + row, w, 1, ush_uwm_mix(top, bottom, row, h - 1)); } } +static void ush_uwm_stroke_rect(ush_uwm_window *win, int x, int y, int w, int h, ush_uwm_u32 color) { + ush_uwm_fill_rect(win, x, y, w, 1, color); + ush_uwm_fill_rect(win, x, y + h - 1, w, 1, color); + ush_uwm_fill_rect(win, x, y, 1, h, color); + ush_uwm_fill_rect(win, x + w - 1, y, 1, h, color); +} + +static char ush_uwm_upper_char(char ch) { + if (ch >= 'a' && ch <= 'z') { + return (char)(ch - ('a' - 'A')); + } + + return ch; +} + +static u64 ush_uwm_glyph_mask(char ch) { + switch (ush_uwm_upper_char(ch)) { + case 'A': + return UWM_GLYPH7(14U, 17U, 17U, 31U, 17U, 17U, 17U); + case 'B': + return UWM_GLYPH7(30U, 17U, 17U, 30U, 17U, 17U, 30U); + case 'C': + return UWM_GLYPH7(14U, 17U, 16U, 16U, 16U, 17U, 14U); + case 'D': + return UWM_GLYPH7(30U, 17U, 17U, 17U, 17U, 17U, 30U); + case 'E': + return UWM_GLYPH7(31U, 16U, 16U, 30U, 16U, 16U, 31U); + case 'F': + return UWM_GLYPH7(31U, 16U, 16U, 30U, 16U, 16U, 16U); + case 'G': + return UWM_GLYPH7(14U, 17U, 16U, 23U, 17U, 17U, 15U); + case 'H': + return UWM_GLYPH7(17U, 17U, 17U, 31U, 17U, 17U, 17U); + case 'I': + return UWM_GLYPH7(31U, 4U, 4U, 4U, 4U, 4U, 31U); + case 'J': + return UWM_GLYPH7(1U, 1U, 1U, 1U, 17U, 17U, 14U); + case 'K': + return UWM_GLYPH7(17U, 18U, 20U, 24U, 20U, 18U, 17U); + case 'L': + return UWM_GLYPH7(16U, 16U, 16U, 16U, 16U, 16U, 31U); + case 'M': + return UWM_GLYPH7(17U, 27U, 21U, 21U, 17U, 17U, 17U); + case 'N': + return UWM_GLYPH7(17U, 25U, 21U, 19U, 17U, 17U, 17U); + case 'O': + return UWM_GLYPH7(14U, 17U, 17U, 17U, 17U, 17U, 14U); + case 'P': + return UWM_GLYPH7(30U, 17U, 17U, 30U, 16U, 16U, 16U); + case 'Q': + return UWM_GLYPH7(14U, 17U, 17U, 17U, 21U, 18U, 13U); + case 'R': + return UWM_GLYPH7(30U, 17U, 17U, 30U, 20U, 18U, 17U); + case 'S': + return UWM_GLYPH7(15U, 16U, 16U, 14U, 1U, 1U, 30U); + case 'T': + return UWM_GLYPH7(31U, 4U, 4U, 4U, 4U, 4U, 4U); + case 'U': + return UWM_GLYPH7(17U, 17U, 17U, 17U, 17U, 17U, 14U); + case 'V': + return UWM_GLYPH7(17U, 17U, 17U, 17U, 17U, 10U, 4U); + case 'W': + return UWM_GLYPH7(17U, 17U, 17U, 21U, 21U, 21U, 10U); + case 'X': + return UWM_GLYPH7(17U, 17U, 10U, 4U, 10U, 17U, 17U); + case 'Y': + return UWM_GLYPH7(17U, 17U, 10U, 4U, 4U, 4U, 4U); + case 'Z': + return UWM_GLYPH7(31U, 1U, 2U, 4U, 8U, 16U, 31U); + case '0': + return UWM_GLYPH7(14U, 17U, 19U, 21U, 25U, 17U, 14U); + case '1': + return UWM_GLYPH7(4U, 12U, 4U, 4U, 4U, 4U, 14U); + case '2': + return UWM_GLYPH7(14U, 17U, 1U, 2U, 4U, 8U, 31U); + case '3': + return UWM_GLYPH7(30U, 1U, 1U, 14U, 1U, 1U, 30U); + case '4': + return UWM_GLYPH7(2U, 6U, 10U, 18U, 31U, 2U, 2U); + case '5': + return UWM_GLYPH7(31U, 16U, 16U, 30U, 1U, 1U, 30U); + case '6': + return UWM_GLYPH7(14U, 16U, 16U, 30U, 17U, 17U, 14U); + case '7': + return UWM_GLYPH7(31U, 1U, 2U, 4U, 8U, 8U, 8U); + case '8': + return UWM_GLYPH7(14U, 17U, 17U, 14U, 17U, 17U, 14U); + case '9': + return UWM_GLYPH7(14U, 17U, 17U, 15U, 1U, 1U, 14U); + case '-': + return UWM_GLYPH7(0U, 0U, 0U, 31U, 0U, 0U, 0U); + case '_': + return UWM_GLYPH7(0U, 0U, 0U, 0U, 0U, 0U, 31U); + case '.': + return UWM_GLYPH7(0U, 0U, 0U, 0U, 0U, 12U, 12U); + case ':': + return UWM_GLYPH7(0U, 12U, 12U, 0U, 12U, 12U, 0U); + case '/': + return UWM_GLYPH7(1U, 1U, 2U, 4U, 8U, 16U, 16U); + case '+': + return UWM_GLYPH7(0U, 4U, 4U, 31U, 4U, 4U, 0U); + case '=': + return UWM_GLYPH7(0U, 0U, 31U, 0U, 31U, 0U, 0U); + case '^': + return UWM_GLYPH7(4U, 10U, 17U, 0U, 0U, 0U, 0U); + case '|': + return UWM_GLYPH7(4U, 4U, 4U, 4U, 4U, 4U, 4U); + default: + return 0ULL; + } +} + +static void ush_uwm_draw_char(ush_uwm_window *win, int x, int y, char ch, int scale, ush_uwm_u32 color) { + u64 mask = ush_uwm_glyph_mask(ch); + int row; + + if (mask == 0ULL || scale <= 0) { + return; + } + + for (row = 0; row < 7; row++) { + int col; + for (col = 0; col < 5; col++) { + unsigned int bit_index = (unsigned int)((6 - row) * 5 + (4 - col)); + if ((mask & (1ULL << bit_index)) != 0ULL) { + ush_uwm_fill_rect(win, x + (col * scale), y + (row * scale), scale, scale, color); + } + } + } +} + +static void ush_uwm_draw_text_limit(ush_uwm_window *win, int x, int y, const char *text, int scale, ush_uwm_u32 color, + int max_x) { + int cursor_x = x; + + if (win == (ush_uwm_window *)0 || text == (const char *)0 || scale <= 0) { + return; + } + + if (max_x <= 0 || max_x > win->w) { + max_x = win->w; + } + + while (*text != 0 && cursor_x + (5 * scale) <= max_x) { + if (*text != ' ') { + ush_uwm_draw_char(win, cursor_x, y, *text, scale, color); + } + cursor_x += 6 * scale; + text++; + } +} + +static void ush_uwm_draw_text(ush_uwm_window *win, int x, int y, const char *text, int scale, ush_uwm_u32 color) { + ush_uwm_draw_text_limit(win, x, y, text, scale, color, win != (ush_uwm_window *)0 ? win->w : 0); +} + +static void ush_uwm_draw_windows_logo(ush_uwm_window *win, int x, int y, int size, ush_uwm_u32 color) { + int half = size / 2; + int gap = (size >= 20) ? 2 : 1; + + ush_uwm_fill_rect(win, x, y, half - gap, half - gap, color); + ush_uwm_fill_rect(win, x + half + gap, y, half - gap, half - gap, color); + ush_uwm_fill_rect(win, x, y + half + gap, half - gap, half - gap, color); + ush_uwm_fill_rect(win, x + half + gap, y + half + gap, half - gap, half - gap, color); +} + +static void ush_uwm_draw_control_button(ush_uwm_window *win, int x, int active, int kind) { + ush_uwm_u32 bg = (kind == 2) ? 0x00E81123U : (active != 0 ? 0x001A5EA0U : 0x00E5E5E5U); + ush_uwm_u32 fg = (kind == 2 || active != 0) ? UWM_COLOR_WHITE : UWM_COLOR_TEXT; + int cy = USH_UWM_TITLE_H / 2; + int cx = x + (USH_UWM_CONTROL_W / 2); + + ush_uwm_fill_rect(win, x, 0, USH_UWM_CONTROL_W, USH_UWM_TITLE_H, bg); + if (kind == 0) { + ush_uwm_fill_rect(win, cx - 6, cy + 4, 12, 1, fg); + } else if (kind == 1) { + ush_uwm_fill_rect(win, cx - 4, cy - 6, 8, 2, fg); + ush_uwm_fill_rect(win, cx - 1, cy - 4, 2, 9, fg); + ush_uwm_fill_rect(win, cx - 6, cy + 4, 12, 2, fg); + } else { + int i; + for (i = 0; i < 11; i++) { + ush_uwm_fill_rect(win, cx - 5 + i, cy - 5 + i, 1, 1, fg); + ush_uwm_fill_rect(win, cx + 5 - i, cy - 5 + i, 1, 1, fg); + } + } +} + +static void ush_uwm_draw_window_controls(ush_uwm_window *win, int active) { + int close_x; + int pin_x; + int min_x; + + if (win == (ush_uwm_window *)0 || win->w < (USH_UWM_CONTROL_W * 3) + 16) { + return; + } + + close_x = win->w - USH_UWM_CONTROL_W; + min_x = close_x - USH_UWM_CONTROL_W; + pin_x = min_x - USH_UWM_CONTROL_W; + ush_uwm_draw_control_button(win, pin_x, active, 1); + ush_uwm_draw_control_button(win, min_x, active, 0); + ush_uwm_draw_control_button(win, close_x, active, 2); +} + +static void ush_uwm_draw_button(ush_uwm_window *win, int x, int y, int w, int h, const char *label, ush_uwm_u32 bg, + ush_uwm_u32 fg, int active) { + ush_uwm_fill_rect(win, x, y, w, h, bg); + if (active != 0) { + ush_uwm_fill_rect(win, x, y + h - 3, w, 3, UWM_COLOR_WIN_BLUE); + } + ush_uwm_draw_text_limit(win, x + 10, y + ((h - 7) / 2), label, 1, fg, x + w - 8); +} + +static void ush_uwm_render_files(ush_uwm_window *win) { + int y; + + ush_uwm_fill_rect(win, 0, USH_UWM_TITLE_H, 92, win->h - USH_UWM_TITLE_H, 0x00F7F7F7U); + ush_uwm_fill_rect(win, 92, USH_UWM_TITLE_H, 1, win->h - USH_UWM_TITLE_H, UWM_COLOR_BORDER); + ush_uwm_draw_text(win, 12, USH_UWM_TITLE_H + 16, "QUICK ACCESS", 1, UWM_COLOR_MUTED); + ush_uwm_draw_text(win, 18, USH_UWM_TITLE_H + 40, "DESKTOP", 1, UWM_COLOR_TEXT); + ush_uwm_draw_text(win, 18, USH_UWM_TITLE_H + 62, "SYSTEM", 1, UWM_COLOR_TEXT); + ush_uwm_draw_text(win, 18, USH_UWM_TITLE_H + 84, "TEMP", 1, UWM_COLOR_TEXT); + + ush_uwm_draw_text(win, 112, USH_UWM_TITLE_H + 16, "THIS PC", 2, UWM_COLOR_TEXT); + ush_uwm_fill_rect(win, 112, USH_UWM_TITLE_H + 48, win->w - 132, 34, UWM_COLOR_PANEL); + ush_uwm_stroke_rect(win, 112, USH_UWM_TITLE_H + 48, win->w - 132, 34, UWM_COLOR_BORDER); + ush_uwm_draw_text(win, 124, USH_UWM_TITLE_H + 60, "C:/ CLEONOS LOCAL DISK", 1, UWM_COLOR_TEXT); + ush_uwm_fill_rect(win, 124, USH_UWM_TITLE_H + 72, win->w - 184, 4, 0x00D9D9D9U); + ush_uwm_fill_rect(win, 124, USH_UWM_TITLE_H + 72, (win->w - 184) / 2, 4, UWM_COLOR_WIN_BLUE); + + y = USH_UWM_TITLE_H + 104; + while (y + 22 < win->h - 14) { + ush_uwm_fill_rect(win, 112, y, win->w - 132, 1, 0x00E6E6E6U); + y += 28; + } + ush_uwm_draw_text(win, 118, USH_UWM_TITLE_H + 110, "SYSTEM", 1, UWM_COLOR_TEXT); + ush_uwm_draw_text(win, 118, USH_UWM_TITLE_H + 138, "SHELL", 1, UWM_COLOR_TEXT); + ush_uwm_draw_text(win, 118, USH_UWM_TITLE_H + 166, "TEMP", 1, UWM_COLOR_TEXT); +} + +static void ush_uwm_render_editor(ush_uwm_window *win) { + int y; + + ush_uwm_fill_rect(win, 0, USH_UWM_TITLE_H, win->w, 24, 0x00F9F9F9U); + ush_uwm_fill_rect(win, 0, USH_UWM_TITLE_H + 23, win->w, 1, UWM_COLOR_BORDER); + ush_uwm_draw_text(win, 12, USH_UWM_TITLE_H + 8, "FILE EDIT VIEW HELP", 1, UWM_COLOR_TEXT); + ush_uwm_fill_rect(win, 0, USH_UWM_TITLE_H + 24, 46, win->h - USH_UWM_TITLE_H - 24, 0x00F0F0F0U); + ush_uwm_fill_rect(win, 46, USH_UWM_TITLE_H + 24, 1, win->h - USH_UWM_TITLE_H - 24, 0x00DDDDDDU); + + for (y = 0; y < 7; y++) { + char label[4]; + label[0] = (char)('1' + y); + label[1] = 0; + ush_uwm_draw_text(win, 18, USH_UWM_TITLE_H + 42 + (y * 18), label, 1, UWM_COLOR_MUTED); + } + + ush_uwm_draw_text(win, 62, USH_UWM_TITLE_H + 42, "CLEONOS UWM REWRITE", 1, UWM_COLOR_TEXT); + ush_uwm_draw_text(win, 62, USH_UWM_TITLE_H + 60, "PIXEL RENDERER ONLINE", 1, 0x00008000U); + ush_uwm_draw_text(win, 62, USH_UWM_TITLE_H + 78, "WINDOWS 10 STYLE SHELL", 1, UWM_COLOR_TEXT); + ush_uwm_draw_text(win, 62, USH_UWM_TITLE_H + 96, "DRAG RESIZE MINIMIZE CLOSE", 1, UWM_COLOR_TEXT); + ush_uwm_fill_rect(win, 62, USH_UWM_TITLE_H + 120, 92, 2, UWM_COLOR_WIN_BLUE); +} + +static void ush_uwm_render_browser(ush_uwm_window *win) { + int card_w; + + ush_uwm_fill_rect(win, 0, USH_UWM_TITLE_H, win->w, 42, 0x00F7F7F7U); + ush_uwm_fill_rect(win, 0, USH_UWM_TITLE_H + 41, win->w, 1, UWM_COLOR_BORDER); + ush_uwm_fill_rect(win, 14, USH_UWM_TITLE_H + 10, win->w - 28, 22, UWM_COLOR_WHITE); + ush_uwm_stroke_rect(win, 14, USH_UWM_TITLE_H + 10, win->w - 28, 22, UWM_COLOR_BORDER); + ush_uwm_draw_text_limit(win, 24, USH_UWM_TITLE_H + 17, "HTTP://EXAMPLE.COM", 1, UWM_COLOR_MUTED, win->w - 24); + + ush_uwm_draw_text(win, 22, USH_UWM_TITLE_H + 66, "WELCOME TO CLEONOS", 2, UWM_COLOR_TEXT); + ush_uwm_draw_text(win, 24, USH_UWM_TITLE_H + 94, "NETWORK AND HTML DEMOS LIVE HERE", 1, UWM_COLOR_MUTED); + card_w = (win->w - 58) / 2; + if (card_w < 80) { + card_w = 80; + } + ush_uwm_fill_rect(win, 22, USH_UWM_TITLE_H + 122, card_w, 54, 0x00EAF4FFU); + ush_uwm_stroke_rect(win, 22, USH_UWM_TITLE_H + 122, card_w, 54, 0x00B7D8F4U); + ush_uwm_draw_text(win, 34, USH_UWM_TITLE_H + 142, "HTTPGET", 1, UWM_COLOR_TEXT); + ush_uwm_fill_rect(win, 36 + card_w, USH_UWM_TITLE_H + 122, card_w, 54, 0x00EAF7EAU); + ush_uwm_stroke_rect(win, 36 + card_w, USH_UWM_TITLE_H + 122, card_w, 54, 0x00B7E0B7U); + ush_uwm_draw_text(win, 48 + card_w, USH_UWM_TITLE_H + 142, "NSLOOKUP", 1, UWM_COLOR_TEXT); +} + +static void ush_uwm_render_app_window(ush_uwm_session *sess, int index) { + ush_uwm_window *win; + int active; + ush_uwm_u32 title_bg; + ush_uwm_u32 title_fg; + + if (sess == (ush_uwm_session *)0 || ush_uwm_app_index_valid(index) == 0) { + return; + } + + win = &sess->windows[index]; + active = (sess->active_window == index && win->minimized == 0 && win->closed == 0) ? 1 : 0; + title_bg = (active != 0) ? win->accent : 0x00F3F3F3U; + title_fg = (active != 0) ? UWM_COLOR_WHITE : UWM_COLOR_TEXT; + + ush_uwm_fill_rect(win, 0, 0, win->w, win->h, UWM_COLOR_LIGHT_BG); + ush_uwm_fill_rect(win, 0, 0, win->w, USH_UWM_TITLE_H, title_bg); + ush_uwm_fill_rect(win, 0, USH_UWM_TITLE_H, win->w, 1, UWM_COLOR_BORDER); + ush_uwm_draw_text_limit(win, 12, 12, win->title, 1, title_fg, win->w - (USH_UWM_CONTROL_W * 3) - 8); + if (win->topmost != 0) { + ush_uwm_draw_text(win, win->w - (USH_UWM_CONTROL_W * 3) - 18, 12, "^", 1, title_fg); + } + ush_uwm_draw_window_controls(win, active); + + if (index == 0) { + ush_uwm_render_files(win); + } else if (index == 1) { + ush_uwm_render_editor(win); + } else { + ush_uwm_render_browser(win); + } + + ush_uwm_fill_rect(win, win->w - 14, win->h - 3, 11, 1, UWM_COLOR_MUTED); + ush_uwm_fill_rect(win, win->w - 10, win->h - 7, 7, 1, UWM_COLOR_MUTED); + ush_uwm_fill_rect(win, win->w - 6, win->h - 11, 3, 1, UWM_COLOR_MUTED); +} + +static void ush_uwm_render_taskbar(ush_uwm_session *sess) { + ush_uwm_window *taskbar; + int search_w; + int app_x; + int i; + + if (sess == (ush_uwm_session *)0) { + return; + } + + taskbar = &sess->windows[USH_UWM_TASKBAR_INDEX]; + ush_uwm_gradient_rect(taskbar, 0, 0, taskbar->w, taskbar->h, 0x00252525U, 0x001A1A1AU); + ush_uwm_fill_rect(taskbar, 0, 0, taskbar->w, 1, 0x004A4A4AU); + ush_uwm_fill_rect(taskbar, 0, 0, USH_UWM_TASKBAR_START_W, taskbar->h, + sess->start_open ? UWM_COLOR_WIN_BLUE : UWM_COLOR_DARKER); + ush_uwm_draw_windows_logo(taskbar, 15, 12, 16, UWM_COLOR_WHITE); + + search_w = USH_UWM_TASKBAR_SEARCH_W; + if (taskbar->w < 720) { + search_w = 128; + } + if (taskbar->w < 520) { + search_w = 0; + } + + app_x = USH_UWM_TASKBAR_START_W + 8; + if (search_w > 0) { + ush_uwm_fill_rect(taskbar, app_x, 6, search_w, taskbar->h - 12, 0x00303030U); + ush_uwm_stroke_rect(taskbar, app_x, 6, search_w, taskbar->h - 12, 0x00484848U); + ush_uwm_draw_text_limit(taskbar, app_x + 12, 17, "TYPE HERE TO SEARCH", 1, 0x00C8C8C8U, app_x + search_w - 8); + app_x += search_w + 10; + } + + for (i = 0; i < (int)USH_UWM_APP_COUNT; i++) { + ush_uwm_window *app = &sess->windows[i]; + ush_uwm_u32 bg = 0x00282828U; + ush_uwm_u32 fg = 0x00EAEAEAU; + int active = 0; + + if (app_x + USH_UWM_TASKBAR_BUTTON_W > taskbar->w - 98) { + break; + } + + if (app->closed != 0) { + bg = 0x001F1F1FU; + fg = 0x007C7C7CU; + } else if (app->minimized != 0) { + bg = 0x002F2F2FU; + } else if (sess->active_window == i) { + bg = 0x00383838U; + active = 1; + } + + ush_uwm_draw_button(taskbar, app_x, 5, USH_UWM_TASKBAR_BUTTON_W, taskbar->h - 10, app->title, bg, fg, active); + app_x += USH_UWM_TASKBAR_BUTTON_W + USH_UWM_TASKBAR_BUTTON_GAP; + } + + if (taskbar->w > 260) { + ush_uwm_draw_text(taskbar, taskbar->w - 86, 11, "CLKS", 1, UWM_COLOR_WHITE); + ush_uwm_draw_text(taskbar, taskbar->w - 86, 24, "UWM", 1, 0x00CFCFCFU); + } +} + +static void ush_uwm_render_start(ush_uwm_session *sess) { + ush_uwm_window *start; + int i; + const char *labels[USH_UWM_APP_COUNT] = {"FILE EXPLORER", "NOTEPAD", "EDGE"}; + + if (sess == (ush_uwm_session *)0) { + return; + } + + start = &sess->windows[USH_UWM_START_INDEX]; + ush_uwm_gradient_rect(start, 0, 0, start->w, start->h, 0x00292929U, 0x001E1E1EU); + ush_uwm_fill_rect(start, 0, 0, 52, start->h, 0x00191919U); + ush_uwm_draw_windows_logo(start, 16, start->h - 34, 18, UWM_COLOR_WHITE); + ush_uwm_draw_text(start, 68, 20, "CLEONOS", 2, UWM_COLOR_WHITE); + ush_uwm_draw_text(start, 70, 48, "START", 1, 0x00BDBDBDU); + + for (i = 0; i < (int)USH_UWM_APP_COUNT; i++) { + int y = 78 + (i * 44); + ush_uwm_u32 bg = (sess->active_window == i && sess->windows[i].closed == 0 && sess->windows[i].minimized == 0) + ? 0x003B3B3BU + : 0x00272727U; + if (y + 34 > start->h - 76) { + break; + } + ush_uwm_fill_rect(start, 66, y, start->w - 82, 34, bg); + ush_uwm_fill_rect(start, 66, y, 4, 34, sess->windows[i].accent); + ush_uwm_draw_text_limit(start, 82, y + 10, labels[i], 1, UWM_COLOR_WHITE, start->w - 12); + } + + if (start->w > 248 && start->h > 260) { + int tile_y = start->h - 68; + int tile_w = (start->w - 88) / 2; + ush_uwm_fill_rect(start, 66, tile_y, tile_w, 48, UWM_COLOR_WIN_BLUE); + ush_uwm_draw_text(start, 78, tile_y + 18, "SETTINGS", 1, UWM_COLOR_WHITE); + ush_uwm_fill_rect(start, 74 + tile_w, tile_y, tile_w, 48, 0x0000A300U); + ush_uwm_draw_text(start, 86 + tile_w, tile_y + 18, "TERMINAL", 1, UWM_COLOR_WHITE); + } +} + +void ush_uwm_render_window(ush_uwm_session *sess, int index) { + if (sess == (ush_uwm_session *)0 || ush_uwm_window_index_valid(index) == 0) { + return; + } + + if (sess->windows[index].pixels == (ush_uwm_u32 *)0) { + return; + } + + if (sess->windows[index].kind == USH_UWM_KIND_TASKBAR) { + ush_uwm_render_taskbar(sess); + } else if (sess->windows[index].kind == USH_UWM_KIND_START) { + ush_uwm_render_start(sess); + } else { + ush_uwm_render_app_window(sess, index); + } + sess->windows[index].dirty = 0; +} + +void ush_uwm_refresh_window(ush_uwm_session *sess, int index) { + if (sess == (ush_uwm_session *)0 || ush_uwm_window_index_valid(index) == 0 || sess->windows[index].alive == 0) { + return; + } + + ush_uwm_render_window(sess, index); + (void)ush_uwm_present_window(&sess->windows[index]); +} + +void ush_uwm_refresh_taskbar(ush_uwm_session *sess) { + ush_uwm_refresh_window(sess, USH_UWM_TASKBAR_INDEX); +} + int ush_uwm_alloc_pixels(ush_uwm_window *win) { + return (win == (ush_uwm_window *)0) ? 0 : ush_uwm_replace_pixels(win, win->w, win->h); +} + +int ush_uwm_replace_pixels(ush_uwm_window *win, int width, int height) { u64 count; u64 bytes; + ush_uwm_u32 *new_pixels; - if (win == (ush_uwm_window *)0 || win->w <= 0 || win->h <= 0) { + if (win == (ush_uwm_window *)0 || width <= 0 || height <= 0) { return 0; } - count = (u64)(unsigned int)win->w * (u64)(unsigned int)win->h; + count = (u64)(unsigned int)width * (u64)(unsigned int)height; if (count == 0ULL || count > (((u64)-1) / 4ULL)) { return 0; } bytes = count * 4ULL; - win->pixels = (ush_uwm_u32 *)malloc((size_t)bytes); - if (win->pixels == (ush_uwm_u32 *)0) { + new_pixels = (ush_uwm_u32 *)malloc((size_t)bytes); + if (new_pixels == (ush_uwm_u32 *)0) { return 0; } + if (win->pixels != (ush_uwm_u32 *)0) { + free(win->pixels); + } + + win->pixels = new_pixels; win->pixel_count = count; + win->w = width; + win->h = height; ush_zero(win->pixels, bytes); + win->dirty = 1; return 1; } @@ -100,7 +623,7 @@ int ush_uwm_create_window(ush_uwm_window *win) { req.y = (u64)(i64)win->y; req.width = (u64)(unsigned int)win->w; req.height = (u64)(unsigned int)win->h; - req.flags = 0ULL; + req.flags = (win->topmost != 0) ? USH_UWM_WM_FLAG_TOPMOST : 0ULL; win->id = cleonos_sys_wm_create(&req); if (win->id == 0ULL) { return 0; @@ -125,7 +648,7 @@ int ush_uwm_present_window(const ush_uwm_window *win) { return (cleonos_sys_wm_present(&req) != 0ULL) ? 1 : 0; } -void ush_uwm_destroy_window(ush_uwm_window *win) { +void ush_uwm_destroy_kernel_window(ush_uwm_window *win) { if (win == (ush_uwm_window *)0) { return; } @@ -134,14 +657,25 @@ void ush_uwm_destroy_window(ush_uwm_window *win) { (void)cleonos_sys_wm_destroy(win->id); } + win->id = 0ULL; + win->alive = 0; +} + +void ush_uwm_destroy_window(ush_uwm_window *win) { + if (win == (ush_uwm_window *)0) { + return; + } + + ush_uwm_destroy_kernel_window(win); if (win->pixels != (ush_uwm_u32 *)0) { free(win->pixels); } - win->id = 0ULL; win->pixels = (ush_uwm_u32 *)0; win->pixel_count = 0ULL; - win->alive = 0; + win->minimized = 0; + win->closed = 1; + win->dirty = 1; } int ush_uwm_window_move_clamped(ush_uwm_session *sess, int index, int target_x, int target_y) { @@ -152,26 +686,29 @@ int ush_uwm_window_move_clamped(ush_uwm_session *sess, int index, int target_x, int new_x; int new_y; - if (sess == (ush_uwm_session *)0 || ush_uwm_window_index_valid(index) == 0) { + if (sess == (ush_uwm_session *)0 || ush_uwm_app_index_valid(index) == 0) { return 0; } win = &sess->windows[index]; - if (win->alive == 0 || win->id == 0ULL) { + if (win->alive == 0 || win->id == 0ULL || win->closed != 0 || win->minimized != 0) { return 0; } max_x = sess->screen_w - win->w; - max_y = sess->screen_h - win->h; + max_y = ush_uwm_work_bottom(sess) - win->h; if (max_x < 0) { max_x = 0; } - if (max_y < 0) { - max_y = 0; + if (max_y < USH_UWM_TOP_CLAMP_Y) { + max_y = USH_UWM_TOP_CLAMP_Y; } new_x = ush_uwm_clampi(target_x, 0, max_x); new_y = ush_uwm_clampi(target_y, USH_UWM_TOP_CLAMP_Y, max_y); + if (new_x == win->x && new_y == win->y) { + return 1; + } req.window_id = win->id; req.x = (u64)(i64)new_x; @@ -185,20 +722,96 @@ int ush_uwm_window_move_clamped(ush_uwm_session *sess, int index, int target_x, return 1; } +int ush_uwm_window_resize(ush_uwm_session *sess, int index, int target_w, int target_h) { + ush_uwm_window *win; + cleonos_wm_resize_req req; + ush_uwm_u32 *new_pixels; + u64 count; + u64 bytes; + int max_w; + int max_h; + int new_w; + int new_h; + + if (sess == (ush_uwm_session *)0 || ush_uwm_app_index_valid(index) == 0) { + return 0; + } + + win = &sess->windows[index]; + if (win->alive == 0 || win->id == 0ULL || win->closed != 0 || win->minimized != 0) { + return 0; + } + + max_w = sess->screen_w - win->x; + max_h = ush_uwm_work_bottom(sess) - win->y; + if (max_w < USH_UWM_MIN_WINDOW_W) { + max_w = USH_UWM_MIN_WINDOW_W; + } + if (max_h < USH_UWM_MIN_WINDOW_H) { + max_h = USH_UWM_MIN_WINDOW_H; + } + + new_w = ush_uwm_clampi(target_w, USH_UWM_MIN_WINDOW_W, max_w); + new_h = ush_uwm_clampi(target_h, USH_UWM_MIN_WINDOW_H, max_h); + if (new_w == win->w && new_h == win->h) { + return 1; + } + + count = (u64)(unsigned int)new_w * (u64)(unsigned int)new_h; + if (count == 0ULL || count > (((u64)-1) / 4ULL)) { + return 0; + } + + bytes = count * 4ULL; + new_pixels = (ush_uwm_u32 *)malloc((size_t)bytes); + if (new_pixels == (ush_uwm_u32 *)0) { + return 0; + } + ush_zero(new_pixels, bytes); + + req.window_id = win->id; + req.width = (u64)(unsigned int)new_w; + req.height = (u64)(unsigned int)new_h; + if (cleonos_sys_wm_resize(&req) == 0ULL) { + free(new_pixels); + return 0; + } + + if (win->pixels != (ush_uwm_u32 *)0) { + free(win->pixels); + } + win->pixels = new_pixels; + win->pixel_count = count; + win->w = new_w; + win->h = new_h; + win->dirty = 1; + + ush_uwm_render_window(sess, index); + (void)ush_uwm_present_window(win); + return 1; +} + void ush_uwm_set_active(ush_uwm_session *sess, int index) { ush_uwm_window *win; + int old_active; - if (sess == (ush_uwm_session *)0 || ush_uwm_window_index_valid(index) == 0) { + if (sess == (ush_uwm_session *)0 || ush_uwm_app_index_valid(index) == 0) { return; } win = &sess->windows[index]; - if (win->alive == 0 || win->id == 0ULL) { + if (win->alive == 0 || win->id == 0ULL || win->closed != 0 || win->minimized != 0) { return; } + old_active = sess->active_window; if (cleonos_sys_wm_set_focus(win->id) != 0ULL) { sess->active_window = index; + if (ush_uwm_app_index_valid(old_active) != 0 && old_active != index) { + ush_uwm_refresh_window(sess, old_active); + } + ush_uwm_refresh_window(sess, index); + ush_uwm_refresh_taskbar(sess); } } @@ -210,17 +823,149 @@ void ush_uwm_focus_next(ush_uwm_session *sess) { return; } - if (ush_uwm_window_index_valid(sess->active_window) != 0) { - start = sess->active_window; - } else { - start = 0; - } - - for (i = 1; i <= (int)USH_UWM_WINDOW_COUNT; i++) { - int idx = (start + i) % (int)USH_UWM_WINDOW_COUNT; - if (sess->windows[idx].alive != 0) { + start = (ush_uwm_app_index_valid(sess->active_window) != 0) ? sess->active_window : 0; + for (i = 1; i <= (int)USH_UWM_APP_COUNT; i++) { + int idx = (start + i) % (int)USH_UWM_APP_COUNT; + if (sess->windows[idx].alive != 0 && sess->windows[idx].minimized == 0 && sess->windows[idx].closed == 0) { ush_uwm_set_active(sess, idx); return; } } + + sess->active_window = -1; + ush_uwm_refresh_taskbar(sess); +} + +void ush_uwm_minimize_window(ush_uwm_session *sess, int index) { + ush_uwm_window *win; + + if (sess == (ush_uwm_session *)0 || ush_uwm_app_index_valid(index) == 0) { + return; + } + + win = &sess->windows[index]; + if (win->alive == 0 || win->closed != 0) { + return; + } + + ush_uwm_destroy_kernel_window(win); + win->minimized = 1; + if (sess->active_window == index) { + ush_uwm_focus_next(sess); + } + ush_uwm_refresh_taskbar(sess); +} + +void ush_uwm_close_window(ush_uwm_session *sess, int index) { + if (sess == (ush_uwm_session *)0 || ush_uwm_app_index_valid(index) == 0) { + return; + } + + ush_uwm_destroy_kernel_window(&sess->windows[index]); + sess->windows[index].closed = 1; + sess->windows[index].minimized = 0; + if (sess->active_window == index) { + ush_uwm_focus_next(sess); + } + ush_uwm_refresh_taskbar(sess); +} + +void ush_uwm_restore_window(ush_uwm_session *sess, int index) { + ush_uwm_window *win; + + if (sess == (ush_uwm_session *)0 || ush_uwm_app_index_valid(index) == 0) { + return; + } + + win = &sess->windows[index]; + if (win->alive != 0) { + win->closed = 0; + win->minimized = 0; + ush_uwm_set_active(sess, index); + return; + } + + if (win->pixels == (ush_uwm_u32 *)0 && ush_uwm_alloc_pixels(win) == 0) { + return; + } + + win->closed = 0; + win->minimized = 0; + ush_uwm_render_window(sess, index); + if (ush_uwm_create_window(win) == 0) { + win->closed = 1; + return; + } + (void)ush_uwm_present_window(win); + ush_uwm_set_active(sess, index); + ush_uwm_refresh_taskbar(sess); +} + +void ush_uwm_toggle_topmost(ush_uwm_session *sess, int index) { + ush_uwm_window *win; + u64 flags; + + if (sess == (ush_uwm_session *)0 || ush_uwm_app_index_valid(index) == 0) { + return; + } + + win = &sess->windows[index]; + if (win->id == 0ULL || win->alive == 0) { + return; + } + + win->topmost = (win->topmost == 0) ? 1 : 0; + flags = (win->topmost != 0) ? USH_UWM_WM_FLAG_TOPMOST : 0ULL; + (void)cleonos_sys_wm_set_flags(win->id, flags); + ush_uwm_refresh_window(sess, index); + ush_uwm_refresh_taskbar(sess); +} + +void ush_uwm_close_start(ush_uwm_session *sess) { + ush_uwm_window *start; + + if (sess == (ush_uwm_session *)0) { + return; + } + + start = &sess->windows[USH_UWM_START_INDEX]; + if (start->alive != 0) { + ush_uwm_destroy_kernel_window(start); + } + sess->start_open = 0; + start->closed = 1; + if (ush_uwm_app_index_valid(sess->active_window) != 0 && sess->windows[sess->active_window].alive != 0) { + (void)cleonos_sys_wm_set_focus(sess->windows[sess->active_window].id); + } + ush_uwm_refresh_taskbar(sess); +} + +void ush_uwm_toggle_start(ush_uwm_session *sess) { + ush_uwm_window *start; + + if (sess == (ush_uwm_session *)0) { + return; + } + + start = &sess->windows[USH_UWM_START_INDEX]; + if (start->alive != 0) { + ush_uwm_close_start(sess); + return; + } + + if (start->pixels == (ush_uwm_u32 *)0 && ush_uwm_alloc_pixels(start) == 0) { + return; + } + + sess->start_open = 1; + start->closed = 0; + ush_uwm_render_window(sess, USH_UWM_START_INDEX); + if (ush_uwm_create_window(start) == 0) { + sess->start_open = 0; + start->closed = 1; + return; + } + (void)ush_uwm_present_window(start); + (void)cleonos_sys_wm_set_focus(start->id); + ush_uwm_refresh_taskbar(sess); } diff --git a/cleonos/c/include/cleonos_syscall.h b/cleonos/c/include/cleonos_syscall.h index 6d71839..1ef742a 100644 --- a/cleonos/c/include/cleonos_syscall.h +++ b/cleonos/c/include/cleonos_syscall.h @@ -60,9 +60,11 @@ typedef struct cleonos_mouse_state { #define CLEONOS_WM_EVENT_FOCUS_GAINED 1ULL #define CLEONOS_WM_EVENT_FOCUS_LOST 2ULL -#define CLEONOS_WM_EVENT_KEY 3ULL -#define CLEONOS_WM_EVENT_MOUSE_MOVE 4ULL -#define CLEONOS_WM_EVENT_MOUSE_BUTTON 5ULL +#define CLEONOS_WM_EVENT_KEY 3ULL +#define CLEONOS_WM_EVENT_MOUSE_MOVE 4ULL +#define CLEONOS_WM_EVENT_MOUSE_BUTTON 5ULL + +#define CLEONOS_WM_FLAG_TOPMOST 0x1ULL typedef struct cleonos_wm_event { u64 type; @@ -88,11 +90,17 @@ typedef struct cleonos_wm_present_req { u64 src_pitch_bytes; } cleonos_wm_present_req; -typedef struct cleonos_wm_move_req { - u64 window_id; - u64 x; - u64 y; -} cleonos_wm_move_req; +typedef struct cleonos_wm_move_req { + u64 window_id; + u64 x; + u64 y; +} cleonos_wm_move_req; + +typedef struct cleonos_wm_resize_req { + u64 window_id; + u64 width; + u64 height; +} cleonos_wm_resize_req; typedef struct cleonos_fb_blit_req { u64 pixels_ptr; @@ -250,9 +258,11 @@ typedef struct cleonos_net_tcp_recv_req { #define CLEONOS_SYSCALL_WM_CREATE 108ULL #define CLEONOS_SYSCALL_WM_DESTROY 109ULL #define CLEONOS_SYSCALL_WM_PRESENT 110ULL -#define CLEONOS_SYSCALL_WM_POLL_EVENT 111ULL -#define CLEONOS_SYSCALL_WM_MOVE 112ULL -#define CLEONOS_SYSCALL_WM_SET_FOCUS 113ULL +#define CLEONOS_SYSCALL_WM_POLL_EVENT 111ULL +#define CLEONOS_SYSCALL_WM_MOVE 112ULL +#define CLEONOS_SYSCALL_WM_SET_FOCUS 113ULL +#define CLEONOS_SYSCALL_WM_SET_FLAGS 114ULL +#define CLEONOS_SYSCALL_WM_RESIZE 115ULL u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); u64 cleonos_sys_log_write(const char *message, u64 length); @@ -366,8 +376,10 @@ u64 cleonos_sys_mouse_state(cleonos_mouse_state *out_state); u64 cleonos_sys_wm_create(const cleonos_wm_create_req *req); u64 cleonos_sys_wm_destroy(u64 window_id); u64 cleonos_sys_wm_present(const cleonos_wm_present_req *req); -u64 cleonos_sys_wm_poll_event(u64 window_id, cleonos_wm_event *out_event); -u64 cleonos_sys_wm_move(const cleonos_wm_move_req *req); -u64 cleonos_sys_wm_set_focus(u64 window_id); - -#endif +u64 cleonos_sys_wm_poll_event(u64 window_id, cleonos_wm_event *out_event); +u64 cleonos_sys_wm_move(const cleonos_wm_move_req *req); +u64 cleonos_sys_wm_set_focus(u64 window_id); +u64 cleonos_sys_wm_set_flags(u64 window_id, u64 flags); +u64 cleonos_sys_wm_resize(const cleonos_wm_resize_req *req); + +#endif diff --git a/cleonos/c/src/libc_stdlib.c b/cleonos/c/src/libc_stdlib.c index a5279b6..50100c5 100644 --- a/cleonos/c/src/libc_stdlib.c +++ b/cleonos/c/src/libc_stdlib.c @@ -240,7 +240,9 @@ unsigned long long strtoull(const char *text, char **out_end, int base) { return (unsigned long long)strtoul(text, out_end, base); } -#define CLIB_HEAP_CAPACITY (4U * 1024U * 1024U) +#ifndef CLIB_HEAP_CAPACITY +#define CLIB_HEAP_CAPACITY (2U * 1024U * 1024U) +#endif #define CLIB_HEAP_ALIGN 8U typedef union clib_heap_storage { diff --git a/cleonos/c/src/syscall.c b/cleonos/c/src/syscall.c index ea4268b..60e6a22 100644 --- a/cleonos/c/src/syscall.c +++ b/cleonos/c/src/syscall.c @@ -484,6 +484,14 @@ u64 cleonos_sys_wm_move(const cleonos_wm_move_req *req) { return cleonos_syscall(CLEONOS_SYSCALL_WM_MOVE, (u64)req, 0ULL, 0ULL); } -u64 cleonos_sys_wm_set_focus(u64 window_id) { - return cleonos_syscall(CLEONOS_SYSCALL_WM_SET_FOCUS, window_id, 0ULL, 0ULL); -} +u64 cleonos_sys_wm_set_focus(u64 window_id) { + return cleonos_syscall(CLEONOS_SYSCALL_WM_SET_FOCUS, window_id, 0ULL, 0ULL); +} + +u64 cleonos_sys_wm_set_flags(u64 window_id, u64 flags) { + return cleonos_syscall(CLEONOS_SYSCALL_WM_SET_FLAGS, window_id, flags, 0ULL); +} + +u64 cleonos_sys_wm_resize(const cleonos_wm_resize_req *req) { + return cleonos_syscall(CLEONOS_SYSCALL_WM_RESIZE, (u64)req, 0ULL, 0ULL); +} diff --git a/clks b/clks index 44d89fd..a0202d1 160000 --- a/clks +++ b/clks @@ -1 +1 @@ -Subproject commit 44d89fd3f7d6a5af2db4f61b0d4157994a17e564 +Subproject commit a0202d15b1b979c470b2ff70b999a631d1a137fd diff --git a/docs/stage30.md b/docs/stage30.md index 35a3709..8e5e3cd 100644 --- a/docs/stage30.md +++ b/docs/stage30.md @@ -2,7 +2,7 @@ ## Goal - Add a minimal user-space desktop environment entrypoint. -- Provide a simple window manager demo (`uwm`) with focus, dragging, and rendering in framebuffer. +- Provide a usable user-space window manager (`uwm`) with focus, dragging, resize, taskbar, and launcher. - Expose mouse snapshot data to user-space through a dedicated syscall. ## Implementation @@ -11,28 +11,36 @@ - Kernel side (`clks/kernel/runtime/syscall.c`) now exports mouse snapshot: - fields: `x`, `y`, `buttons`, `packet_count`, `ready`. - User wrapper added: `cleonos_sys_mouse_state()`. +- Window protocol update: + - Added `WM_SET_FLAGS` (`id=114`) for `CLEONOS_WM_FLAG_TOPMOST`. + - Added `WM_RESIZE` (`id=115`) so resize keeps the same `window_id`. - New user app: - Added `cleonos/c/apps/uwm_main.c`. - Features: - - Desktop background rendering. + - Bottom taskbar with window buttons. + - Start launcher menu for built-in demo windows. - Multiple windows with z-order. + - Title bar with close, minimize, topmost, and resize controls. - Active window focus + border highlight. - Drag by title bar (left mouse). - - Keyboard fallback controls (`Tab`, `W/A/S/D`, `Q/Esc`). + - Keyboard fallback controls (`Tab`, `1/2/3`, `W/A/S/D`, `M`, `X`, `T`, `+/-`, `Q`). - Build integration: - Added `uwm` into shell command app set in `cleonos/CMakeLists.txt`. - Updated shell help text and standalone `help` app output. - QEMU run/debug update: - - Added optional absolute pointer device by default: + - QEMU now keeps the default PS/2 mouse path enabled for `make run`/`make debug`. + - USB tablet is optional and disabled by default because the kernel currently has a PS/2 mouse driver, not a USB HID tablet driver. + - Optional absolute pointer device: - `-usb -device usb-tablet` - New CMake option: - - `CLEONOS_QEMU_ENABLE_USB_TABLET=ON` (default). + - `CLEONOS_QEMU_ENABLE_USB_TABLET=OFF` (default). + - Set it to `ON` only after USB HID tablet support is available. ## Acceptance Criteria - `uwm` can be started from user shell. -- Framebuffer window demo draws multiple windows and supports dragging. +- UWM draws multiple windows, taskbar, and launcher, and supports dragging, minimize/restore, topmost, close, and resize. - Mouse state is retrievable from user-space via syscall `107`. -- `make run`/`make debug` use USB tablet by default for smoother pointer interaction. +- `make run`/`make debug` use the PS/2 mouse path by default so the existing kernel mouse driver receives movement and buttons. ## Build Targets - `make userapps` @@ -45,8 +53,8 @@ ## Debug Notes - If `uwm` reports framebuffer unavailable: - verify `FB_INFO` syscall is enabled and framebuffer bpp is `32`. -- If pointer behavior is relative/jumpy: - - keep `CLEONOS_QEMU_ENABLE_USB_TABLET=ON`. - - for manual QEMU invocation, include `-usb -device usb-tablet`. +- If the UWM cursor does not move under QEMU: + - keep `CLEONOS_QEMU_ENABLE_USB_TABLET=OFF`. + - for manual QEMU invocation, do not include `-usb -device usb-tablet` until USB HID support exists. - If mouse syscall returns `ready=0`: - check boot log for PS/2 mouse init status under `[INFO][MOUSE]`. diff --git a/docs/syscall.md b/docs/syscall.md index 3ae4dae..98f3149 100644 --- a/docs/syscall.md +++ b/docs/syscall.md @@ -83,7 +83,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `/proc/`:指定 PID 快照文本 - `/proc` 为只读;写入类 syscall 不支持。 -## 4. Syscall 列表(0~113) +## 4. Syscall 列表(0~115) ### 0 `CLEONOS_SYSCALL_LOG_WRITE` @@ -870,6 +870,23 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `arg0`: `u64 window_id` - 返回:成功 `1`,失败 `0` - 说明:将目标窗口置为焦点并提升到顶层 z-order。 + +### 114 `CLEONOS_SYSCALL_WM_SET_FLAGS` + +- 参数: +- `arg0`: `u64 window_id` +- `arg1`: `u64 flags` +- 返回:成功 `1`,失败 `0` +- 说明: +- 设置窗口协议标志;当前支持 `CLEONOS_WM_FLAG_TOPMOST`(`bit0`),置位后窗口保持在普通窗口之上。 + +### 115 `CLEONOS_SYSCALL_WM_RESIZE` + +- 参数: +- `arg0`: `const struct { u64 window_id; u64 width; u64 height; } *req` +- 返回:成功 `1`,失败 `0` +- 说明: +- 调整窗口尺寸并保持 `window_id` 不变;调整后用户态应重新 `WM_PRESENT` 一次提交新尺寸内容。 ## 5. 用户态封装函数 @@ -919,7 +936,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); ## 7. Wine 兼容说明 -- `wine/cleonos_wine_lib/runner.py` 当前已覆盖到 `0..113`(含 `DL_*`、`FB_*`、`KERNEL_VERSION`、`DISK_*`、`NET_*`、`MOUSE_STATE`、`WM_*`)。 +- `wine/cleonos_wine_lib/runner.py` 当前已覆盖到 `0..115`(含 `DL_*`、`FB_*`、`KERNEL_VERSION`、`DISK_*`、`NET_*`、`MOUSE_STATE`、`WM_*`)。 - `DL_*`(`77..79`)在 Wine 中为“可运行兼容”实现: - `DL_OPEN`:加载 guest ELF 到当前 Unicorn 地址空间,返回稳定 `handle`,并做引用计数。 - `DL_SYM`:解析 ELF `SYMTAB/DYNSYM` 并返回 guest 可调用地址。 @@ -938,7 +955,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `DISK_READ_SECTOR`/`DISK_WRITE_SECTOR`(`93..94`)在 Wine 中已实现为 512B 原始扇区读写(host 文件后端)。 - 网络 syscall(`95..106`)在 Wine 当前为兼容占位实现(统一返回 `0`);即 Wine 运行模式下不会提供真实网络收发。 - `MOUSE_STATE`(`107`)在 Wine 中为基础兼容实现:可返回指针数据结构;未启用窗口鼠标事件时 `ready` 可能为 `0`。 -- `WM_*`(`108..113`)在 Wine 当前为兼容占位实现(统一返回 `0`);不会创建真实窗口服务。 +- `WM_*`(`108..115`)在 Wine 当前为兼容占位实现(统一返回 `0`);不会创建真实窗口服务。 - Wine 在运行时崩溃场景下会生成与内核一致格式的“信号编码退出状态”,可通过 `WAITPID` 读取。 - Wine 当前音频 syscall 为占位实现:`AUDIO_AVAILABLE=0`,`AUDIO_PLAY_TONE=0`,`AUDIO_STOP=1`。 - Wine 版本号策略固定为 `85.0.0-wine`(历史兼容号;不会随 syscall 扩展继续增长)。 diff --git a/kit b/kit index 58fa3d6..46c6d4a 160000 --- a/kit +++ b/kit @@ -1 +1 @@ -Subproject commit 58fa3d6d1a6e7713e0f894d259e2ae686f5fea4a +Subproject commit 46c6d4a547e4d8c9a0240a64e07b18c34d81228d diff --git a/wine b/wine index b85078c..c31867a 160000 --- a/wine +++ b/wine @@ -1 +1 @@ -Subproject commit b85078cb5445400aa436889db199809c0c168e40 +Subproject commit c31867a23ab1baf5558b0dcb5fa3988530193fcd