summaryrefslogtreecommitdiff
path: root/src/kernel/window.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/window.c')
-rw-r--r--src/kernel/window.c253
1 files changed, 253 insertions, 0 deletions
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