From 8ac2d2c0d49c677fda4013648cc11924c41bdf9c Mon Sep 17 00:00:00 2001 From: Ry Date: Tue, 27 Dec 2022 18:01:55 -0800 Subject: [PATCH] kernel: Add a very basic GUI widget system --- applications/terminal/main.asm | 3 +- fox32os.def | 10 ++ kernel/main.asm | 6 + kernel/widget/button.asm | 95 ++++++++++++++++ kernel/widget/widget.asm | 194 +++++++++++++++++++++++++++++++++ kernel/window/event.asm | 3 + kernel/window/window.asm | 7 +- 7 files changed, 316 insertions(+), 2 deletions(-) create mode 100644 kernel/widget/button.asm create mode 100644 kernel/widget/widget.asm diff --git a/applications/terminal/main.asm b/applications/terminal/main.asm index f7907b2..f544075 100644 --- a/applications/terminal/main.asm +++ b/applications/terminal/main.asm @@ -7,6 +7,7 @@ mov r4, 32 mov r5, 32 mov r6, 0 + mov r7, 0 call new_window call get_unused_task_id @@ -85,7 +86,7 @@ close_window: jmp event_loop_end window_title: data.str "Terminal" data.8 0 -window_struct: data.fill 0, 32 +window_struct: data.fill 0, 36 shell_task_id: data.8 0 diff --git a/fox32os.def b/fox32os.def index 24c3684..44d3366 100644 --- a/fox32os.def +++ b/fox32os.def @@ -38,3 +38,13 @@ write: jmp [0x00000D20] ; shell jump table new_shell_task: jmp [0x00000E10] + +; widget jump table +draw_widgets_to_window: jmp [0x00000F10] +handle_widget_click: jmp [0x00000F14] + +; event types +const EVENT_TYPE_BUTTON_CLICK: 0x80000000 + +; widget types +const WIDGET_TYPE_BUTTON: 0x00000000 diff --git a/kernel/main.asm b/kernel/main.asm index 3470fcc..9d60fe1 100644 --- a/kernel/main.asm +++ b/kernel/main.asm @@ -56,6 +56,11 @@ jump_table: ; shell jump table org.pad 0x00000610 data.32 new_shell_task + + ; widget jump table + org.pad 0x00000710 + data.32 draw_widgets_to_window + data.32 handle_widget_click jump_table_end: ; initialization code @@ -265,6 +270,7 @@ get_os_version: #include "fxf/fxf.asm" #include "shell/shell.asm" #include "task.asm" + #include "widget/widget.asm" #include "window/window.asm" #include "vfs.asm" diff --git a/kernel/widget/button.asm b/kernel/widget/button.asm new file mode 100644 index 0000000..ffdf900 --- /dev/null +++ b/kernel/widget/button.asm @@ -0,0 +1,95 @@ +; button widget routines + +; button widget struct: +; data.32 next_ptr - pointer to next widget, or 0 for none +; data.32 id - the ID number of this widget +; data.32 type - the type of this widget +; data.32 text_ptr - pointer to null-terminated text string +; data.32 foreground_color - text foreground color +; data.32 background_color - button background color +; data.16 width - width of this button +; data.16 reserved +; data.16 x_pos - X coordinate of this widget +; data.16 y_pos - Y coordinate of this widget + +const BUTTON_WIDGET_STRUCT_SIZE: 32 ; 8 words = 32 bytes + +; draw a button widget to a window +; FIXME: this needs some major cleanup, this is like register soup +; inputs: +; r0: pointer to window struct +; r1: pointer to a null-terminated string +; r2: foreground color +; r3: background color +; r4: button width +; r5: X coordinate +; r6: Y coordinate +draw_button_widget: + push r0 + push r1 + push r2 + push r3 + push r4 + push r5 + push r10 + push r20 + push r30 + push r31 + + push r1 + mov r20, 0 +draw_button_widget_strlen_loop: + inc r1 + inc r20 + cmp.8 [r1], 0 + ifnz jmp draw_button_widget_strlen_loop + pop r1 + + mov r30, r5 + mov r31, r4 + div r4, 2 + mul r20, 8 + div r20, 2 + sub r4, r20 + add r5, r4 + + call get_window_overlay_number + mov r10, r0 + + push r1 + push r2 + push r3 + push r4 + push r5 + mov r5, r0 + mov r2, r31 + mov r4, r3 + mov r3, 16 + mov r0, r30 + mov r1, r6 + call draw_filled_rectangle_to_overlay + pop r5 + pop r4 + pop r3 + pop r2 + pop r1 + + mov r0, r1 + mov r4, r3 + mov r3, r2 + mov r1, r5 + mov r2, r6 + mov r5, r10 + call draw_str_to_overlay + + pop r31 + pop r30 + pop r20 + pop r10 + pop r5 + pop r4 + pop r3 + pop r2 + pop r1 + pop r0 + ret diff --git a/kernel/widget/widget.asm b/kernel/widget/widget.asm new file mode 100644 index 0000000..5b2418c --- /dev/null +++ b/kernel/widget/widget.asm @@ -0,0 +1,194 @@ +; widget management routines + +; widget types +const WIDGET_TYPE_BUTTON: 0x00000000 + +; widget struct: +; data.32 next_ptr - pointer to next widget, or 0 for none +; data.32 id - the ID number of this widget +; data.32 type - the type of this widget +; remaining entries vary depending on widget type + +; draw all of a window's widgets to a window +; inputs: +; r0: pointer to window struct +; outputs: +; none +draw_widgets_to_window: + push r10 + + ; get pointer to first widget + mov r10, r0 + add r10, 32 + mov r10, [r10] +draw_widgets_to_window_next: + ; check widget type + add r10, 8 + cmp [r10], WIDGET_TYPE_BUTTON + ifz call draw_widgets_to_window_button + + ; point to the next widget, if any + sub r10, 8 + mov r10, [r10] + cmp r10, 0 + ifz jmp draw_widgets_to_window_done + jmp draw_widgets_to_window_next +draw_widgets_to_window_done: + pop r10 + ret +draw_widgets_to_window_button: + push r1 + push r2 + push r3 + push r4 + push r5 + push r6 + push r10 + + ; put button parameters in registers for the drawing routine + add r10, 4 + mov r1, [r10] ; text_ptr + add r10, 4 + mov r2, [r10] ; foreground_color + add r10, 4 + mov r3, [r10] ; background_color + add r10, 4 + movz.16 r4, [r10] ; width + add r10, 4 + movz.16 r5, [r10] ; x_pos + add r10, 2 + movz.16 r6, [r10] ; y_pos + call draw_button_widget + + pop r10 + pop r6 + pop r5 + pop r4 + pop r3 + pop r2 + pop r1 + ret + +; check if a widget was clicked and if so, add an event to the window's event queue +; inputs: +; r0: pointer to window struct +; r1: X coordinate of click +; r2: Y coordinate of click +; outputs: +; none +handle_widget_click: + push r0 + push r1 + push r2 + push r3 + push r4 + push r5 + push r6 + push r7 + push r8 + push r30 + + mov r30, r0 + + ; get pointer to first widget + add r0, 32 + mov r0, [r0] +handle_widget_click_check_type: + ; check widget type + add r0, 8 + cmp [r0], WIDGET_TYPE_BUTTON + ifz jmp handle_widget_click_button +handle_widget_click_done: + pop r30 + pop r8 + pop r7 + pop r6 + pop r5 + pop r4 + pop r3 + pop r2 + pop r1 + pop r0 + ret +handle_widget_click_button: + push r0 + push r10 + push r11 + push r12 + push r21 + push r22 + + ; get button width + add r0, 16 + movz.16 r10, [r0] + + ; get button X coordinate + add r0, 4 + movz.16 r11, [r0] + + ; get button Y coordinate + add r0, 2 + movz.16 r12, [r0] + + ; calculate button's right side coordinate + mov r21, r11 + add r21, r10 + + ; calculate button's bottom right corner coordinate + mov r22, r12 + add r22, 16 + + ; check if r1 is between r11 and r21 + ; and if r2 is between r12 and r22 + cmp r1, r11 + iflt jmp handle_widget_click_button_no_click + cmp r1, r21 + ifgt jmp handle_widget_click_button_no_click + cmp r2, r12 + iflt jmp handle_widget_click_button_no_click + cmp r2, r22 + ifgt jmp handle_widget_click_button_no_click + + ; if we reach this point then the button was clicked!! + pop r22 + pop r21 + pop r12 + pop r11 + pop r10 + pop r0 + + ; add a button click event to the window + sub r0, 4 + mov r1, [r0] ; parameter 0: widget ID + mov r2, 0 + mov r3, 0 + mov r4, 0 + mov r5, 0 + mov r6, 0 + mov r7, 0 + mov r8, r30 + mov r0, EVENT_TYPE_BUTTON_CLICK + call new_window_event + + jmp handle_widget_click_done +handle_widget_click_button_no_click: + pop r22 + pop r21 + pop r12 + pop r11 + pop r10 + pop r0 + + ; get pointer to next widget + sub r0, 8 + mov r0, [r0] + + ; if this is the last widget, then exit + cmp r0, 0 + ifz jmp handle_widget_click_done + + ; retry + jmp handle_widget_click_check_type + + ; include widget types + #include "widget/button.asm" diff --git a/kernel/window/event.asm b/kernel/window/event.asm index 0f766a0..a0f4b43 100644 --- a/kernel/window/event.asm +++ b/kernel/window/event.asm @@ -1,5 +1,8 @@ ; window event routines +; event types +const EVENT_TYPE_BUTTON_CLICK: 0x80000000 + const WINDOW_EVENT_SIZE: 32 ; get the next window event and remove it from the event queue diff --git a/kernel/window/window.asm b/kernel/window/window.asm index e26c8e3..1755279 100644 --- a/kernel/window/window.asm +++ b/kernel/window/window.asm @@ -13,8 +13,9 @@ ; data.8 reserved_1 ; data.16 reserved_2 ; data.32 menu_bar_ptr - pointer to this window's menu bar root struct, or 0 for none +; data.32 first_widget_ptr - pointer to this window's first widget -const WINDOW_STRUCT_SIZE: 32 ; 8 words = 32 bytes +const WINDOW_STRUCT_SIZE: 36 ; 9 words = 36 bytes const TITLE_BAR_HEIGHT: 16 ; create a new window and allocate memory as required @@ -26,6 +27,7 @@ const TITLE_BAR_HEIGHT: 16 ; r4: initial X coordinate (top left corner of title bar) ; r5: initial Y coordinate (top left corner of title bar) ; r6: pointer to menu bar root struct, or 0x00000000 for no menu bar +; r7: pointer to first widget, or 0x00000000 for no widgets ; outputs: ; none new_window: @@ -57,6 +59,9 @@ new_window: ; menu bar root struct pointer add r10, 6 mov [r10], r6 + ; first widget pointer + add r10, 4 + mov [r10], r7 ; then, allocate memory for the framebuffer ; the space required is width * (height + TITLE_BAR_HEIGHT) * 4