From 89ee1342fec023bf2fb4979edd0ebaba7446fa79 Mon Sep 17 00:00:00 2001 From: Ry Date: Wed, 5 Oct 2022 16:15:36 -0700 Subject: [PATCH] Start working on a very basic window manager --- fox32os.def | 31 +- kernel/main.asm | 22 ++ kernel/window/event.asm | 136 +++++++++ kernel/window/event_manager_task.asm | 54 ++++ kernel/window/window.asm | 437 +++++++++++++++++++++++++++ launcher/about.asm | 135 ++++++--- 6 files changed, 760 insertions(+), 55 deletions(-) create mode 100644 kernel/window/event.asm create mode 100644 kernel/window/event_manager_task.asm create mode 100644 kernel/window/window.asm diff --git a/fox32os.def b/fox32os.def index 416bed3..6417aa3 100644 --- a/fox32os.def +++ b/fox32os.def @@ -1,19 +1,30 @@ ; fox32os routine definitions ; system jump table -get_os_version: jmp [0x00000810] +get_os_version: jmp [0x00000810] ; FXF jump table -parse_fxf_binary: jmp [0x00000910] +parse_fxf_binary: jmp [0x00000910] ; task jump table -new_task: jmp [0x00000A10] -yield_task: jmp [0x00000A14] -end_current_task: jmp [0x00000A18] -get_current_task_id: jmp [0x00000A1C] -get_unused_task_id: jmp [0x00000A20] -is_task_id_used: jmp [0x00000A24] +new_task: jmp [0x00000A10] +yield_task: jmp [0x00000A14] +end_current_task: jmp [0x00000A18] +get_current_task_id: jmp [0x00000A1C] +get_unused_task_id: jmp [0x00000A20] +is_task_id_used: jmp [0x00000A24] ; memory jump table -allocate_memory: jmp [0x00000B10] -free_memory: jmp [0x00000B14] +allocate_memory: jmp [0x00000B10] +free_memory: jmp [0x00000B14] + +; window jump table +new_window: jmp [0x00000C10] +destroy_window: jmp [0x00000C14] +new_window_event: jmp [0x00000C18] +get_next_window_event: jmp [0x00000C1C] +draw_title_bar_to_window: jmp [0x00000C20] +move_window: jmp [0x00000C24] +fill_window: jmp [0x00000C28] +get_window_overlay_number: jmp [0x00000C2C] +start_dragging_window: jmp [0x00000C30] diff --git a/kernel/main.asm b/kernel/main.asm index b96414c..45eae38 100644 --- a/kernel/main.asm +++ b/kernel/main.asm @@ -35,6 +35,18 @@ jump_table: data.32 allocate_memory data.32 free_memory + ; window jump table + org.pad 0x00000C10 + data.32 new_window + data.32 destroy_window + data.32 new_window_event + data.32 get_next_window_event + data.32 draw_title_bar_to_window + data.32 move_window + data.32 fill_window + data.32 get_window_overlay_number + data.32 start_dragging_window + ; initialization code entry: mov rsp, SYSTEM_STACK @@ -57,6 +69,12 @@ entry: mov r12, FOX32OS_VERSION_PATCH call draw_format_str_to_background + ; check if a disk is inserted as disk 1 + ; if so, skip checking startup.cfg and just run disk 1 + in r31, 0x80001001 + cmp r31, 0 + ifnz jmp boot_disk_1 + ; open startup.cfg mov r0, startup_cfg mov r1, 0 @@ -135,6 +153,9 @@ load_startup_task: jmp load_startup_task no_other_tasks: + ; start the event manager task + call start_event_manager_task + ; jump back to it without adding this "task" (not really a task) into the queue. ; end_current_task_no_mark_no_free is used specifically because it doesn't mark ; the current task (still set to 0) as unused, and it doesn't free the memory @@ -225,6 +246,7 @@ get_os_version: #include "allocator.asm" #include "fxf/fxf.asm" #include "task.asm" + #include "window/window.asm" startup_str: data.str "fox32 - OS version %u.%u.%u" data.8 0 startup_error_str: data.str "fox32 - OS version %u.%u.%u - startup.cfg is invalid!" data.8 0 diff --git a/kernel/window/event.asm b/kernel/window/event.asm new file mode 100644 index 0000000..0f766a0 --- /dev/null +++ b/kernel/window/event.asm @@ -0,0 +1,136 @@ +; window event routines + +const WINDOW_EVENT_SIZE: 32 + +; get the next window event and remove it from the event queue +; inputs: +; r0: pointer to window struct +; outputs: +; r0: event type +; r1-r7: event parameters +get_next_window_event: + push r10 + push r11 + + ; r10: event_queue_ptr + mov r10, r0 + add r10, 4 + + ; r11: event_queue_bottom + mov r11, r0 + add r11, 8 + + cmp [r10], [r11] + ifz pop r11 + ifz pop r10 + ifz jmp window_event_empty + +get_next_window_event_0: + push r8 + push r9 + + mov r8, [r11] + call window_event_load + mov r8, window_event_temp + call window_event_store + + mov r9, [r11] + +get_next_window_event_1: + add r9, WINDOW_EVENT_SIZE + + cmp [r10], r9 + ifz jmp get_next_window_event_2 + + mov r8, r9 + call window_event_load + + mov r8, r9 + sub r8, WINDOW_EVENT_SIZE + call window_event_store + + jmp get_next_window_event_1 + +get_next_window_event_2: + mov r8, window_event_temp + call window_event_load + + sub [r10], WINDOW_EVENT_SIZE + + pop r9 + pop r8 + pop r11 + pop r10 + ret + +; add an event to a window's event queue +; inputs: +; r0: event type +; r1-r7: event parameters +; r8: pointer to window struct +; outputs: +; none +new_window_event: + push r8 + push r9 + + mov r9, r8 + add r9, 4 ; point to event_queue_ptr in the window struct + mov r8, [r9] + call window_event_store + mov [r9], r8 + + pop r9 + pop r8 + ret + +window_event_load: + mov r0, [r8] + add r8, 4 + mov r1, [r8] + add r8, 4 + mov r2, [r8] + add r8, 4 + mov r3, [r8] + add r8, 4 + mov r4, [r8] + add r8, 4 + mov r5, [r8] + add r8, 4 + mov r6, [r8] + add r8, 4 + mov r7, [r8] + add r8, 4 + ret + +window_event_store: + mov [r8], r0 + add r8, 4 + mov [r8], r1 + add r8, 4 + mov [r8], r2 + add r8, 4 + mov [r8], r3 + add r8, 4 + mov [r8], r4 + add r8, 4 + mov [r8], r5 + add r8, 4 + mov [r8], r6 + add r8, 4 + mov [r8], r7 + add r8, 4 + ret + +window_event_empty: + mov r0, EVENT_TYPE_EMPTY + mov r1, 0 + mov r2, 0 + mov r3, 0 + mov r4, 0 + mov r5, 0 + mov r6, 0 + mov r7, 0 + ret + +window_event_temp: data.fill 0, 32 ; 8 entries * 4 bytes per word diff --git a/kernel/window/event_manager_task.asm b/kernel/window/event_manager_task.asm new file mode 100644 index 0000000..4eb8c53 --- /dev/null +++ b/kernel/window/event_manager_task.asm @@ -0,0 +1,54 @@ +; task that manages window events + +; start a task which handles passing system events into the correct window event queue +; inputs: +; none +; outputs: +; none +start_event_manager_task: + ; allocate 256 bytes for the stack + mov r0, 256 + call allocate_memory + add r0, 256 ; add 256 so the stach pointer is at the end of the stack block (stack grows down) + mov r10, r0 + + ; then start the task + call get_unused_task_id + mov r1, event_manager_task_loop ; initial instruction pointer + mov r2, r10 ; initial stack pointer + mov r3, 0 ; pointer to task code block to free when task ends + ; (zero since we don't want to free any code blocks when the task ends) + mov r4, r10 ; pointer to task stack block to free when task ends + sub r4, 256 ; point to the start of the stack block that we allocated above + call new_task + + ret + +event_manager_task_loop: + call get_next_event + + ; HACK: put menu bar events back into the system event queue + cmp r0, EVENT_TYPE_MENU_BAR_CLICK + ifz call new_event + cmp r0, EVENT_TYPE_MENU_CLICK + ifz call new_event + cmp r0, EVENT_TYPE_MENU_UPDATE + ifz call new_event + + cmp [active_window], 0 + ifz rjmp event_manager_task_loop_end + + ; mouse + cmp r0, EVENT_TYPE_MOUSE_CLICK + ifz call add_mouse_event_to_active_window + cmp r0, EVENT_TYPE_MOUSE_RELEASE + ifz call add_mouse_event_to_active_window + + ; keyboard + cmp r0, EVENT_TYPE_KEY_DOWN + ifz call add_event_to_active_window + cmp r0, EVENT_TYPE_KEY_UP + ifz call add_event_to_active_window +event_manager_task_loop_end: + call yield_task + rjmp event_manager_task_loop diff --git a/kernel/window/window.asm b/kernel/window/window.asm new file mode 100644 index 0000000..d564700 --- /dev/null +++ b/kernel/window/window.asm @@ -0,0 +1,437 @@ +; window management routines + +; window struct: +; data.32 framebuffer_ptr - pointer to this window's framebuffer +; data.32 event_queue_ptr - current event queue pointer +; data.32 event_queue_bottom - pointer to beginning of this window's event queue +; data.32 title_ptr - pointer to null-terminated title string +; data.16 width - width of this window +; data.16 height - height of this window, not including the title bar +; data.16 x_pos - X coordinate of this window (top left corner of title bar) +; data.16 y_pos - Y coordinate of this window (top left corner of title bar) +; data.8 overlay - overlay number of this window +; data.8 reserved_1 +; data.16 reserved_2 +; data.32 reserved_3 + +const WINDOW_STRUCT_SIZE: 32 ; 8 words = 32 bytes +const TITLE_BAR_HEIGHT: 16 + +; create a new window and allocate memory as required +; inputs: +; r0: pointer to empty 32 byte window struct +; r1: pointer to null-terminated title string +; r2: window width +; r3: window height, not including the title bar +; r4: initial X coordinate (top left corner of title bar) +; r5: initial Y coordinate (top left corner of title bar) +; outputs: +; none +new_window: + push r1 + push r2 + push r3 + push r4 + push r5 + push r10 + push r11 + push r12 + + ; first, set up the initial struct values + ; title string + mov r10, r0 + add r10, 12 + mov [r10], r1 + ; window size + add r10, 4 + mov.16 [r10], r2 + add r10, 2 + mov.16 [r10], r3 + ; window position + add r10, 2 + mov.16 [r10], r4 + add r10, 2 + mov.16 [r10], r5 + + ; then, allocate memory for the framebuffer + ; the space required is width * (height + TITLE_BAR_HEIGHT) * 4 + mov r10, r2 + mov r11, r3 + add r11, TITLE_BAR_HEIGHT + mul r10, r11 + mul r10, 4 + push r0 + mov r0, r10 + call allocate_memory + cmp r0, 0 + ifz jmp memory_error + mov r10, r0 + pop r0 + mov [r0], r10 + mov r12, r10 + + ; then, allocate memory for the event queue + ; 32 events * 8 entries per event * 4 bytes per word = 1024 bytes + push r0 + mov r0, 1024 + call allocate_memory + cmp r0, 0 + ifz jmp memory_error + mov r11, r0 + pop r0 + mov r10, r0 + add r10, 8 + mov [r10], r11 + sub r10, 4 + mov [r10], r11 + + ; then, find an overlay to use for this window + push r0 + call get_unused_overlay + mov r11, r0 + pop r0 + mov r10, r0 + add r10, 24 + mov.8 [r10], r11 + + ; finally, set the properties of the overlay + push r0 + push r1 + push r2 + mov r0, r4 + mov r1, r5 + mov r2, r11 + call move_overlay + pop r2 + pop r1 + mov r0, r2 + mov r1, r3 + add r1, TITLE_BAR_HEIGHT + mov r2, r11 + call resize_overlay + mov r0, r12 + mov r1, r11 + call set_overlay_framebuffer_pointer + mov r0, r11 + call enable_overlay + mov r0, 0xFF000000 + mov r1, r11 + call fill_overlay + pop r0 + + mov [active_window], r0 + call draw_title_bar_to_window + + pop r12 + pop r11 + pop r10 + pop r5 + pop r4 + pop r3 + pop r2 + pop r1 + ret + +; destroy a window and free memory used by it +; note that this does not free the memory used by the window struct itself +; inputs: +; r0: pointer to window struct +; outputs: +; none +destroy_window: + push r0 + push r1 + + mov r1, r0 + + ; free framebuffer memory + mov r0, [r1] + call free_memory + + ; free event queue memory + add r1, 8 + mov r0, [r1] + call free_memory + + add r1, 16 + movz.8 r0, [r1] + call disable_overlay + + pop r1 + pop r0 + ret + +; call this if the user clicks on a window's title bar +; inputs: +; r0: pointer to window struct +; outputs: +; none +start_dragging_window: + push r0 + push r1 + push r2 + push r4 + + mov r2, r0 + mov r4, r0 + add r4, 16 + movz.16 r4, [r4] + div r4, 2 +start_dragging_window_loop: + call get_mouse_position + sub r0, r4 + sub r1, 8 + call move_window + + call get_mouse_button + bts r0, 2 + ifnz jmp start_dragging_window_loop + + pop r4 + pop r2 + pop r1 + pop r0 + ret + +; move a window +; r0: X position +; r1: Y position +; r2: pointer to window struct +move_window: + push r2 + + add r2, 20 + mov.16 [r2], r0 + add r2, 2 + mov.16 [r2], r1 + add r2, 2 + movz.8 r2, [r2] + call move_overlay + + pop r2 + ret + +; fill a whole window with a color +; inputs: +; r0: color +; r1: pointer to window struct +; outputs: +; none +fill_window: + push r1 + push r2 + + mov r2, r1 + + add r1, 24 + movz.8 r1, [r1] + call fill_overlay + + mov r0, r2 + call draw_title_bar_to_window + + pop r2 + pop r1 + ret + +; get the overlay used by a window +; DO NOT CACHE THIS VALUE, it can change any time the window order changes +; inputs: +; r0: pointer to window struct +; outputs: +; r0: overlay number +get_window_overlay_number: + add r0, 24 + movz.8 r0, [r0] + + ret + +; draw a window's title bar +; inputs: +; r0: pointer to window struct +; outputs: +; none +draw_title_bar_to_window: + push r0 + push r3 + push r4 + push r10 + push r11 + push r12 + push r31 + + ; get the title string + add r0, 12 + mov r12, [r0] + + ; get the width of this window + add r0, 4 + movz.16 r11, [r0] + + ; get overlay number of this window + add r0, 8 + movz.8 r10, [r0] + + ; save the old tilemap + call get_tilemap + push r0 + push r1 + push r2 + + ; set the tilemap to our 1x16 tile patterns + mov r0, window_title_bar_patterns + mov r1, 1 + mov r2, 16 + call set_tilemap + + mov r1, 0 + mov r2, 0 + mov r3, r10 + mov r31, r11 +draw_title_bar_to_window_loop: + mov r4, r31 + rem r4, 2 + cmp r4, 0 + ifz mov r0, 0 + ifnz mov r0, 1 + call draw_tile_to_overlay + inc r1 + loop draw_title_bar_to_window_loop + + ; restore the old tilemap + pop r2 + pop r1 + pop r0 + call set_tilemap + + ; draw the title text + mov r0, r12 + mov r1, 8 + mov r2, 0 + mov r3, 0xFF000000 + mov r4, 0xFFFFFFFF + mov r5, r10 + call draw_str_to_overlay + + ; draw the close button + mov r0, 1 + mov r1, 4 + mov r2, 6 + mov r3, 8 + mov r4, 0xFFFFFFFF + mov r5, r10 + call draw_filled_rectangle_to_overlay + mov r0, 2 + mov r1, 5 + mov r2, 4 + mov r3, 6 + mov r4, 0xFF000000 + mov r5, r10 + call draw_filled_rectangle_to_overlay + + pop r31 + pop r12 + pop r11 + pop r10 + pop r4 + pop r3 + pop r0 + ret + +; add an event to the active window +; inputs: +; r0-r7: event +; outputs: +; none +add_event_to_active_window: + mov r8, [active_window] + call new_window_event + + ret + +; add a mouse event to the active window if the mouse was clicked inside the active window +; if so, automatically convert the X and Y coords to be relative to the window +; inputs: +; r0-r7: event +; outputs: +; none +add_mouse_event_to_active_window: + push r0 + push r2 + push r10 + push r11 + push r12 + + ; save X and Y coords of the click and the event type + mov r10, r1 + mov r11, r2 + mov r12, r0 + + ; get the window's overlay number + mov r0, [active_window] + call get_window_overlay_number + + ; check if the window's overlay covers the clicked position + mov r2, r0 + mov r0, r10 + mov r1, r11 + call check_if_overlay_covers_position + ; if it doesn't, then end here + ifnz jmp add_mouse_event_to_active_window_end + ; if it does, then make the X and Y coords relative to the overlay + call make_coordinates_relative_to_overlay + + ; add the event + mov r2, r1 + mov r1, r0 + mov r0, r12 + call add_event_to_active_window +add_mouse_event_to_active_window_end: + pop r12 + pop r11 + pop r10 + pop r2 + pop r0 + ret + +window_title_bar_patterns: + ; 1x16 tile + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + + ; 1x16 tile + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + data.32 0xFFFFFFFF + data.32 0xFF000000 + +active_window: data.32 0 + + #include "window/event.asm" + #include "window/event_manager_task.asm" diff --git a/launcher/about.asm b/launcher/about.asm index f487574..de8f21f 100644 --- a/launcher/about.asm +++ b/launcher/about.asm @@ -1,63 +1,108 @@ ; about dialog +const BACKGROUND_COLOR: 0xFF674764 + ; show the about dialog ; inputs: ; none ; outputs: ; none about_dialog: - ; return if the dialog overlay is already enabled - in r0, 0x80000300 - cmp r0, 0 - ifnz ret + call disable_menu_bar - ; set overlay position - mov r0, 64 - mov r1, 64 - mov r2, 0 - call move_overlay + ; create the window + mov r0, about_dialog_window_struct + mov r1, about_dialog_window_title + mov r2, 288 + mov r3, 128 + mov r4, 64 + mov r5, 64 + call new_window - ; set overlay size - mov r0, 256 - mov r1, 128 - mov r2, 0 - call resize_overlay + ; fill the window with the fox32 purple color + mov r0, BACKGROUND_COLOR + mov r1, about_dialog_window_struct + call fill_window - ; allocate memory for the overlay framebuffer - mov r0, 131072 ; 256x128x4 - call allocate_memory - cmp r0, 0 - ifz jmp allocate_error - mov [about_dialog_framebuffer_ptr], r0 - mov r1, 0 - call set_overlay_framebuffer_pointer + mov r0, about_dialog_window_struct + call get_window_overlay_number - ; fill the overlay with all black - mov r0, 0xFF000000 - mov r1, 0 - call fill_overlay - - ; enable it!! - mov r0, 0 - call enable_overlay + ; draw strings + mov r5, r0 + mov r0, about_dialog_window_launcher_string + mov r1, 4 + mov r2, 20 + mov r3, 0xFFFFFFFF + mov r4, BACKGROUND_COLOR + call draw_str_to_overlay + call get_os_version + mov r10, r0 + mov r11, r1 + mov r12, r2 + mov r0, about_dialog_window_os_version_string + mov r1, 4 + mov r2, 36 + mov r3, 0xFFFFFFFF + mov r4, BACKGROUND_COLOR + call draw_format_str_to_overlay + call get_rom_version + mov r10, r0 + mov r11, r1 + mov r12, r2 + mov r0, about_dialog_window_rom_version_string + mov r1, 4 + mov r2, 52 + mov r3, 0xFFFFFFFF + mov r4, BACKGROUND_COLOR + call draw_format_str_to_overlay + mov r0, about_dialog_window_made_by_string_1 + mov r1, 4 + mov r2, 104 + mov r3, 0xFFFFFFFF + mov r4, BACKGROUND_COLOR + call draw_str_to_overlay + mov r0, about_dialog_window_made_by_string_2 + mov r1, 180 + mov r2, 120 + mov r3, 0xFFFFFFFF + mov r4, BACKGROUND_COLOR + call draw_str_to_overlay about_dialog_event_loop: - call get_next_event + mov r0, about_dialog_window_struct + call get_next_window_event - ; did the user click the menu bar? - cmp r0, EVENT_TYPE_MENU_BAR_CLICK - ifz mov r0, menu_items_root - ifz call menu_bar_click_event - - ; is the user in a menu? - cmp r0, EVENT_TYPE_MENU_UPDATE - ifz call menu_update_event - - ; did the user click a menu item? - cmp r0, EVENT_TYPE_MENU_CLICK - ifz call menu_click_event + cmp r0, EVENT_TYPE_MOUSE_CLICK + ifz jmp about_dialog_mouse_down +about_dialog_event_loop_end: call yield_task - jmp about_dialog_event_loop + rjmp about_dialog_event_loop -about_dialog_framebuffer_ptr: data.32 0 +about_dialog_mouse_down: + ; check if we are attempting to drag or close the window + cmp r2, 16 + iflteq jmp about_dialog_drag_or_close_window + + jmp about_dialog_event_loop_end + +about_dialog_drag_or_close_window: + cmp r1, 8 + iflteq jmp about_dialog_close_window + mov r0, about_dialog_window_struct + call start_dragging_window + jmp about_dialog_event_loop_end +about_dialog_close_window: + mov r0, about_dialog_window_struct + call destroy_window + call enable_menu_bar + jmp event_loop + +about_dialog_window_title: data.str "About" data.8 0 +about_dialog_window_struct: data.fill 0, 32 + +about_dialog_window_launcher_string: data.str "Launcher - the fox32os FXF launcher" data.8 0 +about_dialog_window_made_by_string_1: data.str "fox32 - the computer made with love" data.8 0 +about_dialog_window_made_by_string_2: data.str "by Ry and Lua" data.8 0 +about_dialog_window_os_version_string: data.str "fox32os version %u.%u.%u" data.8 0 +about_dialog_window_rom_version_string: data.str "fox32rom version %u.%u.%u" data.8 0