// MIT License, Copyright (c) 2020 Marvin Borner #include #include #include #include #include #include #include #include #include //#define FLUSH_TIMEOUT 6 struct client { u32 pid; }; struct window { u32 id; const char *name; struct context ctx; struct client client; u32 flags; vec2 pos; vec2 pos_prev; }; struct rectangle { vec2 pos1; // Upper left vec2 pos2; // Lower right void *data; }; static u8 bypp = 4; static struct vbe screen = { 0 }; static struct list *windows = NULL; // THIS LIST SHALL BE SORTED BY Z-INDEX! static struct window *direct = NULL; static struct window *root = NULL; static struct window *wallpaper = NULL; static struct window *cursor = NULL; static struct keymap *keymap = NULL; static struct client wm_client = { 0 }; static struct { u8 shift : 1; u8 alt : 1; u8 ctrl : 1; } special_keys = { 0 }; static struct { vec2 pos; u8 left : 1; u8 mid : 1; u8 right : 1; } mouse = { 0 }; static void buffer_flush(void) { #ifdef FLUSH_TIMEOUT static u32 time_flush = 0; u32 time_now = time(); if (time_now - time_flush > FLUSH_TIMEOUT) { memcpy(direct->ctx.fb, root->ctx.fb, root->ctx.bytes); time_flush = time_now; } #else memcpy(direct->ctx.fb, root->ctx.fb, root->ctx.bytes); #endif } static struct window *window_new(struct client client, const char *name, struct vec2 pos, struct vec2 size, u32 flags) { struct window *win = malloc(sizeof(*win)); win->id = rand(); win->name = name; // strdup? win->ctx.size = size; win->ctx.bpp = screen.bpp; win->ctx.pitch = size.x * bypp; win->ctx.bytes = win->ctx.pitch * win->ctx.size.y; if ((flags & WF_NO_FB) != 0) { win->ctx.fb = NULL; } else { win->ctx.fb = zalloc(size.y * win->ctx.pitch); } win->client = client; win->flags = flags; win->pos = pos; win->pos_prev = pos; list_add(windows, win); return win; } static struct window *window_find(u32 id) { struct node *iterator = windows->head; while (iterator) { struct window *win = iterator->data; if (win->id == id) return win; iterator = iterator->next; } return NULL; } /*static void window_destroy(struct window *win) { //free(win->name); free(win->ctx.fb); free(win); }*/ // Beautiful static void windows_at_rec(vec2 pos1, vec2 pos2, struct list *list) { u32 width = pos2.x - pos1.x; u32 height = pos2.y - pos1.y; vec2 rec_corners[] = { pos1, vec2_add(pos1, vec2(width, 0)), pos2, vec2_add(pos1, vec2(0, height)), }; struct node *iterator = windows->head; while (iterator) { struct window *win = iterator->data; if ((win->flags & WF_NO_WINDOW) != 0) goto next; vec2 corners[] = { win->pos, vec2_add(win->pos, vec2(win->ctx.size.x, 0)), vec2_add(win->pos, vec2(win->ctx.size.x, win->ctx.size.y)), vec2_add(win->pos, vec2(0, win->ctx.size.y)), }; for (int i = 0; i < 4; i++) { vec2 corner = corners[i]; if ((pos1.x < corner.x && pos1.y < corner.y) && (pos2.x > corner.x && pos2.y > corner.y)) { list_add(list, win); goto next; } } vec2 win_pos1 = win->pos; vec2 win_pos2 = vec2_add(win->pos, win->ctx.size); for (int i = 0; i < 4; i++) { vec2 corner = rec_corners[i]; if ((win_pos1.x < corner.x && win_pos1.y < corner.y) && (win_pos2.x > corner.x && win_pos2.y > corner.y)) { list_add(list, win); goto next; } } next: iterator = iterator->next; } } static struct rectangle rectangle_at(vec2 pos1, vec2 pos2, struct window *excluded) { u32 width = pos2.x - pos1.x; u32 height = pos2.y - pos1.y; u32 pitch = width * bypp; u8 *data = zalloc(width * height * bypp); struct list *windows_at = list_new(); windows_at_rec(pos1, pos2, windows_at); struct node *iterator = windows_at->head; while (iterator) { struct window *win = iterator->data; iterator = iterator->next; if (win == excluded) continue; s32 start_x = win->pos.x - pos1.x; u32 end_x = width; if (start_x <= 0) { // Either right side or background u32 right = start_x + win->ctx.size.x; if (right <= width) { // Right side start_x = 0; end_x = right; } else { // Background start_x = 0; } } s32 start_y = win->pos.y - pos1.y; u32 end_y = height; if (start_y <= 0) { // Either bottom side or background u32 bot = start_y + win->ctx.size.y; if (bot <= height) { // Bottom side start_y = 0; end_y = bot; } else { // Background start_y = 0; } } vec2 pos = vec2_sub(pos1, win->pos); u8 *srcfb = &win->ctx.fb[(pos.x + start_x) * bypp + (pos.y + start_y) * win->ctx.pitch]; u8 *destfb = &data[start_x * bypp + start_y * pitch]; // Copy window data to rectangle buffer for (u32 cy = start_y; cy < end_y; cy++) { int diff = 0; for (u32 cx = start_x; cx < end_x; cx++) { if (srcfb[bypp - 1]) memcpy(destfb, srcfb, bypp); srcfb += bypp; destfb += bypp; diff += bypp; } srcfb += win->ctx.pitch - diff; destfb += pitch - diff; } } list_destroy(windows_at); return (struct rectangle){ .pos1 = pos1, .pos2 = pos2, .data = data }; } static void window_redraw(struct window *win) { // TODO: Only redraw difference of prev/curr (difficult with negative directions) vec2 pos1 = win->pos_prev; vec2 pos2 = vec2(pos1.x + win->ctx.size.x, pos1.y + win->ctx.size.y); struct rectangle rec = rectangle_at(pos1, pos2, win); u8 *srcfb = rec.data; u8 *destfb = &root->ctx.fb[rec.pos1.x * bypp + rec.pos1.y * root->ctx.pitch]; for (u32 cy = 0; cy < win->ctx.size.y; cy++) { memcpy(destfb, srcfb, win->ctx.size.x * bypp); srcfb += win->ctx.pitch; destfb += root->ctx.pitch; } free(rec.data); gfx_ctx_on_ctx(&root->ctx, &win->ctx, win->pos); if (win != cursor) window_redraw(cursor); buffer_flush(); } static void handle_event_keyboard(struct event_keyboard *event) { if (event->magic != KEYBOARD_MAGIC) { log("Keyboard magic doesn't match!\n"); return; } if (event->scancode == KEY_LEFTSHIFT || event->scancode == KEY_RIGHTSHIFT) special_keys.shift ^= 1; else if (event->scancode == KEY_LEFTALT || event->scancode == KEY_RIGHTALT) special_keys.alt ^= 1; else if (event->scancode == KEY_LEFTCTRL || event->scancode == KEY_RIGHTCTRL) special_keys.ctrl ^= 1; if (event->scancode > KEYMAP_LENGTH) return; char ch; if (special_keys.shift) ch = keymap->shift_map[event->scancode]; else if (special_keys.alt) ch = keymap->alt_map[event->scancode]; else ch = keymap->map[event->scancode]; (void)ch; } static void handle_event_mouse(struct event_mouse *event) { if (event->magic != MOUSE_MAGIC) { log("Mouse magic doesn't match!\n"); return; } cursor->pos_prev = mouse.pos; mouse.pos.x += event->diff_x; mouse.pos.y -= event->diff_y; // Fix x overflow if ((signed)mouse.pos.x < 0) mouse.pos.x = 0; else if (mouse.pos.x + cursor->ctx.size.x > (unsigned)screen.width - 1) mouse.pos.x = screen.width - cursor->ctx.size.x - 1; // Fix y overflow if ((signed)mouse.pos.y < 0) mouse.pos.y = 0; else if (mouse.pos.y + cursor->ctx.size.y > (unsigned)screen.height - 1) mouse.pos.y = screen.height - cursor->ctx.size.y - 1; cursor->pos = mouse.pos; if (!vec2_eq(cursor->pos, cursor->pos_prev)) window_redraw(cursor); } static void handle_message_new_window(struct message *msg) { if (!msg->data) { msg_send(msg->src, GUI_NEW_WINDOW | MSG_FAILURE, NULL); return; } struct gui_window *buf = msg->data; struct window *win = window_new((struct client){ .pid = msg->src }, "idk", vec2(500, 600), vec2(600, 400), 0); buf->id = win->id; buf->ctx = &win->ctx; buf->pos = &win->pos; msg_send(msg->src, GUI_NEW_WINDOW | MSG_SUCCESS, NULL); /* window_redraw(win); */ } static void handle_message_redraw_window(struct message *msg) { if (!msg->data) { msg_send(msg->src, GUI_REDRAW_WINDOW | MSG_FAILURE, NULL); return; } u32 id = *(u32 *)msg->data; struct window *win = window_find(id); if (!win) { msg_send(msg->src, GUI_REDRAW_WINDOW | MSG_FAILURE, NULL); return; } msg_send(msg->src, GUI_REDRAW_WINDOW | MSG_SUCCESS, NULL); window_redraw(win); } static void handle_message(struct message *msg) { if (msg->magic != MSG_MAGIC) { log("Message magic doesn't match!\n"); return; } switch (msg->type) { case GUI_NEW_WINDOW: handle_message_new_window(msg); break; case GUI_REDRAW_WINDOW: handle_message_redraw_window(msg); break; default: log("Message type %d not implemented!\n", msg->type); msg_send(msg->src, MSG_FAILURE, NULL); } } int main(int argc, char **argv) { (void)argc; screen = *(struct vbe *)argv[1]; wm_client = (struct client){ .pid = getpid() }; bypp = (screen.bpp >> 3); log("WM loaded: %dx%d\n", screen.width, screen.height); windows = list_new(); keymap = keymap_parse("/res/keymaps/en.keymap"); direct = window_new(wm_client, "direct", vec2(0, 0), vec2(screen.width, screen.height), WF_NO_WINDOW | WF_NO_FB | WF_NO_DRAG | WF_NO_FOCUS | WF_NO_RESIZE); direct->ctx.fb = screen.fb; direct->flags ^= WF_NO_FB; root = window_new(wm_client, "root", vec2(0, 0), vec2(screen.width, screen.height), WF_NO_WINDOW | WF_NO_DRAG | WF_NO_FOCUS | WF_NO_RESIZE); wallpaper = window_new(wm_client, "wallpaper", vec2(0, 0), vec2(screen.width, screen.height), WF_NO_DRAG | WF_NO_FOCUS | WF_NO_RESIZE); cursor = window_new(wm_client, "cursor", vec2(0, 0), vec2(32, 32), WF_NO_WINDOW | WF_NO_DRAG | WF_NO_FOCUS | WF_NO_RESIZE); /* gfx_write(&direct->ctx, vec2(0, 0), FONT_32, COLOR_FG, "Loading Melvix..."); */ gfx_load_wallpaper(&wallpaper->ctx, "/res/wall.png"); gfx_load_wallpaper(&cursor->ctx, "/res/cursor.png"); window_redraw(wallpaper); struct message msg = { 0 }; struct event_keyboard event_keyboard = { 0 }; struct event_mouse event_mouse = { 0 }; const char *listeners[] = { "/dev/kbd", "/dev/mouse", "/proc/self/msg" }; while (1) { int poll_ret = 0; if ((poll_ret = poll(listeners)) >= 0) { if (poll_ret == 0) { if (read(listeners[poll_ret], &event_keyboard, 0, sizeof(event_keyboard)) > 0) handle_event_keyboard(&event_keyboard); continue; } else if (poll_ret == 1) { if (read(listeners[poll_ret], &event_mouse, 0, sizeof(event_mouse)) > 0) handle_event_mouse(&event_mouse); continue; } else if (poll_ret == 2) { if (read(listeners[poll_ret], &msg, 0, sizeof(msg)) > 0) handle_message(&msg); continue; } } else { err(1, "POLL ERROR!\n"); } }; // TODO: Execute? free(keymap); list_destroy(windows); return 0; }