#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) #define BORDER_LIGHT 0x1c #define BORDER_DARK 0x08 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; #define SCREEN_HEIGHT 200 #define SCREEN_WIDTH 320 #define VGA_MEMORY ((uint8_t *)0xa0000) static inline void draw_hz_line(uint16_t y, uint16_t xs, uint16_t xm, uint8_t color) { if (y >= SCREEN_HEIGHT) return; if (xs >= SCREEN_WIDTH) xs = 0; if (xm > SCREEN_WIDTH) xm = SCREEN_WIDTH; uint8_t *const line_start = VGA_MEMORY + y * SCREEN_WIDTH; for (uint8_t *p = line_start + xs; p < line_start + xm; ++p) *p = color; } static inline void draw_vt_line(uint16_t x, uint16_t ys, uint16_t ym, uint8_t color) { if (x >= SCREEN_WIDTH) return; if (ys >= SCREEN_HEIGHT) ys = 0; if (ym > SCREEN_HEIGHT) ym = SCREEN_HEIGHT; uint8_t *const line_start = VGA_MEMORY + x; for (uint8_t *p = line_start + ys * SCREEN_WIDTH; p < line_start + ym * SCREEN_WIDTH; p += SCREEN_WIDTH) *p = color; } static void paint_and_above(const struct window *w) { switch_to_kernel_cr3(); for (const struct window *i = w; i; i = i->above) { const uint16_t ys = i->ypos < SCREEN_HEIGHT ? 0 : -i->ypos; const uint16_t xs = i->xpos < SCREEN_WIDTH ? 0 : -i->xpos; const uint16_t ym = i->ypos + i->height <= SCREEN_HEIGHT ? i->height : SCREEN_HEIGHT - i->ypos; const uint16_t xm = i->xpos + i->width <= SCREEN_WIDTH ? i->width : SCREEN_WIDTH - i->xpos; for (uint16_t y = ys; y < ym; ++y) for (uint16_t x = xs; x < xm; ++x) { const uint8_t pixel = ((uint8_t *)i->pixel_buffer_pma)[y * i->width + x]; if (pixel) VGA_MEMORY[(y + i->ypos) * SCREEN_WIDTH + x + i->xpos] = pixel; } draw_hz_line(i->ypos - 2, i->xpos - 2, i->xpos + i->width + 2, BORDER_LIGHT); draw_vt_line(i->xpos - 2, i->ypos - 1, i->ypos + i->height + 2, BORDER_LIGHT); draw_hz_line(i->ypos - 1, i->xpos - 1, i->xpos + i->width + 2, BORDER_DARK); draw_vt_line(i->xpos - 1, i->ypos, i->ypos + i->height + 2, BORDER_DARK); draw_hz_line(i->ypos + i->height, i->xpos, i->xpos + i->width + 1, BORDER_LIGHT); draw_vt_line(i->xpos + i->width, i->ypos, i->ypos + i->height, BORDER_LIGHT); draw_hz_line(i->ypos + i->height + 1, i->xpos, i->xpos + i->width + 2, BORDER_DARK); draw_vt_line(i->xpos + i->width + 1, i->ypos, i->ypos + i->height + 1, BORDER_DARK); } switch_to_task_cr3(); } void paint_bg() { for (uint16_t y = 0; y < SCREEN_HEIGHT; ++y) for (uint16_t x = 0; x < SCREEN_WIDTH; ++x) VGA_MEMORY[y * SCREEN_WIDTH + x] = 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; w->xpos = 10; w->ypos = 10; 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; } static void del_no_paint(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; } void del_window(struct window *w) { del_no_paint(w); paint_all(); } void delete_any_windows_from(struct task_state *tstate) { bool need_to_paint = false; for (struct window *w = windows; w < windows + MAX_WINDOWS; ++w) if (w->pixel_buffer_pma && (w->from_task == tstate)) { del_no_paint(w); need_to_paint = true; } if (need_to_paint) paint_all(); } void resize_window(struct window *w, uint16_t width, uint16_t height, const void *pixel_buffer) { const bool smaller = (width < w->width) || (height < w->height); w->width = width; w->height = height; reassign_pixel_buffer(w, pixel_buffer); 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); } }