From bce944d1498eaa3b6940ee234c863b3548a66b37 Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Sun, 24 Jan 2021 12:00:11 -0500 Subject: graphics! --- src/kernel/window.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 src/kernel/window.c (limited to 'src/kernel/window.c') diff --git a/src/kernel/window.c b/src/kernel/window.c new file mode 100644 index 0000000..28798d1 --- /dev/null +++ b/src/kernel/window.c @@ -0,0 +1,253 @@ +#include "paging.h" +#include "window.h" +#include "drive.h" +#include "task.h" +#include "util.h" +#include "pmap.h" +#include "log.h" + +#define MAX_WINDOWS 64 +#define ACTION_BUFFER_PAGES 1 +#define AB_END(buf) (buf + (ACTION_BUFFER_PAGES * 4096) / sizeof(struct window_action)) + +#define BACKGROUND(x, y) ((x / 16 + y / 16) % 2 ? 0x14 : 0x07) + +static struct window { + const volatile void *pixel_buffer_pma; + uint16_t width; + uint16_t height; + + uint16_t xpos; + uint16_t ypos; + + struct window_action *action_buffer; + struct window_action *next_action_read; + struct window_action *next_action_write; + + struct window *above; + struct window *below; + + struct task_state *from_task; +} windows[MAX_WINDOWS]; + +static struct window *bottom_window = 0; +static struct window *top_window = 0; + +static inline void set_pix(uint16_t x, uint16_t y, uint8_t value) { + if ((x < 320) && (y < 200)) + *(uint8_t *)(0xa0000 + y * 320 + x) = value; +} + +static void paint_and_above(const struct window *w) { + switch_to_kernel_cr3(); + for (const struct window *i = w; i; i = i->above) + for (uint16_t y = 0; y < i->height; ++y) + for (uint16_t x = 0; x < i->width; ++x) { + const uint8_t pixel = ((uint8_t *)i->pixel_buffer_pma)[y * i->width + x]; + if (pixel) + set_pix(x + i->xpos, y + i->ypos, pixel); + } + switch_to_task_cr3(); +} + +void paint_bg() { + for (uint16_t y = 0; y < 200; ++y) + for (uint16_t x = 0; x < 320; ++x) + set_pix(x, y, BACKGROUND(x, y)); +} + +static void paint_all() { + paint_bg(); + paint_and_above(bottom_window); +} + +struct window *new_window(uint16_t width, uint16_t height, const void *pixel_buffer) { + if (!pixel_buffer) { + logf(LOG_WARN, "Refusing to create window with null pixel buffer for task %s.", active_task->name); + return 0; + } + + struct window *w; + for (uint8_t i = 0; i < MAX_WINDOWS; ++i) + if (!windows[i].pixel_buffer_pma) { + w = &windows[i]; + goto got_window; + } + return 0; +got_window: + + w->pixel_buffer_pma = vma_to_pma(active_task->page_directory, pixel_buffer); + w->width = width; + w->height = height; + + struct window_action *const ab = allocate_kernel_pages(ACTION_BUFFER_PAGES); + w->action_buffer = ab; + w->next_action_read = ab; + w->next_action_write = ab; + + if (top_window) + top_window->above = w; + else + bottom_window = w; + w->above = 0; + w->below = top_window; + top_window = w; + + w->from_task = active_task; + + paint_and_above(w); + return w; +} + +void del_window(struct window *w) { + if (w == top_window) + top_window = w->below; + if (w == bottom_window) + bottom_window = w->above; + if (w->below) + w->below->above = w->above; + if (w->above) + w->above->below = w->below; + + free_pages(w->action_buffer, ACTION_BUFFER_PAGES); + w->pixel_buffer_pma = 0; + + paint_all(); +} + +void resize_window(struct window *w, uint16_t width, uint16_t height) { + const bool smaller = (width < w->width) || (height < w->height); + + w->width = width; + w->height = height; + + if (smaller) + paint_all(); + else + paint_and_above(w); +} + +void reassign_pixel_buffer(struct window *w, const void *pixel_buffer) { + w->pixel_buffer_pma = vma_to_pma(active_task->page_directory, pixel_buffer); +} + +void push_window_paint(const struct window *w) { + paint_and_above(w); +} + +struct window_action next_window_action(struct window *w) { + if (w->next_action_write == w->next_action_read) + return (struct window_action){.action_type = NONE}; + const struct window_action *const action = w->next_action_read; + if (++(w->next_action_read) >= AB_END(w->action_buffer)) + w->next_action_read = w->action_buffer; + return *action; +} + +static void send_action(struct window *w, struct window_action packet) { + struct window_action *next_next = w->next_action_write + 1; + if (next_next >= AB_END(w->action_buffer)) + next_next = w->action_buffer; + if (next_next != w->next_action_read) { + *(w->next_action_write) = packet; + w->next_action_write = next_next; + unwait(w->from_task, (struct wait){.mode = WINDOW_ACTION}); + } +} + +enum wm_action { + WM_SHUFFLE_UP, + WM_SHUFFLE_DOWN, + WM_MOVE_LEFT, + WM_MOVE_RIGHT, + WM_MOVE_UP, + WM_MOVE_DOWN, + + N_WM_ACTIONS +}; + +static struct key_packet keybinds[] = { + {.key_id = KEY_PAGE_DOWN, .modifiers = WINS}, + {.key_id = KEY_PAGE_UP, .modifiers = WINS}, + {.key_id = KEY_LEFT_ARROW, .modifiers = WINS}, + {.key_id = KEY_RIGHT_ARROW, .modifiers = WINS}, + {.key_id = KEY_UP_ARROW, .modifiers = WINS}, + {.key_id = KEY_DOWN_ARROW, .modifiers = WINS} +}; + +static inline bool fuzzy_key_match(struct key_packet t, struct key_packet a) { + if (t.key_id != a.key_id) + return false; + + if (((t.modifiers & SHIFTS) == SHIFTS) && (a.modifiers & SHIFTS)) + a.modifiers |= SHIFTS; + if (((t.modifiers & CTRLS) == CTRLS) && (a.modifiers & CTRLS)) + a.modifiers |= CTRLS; + if (((t.modifiers & ALTS) == ALTS) && (a.modifiers & ALTS)) + a.modifiers |= ALTS; + if (((t.modifiers & WINS) == WINS) && (a.modifiers & WINS)) + a.modifiers |= WINS; + + return a.modifiers == t.modifiers; +} + +#include "log.h" + +void on_action(struct window_action packet) { +//logf(LOG_INFO, "Window action, top window = 0x%d from %s.", top_window, top_window->from_task->name); + + if (packet.action_type == NOT_READY) + return; + + if (top_window) { + if (packet.action_type == KEY_DOWN) + for (uint8_t i = 0; i < N_WM_ACTIONS; ++i) + if (fuzzy_key_match(keybinds[i], packet.as_key)) { + switch_to_kernel_cr3(); + struct window *old_top, *old_bottom; + switch (i) { + case WM_SHUFFLE_UP: + old_top = top_window; + old_bottom = bottom_window; + top_window = old_top->below; + top_window->above = 0; + old_top->below = 0; + old_top->above = old_bottom; + old_bottom->below = old_top; + bottom_window = old_top; + paint_and_above(bottom_window->above); + break; + case WM_SHUFFLE_DOWN: + old_top = top_window; + old_bottom = bottom_window; + bottom_window = old_bottom->above; + bottom_window->below = 0; + old_bottom->above = 0; + old_bottom->below = old_top; + old_top->above = old_bottom; + top_window = old_bottom; + paint_and_above(top_window); + break; + case WM_MOVE_LEFT: + --top_window->xpos; + paint_all(); + break; + case WM_MOVE_RIGHT: + ++top_window->xpos; + paint_all(); + break; + case WM_MOVE_UP: + --top_window->ypos; + paint_all(); + break; + case WM_MOVE_DOWN: + ++top_window->ypos; + paint_all(); + } + switch_to_task_cr3(); + return; + } + + send_action(top_window, packet); + } +} \ No newline at end of file -- cgit v1.2.3