桌面环境

This commit is contained in:
2026-04-26 00:43:06 +08:00
parent 3e9c8ead23
commit 87d9e31385
19 changed files with 1586 additions and 220 deletions

View File

@@ -36,7 +36,7 @@ set(CLEONOS_QEMU_ACCEL_ARGS "")
if(CLEONOS_QEMU_ENABLE_KVM) if(CLEONOS_QEMU_ENABLE_KVM)
list(APPEND CLEONOS_QEMU_ACCEL_ARGS -enable-kvm -cpu host) list(APPEND CLEONOS_QEMU_ACCEL_ARGS -enable-kvm -cpu host)
endif() 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 "") set(CLEONOS_QEMU_INPUT_ARGS "")
if(CLEONOS_QEMU_ENABLE_USB_TABLET) if(CLEONOS_QEMU_ENABLE_USB_TABLET)
list(APPEND CLEONOS_QEMU_INPUT_ARGS -usb -device usb-tablet) list(APPEND CLEONOS_QEMU_INPUT_ARGS -usb -device usb-tablet)

View File

@@ -21,6 +21,7 @@ MENUCONFIG_PRESET ?=
DISK_IMAGE_MB ?= DISK_IMAGE_MB ?=
CLEONOS_ENABLE ?= auto CLEONOS_ENABLE ?= auto
QEMU_DRIVE_IMAGE ?= build/x86_64/cleonos_disk.img QEMU_DRIVE_IMAGE ?= build/x86_64/cleonos_disk.img
CLEONOS_QEMU_ENABLE_USB_TABLET ?= OFF
SHOW_COMMANDS ?= 0 SHOW_COMMANDS ?= 0
V ?= 0 V ?= 0
@@ -68,6 +69,7 @@ MENUCONFIG_SCOPE_ARG += --clks-only
endif endif
CMAKE_PASSTHROUGH_ARGS += -DCLEONOS_ENABLE=$(CLEONOS_ENABLE_EFFECTIVE) 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)),) ifneq ($(strip $(LIMINE_SKIP_CONFIGURE)),)
CMAKE_PASSTHROUGH_ARGS += -DLIMINE_SKIP_CONFIGURE=$(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=ON' # force on"
> $(Q)echo " make run CMAKE_EXTRA_ARGS='-DCLEONOS_QEMU_ENABLE_KVM=OFF' # force off" > $(Q)echo " make run CMAKE_EXTRA_ARGS='-DCLEONOS_QEMU_ENABLE_KVM=OFF' # force off"
> $(Q)echo "QEMU USB tablet toggle:" > $(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 CLEONOS_QEMU_ENABLE_USB_TABLET=OFF # default: PS/2 mouse path"
> $(Q)echo " make run CMAKE_EXTRA_ARGS='-DCLEONOS_QEMU_ENABLE_USB_TABLET=OFF' # disable tablet" > $(Q)echo " make run CLEONOS_QEMU_ENABLE_USB_TABLET=ON # USB tablet, requires USB HID support"
> $(Q)echo "" > $(Q)echo ""
> $(Q)echo "Pass custom CMake cache args via:" > $(Q)echo "Pass custom CMake cache args via:"
> $(Q)echo " make configure CMAKE_EXTRA_ARGS='-DLIMINE_SKIP_CONFIGURE=1 -DOBJCOPY_FOR_TARGET=objcopy'" > $(Q)echo " make configure CMAKE_EXTRA_ARGS='-DLIMINE_SKIP_CONFIGURE=1 -DOBJCOPY_FOR_TARGET=objcopy'"

View File

@@ -1,5 +1,9 @@
file(GLOB_RECURSE USER_INC_SOURCES_ABS CONFIGURE_DEPENDS file(GLOB_RECURSE USER_INC_SOURCES_ABS CONFIGURE_DEPENDS
"${CMAKE_SOURCE_DIR}/cleonos/**/*.inc" "${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) list(SORT USER_INC_SOURCES_ABS)

View File

@@ -1,6 +1,7 @@
#include "cmd_runtime.h" #include "cmd_runtime.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "gumbo.h" #include "gumbo.h"
@@ -8,15 +9,19 @@
#define USH_BROWSER_SOURCE_MAX 256U #define USH_BROWSER_SOURCE_MAX 256U
#define USH_BROWSER_HOST_MAX 128U #define USH_BROWSER_HOST_MAX 128U
#define USH_BROWSER_PATH_MAX 256U #define USH_BROWSER_PATH_MAX 256U
#define USH_BROWSER_HTML_MAX (512U * 1024U) #define USH_BROWSER_HTML_MAX (256U * 1024U)
#define USH_BROWSER_TEXT_MAX (160U * 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_TITLE_MAX 128U
#define USH_BROWSER_DNS_PACKET_MAX 512U #define USH_BROWSER_DNS_PACKET_MAX 512U
#define USH_BROWSER_HTTP_RECV_CHUNK 2048U #define USH_BROWSER_HTTP_RECV_CHUNK 2048U
#define USH_BROWSER_TCP_POLL_BUDGET 200000000ULL #define USH_BROWSER_TCP_POLL_BUDGET 200000000ULL
#define USH_BROWSER_TCP_RECV_IDLE_LOOPS 40ULL #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_TEXT_MAX 96U
#define USH_BROWSER_LINK_HREF_MAX 192U #define USH_BROWSER_LINK_HREF_MAX 192U
#define USH_BROWSER_HISTORY_MAX 16U #define USH_BROWSER_HISTORY_MAX 16U
@@ -24,7 +29,7 @@
#define USH_BROWSER_SEG_MAX 32U #define USH_BROWSER_SEG_MAX 32U
#define USH_BROWSER_SEG_LEN_MAX 63U #define USH_BROWSER_SEG_LEN_MAX 63U
#define USH_BROWSER_CSS_TEXT_MAX 4096U #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_CSS_IDENT_MAX 48U
#define USH_BROWSER_ANSI_RESET "\x1B[0m" #define USH_BROWSER_ANSI_RESET "\x1B[0m"
#define USH_BROWSER_ANSI_BLUE "\x1B[34m" #define USH_BROWSER_ANSI_BLUE "\x1B[34m"
@@ -76,9 +81,9 @@ typedef struct ush_browser_css_rule {
ush_browser_style_delta delta; ush_browser_style_delta delta;
} ush_browser_css_rule; } ush_browser_css_rule;
static char ush_browser_html_buf[USH_BROWSER_HTML_MAX + 1U]; static char *ush_browser_html_buf;
static char ush_browser_http_raw_buf[USH_BROWSER_HTML_MAX + 1U]; static char *ush_browser_http_raw_buf;
static char ush_browser_text_buf[USH_BROWSER_TEXT_MAX + 1U]; static char *ush_browser_text_buf;
static char ush_browser_title[USH_BROWSER_TITLE_MAX]; static char ush_browser_title[USH_BROWSER_TITLE_MAX];
static ush_browser_link ush_browser_links[USH_BROWSER_LINK_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 ush_browser_css_rule ush_browser_css_rules[USH_BROWSER_CSS_RULE_MAX];
static u64 ush_browser_css_rule_count = 0ULL; 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) { static int ush_browser_is_http_url(const char *text) {
if (text == (const char *)0) { if (text == (const char *)0) {
return 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_size = 0ULL;
out_html[0] = '\0'; 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) { if (cleonos_sys_net_available() == 0ULL) {
return 0; return 0;
@@ -969,11 +994,11 @@ static int ush_browser_fetch_http(const char *url_text, char *out_html, u64 out_
goto cleanup; 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; cleonos_net_tcp_recv_req recv_req;
u8 chunk[USH_BROWSER_HTTP_RECV_CHUNK]; u8 chunk[USH_BROWSER_HTTP_RECV_CHUNK];
u64 got; 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.out_payload_ptr = (u64)(usize)chunk;
recv_req.payload_capacity = (u64)sizeof(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; return;
} }
@@ -1134,7 +1159,7 @@ static void ush_browser_text_newline(void) {
} }
static void ush_browser_text_append_char(char ch) { 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; return;
} }
@@ -1180,7 +1205,7 @@ static void ush_browser_text_append_raw(const char *text) {
} }
while (text[i] != '\0') { 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; 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; return (total > 0ULL) ? 1 : 0;
} }
static int ush_browser_render_html(const char *html, u64 html_size) { static int ush_browser_render_html_fallback(const char *html, u64 html_size) {
GumboOutput *output; u64 i = 0ULL;
ush_browser_style root_style; int in_tag = 0;
int pending_space = 0;
if (html == (const char *)0 || html_size == 0ULL) { if (html == (const char *)0 || html_size == 0ULL) {
return 0; return 0;
} }
output = gumbo_parse_with_options(&kGumboDefaultOptions, html, (size_t)html_size); ush_browser_link_count = 0ULL;
if (output == (GumboOutput *)0 || output->root == (GumboNode *)0) { 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; 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_link_count = 0ULL;
ush_browser_css_rule_count = 0ULL; ush_browser_css_rule_count = 0ULL;
ush_browser_text_reset(); 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_walk_dom_styled(output->root, &root_style);
ush_browser_text_trim_trailing_spaces(); ush_browser_text_trim_trailing_spaces();
gumbo_destroy_output(&kGumboDefaultOptions, output); gumbo_destroy_output(&options, output);
return 1; return 1;
} }
@@ -2929,6 +3024,11 @@ static int ush_cmd_browser(const ush_state *sh, const char *arg) {
return 0; 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++) { for (i = 0ULL; i < (u64)USH_BROWSER_HISTORY_MAX; i++) {
history[i][0] = '\0'; history[i][0] = '\0';
} }
@@ -2953,7 +3053,7 @@ static int ush_cmd_browser(const ush_state *sh, const char *arg) {
} }
for (;;) { 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) { &html_size) == 0) {
if (ush_browser_is_https_url(current_source) != 0) { if (ush_browser_is_https_url(current_source) != 0) {
ush_writeln("browser: https:// is not supported yet"); ush_writeln("browser: https:// is not supported yet");

View File

@@ -16,7 +16,7 @@ static int ush_cmd_help(void) {
ush_writeln(" bmpview <file.bmp> [cols]"); ush_writeln(" bmpview <file.bmp> [cols]");
ush_writeln(" qrcode [--ecc <L|M|Q|H>] <text>"); ush_writeln(" qrcode [--ecc <L|M|Q|H>] <text>");
ush_writeln(" vim [file] (vim-like editor: normal/insert/:w/:q/:wq)"); 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 <file.wav> [steps] [ticks] / wavplay --stop"); ush_writeln(" wavplay <file.wav> [steps] [ticks] / wavplay --stop");
ush_writeln(" fastfetch [--plain]"); ush_writeln(" fastfetch [--plain]");
ush_writeln(" doom [wad_path] (framebuffer bootstrap renderer)"); ush_writeln(" doom [wad_path] (framebuffer bootstrap renderer)");

View File

@@ -138,7 +138,7 @@ static int ush_cmd_help(void) {
ush_writeln(" bmpview <file.bmp> [cols]"); ush_writeln(" bmpview <file.bmp> [cols]");
ush_writeln(" qrcode [--ecc <L|M|Q|H>] <text>"); ush_writeln(" qrcode [--ecc <L|M|Q|H>] <text>");
ush_writeln(" vim [file] (vim-like editor)"); 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 <file.wav> [steps] [ticks] / wavplay --stop"); ush_writeln(" wavplay <file.wav> [steps] [ticks] / wavplay --stop");
ush_writeln(" fastfetch [--plain]"); ush_writeln(" fastfetch [--plain]");
ush_writeln(" doom [wad_path] (framebuffer bootstrap renderer)"); ush_writeln(" doom [wad_path] (framebuffer bootstrap renderer)");

View File

@@ -3,33 +3,65 @@
#include "../cmd_runtime.h" #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_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_EVENT_BUDGET 128ULL
#define USH_UWM_STARTUP_KEY_DRAIN_MAX 256ULL #define USH_UWM_STARTUP_KEY_DRAIN_MAX 256ULL
#define USH_UWM_MIN_WINDOW_W 180 #define USH_UWM_IDLE_SPINS 32
#define USH_UWM_MIN_WINDOW_H 120
#define USH_UWM_TOP_CLAMP_Y 24 #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_LEFT 1ULL
#define USH_UWM_KEY_RIGHT 2ULL #define USH_UWM_KEY_RIGHT 2ULL
#define USH_UWM_KEY_UP 3ULL #define USH_UWM_KEY_UP 3ULL
#define USH_UWM_KEY_DOWN 4ULL #define USH_UWM_KEY_DOWN 4ULL
#define USH_UWM_WM_FLAG_TOPMOST 0x1ULL
typedef unsigned int ush_uwm_u32; 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 { typedef struct ush_uwm_window {
u64 id; u64 id;
int x; int x;
int y; int y;
int w; int w;
int h; int h;
ush_uwm_u32 color;
ush_uwm_u32 *pixels; ush_uwm_u32 *pixels;
u64 pixel_count; u64 pixel_count;
int alive; 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; } ush_uwm_window;
typedef struct ush_uwm_session { typedef struct ush_uwm_session {
@@ -40,24 +72,47 @@ typedef struct ush_uwm_session {
int drag_window; int drag_window;
int drag_offset_x; int drag_offset_x;
int drag_offset_y; 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; u64 tty_before;
int tty_switched; int tty_switched;
char last_error[96];
ush_uwm_window windows[USH_UWM_WINDOW_COUNT]; ush_uwm_window windows[USH_UWM_WINDOW_COUNT];
} ush_uwm_session; } ush_uwm_session;
int ush_uwm_window_index_valid(int index); 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_clampi(int value, int min_value, int max_value);
int ush_uwm_u64_as_i32(u64 raw); int ush_uwm_u64_as_i32(u64 raw);
void ush_uwm_drain_startup_keys(void); 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_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_create_window(ush_uwm_window *win);
int ush_uwm_present_window(const 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); 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_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_set_active(ush_uwm_session *sess, int index);
void ush_uwm_focus_next(ush_uwm_session *sess); 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); 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); int ush_uwm_loop(ush_uwm_session *sess);

View File

@@ -2,16 +2,16 @@
static void ush_uwm_usage(void) { static void ush_uwm_usage(void) {
ush_writeln("usage: uwm"); ush_writeln("usage: uwm");
ush_writeln("wm mode: kernel compositor + user window manager"); ush_writeln("keys: q quit, tab focus, 1/2/3 restore, wasd/arrow move");
ush_writeln("keys: q quit, tab focus next, 1/2/3 focus, wasd/arrow move"); ush_writeln("keys: m minimize, x close, t pin top, +/- resize");
ush_writeln("mouse: drag focused window by title bar"); ush_writeln("mouse: drag titlebar, resize bottom-right, use taskbar/start");
} }
static int ush_uwm_parse_args(const char *arg) { static int ush_uwm_parse_args(const char *arg) {
char first[USH_PATH_MAX]; char first[USH_PATH_MAX];
const char *rest = ""; const char *rest = "";
if (arg == (const char *)0 || arg[0] == '\0') { if (arg == (const char *)0 || arg[0] == 0) {
return 1; return 1;
} }
@@ -19,7 +19,7 @@ static int ush_uwm_parse_args(const char *arg) {
return 0; return 0;
} }
if (rest != (const char *)0 && rest[0] != '\0') { if (rest != (const char *)0 && rest[0] != 0) {
return 0; return 0;
} }
@@ -30,74 +30,170 @@ static int ush_uwm_parse_args(const char *arg) {
return 0; 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) { int ush_uwm_prepare_session(ush_uwm_session *sess) {
cleonos_fb_info fb; cleonos_fb_info fb;
int i; int work_bottom;
int work_h;
int base_w; int base_w;
int base_h; int base_h;
const int x_offsets[USH_UWM_WINDOW_COUNT] = {64, 220, 380}; int start_w;
const int y_offsets[USH_UWM_WINDOW_COUNT] = {80, 140, 220}; int start_h;
const ush_uwm_u32 colors[USH_UWM_WINDOW_COUNT] = {0x002B3C66UL, 0x00385C7AUL, 0x004A7288UL}; 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) { if (sess == (ush_uwm_session *)0) {
return 0; return 0;
} }
ush_zero(sess, (u64)sizeof(*sess)); 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) { 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) { 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_w = (int)fb.width;
sess->screen_h = (int)fb.height; sess->screen_h = (int)fb.height;
sess->active_window = 0; sess->active_window = 0;
sess->dragging = 0;
sess->drag_window = -1; sess->drag_window = -1;
sess->drag_offset_x = 0; sess->resize_window = -1;
sess->drag_offset_y = 0;
sess->tty_before = cleonos_sys_tty_active(); sess->tty_before = cleonos_sys_tty_active();
sess->tty_switched = 0;
base_w = sess->screen_w / 3; work_bottom = ush_uwm_work_bottom(sess);
base_h = sess->screen_h / 3; work_h = work_bottom - USH_UWM_TOP_CLAMP_Y;
if (base_w < USH_UWM_MIN_WINDOW_W) { if (work_h < 80) {
base_w = USH_UWM_MIN_WINDOW_W; work_h = 80;
}
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;
} }
for (i = 0; i < (int)USH_UWM_WINDOW_COUNT; i++) { base_w = ush_uwm_fit_dimension(USH_UWM_APP_START_W, USH_UWM_MIN_WINDOW_W, sess->screen_w - 48);
ush_uwm_window *win = &sess->windows[i]; 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_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) { if (max_x < 0) {
max_x = 0; max_x = 0;
} }
if (max_y < 0) { if (max_y < USH_UWM_TOP_CLAMP_Y) {
max_y = 0; max_y = USH_UWM_TOP_CLAMP_Y;
} }
win->id = 0ULL; ush_uwm_init_window(&sess->windows[i], USH_UWM_KIND_APP, titles[i], subtitles[i], ush_uwm_clampi(x, 0, max_x),
win->x = ush_uwm_clampi(x_offsets[i], 0, max_x); ush_uwm_clampi(y, USH_UWM_TOP_CLAMP_Y, max_y), base_w, base_h, accents[i], 0, 0);
win->y = ush_uwm_clampi(y_offsets[i], USH_UWM_TOP_CLAMP_Y, max_y); }
win->w = base_w;
win->h = base_h; ush_uwm_init_window(&sess->windows[USH_UWM_TASKBAR_INDEX], USH_UWM_KIND_TASKBAR, "TASKBAR", "", 0,
win->color = colors[i]; sess->screen_h - USH_UWM_TASKBAR_H, sess->screen_w, USH_UWM_TASKBAR_H, 0x000078D7U, 1, 0);
win->pixels = (ush_uwm_u32 *)0; if (sess->windows[USH_UWM_TASKBAR_INDEX].y < USH_UWM_TOP_CLAMP_Y) {
win->pixel_count = 0ULL; sess->windows[USH_UWM_TASKBAR_INDEX].y = USH_UWM_TOP_CLAMP_Y;
win->alive = 0; }
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; return 1;
@@ -105,30 +201,34 @@ int ush_uwm_prepare_session(ush_uwm_session *sess) {
int ush_uwm_start(ush_uwm_session *sess) { int ush_uwm_start(ush_uwm_session *sess) {
int i; int i;
int started = 0;
if (sess == (ush_uwm_session *)0) { if (sess == (ush_uwm_session *)0) {
return 0; return 0;
} }
for (i = 0; i < (int)USH_UWM_WINDOW_COUNT; i++) { if (ush_uwm_boot_window(sess, USH_UWM_TASKBAR_INDEX) == 0) {
ush_uwm_window *win = &sess->windows[i]; return ush_uwm_fail(sess, "uwm: taskbar create failed");
}
if (ush_uwm_alloc_pixels(win) == 0) { for (i = 0; i < (int)USH_UWM_APP_COUNT; i++) {
return 0; if (ush_uwm_boot_window(sess, i) != 0) {
} started++;
if (ush_uwm_create_window(win) == 0) {
return 0;
}
ush_uwm_render_content(win);
if (ush_uwm_present_window(win) == 0) {
return 0;
} }
} }
sess->active_window = (int)USH_UWM_WINDOW_COUNT - 1; if (started == 0) {
ush_uwm_set_active(sess, sess->active_window); 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; return 1;
} }
@@ -185,7 +285,7 @@ int ush_cmd_uwm(const char *arg) {
} }
if (ush_uwm_prepare_session(&sess) == 0) { if (ush_uwm_prepare_session(&sess) == 0) {
ush_writeln("uwm: framebuffer unavailable"); ush_writeln(sess.last_error);
return 0; return 0;
} }
@@ -195,13 +295,10 @@ int ush_cmd_uwm(const char *arg) {
if (ush_uwm_start(&sess) == 0) { if (ush_uwm_start(&sess) == 0) {
ush_uwm_stop(&sess); ush_uwm_stop(&sess);
ush_uwm_restore_tty(&sess); ush_uwm_restore_tty(&sess);
ush_writeln("uwm: kernel wm unavailable or init failed"); ush_writeln(sess.last_error);
return 0; 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) { if (ush_uwm_loop(&sess) != 0) {
success = 1; success = 1;
} }

View File

@@ -1,7 +1,67 @@
#include "uwm.h" #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) { static void ush_uwm_handle_key_event(ush_uwm_session *sess, u64 key, int *running) {
int idx = 0; int idx;
int dx = 0; int dx = 0;
int dy = 0; int dy = 0;
@@ -14,21 +74,43 @@ static void ush_uwm_handle_key_event(ush_uwm_session *sess, u64 key, int *runnin
return; return;
} }
if (key == (u64)'\t') { if (key == (u64)' ') {
ush_uwm_focus_next(sess); ush_uwm_focus_next(sess);
return; return;
} }
if (key >= (u64)'1' && key <= (u64)'3') { if (key == (u64)'1' || key == (u64)'2' || key == (u64)'3') {
int candidate = (int)(key - (u64)'1'); ush_uwm_restore_window(sess, (int)(key - (u64)'1'));
if (ush_uwm_window_index_valid(candidate) != 0) {
ush_uwm_set_active(sess, candidate);
}
return; return;
} }
idx = sess->active_window; 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; 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) { 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 buttons;
u64 changed; u64 changed;
int local_x; int local_x;
@@ -60,6 +201,7 @@ static void ush_uwm_handle_mouse_button(ush_uwm_session *sess, int window_index,
return; return;
} }
win = &sess->windows[window_index];
buttons = event->arg0; buttons = event->arg0;
changed = event->arg1; changed = event->arg1;
local_x = ush_uwm_u64_as_i32(event->arg2); 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; return;
} }
if (left_down != 0) { if (left_down == 0) {
if (local_y >= 0 && local_y < USH_UWM_TITLE_DRAG_HEIGHT) { if (sess->dragging != 0 && sess->drag_window == window_index) {
sess->dragging = 1; sess->dragging = 0;
sess->drag_window = window_index; sess->drag_window = -1;
sess->drag_offset_x = local_x;
sess->drag_offset_y = local_y;
sess->active_window = window_index;
} }
} else if (sess->dragging != 0 && sess->drag_window == window_index) { if (sess->resizing != 0 && sess->resize_window == window_index) {
sess->dragging = 0; 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) { static void ush_uwm_handle_mouse_move(ush_uwm_session *sess, int window_index, const cleonos_wm_event *event) {
int global_x; int global_x;
int global_y; int global_y;
int new_x;
int new_y;
if (sess == (ush_uwm_session *)0 || event == (const cleonos_wm_event *)0 || if (sess == (ush_uwm_session *)0 || event == (const cleonos_wm_event *)0 ||
ush_uwm_window_index_valid(window_index) == 0) { ush_uwm_window_index_valid(window_index) == 0) {
return; 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; return;
} }
global_x = ush_uwm_u64_as_i32(event->arg0); if (sess->dragging != 0 && sess->drag_window == window_index && ush_uwm_app_index_valid(window_index) != 0) {
global_y = ush_uwm_u64_as_i32(event->arg1); (void)ush_uwm_window_move_clamped(sess, window_index, global_x - sess->drag_offset_x,
new_x = global_x - sess->drag_offset_x; global_y - sess->drag_offset_y);
new_y = global_y - sess->drag_offset_y; }
(void)ush_uwm_window_move_clamped(sess, window_index, new_x, new_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) { 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) { switch (event->type) {
case CLEONOS_WM_EVENT_FOCUS_GAINED: 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; break;
case CLEONOS_WM_EVENT_FOCUS_LOST: case CLEONOS_WM_EVENT_FOCUS_LOST:
if (sess->drag_window == window_index) { if (sess->drag_window == window_index) {
sess->dragging = 0; sess->dragging = 0;
sess->drag_window = -1;
}
if (sess->resize_window == window_index) {
ush_uwm_finish_resize(sess);
} }
break; break;
case CLEONOS_WM_EVENT_KEY: 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 ush_uwm_loop(ush_uwm_session *sess) {
int running = 1; int running = 1;
int idle_spins = 0;
if (sess == (ush_uwm_session *)0) { if (sess == (ush_uwm_session *)0) {
return 0; return 0;
@@ -144,30 +436,33 @@ int ush_uwm_loop(ush_uwm_session *sess) {
while (running != 0) { while (running != 0) {
int i; 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++) { for (i = 0; i < (int)USH_UWM_WINDOW_COUNT; i++) {
ush_uwm_window *win = &sess->windows[i]; if (i == preferred_window) {
u64 budget = 0ULL;
if (win->alive == 0 || win->id == 0ULL) {
continue; continue;
} }
handled_events += ush_uwm_poll_window_events(sess, i, &running);
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++;
}
if (running == 0) { if (running == 0) {
break; break;
} }
@@ -177,7 +472,20 @@ int ush_uwm_loop(ush_uwm_session *sess) {
break; 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; return 1;

View File

@@ -4,7 +4,15 @@ int ush_uwm_window_index_valid(int index) {
return (index >= 0 && index < (int)USH_UWM_WINDOW_COUNT) ? 1 : 0; 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) { int ush_uwm_clampi(int value, int min_value, int max_value) {
if (max_value < min_value) {
return min_value;
}
if (value < min_value) { if (value < min_value) {
return min_value; return min_value;
} }

View File

@@ -1,5 +1,65 @@
#include "uwm.h" #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) { 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 left;
int top; 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; u64 base = (u64)(unsigned int)row * (u64)(unsigned int)win->w;
int col; int col;
if (base + (u64)(unsigned int)right > win->pixel_count) {
return;
}
for (col = left; col < right; col++) { for (col = left; col < right; col++) {
u64 idx = base + (u64)(unsigned int)col; win->pixels[base + (u64)(unsigned int)col] = color;
if (idx >= win->pixel_count) {
return;
}
win->pixels[idx] = color;
} }
} }
} }
void ush_uwm_render_content(ush_uwm_window *win) { static void ush_uwm_gradient_rect(ush_uwm_window *win, int x, int y, int w, int h, ush_uwm_u32 top,
int y; ush_uwm_u32 bottom) {
int row;
if (win == (ush_uwm_window *)0 || win->pixels == (ush_uwm_u32 *)0) { for (row = 0; row < h; row++) {
return; ush_uwm_fill_rect(win, x, y + row, w, 1, ush_uwm_mix(top, bottom, row, h - 1));
}
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);
} }
} }
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) { 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 count;
u64 bytes; 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; 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)) { if (count == 0ULL || count > (((u64)-1) / 4ULL)) {
return 0; return 0;
} }
bytes = count * 4ULL; bytes = count * 4ULL;
win->pixels = (ush_uwm_u32 *)malloc((size_t)bytes); new_pixels = (ush_uwm_u32 *)malloc((size_t)bytes);
if (win->pixels == (ush_uwm_u32 *)0) { if (new_pixels == (ush_uwm_u32 *)0) {
return 0; return 0;
} }
if (win->pixels != (ush_uwm_u32 *)0) {
free(win->pixels);
}
win->pixels = new_pixels;
win->pixel_count = count; win->pixel_count = count;
win->w = width;
win->h = height;
ush_zero(win->pixels, bytes); ush_zero(win->pixels, bytes);
win->dirty = 1;
return 1; return 1;
} }
@@ -100,7 +623,7 @@ int ush_uwm_create_window(ush_uwm_window *win) {
req.y = (u64)(i64)win->y; req.y = (u64)(i64)win->y;
req.width = (u64)(unsigned int)win->w; req.width = (u64)(unsigned int)win->w;
req.height = (u64)(unsigned int)win->h; 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); win->id = cleonos_sys_wm_create(&req);
if (win->id == 0ULL) { if (win->id == 0ULL) {
return 0; 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; 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) { if (win == (ush_uwm_window *)0) {
return; return;
} }
@@ -134,14 +657,25 @@ void ush_uwm_destroy_window(ush_uwm_window *win) {
(void)cleonos_sys_wm_destroy(win->id); (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) { if (win->pixels != (ush_uwm_u32 *)0) {
free(win->pixels); free(win->pixels);
} }
win->id = 0ULL;
win->pixels = (ush_uwm_u32 *)0; win->pixels = (ush_uwm_u32 *)0;
win->pixel_count = 0ULL; 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) { 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_x;
int new_y; 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; return 0;
} }
win = &sess->windows[index]; 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; return 0;
} }
max_x = sess->screen_w - win->w; 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) { if (max_x < 0) {
max_x = 0; max_x = 0;
} }
if (max_y < 0) { if (max_y < USH_UWM_TOP_CLAMP_Y) {
max_y = 0; max_y = USH_UWM_TOP_CLAMP_Y;
} }
new_x = ush_uwm_clampi(target_x, 0, max_x); new_x = ush_uwm_clampi(target_x, 0, max_x);
new_y = ush_uwm_clampi(target_y, USH_UWM_TOP_CLAMP_Y, max_y); 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.window_id = win->id;
req.x = (u64)(i64)new_x; 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; 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) { void ush_uwm_set_active(ush_uwm_session *sess, int index) {
ush_uwm_window *win; 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; return;
} }
win = &sess->windows[index]; 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; return;
} }
old_active = sess->active_window;
if (cleonos_sys_wm_set_focus(win->id) != 0ULL) { if (cleonos_sys_wm_set_focus(win->id) != 0ULL) {
sess->active_window = index; 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; return;
} }
if (ush_uwm_window_index_valid(sess->active_window) != 0) { start = (ush_uwm_app_index_valid(sess->active_window) != 0) ? sess->active_window : 0;
start = sess->active_window; for (i = 1; i <= (int)USH_UWM_APP_COUNT; i++) {
} else { int idx = (start + i) % (int)USH_UWM_APP_COUNT;
start = 0; if (sess->windows[idx].alive != 0 && sess->windows[idx].minimized == 0 && sess->windows[idx].closed == 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) {
ush_uwm_set_active(sess, idx); ush_uwm_set_active(sess, idx);
return; 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);
} }

View File

@@ -64,6 +64,8 @@ typedef struct cleonos_mouse_state {
#define CLEONOS_WM_EVENT_MOUSE_MOVE 4ULL #define CLEONOS_WM_EVENT_MOUSE_MOVE 4ULL
#define CLEONOS_WM_EVENT_MOUSE_BUTTON 5ULL #define CLEONOS_WM_EVENT_MOUSE_BUTTON 5ULL
#define CLEONOS_WM_FLAG_TOPMOST 0x1ULL
typedef struct cleonos_wm_event { typedef struct cleonos_wm_event {
u64 type; u64 type;
u64 arg0; u64 arg0;
@@ -94,6 +96,12 @@ typedef struct cleonos_wm_move_req {
u64 y; u64 y;
} cleonos_wm_move_req; } 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 { typedef struct cleonos_fb_blit_req {
u64 pixels_ptr; u64 pixels_ptr;
u64 src_width; u64 src_width;
@@ -253,6 +261,8 @@ typedef struct cleonos_net_tcp_recv_req {
#define CLEONOS_SYSCALL_WM_POLL_EVENT 111ULL #define CLEONOS_SYSCALL_WM_POLL_EVENT 111ULL
#define CLEONOS_SYSCALL_WM_MOVE 112ULL #define CLEONOS_SYSCALL_WM_MOVE 112ULL
#define CLEONOS_SYSCALL_WM_SET_FOCUS 113ULL #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_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
u64 cleonos_sys_log_write(const char *message, u64 length); u64 cleonos_sys_log_write(const char *message, u64 length);
@@ -369,5 +379,7 @@ 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_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_move(const cleonos_wm_move_req *req);
u64 cleonos_sys_wm_set_focus(u64 window_id); 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 #endif

View File

@@ -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); 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 #define CLIB_HEAP_ALIGN 8U
typedef union clib_heap_storage { typedef union clib_heap_storage {

View File

@@ -487,3 +487,11 @@ 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_focus(u64 window_id) {
return cleonos_syscall(CLEONOS_SYSCALL_WM_SET_FOCUS, window_id, 0ULL, 0ULL); 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);
}

2
clks

Submodule clks updated: 44d89fd3f7...a0202d15b1

View File

@@ -2,7 +2,7 @@
## Goal ## Goal
- Add a minimal user-space desktop environment entrypoint. - 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. - Expose mouse snapshot data to user-space through a dedicated syscall.
## Implementation ## Implementation
@@ -11,28 +11,36 @@
- Kernel side (`clks/kernel/runtime/syscall.c`) now exports mouse snapshot: - Kernel side (`clks/kernel/runtime/syscall.c`) now exports mouse snapshot:
- fields: `x`, `y`, `buttons`, `packet_count`, `ready`. - fields: `x`, `y`, `buttons`, `packet_count`, `ready`.
- User wrapper added: `cleonos_sys_mouse_state()`. - 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: - New user app:
- Added `cleonos/c/apps/uwm_main.c`. - Added `cleonos/c/apps/uwm_main.c`.
- Features: - Features:
- Desktop background rendering. - Bottom taskbar with window buttons.
- Start launcher menu for built-in demo windows.
- Multiple windows with z-order. - Multiple windows with z-order.
- Title bar with close, minimize, topmost, and resize controls.
- Active window focus + border highlight. - Active window focus + border highlight.
- Drag by title bar (left mouse). - 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: - Build integration:
- Added `uwm` into shell command app set in `cleonos/CMakeLists.txt`. - Added `uwm` into shell command app set in `cleonos/CMakeLists.txt`.
- Updated shell help text and standalone `help` app output. - Updated shell help text and standalone `help` app output.
- QEMU run/debug update: - 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` - `-usb -device usb-tablet`
- New CMake option: - 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 ## Acceptance Criteria
- `uwm` can be started from user shell. - `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`. - 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 ## Build Targets
- `make userapps` - `make userapps`
@@ -45,8 +53,8 @@
## Debug Notes ## Debug Notes
- If `uwm` reports framebuffer unavailable: - If `uwm` reports framebuffer unavailable:
- verify `FB_INFO` syscall is enabled and framebuffer bpp is `32`. - verify `FB_INFO` syscall is enabled and framebuffer bpp is `32`.
- If pointer behavior is relative/jumpy: - If the UWM cursor does not move under QEMU:
- keep `CLEONOS_QEMU_ENABLE_USB_TABLET=ON`. - keep `CLEONOS_QEMU_ENABLE_USB_TABLET=OFF`.
- for manual QEMU invocation, include `-usb -device usb-tablet`. - for manual QEMU invocation, do not include `-usb -device usb-tablet` until USB HID support exists.
- If mouse syscall returns `ready=0`: - If mouse syscall returns `ready=0`:
- check boot log for PS/2 mouse init status under `[INFO][MOUSE]`. - check boot log for PS/2 mouse init status under `[INFO][MOUSE]`.

View File

@@ -83,7 +83,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
- `/proc/<pid>`:指定 PID 快照文本 - `/proc/<pid>`:指定 PID 快照文本
- `/proc` 为只读;写入类 syscall 不支持。 - `/proc` 为只读;写入类 syscall 不支持。
## 4. Syscall 列表0~113 ## 4. Syscall 列表0~115
### 0 `CLEONOS_SYSCALL_LOG_WRITE` ### 0 `CLEONOS_SYSCALL_LOG_WRITE`
@@ -871,6 +871,23 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
- 返回:成功 `1`,失败 `0` - 返回:成功 `1`,失败 `0`
- 说明:将目标窗口置为焦点并提升到顶层 z-order。 - 说明:将目标窗口置为焦点并提升到顶层 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. 用户态封装函数 ## 5. 用户态封装函数
用户态封装位于: 用户态封装位于:
@@ -919,7 +936,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
## 7. Wine 兼容说明 ## 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_*``77..79`)在 Wine 中为“可运行兼容”实现:
- `DL_OPEN`:加载 guest ELF 到当前 Unicorn 地址空间,返回稳定 `handle`,并做引用计数。 - `DL_OPEN`:加载 guest ELF 到当前 Unicorn 地址空间,返回稳定 `handle`,并做引用计数。
- `DL_SYM`:解析 ELF `SYMTAB/DYNSYM` 并返回 guest 可调用地址。 - `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 文件后端)。 - `DISK_READ_SECTOR`/`DISK_WRITE_SECTOR``93..94`)在 Wine 中已实现为 512B 原始扇区读写host 文件后端)。
- 网络 syscall`95..106`)在 Wine 当前为兼容占位实现(统一返回 `0`);即 Wine 运行模式下不会提供真实网络收发。 - 网络 syscall`95..106`)在 Wine 当前为兼容占位实现(统一返回 `0`);即 Wine 运行模式下不会提供真实网络收发。
- `MOUSE_STATE``107`)在 Wine 中为基础兼容实现:可返回指针数据结构;未启用窗口鼠标事件时 `ready` 可能为 `0` - `MOUSE_STATE``107`)在 Wine 中为基础兼容实现:可返回指针数据结构;未启用窗口鼠标事件时 `ready` 可能为 `0`
- `WM_*``108..113`)在 Wine 当前为兼容占位实现(统一返回 `0`);不会创建真实窗口服务。 - `WM_*``108..115`)在 Wine 当前为兼容占位实现(统一返回 `0`);不会创建真实窗口服务。
- Wine 在运行时崩溃场景下会生成与内核一致格式的“信号编码退出状态”,可通过 `WAITPID` 读取。 - Wine 在运行时崩溃场景下会生成与内核一致格式的“信号编码退出状态”,可通过 `WAITPID` 读取。
- Wine 当前音频 syscall 为占位实现:`AUDIO_AVAILABLE=0``AUDIO_PLAY_TONE=0``AUDIO_STOP=1` - Wine 当前音频 syscall 为占位实现:`AUDIO_AVAILABLE=0``AUDIO_PLAY_TONE=0``AUDIO_STOP=1`
- Wine 版本号策略固定为 `85.0.0-wine`(历史兼容号;不会随 syscall 扩展继续增长)。 - Wine 版本号策略固定为 `85.0.0-wine`(历史兼容号;不会随 syscall 扩展继续增长)。

2
kit

Submodule kit updated: 58fa3d6d1a...46c6d4a547

2
wine

Submodule wine updated: b85078cb54...c31867a23a