#include "wm.h" #include "graphics.h" #include "io.h" #include "cmd.h" #include "calculator.h" #include "cli_apps/cli_utils.h" #include "explorer.h" #include "editor.h" #include "markdown.h" #include #include #include "notepad.h" #include "viewer.h" #include "wallpaper.h" #include "control_panel.h" #include "about.h" #include "minesweeper.h" #include "fat32.h" #include "memory_manager.h" #include "paint.h" #include "disk.h" // --- State --- static int mx = 400, my = 300; // Mouse Pos static int prev_mx = 400, prev_my = 300; // Previous mouse position static bool start_menu_open = false; static char *start_menu_pending_app = NULL; // For click vs drag detection static int pending_desktop_icon_click = -1; // For desktop icon click vs drag // Desktop Context Menu static bool desktop_menu_visible = false; static int desktop_menu_x = 0; static int desktop_menu_y = 0; static int desktop_menu_target_icon = -1; // -1 for background // Desktop Dialog State static int desktop_dialog_state = 0; // 0=None, 8=Rename static char desktop_dialog_input[64]; static int desktop_dialog_cursor = 0; static int desktop_dialog_target = -1; // Message Box static bool msg_box_visible = false; static char msg_box_title[64]; static char msg_box_text[64]; // Hook definition void (*wm_custom_paint_hook)(void) = NULL; // Dragging State static bool is_dragging = false; static Window *drag_window = NULL; static int drag_offset_x = 0; static int drag_offset_y = 0; // File Dragging State bool is_dragging_file = false; static char drag_file_path[256]; static int drag_icon_type = 0; // 0=File, 1=Folder, 2=App static int drag_start_x = 0; static int drag_start_y = 0; static int drag_icon_orig_x = 0; static int drag_icon_orig_y = 0; static Window *drag_src_win = NULL; // Windows array for z-order management static Window *all_windows[32]; static int window_count = 0; // Redraw system static bool force_redraw = true; // Force full redraw on next tick static uint32_t timer_ticks = 0; static int desktop_refresh_timer = 0; // Cursor state static bool cursor_visible = true; static int last_cursor_x = 400; static int last_cursor_y = 300; // --- Desktop State --- #define MAX_DESKTOP_ICONS 32 typedef struct { char name[64]; int x, y; int type; // 0=File, 1=Folder, 2=App } DesktopIcon; static DesktopIcon desktop_icons[MAX_DESKTOP_ICONS]; static int desktop_icon_count = 0; // Desktop Settings bool desktop_snap_to_grid = true; bool desktop_auto_align = true; int desktop_max_rows_per_col = 13; int desktop_max_cols = 23; // Mouse Settings int mouse_speed = 10; // Default 1.0x (range 1-50) static int mouse_accum_x = 0; static int mouse_accum_y = 0; // Helper to check if string ends with suffix static bool str_ends_with(const char *str, const char *suffix) { int str_len = 0; while(str[str_len]) str_len++; int suf_len = 0; while(suffix[suf_len]) suf_len++; if (suf_len > str_len) return false; for (int i = 0; i < suf_len; i++) { if (str[str_len - suf_len + i] != suffix[i]) return false; } return true; } // Helper to check if string starts with prefix static bool str_starts_with(const char *str, const char *prefix) { while(*prefix) { if (*prefix++ != *str++) return false; } return true; } static int str_eq(const char *s1, const char *s2) { while (*s1 && (*s1 == *s2)) { s1++; s2++; } return *(const unsigned char*)s1 - *(const unsigned char*)s2; } static void refresh_desktop_icons(void) { // Update limit in FS fat32_set_desktop_limit(desktop_max_cols * desktop_max_rows_per_col); FAT32_FileInfo *files = (FAT32_FileInfo*)kmalloc(MAX_DESKTOP_ICONS * sizeof(FAT32_FileInfo)); if (!files) return; int file_count = fat32_list_directory("/Desktop", files, MAX_DESKTOP_ICONS); // Temp array to hold new state DesktopIcon new_icons[MAX_DESKTOP_ICONS]; int new_count = 0; bool file_processed[MAX_DESKTOP_ICONS]; for(int i=0; i= MAX_DESKTOP_ICONS) break; DesktopIcon *dest = &new_icons[new_count]; int k = 0; while(files[i].name[k] && k < 63) { dest->name[k] = files[i].name[k]; k++; } dest->name[k] = 0; if (files[i].is_directory) dest->type = 1; else if (str_ends_with(dest->name, ".shortcut")) dest->type = 2; else dest->type = 0; dest->x = -1; // Mark as new for layout dest->y = -1; new_count++; } } desktop_icon_count = new_count; for(int i=0; i= desktop_max_rows_per_col) { grid_y = 0; grid_x++; } } } else { // Place new icons in first available spot bool occupied[16][16] = {false}; for (int i = 0; i < desktop_icon_count; i++) { if (desktop_icons[i].x != -1) { int col = (desktop_icons[i].x - 20) / 80; int row = (desktop_icons[i].y - 20) / 80; if (col >= 0 && col < 16 && row >= 0 && row < 16) occupied[col][row] = true; } } for (int i = 0; i < desktop_icon_count; i++) { if (desktop_icons[i].x == -1) { int found_col = -1, found_row = -1; for (int c = 0; c < 16; c++) { for (int r = 0; r < desktop_max_rows_per_col; r++) { if (!occupied[c][r]) { found_col = c; found_row = r; goto found; } } } found: if (found_col != -1) { desktop_icons[i].x = 20 + found_col * 80; desktop_icons[i].y = 20 + found_row * 80; occupied[found_col][found_row] = true; } } } } } void wm_refresh_desktop(void) { refresh_desktop_icons(); force_redraw = true; } static void create_desktop_shortcut(const char *app_name) { char path[128] = "/Desktop/"; int p = 9; int n = 0; while(app_name[n]) path[p++] = app_name[n++]; const char *ext = ".shortcut"; int e = 0; while(ext[e]) path[p++] = ext[e++]; path[p] = 0; FAT32_FileHandle *fh = fat32_open(path, "w"); if (fh) fat32_close(fh); refresh_desktop_icons(); } int wm_get_desktop_icon_count(void) { return desktop_icon_count; } void wm_show_message(const char *title, const char *message) { int i=0; while(title[i] && i<63) { msg_box_title[i] = title[i]; i++; } msg_box_title[i] = 0; i=0; while(message[i] && i<63) { msg_box_text[i] = message[i]; i++; } msg_box_text[i] = 0; msg_box_visible = true; force_redraw = true; } static void draw_icon_label(int x, int y, const char *label) { char line1[11] = {0}; // 10 chars + null char line2[11] = {0}; // 10 chars + null int len = 0; while(label[len]) len++; if (len <= 10) { for (int i = 0; i < len; i++) line1[i] = label[i]; } else { // Dot-based wrap: keep extension together if prefix fits int dot_pos = -1; for (int i = len - 1; i >= 0; i--) { if (label[i] == '.') { dot_pos = i; break; } } int split = -1; if (dot_pos != -1 && dot_pos > 0 && dot_pos <= 10) { split = dot_pos; } else { // Word-based wrap: look for space in the first 11 characters for (int i = 10; i >= 0; i--) { if (label[i] == ' ') { split = i; break; } } } if (split != -1) { for (int i = 0; i < split; i++) line1[i] = label[i]; int start2 = (label[split] == ' ') ? split + 1 : split; int j = 0; while (label[start2 + j] && j < 10) { line2[j] = label[start2 + j]; j++; } if (label[start2 + j] != 0) { int t = (j > 8) ? 8 : j; line2[t] = '.'; line2[t+1] = '.'; line2[t+2] = 0; } } else { for (int i = 0; i < 10; i++) line1[i] = label[i]; int j = 0; while (label[10 + j] && j < 10) { line2[j] = label[10 + j]; j++; } if (label[10 + j] != 0) { int t = (j > 8) ? 8 : j; line2[t] = '.'; line2[t+1] = '.'; line2[t+2] = 0; } } } // Draw Line 1 Centered in 80px cell int l1_len = 0; while(line1[l1_len]) l1_len++; int l1_w = l1_len * 8; // x passed is cell left. Center is x + 40. Text start is x + 40 - w/2 draw_string(x + (80 - l1_w)/2, y + 30, line1, COLOR_WHITE); // Draw Line 2 Centered if (line2[0]) { int l2_len = 0; while(line2[l2_len]) l2_len++; int l2_w = l2_len * 8; draw_string(x + (80 - l2_w)/2, y + 40, line2, COLOR_WHITE); } } // --- Drawing Helpers --- void draw_bevel_rect(int x, int y, int w, int h, bool sunken) { draw_rect(x, y, w, h, COLOR_GRAY); uint32_t top_left = sunken ? COLOR_DKGRAY : COLOR_WHITE; uint32_t bot_right = sunken ? COLOR_WHITE : COLOR_DKGRAY; // Top draw_rect(x, y, w, 1, top_left); // Left draw_rect(x, y, 1, h, top_left); // Bottom draw_rect(x, y + h - 1, w, 1, bot_right); // Right draw_rect(x + w - 1, y, 1, h, bot_right); } void draw_button(int x, int y, int w, int h, const char *text, bool pressed) { draw_bevel_rect(x, y, w, h, pressed); // Center Text int len = 0; while(text[len]) len++; int tx = x + (w - (len * 8)) / 2; int ty = y + (h - 8) / 2; if (pressed) { tx++; ty++; } draw_string(tx, ty, text, COLOR_BLACK); } void draw_icon(int x, int y, const char *label) { // Simple "File" Icon draw_rect(x + 29, y, 20, 25, COLOR_WHITE); draw_rect(x + 29, y, 20, 1, COLOR_BLACK); draw_rect(x + 29, y, 1, 25, COLOR_BLACK); draw_rect(x + 49, y, 1, 25, COLOR_BLACK); draw_rect(x + 29, y + 25, 21, 1, COLOR_BLACK); // Label draw_icon_label(x, y, label); } void draw_folder_icon(int x, int y, const char *label) { // Folder icon (yellow folder) // Folder tab draw_rect(x + 27, y, 15, 6, COLOR_LTGRAY); draw_rect(x + 27, y, 15, 1, COLOR_BLACK); draw_rect(x + 27, y, 1, 6, COLOR_BLACK); draw_rect(x + 41, y, 1, 6, COLOR_BLACK); // Folder body draw_rect(x + 27, y + 6, 25, 15, COLOR_APPLE_YELLOW); draw_rect(x + 27, y + 6, 25, 1, COLOR_BLACK); draw_rect(x + 27, y + 6, 1, 15, COLOR_BLACK); draw_rect(x + 51, y + 6, 1, 15, COLOR_BLACK); draw_rect(x + 27, y + 20, 25, 1, COLOR_BLACK); // Label draw_icon_label(x, y, label); } void draw_document_icon(int x, int y, const char *label) { // Document icon (white paper with lines) draw_rect(x + 29, y, 20, 25, COLOR_WHITE); draw_rect(x + 29, y, 20, 1, COLOR_BLACK); draw_rect(x + 29, y, 1, 25, COLOR_BLACK); draw_rect(x + 49, y, 1, 25, COLOR_BLACK); draw_rect(x + 29, y + 25, 21, 1, COLOR_BLACK); // Lines on document draw_rect(x + 33, y + 8, 12, 1, COLOR_BLACK); draw_rect(x + 33, y + 12, 12, 1, COLOR_BLACK); draw_rect(x + 33, y + 16, 12, 1, COLOR_BLACK); // Label draw_icon_label(x, y, label); } void draw_notepad_icon(int x, int y, const char *label) { // Notepad icon (Blue notebook) draw_rect(x + 29, y, 20, 25, COLOR_BLUE); draw_rect(x + 29, y, 20, 1, COLOR_BLACK); draw_rect(x + 29, y, 1, 25, COLOR_BLACK); draw_rect(x + 49, y, 1, 25, COLOR_BLACK); draw_rect(x + 29, y + 25, 21, 1, COLOR_BLACK); // White page inside draw_rect(x + 31, y + 2, 17, 22, COLOR_WHITE); // Lines draw_rect(x + 33, y + 6, 13, 1, COLOR_GRAY); draw_rect(x + 33, y + 10, 13, 1, COLOR_GRAY); draw_rect(x + 33, y + 14, 13, 1, COLOR_GRAY); draw_icon_label(x, y, label); } void draw_calculator_icon(int x, int y, const char *label) { // Calculator icon draw_rect(x + 29, y, 20, 25, COLOR_DKGRAY); draw_rect(x + 29, y, 20, 1, COLOR_BLACK); draw_rect(x + 29, y, 1, 25, COLOR_BLACK); draw_rect(x + 49, y, 1, 25, COLOR_BLACK); draw_rect(x + 29, y + 25, 21, 1, COLOR_BLACK); // Screen draw_rect(x + 32, y + 3, 14, 6, COLOR_APPLE_GREEN); // Buttons for(int r=0; r<3; r++) { for(int c=0; c<3; c++) { draw_rect(x + 32 + c*5, y + 12 + r*4, 3, 2, COLOR_WHITE); } } draw_icon_label(x, y, label); } void draw_terminal_icon(int x, int y, const char *label) { // Terminal icon draw_rect(x + 27, y + 2, 24, 20, COLOR_BLACK); draw_rect(x + 27, y + 2, 24, 1, COLOR_GRAY); draw_rect(x + 27, y + 2, 1, 20, COLOR_GRAY); draw_rect(x + 51, y + 2, 1, 20, COLOR_GRAY); draw_rect(x + 27, y + 22, 25, 1, COLOR_GRAY); // Prompt draw_rect(x + 31, y + 6, 4, 1, COLOR_APPLE_GREEN); // > draw_rect(x + 32, y + 7, 2, 1, COLOR_APPLE_GREEN); draw_rect(x + 31, y + 8, 4, 1, COLOR_APPLE_GREEN); draw_rect(x + 37, y + 6, 6, 1, COLOR_APPLE_GREEN); // _ draw_icon_label(x, y, label); } void draw_minesweeper_icon(int x, int y, const char *label) { // Mine icon draw_rect(x + 29, y, 20, 25, COLOR_LTGRAY); draw_rect(x + 29, y, 20, 1, COLOR_BLACK); draw_rect(x + 29, y, 1, 25, COLOR_BLACK); draw_rect(x + 49, y, 1, 25, COLOR_BLACK); draw_rect(x + 29, y + 25, 21, 1, COLOR_BLACK); // Mine draw_rect(x + 36, y + 8, 6, 8, COLOR_BLACK); draw_rect(x + 34, y + 10, 10, 4, COLOR_BLACK); // Spikes draw_rect(x + 39, y + 6, 1, 12, COLOR_BLACK); draw_rect(x + 33, y + 12, 12, 1, COLOR_BLACK); draw_icon_label(x, y, label); } void draw_control_panel_icon(int x, int y, const char *label) { // Control Panel (Gear/Sliders) draw_rect(x + 29, y, 20, 25, COLOR_GRAY); draw_rect(x + 29, y, 20, 1, COLOR_BLACK); draw_rect(x + 29, y, 1, 25, COLOR_BLACK); draw_rect(x + 49, y, 1, 25, COLOR_BLACK); draw_rect(x + 29, y + 25, 21, 1, COLOR_BLACK); // Sliders draw_rect(x + 34, y + 5, 2, 15, COLOR_DKGRAY); draw_rect(x + 33, y + 10, 4, 3, COLOR_WHITE); // Knob draw_rect(x + 42, y + 5, 2, 15, COLOR_DKGRAY); draw_rect(x + 41, y + 16, 4, 3, COLOR_WHITE); // Knob draw_icon_label(x, y, label); } void draw_about_icon(int x, int y, const char *label) { // About icon (Info) draw_rect(x + 29, y, 20, 25, COLOR_WHITE); draw_rect(x + 29, y, 20, 1, COLOR_BLACK); draw_rect(x + 29, y, 1, 25, COLOR_BLACK); draw_rect(x + 49, y, 1, 25, COLOR_BLACK); draw_rect(x + 29, y + 25, 21, 1, COLOR_BLACK); // 'i' draw_rect(x + 38, y + 5, 3, 3, COLOR_BLUE); // Dot draw_rect(x + 38, y + 10, 3, 10, COLOR_BLUE); // Body draw_icon_label(x, y, label); } void draw_recycle_bin_icon(int x, int y, const char *label) { // Recycle Bin (Trash can) draw_rect(x + 29, y, 20, 25, COLOR_LTGRAY); draw_rect(x + 29, y, 20, 1, COLOR_BLACK); draw_rect(x + 29, y, 1, 25, COLOR_BLACK); draw_rect(x + 49, y, 1, 25, COLOR_BLACK); draw_rect(x + 29, y + 25, 21, 1, COLOR_BLACK); // Ribs draw_rect(x + 32, y + 5, 2, 15, COLOR_DKGRAY); draw_rect(x + 38, y + 5, 2, 15, COLOR_DKGRAY); draw_rect(x + 44, y + 5, 2, 15, COLOR_DKGRAY); draw_icon_label(x, y, label); } void draw_paint_icon(int x, int y, const char *label) { // Paint Palette Icon draw_rect(x + 27, y + 2, 26, 20, COLOR_WHITE); draw_rect(x + 27, y + 2, 26, 1, COLOR_BLACK); draw_rect(x + 27, y + 2, 1, 20, COLOR_BLACK); draw_rect(x + 52, y + 2, 1, 20, COLOR_BLACK); draw_rect(x + 27, y + 22, 26, 1, COLOR_BLACK); // Color dots draw_rect(x + 30, y + 5, 4, 4, COLOR_RED); draw_rect(x + 38, y + 5, 4, 4, COLOR_APPLE_GREEN); draw_rect(x + 46, y + 5, 4, 4, COLOR_APPLE_BLUE); draw_rect(x + 30, y + 13, 4, 4, COLOR_APPLE_YELLOW); draw_rect(x + 38, y + 13, 4, 4, COLOR_PURPLE); draw_icon_label(x, y, label); } static void draw_filled_circle(int cx, int cy, int r, uint32_t color); // Draw traffic light (close button - red) void draw_traffic_light(int x, int y) { draw_filled_circle(x + 6, y + 6, 6, COLOR_TRAFFIC_RED); draw_filled_circle(x + 6, y + 6, 2, COLOR_WHITE); } // Draw a squircle-style app icon void draw_squircle_icon(int x, int y, const char *label, uint32_t bg_color) { // Simplified squircle using rounded rectangle draw_rounded_rect_filled(x + 12, y, 56, 56, 12, bg_color); draw_icon_label(x, y + 60, label); } // Files icon void draw_files_icon(int x, int y, const char *label) { draw_rounded_rect_filled(x + 27, y + 6, 25, 15, 3, 0xFF4A90E2); // Blue color draw_squircle_icon(x, y, label, 0xFF4A90E2); } // Settings/Gear icon void draw_settings_icon(int x, int y, const char *label) { // Gear icon with dark background draw_squircle_icon(x, y, label, 0xFF666666); // Simple gear shape in the middle of squircle int cx = x + 12 + 28; int cy = y + 28; draw_rect(cx - 2, cy - 2, 4, 4, COLOR_WHITE); // Center } static int isqrt_local(int n) { if (n <= 0) return 0; int x = n; int y = (x + 1) / 2; while (y < x) { x = y; y = (x + n / x) / 2; } return x; } static void draw_filled_circle(int cx, int cy, int r, uint32_t color) { for (int dy = -r; dy <= r; dy++) { int dx = isqrt_local(r * r - dy * dy); draw_rect(cx - dx, cy + dy, dx * 2 + 1, 1, color); } } static void draw_dock_files(int x, int y) { draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFF1D5FAA); draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFF4A90E2); draw_rounded_rect_filled(x + 1, y + 24, 46, 23, 9, 0xFF2E72C9); draw_rounded_rect_filled(x + 7, y + 13, 15, 7, 3, 0xFF62A8F5); draw_rounded_rect_filled(x + 8, y + 12, 13, 5, 3, 0xFF7DB8F8); draw_rounded_rect_filled(x + 5, y + 17, 38, 23, 5, 0xFFCDE4FA); draw_rounded_rect_filled(x + 7, y + 19, 34, 19, 4, 0xFFEAF5FF); draw_rect(x + 12, y + 23, 24, 2, 0xFF88B8D8); draw_rect(x + 12, y + 27, 17, 2, 0xFF88B8D8); draw_rect(x + 12, y + 31, 21, 2, 0xFF88B8D8); } static void draw_dock_settings(int x, int y) { draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFF3A3A3A); draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFF6E6E6E); draw_rounded_rect_filled(x + 1, y + 24, 46, 23, 9, 0xFF4A4A4A); int cx = x + 24, cy = y + 25; draw_rounded_rect_filled(cx - 4, cy - 18, 8, 7, 2, 0xFFCCCCCC); draw_rounded_rect_filled(cx - 4, cy + 11, 8, 7, 2, 0xFFCCCCCC); draw_rounded_rect_filled(cx - 18, cy - 4, 7, 8, 2, 0xFFCCCCCC); draw_rounded_rect_filled(cx + 11, cy - 4, 7, 8, 2, 0xFFCCCCCC); draw_rounded_rect_filled(cx - 14, cy - 14, 6, 6, 2, 0xFFCCCCCC); draw_rounded_rect_filled(cx + 8, cy - 14, 6, 6, 2, 0xFFCCCCCC); draw_rounded_rect_filled(cx - 14, cy + 8, 6, 6, 2, 0xFFCCCCCC); draw_rounded_rect_filled(cx + 8, cy + 8, 6, 6, 2, 0xFFCCCCCC); draw_filled_circle(cx, cy, 13, 0xFFCCCCCC); draw_filled_circle(cx, cy, 6, 0xFF4A4A4A); draw_filled_circle(cx, cy, 4, 0xFF3A3A3A); } static void draw_dock_notepad(int x, int y) { draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFFCC9A00); draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFFFFD700); draw_rounded_rect_filled(x + 1, y + 24, 46, 23, 9, 0xFFE8BC00); draw_rounded_rect_filled(x + 7, y + 9, 34, 32, 3, 0xFFFFFDE7); draw_rounded_rect_filled(x + 7, y + 9, 34, 8, 3, 0xFFFFCA28); draw_rect(x + 7, y + 13, 34, 4, 0xFFFFCA28); draw_rect(x + 11, y + 21, 18, 2, 0xFFBBAA70); draw_rect(x + 11, y + 25, 26, 1, 0xFFCCBB88); draw_rect(x + 11, y + 28, 22, 1, 0xFFCCBB88); draw_rect(x + 11, y + 31, 24, 1, 0xFFCCBB88); draw_rect(x + 11, y + 34, 18, 1, 0xFFCCBB88); draw_rect(x + 32, y + 11, 3, 13, 0xFFF5DEB3); draw_rect(x + 32, y + 9, 3, 4, 0xFFFF9800); draw_rect(x + 33, y + 24, 1, 2, 0xFF555555); } static void draw_dock_calculator(int x, int y) { draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFF111111); draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFF222222); draw_rounded_rect_filled(x + 1, y + 24, 46, 23, 9, 0xFF151515); draw_rounded_rect_filled(x + 6, y + 6, 36, 11, 3, 0xFF1A3A2A); draw_rect(x + 25, y + 9, 14, 5, 0xFF33FF88); // 3x3 button grid uint32_t btn_clr[3][3] = { {0xFF555555, 0xFF555555, 0xFF555555}, {0xFF444444, 0xFF444444, 0xFF444444}, {0xFF444444, 0xFF444444, 0xFFFF9500}, }; for (int row = 0; row < 3; row++) { for (int col = 0; col < 3; col++) { draw_rounded_rect_filled(x + 10 + col * 9, y + 22 + row * 6, 7, 5, 2, btn_clr[row][col]); } } } static void draw_dock_terminal(int x, int y) { draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFF0A0A0A); draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFF161616); draw_rounded_rect_filled(x + 1, y + 24, 46, 23, 9, 0xFF0D0D0D); int px = x + 12; int py = y + 24; for (int i = 0; i < 6; i++) { draw_rect(px + i, py - 4 + i, 2, 1, 0xFF33DD33); } for (int i = 0; i < 6; i++) { draw_rect(px + i, py + 4 - i, 2, 1, 0xFF33DD33); } draw_rect(px + 10, py + 7, 8, 1, 0xFF33DD33); } static void draw_dock_minesweeper(int x, int y) { draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFF1B5E20); draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFF4CAF50); draw_rounded_rect_filled(x + 1, y + 24, 46, 23, 9, 0xFF388E3C); for (int row = 0; row < 5; row++) { for (int col = 0; col < 5; col++) { int bx = x + 6 + col * 8, by = y + 7 + row * 8; if ((row == 2 && col == 2)) { draw_rounded_rect_filled(bx, by, 6, 6, 1, 0xFFC8E6C9); } else if ((row + col) % 2 == 0) { draw_rounded_rect_filled(bx, by, 6, 6, 1, 0xFF81C784); draw_rect(bx, by, 6, 1, 0xFFA5D6A7); draw_rect(bx, by, 1, 6, 0xFFA5D6A7); draw_rect(bx + 5, by + 1, 1, 5, 0xFF2E7D32); draw_rect(bx + 1, by + 5, 5, 1, 0xFF2E7D32); } else { draw_rounded_rect_filled(bx, by, 6, 6, 1, 0xFF66BB6A); draw_rect(bx, by, 6, 1, 0xFF81C784); draw_rect(bx, by, 1, 6, 0xFF81C784); } } } int mx = x + 6 + 2 * 8 + 3, my = y + 7 + 2 * 8 + 3; draw_filled_circle(mx, my, 4, 0xFF111111); draw_rect(mx - 5, my - 1, 11, 2, 0xFF111111); draw_rect(mx - 1, my - 5, 2, 11, 0xFF111111); draw_rect(mx - 3, my - 3, 2, 2, 0xFF111111); draw_rect(mx + 1, my - 3, 2, 2, 0xFF111111); draw_rect(mx - 3, my + 1, 2, 2, 0xFF111111); draw_rect(mx + 1, my + 1, 2, 2, 0xFF111111); draw_rect(mx - 1, my - 2, 2, 2, 0xFFFFFFFF); draw_rect(x + 7, y + 40, 1, 6, 0xFF333333); draw_rect(x + 8, y + 40, 4, 3, 0xFFFF3333); } static void draw_dock_paint(int x, int y) { draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFFBBBBBB); draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFFFFFFFF); draw_rounded_rect_filled(x + 1, y + 24, 46, 23, 9, 0xFFEEEEEE); draw_rounded_rect_filled(x + 6, y + 9, 36, 30, 10, 0xFFEEDDB0); draw_rounded_rect_filled(x + 8, y + 11, 32, 26, 8, 0xFFF5E8C0); draw_filled_circle(x + 35, y + 32, 5, 0xFFEEDDB0); draw_filled_circle(x + 35, y + 32, 3, 0xFFFFFFFF); draw_filled_circle(x + 15, y + 18, 5, 0xFFFF3333); draw_filled_circle(x + 23, y + 14, 5, 0xFF3399FF); draw_filled_circle(x + 31, y + 18, 5, 0xFFFFCC00); draw_filled_circle(x + 28, y + 27, 5, 0xFF33CC33); draw_filled_circle(x + 16, y + 27, 5, 0xFFFF6600); draw_rect(x + 30, y + 30, 3, 14, 0xFF8B6914); draw_rounded_rect_filled(x + 29, y + 27, 5, 5, 2, 0xFFBBBBBB); draw_rounded_rect_filled(x + 30, y + 22, 3, 7, 1, 0xFF1A1A1A); } static void draw_dock_editor(int x, int y) { draw_rounded_rect_filled(x, y, 48, 48, 10, 0xFF0A1628); draw_rounded_rect_filled(x + 1, y + 1, 46, 28, 9, 0xFF1565C0); draw_rounded_rect_filled(x + 1, y + 24, 46, 23, 9, 0xFF0D47A1); draw_rect(x + 5, y + 8, 9, 32, 0xFF1A237E); draw_filled_circle(x + 10, y + 14, 2, 0xFF7986CB); draw_filled_circle(x + 10, y + 22, 2, 0xFF7986CB); draw_filled_circle(x + 10, y + 30, 2, 0xFF7986CB); draw_rect(x + 15, y + 8, 28, 32, 0xFF1B2B3C); draw_rect(x + 15, y + 8, 14, 5, 0xFF1B2B3C); draw_rect(x + 15, y + 8, 14, 1, 0xFF569CD6); draw_rect(x + 18, y + 13, 9, 2, 0xFF569CD6); draw_rect(x + 29, y + 13, 8, 2, 0xFF4EC9B0); draw_rect(x + 18, y + 18, 5, 2, 0xFFCE9178); draw_rect(x + 25, y + 18, 7, 2, 0xFFCE9178); draw_rect(x + 21, y + 23, 7, 2, 0xFF9CDCFE); draw_rect(x + 30, y + 23, 5, 2, 0xFFD4D4D4); draw_rect(x + 18, y + 28, 16, 2, 0xFF6A9955); draw_rect(x + 18, y + 33, 10, 2, 0xFFD4D4D4); draw_rect(x + 30, y + 33, 6, 2, 0xFF569CD6); } void draw_window(Window *win) { if (!win->visible) return; // Dark mode window with rounded corners // Border/Shadow effect draw_rounded_rect_filled(win->x - 1, win->y - 1, win->w + 2, win->h + 2, 8, 0xFF000000); // Main window body (fully rounded) draw_rounded_rect_filled(win->x, win->y, win->w, win->h, 8, COLOR_DARK_PANEL); // Title Bar (rounded at top only - overdraw bottom to hide rounding) draw_rounded_rect_filled(win->x, win->y, win->w, 20, 8, COLOR_DARK_TITLEBAR); draw_rect(win->x, win->y + 12, win->w, 8, COLOR_DARK_TITLEBAR); // Cover bottom rounded corners draw_string(win->x + 28, win->y + 5, win->title, COLOR_DARK_TEXT); // Traffic Light (close button - red) draw_traffic_light(win->x + 8, win->y + 2); // Client Area with dark background, rounded only at bottom draw_rounded_rect_filled(win->x, win->y + 20, win->w, win->h - 20, 8, COLOR_DARK_BG); draw_rect(win->x, win->y + 20, win->w, 8, COLOR_DARK_BG); if (win->paint) { win->paint(win); } } // Draw Mouse Cursor (Simple Arrow) void draw_cursor(int x, int y) { // 0 = Transparent (skip), 1 = Black, 2 = White static const uint8_t cursor_bitmap[10][10] = { {1,1,0,0,0,0,0,0,0,0}, {1,2,1,0,0,0,0,0,0,0}, {1,2,2,1,0,0,0,0,0,0}, {1,2,2,2,1,0,0,0,0,0}, {1,2,2,2,2,1,0,0,0,0}, {1,2,2,2,2,2,1,0,0,0}, {1,2,2,1,1,1,1,0,0,0}, {1,1,1,0,1,2,1,0,0,0}, {0,0,0,0,0,1,2,1,0,0}, {0,0,0,0,0,0,1,0,0,0} }; for (int r = 0; r < 10; r++) { for (int c = 0; c < 10; c++) { uint8_t p = cursor_bitmap[r][c]; if (p == 1) put_pixel(x + c, y + r, COLOR_BLACK); else if (p == 2) put_pixel(x + c, y + r, COLOR_WHITE); } } } // Erase cursor by redrawing the background in that area static void erase_cursor(int x, int y) { int sw = get_screen_width(); int sh = get_screen_height(); // Clamp to screen int x1 = x < 0 ? 0 : x; int y1 = y < 0 ? 0 : y; int x2 = x + 10 > sw ? sw : x + 10; int y2 = y + 10 > sh ? sh : y + 10; int w = x2 - x1; int h = y2 - y1; // Check what's underneath the cursor and redraw it if (y1 < sh - 28) { // Desktop or window area - draw teal background draw_rect(x1, y1, w, h, COLOR_TEAL); } else { // Taskbar draw_rect(x1, y1, w, h, COLOR_GRAY); } } // --- Clock --- static uint8_t rtc_read(uint8_t reg) { outb(0x70, reg); return inb(0x71); } static void draw_clock(int x, int y) { // Wait for update in progress while (rtc_read(0x0A) & 0x80); uint8_t s = rtc_read(0x00); uint8_t m = rtc_read(0x02); uint8_t h = rtc_read(0x04); uint8_t b = rtc_read(0x0B); if (!(b & 0x04)) { s = (s & 0x0F) + ((s >> 4) * 10); m = (m & 0x0F) + ((m >> 4) * 10); h = (h & 0x0F) + ((h >> 4) * 10); } char buf[9]; buf[0] = '0' + (h / 10); buf[1] = '0' + (h % 10); buf[2] = ':'; buf[3] = '0' + (m / 10); buf[4] = '0' + (m % 10); buf[5] = ':'; buf[6] = '0' + (s / 10); buf[7] = '0' + (s % 10); buf[8] = 0; draw_string(x, y, buf, COLOR_WHITE); } // --- Main Paint Function --- void wm_paint(void) { int sw = get_screen_width(); int sh = get_screen_height(); DirtyRect dirty = graphics_get_dirty_rect(); if (dirty.active) { graphics_set_clipping(dirty.x, dirty.y, dirty.w, dirty.h); } else { graphics_clear_clipping(); } // 1. Desktop Background (respects wallpaper color/pattern) draw_desktop_background(); // Draw Desktop Icons for (int i = 0; i < desktop_icon_count; i++) { DesktopIcon *icon = &desktop_icons[i]; if (dirty.active) { if (icon->x + 80 <= dirty.x || icon->x >= dirty.x + dirty.w || icon->y + 80 <= dirty.y || icon->y >= dirty.y + dirty.h) { continue; } } if (icon->type == 1) draw_folder_icon(icon->x, icon->y, icon->name); else if (icon->type == 2) { // App icon - strip .shortcut for display char label[64]; int len = 0; while(icon->name[len] && len < 63) { label[len] = icon->name[len]; len++; } label[len] = 0; if (len > 9 && str_ends_with(label, ".shortcut")) { label[len-9] = 0; } if (str_starts_with(icon->name, "Notepad")) draw_notepad_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "Calculator")) draw_calculator_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "Terminal")) draw_terminal_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "Minesweeper")) draw_minesweeper_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "Settings")) draw_control_panel_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "About")) draw_about_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "Recycle Bin")) draw_recycle_bin_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "Files")) draw_folder_icon(icon->x, icon->y, label); else if (str_starts_with(icon->name, "Paint")) draw_paint_icon(icon->x, icon->y, label); else draw_icon(icon->x, icon->y, label); } else { if (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name); else if (str_ends_with(icon->name, ".md")) { draw_document_icon(icon->x, icon->y, icon->name); draw_string(icon->x + 31, icon->y + 2, "MD", COLOR_DARK_TEXT); } else if (str_ends_with(icon->name, ".c") || str_ends_with(icon->name, ".C")) { draw_document_icon(icon->x, icon->y, icon->name); draw_string(icon->x + 31, icon->y + 2, "C", COLOR_APPLE_BLUE); } else if (str_ends_with(icon->name, ".jpg") || str_ends_with(icon->name, ".JPG")) { draw_document_icon(icon->x, icon->y, icon->name); draw_string(icon->x + 27, icon->y + 2, "JPG", 0xFF44BB44); } else draw_document_icon(icon->x, icon->y, icon->name); } } // 3. Windows - sort by z-index and draw Window *sorted_windows[32]; for (int i = 0; i < window_count; i++) { sorted_windows[i] = all_windows[i]; } for (int i = 0; i < window_count - 1; i++) { for (int j = 0; j < window_count - i - 1; j++) { if (sorted_windows[j]->z_index > sorted_windows[j + 1]->z_index) { Window *temp = sorted_windows[j]; sorted_windows[j] = sorted_windows[j + 1]; sorted_windows[j + 1] = temp; } } } // Draw windows in z-order (lowest first) for (int i = 0; i < window_count; i++) { Window *win = sorted_windows[i]; if (!win->visible) continue; if (dirty.active && !win->focused) { if (win->x + win->w <= dirty.x || win->x >= dirty.x + dirty.w || win->y + win->h <= dirty.y || win->y >= dirty.y + dirty.h) { continue; } } draw_window(win); } // 4. Top Menu Bar draw_rect(0, 0, sw, 30, COLOR_TOPBAR_BG); // Logo dropdown (top-left) draw_boredos_logo(8, 8, 1); // Clock (top-right) draw_clock(sw - 80, 12); // Top menu dropdown (if logo clicked) if (start_menu_open) { int menu_h = 85; draw_rounded_rect_filled(8, 40, 160, menu_h, 8, COLOR_DARK_PANEL); draw_string(20, 48, "About BoredOS", COLOR_DARK_TEXT); draw_string(20, 68, "Settings", COLOR_DARK_TEXT); draw_string(20, 88, "Shutdown", COLOR_DARK_TEXT); draw_string(20, 108, "Restart", COLOR_DARK_TEXT); } // 5. Dock (bottom - macOS style, floating with rounded corners) int dock_h = 60; int dock_y = sh - dock_h - 6; // Float above bottom int dock_item_size = 48; int dock_spacing = 10; int total_dock_width = 7 * (dock_item_size + dock_spacing); int dock_bg_x = (sw - total_dock_width) / 2 - 12; // Rounded background extends beyond icons int dock_bg_w = total_dock_width + 24; draw_rounded_rect_filled(dock_bg_x, dock_y, dock_bg_w, dock_h, 18, COLOR_DOCK_BG); // Draw dock apps (centered) int dock_x = (sw - total_dock_width) / 2; int dock_item_y = dock_y + 6; draw_dock_files(dock_x, dock_item_y); dock_x += dock_item_size + dock_spacing; draw_dock_settings(dock_x, dock_item_y); dock_x += dock_item_size + dock_spacing; draw_dock_notepad(dock_x, dock_item_y); dock_x += dock_item_size + dock_spacing; draw_dock_calculator(dock_x, dock_item_y); dock_x += dock_item_size + dock_spacing; draw_dock_terminal(dock_x, dock_item_y); dock_x += dock_item_size + dock_spacing; draw_dock_minesweeper(dock_x, dock_item_y); dock_x += dock_item_size + dock_spacing; draw_dock_paint(dock_x, dock_item_y); // Editor removed from dock // Desktop Context Menu (with rounded corners) if (desktop_menu_visible) { int menu_w = 140; int item_h = 25; int menu_h = (desktop_menu_target_icon != -1) ? 125 : 75; draw_rounded_rect_filled(desktop_menu_x, desktop_menu_y, menu_w, menu_h, 8, COLOR_DARK_PANEL); if (desktop_menu_target_icon != -1) { bool can_paste = explorer_clipboard_has_content(); draw_string(desktop_menu_x + 10, desktop_menu_y + 5, "Cut", COLOR_WHITE); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h, "Copy", COLOR_WHITE); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 2, "Paste", can_paste ? COLOR_WHITE : COLOR_DKGRAY); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 3, "Delete", COLOR_TRAFFIC_RED); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 4, "Rename", COLOR_WHITE); } else { bool can_paste = explorer_clipboard_has_content(); draw_string(desktop_menu_x + 10, desktop_menu_y + 5, "New File", COLOR_WHITE); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h, "New Folder", COLOR_WHITE); draw_string(desktop_menu_x + 10, desktop_menu_y + 5 + item_h * 2, "Paste", can_paste ? COLOR_WHITE : COLOR_DKGRAY); } } // Desktop Dialogs (dark mode) if (desktop_dialog_state != 0) { int dlg_w = 300; int dlg_h = 110; int dlg_x = (sw - dlg_w) / 2; int dlg_y = (sh - dlg_h) / 2; draw_rounded_rect_filled(dlg_x, dlg_y, dlg_w, dlg_h, 8, COLOR_DARK_PANEL); const char *title = "Rename"; const char *btn_text = "Rename"; if (desktop_dialog_state == 1) { title = "Create New File"; btn_text = "Create"; } else if (desktop_dialog_state == 2) { title = "Create New Folder"; btn_text = "Create"; } draw_string(dlg_x + 10, dlg_y + 10, title, COLOR_WHITE); draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG); draw_string(dlg_x + 15, dlg_y + 40, desktop_dialog_input, COLOR_WHITE); // Cursor draw_rect(dlg_x + 15 + desktop_dialog_cursor * 8, dlg_y + 39, 2, 12, COLOR_WHITE); draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 4, COLOR_DARK_BORDER); draw_string(dlg_x + 70, dlg_y + 72, btn_text, COLOR_WHITE); draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 4, COLOR_DARK_BORDER); draw_string(dlg_x + 185, dlg_y + 72, "Cancel", COLOR_WHITE); } // Message Box (dark mode) if (msg_box_visible) { int mw = 320; int mh = 100; int mx = (sw - mw) / 2; int my = (sh - mh) / 2; draw_rounded_rect_filled(mx, my, mw, mh, 8, COLOR_DARK_PANEL); draw_string(mx + 15, my + 10, msg_box_title, COLOR_DARK_TEXT); draw_string(mx + 10, my + 40, msg_box_text, COLOR_DARK_TEXT); draw_rounded_rect_filled(mx + mw/2 - 30, my + 70, 60, 20, 4, COLOR_DARK_BORDER); draw_string(mx + mw/2 - 18, my + 75, "OK", COLOR_DARK_TEXT); } // Custom Overlay (VM Graphics) if (wm_custom_paint_hook) { wm_custom_paint_hook(); } // Draw Dragged Icon if (is_dragging_file) { if (drag_icon_type == 1) draw_folder_icon(mx - 20, my - 20, "Moving..."); else if (drag_icon_type == 2) draw_icon(mx - 20, my - 20, "Moving..."); else draw_document_icon(mx - 20, my - 20, "Moving..."); } // 7. Mouse cursor (draw last so it's on top) draw_cursor(mx, my); last_cursor_x = mx; last_cursor_y = my; // Flip the buffer - display the rendered frame atomically graphics_flip_buffer(); } // --- Input Handling --- bool rect_contains(int x, int y, int w, int h, int px, int py) { return px >= x && px < x + w && py >= y && py < y + h; } void wm_bring_to_front(Window *win) { // Clear focus from all windows for (int i = 0; i < window_count; i++) { all_windows[i]->focused = false; } // Find current max z-index int max_z = 0; for (int i = 0; i < window_count; i++) { if (all_windows[i]->z_index > max_z) max_z = all_windows[i]->z_index; } win->visible = true; win->focused = true; win->z_index = max_z + 1; force_redraw = true; } void wm_add_window(Window *win) { if (window_count < 32) { all_windows[window_count++] = win; } } void wm_handle_click(int x, int y) { int sh = get_screen_height(); int sw = get_screen_width(); if (msg_box_visible) { int mw = 320; int mh = 100; int mx = (sw - mw) / 2; int my = (sh - mh) / 2; if (rect_contains(mx + mw/2 - 30, my + 70, 60, 20, x, y)) { msg_box_visible = false; force_redraw = true; } return; } // Handle Desktop Context Menu Click if (desktop_menu_visible) { int menu_w = 140; int menu_h = (desktop_menu_target_icon != -1) ? 125 : 75; if (rect_contains(desktop_menu_x, desktop_menu_y, menu_w, menu_h, x, y)) { int rel_y = y - desktop_menu_y; int item = rel_y / 25; if (item == 0 && desktop_menu_target_icon != -1) { // Cut DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon]; char path[128] = "/Desktop/"; int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; explorer_clipboard_cut(path); } else if (item == 1 && desktop_menu_target_icon != -1) { // Copy DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon]; char path[128] = "/Desktop/"; int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; explorer_clipboard_copy(path); } else if (item == 0 && desktop_menu_target_icon == -1) { // New File desktop_dialog_state = 1; desktop_dialog_input[0] = 0; desktop_dialog_cursor = 0; } else if (item == 1 && desktop_menu_target_icon == -1) { // New Folder desktop_dialog_state = 2; desktop_dialog_input[0] = 0; desktop_dialog_cursor = 0; } else if (item == 2) { // Paste bool can_paste = explorer_clipboard_has_content(); if (desktop_menu_target_icon != -1) { DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon]; if (icon->type != 1) can_paste = false; } if (can_paste) { int old_count = desktop_icon_count; if (desktop_menu_target_icon != -1 && desktop_icons[desktop_menu_target_icon].type == 1) { // Paste into folder char path[128] = "/Desktop/"; DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon]; int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; explorer_clipboard_paste(&win_explorer, path); } else { // Paste to desktop explorer_clipboard_paste(&win_explorer, "/Desktop"); } refresh_desktop_icons(); if (!desktop_auto_align && desktop_icon_count > old_count && desktop_menu_target_icon == -1) { int new_idx = desktop_icon_count - 1; desktop_icons[new_idx].x = desktop_menu_x - 20; desktop_icons[new_idx].y = desktop_menu_y - 20; if (desktop_snap_to_grid) { int col = (desktop_icons[new_idx].x - 20 + 40) / 80; int row = (desktop_icons[new_idx].y - 20 + 40) / 80; if (col < 0) col = 0; if (row < 0) row = 0; desktop_icons[new_idx].x = 20 + col * 80; desktop_icons[new_idx].y = 20 + row * 80; } } } } else if (item == 3 && desktop_menu_target_icon != -1) { // Delete DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon]; char path[128] = "/Desktop/"; int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; explorer_delete_recursive(path); refresh_desktop_icons(); } else if (item == 4 && desktop_menu_target_icon != -1) { // Rename desktop_dialog_state = 8; desktop_dialog_target = desktop_menu_target_icon; int k=0; while(desktop_icons[desktop_dialog_target].name[k]) { desktop_dialog_input[k] = desktop_icons[desktop_dialog_target].name[k]; k++; } desktop_dialog_input[k] = 0; desktop_dialog_cursor = k; } } desktop_menu_visible = false; force_redraw = true; return; } // Handle Desktop Dialog Clicks if (desktop_dialog_state != 0) { int dlg_x = (sw - 300) / 2; int dlg_y = (sh - 110) / 2; if (rect_contains(dlg_x + 50, dlg_y + 65, 80, 25, x, y)) { // Confirm if (desktop_dialog_state == 8) { // Rename char old_path[128] = "/Desktop/"; char new_path[128] = "/Desktop/"; int p=9; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0; p=9; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0; if (fat32_rename(old_path, new_path)) { refresh_desktop_icons(); explorer_refresh_all(); } } else if (desktop_dialog_state == 1 || desktop_dialog_state == 2) { // Create File/Folder if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) { wm_show_message("Error", "Desktop is full!"); } else if (desktop_dialog_input[0] != 0) { char path[128] = "/Desktop/"; int p=9; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0; if (desktop_dialog_state == 1) { FAT32_FileHandle *fh = fat32_open(path, "w"); if (fh) fat32_close(fh); } else { fat32_mkdir(path); } refresh_desktop_icons(); explorer_refresh_all(); } } desktop_dialog_state = 0; force_redraw = true; return; } if (rect_contains(dlg_x + 170, dlg_y + 65, 80, 25, x, y)) { // Cancel desktop_dialog_state = 0; force_redraw = true; return; } if (rect_contains(dlg_x + 10, dlg_y + 35, 280, 20, x, y)) { desktop_dialog_cursor = (x - dlg_x - 15) / 8; int len = 0; while(desktop_dialog_input[len]) len++; if (desktop_dialog_cursor > len) desktop_dialog_cursor = len; force_redraw = true; return; } } // Check Top Bar Logo (toggle dropdown menu) if (rect_contains(8, 8, 24, 24, x, y)) { start_menu_open = !start_menu_open; force_redraw = true; pending_desktop_icon_click = -1; return; } // Handle top bar dropdown menu items if (start_menu_open && rect_contains(8, 40, 160, 120, x, y)) { int rel_y = y - 40; int item = rel_y / 20; if (item == 0) { // About wm_bring_to_front(&win_about); } else if (item == 1) { // Settings wm_bring_to_front(&win_control_panel); } else if (item == 2) { // Shutdown cli_cmd_shutdown(NULL); } else if (item == 3) { // Restart cli_cmd_reboot(NULL); } start_menu_open = false; force_redraw = true; return; } // Find topmost window at click location Window *topmost = NULL; int topmost_z = -1; for (int i = 0; i < window_count; i++) { Window *win = all_windows[i]; if (win->visible && rect_contains(win->x, win->y, win->w, win->h, x, y)) { if (win->z_index > topmost_z) { topmost = win; topmost_z = win->z_index; } } } // If a window was clicked if (topmost != NULL) { wm_bring_to_front(topmost); // Check traffic light close button (now at top-left) if (rect_contains(topmost->x + 8, topmost->y + 2, 12, 12, x, y)) { topmost->visible = false; // Reset window state on close if (topmost == &win_explorer) { explorer_reset(); } else if (topmost == &win_control_panel) { control_panel_reset(); } else if (topmost == &win_paint) { paint_reset(); } } else if (y < topmost->y + 30) { // Dragging the title bar is_dragging = true; drag_window = topmost; drag_offset_x = x - topmost->x; drag_offset_y = y - topmost->y; } else { // Content click if (topmost->handle_click) { topmost->handle_click(topmost, x - topmost->x, y - topmost->y); } } pending_desktop_icon_click = -1; } else { // No window clicked - check desktop icons // Clear focus from all windows first for (int w = 0; w < window_count; w++) { all_windows[w]->focused = false; } pending_desktop_icon_click = -1; for (int i = 0; i < desktop_icon_count; i++) { DesktopIcon *icon = &desktop_icons[i]; if (rect_contains(icon->x + 20, icon->y, 40, 40, x, y)) { // Handle click - Defer to mouse up to allow dragging pending_desktop_icon_click = i; return; } } } // Close dropdown menu if clicked elsewhere if (start_menu_open) { start_menu_open = false; } force_redraw = true; } // Handle right click (context menu or special actions) void wm_handle_right_click(int x, int y) { desktop_menu_visible = false; // Close if open // Find topmost window at click location Window *topmost = NULL; int topmost_z = -1; for (int i = 0; i < window_count; i++) { Window *win = all_windows[i]; if (win->visible && rect_contains(win->x, win->y, win->w, win->h, x, y)) { if (win->z_index > topmost_z) { topmost = win; topmost_z = win->z_index; } } } // If a window was clicked if (topmost != NULL) { // Don't process close button or title bar for right click if (y >= topmost->y + 24) { // Content right click if (topmost->handle_right_click) { topmost->handle_right_click(topmost, x - topmost->x, y - topmost->y); } } } else { // Desktop Right Click desktop_menu_visible = true; desktop_menu_x = x; desktop_menu_y = y; desktop_menu_target_icon = -1; // Check if clicked on an icon for (int i = 0; i < desktop_icon_count; i++) { if (rect_contains(desktop_icons[i].x + 20, desktop_icons[i].y, 40, 40, x, y)) { desktop_menu_target_icon = i; break; } } } force_redraw = true; }void wm_handle_mouse(int dx, int dy, uint8_t buttons) { int sw = get_screen_width(); int sh = get_screen_height(); prev_mx = mx; prev_my = my; mouse_accum_x += dx * mouse_speed; mouse_accum_y += dy * mouse_speed; int move_x = mouse_accum_x / 10; int move_y = mouse_accum_y / 10; mouse_accum_x -= move_x * 10; mouse_accum_y -= move_y * 10; mx += move_x; my += move_y; if (mx < 0) mx = 0; if (my < 0) my = 0; if (mx >= sw) mx = sw - 1; if (my >= sh) my = sh - 1; static bool prev_left = false; static bool prev_right = false; bool left = buttons & 0x01; bool right = buttons & 0x02; if (left && !prev_left) { // Mouse Down drag_start_x = mx; drag_start_y = my; if (win_paint.focused && win_paint.visible) { paint_reset_last_pos(); } // Check Dock for app clicks (bottom of screen, floating) int dock_h = 60; int dock_y = sh - dock_h - 6; // Float above bottom int dock_item_size = 48; int dock_spacing = 10; int total_dock_width = 7 * (dock_item_size + dock_spacing); int dock_bg_x = (sw - total_dock_width) / 2 - 12; int dock_bg_w = total_dock_width + 24; if (rect_contains(dock_bg_x, dock_y, dock_bg_w, dock_h, mx, my)) { int dock_x = (sw - total_dock_width) / 2; // Check which dock item was clicked int item_x = mx - dock_x; if (item_x >= 0) { int item = item_x / (dock_item_size + dock_spacing); if (item == 0) start_menu_pending_app = "Files"; else if (item == 1) start_menu_pending_app = "Settings"; else if (item == 2) start_menu_pending_app = "Notepad"; else if (item == 3) start_menu_pending_app = "Calculator"; else if (item == 4) start_menu_pending_app = "Terminal"; else if (item == 5) start_menu_pending_app = "Minesweeper"; else if (item == 6) start_menu_pending_app = "Paint"; } } else { wm_handle_click(mx, my); } } else if (right && !prev_right) { wm_handle_right_click(mx, my); } else if (left && win_paint.focused && win_paint.visible && !is_dragging) { int rel_x = mx - win_paint.x; int rel_y = my - win_paint.y; paint_handle_mouse(rel_x, rel_y); force_redraw = true; } else if (left && is_dragging && drag_window) { drag_window->x = mx - drag_offset_x; drag_window->y = my - drag_offset_y; // Mark for full redraw since window moved force_redraw = true; } else if (left && !is_dragging && !is_dragging_file && (dx != 0 || dy != 0)) { // Check deadzone int dist_x = mx - drag_start_x; int dist_y = my - drag_start_y; if (dist_x < 0) dist_x = -dist_x; if (dist_y < 0) dist_y = -dist_y; if (dist_x >= 5 || dist_y >= 5) { // Check for Start Menu Drag if (start_menu_pending_app) { // Start dragging app from start menu is_dragging_file = true; drag_icon_type = 2; // Construct special path for app drag char *p = drag_file_path; const char *prefix = "::APP::"; while(*prefix) *p++ = *prefix++; char *n = start_menu_pending_app; while(*n) *p++ = *n++; *p = 0; start_menu_pending_app = NULL; } // Mouse moving with left button, check for file drag start // 1. Check Desktop Icons if (pending_desktop_icon_click != -1) { int i = pending_desktop_icon_click; DesktopIcon *icon = &desktop_icons[i]; is_dragging_file = true; drag_icon_type = icon->type; pending_desktop_icon_click = -1; drag_icon_orig_x = icon->x; drag_icon_orig_y = icon->y; // Construct path char path[128] = "/Desktop/"; int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; int k=0; while(path[k]) { drag_file_path[k] = path[k]; k++; } drag_file_path[k]=0; } // 2. Check Explorer Items if (!is_dragging_file) { bool is_dir; if (explorer_get_file_at(drag_start_x, drag_start_y, drag_file_path, &is_dir)) { is_dragging_file = true; drag_icon_type = is_dir ? 1 : 0; drag_src_win = NULL; // Find which explorer window was clicked to clear its state for (int w = 0; w < window_count; w++) { Window *win = all_windows[w]; if (win->visible && rect_contains(win->x, win->y, win->w, win->h, drag_start_x, drag_start_y)) { if (str_starts_with(win->title, "Files")) { drag_src_win = win; explorer_clear_click_state(win); } } } } } if (is_dragging_file) force_redraw = true; } } else if (!left) { if (is_dragging) { is_dragging = false; drag_window = NULL; force_redraw = true; } // Handle Dock App Click (Mouse Up without Drag) if (start_menu_pending_app) { // Launch App if (str_starts_with(start_menu_pending_app, "Files")) { explorer_open_directory("/"); } else if (str_starts_with(start_menu_pending_app, "Notepad")) { wm_bring_to_front(&win_notepad); } else if (str_starts_with(start_menu_pending_app, "Editor")) { wm_bring_to_front(&win_editor); } else if (str_starts_with(start_menu_pending_app, "Terminal")) { cmd_reset(); wm_bring_to_front(&win_cmd); } else if (str_starts_with(start_menu_pending_app, "Calculator")) { wm_bring_to_front(&win_calculator); } else if (str_starts_with(start_menu_pending_app, "Minesweeper")) { wm_bring_to_front(&win_minesweeper); } else if (str_starts_with(start_menu_pending_app, "Settings")) { wm_bring_to_front(&win_control_panel); } else if (str_starts_with(start_menu_pending_app, "Paint")) { wm_bring_to_front(&win_paint); } else if (str_starts_with(start_menu_pending_app, "About")) { wm_bring_to_front(&win_about); } else if (str_starts_with(start_menu_pending_app, "Shutdown")) { cli_cmd_shutdown(NULL); } else if (str_starts_with(start_menu_pending_app, "Restart")) { cli_cmd_reboot(NULL); } start_menu_pending_app = NULL; force_redraw = true; } // Handle Desktop Icon Click (Mouse Up) if (pending_desktop_icon_click != -1) { int i = pending_desktop_icon_click; if (i < desktop_icon_count) { DesktopIcon *icon = &desktop_icons[i]; bool handled = false; if (icon->type == 2) { // App Shortcut // Check name to launch app if (str_ends_with(icon->name, "Notepad.shortcut")) { wm_bring_to_front(&win_notepad); handled = true; } else if (str_ends_with(icon->name, "Calculator.shortcut")) { wm_bring_to_front(&win_calculator); handled = true; } else if (str_ends_with(icon->name, "Minesweeper.shortcut")) { wm_bring_to_front(&win_minesweeper); handled = true; } else if (str_ends_with(icon->name, "Settings.shortcut")) { wm_bring_to_front(&win_control_panel); handled = true; } else if (str_ends_with(icon->name, "Terminal.shortcut")) { wm_bring_to_front(&win_cmd); handled = true; } else if (str_ends_with(icon->name, "About.shortcut")) { wm_bring_to_front(&win_about); handled = true; } else if (str_ends_with(icon->name, "Files.shortcut")) { explorer_open_directory("/"); handled = true; } else if (str_ends_with(icon->name, "Recycle Bin.shortcut")) { explorer_open_directory("/RecycleBin"); handled = true; } else if (str_ends_with(icon->name, "Paint.shortcut")) { wm_bring_to_front(&win_paint); handled = true; } if (!handled) { // Generic Shortcut Handling char path[128] = "/Desktop/"; int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; if (str_ends_with(icon->name, ".shortcut") && !str_starts_with(icon->name, "Recycle Bin")) { FAT32_FileHandle *fh = fat32_open(path, "r"); if (fh) { char buf[256]; int len = fat32_read(fh, buf, 255); fat32_close(fh); if (len > 0) { buf[len] = 0; if (fat32_is_directory(buf)) { explorer_open_directory(buf); } else { editor_open_file(buf); wm_bring_to_front(&win_editor); } pending_desktop_icon_click = -1; force_redraw = true; return; } } } } } else if (icon->type == 1) { // Folder char path[128] = "/Desktop/"; int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; explorer_open_directory(path); } else { // File char path[128] = "/Desktop/"; int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; if (str_ends_with(icon->name, ".pnt")) { paint_load(path); wm_bring_to_front(&win_paint); } else if (str_ends_with(icon->name, ".md")) { markdown_open_file(path); wm_bring_to_front(&win_markdown); } else if (str_ends_with(icon->name, ".jpg") || str_ends_with(icon->name, ".JPG")) { viewer_open_file(path); } else { editor_open_file(path); wm_bring_to_front(&win_editor); } } } pending_desktop_icon_click = -1; } if (is_dragging_file) { // Drop logic // Check drop target - iterate through all windows to find if dropped on an Explorer Window *drop_win = NULL; int topmost_z = -1; for (int w = 0; w < window_count; w++) { Window *win = all_windows[w]; if (win->visible && rect_contains(win->x, win->y, win->w, win->h, mx, my)) { if (win->z_index > topmost_z && str_starts_with(win->title, "Files")) { drop_win = win; topmost_z = win->z_index; } } } if (drop_win) { char target_path[256]; bool is_dir; // Check if dropped on a folder inside this explorer if (explorer_get_file_at(mx, my, target_path, &is_dir) && is_dir) { explorer_import_file_to(drop_win, drag_file_path, target_path); } else { // Dropped in current dir of this explorer explorer_import_file(drop_win, drag_file_path); } if (str_starts_with(drag_file_path, "/Desktop/")) { refresh_desktop_icons(); } } else { // Dropped on Desktop (or elsewhere) if (drag_file_path[0] == ':' && drag_file_path[1] == ':' && drag_file_path[2] == 'A') { // Dropped Start Menu App -> Create Shortcut create_desktop_shortcut(drag_file_path + 7); // Skip ::APP:: } else { // If source was NOT desktop, move to desktop // Check if path starts with /Desktop/ bool from_desktop = (drag_file_path[0]=='/' && drag_file_path[1]=='D' && drag_file_path[2]=='e'); bool dropped_on_target = false; for (int i = 0; i < desktop_icon_count; i++) { if (from_desktop) { char path[128] = "/Desktop/"; int p=9; int n=0; while(desktop_icons[i].name[n]) path[p++] = desktop_icons[i].name[n++]; path[p]=0; if (str_eq(path, drag_file_path) == 0) continue; } if (rect_contains(desktop_icons[i].x + 20, desktop_icons[i].y, 40, 40, mx, my)) { if (desktop_icons[i].type == 1) { char target_path[256] = "/Desktop/"; int p=9; int n=0; while(desktop_icons[i].name[n]) target_path[p++] = desktop_icons[i].name[n++]; target_path[p]=0; explorer_import_file_to(&win_explorer, drag_file_path, target_path); refresh_desktop_icons(); dropped_on_target = true; break; } else if (desktop_icons[i].type == 2 && str_starts_with(desktop_icons[i].name, "Recycle Bin")) { explorer_import_file_to(&win_explorer, drag_file_path, "/RecycleBin"); refresh_desktop_icons(); dropped_on_target = true; break; } else { dropped_on_target = true; break; } } } if (!dropped_on_target && !from_desktop) { // Dragged from Explorer to Desktop // Check limit first if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) { wm_show_message("Error", "Desktop is full!"); } else { explorer_import_file_to(&win_explorer, drag_file_path, "/Desktop"); } // Handle insertion at specific position char filename[64]; int len = 0; while(drag_file_path[len]) len++; int s = len - 1; while(s >= 0 && drag_file_path[s] != '/') s--; s++; int d = 0; while(drag_file_path[s] && d < 63) filename[d++] = drag_file_path[s++]; filename[d] = 0; if (desktop_auto_align && !msg_box_visible) { int new_idx = -1; for(int i=0; i= desktop_icon_count) target_idx = desktop_icon_count - 1; // Move new_idx to target_idx DesktopIcon temp = desktop_icons[new_idx]; // Shift down to make space for (int i = new_idx; i > target_idx; i--) desktop_icons[i] = desktop_icons[i-1]; desktop_icons[target_idx] = temp; refresh_desktop_icons(); // Re-apply layout } } else if (!desktop_auto_align && !msg_box_visible) { for(int i=0; i (int)(cell_h * 0.2)) { target_row++; } int target_idx = target_col * desktop_max_rows_per_col + target_row; if (target_idx >= desktop_icon_count) target_idx = desktop_icon_count - 1; // Shift items DesktopIcon temp = desktop_icons[dragged_idx]; if (target_idx > dragged_idx) { for (int i = dragged_idx; i < target_idx; i++) desktop_icons[i] = desktop_icons[i+1]; } else { for (int i = dragged_idx; i > target_idx; i--) desktop_icons[i] = desktop_icons[i-1]; } desktop_icons[target_idx] = temp; refresh_desktop_icons(); // Re-applies layout } else { desktop_icons[dragged_idx].x = mx - 20; desktop_icons[dragged_idx].y = my - 20; if (desktop_snap_to_grid) { int col = (desktop_icons[dragged_idx].x - 20 + 40) / 80; int row = (desktop_icons[dragged_idx].y - 20 + 40) / 80; if (col < 0) col = 0; if (row < 0) row = 0; desktop_icons[dragged_idx].x = 20 + col * 80; desktop_icons[dragged_idx].y = 20 + row * 80; } // Check for collision with other icons // Folders already checked above, this is for overlap prevention for (int i = 0; i < desktop_icon_count; i++) { if (i == dragged_idx) continue; // Simple distance check or rect overlap int dx = desktop_icons[i].x - desktop_icons[dragged_idx].x; int dy = desktop_icons[i].y - desktop_icons[dragged_idx].y; if (dx < 0) dx = -dx; if (dy < 0) dy = -dy; if (dx < 35 && dy < 35) { // Revert position desktop_icons[dragged_idx].x = drag_icon_orig_x; desktop_icons[dragged_idx].y = drag_icon_orig_y; break; } } } } } } } is_dragging_file = false; force_redraw = true; } } if (is_dragging_file) { force_redraw = true; } prev_left = left; prev_right = right; if (prev_mx != mx || prev_my != my) { // Cursor moved - just mark dirty cursor areas wm_mark_dirty(prev_mx, prev_my, 10, 10); wm_mark_dirty(mx, my, 10, 10); } prev_left = left; } // Input Queue #define INPUT_QUEUE_SIZE 128 static char key_queue[INPUT_QUEUE_SIZE]; static volatile int key_head = 0; static volatile int key_tail = 0; static void wm_dispatch_key(char c) { if (desktop_dialog_state != 0) { int len = 0; while(desktop_dialog_input[len]) len++; if (c == '\n') { if (desktop_dialog_state == 8) { // Rename char old_path[128] = "/Desktop/"; char new_path[128] = "/Desktop/"; int p=9; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0; p=9; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0; if (fat32_rename(old_path, new_path)) { refresh_desktop_icons(); explorer_refresh_all(); } } else if (desktop_dialog_state == 1 || desktop_dialog_state == 2) { // Create File/Folder if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) { wm_show_message("Error", "Desktop is full!"); } else if (desktop_dialog_input[0] != 0) { char path[128] = "/Desktop/"; int p=9; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0; if (desktop_dialog_state == 1) { FAT32_FileHandle *fh = fat32_open(path, "w"); if (fh) fat32_close(fh); } else { fat32_mkdir(path); } refresh_desktop_icons(); explorer_refresh_all(); } } desktop_dialog_state = 0; } else if (c == 27) { desktop_dialog_state = 0; } else if (c == '\b' || c == 127) { if (desktop_dialog_cursor > 0) { for(int i = desktop_dialog_cursor - 1; i < len; i++) desktop_dialog_input[i] = desktop_dialog_input[i+1]; desktop_dialog_cursor--; } } else if (c == 19) { // Left if (desktop_dialog_cursor > 0) desktop_dialog_cursor--; } else if (c == 20) { // Right if (desktop_dialog_cursor < len) desktop_dialog_cursor++; } else if (c >= 32 && c <= 126 && len < 63) { for(int i = len; i >= desktop_dialog_cursor; i--) desktop_dialog_input[i+1] = desktop_dialog_input[i]; desktop_dialog_input[desktop_dialog_cursor] = c; desktop_dialog_cursor++; } force_redraw = true; return; } Window *target = NULL; for (int i = 0; i < window_count; i++) { if (all_windows[i]->focused && all_windows[i]->visible) { target = all_windows[i]; break; } } if (!target) return; if (target->handle_key) { target->handle_key(target, c); } // Mark window as needing redraw on next timer tick wm_mark_dirty(target->x, target->y, target->w, target->h); } void wm_handle_key(char c) { int next = (key_head + 1) % INPUT_QUEUE_SIZE; if (next != key_tail) { key_queue[key_head] = c; key_head = next; } } void wm_process_input(void) { while (key_head != key_tail) { char c = key_queue[key_tail]; key_tail = (key_tail + 1) % INPUT_QUEUE_SIZE; wm_dispatch_key(c); } } void wm_mark_dirty(int x, int y, int w, int h) { graphics_mark_dirty(x, y, w, h); } void wm_refresh(void) { force_redraw = true; } void wm_init(void) { disk_manager_init(); disk_manager_scan(); // Drives are now dynamically managed - only real drives are registered notepad_init(); cmd_init(); calculator_init(); explorer_init(); editor_init(); markdown_init(); control_panel_init(); about_init(); minesweeper_init(); paint_init(); viewer_init(); wallpaper_init(); refresh_desktop_icons(); // Initialize z-indices win_notepad.z_index = 0; win_cmd.z_index = 1; win_calculator.z_index = 2; win_explorer.z_index = 3; win_editor.z_index = 4; win_markdown.z_index = 5; win_control_panel.z_index = 6; win_about.z_index = 7; win_minesweeper.z_index = 8; win_paint.z_index = 9; all_windows[0] = &win_notepad; all_windows[1] = &win_cmd; all_windows[2] = &win_calculator; all_windows[3] = &win_explorer; all_windows[4] = &win_editor; all_windows[5] = &win_markdown; all_windows[6] = &win_control_panel; all_windows[7] = &win_about; all_windows[8] = &win_minesweeper; all_windows[9] = &win_paint; all_windows[10] = &win_viewer; window_count = 11; // Only show Explorer and Notepad on desktop (Explorer on top) win_explorer.visible = false; win_explorer.focused = false; win_explorer.z_index = 10; win_notepad.visible = false; win_notepad.focused = false; win_notepad.z_index = 9; // Rest are hidden initially win_cmd.visible = false; win_calculator.visible = false; win_editor.visible = false; win_markdown.visible = false; win_control_panel.visible = false; win_about.visible = false; win_minesweeper.visible = false; force_redraw = true; } uint32_t wm_get_ticks(void) { return timer_ticks; } // Called by timer interrupt ~60Hz void wm_timer_tick(void) { timer_ticks++; // Auto-refresh desktop every 5 seconds to save CPU in QEMU // But NOT if the user is dragging a window or file. if (!is_dragging && !is_dragging_file) { desktop_refresh_timer++; if (desktop_refresh_timer >= 300) { refresh_desktop_icons(); explorer_refresh_all(); desktop_refresh_timer = 0; force_redraw = true; } } // Only redraw if there are dirty areas (clock updates at most every second, cursor rarely moves in timer only) // Most of the time, nothing changes between ticks static uint8_t last_second = 0xFF; outb(0x70, 0x00); uint8_t current_sec = inb(0x71); if (current_sec != last_second) { last_second = current_sec; int sw = get_screen_width(); // Mark clock area in the top menu bar (around draw_clock at sw - 80, y=12) wm_mark_dirty(sw - 110, 6, 110, 24); } // If force_redraw is set, do a full redraw if (force_redraw) { graphics_mark_screen_dirty(); force_redraw = false; } // Perform redraw if there are dirty areas DirtyRect dirty = graphics_get_dirty_rect(); if (dirty.active) { wm_paint(); graphics_clear_dirty(); } }