U0 @truetype_init_fonts() { CDirEntry* font_files = FilesFind("M:/Fonts/*.ttf"); CDirEntry* de = font_files; stbtt_fontinfo* info; U8 name_buffer[512]; U8* name_ptr; I32 length; while (de) { info = CAlloc(sizeof(stbtt_fontinfo), adam_task); if (@stbtt_InitFont(info, FileRead(de->full_name), 0)) { MemSet(name_buffer, NULL, 512); name_ptr = @stbtt_GetFontNameDefault(info, &length); MemCpy(name_buffer, name_ptr, length); Fonts->set(name_buffer, info, JSON_NUMBER); } de = de->next; } DirTreeDel(font_files); Fonts->set("sans-serif", Fonts->@("Free Sans"), JSON_NUMBER); Fonts->set("sans-serif Bold", Fonts->@("Free Sans Bold"), JSON_NUMBER); Fonts->set("sans-serif Bold Italic", Fonts->@("Free Sans Bold Oblique"), JSON_NUMBER); Fonts->set("sans-serif Italic", Fonts->@("Free Sans Oblique"), JSON_NUMBER); Fonts->set("serif", Fonts->@("Free Serif"), JSON_NUMBER); Fonts->set("serif Bold", Fonts->@("Free Serif Bold"), JSON_NUMBER); Fonts->set("serif Bold Italic", Fonts->@("Free Serif Bold Italic"), JSON_NUMBER); Fonts->set("serif Italic", Fonts->@("Free Serif Italic"), JSON_NUMBER); Fonts->set("monospace", Fonts->@("Free Monospaced"), JSON_NUMBER); Fonts->set("monospace Bold", Fonts->@("Free Monospaced Bold"), JSON_NUMBER); Fonts->set("monospace Bold Italic", Fonts->@("Free Monospaced Bold Oblique"), JSON_NUMBER); Fonts->set("monospace Italic", Fonts->@("Free Monospaced Oblique"), JSON_NUMBER); } @truetype_init_fonts; #define CPZ_MSG_WIN_CREATE 0x1001 #define CPZ_MSG_WIN_DESTROY 0x1002 #define CPZ_MSG_WIN_REPAINT 0x1003 #define CPZ_MSG_WIN_MOVE_TO 0x1004 #define CPZ_MSG_WIN_RESIZE_TO 0x1005 #define CPZ_MSG_WIN_MOUSE_AT 0x1006 #define CPZ_MSG_WIN_MOUSE_WHEEL 0x1007 #define CPZ_MSG_WIN_LEFT_BTN_UP 0x1008 #define CPZ_MSG_WIN_LEFT_BTN_DOWN 0x1009 #define CPZ_MSG_WIN_RIGHT_BTN_UP 0x100A #define CPZ_MSG_WIN_RIGHT_BTN_DOWN 0x100B #define CPZ_MSG_WIN_KEY_PRESS 0x100C #define CPZ_MSG_WIN_WIDGET_DESTROY 0x100D #define CPZ_MSG_WIN_SET_Z_INDEX 0x100E #define CPZ_MSG_SET_WALLPAPER 0x100F #define CPZ_WALLPAPER_CENTERED 0x0 #define CPZ_WALLPAPER_AUTORESIZE 0x1 #define CPZ_WALLPAPER_REPEAT 0x2 class @compositor_menubar { Window* win; CTask* task; TextInputWidget* title; }; class @compositor_windows_list { @compositor_windows_list* prev; @compositor_windows_list* next; Window* window; }; class @compositor { I64 next_id; I64 max_z_index; Window* active_win; Context2D* blend_ctx; Context2D* ctx; Context2D* pointer; @compositor_menubar menubar; @compositor_windows_list* windows; @compositor_windows_list* global_input_event_listeners; Bounds2D bounds; @mouse mouse; @session session; @theme theme; Bool in_drag; Bool in_resize; CTask* task; U0 (*Init)(); Window* (*CreateWindow)(I64 x, I64 y, I64 width, I64 height, I64 flags = WIN_FLAGS_DEFAULT, U8* title = NULL, Context2D* icon = NULL); U0 (*DestroyWindow)(Window* win); Window (*GetWindowByTitle)(U8* title); Window (*GetWindowByZIndex)(I64 index); U0 (*HideWindow)(Window* win); U0 (*ShowWindow)(Window* win); U0 (*RegisterForGlobalInputEvents)(Window* win); U0 (*UnregisterForGlobalInputEvents)(Window* win); U0 (*SetWallpaper)(Context2D* ctx, U32 mode = CPZ_WALLPAPER_AUTORESIZE, U32 background = Color(0, 0, 0)); U0 (*Task)(); }; @compositor Compositor; U0 @compositor_add_window_to_list(Window* win) { @compositor_windows_list* win_list = Compositor.windows; @compositor_windows_list* win_next = CAlloc(sizeof(@compositor_windows_list)); while (win_list->next) { win_list = win_list->next; } win_next->window = win; win_next->prev = win_list; win_list->next = win_next; } U0 @compositor_add_global_input_event_listener_to_list(Window* win) { @compositor_windows_list* win_list = Compositor.global_input_event_listeners; @compositor_windows_list* win_next = CAlloc(sizeof(@compositor_windows_list)); while (win_list->next) { win_list = win_list->next; } win_next->window = win; win_next->prev = win_list; win_list->next = win_next; } U0 @compositor_remove_global_input_event_listener_from_list(Window* win) { @compositor_windows_list* win_list = Compositor.global_input_event_listeners; @compositor_windows_list* win_list_prev = NULL; @compositor_windows_list* win_list_next = NULL; while (win_list) { if (win_list->window == win) { win_list_prev = win_list->prev; win_list_next = win_list->next; win_list_prev->next = win_list_next; win_list_next->prev = win_list_prev; Free(win_list); return; } win_list = win_list->next; } } U0 @compositor_refresh(Window* win) { IpcMessage* msg = CAlloc(sizeof(IpcMessage)); msg->client = win->client; msg->type = CPZ_MSG_WIN_REPAINT; msg->payload = win; Ipc.MsgSend(msg->client, msg); if (!Compositor.menubar.win || Compositor.menubar.win == win) return; if (Compositor.active_win == win && Compositor.menubar.title) { Gui.Widget.SetText(Compositor.menubar.title, win->title); } } U0 @compositor_set_z_index(Window* win, I64 index) { if (!win) return; I64 i = 0; @compositor_windows_list* win_index = NULL; @compositor_windows_list* win_list = Compositor.windows->next; @compositor_windows_list* prev = NULL; @compositor_windows_list* next = NULL; while (win_list) { if (win_list->window == win) { win_index = win_list; if (win_index->prev) prev = win_index->prev; if (win_index->next) next = win_index->next; if (prev) prev->next = next; if (next) next->prev = prev; break; } win_list = win_list->next; } win_list = Compositor.windows->next; prev = NULL; while (win_list) { if (i == index) { prev = win_list->prev; if (prev) prev->next = win_index; win_index->prev = prev; win_index->next = win_list; win_list->prev = win_index; break; } i++; win_list = win_list->next; } } U0 @compositor_set_z_index_send_msg(Window* win, I64 index) { if (!win) return; IpcMessage* msg = CAlloc(sizeof(IpcMessage)); msg->client = win->client; msg->type = CPZ_MSG_WIN_SET_Z_INDEX; msg->payload = win; msg->i64 = index; Ipc.MsgSend(Compositor.task, msg); } U0 @compositor_set_wallpaper(Context2D* ctx, U32 mode, U32 background) { // Sets the wallpaper, doesn't Free the Context, you need to Free it after. if (!ctx) { return; } Window* win = Compositor.GetWindowByTitle("Wallpaper"); Context2D* tmp = ctx; if (!win) { // Please, don't call this if the system isn't initialized System.Log(Fs, "Trying to set the Wallpaper, but the System isn't initialized completely!"); return; } if (Display.Width() != ctx->width || Display.Height() != ctx->height) { if (Display.Width() > ctx->width && Display.Height() > ctx->height) { switch (mode) { case CPZ_WALLPAPER_CENTERED: tmp = NewContext2D(Display.Width(), Display.Height()); tmp->fill(background); CopyRect2D(tmp, (Display.Width() / 2) - (ctx->width / 2), (Display.Height() / 2) - (ctx->height / 2), ctx); break; case CPZ_WALLPAPER_REPEAT: tmp = NewContext2D(Display.Width(), Display.Height()); I64 x = 0; I64 y = 0; while (TRUE) { CopyRect2D(tmp, x, y, ctx); x += ctx->width; if (x >= Display.Width()) { x = 0; y += ctx->height; if (y >= Display.Height()) break; } } break; case CPZ_WALLPAPER_AUTORESIZE: default: tmp = Scale2D(tmp, Display.Width() / ToF64(ctx->width), Display.Height() / ToF64(ctx->height)); break; } } else { tmp = Scale2D(tmp, Display.Width() / ToF64(ctx->width), Display.Height() / ToF64(ctx->height)); } } MemCpyU32(win->backing_store->fb, tmp->fb, Display.Width() * Display.Height()); if (tmp != ctx) // We don't want to delete the context so it can be reused later (If necessary) DelContext2D(tmp); } U0 @compositor_set_wallpaper_send_msg(Context2D* ctx, U32 mode = CPZ_WALLPAPER_AUTORESIZE, U32 background = Color(0, 0, 0)) { if (!ctx) return; IpcMessage* msg = CAlloc(sizeof(IpcMessage)); msg->client = Fs; msg->type = CPZ_MSG_SET_WALLPAPER; msg->payload = ctx; msg->i64.u32[0] = mode; msg->i64.u32[1] = background; Ipc.MsgSend(Compositor.task, msg); } U0 @compositor_ipc_queue_process() { Window* win; IpcMessage* msg; @compositor_windows_list* win_list = Compositor.windows->next; @compositor_windows_list* prev; @compositor_windows_list* next; msg = Ipc.MsgRecv(); if (msg) { switch (msg->type) { case CPZ_MSG_WIN_CREATE: win = msg->payload; win->client = msg->client; win->render_ctx = NewContext2D(Display.Width(), Display.Height()); win->backing_store = NewContext2D(Display.Width(), Display.Height()); win->backing_store->width = win->width; win->backing_store->height = win->height; @compositor_add_window_to_list(win); System.Log(Fs, "Received message ← CreateWindow (%dx%d at %d, %d)", win->width, win->height, win->x, win->y); Free(msg); msg = CAlloc(sizeof(IpcMessage)); msg->client = win->client; msg->type = CPZ_MSG_WIN_REPAINT; msg->payload = win; Ipc.MsgSend(msg->client, msg); break; case CPZ_MSG_WIN_DESTROY: win = msg->payload; while (win_list) { if (win_list->window == win) { prev = win_list->prev; next = win_list->next; prev->next = next; next->prev = prev; if (win_list->window->backing_store) DelContext2D(win_list->window->backing_store); if (win_list->window->render_ctx) DelContext2D(win_list->window->render_ctx); // FIXME: free Widgets Free(win_list->window); Free(win_list); Compositor.active_win = NULL; System.Log(Fs, "Received message ← DestroyWindow 0x%08x", win); break; } win_list = win_list->next; } break; case CPZ_MSG_WIN_SET_Z_INDEX: @compositor_set_z_index(msg->payload, msg->i64); System.Log(Fs, "Received message ← SetZIndex (%08X, %d)", msg->payload, msg->i64); Free(msg); break; case CPZ_MSG_SET_WALLPAPER: Context2D* ctx = msg->payload; U32 mode = msg->i64.u32[0]; U32 background = msg->i64.u32[1]; System.Log(Fs, "Received message ← SetWallpaper (%08X, Mode: %d, Background: %d)", ctx, mode, background); @compositor_set_wallpaper(ctx, mode, background); Free(msg); break; default: Free(msg); break; } } } Window* @compositor_get_window_by_title(U8* title) { @compositor_windows_list* win_list = Compositor.windows->next; while (win_list) { if (win_list->window) if (!StrCmp(&win_list->window->title, title)) return win_list->window; win_list = win_list->next; } return NULL; } Window* @compositor_get_window_by_z_index(I64 index) { @compositor_windows_list* win_list = Compositor.windows->next; I64 i = 0; while (win_list) { if (i == index) return win_list->window; i++; win_list = win_list->next; } return NULL; } U0 @compositor_handle_active_window_flags() { if (!Compositor.active_win) return; if (Compositor.active_win->flags & WIN_FLAGS_MINIMIZED) { Compositor.active_win = Compositor.GetWindowByTitle("Wallpaper"); if (Compositor.menubar.win) { Gui.Widget.SetText(Compositor.menubar.title, &Compositor.active_win->title); Gui.Window.Refresh(Compositor.menubar.win); } } } Context2D* @compositor_set_theme_pointer(U8* path, U8* pointer) { U8 file_path[512]; StrPrint(&file_path, "%sPointer/%s.png", path, pointer); Context2D* ctx = Image.FileToContext2D(&file_path); return ctx; } U0 @compositor_set_theme_wallpaper(U8* path) { U8 file_path[512]; StrPrint(&file_path, "%s%s", path, "wallpaper.jpg"); Compositor.theme.wallpaper = Image.FileToContext2D(file_path); if (!Compositor.theme.wallpaper) { Compositor.theme.wallpaper = NewContext2D(Display.Width(), Display.Height()); } } U0 @compositor_set_theme(U8* theme) { U8 theme_path[512]; U8 exec_path[512]; StrPrint(&theme_path, "M:/Media/Themes/%s/", theme); if (!IsDir(&theme_path)) { System.Log(Fs, "SetTheme failed: Theme does not exist: '%s'", theme); return; } // FIXME: This is disgusting Compositor.theme.pointer.pointer = @compositor_set_theme_pointer(&theme_path, "pointer"); Compositor.theme.pointer.pen = @compositor_set_theme_pointer(&theme_path, "pen"); Compositor.theme.pointer.move = @compositor_set_theme_pointer(&theme_path, "move"); Compositor.theme.pointer.link = @compositor_set_theme_pointer(&theme_path, "link"); Compositor.theme.pointer.horz = @compositor_set_theme_pointer(&theme_path, "horz"); Compositor.theme.pointer.vert = @compositor_set_theme_pointer(&theme_path, "vert"); Compositor.theme.pointer.text = @compositor_set_theme_pointer(&theme_path, "text"); Compositor.theme.pointer.cross = @compositor_set_theme_pointer(&theme_path, "cross"); Compositor.theme.pointer.dgn1 = @compositor_set_theme_pointer(&theme_path, "dgn1"); Compositor.theme.pointer.dgn2 = @compositor_set_theme_pointer(&theme_path, "dgn2"); Compositor.theme.pointer.help = @compositor_set_theme_pointer(&theme_path, "help"); Compositor.theme.pointer.alternate = @compositor_set_theme_pointer(&theme_path, "alternate"); Compositor.theme.pointer.unavailable = @compositor_set_theme_pointer(&theme_path, "unavailable"); @compositor_set_theme_wallpaper(&theme_path); StrPrint(&exec_path, "%sTheme.HC", &theme_path); ExeDoc(DocRead(exec_path)); } Bool @compositor_active_win_flag_is_set(U64 flag) { if (@gui_window_flag_is_set(Compositor.active_win, flag)) return TRUE; return FALSE; } U0 @compositor_set_pointer(Context2D* pointer = NULL) { if (!pointer) pointer = Compositor.theme.pointer.pointer; if (Animation2D.IsAnimation(pointer)) Compositor.pointer = Animation2D.Frame(pointer); else Compositor.pointer = pointer; Mouse.PointerSet(Compositor.pointer->fb, Compositor.pointer->width, Compositor.pointer->height); } U0 @compositor_set_active_window(Window* win) { if (!win) return; if (Compositor.active_win == win) return; IpcMessage* msg; @compositor_windows_list* win_list = Compositor.windows->next; @compositor_windows_list* prev; @compositor_windows_list* next; while (win_list) { if (!(win->flags & WIN_FLAGS_NO_REINDEX)) { if (win_list->window == win && win_list->next) { prev = win_list->prev; next = win_list->next; prev->next = next; next->prev = prev; while (next->next) { next = next->next; } win_list->prev = next; win_list->next = NULL; next->next = win_list; break; } } win_list = win_list->next; } if (Compositor.active_win && Compositor.active_win != win) { msg = CAlloc(sizeof(IpcMessage)); msg->client = Compositor.active_win->client; msg->type = CPZ_MSG_WIN_REPAINT; msg->payload = Compositor.active_win; Ipc.MsgSend(msg->client, msg); } Compositor.active_win = win; if (Compositor.menubar.win && Compositor.menubar.title) { Gui.Widget.SetText(Compositor.menubar.title, win->title); Gui.Window.Refresh(Compositor.menubar.win); } System.Log(Fs, "SetActiveWindow (%dx%d at %d, %d)", win->width, win->height, win->x, win->y); msg = CAlloc(sizeof(IpcMessage)); msg->client = win->client; msg->type = CPZ_MSG_WIN_REPAINT; msg->payload = win; Ipc.MsgSend(msg->client, msg); @compositor_set_pointer(); } U0 @compositor_handle_window_resize() { if (!Compositor.active_win) return; if (Compositor.active_win->signature != WIN_SIGNATURE) return; if (!(@compositor_active_win_flag_is_set(WIN_FLAGS_RESIZABLE))) goto resize_set_pointer; Bool set_pointer_to_resize = FALSE; I64 new_width; I64 new_height; // Bottom right if (Mouse.x > Compositor.active_win->x + Compositor.active_win->width - 16 && Mouse.x < Compositor.active_win->x + Compositor.active_win->width && Mouse.y > Compositor.active_win->y + Compositor.active_win->height - 16 && Mouse.y < Compositor.active_win->y + Compositor.active_win->height) { @compositor_set_pointer(Compositor.theme.pointer.dgn1); set_pointer_to_resize = TRUE; } if (Mouse.left && !Compositor.mouse.left && Compositor.active_win) { if (Mouse.x > Compositor.active_win->x + Compositor.active_win->width - 16 && Mouse.x < Compositor.active_win->x + Compositor.active_win->width && Mouse.y > Compositor.active_win->y + Compositor.active_win->height - 16 && Mouse.y < Compositor.active_win->y + Compositor.active_win->height) { Compositor.active_win->origin.x = Compositor.active_win->x; Compositor.active_win->origin.y = Compositor.active_win->y; Compositor.active_win->origin.width = Compositor.active_win->width; Compositor.active_win->origin.height = Compositor.active_win->height; Compositor.active_win->origin.mouse_x = Mouse.x; Compositor.active_win->origin.mouse_y = Mouse.y; Compositor.in_resize = TRUE; } } if (!Mouse.left) { Compositor.in_drag = FALSE; Compositor.in_resize = FALSE; } if (Compositor.in_resize) { // FIXME: Set minimum width and height in Compositor.theme or Window. new_width = Max(16, Compositor.active_win->origin.width + (Mouse.x - Compositor.active_win->origin.mouse_x)); new_height = Max(16, Compositor.active_win->origin.height + (Mouse.y - Compositor.active_win->origin.mouse_y)); new_width = Max(Compositor.theme.window.min_width, new_width); new_height = Max(Compositor.theme.window.min_height, new_height); if (Compositor.active_win->width != new_width || Compositor.active_win->height != new_height && !Compositor.active_win->repainting) { Window* win = Compositor.active_win; IpcMessage* msg = CAlloc(sizeof(IpcMessage)); System.Log(Fs, "Sent message → WindowResizeTo (%dx%d at %d, %d) to (%dx%d)", Compositor.active_win->width, Compositor.active_win->height, Compositor.active_win->x, Compositor.active_win->y, new_width, new_height); if (win->signature != WIN_SIGNATURE) return; win->width = new_width; win->height = new_height; msg->client = Compositor.active_win->client; msg->type = CPZ_MSG_WIN_RESIZE_TO; msg->payload = win; Ipc.MsgSend(msg->client, msg); } } resize_set_pointer: // FIXME: Move this to @compositor_set_pointer_for_window() or somewhere more // appropriate? if (!set_pointer_to_resize) { if (Compositor.active_win) if (@gui_window_is_hovered(Compositor.active_win)) if (Compositor.active_win->hovered_widget) if (Compositor.active_win->hovered_widget->pointer) @compositor_set_pointer( Compositor.active_win->hovered_widget->pointer); else { if (Compositor.active_win->pointer) @compositor_set_pointer(Compositor.active_win->pointer); else @compositor_set_pointer(); } else { if (Compositor.active_win->pointer) @compositor_set_pointer(Compositor.active_win->pointer); else @compositor_set_pointer(); } else @compositor_set_pointer(); else @compositor_set_pointer(); } } U0 @compositor_handle_window_drag() { if (!(@compositor_active_win_flag_is_set(WIN_FLAGS_MOVABLE))) return; I64 title_length; I64 title_offset; I64 new_x; I64 new_y; // FIXME: Get title_length and title_offset values from theme and Window // flags. title_offset = 0; title_length = Compositor.active_win->width; if (@compositor_active_win_flag_is_set(WIN_FLAGS_TITLE_BAR)) { title_offset = Compositor.active_win->title_bar_x; title_length = Compositor.active_win->title_bar_width; } if (Mouse.left && !Compositor.mouse.left && Compositor.active_win) { if (Mouse.x > Compositor.active_win->x + title_offset && Mouse.x < Compositor.active_win->x + title_offset + title_length && Mouse.y > Compositor.active_win->y && Mouse.y < Compositor.active_win->y + 18) { Compositor.active_win->origin.x = Compositor.active_win->x; Compositor.active_win->origin.y = Compositor.active_win->y; Compositor.active_win->origin.width = Compositor.active_win->width; Compositor.active_win->origin.height = Compositor.active_win->height; Compositor.active_win->origin.mouse_x = Mouse.x; Compositor.active_win->origin.mouse_y = Mouse.y; Compositor.in_drag = TRUE; } } if (!Mouse.left) { Compositor.in_drag = FALSE; Compositor.in_resize = FALSE; } if (Compositor.in_drag) { new_x = Compositor.active_win->origin.x + (Mouse.x - Compositor.active_win->origin.mouse_x); new_y = Compositor.active_win->origin.y + (Mouse.y - Compositor.active_win->origin.mouse_y); if (Compositor.active_win->x != new_x || Compositor.active_win->y != new_y) { Window* win = Compositor.active_win; IpcMessage* msg = CAlloc(sizeof(IpcMessage)); System.Log( Fs, "Sent message → WindowMoveTo (%dx%d at %d, %d) to (%d, %d)", Compositor.active_win->width, Compositor.active_win->height, Compositor.active_win->x, Compositor.active_win->y, new_x, new_y); win->x = Min(Compositor.bounds.x2, Max(Compositor.bounds.x1, new_x)); win->y = Min(Compositor.bounds.y2, Max(Compositor.bounds.y1, new_y)); msg->client = Compositor.active_win->client; msg->type = CPZ_MSG_WIN_MOVE_TO; msg->payload = win; Ipc.MsgSend(msg->client, msg); } } } U0 @compositor_handle_window_select() { Window* win; @compositor_windows_list* win_list; if (Mouse.left && !Compositor.mouse.left) { if (((Mouse.x < Compositor.active_win->x || Mouse.x > Compositor.active_win->x + Compositor.active_win->width) || (Mouse.y < Compositor.active_win->y || Mouse.y > Compositor.active_win->y + Compositor.active_win->height)) || @compositor_active_win_flag_is_set(WIN_FLAGS_NO_REINDEX)) { win_list = Compositor.windows->next; while (win_list->next) { win_list = win_list->next; } while (win_list) { if (Mouse.x > win_list->window->x && Mouse.x < win_list->window->x + win_list->window->width && Mouse.y > win_list->window->y && Mouse.y < win_list->window->y + win_list->window->height) { if (Compositor.active_win != win_list->window && !(@gui_window_flag_is_set(win_list->window, WIN_FLAGS_MINIMIZED)) && !(@gui_window_flag_is_set(win_list->window, WIN_FLAGS_HIDDEN))) { @compositor_set_active_window(win_list->window); return; } } win_list = win_list->prev; } } } } U0 @compositor_handle_global_input_events() { // FIXME: Handle registered global input events IpcMessage* msg; I64 mouse_x; I64 mouse_y; I64 type = NULL; Bool mouse_left = Mouse.left; Bool mouse_right = Mouse.right; I64 key = Keyboard.active_key; I64 tS = Keyboard.active_key_tS; if (key && tS != Keyboard.last_key_tS) { @compositor_windows_list* win_list = Compositor.global_input_event_listeners->next; while (win_list) { msg = CAlloc(sizeof(IpcMessage)); System.Log(Fs, "Sent message → WinKeyPress [%08x] to window 0x%08x", key, win_list->window); msg->client = win_list->window->client; msg->type = CPZ_MSG_WIN_KEY_PRESS; msg->payload = win_list->window; msg->i64 = key; Ipc.MsgSend(msg->client, msg); win_list = win_list->next; } } } U0 @compositor_register_global_input_event_listener(Window* win) { @compositor_add_global_input_event_listener_to_list(win); } U0 @compositor_unregister_global_input_event_listener(Window* win) { @compositor_remove_global_input_event_listener_from_list(win); } U0 @compositor_handle_window_input_events(Window* win) { if (!win) return; IpcMessage* msg; I64 mouse_x; I64 mouse_y; I64 type = NULL; Bool mouse_left = Mouse.left; Bool mouse_right = Mouse.right; if (win->focused_widget) { if (win->focused_widget->type == WIDGET_TYPE_INPUT) { if (@widget_input_handle_key(win->focused_widget)) { msg = CAlloc(sizeof(IpcMessage)); System.Log(Fs, "Sent message → WinKeyPress"); msg->client = win->client; msg->type = CPZ_MSG_WIN_KEY_PRESS; msg->payload = win; Ipc.MsgSend(msg->client, msg); } if (win->focused_widget(BitmapFontTextInputWidget*) ->selected_region_start == -1 || win->focused_widget(BitmapFontTextInputWidget*) ->selected_region_end == -1) { if (win->focused_widget(BitmapFontTextInputWidget*)->blink != Blink) { win->focused_widget(BitmapFontTextInputWidget*)->blink = !win->focused_widget(BitmapFontTextInputWidget*)->blink; @compositor_refresh(win); } } } } if (Mouse.z != Compositor.mouse.delta_z) { msg = CAlloc(sizeof(IpcMessage)); System.Log(Fs, "Sent message → WindowMouseWheel"); msg->client = win->client; msg->type = CPZ_MSG_WIN_MOUSE_WHEEL; msg->payload = win; Ipc.MsgSend(msg->client, msg); } if (Mouse.x != Compositor.mouse.x || Mouse.y != Compositor.mouse.y || Mouse.left != Compositor.mouse.left || Mouse.right != Compositor.mouse.right) { if (Mouse.x != Compositor.mouse.x || Mouse.y != Compositor.mouse.y) { mouse_x = Mouse.x - win->x; mouse_y = Mouse.y - win->y; if (Mouse.x >= win->x && Mouse.x <= win->x + win->width && Mouse.y >= win->y && Mouse.y <= win->y + win->height && (mouse_x != win->mouse.x || mouse_y != win->mouse.y)) { win->mouse.x = mouse_x; win->mouse.y = mouse_y; msg = CAlloc(sizeof(IpcMessage)); System.Log(Fs, "Sent message → WindowMouseAt (%dx%d)", mouse_x, mouse_y); msg->client = win->client; msg->type = CPZ_MSG_WIN_MOUSE_AT; msg->payload = win; Ipc.MsgSend(msg->client, msg); } } if (mouse_left != win->mouse.left) { switch (mouse_left) { case MS_UP: System.Log(Fs, "Sent message → WindowMouseLeftBtnUp"); type = CPZ_MSG_WIN_LEFT_BTN_UP; break; case MS_DOWN: System.Log(Fs, "Sent message → WindowMouseLeftBtnDown"); type = CPZ_MSG_WIN_LEFT_BTN_DOWN; break; default: break; } win->mouse.left = mouse_left; win->left_btn_down.x = Mouse.x - win->x; win->left_btn_down.y = Mouse.y - win->y; msg = CAlloc(sizeof(IpcMessage)); msg->client = win->client; msg->type = type; msg->payload = win; Ipc.MsgSend(msg->client, msg); } if (mouse_right != win->mouse.right) { switch (mouse_right) { case MS_UP: System.Log(Fs, "Sent message → WindowMouseRightBtnUp"); type = CPZ_MSG_WIN_RIGHT_BTN_UP; break; case MS_DOWN: System.Log(Fs, "Sent message → WindowMouseRightBtnDown"); type = CPZ_MSG_WIN_RIGHT_BTN_DOWN; break; default: break; } win->mouse.right = mouse_right; win->right_btn_down.x = Mouse.x - win->x; win->right_btn_down.y = Mouse.y - win->y; msg = CAlloc(sizeof(IpcMessage)); msg->client = win->client; msg->type = type; msg->payload = win; Ipc.MsgSend(msg->client, msg); } } } U0 @compositor_handle_windows_input_events() { if (Compositor.active_win) @compositor_handle_window_input_events(Compositor.active_win); @compositor_windows_list* win_list = Compositor.windows; Compositor.max_z_index = 0; while (win_list) { if (win_list->window) { if (win_list->window->flags & WIN_FLAGS_MENU && win_list->window != Compositor.active_win && Gui.Window.IsVisible(win_list->window)) @compositor_handle_window_input_events(win_list->window); } Compositor.max_z_index++; win_list = win_list->next; } } U0 @compositor_reset_input_event_timestamps() { Keyboard.last_key_tS = Keyboard.active_key_tS; } Window* @compositor_create_window(I64 x, I64 y, I64 width, I64 height, I64 flags = WIN_FLAGS_DEFAULT, U8* title = NULL, Context2D* icon = NULL) { Window* win = CAlloc(sizeof(Window)); IpcMessage* msg = CAlloc(sizeof(IpcMessage)); win->x = x; win->y = y; win->width = width; win->height = height; win->opacity = 255; win->flags = flags; win->icon = icon; win->client = Fs; win->signature = WIN_SIGNATURE; win->widget = CAlloc(sizeof(@window_widgets_list)); win->callback.close = &@gui_window_callback_close; win->callback.maximize = &@gui_window_callback_maximize; win->callback.minimize = &@gui_window_callback_minimize; win->callback.repaint = NULL; if (title) StrCpy(&win->title, title); else StrCpy(&win->title, "Untitled window"); msg->client = Fs; msg->type = CPZ_MSG_WIN_CREATE; msg->payload = win; Ipc.MsgSend(Compositor.task, msg); while (!win->backing_store) { Sleep(1); }; return win; } U0 @compositor_destroy_window(Window* win) { IpcMessage* msg = CAlloc(sizeof(IpcMessage)); msg->client = Fs; msg->type = CPZ_MSG_WIN_DESTROY; msg->payload = win; Ipc.MsgSend(Compositor.task, msg); } U0 @compositor_hide_window(Window* win) { @gui_window_hide(win); Compositor.active_win = NULL; } U0 @compositor_show_window(Window* win) { @gui_window_show(win); } Context2D* @compositor_win_select_backing_store(Window* win) { I64 iterations = 0; while (Compositor.in_resize && win == Compositor.active_win && (win->width != win->backing_store->width || win->height != win->backing_store->height) && iterations < 64) { iterations++; if (!(iterations % 8)) @compositor_refresh(win); Sleep(1); } return win->backing_store; } U0 @compositor_blit_window_backing_stores() { @compositor_windows_list* win_list; win_list = Compositor.windows; while (win_list) { if (win_list->window) { if (!(@gui_window_flag_is_set(win_list->window, WIN_FLAGS_MINIMIZED)) && !(@gui_window_flag_is_set(win_list->window, WIN_FLAGS_HIDDEN)) && win_list->window->signature == WIN_SIGNATURE) { if (@compositor_win_select_backing_store(win_list->window) == win_list->window->resize_ctx) { CopyRect2D(Compositor.ctx, win_list->window->x, win_list->window->y, win_list->window->resize_ctx); } else { if (win_list->window->alpha) { Compositor.blend_ctx->width = win_list->window->width; Compositor.blend_ctx->height = win_list->window->height; CopyRect2D(Compositor.blend_ctx, -win_list->window->x, -win_list->window->y, Compositor.ctx); BlendRect2D(win_list->window->backing_store, Compositor.blend_ctx); CopyRect2D(Compositor.ctx, win_list->window->x, win_list->window->y, Compositor.blend_ctx); } else { CopyRect2D(Compositor.ctx, win_list->window->x, win_list->window->y, win_list->window->backing_store); } } if (win_list->window == Compositor.active_win && !(@gui_window_flag_is_set(win_list->window, WIN_FLAGS_NOHILIGHT))) { // Top border Line2D(Compositor.ctx, win_list->window->x - 1, win_list->window->y - 1, win_list->window->x - 1 + win_list->window->width + 2, win_list->window->y - 1, Compositor.theme.color.active_border); // Left border Line2D(Compositor.ctx, win_list->window->x - 1, win_list->window->y - 1, win_list->window->x - 1, win_list->window->y + win_list->window->height, Compositor.theme.color.active_border); // Bottom border Line2D(Compositor.ctx, win_list->window->x - 1, win_list->window->y + win_list->window->height, win_list->window->x - 1 + win_list->window->width + 2, win_list->window->y + win_list->window->height, Compositor.theme.color.active_border); // Right border Line2D(Compositor.ctx, win_list->window->x - 1 + win_list->window->width + 1, win_list->window->y - 1, win_list->window->x - 1 + win_list->window->width + 1, win_list->window->y + win_list->window->height, Compositor.theme.color.active_border); // Bottom shadow Line2D(Compositor.ctx, win_list->window->x + 1, win_list->window->y + win_list->window->height + 1, win_list->window->x + win_list->window->width + 2, win_list->window->y + win_list->window->height + 1, Compositor.theme.color.active_border); // Right shadow Line2D(Compositor.ctx, win_list->window->x - 1 + win_list->window->width + 2, win_list->window->y + 1, win_list->window->x - 1 + win_list->window->width + 2, win_list->window->y + win_list->window->height + 1, Compositor.theme.color.active_border); } } } win_list = win_list->next; } } U0 @compositor_init() { Compositor.blend_ctx = NewContext2D(Display.Width(), Display.Height()); Compositor.ctx = NewContext2D(Display.Width(), Display.Height()); Compositor.windows = CAlloc(sizeof(@compositor_windows_list)); Compositor.global_input_event_listeners = CAlloc(sizeof(@compositor_windows_list)); Compositor.bounds.x1 = -Display.Width(); Compositor.bounds.y1 = 33; Compositor.bounds.x2 = Display.Width(); Compositor.bounds.y2 = Display.Height(); Compositor.active_win = NULL; Compositor.menubar.win = NULL; Compositor.menubar.task = NULL; Compositor.menubar.title = NULL; Compositor.next_id = 0; Compositor.session.user.uid = 1; StrCpy(&Compositor.session.home, "/home/alec"); StrCpy(&Compositor.session.hostname, "erythros"); StrCpy(&Compositor.session.user.name, "alec"); StrCpy(&Compositor.session.user.fullname, "Alec Murphy"); @compositor_set_theme("Umami"); @compositor_set_pointer(); } U0 @compositor_task() { Window* win; Ipc.InitQueue(Fs); Compositor.task = Fs; System.Log(Fs, "Task running at 0x%08x", Fs); I64 total_mem; I64 free_mem; while (1) { // Process client IpcMessages. @compositor_ipc_queue_process(); // Handle active window flags @compositor_handle_active_window_flags(); // Handle window resize @compositor_handle_window_resize(); // Handle window drag @compositor_handle_window_drag(); // Handle window select @compositor_handle_window_select(); // Handle global input events @compositor_handle_global_input_events(); // Handle windows input events @compositor_handle_windows_input_events(); // Reset input event timestamps @compositor_reset_input_event_timestamps(); // Blit window backing stores @compositor_blit_window_backing_stores(); // Draw Mouse Pointer (if no mouse integration). // if (!Mouse.Update || Mouse.integration_type == MI_QEMU) Compositor.ctx->blot(Mouse.X(), Mouse.Y(), Compositor.pointer); // Debug stuff I64 debug_row; if (KeyDown(SC_F12)) { debug_row = 32; total_mem = sys_code_bp->alloced_u8s; if (sys_data_bp) { total_mem += sys_data_bp->alloced_u8s; } free_mem = sys_code_bp->alloced_u8s - sys_code_bp->used_u8s; if (sys_data_bp) { free_mem += sys_data_bp->alloced_u8s - sys_data_bp->used_u8s; } ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , "SysTimerRead : 0x%08x", SysTimerRead); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , "Total Memory : 0x%08x", total_mem); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , "Free Memory : 0x%08x", free_mem); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , "Mouse.X() : %d", Mouse.X()); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , "Mouse.Y() : %d", Mouse.Y()); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , "Mouse.Z() : %d", Mouse.z); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , "Compositor.mouse.z : %d", Compositor.mouse.z); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , "Mouse.left() : %d", Compositor.mouse.left > 0); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , "Mouse.right() : %d", Compositor.mouse.right > 0); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , "Active window: 0x%08x", Compositor.active_win); if (Compositor.active_win) { ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , "[+] x : %d", Compositor.active_win->x); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , " y : %d", Compositor.active_win->y); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , " width : %d", Compositor.active_win->width); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , " height : %d", Compositor.active_win->height); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , " title : %s", Compositor.active_win->title); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , " flags : %016b", Compositor.active_win->flags); ConsolePrint2D(Compositor.ctx, 08 * 03, 16 * debug_row++, , , " opacity : %d", Compositor.active_win->opacity); } } // Flip off-screen context to framebuffer. Graphics2D.Flip(Compositor.ctx); // Update Compositor mouse button state Compositor.mouse.x = Mouse.x; Compositor.mouse.y = Mouse.y; if (Compositor.mouse.delta_z != Mouse.z) Compositor.mouse.z = (Mouse.z - Compositor.mouse.delta_z) * T(Mouse.natural_scroll, -Mouse.wheel_sensitivity, Mouse.wheel_sensitivity); Compositor.mouse.delta_z = Mouse.z; Compositor.mouse.left = Mouse.left; Compositor.mouse.right = Mouse.right; if (Display.Update) Display.Update(); Sleep(1); } } Gui.Window.Refresh = &@compositor_refresh; Gui.Window.SetFocus = &@compositor_set_active_window; Gui.Window.SetZIndex = &@compositor_set_z_index_send_msg; Compositor.GetWindowByTitle = &@compositor_get_window_by_title; Compositor.GetWindowByZIndex = &@compositor_get_window_by_z_index; Compositor.Init = &@compositor_init; Compositor.CreateWindow = &@compositor_create_window; Compositor.DestroyWindow = &@compositor_destroy_window; Compositor.HideWindow = &@compositor_hide_window; Compositor.RegisterForGlobalInputEvents = &@compositor_register_global_input_event_listener; Compositor.ShowWindow = &@compositor_show_window; Compositor.Task = &@compositor_task; Compositor.UnregisterForGlobalInputEvents = &@compositor_unregister_global_input_event_listener; Compositor.SetWallpaper = &@compositor_set_wallpaper_send_msg; "compositor ";