diff --git a/cleonos/c/apps/uwm/uwm.h b/cleonos/c/apps/uwm/uwm.h new file mode 100644 index 0000000..b94c2bf --- /dev/null +++ b/cleonos/c/apps/uwm/uwm.h @@ -0,0 +1,72 @@ +#ifndef CLEONOS_UWM_H +#define CLEONOS_UWM_H + +#include "../cmd_runtime.h" + +#define USH_UWM_WINDOW_COUNT 3U +#define USH_UWM_TTY_DISPLAY 1ULL +#define USH_UWM_TITLE_DRAG_HEIGHT 18 +#define USH_UWM_MOVE_STEP 12 +#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_KEY_LEFT 1ULL +#define USH_UWM_KEY_RIGHT 2ULL +#define USH_UWM_KEY_UP 3ULL +#define USH_UWM_KEY_DOWN 4ULL + +typedef unsigned int ush_uwm_u32; + +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; +} ush_uwm_window; + +typedef struct ush_uwm_session { + int screen_w; + int screen_h; + int active_window; + int dragging; + int drag_window; + int drag_offset_x; + int drag_offset_y; + u64 tty_before; + int tty_switched; + ush_uwm_window windows[USH_UWM_WINDOW_COUNT]; +} ush_uwm_session; + +int ush_uwm_window_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_create_window(ush_uwm_window *win); +int ush_uwm_present_window(const 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); +void ush_uwm_set_active(ush_uwm_session *sess, int index); +void ush_uwm_focus_next(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); + +int ush_uwm_prepare_session(ush_uwm_session *sess); +int ush_uwm_start(ush_uwm_session *sess); +void ush_uwm_stop(ush_uwm_session *sess); +int ush_uwm_switch_to_display_tty(ush_uwm_session *sess); +void ush_uwm_restore_tty(ush_uwm_session *sess); +int ush_cmd_uwm(const char *arg); + +#endif diff --git a/cleonos/c/apps/uwm/uwm_app.c b/cleonos/c/apps/uwm/uwm_app.c new file mode 100644 index 0000000..a06b959 --- /dev/null +++ b/cleonos/c/apps/uwm/uwm_app.c @@ -0,0 +1,213 @@ +#include "uwm.h" + +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"); +} + +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') { + return 1; + } + + if (ush_split_first_and_rest(arg, first, (u64)sizeof(first), &rest) == 0) { + return 0; + } + + if (rest != (const char *)0 && rest[0] != '\0') { + return 0; + } + + if (ush_streq(first, "--help") != 0 || ush_streq(first, "-h") != 0) { + return 2; + } + + return 0; +} + +int ush_uwm_prepare_session(ush_uwm_session *sess) { + cleonos_fb_info fb; + int i; + 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}; + + if (sess == (ush_uwm_session *)0) { + return 0; + } + + ush_zero(sess, (u64)sizeof(*sess)); + if (cleonos_sys_fb_info(&fb) == 0ULL || fb.width == 0ULL || fb.height == 0ULL || fb.bpp != 32ULL) { + return 0; + } + + if (fb.width > 4096ULL || fb.height > 4096ULL) { + return 0; + } + + 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->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; + } + + for (i = 0; i < (int)USH_UWM_WINDOW_COUNT; i++) { + ush_uwm_window *win = &sess->windows[i]; + int max_x = sess->screen_w - base_w; + int max_y = sess->screen_h - base_h; + + if (max_x < 0) { + max_x = 0; + } + if (max_y < 0) { + max_y = 0; + } + + 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; + } + + return 1; +} + +int ush_uwm_start(ush_uwm_session *sess) { + int i; + + 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_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; + } + } + + sess->active_window = (int)USH_UWM_WINDOW_COUNT - 1; + ush_uwm_set_active(sess, sess->active_window); + return 1; +} + +void ush_uwm_stop(ush_uwm_session *sess) { + int i; + + if (sess == (ush_uwm_session *)0) { + return; + } + + for (i = 0; i < (int)USH_UWM_WINDOW_COUNT; i++) { + ush_uwm_destroy_window(&sess->windows[i]); + } +} + +int ush_uwm_switch_to_display_tty(ush_uwm_session *sess) { + if (sess == (ush_uwm_session *)0) { + return 0; + } + + if (sess->tty_before != USH_UWM_TTY_DISPLAY) { + (void)cleonos_sys_tty_switch(USH_UWM_TTY_DISPLAY); + sess->tty_switched = 1; + } + + return 1; +} + +void ush_uwm_restore_tty(ush_uwm_session *sess) { + if (sess == (ush_uwm_session *)0) { + return; + } + + if (sess->tty_switched != 0) { + (void)cleonos_sys_tty_switch(sess->tty_before); + sess->tty_switched = 0; + } +} + +int ush_cmd_uwm(const char *arg) { + ush_uwm_session sess; + int parse_result; + int success = 0; + + parse_result = ush_uwm_parse_args(arg); + if (parse_result == 2) { + ush_uwm_usage(); + return 1; + } + + if (parse_result == 0) { + ush_uwm_usage(); + return 0; + } + + if (ush_uwm_prepare_session(&sess) == 0) { + ush_writeln("uwm: framebuffer unavailable"); + return 0; + } + + ush_uwm_drain_startup_keys(); + (void)ush_uwm_switch_to_display_tty(&sess); + + if (ush_uwm_start(&sess) == 0) { + ush_uwm_stop(&sess); + ush_uwm_restore_tty(&sess); + ush_writeln("uwm: kernel wm unavailable or init failed"); + 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; + } + + ush_uwm_stop(&sess); + ush_uwm_restore_tty(&sess); + ush_writeln("uwm: exit"); + return success; +} diff --git a/cleonos/c/apps/uwm/uwm_events.c b/cleonos/c/apps/uwm/uwm_events.c new file mode 100644 index 0000000..6a0eb18 --- /dev/null +++ b/cleonos/c/apps/uwm/uwm_events.c @@ -0,0 +1,184 @@ +#include "uwm.h" + +static void ush_uwm_handle_key_event(ush_uwm_session *sess, u64 key, int *running) { + int idx = 0; + int dx = 0; + int dy = 0; + + if (sess == (ush_uwm_session *)0 || running == (int *)0) { + return; + } + + if (key == (u64)'q' || key == (u64)'Q') { + *running = 0; + return; + } + + if (key == (u64)'\t') { + 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); + } + return; + } + + idx = sess->active_window; + if (ush_uwm_window_index_valid(idx) == 0 || sess->windows[idx].alive == 0) { + return; + } + + if (key == (u64)'a' || key == (u64)'A' || key == USH_UWM_KEY_LEFT) { + dx = -USH_UWM_MOVE_STEP; + } else if (key == (u64)'d' || key == (u64)'D' || key == USH_UWM_KEY_RIGHT) { + dx = USH_UWM_MOVE_STEP; + } else if (key == (u64)'w' || key == (u64)'W' || key == USH_UWM_KEY_UP) { + dy = -USH_UWM_MOVE_STEP; + } else if (key == (u64)'s' || key == (u64)'S' || key == USH_UWM_KEY_DOWN) { + dy = USH_UWM_MOVE_STEP; + } + + if (dx != 0 || dy != 0) { + (void)ush_uwm_window_move_clamped(sess, idx, sess->windows[idx].x + dx, sess->windows[idx].y + dy); + } +} + +static void ush_uwm_handle_mouse_button(ush_uwm_session *sess, int window_index, const cleonos_wm_event *event) { + u64 buttons; + u64 changed; + int local_x; + int local_y; + int left_changed; + int left_down; + + if (sess == (ush_uwm_session *)0 || event == (const cleonos_wm_event *)0 || + ush_uwm_window_index_valid(window_index) == 0) { + return; + } + + buttons = event->arg0; + changed = event->arg1; + local_x = ush_uwm_u64_as_i32(event->arg2); + local_y = ush_uwm_u64_as_i32(event->arg3); + left_changed = ((changed & 0x1ULL) != 0ULL) ? 1 : 0; + left_down = ((buttons & 0x1ULL) != 0ULL) ? 1 : 0; + + if (left_changed == 0) { + 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; + } + } else if (sess->dragging != 0 && sess->drag_window == window_index) { + sess->dragging = 0; + } +} + +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) { + 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); +} + +void ush_uwm_handle_event(ush_uwm_session *sess, int window_index, const cleonos_wm_event *event, int *running) { + if (sess == (ush_uwm_session *)0 || event == (const cleonos_wm_event *)0 || running == (int *)0 || + ush_uwm_window_index_valid(window_index) == 0) { + return; + } + + switch (event->type) { + case CLEONOS_WM_EVENT_FOCUS_GAINED: + sess->active_window = window_index; + break; + case CLEONOS_WM_EVENT_FOCUS_LOST: + if (sess->drag_window == window_index) { + sess->dragging = 0; + } + break; + case CLEONOS_WM_EVENT_KEY: + ush_uwm_handle_key_event(sess, event->arg0, running); + break; + case CLEONOS_WM_EVENT_MOUSE_MOVE: + ush_uwm_handle_mouse_move(sess, window_index, event); + break; + case CLEONOS_WM_EVENT_MOUSE_BUTTON: + ush_uwm_handle_mouse_button(sess, window_index, event); + break; + default: + break; + } +} + +int ush_uwm_loop(ush_uwm_session *sess) { + int running = 1; + + if (sess == (ush_uwm_session *)0) { + return 0; + } + + while (running != 0) { + int i; + + 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) { + 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++; + } + + if (running == 0) { + break; + } + } + + if (running == 0) { + break; + } + + (void)cleonos_sys_yield(); + } + + return 1; +} diff --git a/cleonos/c/apps/uwm/uwm_util.c b/cleonos/c/apps/uwm/uwm_util.c new file mode 100644 index 0000000..efa14fe --- /dev/null +++ b/cleonos/c/apps/uwm/uwm_util.c @@ -0,0 +1,42 @@ +#include "uwm.h" + +int ush_uwm_window_index_valid(int index) { + return (index >= 0 && index < (int)USH_UWM_WINDOW_COUNT) ? 1 : 0; +} + +int ush_uwm_clampi(int value, int min_value, int max_value) { + if (value < min_value) { + return min_value; + } + + if (value > max_value) { + return max_value; + } + + return value; +} + +int ush_uwm_u64_as_i32(u64 raw) { + i64 value = (i64)raw; + + if (value < (-2147483647LL - 1LL)) { + return -2147483647 - 1; + } + + if (value > 2147483647LL) { + return 2147483647; + } + + return (int)value; +} + +void ush_uwm_drain_startup_keys(void) { + u64 drained = 0ULL; + + while (drained < USH_UWM_STARTUP_KEY_DRAIN_MAX) { + if (cleonos_sys_kbd_get_char() == (u64)-1) { + break; + } + drained++; + } +} diff --git a/cleonos/c/apps/uwm/uwm_window.c b/cleonos/c/apps/uwm/uwm_window.c new file mode 100644 index 0000000..450b73d --- /dev/null +++ b/cleonos/c/apps/uwm/uwm_window.c @@ -0,0 +1,227 @@ +#include "uwm.h" + +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; + int right; + int bottom; + int row; + + if (win == (ush_uwm_window *)0 || win->pixels == (ush_uwm_u32 *)0 || win->w <= 0 || win->h <= 0 || w <= 0 || + h <= 0) { + return; + } + + left = x; + top = y; + right = x + w; + bottom = y + h; + + if (left < 0) { + left = 0; + } + if (top < 0) { + top = 0; + } + if (right > win->w) { + right = win->w; + } + if (bottom > win->h) { + bottom = win->h; + } + + if (left >= right || top >= bottom) { + return; + } + + for (row = top; row < bottom; row++) { + u64 base = (u64)(unsigned int)row * (u64)(unsigned int)win->w; + int col; + + for (col = left; col < right; col++) { + u64 idx = base + (u64)(unsigned int)col; + if (idx >= win->pixel_count) { + return; + } + win->pixels[idx] = color; + } + } +} + +void ush_uwm_render_content(ush_uwm_window *win) { + int y; + + 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); + } +} + +int ush_uwm_alloc_pixels(ush_uwm_window *win) { + u64 count; + u64 bytes; + + if (win == (ush_uwm_window *)0 || win->w <= 0 || win->h <= 0) { + return 0; + } + + count = (u64)(unsigned int)win->w * (u64)(unsigned int)win->h; + 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) { + return 0; + } + + win->pixel_count = count; + ush_zero(win->pixels, bytes); + return 1; +} + +int ush_uwm_create_window(ush_uwm_window *win) { + cleonos_wm_create_req req; + + if (win == (ush_uwm_window *)0) { + return 0; + } + + req.x = (u64)(i64)win->x; + req.y = (u64)(i64)win->y; + req.width = (u64)(unsigned int)win->w; + req.height = (u64)(unsigned int)win->h; + req.flags = 0ULL; + win->id = cleonos_sys_wm_create(&req); + if (win->id == 0ULL) { + return 0; + } + + win->alive = 1; + return 1; +} + +int ush_uwm_present_window(const ush_uwm_window *win) { + cleonos_wm_present_req req; + + if (win == (const ush_uwm_window *)0 || win->alive == 0 || win->id == 0ULL || + win->pixels == (ush_uwm_u32 *)0) { + return 0; + } + + req.window_id = win->id; + req.pixels_ptr = (u64)(usize)win->pixels; + req.src_width = (u64)(unsigned int)win->w; + req.src_height = (u64)(unsigned int)win->h; + req.src_pitch_bytes = (u64)(unsigned int)win->w * 4ULL; + return (cleonos_sys_wm_present(&req) != 0ULL) ? 1 : 0; +} + +void ush_uwm_destroy_window(ush_uwm_window *win) { + if (win == (ush_uwm_window *)0) { + return; + } + + if (win->id != 0ULL) { + (void)cleonos_sys_wm_destroy(win->id); + } + + 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; +} + +int ush_uwm_window_move_clamped(ush_uwm_session *sess, int index, int target_x, int target_y) { + ush_uwm_window *win; + cleonos_wm_move_req req; + int max_x; + int max_y; + int new_x; + int new_y; + + if (sess == (ush_uwm_session *)0 || ush_uwm_window_index_valid(index) == 0) { + return 0; + } + + win = &sess->windows[index]; + if (win->alive == 0 || win->id == 0ULL) { + return 0; + } + + max_x = sess->screen_w - win->w; + max_y = sess->screen_h - win->h; + if (max_x < 0) { + max_x = 0; + } + if (max_y < 0) { + max_y = 0; + } + + new_x = ush_uwm_clampi(target_x, 0, max_x); + new_y = ush_uwm_clampi(target_y, USH_UWM_TOP_CLAMP_Y, max_y); + + req.window_id = win->id; + req.x = (u64)(i64)new_x; + req.y = (u64)(i64)new_y; + if (cleonos_sys_wm_move(&req) == 0ULL) { + return 0; + } + + win->x = new_x; + win->y = new_y; + return 1; +} + +void ush_uwm_set_active(ush_uwm_session *sess, int index) { + ush_uwm_window *win; + + if (sess == (ush_uwm_session *)0 || ush_uwm_window_index_valid(index) == 0) { + return; + } + + win = &sess->windows[index]; + if (win->alive == 0 || win->id == 0ULL) { + return; + } + + if (cleonos_sys_wm_set_focus(win->id) != 0ULL) { + sess->active_window = index; + } +} + +void ush_uwm_focus_next(ush_uwm_session *sess) { + int start; + int i; + + if (sess == (ush_uwm_session *)0) { + 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) { + ush_uwm_set_active(sess, idx); + return; + } + } +} diff --git a/cleonos/c/apps/uwm_main.c b/cleonos/c/apps/uwm_main.c index ba86d11..15c5224 100644 --- a/cleonos/c/apps/uwm_main.c +++ b/cleonos/c/apps/uwm_main.c @@ -1,684 +1,4 @@ -#include "cmd_runtime.h" - -#define USH_UWM_WINDOW_COUNT 3ULL -#define USH_UWM_TITLE_HEIGHT 18 -#define USH_UWM_BORDER_THICKNESS 2 -#define USH_UWM_WINDOW_MIN_W 120 -#define USH_UWM_WINDOW_MIN_H 80 -#define USH_UWM_STARTUP_KEY_DRAIN_MAX 256ULL -#define USH_UWM_BLIT_MAX_FAIL_STREAK 30ULL - -#define USH_UWM_KEY_LEFT 1 -#define USH_UWM_KEY_RIGHT 2 -#define USH_UWM_KEY_UP 3 -#define USH_UWM_KEY_DOWN 4 - -#define USH_UWM_COLOR_BG 0x00161D2BUL -#define USH_UWM_COLOR_PANEL 0x00263352UL -#define USH_UWM_COLOR_CURSOR 0x00FFFFFFUL -#define USH_UWM_COLOR_CURSOR_SHADOW 0x00000000UL -#define USH_UWM_COLOR_BORDER_ACTIVE 0x00FFD166UL -#define USH_UWM_COLOR_BORDER_INACTIVE 0x004A566EL -#define USH_UWM_COLOR_TITLE_ACTIVE 0x005A77B7UL -#define USH_UWM_COLOR_TITLE_INACTIVE 0x00394C78UL - -typedef unsigned int ush_uwm_u32; - -typedef struct ush_uwm_window { - int x; - int y; - int w; - int h; - ush_uwm_u32 body_color; -} ush_uwm_window; - -typedef struct ush_uwm_runtime { - cleonos_fb_info fb; - ush_uwm_u32 *canvas; - u64 canvas_pixels; - int screen_w; - int screen_h; - int mouse_x; - int mouse_y; - u64 mouse_buttons; - int mouse_ready; -} ush_uwm_runtime; - -static int ush_uwm_window_index_valid(int index) { - return (index >= 0 && index < (int)USH_UWM_WINDOW_COUNT) ? 1 : 0; -} - -static void ush_uwm_z_order_sanitize(int *z_order) { - int seen[USH_UWM_WINDOW_COUNT]; - int fill_index = 0; - u64 i; - - if (z_order == (int *)0) { - return; - } - - for (i = 0ULL; i < USH_UWM_WINDOW_COUNT; i++) { - seen[i] = 0; - } - - for (i = 0ULL; i < USH_UWM_WINDOW_COUNT; i++) { - int value = z_order[i]; - - if (ush_uwm_window_index_valid(value) != 0 && seen[(u64)value] == 0) { - seen[(u64)value] = 1; - continue; - } - - while (fill_index < (int)USH_UWM_WINDOW_COUNT && seen[(u64)fill_index] != 0) { - fill_index++; - } - - if (fill_index >= (int)USH_UWM_WINDOW_COUNT) { - fill_index = (int)USH_UWM_WINDOW_COUNT - 1; - } - - z_order[i] = fill_index; - seen[(u64)fill_index] = 1; - } -} - -static int ush_uwm_runtime_sane(const ush_uwm_runtime *rt) { - u64 min_pixels; - - if (rt == (const ush_uwm_runtime *)0 || rt->canvas == (ush_uwm_u32 *)0) { - return 0; - } - - if (rt->screen_w <= 0 || rt->screen_h <= 0 || rt->screen_w > 4096 || rt->screen_h > 4096) { - return 0; - } - - min_pixels = (u64)(unsigned int)rt->screen_w * (u64)(unsigned int)rt->screen_h; - if (rt->canvas_pixels < min_pixels) { - return 0; - } - - return 1; -} - -static int ush_uwm_clampi(int value, int min_value, int max_value) { - if (value < min_value) { - return min_value; - } - - if (value > max_value) { - return max_value; - } - - return value; -} - -static int ush_uwm_u64_to_i32_sat(u64 value) { - if (value > 0x7FFFFFFFULL) { - return 0x7FFFFFFF; - } - - return (int)value; -} - -static void ush_uwm_fill_rect(ush_uwm_runtime *rt, int x, int y, int w, int h, ush_uwm_u32 color) { - int left; - int top; - int right; - int bottom; - int row; - - if (ush_uwm_runtime_sane(rt) == 0 || w <= 0 || h <= 0) { - return; - } - - left = x; - top = y; - right = x + w; - bottom = y + h; - - if (left < 0) { - left = 0; - } - if (top < 0) { - top = 0; - } - if (right > rt->screen_w) { - right = rt->screen_w; - } - if (bottom > rt->screen_h) { - bottom = rt->screen_h; - } - - if (left >= right || top >= bottom) { - return; - } - - for (row = top; row < bottom; row++) { - u64 offset = ((u64)(unsigned int)row * (u64)(unsigned int)rt->screen_w) + (u64)(unsigned int)left; - u64 max_pixels = rt->canvas_pixels; - int col; - - if (offset >= max_pixels) { - break; - } - - for (col = left; col < right; col++) { - if (offset >= max_pixels) { - break; - } - rt->canvas[offset++] = color; - } - } -} - -static void ush_uwm_draw_window(ush_uwm_runtime *rt, const ush_uwm_window *win, int active) { - ush_uwm_u32 border_color = (active != 0) ? USH_UWM_COLOR_BORDER_ACTIVE : USH_UWM_COLOR_BORDER_INACTIVE; - ush_uwm_u32 title_color = (active != 0) ? USH_UWM_COLOR_TITLE_ACTIVE : USH_UWM_COLOR_TITLE_INACTIVE; - int title_h; - - if (rt == (ush_uwm_runtime *)0 || win == (const ush_uwm_window *)0) { - return; - } - - title_h = USH_UWM_TITLE_HEIGHT; - if (title_h > win->h) { - title_h = win->h; - } - - ush_uwm_fill_rect(rt, win->x, win->y, win->w, win->h, border_color); - ush_uwm_fill_rect(rt, win->x + USH_UWM_BORDER_THICKNESS, win->y + USH_UWM_BORDER_THICKNESS, - win->w - (USH_UWM_BORDER_THICKNESS * 2), title_h - USH_UWM_BORDER_THICKNESS, title_color); - ush_uwm_fill_rect(rt, win->x + USH_UWM_BORDER_THICKNESS, win->y + title_h, win->w - (USH_UWM_BORDER_THICKNESS * 2), - win->h - title_h - USH_UWM_BORDER_THICKNESS, win->body_color); - - ush_uwm_fill_rect(rt, win->x + win->w - 16, win->y + 4, 10, 10, 0x00E76F51UL); -} - -static void ush_uwm_draw_cursor(ush_uwm_runtime *rt, int x, int y) { - int i; - - if (rt == (ush_uwm_runtime *)0) { - return; - } - - for (i = -6; i <= 6; i++) { - ush_uwm_fill_rect(rt, x + i, y, 1, 1, (i == 0) ? USH_UWM_COLOR_CURSOR : USH_UWM_COLOR_CURSOR_SHADOW); - ush_uwm_fill_rect(rt, x, y + i, 1, 1, (i == 0) ? USH_UWM_COLOR_CURSOR : USH_UWM_COLOR_CURSOR_SHADOW); - } - - ush_uwm_fill_rect(rt, x - 1, y - 1, 3, 3, USH_UWM_COLOR_CURSOR); -} - -static void ush_uwm_draw_background(ush_uwm_runtime *rt) { - int y; - - if (rt == (ush_uwm_runtime *)0) { - return; - } - - ush_uwm_fill_rect(rt, 0, 0, rt->screen_w, rt->screen_h, USH_UWM_COLOR_BG); - ush_uwm_fill_rect(rt, 0, 0, rt->screen_w, 30, USH_UWM_COLOR_PANEL); - - for (y = 60; y < rt->screen_h; y += 40) { - ush_uwm_fill_rect(rt, 0, y, rt->screen_w, 1, 0x001E283BUL); - } -} - -static int ush_uwm_window_hit_title(const ush_uwm_window *win, int x, int y) { - int title_h; - - if (win == (const ush_uwm_window *)0) { - return 0; - } - - title_h = USH_UWM_TITLE_HEIGHT; - if (title_h > win->h) { - title_h = win->h; - } - - if (x < win->x || y < win->y) { - return 0; - } - - if (x >= win->x + win->w || y >= win->y + title_h) { - return 0; - } - - return 1; -} - -static void ush_uwm_bring_to_front(int *z_order, u64 count, int window_index) { - u64 i; - u64 pos = count; - - if (z_order == (int *)0 || count == 0ULL || ush_uwm_window_index_valid(window_index) == 0) { - return; - } - - for (i = 0ULL; i < count; i++) { - if (z_order[i] == window_index) { - pos = i; - break; - } - } - - if (pos >= count || pos + 1ULL >= count) { - return; - } - - for (i = pos; i + 1ULL < count; i++) { - z_order[i] = z_order[i + 1ULL]; - } - z_order[count - 1ULL] = window_index; -} - -static int ush_uwm_cycle_focus(const int *z_order, u64 count, int active_window) { - u64 i; - u64 pos = 0ULL; - - if (z_order == (const int *)0 || count == 0ULL) { - return 0; - } - - if (ush_uwm_window_index_valid(active_window) == 0) { - return z_order[count - 1ULL]; - } - - for (i = 0ULL; i < count; i++) { - if (z_order[i] == active_window) { - pos = i; - break; - } - } - - return z_order[(pos + 1ULL) % count]; -} - -static int ush_uwm_poll_mouse(ush_uwm_runtime *rt) { - cleonos_mouse_state ms; - int max_x; - int max_y; - - if (rt == (ush_uwm_runtime *)0) { - return 0; - } - - ush_zero(&ms, (u64)sizeof(ms)); - if (cleonos_sys_mouse_state(&ms) == 0ULL) { - return 0; - } - - rt->mouse_buttons = ms.buttons; - rt->mouse_ready = (ms.ready != 0ULL) ? 1 : 0; - - if (rt->screen_w <= 0 || rt->screen_h <= 0) { - return rt->mouse_ready; - } - - max_x = rt->screen_w - 1; - max_y = rt->screen_h - 1; - - rt->mouse_x = ush_uwm_clampi(ush_uwm_u64_to_i32_sat(ms.x), 0, max_x); - rt->mouse_y = ush_uwm_clampi(ush_uwm_u64_to_i32_sat(ms.y), 0, max_y); - return rt->mouse_ready; -} - -static int ush_uwm_present(ush_uwm_runtime *rt) { - cleonos_fb_blit_req req; - - if (ush_uwm_runtime_sane(rt) == 0) { - return 0; - } - - req.pixels_ptr = (u64)(usize)rt->canvas; - req.src_width = (u64)(unsigned int)rt->screen_w; - req.src_height = (u64)(unsigned int)rt->screen_h; - req.src_pitch_bytes = (u64)(unsigned int)rt->screen_w * 4ULL; - req.dst_x = 0ULL; - req.dst_y = 0ULL; - req.scale = 1ULL; - return (cleonos_sys_fb_blit(&req) != 0ULL) ? 1 : 0; -} - -static u64 ush_uwm_drain_startup_keys(void) { - u64 drained = 0ULL; - - while (drained < USH_UWM_STARTUP_KEY_DRAIN_MAX) { - u64 key = cleonos_sys_kbd_get_char(); - if (key == (u64)-1) { - break; - } - drained++; - } - - return drained; -} - -/* return: 0 fail, 1 ok, 2 help */ -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') { - return 1; - } - - if (ush_split_first_and_rest(arg, first, (u64)sizeof(first), &rest) == 0) { - return 0; - } - - if (rest != (const char *)0 && rest[0] != '\0') { - return 0; - } - - if (ush_streq(first, "--help") != 0 || ush_streq(first, "-h") != 0) { - return 2; - } - - return 0; -} - -static void ush_uwm_usage(void) { - ush_writeln("usage: uwm"); - ush_writeln("keys: q quit, tab cycle focus, wasd move active window"); - ush_writeln("mouse: drag window by title bar"); -} - -static int ush_cmd_uwm(const char *arg) { - ush_uwm_runtime rt; - ush_uwm_window windows[USH_UWM_WINDOW_COUNT]; - int z_order[USH_UWM_WINDOW_COUNT]; - int active_window = 0; - int dragging = 0; - int drag_window = 0; - int drag_offset_x = 0; - int drag_offset_y = 0; - u64 prev_buttons = 0ULL; - u64 blit_fail_streak = 0ULL; - int running = 1; - int parse_result; - u64 drained_keys; - - parse_result = ush_uwm_parse_args(arg); - if (parse_result == 2) { - ush_uwm_usage(); - return 1; - } - - if (parse_result == 0) { - ush_uwm_usage(); - return 0; - } - - ush_zero(&rt, (u64)sizeof(rt)); - - if (cleonos_sys_fb_info(&rt.fb) == 0ULL || rt.fb.width == 0ULL || rt.fb.height == 0ULL || rt.fb.bpp != 32ULL) { - ush_writeln("uwm: framebuffer unavailable"); - return 0; - } - - if (rt.fb.width > 4096ULL || rt.fb.height > 4096ULL) { - ush_writeln("uwm: framebuffer too large"); - return 0; - } - - rt.screen_w = (int)rt.fb.width; - rt.screen_h = (int)rt.fb.height; - rt.canvas_pixels = rt.fb.width * rt.fb.height; - rt.canvas = (ush_uwm_u32 *)malloc((size_t)(rt.canvas_pixels * 4ULL)); - if (rt.canvas == (ush_uwm_u32 *)0) { - ush_writeln("uwm: framebuffer buffer alloc failed"); - return 0; - } - - windows[0].x = 50; - windows[0].y = 70; - windows[0].w = rt.screen_w / 3; - windows[0].h = rt.screen_h / 3; - windows[0].body_color = 0x002C3E6BUL; - - windows[1].x = 180; - windows[1].y = 140; - windows[1].w = rt.screen_w / 3; - windows[1].h = rt.screen_h / 3; - windows[1].body_color = 0x003F5B7EUL; - - windows[2].x = 310; - windows[2].y = 210; - windows[2].w = rt.screen_w / 3; - windows[2].h = rt.screen_h / 3; - windows[2].body_color = 0x00506F89UL; - - z_order[0] = 0; - z_order[1] = 1; - z_order[2] = 2; - active_window = 2; - - { - u64 i; - for (i = 0ULL; i < USH_UWM_WINDOW_COUNT; i++) { - int max_w = rt.screen_w - 20; - int max_h = rt.screen_h - 40; - if (max_w < USH_UWM_WINDOW_MIN_W) { - max_w = USH_UWM_WINDOW_MIN_W; - } - if (max_h < USH_UWM_WINDOW_MIN_H) { - max_h = USH_UWM_WINDOW_MIN_H; - } - - if (windows[i].w < USH_UWM_WINDOW_MIN_W) { - windows[i].w = USH_UWM_WINDOW_MIN_W; - } - if (windows[i].h < USH_UWM_WINDOW_MIN_H) { - windows[i].h = USH_UWM_WINDOW_MIN_H; - } - if (windows[i].w > max_w) { - windows[i].w = max_w; - } - if (windows[i].h > max_h) { - windows[i].h = max_h; - } - } - } - - rt.mouse_x = rt.screen_w / 2; - rt.mouse_y = rt.screen_h / 2; - rt.mouse_buttons = 0ULL; - rt.mouse_ready = 0; - - ush_writeln("uwm: enter user window manager"); - ush_writeln("uwm: q quit, tab focus, wasd move, drag title bar with mouse"); - - (void)cleonos_sys_fb_clear(USH_UWM_COLOR_BG); - drained_keys = ush_uwm_drain_startup_keys(); - if (drained_keys > 0ULL) { - ush_writeln("uwm: stale keyboard input discarded"); - } - - while (running != 0) { - u64 key; - int left_down; - int left_press; - int left_release; - - if (ush_uwm_runtime_sane(&rt) == 0) { - free(rt.canvas); - ush_writeln("uwm: runtime state corrupted"); - return 0; - } - - ush_uwm_z_order_sanitize(z_order); - if (ush_uwm_window_index_valid(active_window) == 0) { - active_window = z_order[USH_UWM_WINDOW_COUNT - 1ULL]; - } - if (ush_uwm_window_index_valid(drag_window) == 0) { - dragging = 0; - drag_window = active_window; - } - - (void)ush_uwm_poll_mouse(&rt); - - for (;;) { - key = cleonos_sys_kbd_get_char(); - if (key == (u64)-1) { - break; - } - - if (key == (u64)'q' || key == (u64)'Q') { - running = 0; - break; - } - - if (key == 27ULL) { - continue; - } - - if (key == (u64)'\t') { - active_window = ush_uwm_cycle_focus(z_order, USH_UWM_WINDOW_COUNT, active_window); - if (ush_uwm_window_index_valid(active_window) != 0) { - ush_uwm_bring_to_front(z_order, USH_UWM_WINDOW_COUNT, active_window); - } - continue; - } - - if (ush_uwm_window_index_valid(active_window) == 0) { - continue; - } - - if (key == (u64)'a' || key == (u64)'A') { - windows[active_window].x -= 8; - } else if (key == (u64)'d' || key == (u64)'D') { - windows[active_window].x += 8; - } else if (key == (u64)'w' || key == (u64)'W') { - windows[active_window].y -= 8; - } else if (key == (u64)'s' || key == (u64)'S') { - windows[active_window].y += 8; - } else if (key == USH_UWM_KEY_LEFT) { - rt.mouse_x -= 10; - } else if (key == USH_UWM_KEY_RIGHT) { - rt.mouse_x += 10; - } else if (key == USH_UWM_KEY_UP) { - rt.mouse_y -= 10; - } else if (key == USH_UWM_KEY_DOWN) { - rt.mouse_y += 10; - } - } - - if (running == 0) { - break; - } - - left_down = ((rt.mouse_buttons & 0x01ULL) != 0ULL) ? 1 : 0; - left_press = (left_down != 0 && (prev_buttons & 0x01ULL) == 0ULL) ? 1 : 0; - left_release = (left_down == 0 && (prev_buttons & 0x01ULL) != 0ULL) ? 1 : 0; - - if (left_press != 0) { - int hit = -1; - i64 z; - - for (z = (i64)USH_UWM_WINDOW_COUNT - 1LL; z >= 0LL; z--) { - int win_index = z_order[(u64)z]; - if (ush_uwm_window_index_valid(win_index) == 0) { - continue; - } - if (ush_uwm_window_hit_title(&windows[win_index], rt.mouse_x, rt.mouse_y) != 0) { - hit = win_index; - break; - } - } - - if (hit >= 0) { - active_window = hit; - ush_uwm_bring_to_front(z_order, USH_UWM_WINDOW_COUNT, hit); - dragging = 1; - drag_window = hit; - drag_offset_x = rt.mouse_x - windows[hit].x; - drag_offset_y = rt.mouse_y - windows[hit].y; - } - } - - if (left_release != 0) { - dragging = 0; - } - - if (dragging != 0 && left_down != 0 && ush_uwm_window_index_valid(drag_window) != 0) { - int new_x = rt.mouse_x - drag_offset_x; - int new_y = rt.mouse_y - drag_offset_y; - int max_x = rt.screen_w - windows[drag_window].w; - int max_y = rt.screen_h - windows[drag_window].h; - int min_y = (max_y >= 30) ? 30 : 0; - - if (max_x < 0) { - max_x = 0; - } - if (max_y < 0) { - max_y = 0; - } - - windows[drag_window].x = ush_uwm_clampi(new_x, 0, max_x); - windows[drag_window].y = ush_uwm_clampi(new_y, min_y, max_y); - } - - if (ush_uwm_window_index_valid(active_window) != 0) { - int max_x = rt.screen_w - windows[active_window].w; - int max_y = rt.screen_h - windows[active_window].h; - int min_y = (max_y >= 30) ? 30 : 0; - - if (max_x < 0) { - max_x = 0; - } - if (max_y < 0) { - max_y = 0; - } - - windows[active_window].x = ush_uwm_clampi(windows[active_window].x, 0, max_x); - windows[active_window].y = ush_uwm_clampi(windows[active_window].y, min_y, max_y); - } - rt.mouse_x = ush_uwm_clampi(rt.mouse_x, 0, rt.screen_w - 1); - rt.mouse_y = ush_uwm_clampi(rt.mouse_y, 0, rt.screen_h - 1); - - ush_uwm_draw_background(&rt); - { - u64 i; - for (i = 0ULL; i < USH_UWM_WINDOW_COUNT; i++) { - int win_index = z_order[i]; - if (ush_uwm_window_index_valid(win_index) == 0) { - continue; - } - int is_active = (win_index == active_window) ? 1 : 0; - ush_uwm_draw_window(&rt, &windows[win_index], is_active); - } - } - ush_uwm_draw_cursor(&rt, rt.mouse_x, rt.mouse_y); - - if (ush_uwm_present(&rt) == 0) { - blit_fail_streak++; - if (blit_fail_streak == 1ULL) { - ush_writeln("uwm: framebuffer blit failed, retrying"); - } - - if (blit_fail_streak >= USH_UWM_BLIT_MAX_FAIL_STREAK) { - free(rt.canvas); - ush_writeln("uwm: framebuffer blit failed too many times"); - return 0; - } - - (void)cleonos_sys_sleep_ticks(1ULL); - continue; - } - - blit_fail_streak = 0ULL; - - prev_buttons = rt.mouse_buttons; - (void)cleonos_sys_sleep_ticks(1ULL); - } - - (void)cleonos_sys_fb_clear(0x00000000ULL); - free(rt.canvas); - ush_writeln("uwm: exit"); - return 1; -} +#include "uwm/uwm.h" int cleonos_app_main(int argc, char **argv, char **envp) { ush_cmd_ctx ctx; diff --git a/cleonos/c/include/cleonos_syscall.h b/cleonos/c/include/cleonos_syscall.h index 1e3f28a..b15b421 100644 --- a/cleonos/c/include/cleonos_syscall.h +++ b/cleonos/c/include/cleonos_syscall.h @@ -50,13 +50,49 @@ typedef struct cleonos_fb_info { u64 bpp; } cleonos_fb_info; -typedef struct cleonos_mouse_state { - u64 x; - u64 y; - u64 buttons; - u64 packet_count; - u64 ready; -} cleonos_mouse_state; +typedef struct cleonos_mouse_state { + u64 x; + u64 y; + u64 buttons; + u64 packet_count; + u64 ready; +} 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 + +typedef struct cleonos_wm_event { + u64 type; + u64 arg0; + u64 arg1; + u64 arg2; + u64 arg3; +} cleonos_wm_event; + +typedef struct cleonos_wm_create_req { + u64 x; + u64 y; + u64 width; + u64 height; + u64 flags; +} cleonos_wm_create_req; + +typedef struct cleonos_wm_present_req { + u64 window_id; + u64 pixels_ptr; + u64 src_width; + u64 src_height; + 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_fb_blit_req { u64 pixels_ptr; @@ -207,10 +243,16 @@ typedef struct cleonos_net_tcp_recv_req { #define CLEONOS_SYSCALL_NET_GATEWAY 101ULL #define CLEONOS_SYSCALL_NET_DNS_SERVER 102ULL #define CLEONOS_SYSCALL_NET_TCP_CONNECT 103ULL -#define CLEONOS_SYSCALL_NET_TCP_SEND 104ULL -#define CLEONOS_SYSCALL_NET_TCP_RECV 105ULL -#define CLEONOS_SYSCALL_NET_TCP_CLOSE 106ULL -#define CLEONOS_SYSCALL_MOUSE_STATE 107ULL +#define CLEONOS_SYSCALL_NET_TCP_SEND 104ULL +#define CLEONOS_SYSCALL_NET_TCP_RECV 105ULL +#define CLEONOS_SYSCALL_NET_TCP_CLOSE 106ULL +#define CLEONOS_SYSCALL_MOUSE_STATE 107ULL +#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 u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); u64 cleonos_sys_log_write(const char *message, u64 length); @@ -318,8 +360,14 @@ u64 cleonos_sys_net_udp_send(const cleonos_net_udp_send_req *req); u64 cleonos_sys_net_udp_recv(cleonos_net_udp_recv_req *req); u64 cleonos_sys_net_tcp_connect(const cleonos_net_tcp_connect_req *req); u64 cleonos_sys_net_tcp_send(const cleonos_net_tcp_send_req *req); -u64 cleonos_sys_net_tcp_recv(cleonos_net_tcp_recv_req *req); -u64 cleonos_sys_net_tcp_close(u64 poll_budget); -u64 cleonos_sys_mouse_state(cleonos_mouse_state *out_state); - -#endif +u64 cleonos_sys_net_tcp_recv(cleonos_net_tcp_recv_req *req); +u64 cleonos_sys_net_tcp_close(u64 poll_budget); +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 diff --git a/cleonos/c/src/syscall.c b/cleonos/c/src/syscall.c index 36d48e9..eeae604 100644 --- a/cleonos/c/src/syscall.c +++ b/cleonos/c/src/syscall.c @@ -460,6 +460,30 @@ u64 cleonos_sys_net_tcp_close(u64 poll_budget) { return cleonos_syscall(CLEONOS_SYSCALL_NET_TCP_CLOSE, poll_budget, 0ULL, 0ULL); } -u64 cleonos_sys_mouse_state(cleonos_mouse_state *out_state) { - return cleonos_syscall(CLEONOS_SYSCALL_MOUSE_STATE, (u64)out_state, 0ULL, 0ULL); -} +u64 cleonos_sys_mouse_state(cleonos_mouse_state *out_state) { + return cleonos_syscall(CLEONOS_SYSCALL_MOUSE_STATE, (u64)out_state, 0ULL, 0ULL); +} + +u64 cleonos_sys_wm_create(const cleonos_wm_create_req *req) { + return cleonos_syscall(CLEONOS_SYSCALL_WM_CREATE, (u64)req, 0ULL, 0ULL); +} + +u64 cleonos_sys_wm_destroy(u64 window_id) { + return cleonos_syscall(CLEONOS_SYSCALL_WM_DESTROY, window_id, 0ULL, 0ULL); +} + +u64 cleonos_sys_wm_present(const cleonos_wm_present_req *req) { + return cleonos_syscall(CLEONOS_SYSCALL_WM_PRESENT, (u64)req, 0ULL, 0ULL); +} + +u64 cleonos_sys_wm_poll_event(u64 window_id, cleonos_wm_event *out_event) { + return cleonos_syscall(CLEONOS_SYSCALL_WM_POLL_EVENT, window_id, (u64)out_event, 0ULL); +} + +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); +} diff --git a/clks b/clks index 80c2e78..6a38167 160000 --- a/clks +++ b/clks @@ -1 +1 @@ -Subproject commit 80c2e781eb2c0b3633b399905becd8ddaea72ace +Subproject commit 6a381675685baec7ab896163d3f5b273a836c86d diff --git a/docs/syscall.md b/docs/syscall.md index 680eda6..3ae4dae 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~107) +## 4. Syscall 列表(0~113) ### 0 `CLEONOS_SYSCALL_LOG_WRITE` @@ -818,6 +818,58 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - 读取当前鼠标状态快照,坐标为 framebuffer 像素坐标系(左上角为原点)。 - `buttons` 位掩码:`bit0=left`、`bit1=right`、`bit2=middle`。 - `ready=1` 表示鼠标设备已上线;`ready=0` 时坐标/按键可能为默认值。 + +### 108 `CLEONOS_SYSCALL_WM_CREATE` + +- 参数: +- `arg0`: `const struct { u64 x; u64 y; u64 width; u64 height; u64 flags; } *req` +- 返回:成功返回非零 `window_id`,失败返回 `0` +- 说明: +- 创建一个由内核合成的窗口(当前在 TTY2 桌面合成器上显示)。 +- `x/y` 为有符号坐标(按 `i64` 解释),`width/height` 为像素尺寸。 + +### 109 `CLEONOS_SYSCALL_WM_DESTROY` + +- 参数: +- `arg0`: `u64 window_id` +- 返回:成功 `1`,失败 `0` +- 说明:销毁窗口并回收其事件队列与内核缓冲。 + +### 110 `CLEONOS_SYSCALL_WM_PRESENT` + +- 参数: +- `arg0`: `const struct { u64 window_id; u64 pixels_ptr; u64 src_width; u64 src_height; u64 src_pitch_bytes; } *req` +- 返回:成功 `1`,失败 `0` +- 说明: +- 将用户态 ARGB/RGB32 像素缓冲提交到指定窗口内容区。 +- 当前要求 `src_width/src_height` 与创建窗口时一致。 + +### 111 `CLEONOS_SYSCALL_WM_POLL_EVENT` + +- 参数: +- `arg0`: `u64 window_id` +- `arg1`: `struct cleonos_wm_event *out_event` +- 返回:有事件时返回 `1` 并写入事件;无事件或失败返回 `0` +- 事件类型: +- `1` = `FOCUS_GAINED` +- `2` = `FOCUS_LOST` +- `3` = `KEY`(`arg0` 为按键值) +- `4` = `MOUSE_MOVE`(`arg0/arg1` 为全局坐标,`arg2/arg3` 为窗口局部坐标) +- `5` = `MOUSE_BUTTON`(`arg0` 为按钮状态位掩码,`arg1` 为变化掩码) + +### 112 `CLEONOS_SYSCALL_WM_MOVE` + +- 参数: +- `arg0`: `const struct { u64 window_id; u64 x; u64 y; } *req` +- 返回:成功 `1`,失败 `0` +- 说明:移动窗口到目标坐标(坐标按 `i64` 解释,内核会进行边界裁剪)。 + +### 113 `CLEONOS_SYSCALL_WM_SET_FOCUS` + +- 参数: +- `arg0`: `u64 window_id` +- 返回:成功 `1`,失败 `0` +- 说明:将目标窗口置为焦点并提升到顶层 z-order。 ## 5. 用户态封装函数 @@ -855,6 +907,8 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `cleonos_sys_net_udp_send()` / `cleonos_sys_net_udp_recv()` - `cleonos_sys_net_tcp_connect()` / `cleonos_sys_net_tcp_send()` / `cleonos_sys_net_tcp_recv()` / `cleonos_sys_net_tcp_close()` - `cleonos_sys_mouse_state()` +- `cleonos_sys_wm_create()` / `cleonos_sys_wm_destroy()` / `cleonos_sys_wm_present()` +- `cleonos_sys_wm_poll_event()` / `cleonos_sys_wm_move()` / `cleonos_sys_wm_set_focus()` ## 6. 开发注意事项 @@ -865,7 +919,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); ## 7. Wine 兼容说明 -- `wine/cleonos_wine_lib/runner.py` 当前已覆盖到 `0..107`(含 `DL_*`、`FB_*`、`KERNEL_VERSION`、`DISK_*`、`NET_*`、`MOUSE_STATE`)。 +- `wine/cleonos_wine_lib/runner.py` 当前已覆盖到 `0..113`(含 `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 可调用地址。 @@ -884,6 +938,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`);不会创建真实窗口服务。 - 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 1e7b5aa..58fa3d6 160000 --- a/kit +++ b/kit @@ -1 +1 @@ -Subproject commit 1e7b5aa8d2c23ed1a2fee53fa74ce71e1b93825e +Subproject commit 58fa3d6d1a6e7713e0f894d259e2ae686f5fea4a