diff --git a/.DS_Store b/.DS_Store index 2aba2fd..3e14859 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/boredos.iso b/boredos.iso index 85729bc..7a5dc8d 100644 Binary files a/boredos.iso and b/boredos.iso differ diff --git a/build/boredos.elf b/build/boredos.elf index ce23f3c..55ca819 100755 Binary files a/build/boredos.elf and b/build/boredos.elf differ diff --git a/build/cli_apps/txtedit.o b/build/cli_apps/txtedit.o deleted file mode 100644 index 7a89178..0000000 Binary files a/build/cli_apps/txtedit.o and /dev/null differ diff --git a/build/cmd.o b/build/cmd.o index dbe7068..8bc3876 100644 Binary files a/build/cmd.o and b/build/cmd.o differ diff --git a/build/control_panel.o b/build/control_panel.o deleted file mode 100644 index e0cb1aa..0000000 Binary files a/build/control_panel.o and /dev/null differ diff --git a/build/editor.o b/build/editor.o deleted file mode 100644 index 587c8b5..0000000 Binary files a/build/editor.o and /dev/null differ diff --git a/build/explorer.o b/build/explorer.o index c186bb2..4086d32 100644 Binary files a/build/explorer.o and b/build/explorer.o differ diff --git a/build/fat32.o b/build/fat32.o index c00a3d2..748db05 100644 Binary files a/build/fat32.o and b/build/fat32.o differ diff --git a/build/graphics.o b/build/graphics.o index 2e99ad7..e54ae8a 100644 Binary files a/build/graphics.o and b/build/graphics.o differ diff --git a/build/idt.o b/build/idt.o index b6d1e78..a2d712a 100644 Binary files a/build/idt.o and b/build/idt.o differ diff --git a/build/main.o b/build/main.o index 433fb1a..52299fc 100644 Binary files a/build/main.o and b/build/main.o differ diff --git a/build/markdown.o b/build/markdown.o deleted file mode 100644 index 9f0bf25..0000000 Binary files a/build/markdown.o and /dev/null differ diff --git a/build/minesweeper.o b/build/minesweeper.o deleted file mode 100644 index c8b8242..0000000 Binary files a/build/minesweeper.o and /dev/null differ diff --git a/build/paint.o b/build/paint.o deleted file mode 100644 index a94b289..0000000 Binary files a/build/paint.o and /dev/null differ diff --git a/build/viewer.o b/build/viewer.o deleted file mode 100644 index 347f736..0000000 Binary files a/build/viewer.o and /dev/null differ diff --git a/build/vm.o b/build/vm.o index 7e51aba..c78b21d 100644 Binary files a/build/vm.o and b/build/vm.o differ diff --git a/build/wm.o b/build/wm.o index 43ea6b2..dd6efe7 100644 Binary files a/build/wm.o and b/build/wm.o differ diff --git a/disk.img b/disk.img index 82d79bc..3fec5bb 100644 Binary files a/disk.img and b/disk.img differ diff --git a/iso_root/bin/calculator.elf b/iso_root/bin/calculator.elf index a9e333b..1339fbe 100755 Binary files a/iso_root/bin/calculator.elf and b/iso_root/bin/calculator.elf differ diff --git a/iso_root/bin/crash.elf b/iso_root/bin/crash.elf index b53e28c..9a31208 100755 Binary files a/iso_root/bin/crash.elf and b/iso_root/bin/crash.elf differ diff --git a/iso_root/bin/hello.elf b/iso_root/bin/hello.elf index 2a76bbc..928a245 100755 Binary files a/iso_root/bin/hello.elf and b/iso_root/bin/hello.elf differ diff --git a/iso_root/bin/markdown.elf b/iso_root/bin/markdown.elf new file mode 100755 index 0000000..1c1a7ff Binary files /dev/null and b/iso_root/bin/markdown.elf differ diff --git a/iso_root/bin/minesweeper.elf b/iso_root/bin/minesweeper.elf new file mode 100755 index 0000000..8532ecf Binary files /dev/null and b/iso_root/bin/minesweeper.elf differ diff --git a/iso_root/bin/notepad.elf b/iso_root/bin/notepad.elf index 6a39864..60ddd31 100755 Binary files a/iso_root/bin/notepad.elf and b/iso_root/bin/notepad.elf differ diff --git a/iso_root/bin/paint.elf b/iso_root/bin/paint.elf new file mode 100755 index 0000000..bf66151 Binary files /dev/null and b/iso_root/bin/paint.elf differ diff --git a/iso_root/bin/settings.elf b/iso_root/bin/settings.elf new file mode 100755 index 0000000..4858796 Binary files /dev/null and b/iso_root/bin/settings.elf differ diff --git a/iso_root/bin/txtedit.elf b/iso_root/bin/txtedit.elf new file mode 100755 index 0000000..f4ca530 Binary files /dev/null and b/iso_root/bin/txtedit.elf differ diff --git a/iso_root/bin/viewer.elf b/iso_root/bin/viewer.elf new file mode 100755 index 0000000..437573c Binary files /dev/null and b/iso_root/bin/viewer.elf differ diff --git a/iso_root/boredos.elf b/iso_root/boredos.elf index ce23f3c..55ca819 100755 Binary files a/iso_root/boredos.elf and b/iso_root/boredos.elf differ diff --git a/iso_root/limine.conf b/iso_root/limine.conf index 9f6665f..533f42f 100644 --- a/iso_root/limine.conf +++ b/iso_root/limine.conf @@ -8,4 +8,10 @@ timeout: 3 module_path: boot():/bin/calculator.elf module_path: boot():/bin/crash.elf module_path: boot():/bin/hello.elf + module_path: boot():/bin/markdown.elf + module_path: boot():/bin/minesweeper.elf module_path: boot():/bin/notepad.elf + module_path: boot():/bin/paint.elf + module_path: boot():/bin/settings.elf + module_path: boot():/bin/txtedit.elf + module_path: boot():/bin/viewer.elf diff --git a/src/kernel/boot.asm b/src/kernel/boot.asm index 4720326..5305b33 100644 --- a/src/kernel/boot.asm +++ b/src/kernel/boot.asm @@ -7,15 +7,11 @@ extern kmain bits 64 _start: - ; Ensure interrupts are disabled cli - ; (Limine guarantees 16-byte alignment) - ; Call the C kernel entry point call kmain - ; Halt if kmain returns hlt .loop: jmp .loop diff --git a/src/kernel/cli_apps/txtedit.c b/src/kernel/cli_apps/txtedit.c deleted file mode 100644 index d4ba19b..0000000 --- a/src/kernel/cli_apps/txtedit.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "cli_utils.h" -#include "fat32.h" -#include "wm.h" - -// Forward declarations from editor.h and wm.c -extern void editor_open_file(const char *filename); -extern void editor_init(void); -extern Window win_editor; -extern Window win_explorer; -extern Window win_cmd; - -void cli_cmd_txtedit(char *args) { - // Parse the file path argument - char filepath[256]; - int i = 0; - - // Skip leading whitespace - while (args && args[i] && (args[i] == ' ' || args[i] == '\t')) { - i++; - } - - // Extract filepath - int j = 0; - while (args && args[i] && args[i] != ' ' && args[i] != '\t' && j < 255) { - filepath[j++] = args[i++]; - } - filepath[j] = 0; - - // If no filepath provided, create a new empty file - if (j == 0) { - cli_write("Usage: txtedit \n"); - cli_write("Example: txtedit myfile.txt\n"); - cli_write(" txtedit /document.txt\n"); - return; - } - - // Normalize the path (handles relative and absolute paths) - char normalized_path[256]; - fat32_normalize_path(filepath, normalized_path); - - // Open the file in the GUI editor - editor_open_file(normalized_path); - - // Make editor window visible and focused, bring to front - win_editor.visible = true; - win_editor.focused = true; - - // Calculate max z_index to bring window to front - int max_z = 0; - if (win_explorer.z_index > max_z) max_z = win_explorer.z_index; - if (win_cmd.z_index > max_z) max_z = win_cmd.z_index; - win_editor.z_index = max_z + 1; - - cli_write("Opening: "); - cli_write(normalized_path); - cli_write("\n"); -} diff --git a/src/kernel/cmd.c b/src/kernel/cmd.c index 00c4dd7..c39698b 100644 --- a/src/kernel/cmd.c +++ b/src/kernel/cmd.c @@ -513,7 +513,7 @@ static void internal_cmd_txtedit(char *args) { i++; } - // Extract filepath (args now includes drive prefix from cmd_exec_single) + // Extract filepath int j = 0; while (args && args[i] && args[i] != ' ' && args[i] != '\t' && j < 255) { filepath[j++] = args[i++]; @@ -523,50 +523,19 @@ static void internal_cmd_txtedit(char *args) { // If no filepath provided, show usage if (j == 0) { cmd_write("Usage: txtedit \n"); - cmd_write("Example: txtedit myfile.txt\n"); - cmd_write(" txtedit /document.txt\n"); return; } - // Normalize the path (filepath already includes drive from cmd_exec_single) + // Normalize the path char normalized_path[256]; fat32_normalize_path(filepath, normalized_path); - // Extract drive from normalized path to set it as current temporarily - char drive = 'A'; - if (normalized_path[1] == ':') { - drive = normalized_path[0]; - if (drive >= 'a' && drive <= 'z') drive -= 32; - } - - // Set global drive temporarily to match file's drive - char saved_drive = fat32_get_current_drive(); - fat32_change_drive(drive); - - // Open the file in the GUI editor - extern void editor_open_file(const char *filename); - extern Window win_editor; - editor_open_file(normalized_path); - - // Restore the drive - fat32_change_drive(saved_drive); - - // Make editor window visible and focused, bring to front - extern Window win_explorer; - extern Window win_cmd; - - win_editor.visible = true; - win_editor.focused = true; - - // Calculate max z_index to bring window to front - int max_z = 0; - if (win_explorer.z_index > max_z) max_z = win_explorer.z_index; - if (win_cmd.z_index > max_z) max_z = win_cmd.z_index; - win_editor.z_index = max_z + 1; - cmd_write("Opening: "); cmd_write(normalized_path); cmd_write("\n"); + + cmd_is_waiting_for_process = true; + process_create_elf("A:/bin/txtedit.elf", normalized_path); } static void internal_cmd_ls(char *args) { diff --git a/src/kernel/control_panel.c b/src/kernel/control_panel.c deleted file mode 100644 index fe6a8e7..0000000 --- a/src/kernel/control_panel.c +++ /dev/null @@ -1,1118 +0,0 @@ -#include "control_panel.h" -#include "graphics.h" -#include -#include "wm.h" -#include "network.h" -#include "cli_apps/cli_utils.h" -#include "wallpaper.h" - -Window win_control_panel; - -#define COLOR_COFFEE 0xFF6B4423 -#define COLOR_TEAL 0xFF008080 -#define COLOR_GREEN 0xFF008000 -#define COLOR_BLUE_BG 0xFF000080 -#define COLOR_PURPLE 0xFF800080 -#define COLOR_GREY 0xFF454545 -#define MOUSE_BEIGE 0xFFD6D2C4 - -// Control panel state -#define VIEW_MAIN 0 -#define VIEW_WALLPAPER 1 -#define VIEW_NETWORK 2 -#define VIEW_DESKTOP 3 -#define VIEW_MOUSE 4 - -static int current_view = VIEW_MAIN; -static char rgb_r[4] = ""; -static char rgb_g[4] = ""; -static char rgb_b[4] = ""; -static int focused_field = -1; -static int input_cursor = 0; - -// Network panel state -static char ip_1[4] = ""; -static char ip_2[4] = ""; -static char ip_3[4] = ""; -static char ip_4[4] = ""; -static char dest_ip_1[4] = ""; -static char dest_ip_2[4] = ""; -static char dest_ip_3[4] = ""; -static char dest_ip_4[4] = ""; -static char udp_port[6] = ""; -static char udp_message[128] = ""; -static char net_status[64] = ""; - -// Pattern buffers (128x128) -#define PATTERN_SIZE 128 -static uint32_t pattern_lumberjack[PATTERN_SIZE * PATTERN_SIZE]; -static uint32_t pattern_blue_diamond[PATTERN_SIZE * PATTERN_SIZE]; - -static void generate_lumberjack_pattern(void) { - // Lumberjack pattern: 3x3 repeating cell, scaled 5x - // Red corners, dark grey cross (top/left/right/bottom), black center - uint32_t red = 0xFFDC143C; - uint32_t dark_grey = 0xFF404040; - uint32_t black = 0xFF000000; - int scale = 5; - - // Fill entire pattern with the 3x3 repeating cell - for (int y = 0; y < PATTERN_SIZE; y++) { - for (int x = 0; x < PATTERN_SIZE; x++) { - int cell_x = (x / scale) % 3; - int cell_y = (y / scale) % 3; - - uint32_t color; - - // Determine color based on position in 3x3 cell - if (cell_x == 1 && cell_y == 1) { - // Center: black - color = black; - } else if (cell_x == 1 || cell_y == 1) { - // Cross (top, left, right, bottom): dark grey - color = dark_grey; - } else { - // Corners: red - color = red; - } - - pattern_lumberjack[y * PATTERN_SIZE + x] = color; - } - } -} - -static void generate_blue_diamond_pattern(void) { - // Blue diamond pattern on light blue background - uint32_t bg_color = 0xFFADD8E6; // Light blue - uint32_t diamond_color = 0xFF0000CD; // Medium blue - - for (int y = 0; y < PATTERN_SIZE; y++) { - for (int x = 0; x < PATTERN_SIZE; x++) { - pattern_blue_diamond[y * PATTERN_SIZE + x] = bg_color; - } - } - - // Draw diamonds (centered at 32, 32 and 96, 96) - for (int dy = -24; dy <= 24; dy++) { - for (int dx = -24; dx <= 24; dx++) { - // Diamond shape: |dx| + |dy| <= 24 - int abs_dx = dx < 0 ? -dx : dx; - int abs_dy = dy < 0 ? -dy : dy; - if (abs_dx + abs_dy <= 24) { - // Top-left diamond - int x1 = 32 + dx; - int y1 = 32 + dy; - if (x1 >= 0 && x1 < PATTERN_SIZE && y1 >= 0 && y1 < PATTERN_SIZE) { - pattern_blue_diamond[y1 * PATTERN_SIZE + x1] = diamond_color; - } - - // Bottom-right diamond - int x2 = 96 + dx; - int y2 = 96 + dy; - if (x2 >= 0 && x2 < PATTERN_SIZE && y2 >= 0 && y2 < PATTERN_SIZE) { - pattern_blue_diamond[y2 * PATTERN_SIZE + x2] = diamond_color; - } - } - } - } -} - -static uint32_t parse_rgb_separate(const char *r, const char *g, const char *b) { - int rv = 0, gv = 0, bv = 0; - - // Parse R - for (int i = 0; r[i] && i < 3; i++) { - if (r[i] >= '0' && r[i] <= '9') { - rv = rv * 10 + (r[i] - '0'); - } - } - - // Parse G - for (int i = 0; g[i] && i < 3; i++) { - if (g[i] >= '0' && g[i] <= '9') { - gv = gv * 10 + (g[i] - '0'); - } - } - - // Parse B - for (int i = 0; b[i] && i < 3; i++) { - if (b[i] >= '0' && b[i] <= '9') { - bv = bv * 10 + (b[i] - '0'); - } - } - - // Clamp values - if (rv > 255) rv = 255; - if (gv > 255) gv = 255; - if (bv > 255) bv = 255; - - return 0xFF000000 | (rv << 16) | (gv << 8) | bv; -} - -static void control_panel_paint_main(Window *win) { - int offset_x = win->x + 8; - int offset_y = win->y + 30; - - // Draw settings items with rounded boxes - int item_y = 15; - int item_h = 60; - int item_spacing = 10; - - // Wallpaper Settings - draw_rounded_rect_filled(offset_x, offset_y + item_y, win->w - 16, item_h, 8, COLOR_DARK_PANEL); - // Wallpaper icon: landscape - draw_rect(offset_x + 12, offset_y + item_y + 8, 40, 40, 0xFF87CEEB); // Sky - draw_rect(offset_x + 12, offset_y + item_y + 28, 40, 20, 0xFF90EE90); // Grass - draw_rect(offset_x + 24, offset_y + item_y + 22, 3, 6, 0xFF654321); // Tree trunk - draw_rect(offset_x + 21, offset_y + item_y + 18, 9, 8, 0xFF228B22); // Tree leaves - draw_string(offset_x + 60, offset_y + item_y + 15, "Wallpaper", COLOR_DARK_TEXT); - draw_string(offset_x + 60, offset_y + item_y + 35, "Choose wallpaper", COLOR_DKGRAY); - - // Network Settings - item_y += item_h + item_spacing; - draw_rounded_rect_filled(offset_x, offset_y + item_y, win->w - 16, item_h, 8, COLOR_DARK_PANEL); - // Network icon - draw_rect(offset_x + 18, offset_y + item_y + 12, 24, 24, 0xFF4169E1); - draw_rect(offset_x + 22, offset_y + item_y + 16, 16, 16, 0xFF87CEEB); - draw_string(offset_x + 60, offset_y + item_y + 15, "Network", COLOR_DARK_TEXT); - draw_string(offset_x + 60, offset_y + item_y + 35, "Internet and connectivity", COLOR_DKGRAY); - - // Desktop Settings - item_y += item_h + item_spacing; - if (offset_y + item_y + item_h < win->y + win->h) { - draw_rounded_rect_filled(offset_x, offset_y + item_y, win->w - 16, item_h, 8, COLOR_DARK_PANEL); - // Desktop icon: folder - draw_rect(offset_x + 12, offset_y + item_y + 10, 36, 8, 0xFFE0C060); - draw_rect(offset_x + 12, offset_y + item_y + 18, 36, 22, 0xFFD4A574); - draw_string(offset_x + 60, offset_y + item_y + 15, "Desktop", COLOR_DARK_TEXT); - draw_string(offset_x + 60, offset_y + item_y + 35, "Desktop alignment", COLOR_DKGRAY); - } - - // Mouse Settings - item_y += item_h + item_spacing; - draw_rounded_rect_filled(offset_x, offset_y + item_y, win->w - 16, item_h, 8, COLOR_DARK_PANEL); - // Mouse icon - draw_rect(offset_x + 18, offset_y + item_y + 8, 20, 28, 0xFFD3D3D3); - draw_rect(offset_x + 20, offset_y + item_y + 10, 16, 10, 0xFFB0B0B0); - draw_string(offset_x + 60, offset_y + item_y + 15, "Mouse", COLOR_DARK_TEXT); - draw_string(offset_x + 60, offset_y + item_y + 35, "Pointer settings", COLOR_DKGRAY); -} - -static void control_panel_paint_wallpaper(Window *win) { - int offset_x = win->x + 8; - int offset_y = win->y + 30; - - // Back button (rounded) - padded lower to avoid title bar - draw_rounded_rect_filled(offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); - draw_string(offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); - - draw_string(offset_x, offset_y + 40, "Presets:", COLOR_DARK_TEXT); - - // Color buttons (rounded) - 30% wider - int button_y = offset_y + 65; - int button_x = offset_x; - - // Coffee button - draw_rounded_rect_filled(button_x, button_y, 91, 25, 6, COLOR_DARK_PANEL); - draw_rect(button_x + 8, button_y + 6, 18, 13, COLOR_COFFEE); - draw_string(button_x + 35, button_y + 8, "Coffee", COLOR_DARK_TEXT); - - // Teal button - draw_rounded_rect_filled(button_x + 100, button_y, 91, 25, 6, COLOR_DARK_PANEL); - draw_rect(button_x + 108, button_y + 6, 18, 13, COLOR_TEAL); - draw_string(button_x + 135, button_y + 8, "Teal", COLOR_DARK_TEXT); - - // Green button - draw_rounded_rect_filled(button_x + 200, button_y, 91, 25, 6, COLOR_DARK_PANEL); - draw_rect(button_x + 208, button_y + 6, 18, 13, COLOR_GREEN); - draw_string(button_x + 235, button_y + 8, "Green", COLOR_DARK_TEXT); - - // Blue button - button_y += 35; - draw_rounded_rect_filled(button_x, button_y, 91, 25, 6, COLOR_DARK_PANEL); - draw_rect(button_x + 8, button_y + 6, 18, 13, COLOR_BLUE_BG); - draw_string(button_x + 35, button_y + 8, "Blue", COLOR_DARK_TEXT); - - // Purple button - draw_rounded_rect_filled(button_x + 100, button_y, 91, 25, 6, COLOR_DARK_PANEL); - draw_rect(button_x + 108, button_y + 6, 18, 13, COLOR_PURPLE); - draw_string(button_x + 132, button_y + 8, "Purple", COLOR_DARK_TEXT); - - // Grey button - draw_rounded_rect_filled(button_x + 200, button_y, 91, 25, 6, COLOR_DARK_PANEL); - draw_rect(button_x + 208, button_y + 6, 18, 13, COLOR_GREY); - draw_string(button_x + 235, button_y + 8, "Grey", COLOR_DARK_TEXT); - - // Pattern section - button_y += 40; - draw_string(offset_x, button_y, "Patterns:", COLOR_DARK_TEXT); - - button_y += 20; - - // Lumberjack pattern button - 20% wider - draw_rounded_rect_filled(button_x, button_y, 132, 25, 6, COLOR_DARK_PANEL); - // Draw small pattern preview (3x3 repeating) - for (int py = 0; py < 10; py++) { - for (int px = 0; px < 12; px++) { - int cell_x = px % 3; - int cell_y = py % 3; - uint32_t color = (cell_x == 1 && cell_y == 1) ? 0xFF000000 : - (cell_x == 1 || cell_y == 1) ? 0xFF404040 : 0xFFDC143C; - draw_rect(button_x + 8 + px, button_y + 7 + py, 1, 1, color); - } - } - draw_string(button_x + 28, button_y + 8, "Lumberjack", COLOR_DARK_TEXT); - - // Blue Diamond pattern button - 20% wider - draw_rounded_rect_filled(button_x + 145, button_y, 132, 25, 6, COLOR_DARK_PANEL); - // Draw small diamond preview - for (int py = 0; py < 8; py++) { - for (int px = 0; px < 10; px++) { - int cx = px - 5; - int cy = py - 4; - int abs_cx = cx < 0 ? -cx : cx; - int abs_cy = cy < 0 ? -cy : cy; - uint32_t color = (abs_cx + abs_cy <= 3) ? 0xFF0000CD : 0xFFADD8E6; - draw_rect(button_x + 153 + px, button_y + 8 + py, 1, 1, color); - } - } - draw_string(button_x + 165, button_y + 8, "Blue Diamond", COLOR_DARK_TEXT); - - // Custom color section - button_y += 40; - draw_string(offset_x, button_y, "Custom color:", COLOR_DARK_TEXT); - - button_y += 20; - - // R input box (dark mode) - draw_string(button_x, button_y + 4, "R:", COLOR_DARK_TEXT); - draw_rounded_rect_filled(button_x + 25, button_y, 50, 18, 4, COLOR_DARK_PANEL); - draw_string(button_x + 30, button_y + 4, rgb_r, (focused_field == 0) ? 0xFFFF6B6B : COLOR_DARK_TEXT); - if (focused_field == 0) { - int cursor_x = button_x + 30 + input_cursor * 8; - draw_rect(cursor_x, button_y + 4, 1, 9, 0xFFFF6B6B); - } - - // G input box (dark mode) - draw_string(button_x + 90, button_y + 4, "G:", COLOR_DARK_TEXT); - draw_rounded_rect_filled(button_x + 115, button_y, 50, 18, 4, COLOR_DARK_PANEL); - draw_string(button_x + 120, button_y + 4, rgb_g, (focused_field == 1) ? 0xFF90EE90 : COLOR_DARK_TEXT); - if (focused_field == 1) { - int cursor_x = button_x + 120 + input_cursor * 8; - draw_rect(cursor_x, button_y + 4, 1, 9, 0xFF90EE90); - } - - // B input box (dark mode) - draw_string(button_x + 180, button_y + 4, "B:", COLOR_DARK_TEXT); - draw_rounded_rect_filled(button_x + 205, button_y, 50, 18, 4, COLOR_DARK_PANEL); - draw_string(button_x + 210, button_y + 4, rgb_b, (focused_field == 2) ? 0xFF87CEEB : COLOR_DARK_TEXT); - if (focused_field == 2) { - int cursor_x = button_x + 210 + input_cursor * 8; - draw_rect(cursor_x, button_y + 4, 1, 9, 0xFF87CEEB); - } - - // Apply button (rounded) - draw_rounded_rect_filled(button_x, button_y + 25, 70, 25, 6, COLOR_DARK_PANEL); - draw_string(button_x + 18, button_y + 33, "Apply", COLOR_DARK_TEXT); - - // Wallpaper Images section - button_y += 60; - draw_string(offset_x, button_y, "Wallpapers:", COLOR_DARK_TEXT); - - button_y += 20; - - // Draw Moon thumbnail (pre-generated at init time) - uint32_t *moon_thumb = wallpaper_get_thumb(0); - draw_rounded_rect_filled(button_x, button_y, WALLPAPER_THUMB_W + 8, WALLPAPER_THUMB_H + 24, 6, COLOR_DARK_PANEL); - if (moon_thumb && wallpaper_thumb_valid(0)) { - for (int ty = 0; ty < WALLPAPER_THUMB_H; ty++) { - for (int tx = 0; tx < WALLPAPER_THUMB_W; tx++) { - put_pixel(button_x + 4 + tx, button_y + 4 + ty, moon_thumb[ty * WALLPAPER_THUMB_W + tx]); - } - } - } else { - draw_string(button_x + 20, button_y + 30, "Error", 0xFFFF4444); - } - draw_string(button_x + 30, button_y + WALLPAPER_THUMB_H + 8, "Moon", COLOR_DARK_TEXT); - - // Draw Mountain thumbnail - uint32_t *mtn_thumb = wallpaper_get_thumb(1); - int thumb2_x = button_x + WALLPAPER_THUMB_W + 20; - draw_rounded_rect_filled(thumb2_x, button_y, WALLPAPER_THUMB_W + 8, WALLPAPER_THUMB_H + 24, 6, COLOR_DARK_PANEL); - if (mtn_thumb && wallpaper_thumb_valid(1)) { - for (int ty = 0; ty < WALLPAPER_THUMB_H; ty++) { - for (int tx = 0; tx < WALLPAPER_THUMB_W; tx++) { - put_pixel(thumb2_x + 4 + tx, button_y + 4 + ty, mtn_thumb[ty * WALLPAPER_THUMB_W + tx]); - } - } - } else { - draw_string(thumb2_x + 20, button_y + 30, "Error", 0xFFFF4444); - } - draw_string(thumb2_x + 16, button_y + WALLPAPER_THUMB_H + 8, "Mountain", COLOR_DARK_TEXT); -} - -static void draw_input_box(int x, int y, int width, const char *text, bool focused, int cursor_pos) { - // Draw box - draw_rect(x, y, width, 18, 0xFFFFFFFF); - draw_rect(x, y, width, 1, COLOR_BLACK); - draw_rect(x, y, 1, 18, COLOR_BLACK); - draw_rect(x + width - 1, y, 1, 18, COLOR_BLACK); - draw_rect(x, y + 17, width, 1, COLOR_BLACK); - - // Draw text - uint32_t text_color = focused ? 0xFF0000FF : COLOR_BLACK; - draw_string(x + 3, y + 4, text, text_color); - - // Draw cursor if focused - if (focused) { - int cursor_x = x + 3 + cursor_pos * 8; - draw_rect(cursor_x, y + 4, 1, 9, 0xFF0000FF); - } -} - -static void control_panel_paint_network(Window *win) { - int offset_x = win->x + 8; - int offset_y = win->y + 30; - - // Back button (rounded) - padded lower to avoid title bar - draw_rounded_rect_filled(offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); - draw_string(offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); - - // Network Init Button - draw_string(offset_x, offset_y + 40, "Network:", COLOR_DARK_TEXT); - draw_rounded_rect_filled(offset_x, offset_y + 55, 140, 25, 6, COLOR_DARK_PANEL); - draw_string(offset_x + 30, offset_y + 63, "Init Network", COLOR_DARK_TEXT); - - // Status message - if (net_status[0] != '\0') { - draw_string(offset_x + 150, offset_y + 63, net_status, 0xFF90EE90); - } - - // Set IP Section - int section_y = offset_y + 85; - draw_string(offset_x, section_y, "Set Static IP:", COLOR_DARK_TEXT); - - section_y += 20; - // IP input boxes (4 octets, dark mode rounded) - with cursor indicators - uint32_t ip1_color = (focused_field == 0) ? 0xFF4A90E2 : COLOR_DARK_TEXT; - draw_rounded_rect_filled(offset_x, section_y, 35, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 5, section_y + 4, ip_1, ip1_color); - if (focused_field == 0) draw_rect(offset_x + 5 + input_cursor * 8, section_y + 4, 1, 9, 0xFF4A90E2); - draw_string(offset_x + 40, section_y + 4, ".", COLOR_DARK_TEXT); - - uint32_t ip2_color = (focused_field == 1) ? 0xFF4A90E2 : COLOR_DARK_TEXT; - draw_rounded_rect_filled(offset_x + 50, section_y, 35, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 55, section_y + 4, ip_2, ip2_color); - if (focused_field == 1) draw_rect(offset_x + 55 + input_cursor * 8, section_y + 4, 1, 9, 0xFF4A90E2); - draw_string(offset_x + 90, section_y + 4, ".", COLOR_DARK_TEXT); - - uint32_t ip3_color = (focused_field == 2) ? 0xFF4A90E2 : COLOR_DARK_TEXT; - draw_rounded_rect_filled(offset_x + 100, section_y, 35, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 105, section_y + 4, ip_3, ip3_color); - if (focused_field == 2) draw_rect(offset_x + 105 + input_cursor * 8, section_y + 4, 1, 9, 0xFF4A90E2); - draw_string(offset_x + 140, section_y + 4, ".", COLOR_DARK_TEXT); - - uint32_t ip4_color = (focused_field == 3) ? 0xFF4A90E2 : COLOR_DARK_TEXT; - draw_rounded_rect_filled(offset_x + 150, section_y, 35, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 155, section_y + 4, ip_4, ip4_color); - if (focused_field == 3) draw_rect(offset_x + 155 + input_cursor * 8, section_y + 4, 1, 9, 0xFF4A90E2); - - // Apply IP button (rounded) - draw_rounded_rect_filled(offset_x + 200, section_y, 70, 20, 6, COLOR_DARK_PANEL); - draw_string(offset_x + 218, section_y + 4, "Apply", COLOR_DARK_TEXT); - - // Send UDP Section - section_y += 30; - draw_string(offset_x, section_y, "Send UDP Message:", COLOR_DARK_TEXT); - - section_y += 20; - draw_string(offset_x, section_y + 4, "IP:", COLOR_DARK_TEXT); - uint32_t dip1_color = (focused_field == 4) ? 0xFF4A90E2 : COLOR_DARK_TEXT; - draw_rounded_rect_filled(offset_x + 25, section_y, 35, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 30, section_y + 4, dest_ip_1, dip1_color); - if (focused_field == 4) draw_rect(offset_x + 30 + input_cursor * 8, section_y + 4, 1, 9, 0xFF4A90E2); - draw_string(offset_x + 65, section_y + 4, ".", COLOR_DARK_TEXT); - - uint32_t dip2_color = (focused_field == 5) ? 0xFF4A90E2 : COLOR_DARK_TEXT; - draw_rounded_rect_filled(offset_x + 70, section_y, 35, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 75, section_y + 4, dest_ip_2, dip2_color); - if (focused_field == 5) draw_rect(offset_x + 75 + input_cursor * 8, section_y + 4, 1, 9, 0xFF4A90E2); - draw_string(offset_x + 110, section_y + 4, ".", COLOR_DARK_TEXT); - - uint32_t dip3_color = (focused_field == 6) ? 0xFF4A90E2 : COLOR_DARK_TEXT; - draw_rounded_rect_filled(offset_x + 115, section_y, 35, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 120, section_y + 4, dest_ip_3, dip3_color); - if (focused_field == 6) draw_rect(offset_x + 120 + input_cursor * 8, section_y + 4, 1, 9, 0xFF4A90E2); - draw_string(offset_x + 155, section_y + 4, ".", COLOR_DARK_TEXT); - - uint32_t dip4_color = (focused_field == 7) ? 0xFF4A90E2 : COLOR_DARK_TEXT; - draw_rounded_rect_filled(offset_x + 160, section_y, 35, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 165, section_y + 4, dest_ip_4, dip4_color); - if (focused_field == 7) draw_rect(offset_x + 165 + input_cursor * 8, section_y + 4, 1, 9, 0xFF4A90E2); - - section_y += 25; - draw_string(offset_x, section_y + 4, "Port:", COLOR_DARK_TEXT); - uint32_t port_color = (focused_field == 8) ? 0xFF4A90E2 : COLOR_DARK_TEXT; - draw_rounded_rect_filled(offset_x + 40, section_y, 60, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 45, section_y + 4, udp_port, port_color); - if (focused_field == 8) draw_rect(offset_x + 45 + input_cursor * 8, section_y + 4, 1, 9, 0xFF4A90E2); - - section_y += 25; - draw_string(offset_x, section_y + 4, "Msg:", COLOR_DARK_TEXT); - uint32_t msg_color = (focused_field == 9) ? 0xFF4A90E2 : COLOR_DARK_TEXT; - draw_rounded_rect_filled(offset_x + 40, section_y, 180, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 45, section_y + 4, udp_message, msg_color); - if (focused_field == 9) draw_rect(offset_x + 45 + input_cursor * 8, section_y + 4, 1, 9, 0xFF4A90E2); - - // Send button (rounded) - section_y += 25; - draw_rounded_rect_filled(offset_x, section_y, 80, 25, 6, COLOR_DARK_PANEL); - draw_string(offset_x + 22, section_y + 7, "Send", COLOR_DARK_TEXT); -} - - -static void control_panel_paint_desktop(Window *win) { - int offset_x = win->x + 8; - int offset_y = win->y + 30; - - // Back button (rounded) - padded lower to avoid title bar - draw_rounded_rect_filled(offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); - draw_string(offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); - draw_string(offset_x, offset_y + 40, "Desktop Settings:", COLOR_DARK_TEXT); - - int section_y = offset_y + 65; - - // Snap to Grid checkbox (rounded) - draw_rounded_rect_filled(offset_x, section_y, 16, 16, 3, COLOR_DARK_PANEL); - if (desktop_snap_to_grid) draw_string(offset_x + 3, section_y + 1, "✓", 0xFF90EE90); - draw_string(offset_x + 25, section_y + 3, "Snap to Grid", COLOR_DARK_TEXT); - - // Auto Align checkbox (rounded) - section_y += 25; - draw_rounded_rect_filled(offset_x, section_y, 16, 16, 3, COLOR_DARK_PANEL); - if (desktop_auto_align) draw_string(offset_x + 3, section_y + 1, "✓", 0xFF90EE90); - draw_string(offset_x + 25, section_y + 3, "Auto Align Icons", COLOR_DARK_TEXT); - - // Max Rows - section_y += 30; - draw_string(offset_x, section_y + 3, "Apps per column:", COLOR_DARK_TEXT); - draw_rounded_rect_filled(offset_x + 130, section_y, 20, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 135, section_y + 4, "-", COLOR_DARK_TEXT); - char num[4]; num[0] = '0' + (desktop_max_rows_per_col / 10); num[1] = '0' + (desktop_max_rows_per_col % 10); num[2] = 0; - if (num[0] == '0') { num[0] = num[1]; num[1] = 0; } - draw_string(offset_x + 160, section_y + 5, num, COLOR_DARK_TEXT); - draw_rounded_rect_filled(offset_x + 180, section_y, 20, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 186, section_y + 4, "+", COLOR_DARK_TEXT); - - // Max Cols - section_y += 30; - draw_string(offset_x, section_y + 3, "Columns:", COLOR_DARK_TEXT); - draw_rounded_rect_filled(offset_x + 130, section_y, 20, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 135, section_y + 4, "-", COLOR_DARK_TEXT); - char num_c[4]; num_c[0] = '0' + (desktop_max_cols / 10); num_c[1] = '0' + (desktop_max_cols % 10); num_c[2] = 0; - if (num_c[0] == '0') { num_c[0] = num_c[1]; num_c[1] = 0; } - draw_string(offset_x + 160, section_y + 5, num_c, COLOR_DARK_TEXT); - draw_rounded_rect_filled(offset_x + 180, section_y, 20, 20, 4, COLOR_DARK_PANEL); - draw_string(offset_x + 186, section_y + 4, "+", COLOR_DARK_TEXT); -} - - -static void control_panel_paint_mouse(Window *win) { - int offset_x = win->x + 8; - int offset_y = win->y + 30; - - // Back button (rounded) - padded lower to avoid title bar - draw_rounded_rect_filled(offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); - draw_string(offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); - draw_string(offset_x, offset_y + 40, "Mouse Settings:", COLOR_DARK_TEXT); - - int section_y = offset_y + 65; - draw_string(offset_x, section_y, "Speed:", COLOR_DARK_TEXT); - - // Slider track (rounded background) - draw_rounded_rect_filled(offset_x + 60, section_y + 8, 200, 8, 4, COLOR_DARK_PANEL); - - // Slider knob (range 1-50, default 10) - rounded with blue color - int knob_x = offset_x + 60 + (mouse_speed - 1) * 190 / 49; - draw_rounded_rect_filled(knob_x, section_y + 2, 10, 14, 3, 0xFF4A90E2); - - draw_string(offset_x + 270, section_y + 4, "x", COLOR_DARK_TEXT); - char speed_str[4]; - cli_itoa(mouse_speed, speed_str); - draw_string(offset_x + 280, section_y + 4, speed_str, COLOR_DARK_TEXT); -} - -static void control_panel_paint(Window *win) { - if (current_view == VIEW_MAIN) { - control_panel_paint_main(win); - } else if (current_view == VIEW_WALLPAPER) { - control_panel_paint_wallpaper(win); - } else if (current_view == VIEW_NETWORK) { - control_panel_paint_network(win); - } else if (current_view == VIEW_DESKTOP) { - control_panel_paint_desktop(win); - } else if (current_view == VIEW_MOUSE) { - control_panel_paint_mouse(win); - } -} - -static void control_panel_handle_click(Window *win, int x, int y) { - (void)win; - - if (current_view == VIEW_MAIN) { - int offset_x = 8; - int offset_y = 30; - - // Settings items layout: each item is 60px tall with 10px spacing - int item_h = 60; - int item_spacing = 10; - - // Check wallpaper button click (entire card area) - int item_y = offset_y + 15; - if (x >= offset_x && x < win->w - 8 && - y >= item_y && y < item_y + item_h) { - current_view = VIEW_WALLPAPER; - focused_field = -1; - } - - // Check network button click - item_y += item_h + item_spacing; - if (x >= offset_x && x < win->w - 8 && - y >= item_y && y < item_y + item_h) { - current_view = VIEW_NETWORK; - focused_field = -1; - } - - // Check desktop button - item_y += item_h + item_spacing; - if (x >= offset_x && x < win->w - 8 && - y >= item_y && y < item_y + item_h) { - current_view = VIEW_DESKTOP; - } - - // Check mouse button - item_y += item_h + item_spacing; - if (offset_y + item_y + item_h < win->y + win->h && - x >= offset_x && x < win->w - 8 && - y >= item_y && y < item_y + item_h) { - current_view = VIEW_MOUSE; - } - } else if (current_view == VIEW_WALLPAPER) { - int offset_x = 8; - int offset_y = 30; - int button_y = offset_y + 65; - int button_x = offset_x; - - // Back button - if (x >= offset_x && x < offset_x + 80 && - y >= offset_y + 5 && y < offset_y + 30) { - current_view = VIEW_MAIN; - return; - } - - // Check Coffee button (91px wide) - if (x >= button_x && x < button_x + 91 && y >= button_y && y < button_y + 25) { - graphics_set_bg_color(COLOR_COFFEE); - wm_refresh(); - return; - } - - // Check Teal button - if (x >= button_x + 100 && x < button_x + 191 && y >= button_y && y < button_y + 25) { - graphics_set_bg_color(COLOR_TEAL); - wm_refresh(); - return; - } - - // Check Green button - if (x >= button_x + 200 && x < button_x + 291 && y >= button_y && y < button_y + 25) { - graphics_set_bg_color(COLOR_GREEN); - wm_refresh(); - return; - } - - // Check Blue button - button_y += 35; - if (x >= button_x && x < button_x + 91 && y >= button_y && y < button_y + 25) { - graphics_set_bg_color(COLOR_BLUE_BG); - wm_refresh(); - return; - } - - // Check Purple button - if (x >= button_x + 100 && x < button_x + 191 && y >= button_y && y < button_y + 25) { - graphics_set_bg_color(COLOR_PURPLE); - wm_refresh(); - return; - } - - // Check Grey button - if (x >= button_x + 200 && x < button_x + 291 && y >= button_y && y < button_y + 25) { - graphics_set_bg_color(COLOR_GREY); - wm_refresh(); - return; - } - - // Pattern section - button_y += 40; - button_y += 20; - - // Check Lumberjack pattern button (132px wide) - if (x >= button_x && x < button_x + 132 && y >= button_y && y < button_y + 25) { - graphics_set_bg_pattern(pattern_lumberjack); - wm_refresh(); - return; - } - - // Check Blue Diamond pattern button (132px wide) - if (x >= button_x + 145 && x < button_x + 277 && y >= button_y && y < button_y + 25) { - graphics_set_bg_pattern(pattern_blue_diamond); - wm_refresh(); - return; - } - - // Custom RGB section - button_y += 40; - button_y += 20; - - // Check R input box click - if (x >= button_x + 25 && x < button_x + 75 && y >= button_y && y < button_y + 18) { - if (focused_field != 0) { - rgb_r[0] = '\0'; // Clear when first focused - } - focused_field = 0; - input_cursor = 0; - return; - } - - // Check G input box click - if (x >= button_x + 115 && x < button_x + 165 && y >= button_y && y < button_y + 18) { - if (focused_field != 1) { - rgb_g[0] = '\0'; // Clear when first focused - } - focused_field = 1; - input_cursor = 0; - return; - } - - // Check B input box click - if (x >= button_x + 205 && x < button_x + 255 && y >= button_y && y < button_y + 18) { - if (focused_field != 2) { - rgb_b[0] = '\0'; // Clear when first focused - } - focused_field = 2; - input_cursor = 0; - return; - } - - // Check Apply button - if (x >= button_x && x < button_x + 70 && y >= button_y + 25 && y < button_y + 50) { - graphics_set_bg_color(parse_rgb_separate(rgb_r, rgb_g, rgb_b)); - wm_refresh(); - return; - } - - // Wallpaper image thumbnails section - button_y += 60; - button_y += 20; - - // Check Moon thumbnail click - if (x >= button_x && x < button_x + WALLPAPER_THUMB_W + 8 && y >= button_y && y < button_y + WALLPAPER_THUMB_H + 24) { - wallpaper_request_set(0); - return; - } - - // Check Mountain thumbnail click - int thumb2_x = button_x + WALLPAPER_THUMB_W + 20; - if (x >= thumb2_x && x < thumb2_x + WALLPAPER_THUMB_W + 8 && y >= button_y && y < button_y + WALLPAPER_THUMB_H + 24) { - wallpaper_request_set(1); - return; - } - } else if (current_view == VIEW_NETWORK) { - int offset_x = 8; - int offset_y = 30; - - // Back button - if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { - current_view = VIEW_MAIN; - focused_field = -1; - return; - } - - // Init Network button (140px wide now) - if (x >= offset_x && x < offset_x + 140 && y >= offset_y + 55 && y < offset_y + 80) { - int result = network_init(); - if (result == 0) { - net_status[0] = 'I'; net_status[1] = 'n'; net_status[2] = 'i'; - net_status[3] = 't'; net_status[4] = 'e'; net_status[5] = 'd'; - net_status[6] = '\0'; - } else { - net_status[0] = 'F'; net_status[1] = 'a'; net_status[2] = 'i'; - net_status[3] = 'l'; net_status[4] = 'e'; net_status[5] = 'd'; - net_status[6] = '\0'; - } - return; - } - - int section_y = offset_y + 85 + 20; - - // IP octet 1 - if (x >= offset_x && x < offset_x + 35 && y >= section_y && y < section_y + 20) { - focused_field = 0; - input_cursor = 0; - return; - } - // IP octet 2 - if (x >= offset_x + 50 && x < offset_x + 85 && y >= section_y && y < section_y + 20) { - focused_field = 1; - input_cursor = 0; - return; - } - // IP octet 3 - if (x >= offset_x + 100 && x < offset_x + 135 && y >= section_y && y < section_y + 20) { - focused_field = 2; - input_cursor = 0; - return; - } - // IP octet 4 - if (x >= offset_x + 150 && x < offset_x + 185 && y >= section_y && y < section_y + 20) { - focused_field = 3; - input_cursor = 0; - return; - } - - // Apply IP button - if (x >= offset_x + 200 && x < offset_x + 270 && y >= section_y && y < section_y + 20) { - ipv4_address_t ip; - ip.bytes[0] = 0; ip.bytes[1] = 0; ip.bytes[2] = 0; ip.bytes[3] = 0; - - // Parse IP octets - for (int i = 0; ip_1[i] && i < 3; i++) { - if (ip_1[i] >= '0' && ip_1[i] <= '9') { - ip.bytes[0] = ip.bytes[0] * 10 + (ip_1[i] - '0'); - } - } - for (int i = 0; ip_2[i] && i < 3; i++) { - if (ip_2[i] >= '0' && ip_2[i] <= '9') { - ip.bytes[1] = ip.bytes[1] * 10 + (ip_2[i] - '0'); - } - } - for (int i = 0; ip_3[i] && i < 3; i++) { - if (ip_3[i] >= '0' && ip_3[i] <= '9') { - ip.bytes[2] = ip.bytes[2] * 10 + (ip_3[i] - '0'); - } - } - for (int i = 0; ip_4[i] && i < 3; i++) { - if (ip_4[i] >= '0' && ip_4[i] <= '9') { - ip.bytes[3] = ip.bytes[3] * 10 + (ip_4[i] - '0'); - } - } - - network_set_ipv4_address(&ip); - net_status[0] = 'I'; net_status[1] = 'P'; net_status[2] = ' '; - net_status[3] = 's'; net_status[4] = 'e'; net_status[5] = 't'; - net_status[6] = '\0'; - return; - } - - section_y += 30; - - // Dest IP octets - if (x >= offset_x + 25 && x < offset_x + 60 && y >= section_y && y < section_y + 20) { - focused_field = 4; - input_cursor = 0; - return; - } - if (x >= offset_x + 70 && x < offset_x + 105 && y >= section_y && y < section_y + 20) { - focused_field = 5; - input_cursor = 0; - return; - } - if (x >= offset_x + 115 && x < offset_x + 150 && y >= section_y && y < section_y + 20) { - focused_field = 6; - input_cursor = 0; - return; - } - if (x >= offset_x + 160 && x < offset_x + 195 && y >= section_y && y < section_y + 20) { - focused_field = 7; - input_cursor = 0; - return; - } - - section_y += 25; - - // Port field - if (x >= offset_x + 40 && x < offset_x + 100 && y >= section_y && y < section_y + 20) { - focused_field = 8; - input_cursor = 0; - return; - } - - section_y += 25; - - // Message field - if (x >= offset_x + 40 && x < offset_x + 220 && y >= section_y && y < section_y + 20) { - focused_field = 9; - input_cursor = 0; - return; - } - - section_y += 25; - - // Send button - if (x >= offset_x && x < offset_x + 80 && y >= section_y && y < section_y + 25) { - ipv4_address_t dest_ip; - dest_ip.bytes[0] = 0; dest_ip.bytes[1] = 0; dest_ip.bytes[2] = 0; dest_ip.bytes[3] = 0; - - // Parse dest IP - for (int i = 0; dest_ip_1[i] && i < 3; i++) { - if (dest_ip_1[i] >= '0' && dest_ip_1[i] <= '9') { - dest_ip.bytes[0] = dest_ip.bytes[0] * 10 + (dest_ip_1[i] - '0'); - } - } - for (int i = 0; dest_ip_2[i] && i < 3; i++) { - if (dest_ip_2[i] >= '0' && dest_ip_2[i] <= '9') { - dest_ip.bytes[1] = dest_ip.bytes[1] * 10 + (dest_ip_2[i] - '0'); - } - } - for (int i = 0; dest_ip_3[i] && i < 3; i++) { - if (dest_ip_3[i] >= '0' && dest_ip_3[i] <= '9') { - dest_ip.bytes[2] = dest_ip.bytes[2] * 10 + (dest_ip_3[i] - '0'); - } - } - for (int i = 0; dest_ip_4[i] && i < 3; i++) { - if (dest_ip_4[i] >= '0' && dest_ip_4[i] <= '9') { - dest_ip.bytes[3] = dest_ip.bytes[3] * 10 + (dest_ip_4[i] - '0'); - } - } - - // Parse port - int port = 0; - for (int i = 0; udp_port[i] && i < 5; i++) { - if (udp_port[i] >= '0' && udp_port[i] <= '9') { - port = port * 10 + (udp_port[i] - '0'); - } - } - - // Get message length - int msg_len = 0; - while (udp_message[msg_len] && msg_len < 127) msg_len++; - - if (msg_len > 0 && port > 0) { - int result = udp_send_packet(&dest_ip, (uint16_t)port, 54321, udp_message, (size_t)msg_len); - if (result == 0) { - net_status[0] = 'S'; net_status[1] = 'e'; net_status[2] = 'n'; - net_status[3] = 't'; net_status[4] = '\0'; - } else { - net_status[0] = 'F'; net_status[1] = 'a'; net_status[2] = 'i'; - net_status[3] = 'l'; net_status[4] = '\0'; - } - } - return; - } - } else if (current_view == VIEW_DESKTOP) { - int offset_x = 8; - int offset_y = 30; - - // Back button - if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { - current_view = VIEW_MAIN; - return; - } - - int section_y = offset_y + 65; - // Snap toggle - click on checkbox only - if (x >= offset_x && x < offset_x + 16 && y >= section_y && y < section_y + 16) { - desktop_snap_to_grid = !desktop_snap_to_grid; - // If Snap is turned OFF, Auto Align must be OFF - if (!desktop_snap_to_grid) { - desktop_auto_align = false; - } - wm_refresh_desktop(); - return; - } - - // Auto Align toggle - click on checkbox only - section_y += 25; - if (x >= offset_x && x < offset_x + 16 && y >= section_y && y < section_y + 16) { - desktop_auto_align = !desktop_auto_align; - // If Auto Align is turned ON, Snap must be ON - if (desktop_auto_align) { - desktop_snap_to_grid = true; - } - wm_refresh_desktop(); - return; - } - - // Rows adjust - section_y += 25; - if (x >= offset_x + 130 && x < offset_x + 150 && y >= section_y && y < section_y + 20) { - if (desktop_max_rows_per_col > 1) { - if (desktop_max_cols * (desktop_max_rows_per_col - 1) < wm_get_desktop_icon_count()) { - wm_show_message("Error", "Cannot reduce rows: too many files!"); - } else { - desktop_max_rows_per_col--; - wm_refresh_desktop(); - } - } - } - if (x >= offset_x + 180 && x < offset_x + 200 && y >= section_y && y < section_y + 20) { - if (desktop_max_rows_per_col < 15) desktop_max_rows_per_col++; - wm_refresh_desktop(); - } - - // Cols adjust - section_y += 25; - if (x >= offset_x + 130 && x < offset_x + 150 && y >= section_y && y < section_y + 20) { - if (desktop_max_cols > 1) { - if ((desktop_max_cols - 1) * desktop_max_rows_per_col < wm_get_desktop_icon_count()) { - wm_show_message("Error", "Cannot reduce cols: too many files!"); - } else { - desktop_max_cols--; - wm_refresh_desktop(); - } - } - } - if (x >= offset_x + 180 && x < offset_x + 200 && y >= section_y && y < section_y + 20) { - desktop_max_cols++; - wm_refresh_desktop(); - } - } else if (current_view == VIEW_MOUSE) { - int offset_x = 8; - int offset_y = 30; - - // Back button - if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { - current_view = VIEW_MAIN; - return; - } - - int section_y = offset_y + 65; - // Slider interaction - if (x >= offset_x + 60 && x <= offset_x + 260 && y >= section_y && y <= section_y + 20) { - int new_speed = 1 + (x - (offset_x + 60)) * 49 / 200; - if (new_speed < 1) new_speed = 1; - if (new_speed > 50) new_speed = 50; - mouse_speed = new_speed; - return; - } - } -} - -static void control_panel_handle_key(Window *win, char c) { - (void)win; - - if (focused_field < 0) return; - - if (current_view == VIEW_WALLPAPER) { - // Get the currently focused field buffer - char *focused_buffer = NULL; - int max_len = 3; // RGB values are 0-255, max 3 digits - - if (focused_field == 0) { - focused_buffer = rgb_r; - } else if (focused_field == 1) { - focused_buffer = rgb_g; - } else if (focused_field == 2) { - focused_buffer = rgb_b; - } else { - return; - } - - if (c == '\b') { // Backspace - if (input_cursor > 0) { - input_cursor--; - focused_buffer[input_cursor] = '\0'; - } - } else if (c >= '0' && c <= '9') { // Digits only - if (input_cursor < max_len) { - focused_buffer[input_cursor] = c; - input_cursor++; - focused_buffer[input_cursor] = '\0'; - } - } else if (c == '\t') { // Tab - switch to next field - focused_field = (focused_field + 1) % 3; - input_cursor = 0; - } - } else if (current_view == VIEW_NETWORK) { - char *focused_buffer = NULL; - int max_len = 3; // Default for IP octets - - // Select the focused field - if (focused_field == 0) { - focused_buffer = ip_1; - } else if (focused_field == 1) { - focused_buffer = ip_2; - } else if (focused_field == 2) { - focused_buffer = ip_3; - } else if (focused_field == 3) { - focused_buffer = ip_4; - } else if (focused_field == 4) { - focused_buffer = dest_ip_1; - } else if (focused_field == 5) { - focused_buffer = dest_ip_2; - } else if (focused_field == 6) { - focused_buffer = dest_ip_3; - } else if (focused_field == 7) { - focused_buffer = dest_ip_4; - } else if (focused_field == 8) { - focused_buffer = udp_port; - max_len = 5; // Ports up to 65535 - } else if (focused_field == 9) { - focused_buffer = udp_message; - max_len = 127; // Message max length - } else { - return; - } - - if (c == '\b') { // Backspace - if (input_cursor > 0) { - input_cursor--; - focused_buffer[input_cursor] = '\0'; - } - } else if (focused_field <= 8 && c >= '0' && c <= '9') { // Digits only for IP/port - if (input_cursor < max_len) { - focused_buffer[input_cursor] = c; - input_cursor++; - focused_buffer[input_cursor] = '\0'; - } - } else if (focused_field == 9 && c >= 32 && c <= 126) { // All printable chars for message - if (input_cursor < max_len) { - focused_buffer[input_cursor] = c; - input_cursor++; - focused_buffer[input_cursor] = '\0'; - } - } else if (c == '\t') { // Tab - switch to next field - focused_field = (focused_field + 1) % 10; - input_cursor = 0; - } - } -} - -void control_panel_init(void) { - win_control_panel.title = "Settings"; - win_control_panel.x = 200; - win_control_panel.y = 150; - win_control_panel.w = 350; - win_control_panel.h = 500; - win_control_panel.visible = false; - win_control_panel.focused = false; - win_control_panel.z_index = 0; - win_control_panel.paint = control_panel_paint; - win_control_panel.handle_key = control_panel_handle_key; - win_control_panel.handle_click = control_panel_handle_click; - win_control_panel.handle_right_click = NULL; - win_control_panel.buf_len = 0; - win_control_panel.cursor_pos = 0; - - // Generate patterns - generate_lumberjack_pattern(); - generate_blue_diamond_pattern(); -} - -void control_panel_reset(void) { - win_control_panel.focused = false; - current_view = VIEW_MAIN; - focused_field = -1; - input_cursor = 0; -} diff --git a/src/kernel/control_panel.h b/src/kernel/control_panel.h deleted file mode 100644 index 471b7e9..0000000 --- a/src/kernel/control_panel.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef CONTROL_PANEL_H -#define CONTROL_PANEL_H - -#include "wm.h" - -extern Window win_control_panel; - -void control_panel_init(void); -void control_panel_reset(void); - -#endif diff --git a/src/kernel/editor.h b/src/kernel/editor.h deleted file mode 100644 index 2e4653b..0000000 --- a/src/kernel/editor.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef EDITOR_H -#define EDITOR_H - -#include "wm.h" - -extern Window win_editor; - -void editor_init(void); -void editor_open_file(const char *filename); - -#endif diff --git a/src/kernel/explorer.c b/src/kernel/explorer.c index 8b70277..799b823 100644 --- a/src/kernel/explorer.c +++ b/src/kernel/explorer.c @@ -4,29 +4,17 @@ #include "disk.h" #include "wm.h" #include "memory_manager.h" -#include "editor.h" -#include "markdown.h" #include "cmd.h" - #include "process.h" -#include "minesweeper.h" -#include "viewer.h" -#include "control_panel.h" #include "about.h" -#include "paint.h" -#include -#include - -// === File Explorer State === -Window win_explorer; - -#define EXPLORER_MAX_FILES 64 #define EXPLORER_ITEM_HEIGHT 80 #define EXPLORER_ITEM_WIDTH 120 #define EXPLORER_COLS 4 #define EXPLORER_ROWS 4 #define EXPLORER_PADDING 15 +Window win_explorer; + // Dialog states #define DIALOG_NONE 0 #define DIALOG_CREATE_FILE 1 @@ -819,16 +807,13 @@ static void explorer_open_target(const char *path) { if (explorer_str_ends_with(path, ".elf")) { process_create_elf(path, NULL); } else if (explorer_is_markdown_file(path)) { - wm_bring_to_front(&win_markdown); - markdown_open_file(path); + process_create_elf("A:/bin/markdown.elf", path); } else if (explorer_str_ends_with(path, ".pnt")) { - paint_load(path); - wm_bring_to_front(&win_paint); + process_create_elf("A:/bin/paint.elf", path); } else if (explorer_str_ends_with(path, ".jpg") || explorer_str_ends_with(path, ".JPG")) { - viewer_open_file(path); + process_create_elf("A:/bin/viewer.elf", path); } else { - wm_bring_to_front(&win_editor); - editor_open_file(path); + process_create_elf("A:/bin/txtedit.elf", path); } } } @@ -859,9 +844,9 @@ static void explorer_open_item(Window *win, int index) { } else if (explorer_strcmp(state->items[index].name, "Terminal.shortcut") == 0) { target = &win_cmd; cmd_reset(); } else if (explorer_strcmp(state->items[index].name, "Minesweeper.shortcut") == 0) { - target = &win_minesweeper; - } else if (explorer_strcmp(state->items[index].name, "Control Panel.shortcut") == 0) { - target = &win_control_panel; + process_create_elf("/bin/minesweeper.elf", NULL); return; + } else if (explorer_strcmp(state->items[index].name, "Control Panel.shortcut") == 0 || explorer_strcmp(state->items[index].name, "Settings.shortcut") == 0) { + process_create_elf("/bin/settings.elf", NULL); return; } else if (explorer_strcmp(state->items[index].name, "About.shortcut") == 0) { target = &win_about; } else if (explorer_strcmp(state->items[index].name, "Explorer.shortcut") == 0) { @@ -1781,17 +1766,7 @@ static void explorer_handle_file_context_menu_click(Window *win, int x, int y) { state->dialog_input_cursor = explorer_strlen(state->dialog_input); explorer_strcpy(state->dialog_target_path, full_path); } else if (clicked_action == 110) { // Open with Text Editor - win_editor.visible = true; win_editor.focused = true; - int max_z = 0; - for (int i = 0; i < explorer_win_count; i++) if (explorer_wins[i]->z_index > max_z) max_z = explorer_wins[i]->z_index; - if (win_cmd.z_index > max_z) max_z = win_cmd.z_index; - if (win_editor.z_index > max_z) max_z = win_editor.z_index; - if (win_markdown.z_index > max_z) max_z = win_markdown.z_index; - if (win_control_panel.z_index > max_z) max_z = win_control_panel.z_index; - if (win_about.z_index > max_z) max_z = win_about.z_index; - if (win_minesweeper.z_index > max_z) max_z = win_minesweeper.z_index; - win_editor.z_index = max_z + 1; - editor_open_file(full_path); + process_create_elf("A:/bin/txtedit.elf", full_path); } else if (clicked_action == ACTION_RESTORE) { explorer_restore_file(win, state->file_context_menu_item); } else if (clicked_action == ACTION_CREATE_SHORTCUT) { @@ -2024,6 +1999,6 @@ void explorer_reset(void) { ExplorerState *state = (ExplorerState*)win_explorer.data; // Reset explorer to root directory on close/reopen explorer_load_directory(&win_explorer, "A:/"); - win_explorer.focused = false; state->explorer_scroll_row = 0; + win_explorer.focused = false; } diff --git a/src/kernel/fat32.c b/src/kernel/fat32.c index f7b65a0..19677f1 100644 --- a/src/kernel/fat32.c +++ b/src/kernel/fat32.c @@ -577,7 +577,7 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m if (entry[e].attributes & ATTR_VOLUME_ID) continue; // Volume label // Compare name (simplistic 8.3 matching) - char name[12]; + char name[13]; int n = 0; for (int k = 0; k < 8 && entry[e].filename[k] != ' '; k++) name[n++] = entry[e].filename[k]; if (entry[e].extension[0] != ' ') { @@ -973,7 +973,7 @@ static bool realfs_delete(char drive, const char *path) { if (entry[e].attributes & ATTR_VOLUME_ID) continue; // Skip volume label // Format name and compare - char name[12]; + char name[13]; int n = 0; for (int k = 0; k < 8 && entry[e].filename[k] != ' '; k++) name[n++] = entry[e].filename[k]; if (entry[e].extension[0] != ' ') { diff --git a/src/kernel/graphics.c b/src/kernel/graphics.c index 2bfa38e..a428479 100644 --- a/src/kernel/graphics.c +++ b/src/kernel/graphics.c @@ -4,34 +4,27 @@ #include "io.h" static struct limine_framebuffer *g_fb = NULL; -static uint32_t g_bg_color = 0xFF696969; // Dark gray background +static uint32_t g_bg_color = 0xFF696969; -// Pattern support #define PATTERN_SIZE 128 static uint32_t g_bg_pattern[PATTERN_SIZE * PATTERN_SIZE]; static bool g_use_pattern = false; -// Wallpaper image support static uint32_t *g_bg_image = NULL; static int g_bg_image_w = 0; static int g_bg_image_h = 0; static bool g_use_image = false; -// Dirty rectangle tracking static DirtyRect g_dirty = {0, 0, 0, 0, false}; -// Double buffering - allocate a back buffer -// Max screen size: 2048x2048 @ 32bpp = 16MB, but allocate for common sizes -// Using a simple approach: allocate max size buffer + #define MAX_FB_WIDTH 2048 #define MAX_FB_HEIGHT 2048 static uint32_t g_back_buffer[MAX_FB_WIDTH * MAX_FB_HEIGHT] __attribute__((aligned(4096))); -// Clipping state static int g_clip_x = 0, g_clip_y = 0, g_clip_w = 0, g_clip_h = 0; static bool g_clip_enabled = false; -// Custom Render Target static uint32_t *g_render_target = NULL; static int g_rt_width = 0; static int g_rt_height = 0; @@ -256,24 +249,17 @@ void draw_rounded_rect_filled(int x, int y, int w, int h, int radius, uint32_t c draw_rect(x, y + radius, radius, h - 2*radius, color); draw_rect(x + w - radius, y + radius, radius, h - 2*radius, color); - // Draw rounded corners using scanline approach (fills gaps properly) for (int dy = 0; dy < radius; dy++) { - // For top corners: distance formula inverted (narrow at top, wide at junction) int dx_top = isqrt(radius*radius - (radius - dy) * (radius - dy)); - // For bottom corners: distance formula normal (wide at junction, narrow at bottom) int dx_bottom = isqrt(radius*radius - dy*dy); - // Top-left corner - horizontal scanline draw_rect(x + radius - dx_top, y + dy, dx_top, 1, color); - // Top-right corner - horizontal scanline draw_rect(x + w - radius, y + dy, dx_top, 1, color); - // Bottom-left corner - horizontal scanline draw_rect(x + radius - dx_bottom, y + h - radius + dy, dx_bottom, 1, color); - // Bottom-right corner - horizontal scanline draw_rect(x + w - radius, y + h - radius + dy, dx_bottom, 1, color); } } @@ -282,7 +268,6 @@ void draw_char(int x, int y, char c, uint32_t color) { unsigned char uc = (unsigned char)c; if (uc > 127) return; - // Fast rejection: if the character is entirely outside the clipping/dirty rect, skip it if (g_clip_enabled && !g_render_target) { if (x + 8 <= g_clip_x || x >= g_clip_x + g_clip_w || y + 8 <= g_clip_y || y >= g_clip_y + g_clip_h) { diff --git a/src/kernel/gui_ipc.h b/src/kernel/gui_ipc.h index 1d2a041..10e530a 100644 --- a/src/kernel/gui_ipc.h +++ b/src/kernel/gui_ipc.h @@ -14,11 +14,15 @@ #define GUI_EVENT_RIGHT_CLICK 3 #define GUI_EVENT_CLOSE 4 #define GUI_EVENT_KEY 5 +#define GUI_EVENT_MOUSE_DOWN 6 +#define GUI_EVENT_MOUSE_UP 7 +#define GUI_EVENT_MOUSE_MOVE 8 typedef struct { int type; int arg1; // For click: x int arg2; // For click: y + int arg3; // For click: button state } gui_event_t; #endif diff --git a/src/kernel/main.c b/src/kernel/main.c index 95722bd..bc81550 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -15,7 +15,6 @@ #include "memory_manager.h" #include "platform.h" #include "wallpaper.h" -#include "viewer.h" // --- Limine Requests --- __attribute__((used, section(".requests"))) @@ -183,7 +182,6 @@ void kmain(void) { while (1) { wm_process_input(); wallpaper_process_pending(); - viewer_process_pending(); asm("hlt"); } } diff --git a/src/kernel/markdown.h b/src/kernel/markdown.h deleted file mode 100644 index c3fec87..0000000 --- a/src/kernel/markdown.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef MARKDOWN_H -#define MARKDOWN_H - -#include "wm.h" - -extern Window win_markdown; - -void markdown_init(void); -void markdown_open_file(const char *filename); - -#endif diff --git a/src/kernel/minesweeper.h b/src/kernel/minesweeper.h deleted file mode 100644 index 120c35f..0000000 --- a/src/kernel/minesweeper.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef MINESWEEPER_H -#define MINESWEEPER_H - -#include "wm.h" - -extern Window win_minesweeper; - -void minesweeper_init(void); - -#endif diff --git a/src/kernel/paint.c b/src/kernel/paint.c deleted file mode 100644 index c086e01..0000000 --- a/src/kernel/paint.c +++ /dev/null @@ -1,205 +0,0 @@ -#include "paint.h" -#include "graphics.h" -#include "wm.h" -#include "memory_manager.h" -#include "fat32.h" - -#define CANVAS_W 300 -#define CANVAS_H 200 -#define PAINT_MAGIC 0x544E5042 // 'BPNT' - -Window win_paint; -static uint32_t *canvas_buffer = NULL; -static uint32_t current_color = COLOR_BLACK; -static int last_mx = -1; -static int last_my = -1; -static char current_file_path[256] = "/Desktop/drawing.pnt"; - -static void paint_strcpy(char *dest, const char *src) { - while (*src) *dest++ = *src++; - *dest = 0; -} - -static void paint_paint(Window *win) { - // Toolbar area - dark mode - draw_rounded_rect_filled(win->x + 10, win->y + 30, 40, win->h - 40, 6, COLOR_DARK_PANEL); - - // Color Palette with rounded corners - uint32_t colors[] = {COLOR_BLACK, COLOR_RED, COLOR_APPLE_GREEN, COLOR_APPLE_BLUE, COLOR_APPLE_YELLOW, COLOR_WHITE}; - for (int i = 0; i < 6; i++) { - int cy = win->y + 40 + (i * 25); - draw_rounded_rect_filled(win->x + 15, cy, 30, 20, 3, colors[i]); - - // Highlight selected color with border - if (current_color == colors[i]) { - draw_rounded_rect(win->x + 13, cy - 2, 34, 24, 4, COLOR_DARK_TEXT); - } - } - - // Toolbar Buttons - dark mode with rounded corners - draw_rounded_rect_filled(win->x + 12, win->y + win->h - 65, 36, 20, 4, COLOR_DARK_BORDER); - draw_string(win->x + 18, win->y + win->h - 58, "CLR", COLOR_DARK_TEXT); - - draw_rounded_rect_filled(win->x + 12, win->y + win->h - 40, 36, 20, 4, COLOR_DARK_BORDER); - draw_string(win->x + 18, win->y + win->h - 33, "SAV", COLOR_DARK_TEXT); - - // Canvas Area with dark background and rounded corners - int canvas_x = win->x + 60; - int canvas_y = win->y + 30; - draw_rounded_rect_filled(canvas_x - 2, canvas_y - 2, CANVAS_W + 4, CANVAS_H + 4, 4, COLOR_DARK_BG); - - if (canvas_buffer) { - for (int y = 0; y < CANVAS_H; y++) { - for (int x = 0; x < CANVAS_W; x++) { - uint32_t color = canvas_buffer[y * CANVAS_W + x]; - put_pixel(canvas_x + x, canvas_y + y, color); - } - } - } -} - -static void paint_put_brush(int cx, int cy) { - if (!canvas_buffer) return; - for (int dy = 0; dy < 2; dy++) { - for (int dx = 0; dx < 2; dx++) { - int px = cx + dx; - int py = cy + dy; - if (px >= 0 && px < CANVAS_W && py >= 0 && py < CANVAS_H) { - canvas_buffer[py * CANVAS_W + px] = current_color; - } - } - } - wm_mark_dirty(win_paint.x + 60 + cx, win_paint.y + 30 + cy, 2, 2); -} - -void paint_handle_mouse(int x, int y) { - int cx = x - 60; - int cy = y - 30; - - if (cx < 0 || cx >= CANVAS_W || cy < 0 || cy >= CANVAS_H) { - last_mx = -1; - return; - } - - if (last_mx == -1) { - paint_put_brush(cx, cy); - } else { - // Bresenham's line algorithm to fill gaps - int x0 = last_mx, y0 = last_my; - int x1 = cx, y1 = cy; - int dx = (x1 - x0 > 0) ? (x1 - x0) : (x0 - x1); - int dy = (y1 - y0 > 0) ? (y1 - y0) : (y0 - y1); - int sx = x0 < x1 ? 1 : -1; - int sy = y0 < y1 ? 1 : -1; - int err = dx - dy; - - while (1) { - paint_put_brush(x0, y0); - if (x0 == x1 && y0 == y1) break; - int e2 = 2 * err; - if (e2 > -dy) { err -= dy; x0 += sx; } - if (e2 < dx) { err += dx; y0 += sy; } - } - } - last_mx = cx; - last_my = cy; -} - -void paint_reset_last_pos(void) { - last_mx = -1; - last_my = -1; -} - -static void paint_save(const char *path) { - FAT32_FileHandle *fh = fat32_open(path, "w"); - if (fh) { - uint32_t header[3] = {PAINT_MAGIC, CANVAS_W, CANVAS_H}; - fat32_write(fh, header, sizeof(header)); - fat32_write(fh, canvas_buffer, CANVAS_W * CANVAS_H * sizeof(uint32_t)); - fat32_close(fh); - wm_show_message("Paint", "Image saved."); - } -} - -void paint_load(const char *path) { - paint_strcpy(current_file_path, path); - FAT32_FileHandle *fh = fat32_open(path, "r"); - if (fh) { - uint32_t header[3]; - if (fat32_read(fh, header, sizeof(header)) == sizeof(header)) { - if (header[0] == PAINT_MAGIC) { - fat32_read(fh, canvas_buffer, CANVAS_W * CANVAS_H * sizeof(uint32_t)); - win_paint.visible = true; - win_paint.focused = true; - } - } - fat32_close(fh); - } -} - -static void paint_click(Window *win, int x, int y) { - (void)win; - // Check Buttons - if (x >= 12 && x < 48) { - if (y >= win->h - 65 && y < win->h - 45) { - paint_reset(); - wm_mark_dirty(win->x, win->y, win->w, win->h); - return; - } - if (y >= win->h - 40 && y < win->h - 20) { - paint_save(current_file_path); - return; - } - } - - // Check Palette - if (x >= 15 && x < 45) { - for (int i = 0; i < 6; i++) { - int cy = 40 + (i * 25); - if (y >= cy && y < cy + 20) { - uint32_t colors[] = {COLOR_BLACK, COLOR_RED, COLOR_APPLE_GREEN, COLOR_APPLE_BLUE, COLOR_APPLE_YELLOW, COLOR_WHITE}; - current_color = colors[i]; - wm_mark_dirty(win->x, win->y, win->w, win->h); - return; - } - } - } - paint_handle_mouse(x, y); -} - -static void paint_mouse_move(Window *win, int x, int y, uint8_t buttons) { - if (buttons & 0x01) { // Left button down - paint_handle_mouse(x, y); - wm_mark_dirty(win->x, win->y, win->w, win->h); - } else { - paint_reset_last_pos(); - } -} - -void paint_init(void) { - win_paint.title = "Paint"; - win_paint.x = 150; - win_paint.y = 100; - win_paint.w = 380; - win_paint.h = 260; - win_paint.visible = false; - win_paint.focused = false; - win_paint.z_index = 0; - win_paint.paint = paint_paint; - win_paint.handle_click = paint_click; - win_paint.handle_right_click = NULL; - win_paint.handle_key = NULL; - - if (!canvas_buffer) { - canvas_buffer = (uint32_t*)kmalloc(CANVAS_W * CANVAS_H * sizeof(uint32_t)); - paint_reset(); - } -} - -void paint_reset(void) { - if (canvas_buffer) { - for (int i = 0; i < CANVAS_W * CANVAS_H; i++) { - canvas_buffer[i] = COLOR_WHITE; - } - } -} \ No newline at end of file diff --git a/src/kernel/paint.h b/src/kernel/paint.h deleted file mode 100644 index d54e390..0000000 --- a/src/kernel/paint.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef PAINT_H -#define PAINT_H - -#include "wm.h" - -extern Window win_paint; - -void paint_init(void); -void paint_reset(void); -void paint_handle_mouse(int x, int y); -void paint_load(const char *path); -void paint_reset_last_pos(void); - -#endif \ No newline at end of file diff --git a/src/kernel/process.c b/src/kernel/process.c index 6cdf43e..b32d95d 100644 --- a/src/kernel/process.c +++ b/src/kernel/process.c @@ -119,6 +119,8 @@ void process_create_elf(const char* filepath, const char* args_str) { new_proc->gui_event_head = 0; new_proc->gui_event_tail = 0; new_proc->ui_window = NULL; + new_proc->heap_start = 0x20000000; // 512MB mark + new_proc->heap_end = 0x20000000; // 2. Load ELF executable uint64_t entry_point = elf_load(filepath, new_proc->pml4_phys); @@ -352,7 +354,6 @@ void process_push_gui_event(process_t *proc, gui_event_t *ev) { // Drop event if queue is full if (next_tail == proc->gui_event_head) { extern void serial_write(const char *str); - serial_write("PROC: GUI event queue full, dropping event!\n"); return; } proc->gui_events[proc->gui_event_tail] = *ev; diff --git a/src/kernel/process.h b/src/kernel/process.h index 344fa93..3de398e 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -30,6 +30,9 @@ typedef struct process { int gui_event_tail; void *ui_window; // Pointer to the active Window + uint64_t heap_start; + uint64_t heap_end; + void *fds[MAX_PROCESS_FDS]; struct process *next; diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index d4a401e..6577565 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -5,7 +5,8 @@ #include "process.h" #include "wm.h" #include "fat32.h" -#include "fat32.h" +#include "paging.h" +#include "platform.h" // Read MSR static inline uint64_t rdmsr(uint32_t msr) { @@ -69,6 +70,50 @@ static void user_window_click(Window *win, int x, int y) { process_push_gui_event(proc, &ev); } +static void user_window_right_click(Window *win, int x, int y) { + process_t *proc = (process_t *)win->data; + if (!proc) return; + gui_event_t ev = { .type = GUI_EVENT_RIGHT_CLICK, .arg1 = x, .arg2 = y }; + process_push_gui_event(proc, &ev); +} + +static void user_window_mouse_down(Window *win, int x, int y) { + process_t *proc = (process_t *)win->data; + if (!proc) return; + gui_event_t ev = { .type = GUI_EVENT_MOUSE_DOWN, .arg1 = x, .arg2 = y }; + process_push_gui_event(proc, &ev); +} + +static void user_window_mouse_up(Window *win, int x, int y) { + process_t *proc = (process_t *)win->data; + if (!proc) return; + gui_event_t ev = { .type = GUI_EVENT_MOUSE_UP, .arg1 = x, .arg2 = y }; + process_push_gui_event(proc, &ev); +} + +static void user_window_mouse_move(Window *win, int x, int y, uint8_t buttons) { + process_t *proc = (process_t *)win->data; + if (!proc) return; + gui_event_t ev = { .type = GUI_EVENT_MOUSE_MOVE, .arg1 = x, .arg2 = y, .arg3 = buttons }; + process_push_gui_event(proc, &ev); +} + +// Helper function for WM to send mouse events +void syscall_send_mouse_move_event(Window *win, int x, int y, uint8_t buttons) { + if (!win || !win->data) return; + user_window_mouse_move(win, x, y, buttons); +} + +void syscall_send_mouse_down_event(Window *win, int x, int y) { + if (!win || !win->data) return; + user_window_mouse_down(win, x, y); +} + +void syscall_send_mouse_up_event(Window *win, int x, int y) { + if (!win || !win->data) return; + user_window_mouse_up(win, x, y); +} + static void user_window_key(Window *win, char c) { process_t *proc = (process_t *)win->data; if (!proc) return; @@ -185,9 +230,9 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u // Set callbacks win->paint = user_window_paint; win->handle_click = user_window_click; + win->handle_right_click = user_window_right_click; win->handle_close = user_window_close; win->handle_key = user_window_key; - win->handle_right_click = NULL; proc->ui_window = win; wm_add_window(win); @@ -398,6 +443,110 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u extern void serial_write(const char *str); serial_write((const char *)arg2); return 0; + } else if (syscall_num == 9) { // SYS_SBRK + int incr = (int)arg1; + process_t *proc = process_get_current(); + if (!proc || !proc->is_user) return (uint64_t)-1; + + uint64_t old_end = proc->heap_end; + if (incr == 0) return old_end; + + uint64_t new_end = old_end + incr; + + // If expanding, we might need to map new pages + if (incr > 0) { + uint64_t start_page = (old_end + 0xFFF) & ~0xFFF; + uint64_t end_page = (new_end + 0xFFF) & ~0xFFF; + + for (uint64_t page = start_page; page < end_page; page += 4096) { + void *phys = kmalloc_aligned(4096, 4096); + if (!phys) return (uint64_t)-1; // Out of memory + + extern void mem_memset(void *dest, int val, size_t len); + mem_memset(phys, 0, 4096); + + paging_map_page(proc->pml4_phys, page, v2p((uint64_t)phys), 0x07); // PT_PRESENT | PT_RW | PT_USER + } + } + + proc->heap_end = new_end; + return old_end; + } else if (syscall_num == 5) { // SYS_SYSTEM + int cmd = (int)arg1; + if (cmd == 1) { // SYSTEM_CMD_SET_BG_COLOR + uint32_t color = (uint32_t)arg2; + extern void graphics_set_bg_color(uint32_t color); + graphics_set_bg_color(color); + return 0; + } else if (cmd == 2) { // SYSTEM_CMD_SET_BG_PATTERN + uint32_t *user_pat = (uint32_t *)arg2; + if (!user_pat) { + extern void graphics_set_bg_pattern(uint32_t *pattern); + graphics_set_bg_pattern(NULL); + } else { + static uint32_t global_bg_pattern[128*128]; + for (int i=0; i<128*128; i++) { + global_bg_pattern[i] = user_pat[i]; + } + extern void graphics_set_bg_pattern(uint32_t *pattern); + graphics_set_bg_pattern(global_bg_pattern); + } + extern void wm_refresh(void); + wm_refresh(); + return 0; + } else if (cmd == 3) { // SYSTEM_CMD_SET_WALLPAPER + int wp_id = (int)arg2; + extern void wallpaper_request_set(int index); + wallpaper_request_set(wp_id); + return 0; + } else if (cmd == 4) { // SYSTEM_CMD_SET_DESKTOP_PROP + int prop = (int)arg2; + int val = (int)arg3; + extern _Bool desktop_snap_to_grid; + extern _Bool desktop_auto_align; + extern int desktop_max_rows_per_col; + extern int desktop_max_cols; + if (prop == 1) desktop_snap_to_grid = val; + if (prop == 2) desktop_auto_align = val; + if (prop == 3) desktop_max_rows_per_col = val; + if (prop == 4) desktop_max_cols = val; + extern void wm_refresh_desktop(void); + wm_refresh_desktop(); + return 0; + } else if (cmd == 5) { // SYSTEM_CMD_SET_MOUSE_SPEED + extern int mouse_speed; + mouse_speed = (int)arg2; + return 0; + } else if (cmd == 6) { // SYSTEM_CMD_NETWORK_INIT + extern int network_init(void); + return network_init(); + } else if (cmd == 7) { // SYSTEM_CMD_GET_DESKTOP_PROP + int prop = (int)arg2; + extern _Bool desktop_snap_to_grid; + extern _Bool desktop_auto_align; + extern int desktop_max_rows_per_col; + extern int desktop_max_cols; + if (prop == 1) return desktop_snap_to_grid; + if (prop == 2) return desktop_auto_align; + if (prop == 3) return desktop_max_rows_per_col; + if (prop == 4) return desktop_max_cols; + return 0; + } else if (cmd == 8) { // SYSTEM_CMD_GET_MOUSE_SPEED + extern int mouse_speed; + return mouse_speed; + } else if (cmd == 9) { // SYSTEM_CMD_GET_WALLPAPER_THUMB + int id = (int)arg2; + uint32_t *dest = (uint32_t *)arg3; + if (!dest) return -1; + extern uint32_t* wallpaper_get_thumb(int index); + extern _Bool wallpaper_thumb_valid(int index); + if (!wallpaper_thumb_valid(id)) return -1; + uint32_t *thumb = wallpaper_get_thumb(id); + if (!thumb) return -1; + for (int i=0; i<100*60; i++) dest[i] = thumb[i]; + return 0; + } + return -1; } return 0; diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h index be4cb56..faa54bf 100644 --- a/src/kernel/syscall.h +++ b/src/kernel/syscall.h @@ -3,6 +3,9 @@ #include +// Forward declarations +typedef struct Window Window; + // MSRs used for syscalls in x86_64 #define MSR_EFER 0xC0000080 #define MSR_STAR 0xC0000081 @@ -32,4 +35,9 @@ void syscall_init(void); uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5); +// Mouse event helpers for WM +void syscall_send_mouse_move_event(Window *win, int x, int y, uint8_t buttons); +void syscall_send_mouse_down_event(Window *win, int x, int y); +void syscall_send_mouse_up_event(Window *win, int x, int y); + #endif // SYSCALL_H diff --git a/src/kernel/userland/Makefile b/src/kernel/userland/Makefile index b934377..e13afcd 100644 --- a/src/kernel/userland/Makefile +++ b/src/kernel/userland/Makefile @@ -8,7 +8,7 @@ LDFLAGS = -m elf_x86_64 -nostdlib -static -no-pie -Ttext=0x40000000 --no-dynamic LIBC_SOURCES = $(wildcard libc/*.c) LIBC_OBJS = $(LIBC_SOURCES:.c=.o) crt0.o -APP_SOURCES = $(wildcard *.c) +APP_SOURCES = $(filter-out nanojpeg.c, $(wildcard *.c)) APP_OBJS = $(APP_SOURCES:.c=.o) APP_ELFS = $(APP_SOURCES:.c=.elf) @@ -23,6 +23,9 @@ libc/%.o: libc/%.c %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ +viewer.elf: $(LIBC_OBJS) viewer.o nanojpeg.o + $(LD) $(LDFLAGS) $^ -o $@ + %.elf: $(LIBC_OBJS) %.o $(LD) $(LDFLAGS) $^ -o $@ diff --git a/src/kernel/userland/calculator.c b/src/kernel/userland/calculator.c index f5243c6..250c051 100644 --- a/src/kernel/userland/calculator.c +++ b/src/kernel/userland/calculator.c @@ -4,7 +4,6 @@ #define SCALE 1000000LL -// Dark Mode Colors mapping to libui #define COLOR_DARK_BG 0xFF1E1E1E #define COLOR_DARK_PANEL 0xFF2D2D2D #define COLOR_DARK_TEXT 0xFFF0F0F0 diff --git a/src/kernel/userland/calculator.elf b/src/kernel/userland/calculator.elf index a9e333b..1339fbe 100755 Binary files a/src/kernel/userland/calculator.elf and b/src/kernel/userland/calculator.elf differ diff --git a/src/kernel/userland/crash.c b/src/kernel/userland/crash.c index 20321b0..f07f99a 100644 --- a/src/kernel/userland/crash.c +++ b/src/kernel/userland/crash.c @@ -4,7 +4,7 @@ int main() { const char* msg = "Attempting to crash via null dereference...\n"; sys_write(1, msg, 45); - // Null pointer dereference + // Null pointer dereference (should not crash the system and instead get this process killed) volatile int* p = (int*)0; *p = 123; diff --git a/src/kernel/userland/crash.elf b/src/kernel/userland/crash.elf index b53e28c..9a31208 100755 Binary files a/src/kernel/userland/crash.elf and b/src/kernel/userland/crash.elf differ diff --git a/src/kernel/userland/hello.elf b/src/kernel/userland/hello.elf index 2a76bbc..928a245 100755 Binary files a/src/kernel/userland/hello.elf and b/src/kernel/userland/hello.elf differ diff --git a/src/kernel/userland/libc/libui.h b/src/kernel/userland/libc/libui.h index 9900c56..6716b29 100644 --- a/src/kernel/userland/libc/libui.h +++ b/src/kernel/userland/libc/libui.h @@ -20,10 +20,15 @@ #define GUI_EVENT_CLOSE 4 #define GUI_EVENT_KEY 5 +#define GUI_EVENT_MOUSE_DOWN 6 +#define GUI_EVENT_MOUSE_UP 7 +#define GUI_EVENT_MOUSE_MOVE 8 + typedef struct { int type; int arg1; // For click: x int arg2; // For click: y + int arg3; // For click: button state } gui_event_t; // Window Handle diff --git a/src/kernel/userland/libc/libui.o b/src/kernel/userland/libc/libui.o new file mode 100644 index 0000000..c11475a Binary files /dev/null and b/src/kernel/userland/libc/libui.o differ diff --git a/src/kernel/userland/libc/stdlib.c b/src/kernel/userland/libc/stdlib.c new file mode 100644 index 0000000..bcbc48f --- /dev/null +++ b/src/kernel/userland/libc/stdlib.c @@ -0,0 +1,140 @@ +#include "stdlib.h" +#include "syscall.h" + +// Simple block allocator over sys_sbrk +typedef struct BlockMeta { + size_t size; + int free; + struct BlockMeta *next; +} BlockMeta; + +#define META_SIZE sizeof(BlockMeta) + +static void *global_base = NULL; + +static BlockMeta *find_free_block(BlockMeta **last, size_t size) { + BlockMeta *current = global_base; + while (current && !(current->free && current->size >= size)) { + *last = current; + current = current->next; + } + return current; +} + +static BlockMeta *request_space(BlockMeta* last, size_t size) { + BlockMeta *block; + block = (BlockMeta *)sys_sbrk(0); + // Ask for space, ensuring everything stays 8-byte aligned if sizes are odd. + // For simplicity, we just request exactly what is needed, + // but typically `size` should be aligned. + size_t align = 8; + if (size % align != 0) { + size += align - (size % align); + } + + void *request = sys_sbrk(size + META_SIZE); + if (request == (void*)-1) { + return NULL; // sbrk failed + } + + if (last) { // NULL on first request + last->next = block; + } + block->size = size; + block->next = NULL; + block->free = 0; + return block; +} + +void *malloc(size_t size) { + BlockMeta *block; + if (size <= 0) { + return NULL; + } + + // Align size to 8 bytes for safety + size_t align = 8; + if (size % align != 0) { + size += align - (size % align); + } + + if (!global_base) { // First call + block = request_space(NULL, size); + if (!block) return NULL; + global_base = block; + } else { + BlockMeta *last = global_base; + block = find_free_block(&last, size); + if (!block) { // Failed to find free block + block = request_space(last, size); + if (!block) return NULL; + } else { // Found free block + block->free = 0; + // We could split the block here if block->size is much larger than size... + } + } + + return (block + 1); +} + +void free(void *ptr) { + if (!ptr) { + return; + } + + BlockMeta *block = (BlockMeta*)ptr - 1; + block->free = 1; +} + +void *calloc(size_t nelem, size_t elsize) { + size_t size = nelem * elsize; + void *ptr = malloc(size); + if (ptr) { + char *p = ptr; + for (size_t i = 0; i < size; i++) { + p[i] = 0; + } + } + return ptr; +} + +void *realloc(void *ptr, size_t size) { + if (!ptr) { + return malloc(size); + } + if (size == 0) { + free(ptr); + return NULL; + } + + BlockMeta *block = (BlockMeta*)ptr - 1; + if (block->size >= size) { + return ptr; + } + + void *new_ptr = malloc(size); + if (!new_ptr) { + return NULL; + } + + char *src = ptr; + char *dst = new_ptr; + for (size_t i = 0; i < block->size; i++) { + dst[i] = src[i]; + } + free(ptr); + return new_ptr; +} + +void *memset(void *s, int c, size_t n) { + unsigned char *p = s; + while (n--) *p++ = (unsigned char)c; + return s; +} + +void *memcpy(void *dest, const void *src, size_t n) { + unsigned char *d = dest; + const unsigned char *s = src; + while (n--) *d++ = *s++; + return dest; +} diff --git a/src/kernel/userland/libc/stdlib.h b/src/kernel/userland/libc/stdlib.h new file mode 100644 index 0000000..ef71f64 --- /dev/null +++ b/src/kernel/userland/libc/stdlib.h @@ -0,0 +1,14 @@ +#ifndef STDLIB_H +#define STDLIB_H + +#include +#include + +void* malloc(size_t size); +void free(void* ptr); +void* calloc(size_t nmemb, size_t size); +void* realloc(void* ptr, size_t size); +void *memset(void *s, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); + +#endif diff --git a/src/kernel/userland/libc/stdlib.o b/src/kernel/userland/libc/stdlib.o new file mode 100644 index 0000000..22e6ecd Binary files /dev/null and b/src/kernel/userland/libc/stdlib.o differ diff --git a/src/kernel/userland/libc/syscall.c b/src/kernel/userland/libc/syscall.c index b866fdf..538903d 100644 --- a/src/kernel/userland/libc/syscall.c +++ b/src/kernel/userland/libc/syscall.c @@ -70,6 +70,14 @@ int sys_write(int fd, const char *buf, int len) { return (int)syscall3(SYS_WRITE, (uint64_t)fd, (uint64_t)buf, (uint64_t)len); } +void *sys_sbrk(int incr) { + return (void *)syscall1(SYS_SBRK, (uint64_t)incr); +} + +int sys_system(int cmd, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4) { + return (int)syscall5(SYS_SYSTEM, (uint64_t)cmd, arg1, arg2, arg3, arg4); +} + int sys_open(const char *path, const char *mode) { return (int)syscall3(SYS_FS, FS_CMD_OPEN, (uint64_t)path, (uint64_t)mode); } diff --git a/src/kernel/userland/libc/syscall.h b/src/kernel/userland/libc/syscall.h index e6227e5..478122b 100644 --- a/src/kernel/userland/libc/syscall.h +++ b/src/kernel/userland/libc/syscall.h @@ -8,6 +8,8 @@ #define SYS_WRITE 1 #define SYS_GUI 3 #define SYS_FS 4 +#define SYS_SYSTEM 5 +#define SYS_SBRK 9 // FS Commands #define FS_CMD_OPEN 1 @@ -33,6 +35,8 @@ extern uint64_t syscall5(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_ // Public API void sys_exit(int status); int sys_write(int fd, const char *buf, int len); +void *sys_sbrk(int incr); +int sys_system(int cmd, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4); // FS API int sys_open(const char *path, const char *mode); diff --git a/src/kernel/userland/libc/syscall.o b/src/kernel/userland/libc/syscall.o index 2cbd73f..141329b 100644 Binary files a/src/kernel/userland/libc/syscall.o and b/src/kernel/userland/libc/syscall.o differ diff --git a/src/kernel/markdown.c b/src/kernel/userland/markdown.c similarity index 62% rename from src/kernel/markdown.c rename to src/kernel/userland/markdown.c index fdd1a7a..44da2fc 100644 --- a/src/kernel/markdown.c +++ b/src/kernel/userland/markdown.c @@ -1,20 +1,19 @@ -#include "markdown.h" -#include "graphics.h" -#include "fat32.h" -#include "wm.h" +#include "libc/syscall.h" +#include "libc/libui.h" #include #include - -// === Markdown Viewer State === -Window win_markdown; +#include #define MD_MAX_CONTENT 16384 #define MD_MAX_LINES 256 #define MD_CHAR_WIDTH 8 #define MD_LINE_HEIGHT 16 -#define MD_CONTENT_Y 40 -#define MD_PADDING_X 12 -#define MD_CONTENT_WIDTH 400 + +#define COLOR_DARK_PANEL 0xFF202020 +#define COLOR_DARK_BG 0xFF121212 +#define COLOR_DARK_TEXT 0xFFE0E0E0 +#define COLOR_DARK_TITLEBAR 0xFF303030 +#define COLOR_BLACK 0xFF000000 typedef enum { MD_LINE_NORMAL, @@ -40,7 +39,8 @@ static int line_count = 0; static int scroll_top = 0; static char open_filename[256] = ""; -// === Helper Functions === +static int win_w = 600; +static int win_h = 400; static size_t md_strlen(const char *str) { size_t len = 0; @@ -63,64 +63,42 @@ static int md_strncpy(char *dest, const char *src, int n) { return i; } -static int md_strcmp(const char *s1, const char *s2) { - (void)s1; // Suppress unused warning - (void)s2; // Suppress unused warning - return 0; -} - -// Check if string starts with pattern -static bool md_starts_with(const char *str, const char *pattern) { - (void)str; // Suppress unused warning - (void)pattern; // Suppress unused warning - return false; -} - -// Parse markdown line and extract formatted text static void md_parse_line(const char *raw_line, char *output, MDLineType *type, int *indent) { int i = 0; int out_idx = 0; *indent = 0; *type = MD_LINE_NORMAL; - // Skip leading whitespace and count indentation while (raw_line[i] == ' ' || raw_line[i] == '\t') { if (raw_line[i] == '\t') *indent += 2; else *indent += 1; i++; } - // Detect line type if (raw_line[i] == '#') { - // Heading int hash_count = 0; while (raw_line[i] == '#') { hash_count++; i++; } - // Skip space after hashes if (raw_line[i] == ' ') i++; if (hash_count == 1) *type = MD_LINE_HEADING1; else if (hash_count == 2) *type = MD_LINE_HEADING2; else if (hash_count <= 6) *type = MD_LINE_HEADING3; } else if (raw_line[i] == '-' || raw_line[i] == '*') { - // Could be list or horizontal rule if ((raw_line[i] == '-' || raw_line[i] == '*') && (raw_line[i+1] == ' ' || raw_line[i+1] == '\t')) { *type = MD_LINE_LIST; - i += 2; // Skip '- ' or '* ' - while (raw_line[i] == ' ' || raw_line[i] == '\t') i++; // Skip extra spaces + i += 2; + while (raw_line[i] == ' ' || raw_line[i] == '\t') i++; } } else if (raw_line[i] == '>') { - // Blockquote *type = MD_LINE_BLOCKQUOTE; i++; if (raw_line[i] == ' ') i++; } - // Parse inline formatting and copy content while (raw_line[i] && out_idx < 255) { - // Handle bold **text** if (raw_line[i] == '*' && raw_line[i+1] == '*') { i += 2; while (raw_line[i] && !(raw_line[i] == '*' && raw_line[i+1] == '*') && out_idx < 255) { @@ -129,8 +107,6 @@ static void md_parse_line(const char *raw_line, char *output, MDLineType *type, if (raw_line[i] == '*' && raw_line[i+1] == '*') i += 2; continue; } - - // Handle italic *text* or _text_ if ((raw_line[i] == '*' || raw_line[i] == '_') && out_idx > 0 && raw_line[i-1] != '\\') { char delim = raw_line[i]; i++; @@ -140,8 +116,6 @@ static void md_parse_line(const char *raw_line, char *output, MDLineType *type, if (raw_line[i] == delim) i++; continue; } - - // Handle inline code `code` if (raw_line[i] == '`') { i++; while (raw_line[i] && raw_line[i] != '`' && out_idx < 255) { @@ -150,29 +124,23 @@ static void md_parse_line(const char *raw_line, char *output, MDLineType *type, if (raw_line[i] == '`') i++; continue; } - - // Handle links [text](url) - keep only text if (raw_line[i] == '[') { i++; while (raw_line[i] && raw_line[i] != ']' && out_idx < 255) { output[out_idx++] = raw_line[i++]; } if (raw_line[i] == ']') i++; - // Skip (url) if (raw_line[i] == '(') { while (raw_line[i] && raw_line[i] != ')') i++; if (raw_line[i] == ')') i++; } continue; } - output[out_idx++] = raw_line[i++]; } - output[out_idx] = 0; } -// Clear all markdown lines static void md_clear_all(void) { for (int i = 0; i < MD_MAX_LINES; i++) { lines[i].content[0] = 0; @@ -185,29 +153,20 @@ static void md_clear_all(void) { open_filename[0] = 0; } -// Load and parse markdown file void markdown_open_file(const char *filename) { md_clear_all(); md_strcpy(open_filename, filename); - FAT32_FileHandle *fh = fat32_open(filename, "r"); - if (!fh) { - // File not found - return; - } + int fd = sys_open(filename, "r"); + if (fd < 0) return; - // Read file content - char buffer[MD_MAX_CONTENT]; - int bytes_read = fat32_read(fh, buffer, sizeof(buffer) - 1); - fat32_close(fh); - - if (bytes_read <= 0) { - return; - } + static char buffer[MD_MAX_CONTENT]; + int bytes_read = sys_read(fd, buffer, sizeof(buffer) - 1); + sys_close(fd); + if (bytes_read <= 0) return; buffer[bytes_read] = 0; - // Parse into markdown lines int line = 0; int col = 0; char raw_line[256] = ""; @@ -215,10 +174,8 @@ void markdown_open_file(const char *filename) { for (int i = 0; i < bytes_read && line < MD_MAX_LINES; i++) { char ch = buffer[i]; - if (ch == '\n') { raw_line[col] = 0; - if (raw_line[0] == '`' && raw_line[1] == '`' && raw_line[2] == '`') { in_code_block = !in_code_block; } else { @@ -240,7 +197,6 @@ void markdown_open_file(const char *filename) { line++; } } - col = 0; raw_line[0] = 0; } else if (col < 255) { @@ -248,7 +204,6 @@ void markdown_open_file(const char *filename) { } } - // Handle last line if no trailing newline if (col > 0 && line < MD_MAX_LINES) { raw_line[col] = 0; if (raw_line[0] == '`' && raw_line[1] == '`' && raw_line[2] == '`') { @@ -270,48 +225,38 @@ void markdown_open_file(const char *filename) { line++; } } - line_count = line; } -// === Paint Function === - -// Helper to draw text with emphasis (bold effect by overlaying) -static void md_draw_text_bold(int x, int y, const char *text, uint32_t color) { - draw_string(x, y, text, color); - draw_string(x + 1, y, text, color); +static void md_draw_text_bold(ui_window_t win, int x, int y, const char *text, uint32_t color) { + ui_draw_string(win, x, y, text, color); + ui_draw_string(win, x + 1, y, text, color); } -static void md_paint(Window *win) { - int offset_x = win->x + 4; - int offset_y = win->y + 24; - int content_width = win->w - 8; - int content_height = win->h - 28; +static void md_paint(ui_window_t win) { + int offset_x = 4; + int offset_y = 24; + int content_width = win_w - 8; + int content_height = win_h - 28; - // Draw filename bar below title (dark mode) - draw_rounded_rect_filled(offset_x, offset_y, content_width, 20, 6, COLOR_DARK_PANEL); - draw_string(offset_x + 4, offset_y + 4, "File", COLOR_DARK_TEXT); - draw_string(offset_x + 50, offset_y + 4, open_filename, COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, offset_x, offset_y, content_width, 20, 6, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 4, offset_y + 4, "File", COLOR_DARK_TEXT); + ui_draw_string(win, offset_x + 50, offset_y + 4, open_filename, COLOR_DARK_TEXT); - // Draw scroll buttons on top right with rounded style int btn_x_up = offset_x + content_width - 50; int btn_y = offset_y + 2; - draw_rounded_rect_filled(btn_x_up, btn_y, 20, 16, 4, COLOR_DARK_TITLEBAR); - draw_string(btn_x_up + 6, btn_y, "^", COLOR_DARK_TEXT); - draw_rounded_rect_filled(btn_x_up + 24, btn_y, 20, 16, 4, COLOR_DARK_TITLEBAR); - draw_string(btn_x_up + 30, btn_y, "v", COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, btn_x_up, btn_y, 20, 16, 4, COLOR_DARK_TITLEBAR); + ui_draw_string(win, btn_x_up + 6, btn_y, "^", COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, btn_x_up + 24, btn_y, 20, 16, 4, COLOR_DARK_TITLEBAR); + ui_draw_string(win, btn_x_up + 30, btn_y, "v", COLOR_DARK_TEXT); - // Content area - starts below filename bar int content_start_y = offset_y + 24; int content_start_x = offset_x + 4; - int usable_content_width = content_width - 8 - 20; // Reserved space for scroll button + int usable_content_width = content_width - 8 - 20; int usable_content_height = content_height - 28; int max_display_lines = usable_content_height / MD_LINE_HEIGHT; - // Draw content background (dark mode) - draw_rounded_rect_filled(win->x + 4, content_start_y, win->w - 24, usable_content_height, 6, COLOR_DARK_BG); - - + ui_draw_rounded_rect_filled(win, 4, content_start_y, win_w - 24, usable_content_height, 6, COLOR_DARK_BG); int display_line = 0; int i = scroll_top; @@ -319,7 +264,6 @@ static void md_paint(Window *win) { while (i < line_count && display_line < max_display_lines) { MDLine *line = &lines[i]; - // Determine spacing and text properties based on heading level (dark mode colors) int line_height = MD_LINE_HEIGHT; int extra_spacing = 0; uint32_t text_color = COLOR_DARK_TEXT; @@ -327,148 +271,117 @@ static void md_paint(Window *win) { switch (line->type) { case MD_LINE_HEADING1: - line_height = MD_LINE_HEIGHT * 2; // Double height - text_color = 0xFF87CEEB; // Light blue + line_height = MD_LINE_HEIGHT * 2; + text_color = 0xFF87CEEB; use_bold = true; extra_spacing = 4; break; case MD_LINE_HEADING2: - line_height = MD_LINE_HEIGHT + 6; // 1.5x height - text_color = 0xFF4A90E2; // Medium blue + line_height = MD_LINE_HEIGHT + 6; + text_color = 0xFF4A90E2; use_bold = true; extra_spacing = 2; break; case MD_LINE_HEADING3: - line_height = MD_LINE_HEIGHT + 2; // Slightly larger - text_color = 0xFF87CEEB; // Light blue + line_height = MD_LINE_HEIGHT + 2; + text_color = 0xFF87CEEB; use_bold = false; break; case MD_LINE_BLOCKQUOTE: - text_color = 0xFFA0A0A0; // Light gray + text_color = 0xFFA0A0A0; break; case MD_LINE_CODE: - text_color = 0xFF90EE90; // Light green for code + text_color = 0xFF90EE90; break; default: text_color = COLOR_DARK_TEXT; break; } - // Check if this heading will fit on the screen - if (display_line + (line_height / MD_LINE_HEIGHT) > max_display_lines) { - break; // Stop rendering if heading won't fit - } + if (display_line + (line_height / MD_LINE_HEIGHT) > max_display_lines) break; - // Adjust X position based on indentation int x_offset = content_start_x + (line->indent_level * 4); int available_width = usable_content_width - (line->indent_level * 4); int max_chars_per_line = available_width / MD_CHAR_WIDTH; - if (max_chars_per_line < 1) max_chars_per_line = 1; - // Handle line wrapping (word-based) const char *text = line->content; int text_len = line->length; int char_idx = 0; int local_display_line = 0; int wrapped_line_count = 0; - while (char_idx < text_len) { + while (char_idx < text_len || (text_len == 0 && local_display_line == 0)) { int line_y = content_start_y + display_line * MD_LINE_HEIGHT + (local_display_line * MD_LINE_HEIGHT); - // Extract line segment - copy up to max_chars_per_line characters char line_segment[256]; int segment_len = 0; int segment_start = char_idx; - // Copy characters up to max_chars_per_line OR until end of string while (char_idx < text_len && segment_len < max_chars_per_line) { line_segment[segment_len++] = text[char_idx++]; } line_segment[segment_len] = 0; if (char_idx < text_len && segment_len > 0) { - // Look for the last space in the segment int last_space = -1; - for (int i = segment_len - 1; i >= 0; i--) { - if (line_segment[i] == ' ') { - last_space = i; - break; + for (int j = segment_len - 1; j >= 0; j--) { + if (line_segment[j] == ' ') { + last_space = j; break; } } - if (last_space > 0) { segment_len = last_space; line_segment[segment_len] = 0; - // Backtrack char_idx to position after the space char_idx = segment_start + last_space + 1; - // Skip any additional spaces at the start of next line - while (char_idx < text_len && text[char_idx] == ' ') { - char_idx++; - } + while (char_idx < text_len && text[char_idx] == ' ') char_idx++; } } if (line->type == MD_LINE_CODE && segment_len > 0) { - draw_rect(x_offset - 2, line_y - 2, (segment_len * MD_CHAR_WIDTH) + 4, 12, COLOR_BLACK); + ui_draw_rect(win, x_offset - 2, line_y - 2, (segment_len * MD_CHAR_WIDTH) + 4, 12, COLOR_BLACK); } - // Draw special elements for first wrapped line of this markdown line if (local_display_line == 0) { switch (line->type) { case MD_LINE_LIST: - // Draw bullet point - draw_rect(x_offset, line_y + MD_LINE_HEIGHT/2 - 1, 2, 2, COLOR_BLACK); + ui_draw_rect(win, x_offset, line_y + MD_LINE_HEIGHT/2 - 1, 2, 2, COLOR_BLACK); x_offset += 12; - // Redraw segment without leading space if (segment_len > 0 && line_segment[0] == ' ') { - for (int j = 0; j < segment_len - 1; j++) { - line_segment[j] = line_segment[j + 1]; - } + for (int j = 0; j < segment_len - 1; j++) line_segment[j] = line_segment[j + 1]; segment_len--; } break; case MD_LINE_BLOCKQUOTE: - // Draw left border - draw_rect(x_offset - 4, line_y, 2, line_height, 0xFF404080); - break; - default: + ui_draw_rect(win, x_offset - 4, line_y, 2, line_height, 0xFF404080); break; + default: break; } } - // Draw the text segment with appropriate styling if (segment_len > 0) { if (use_bold) { - md_draw_text_bold(x_offset, line_y + extra_spacing, line_segment, text_color); + md_draw_text_bold(win, x_offset, line_y + extra_spacing, line_segment, text_color); } else { - draw_string(x_offset, line_y, line_segment, text_color); + ui_draw_string(win, x_offset, line_y, line_segment, text_color); } } local_display_line++; wrapped_line_count++; - if (char_idx >= text_len) break; } - - display_line += wrapped_line_count; - + display_line += (wrapped_line_count > 0 ? wrapped_line_count : 1); i++; } } -// === Input Handling === - -static void md_handle_key(Window *win, char c) { - (void)win; // Suppress unused warning - - - if (c == 'w' || c == 'W' || c == 17) { // Page up or UP arrow +static void md_handle_key(char c) { + if (c == 'w' || c == 'W' || c == 17) { scroll_top -= 3; if (scroll_top < 0) scroll_top = 0; - } else if (c == 's' || c == 'S' || c == 18) { // Page down or DOWN arrow + } else if (c == 's' || c == 'S' || c == 18) { scroll_top += 3; int max_scroll = line_count - 10; if (scroll_top > max_scroll) scroll_top = max_scroll; @@ -476,24 +389,18 @@ static void md_handle_key(Window *win, char c) { } } -static void md_handle_click(Window *win, int x, int y) { - // x and y are relative to window origin - int content_width = win->w - 8; - - // Top right up button: 4 + content_width - 50, 24 + 2, 20x16 +static void md_handle_click(int x, int y) { + int content_width = win_w - 8; int btn_x_up = 4 + content_width - 50; int btn_y = 24 + 2; if (x >= btn_x_up && x < btn_x_up + 20 && y >= btn_y && y < btn_y + 16) { - // Scroll up scroll_top -= 3; if (scroll_top < 0) scroll_top = 0; return; } - // Top right down button: 4 + content_width - 50 + 24, 24 + 2, 20x16 int btn_x_down_top = 4 + content_width - 50 + 24; if (x >= btn_x_down_top && x < btn_x_down_top + 20 && y >= btn_y && y < btn_y + 16) { - // Scroll down scroll_top += 3; int max_scroll = line_count - 10; if (scroll_top > max_scroll) scroll_top = max_scroll; @@ -502,21 +409,33 @@ static void md_handle_click(Window *win, int x, int y) { } } -// === Initialization === +int main(int argc, char **argv) { + ui_window_t win = ui_window_create("Markdown Viewer", 150, 180, win_w, win_h); + if (!win) return 1; -void markdown_init(void) { - win_markdown.title = "Markdown Viewer"; - win_markdown.x = 150; - win_markdown.y = 180; - win_markdown.w = 600; - win_markdown.h = 400; - win_markdown.visible = false; - win_markdown.focused = false; - win_markdown.z_index = 0; - win_markdown.paint = md_paint; - win_markdown.handle_key = md_handle_key; - win_markdown.handle_click = md_handle_click; - win_markdown.handle_right_click = NULL; - md_clear_all(); + if (argc > 1) { + markdown_open_file(argv[1]); + } + + gui_event_t ev; + while (1) { + if (ui_get_event(win, &ev)) { + if (ev.type == GUI_EVENT_PAINT) { + md_paint(win); + ui_mark_dirty(win, 0, 0, win_w, win_h); + } else if (ev.type == GUI_EVENT_CLICK) { + md_handle_click(ev.arg1, ev.arg2); + md_paint(win); + ui_mark_dirty(win, 0, 0, win_w, win_h); + } else if (ev.type == GUI_EVENT_KEY) { + md_handle_key((char)ev.arg1); + md_paint(win); + ui_mark_dirty(win, 0, 0, win_w, win_h); + } else if (ev.type == GUI_EVENT_CLOSE) { + sys_exit(0); + } + } + } + return 0; } diff --git a/src/kernel/userland/markdown.elf b/src/kernel/userland/markdown.elf new file mode 100755 index 0000000..1c1a7ff Binary files /dev/null and b/src/kernel/userland/markdown.elf differ diff --git a/src/kernel/minesweeper.c b/src/kernel/userland/minesweeper.c similarity index 61% rename from src/kernel/minesweeper.c rename to src/kernel/userland/minesweeper.c index e982ae3..eec647a 100644 --- a/src/kernel/minesweeper.c +++ b/src/kernel/userland/minesweeper.c @@ -1,10 +1,23 @@ -#include "minesweeper.h" -#include "graphics.h" -#include "wm.h" +#include "libc/syscall.h" +#include "libc/libui.h" #include #include +#include -Window win_minesweeper; +#define COLOR_DARK_BG 0xFF121212 +#define COLOR_DARK_PANEL 0xFF202020 +#define COLOR_DARK_BORDER 0xFF404040 +#define COLOR_DARK_TEXT 0xFFE0E0E0 +#define COLOR_TRAFFIC_RED 0xFFFF6B6B + +// Debugging helper +static void debug_print(const char *msg) { + sys_write(1, msg, 0); + int i = 0; + while (msg[i]) i++; + sys_write(1, msg, i); + sys_write(1, "\n", 1); +} // Game constants #define GRID_WIDTH 10 @@ -73,9 +86,6 @@ static void init_game(void) { revealed_count = 0; } -static void reveal_cell(int x, int y); - -// Flood fill for empty cells static void flood_fill(int x, int y) { if (x < 0 || x >= GRID_WIDTH || y < 0 || y >= GRID_HEIGHT) return; if (revealed[y][x] || flagged[y][x]) return; @@ -131,41 +141,24 @@ static void flag_cell(int x, int y) { flagged[y][x] = !flagged[y][x]; } -static void minesweeper_right_click(Window *win, int x, int y) { - // x and y are relative to window content (0,0 is top-left of window) - int grid_start_x = 10; - int grid_start_y = 50; +static void minesweeper_paint(ui_window_t win) { + int win_w = 240, win_h = 340; - // Check grid cells - if (x >= grid_start_x && x < grid_start_x + GRID_WIDTH * CELL_SIZE && - y >= grid_start_y && y < grid_start_y + GRID_HEIGHT * CELL_SIZE) { - - if (game_over || game_won) return; - - int cell_x = (x - grid_start_x) / CELL_SIZE; - int cell_y = (y - grid_start_y) / CELL_SIZE; - - flag_cell(cell_x, cell_y); - wm_mark_dirty(win->x, win->y, win->w, win->h); - } -} - -static void minesweeper_paint(Window *win) { // Background - dark mode - draw_rect(win->x + 4, win->y + 30, win->w - 8, win->h - 34, COLOR_DARK_BG); + ui_draw_rect(win, 4, 30, win_w - 8, win_h - 34, COLOR_DARK_BG); // Game status if (game_over) { - draw_string(win->x + 10, win->y + 36, "Game Over!", COLOR_TRAFFIC_RED); + ui_draw_string(win, 10, 36, "Game Over!", COLOR_TRAFFIC_RED); } else if (game_won) { - draw_string(win->x + 10, win->y + 36, "You Won!", 0xFF00FF00); // Bright green + ui_draw_string(win, 10, 36, "You Won!", 0xFF00FF00); // Bright green } else { - draw_string(win->x + 10, win->y + 36, "", COLOR_DARK_TEXT); + ui_draw_string(win, 10, 36, "", COLOR_DARK_TEXT); } // Draw grid - int grid_start_x = win->x + 10; - int grid_start_y = win->y + 56; + int grid_start_x = 10; + int grid_start_y = 56; for (int y = 0; y < GRID_HEIGHT; y++) { for (int x = 0; x < GRID_WIDTH; x++) { @@ -174,45 +167,42 @@ static void minesweeper_paint(Window *win) { if (revealed[y][x]) { // Revealed cell - dark mode - draw_rounded_rect_filled(px, py, CELL_SIZE, CELL_SIZE, 2, COLOR_DARK_PANEL); + ui_draw_rounded_rect_filled(win, px, py, CELL_SIZE, CELL_SIZE, 2, COLOR_DARK_PANEL); if (grid[y][x] == -1) { // Mine - draw_string(px + 8, py + 6, "*", COLOR_TRAFFIC_RED); + ui_draw_string(win, px + 8, py + 6, "*", COLOR_TRAFFIC_RED); } else if (grid[y][x] > 0) { // Number char num[2] = { '0' + grid[y][x], 0 }; - draw_string(px + 8, py + 6, num, COLOR_DARK_TEXT); + ui_draw_string(win, px + 8, py + 6, num, COLOR_DARK_TEXT); } - // 0 = empty, nothing to draw } else { - // Unrevealed cell - raised/button look - draw_rounded_rect_filled(px, py, CELL_SIZE, CELL_SIZE, 2, COLOR_DARK_BORDER); + // Unrevealed cell + ui_draw_rounded_rect_filled(win, px, py, CELL_SIZE, CELL_SIZE, 2, COLOR_DARK_BORDER); if (flagged[y][x]) { - draw_string(px + 7, py + 6, "F", COLOR_TRAFFIC_RED); + ui_draw_string(win, px + 7, py + 6, "F", COLOR_TRAFFIC_RED); } } } } - // Draw new game button (narrower, less dead space) + // Draw new game button int btn_y = grid_start_y + GRID_HEIGHT * CELL_SIZE + 10; - draw_rounded_rect_filled(grid_start_x, btn_y, 70, 24, 4, COLOR_DARK_BORDER); - draw_string(grid_start_x + 6, btn_y + 8, "New Game", COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, grid_start_x, btn_y, 70, 24, 4, COLOR_DARK_BORDER); + ui_draw_string(win, grid_start_x + 6, btn_y + 8, "New Game", COLOR_DARK_TEXT); } -static void minesweeper_click(Window *win, int x, int y) { - // x and y are relative to window content (0,0 is top-left of window) +static void minesweeper_handle_click(ui_window_t win, int x, int y, int button) { int grid_start_x = 10; - int grid_start_y = 50; + int grid_start_y = 56; int btn_y = grid_start_y + GRID_HEIGHT * CELL_SIZE + 10; // Check "New Game" button - if (x >= grid_start_x && x < grid_start_x + 90 && + if (x >= grid_start_x && x < grid_start_x + 70 && y >= btn_y && y < btn_y + 24) { init_game(); - wm_mark_dirty(win->x, win->y, win->w, win->h); return; } @@ -225,25 +215,46 @@ static void minesweeper_click(Window *win, int x, int y) { int cell_x = (x - grid_start_x) / CELL_SIZE; int cell_y = (y - grid_start_y) / CELL_SIZE; - reveal_cell(cell_x, cell_y); - - wm_mark_dirty(win->x, win->y, win->w, win->h); + if (button == GUI_EVENT_RIGHT_CLICK) { + debug_print("[MINESWEEPER] Flagging cell"); + flag_cell(cell_x, cell_y); + } else { + debug_print("[MINESWEEPER] Revealing cell"); + reveal_cell(cell_x, cell_y); + } } } -void minesweeper_init(void) { - win_minesweeper.title = "Minesweeper"; - win_minesweeper.x = 250; - win_minesweeper.y = 100; - win_minesweeper.w = 240; - win_minesweeper.h = 340; - win_minesweeper.visible = false; - win_minesweeper.focused = false; - win_minesweeper.z_index = 0; - win_minesweeper.paint = minesweeper_paint; - win_minesweeper.handle_click = minesweeper_click; - win_minesweeper.handle_right_click = minesweeper_right_click; - - // Initialize game +int main(int argc, char **argv) { + (void)argc; + (void)argv; + ui_window_t win = ui_window_create("Minesweeper", 250, 100, 240, 340); + if (!win) return 1; + + // Use current time or something for seed? No syscall for time right now. + random_seed = 987654321; init_game(); + + gui_event_t ev; + while (1) { + if (ui_get_event(win, &ev)) { + if (ev.type == GUI_EVENT_PAINT) { + minesweeper_paint(win); + ui_mark_dirty(win, 0, 0, 240, 340); + } else if (ev.type == GUI_EVENT_CLICK) { + debug_print("[MINESWEEPER] LEFT CLICK"); + minesweeper_handle_click(win, ev.arg1, ev.arg2, ev.type); + minesweeper_paint(win); + ui_mark_dirty(win, 0, 0, 240, 340); + } else if (ev.type == GUI_EVENT_RIGHT_CLICK) { + debug_print("[MINESWEEPER] RIGHT CLICK DETECTED"); + minesweeper_handle_click(win, ev.arg1, ev.arg2, ev.type); + minesweeper_paint(win); + ui_mark_dirty(win, 0, 0, 240, 340); + } else if (ev.type == GUI_EVENT_CLOSE) { + sys_exit(0); + } + } + } + return 0; } diff --git a/src/kernel/userland/minesweeper.elf b/src/kernel/userland/minesweeper.elf new file mode 100755 index 0000000..8532ecf Binary files /dev/null and b/src/kernel/userland/minesweeper.elf differ diff --git a/src/kernel/userland/nanojpeg.c b/src/kernel/userland/nanojpeg.c new file mode 100644 index 0000000..1ebb74f --- /dev/null +++ b/src/kernel/userland/nanojpeg.c @@ -0,0 +1,967 @@ +// NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder +// version 1.3 (2012-03-05) +// by Martin J. Fiedler +// +// This software is published under the terms of KeyJ's Research License, +// version 0.2. Usage of this software is subject to the following conditions: +// 0. There's no warranty whatsoever. The author(s) of this software can not +// be held liable for any damages that occur when using this software. +// 1. This software may be used freely for both non-commercial and commercial +// purposes. +// 2. This software may be redistributed freely as long as no fees are charged +// for the distribution and this license information is included. +// 3. This software may be modified freely except for this license information, +// which must not be changed in any way. +// 4. If anything other than configuration, indentation or comments have been +// altered in the code, the original author(s) must receive a copy of the +// modified code. + + +/////////////////////////////////////////////////////////////////////////////// +// DOCUMENTATION SECTION // +// read this if you want to know what this is all about // +/////////////////////////////////////////////////////////////////////////////// + +// INTRODUCTION +// ============ +// +// This is a minimal decoder for baseline JPEG images. It accepts memory dumps +// of JPEG files as input and generates either 8-bit grayscale or packed 24-bit +// RGB images as output. It does not parse JFIF or Exif headers; all JPEG files +// are assumed to be either grayscale or YCbCr. CMYK or other color spaces are +// not supported. All YCbCr subsampling schemes with power-of-two ratios are +// supported, as are restart intervals. Progressive or lossless JPEG is not +// supported. +// Summed up, NanoJPEG should be able to decode all images from digital cameras +// and most common forms of other non-progressive JPEG images. +// The decoder is not optimized for speed, it's optimized for simplicity and +// small code. Image quality should be at a reasonable level. A bicubic chroma +// upsampling filter ensures that subsampled YCbCr images are rendered in +// decent quality. The decoder is not meant to deal with broken JPEG files in +// a graceful manner; if anything is wrong with the bitstream, decoding will +// simply fail. +// The code should work with every modern C compiler without problems and +// should not emit any warnings. It uses only (at least) 32-bit integer +// arithmetic and is supposed to be endianness independent and 64-bit clean. +// However, it is not thread-safe. + + +// COMPILE-TIME CONFIGURATION +// ========================== +// +// The following aspects of NanoJPEG can be controlled with preprocessor +// defines: +// +// _NJ_EXAMPLE_PROGRAM = Compile a main() function with an example +// program. +// _NJ_INCLUDE_HEADER_ONLY = Don't compile anything, just act as a header +// file for NanoJPEG. Example: +// #define _NJ_INCLUDE_HEADER_ONLY +// #include "nanojpeg.c" +// int main(void) { +// njInit(); +// // your code here +// njDone(); +// } +// NJ_USE_LIBC=1 = Use the malloc(), free(), memset() and memcpy() +// functions from the standard C library (default). +// NJ_USE_LIBC=0 = Don't use the standard C library. In this mode, +// external functions njAlloc(), njFreeMem(), +// njFillMem() and njCopyMem() need to be defined +// and implemented somewhere. +// NJ_USE_WIN32=0 = Normal mode (default). +// NJ_USE_WIN32=1 = If compiling with MSVC for Win32 and +// NJ_USE_LIBC=0, NanoJPEG will use its own +// implementations of the required C library +// functions (default if compiling with MSVC and +// NJ_USE_LIBC=0). +// NJ_CHROMA_FILTER=1 = Use the bicubic chroma upsampling filter +// (default). // 图像resize的一种算法 +// NJ_CHROMA_FILTER=0 = Use simple pixel repetition for chroma upsampling +// (bad quality, but faster and less code). + + +// API +// === +// +// For API documentation, read the "header section" below. + + +// EXAMPLE +// ======= +// +// A few pages below, you can find an example program that uses NanoJPEG to +// convert JPEG files into PGM or PPM. To compile it, use something like +// gcc -O3 -D_NJ_EXAMPLE_PROGRAM -o nanojpeg nanojpeg.c +// You may also add -std=c99 -Wall -Wextra -pedantic -Werror, if you want :) + + +/////////////////////////////////////////////////////////////////////////////// +// HEADER SECTION // +// copy and pase this into nanojpeg.h if you want // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _NANOJPEG_H +#define _NANOJPEG_H + +// nj_result_t: Result codes for njDecode(). +typedef enum _nj_result { + NJ_OK = 0, // no error, decoding successful + NJ_NO_JPEG, // not a JPEG file + NJ_UNSUPPORTED, // unsupported format + NJ_OUT_OF_MEM, // out of memory + NJ_INTERNAL_ERR, // internal error + NJ_SYNTAX_ERROR, // syntax error + __NJ_FINISHED, // used internally, will never be reported +} nj_result_t; + +// njInit: Initialize NanoJPEG. +// For safety reasons, this should be called at least one time before using +// using any of the other NanoJPEG functions. +void njInit(void); + +// njDecode: Decode a JPEG image. +// Decodes a memory dump of a JPEG file into internal buffers. +// Parameters: +// jpeg = The pointer to the memory dump. +// size = The size of the JPEG file. +// Return value: The error code in case of failure, or NJ_OK (zero) on success. +nj_result_t njDecode(const void* jpeg, const int size); + +// njGetWidth: Return the width (in pixels) of the most recently decoded +// image. If njDecode() failed, the result of njGetWidth() is undefined. +int njGetWidth(void); + +// njGetHeight: Return the height (in pixels) of the most recently decoded +// image. If njDecode() failed, the result of njGetHeight() is undefined. +int njGetHeight(void); + +// njIsColor: Return 1 if the most recently decoded image is a color image +// (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result +// of njGetWidth() is undefined. +int njIsColor(void); + +// njGetImage: Returns the decoded image data. +// Returns a pointer to the most recently image. The memory layout it byte- +// oriented, top-down, without any padding between lines. Pixels of color +// images will be stored as three consecutive bytes for the red, green and +// blue channels. This data format is thus compatible with the PGM or PPM +// file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8. +// If njDecode() failed, the result of njGetImage() is undefined. +unsigned char* njGetImage(void); + +// njGetImageSize: Returns the size (in bytes) of the image data returned +// by njGetImage(). If njDecode() failed, the result of njGetImageSize() is +// undefined. +int njGetImageSize(void); + +// njDone: Uninitialize NanoJPEG. +// Resets NanoJPEG's internal state and frees all memory that has been +// allocated at run-time by NanoJPEG. It is still possible to decode another +// image after a njDone() call. +void njDone(void); + +#endif//_NANOJPEG_H + + +/////////////////////////////////////////////////////////////////////////////// +// CONFIGURATION SECTION // +// adjust the default settings for the NJ_ defines here // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef NJ_USE_LIBC + #define NJ_USE_LIBC 1 +#endif + +#ifndef NJ_USE_WIN32 + #ifdef _MSC_VER + #define NJ_USE_WIN32 (!NJ_USE_LIBC) + #else + #define NJ_USE_WIN32 0 + #endif +#endif + +#ifndef NJ_CHROMA_FILTER + #define NJ_CHROMA_FILTER 1 +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EXAMPLE PROGRAM // +// just define _NJ_EXAMPLE_PROGRAM to compile this (requires NJ_USE_LIBC) // +/////////////////////////////////////////////////////////////////////////////// + +#ifdef _NJ_EXAMPLE_PROGRAM + +#include +#include "libc/stdlib.h" +#include "libc/string.h" + +int main(int argc, char* argv[]) { + int size; + char *buf; + FILE *f; + + if (argc < 2) { + printf("Usage: %s []\n", argv[0]); + return 2; + } + f = fopen(argv[1], "rb"); + if (!f) { + printf("Error opening the input file.\n"); + return 1; + } + fseek(f, 0, SEEK_END); + size = (int) ftell(f); // 字节 + buf = malloc(size); + fseek(f, 0, SEEK_SET); + size = (int) fread(buf, 1, size, f); // 读取整个文件内容到buf + fclose(f); + + njInit(); // 初始化nj_context_t + if (njDecode(buf, size)) { + printf("Error decoding the input file.\n"); + return 1; + } + + f = fopen((argc > 2) ? argv[2] : (njIsColor() ? "nanojpeg_out.ppm" : "nanojpeg_out.pgm"), "wb"); + if (!f) { + printf("Error opening the output file.\n"); + return 1; + } + fprintf(f, "P%d\n%d %d\n255\n", njIsColor() ? 6 : 5, njGetWidth(), njGetHeight()); + fwrite(njGetImage(), 1, njGetImageSize(), f); + fclose(f); + njDone(); + return 0; +} + +#endif + +// 解释什么是stride http://msdn.microsoft.com/en-us/library/windows/desktop/aa473780(v=vs.85).aspx + +/////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION SECTION // +// you may stop reading here // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _NJ_INCLUDE_HEADER_ONLY + +#include // For NULL in freestanding mode + +#ifdef _MSC_VER + #define NJ_INLINE static __inline + #define NJ_FORCE_INLINE static __forceinline +#else + #define NJ_INLINE static inline + #define NJ_FORCE_INLINE static inline +#endif + +#if NJ_USE_LIBC + #include "libc/stdlib.h" + #define njAllocMem malloc + #define njFreeMem free + #define njFillMem memset + #define njCopyMem memcpy +#elif NJ_USE_WIN32 + #include + #define njAllocMem(size) ((void*) LocalAlloc(LMEM_FIXED, (SIZE_T)(size))) + #define njFreeMem(block) ((void) LocalFree((HLOCAL) block)) + NJ_INLINE void njFillMem(void* block, unsigned char value, int count) { __asm { + mov edi, block + mov al, value + mov ecx, count + rep stosb + } } + NJ_INLINE void njCopyMem(void* dest, const void* src, int count) { __asm { + mov edi, dest + mov esi, src + mov ecx, count + rep movsb + } } +#else + extern void* njAllocMem(int size); + extern void njFreeMem(void* block); + extern void njFillMem(void* block, unsigned char byte, int size); + extern void njCopyMem(void* dest, const void* src, int size); +#endif + +typedef struct _nj_code { + unsigned char bits, code; +} nj_vlc_code_t; + +typedef struct _nj_cmp { + int cid; + int ssx, ssy; // 水平/垂直因子 + int width, height; + int stride; + int qtsel; // Quantization Table量化表 + int actabsel, dctabsel; // AC/DC Huffman Table + int dcpred; // DC prediction + unsigned char *pixels; +} nj_component_t; // 颜色分量 + +typedef struct _nj_ctx { + nj_result_t error; + const unsigned char *pos; // 待解码数据指针(按字节来) + int size; // 整个数据的长度 + int length; // 某一个marker内容的长度 + int width, height; // 图片宽和高度 + int mbwidth, mbheight; // MCU水平/垂直个数 + int mbsizex, mbsizey; // MCU宽/高 + int ncomp; // 颜色分量数 + nj_component_t comp[3]; // YCbCr + int qtused, qtavail; // 这两个目前看不出来很大用处 + unsigned char qtab[4][64]; // 但是目前似乎只有2个 + nj_vlc_code_t vlctab[4][65536]; // 构造所有16位数的Huffman基数 + // 目前基本上是4个(直/交/0/1) + int buf, bufbits; // 这是用来做什么的 buf是存放内容的 bufbits是计数器,存放了多少个bits + int block[64]; + int rstinterval; + unsigned char *rgb; // 解析出来的RGB所要占用的内存 // 每1个点包含3个字节,按找RGB的顺序 +} nj_context_t; + +static nj_context_t nj; + +static const char njZZ[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, +11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, +42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, +38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; + +/* +0 1 2 3 4 5 6 7 + +8 9 10 11 12 13 14 15 + +16 17 18 19 20 21 22 23 + +24 25 26 27 28 29 30 31 + +32 33 34 35 36 37 38 39 + +40 41 42 43 44 45 46 47 + +48 49 50 51 52 53 54 55 + +56 57 58 59 60 61 62 63 +*/ + +NJ_FORCE_INLINE unsigned char njClip(const int x) { // 限定范围是0 ~ 255之间 + return (x < 0) ? 0 : ((x > 0xFF) ? 0xFF : (unsigned char) x); +} + +#define W1 2841 +#define W2 2676 +#define W3 2408 +#define W5 1609 +#define W6 1108 +#define W7 565 + +NJ_INLINE void njRowIDCT(int* blk) { // 按行来操作的 0 ~ 7 // 8 ~ 15 + int x0, x1, x2, x3, x4, x5, x6, x7, x8; + if (!((x1 = blk[4] << 11) + | (x2 = blk[6]) + | (x3 = blk[2]) + | (x4 = blk[1]) + | (x5 = blk[7]) + | (x6 = blk[5]) + | (x7 = blk[3]))) + { + blk[0] = blk[1] = blk[2] = blk[3] = blk[4] = blk[5] = blk[6] = blk[7] = blk[0] << 3; + return; + } + x0 = (blk[0] << 11) + 128; + x8 = W7 * (x4 + x5); + x4 = x8 + (W1 - W7) * x4; + x5 = x8 - (W1 + W7) * x5; + x8 = W3 * (x6 + x7); + x6 = x8 - (W3 - W5) * x6; + x7 = x8 - (W3 + W5) * x7; + x8 = x0 + x1; + x0 -= x1; + x1 = W6 * (x3 + x2); + x2 = x1 - (W2 + W6) * x2; + x3 = x1 + (W2 - W6) * x3; + x1 = x4 + x6; + x4 -= x6; + x6 = x5 + x7; + x5 -= x7; + x7 = x8 + x3; + x8 -= x3; + x3 = x0 + x2; + x0 -= x2; + x2 = (181 * (x4 + x5) + 128) >> 8; + x4 = (181 * (x4 - x5) + 128) >> 8; + blk[0] = (x7 + x1) >> 8; + blk[1] = (x3 + x2) >> 8; + blk[2] = (x0 + x4) >> 8; + blk[3] = (x8 + x6) >> 8; + blk[4] = (x8 - x6) >> 8; + blk[5] = (x0 - x4) >> 8; + blk[6] = (x3 - x2) >> 8; + blk[7] = (x7 - x1) >> 8; +} + +NJ_INLINE void njColIDCT(const int* blk, unsigned char *out, int stride) { + int x0, x1, x2, x3, x4, x5, x6, x7, x8; + if (!((x1 = blk[8*4] << 8) + | (x2 = blk[8*6]) + | (x3 = blk[8*2]) + | (x4 = blk[8*1]) + | (x5 = blk[8*7]) + | (x6 = blk[8*5]) + | (x7 = blk[8*3]))) + { + x1 = njClip(((blk[0] + 32) >> 6) + 128); + for (x0 = 8; x0; --x0) { + *out = (unsigned char) x1; + out += stride; + } + return; + } + x0 = (blk[0] << 8) + 8192; + x8 = W7 * (x4 + x5) + 4; + x4 = (x8 + (W1 - W7) * x4) >> 3; + x5 = (x8 - (W1 + W7) * x5) >> 3; + x8 = W3 * (x6 + x7) + 4; + x6 = (x8 - (W3 - W5) * x6) >> 3; + x7 = (x8 - (W3 + W5) * x7) >> 3; + x8 = x0 + x1; + x0 -= x1; + x1 = W6 * (x3 + x2) + 4; + x2 = (x1 - (W2 + W6) * x2) >> 3; + x3 = (x1 + (W2 - W6) * x3) >> 3; + x1 = x4 + x6; + x4 -= x6; + x6 = x5 + x7; + x5 -= x7; + x7 = x8 + x3; + x8 -= x3; + x3 = x0 + x2; + x0 -= x2; + x2 = (181 * (x4 + x5) + 128) >> 8; // Y,Cb和Cr的值都范围都是-128 ~ 127,并且在FDCT的时候有先减去128,所以现在要IDCT之后再加上128 + x4 = (181 * (x4 - x5) + 128) >> 8; + *out = njClip(((x7 + x1) >> 14) + 128); out += stride; + *out = njClip(((x3 + x2) >> 14) + 128); out += stride; + *out = njClip(((x0 + x4) >> 14) + 128); out += stride; + *out = njClip(((x8 + x6) >> 14) + 128); out += stride; + *out = njClip(((x8 - x6) >> 14) + 128); out += stride; + *out = njClip(((x0 - x4) >> 14) + 128); out += stride; + *out = njClip(((x3 - x2) >> 14) + 128); out += stride; + *out = njClip(((x7 - x1) >> 14) + 128); +} + +#define njThrow(e) do { nj.error = e; return; } while (0) +#define njCheckError() do { if (nj.error) return; } while (0) + +static int njShowBits(int bits) { // 能放得下大于32位的值么? + unsigned char newbyte; + if (!bits) return 0; + while (nj.bufbits < bits) { // 也就是说要buf的位数小于已经buf的位数的时候,就直接读出来? + if (nj.size <= 0) { + nj.buf = (nj.buf << 8) | 0xFF; + nj.bufbits += 8; + continue; + } + newbyte = *nj.pos++; // 数据指针是按字节 + nj.size--; + nj.bufbits += 8; + nj.buf = (nj.buf << 8) | newbyte; // 高位最终会被覆盖掉,比如我要buf一个64位的值怎么办? + if (newbyte == 0xFF) { + if (nj.size) { + unsigned char marker = *nj.pos++; + nj.size--; + switch (marker) { + case 0x00: + case 0xFF: + break; + case 0xD9: nj.size = 0; break; + default: + if ((marker & 0xF8) != 0xD0) + nj.error = NJ_SYNTAX_ERROR; + else { + nj.buf = (nj.buf << 8) | marker; + nj.bufbits += 8; + } + } + } else + nj.error = NJ_SYNTAX_ERROR; + } + } + return (nj.buf >> (nj.bufbits - bits)) & ((1 << bits) - 1); +} + +NJ_INLINE void njSkipBits(int bits) { + if (nj.bufbits < bits) + (void) njShowBits(bits); + nj.bufbits -= bits; +} + +NJ_INLINE int njGetBits(int bits) { + int res = njShowBits(bits); + njSkipBits(bits); + return res; +} + +NJ_INLINE void njByteAlign(void) { + nj.bufbits &= 0xF8; // (1111 1000)8的倍数,不满8的部分丢弃 +} + +static void njSkip(int count) { + nj.pos += count; // 数据指针增加 + nj.size -= count; // 总体数据大小减去count + nj.length -= count; // 当前marker长度减去count + if (nj.size < 0) nj.error = NJ_SYNTAX_ERROR; +} + +NJ_INLINE unsigned short njDecode16(const unsigned char *pos) { + return (pos[0] << 8) | pos[1]; // 00000000 00001101 +} + +static void njDecodeLength(void) { // decode长度字段,这个方法调用一般都是已经进入到特定的marker之后 + if (nj.size < 2) njThrow(NJ_SYNTAX_ERROR); + nj.length = njDecode16(nj.pos); // 该marker的长度(除去marker名字所占用的2个字节) + if (nj.length > nj.size) njThrow(NJ_SYNTAX_ERROR); + njSkip(2); +} + +NJ_INLINE void njSkipMarker(void) { + njDecodeLength(); + njSkip(nj.length); +} + +NJ_INLINE void njDecodeSOF(void) { // 解析Start of Frame的时候就会把所需要的内存都分配好 + int i, ssxmax = 0, ssymax = 0; + nj_component_t* c; + njDecodeLength(); // 解析长度并移动数据指针 + if (nj.length < 9) njThrow(NJ_SYNTAX_ERROR); + if (nj.pos[0] != 8) njThrow(NJ_UNSUPPORTED); // 样本精度,一般都是8 + nj.height = njDecode16(nj.pos + 1); // 图片高度/宽度 + nj.width = njDecode16(nj.pos + 3); + nj.ncomp = nj.pos[5]; // 颜色分量数据,一般都是3 + njSkip(6); // 之前共6个字节数据,所以移动数据指针6个字节 + switch (nj.ncomp) { // 目前只支持1和3这两种 + case 1: + case 3: + break; + default: + njThrow(NJ_UNSUPPORTED); + } + if (nj.length < (nj.ncomp * 3)) njThrow(NJ_SYNTAX_ERROR); // 数据量肯定是要大于颜色分量数 multiply 3,因为接着存颜色分量信息的每个结构占3个字节 + // 颜色分量ID占用1个字节,水平/垂直因子占用1个字节(高4位水平,低4位垂直),量化表占用1个字节 + for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) { + c->cid = nj.pos[0]; // 颜色分量ID + if (!(c->ssx = nj.pos[1] >> 4)) njThrow(NJ_SYNTAX_ERROR); // 高4位(水平因子) + if (c->ssx & (c->ssx - 1)) njThrow(NJ_UNSUPPORTED); // non-power of two + if (!(c->ssy = nj.pos[1] & 15)) njThrow(NJ_SYNTAX_ERROR); // (00001111)低4位(垂直因子) + if (c->ssy & (c->ssy - 1)) njThrow(NJ_UNSUPPORTED); // non-power of two + if ((c->qtsel = nj.pos[2]) & 0xFC) njThrow(NJ_SYNTAX_ERROR); // (11111101) 这里0xFC是用在这里干什么的? + njSkip(3); // 移动数据指针到下一个颜色分量 + nj.qtused |= 1 << c->qtsel; // 这里是做什么用的?看不出来 + if (c->ssx > ssxmax) ssxmax = c->ssx; // 记录最大水平因子 + if (c->ssy > ssymax) ssymax = c->ssy; // 记录最大垂直因子 + } + if (nj.ncomp == 1) { // 只有一种颜色分量的时候就简单啦 + c = nj.comp; + c->ssx = c->ssy = ssxmax = ssymax = 1; + } + nj.mbsizex = ssxmax << 3; // MCU宽 是 水平采样因子最大值 multiply 8 + nj.mbsizey = ssymax << 3; // MCU高 是 垂直采样因子最大值 multiply 8 + nj.mbwidth = (nj.width + nj.mbsizex - 1) / nj.mbsizex; // 分子采用+ nj.mbsizex - 1就取到大于但是最接近(等于)宽度的值, + // 并且这个值是MCU宽度整数倍 // 这里是水平方向MCU的个数 + nj.mbheight = (nj.height + nj.mbsizey - 1) / nj.mbsizey; // 这里是垂直方向MCU的个数 + for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) { + c->width = (nj.width * c->ssx + ssxmax - 1) / ssxmax; // 采样宽度? 最大水平/垂直因子的值就是图片原来的值,否则就会根据因子做相应的减少 + c->stride = (c->width + 7) & 0x7FFFFFF8; // (0111 1111 1111 1111 1111 1111 1111 1000) 做什么?以1234567结尾的都省略掉? + // 变成8的整数 + // 补齐8位,注意前面有加7,所以总是不会比原来的少,比如原来是227,那么这里就会变成232 + // 这是按照数据单元计算的,所以不对 + + c->height = (nj.height * c->ssy + ssymax - 1) / ssymax; + c->stride = nj.mbwidth * nj.mbsizex * c->ssx / ssxmax; // 再计算一遍stride有什么用?前面计算的是错误的,没有考虑MCU宽度 + // 这里都已经是round过的了,所以直接计算 + + if (((c->width < 3) && (c->ssx != ssxmax)) || ((c->height < 3) && (c->ssy != ssymax))) njThrow(NJ_UNSUPPORTED); + if (!(c->pixels = njAllocMem(c->stride * (nj.mbheight * nj.mbsizey * c->ssy / ssymax)))) njThrow(NJ_OUT_OF_MEM); // 为分量分配内存 + // 大小是所有MCU的 + // 可能比图片实际 + // 尺寸大 + } + if (nj.ncomp == 3) { // 只有有3个颜色分量的时候才需要 + nj.rgb = njAllocMem(nj.width * nj.height * nj.ncomp); + if (!nj.rgb) njThrow(NJ_OUT_OF_MEM); + } + njSkip(nj.length); +} + + + +NJ_INLINE void njDecodeDHT(void) { + int codelen, currcnt, remain, spread, i, j; + nj_vlc_code_t *vlc; + static unsigned char counts[16]; // 码字 + njDecodeLength(); + while (nj.length >= 17) { // 码字的数量(16) + 类型和ID(1) + i = nj.pos[0]; // 类型和ID + if (i & 0xEC) njThrow(NJ_SYNTAX_ERROR); // (11101100) + if (i & 0x02) njThrow(NJ_UNSUPPORTED); // (00000010) + i = (i | (i >> 3)) & 3; // combined DC/AC + tableid value + // 直流0,直流1,交流0,交流1 + for (codelen = 1; codelen <= 16; ++codelen) // 码字长度 + counts[codelen - 1] = nj.pos[codelen]; // 读取码字 DHT 当中的16个字节 00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00 + njSkip(17); + + vlc = &nj.vlctab[i][0]; + remain = spread = 65536; + for (codelen = 1; codelen <= 16; ++codelen) { + spread >>= 1; // 干什么? // 65536 >> 16 = 1 每个category所包含的编码个数 + currcnt = counts[codelen - 1]; + if (!currcnt) continue; // 如果该位数没有码字 + if (nj.length < currcnt) njThrow(NJ_SYNTAX_ERROR); + remain -= currcnt << (16 - codelen); // 干什么? 计算当前size的码字占用多少VLC表的空间,得到剩下的空间 + if (remain < 0) njThrow(NJ_SYNTAX_ERROR); + for (i = 0; i < currcnt; ++i) { // 码字个数,同样位数的码字可以有多个 + register unsigned char code = nj.pos[i]; // 有多少个就,读多少个字节 + for (j = spread; j; --j) { // 保存这么多个有什么作用? + vlc->bits = (unsigned char) codelen; // 码字位数 + vlc->code = code; // 码字值(这个读取出来的到底是什么?00 01 02 03 04 05 06 07 08 09 0A 0B,是值,还是权重?) + ++vlc; + } + } + njSkip(currcnt); + } + while (remain--) { // 16位都填充完成,剩下的就用0填(1位码字XX个,2位码字XX个,...) +// printf("i'm nothing vlc id %d\n", tblid); + vlc->bits = 0; + ++vlc; + } + + // for debug +// printf("Huffman vlc id %d\n", tblid); +// njPrintHT(tblid); + } + + if (nj.length) njThrow(NJ_SYNTAX_ERROR); +} + +NJ_INLINE void njDecodeDQT(void) { + int i; + unsigned char *t; + njDecodeLength(); + while (nj.length >= 65) { + i = nj.pos[0]; // QT信息,高4位为QT精度,低4位为QT号 + if (i & 0xFC) njThrow(NJ_SYNTAX_ERROR); // (1111 1110)这个用来检测QT号码是否正确的吗?目前精度好像都为0,所以这么写? + nj.qtavail |= 1 << i; // XXX 直接通过这里转换为数量? + t = &nj.qtab[i][0]; + for (i = 0; i < 64; ++i) + t[i] = nj.pos[i + 1]; // 读取到QT数组当中,但应该还是按照文件流当中的排列 + njSkip(65); + } + if (nj.length) njThrow(NJ_SYNTAX_ERROR); +} + +NJ_INLINE void njDecodeDRI(void) { + njDecodeLength(); + if (nj.length < 2) njThrow(NJ_SYNTAX_ERROR); + nj.rstinterval = njDecode16(nj.pos); + njSkip(nj.length); +} + +static int njGetVLC(nj_vlc_code_t* vlc, unsigned char* code) { // Variable Length Coding + int value = njShowBits(16); // 为什么是2个字节? 这又是什么? 或许是这里的Huffman编码的码字永远是少于16位的 + int bits = vlc[value].bits; + + if (!bits) { nj.error = NJ_SYNTAX_ERROR; return 0; } + njSkipBits(bits); + value = vlc[value].code; + + if (code) *code = (unsigned char) value; + bits = value & 15; // 这个value必须是0~15之间? + if (!bits) { + return 0; + } + value = njGetBits(bits); // 如果这里需要读取的值的位数超过之前njShowBits剩余的值,这里会重新读取 + if (value < (1 << (bits - 1))) + value += ((-1) << bits) + 1; + + return value; +} + +NJ_INLINE void njDecodeBlock(nj_component_t* c, unsigned char* out) { // 8 x 8 + unsigned char code = 0; + int value, coef = 0; + njFillMem(nj.block, 0, sizeof(nj.block)); + int dcvlcval = njGetVLC(&nj.vlctab[c->dctabsel][0], NULL); + c->dcpred += dcvlcval; + nj.block[0] = (c->dcpred) * nj.qtab[c->qtsel][0]; // DC // 这里是反量化? + do { + value = njGetVLC(&nj.vlctab[c->actabsel][0], &code); // DC 2/3 + if (!code) break; // EOB + if (!(code & 0x0F) && (code != 0xF0)) njThrow(NJ_SYNTAX_ERROR); // 这是什么字段?(难道是为了兼容这个过程中可以遇到0xF0这样的数据) + coef += (code >> 4) + 1; // coefficient 系数 + if (coef > 63) njThrow(NJ_SYNTAX_ERROR); + nj.block[(int) njZZ[coef]] = value * nj.qtab[c->qtsel][coef]; // AC 这里是反量化? + } while (coef < 63); + for (coef = 0; coef < 64; coef += 8) + njRowIDCT(&nj.block[coef]); // 上面先Huffman解码/反量化,这里行(反DCT) + for (coef = 0; coef < 8; ++coef) + njColIDCT(&nj.block[coef], &out[coef], c->stride); +} + +NJ_INLINE void njDecodeScan(void) { +// njPrintHT(0); +// njPrintHT(2); +// njPrintHT(1); +// njPrintHT(3); + + int i, mbx, mby, sbx, sby; + int rstcount = nj.rstinterval, nextrst = 0; + nj_component_t* c; + njDecodeLength(); + if (nj.length < (4 + 2 * nj.ncomp)) njThrow(NJ_SYNTAX_ERROR); + if (nj.pos[0] != nj.ncomp) njThrow(NJ_UNSUPPORTED); + njSkip(1); // 颜色分量数量 + for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) { + if (nj.pos[0] != c->cid) njThrow(NJ_SYNTAX_ERROR); // 颜色分量ID + if (nj.pos[1] & 0xEE) njThrow(NJ_SYNTAX_ERROR); + c->dctabsel = nj.pos[1] >> 4; // 高4位为直流表DC Table + c->actabsel = (nj.pos[1] & 1) | 2; // 低4位为交流表AC Table(这里有做特殊处理,所以AC的表名不会和DC相同) + + + + njSkip(2); + } + if (nj.pos[0] || (nj.pos[1] != 63) || nj.pos[2]) njThrow(NJ_UNSUPPORTED); + njSkip(nj.length); // 忽略3个字节 通常为 00 3F 00 + // 2 + 1 + 6 + 3为12字节,这个marker的长度刚好为12字节 + // 接下来都是编码过的图像数据 + for (mbx = mby = 0;;) { + for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) // 每个分量都要decode + for (sby = 0; sby < c->ssy; ++sby) // 水平/垂直因子 + for (sbx = 0; sbx < c->ssx; ++sbx) { + njDecodeBlock(c, &c->pixels[((mby * c->ssy + sby) * c->stride + mbx * c->ssx + sbx) << 3]); // 读取原始编码过 + // 的图片数据到block中 + // 并反量化,反离散余弦变换 + njCheckError(); + } + if (++mbx >= nj.mbwidth) { // 读完所有的MCU,到达最右就返回从下一行开始 + mbx = 0; + if (++mby >= nj.mbheight) break; // 到达最底行的时候推出,decode结束 + } + if (nj.rstinterval && !(--rstcount)) { // restart marker + njByteAlign(); + i = njGetBits(16); + if (((i & 0xFFF8) != 0xFFD0) || ((i & 7) != nextrst)) njThrow(NJ_SYNTAX_ERROR); + nextrst = (nextrst + 1) & 7; + rstcount = nj.rstinterval; + for (i = 0; i < 3; ++i) + nj.comp[i].dcpred = 0; + } + } + nj.error = __NJ_FINISHED; +} + +#if NJ_CHROMA_FILTER + +#define CF4A (-9) +#define CF4B (111) +#define CF4C (29) +#define CF4D (-3) +#define CF3A (28) +#define CF3B (109) +#define CF3C (-9) +#define CF3X (104) +#define CF3Y (27) +#define CF3Z (-3) +#define CF2A (139) +#define CF2B (-11) +#define CF(x) njClip(((x) + 64) >> 7) + +// 通常我们放大图片的时候就需要upsampling,缩小的时候就downsampling,通称为resampling +// 这里Cb/Cr分量的会少些,所以需要upsampling + +NJ_INLINE void njUpsampleH(nj_component_t* c) { + + const int xmax = c->width - 3; + unsigned char *out, *lin, *lout; + int x, y; + out = njAllocMem((c->width * c->height) << 1); + if (!out) njThrow(NJ_OUT_OF_MEM); + lin = c->pixels; + lout = out; + for (y = c->height; y; --y) { + lout[0] = CF(CF2A * lin[0] + CF2B * lin[1]); + lout[1] = CF(CF3X * lin[0] + CF3Y * lin[1] + CF3Z * lin[2]); + lout[2] = CF(CF3A * lin[0] + CF3B * lin[1] + CF3C * lin[2]); + for (x = 0; x < xmax; ++x) { + lout[(x << 1) + 3] = CF(CF4A * lin[x] + CF4B * lin[x + 1] + CF4C * lin[x + 2] + CF4D * lin[x + 3]); + lout[(x << 1) + 4] = CF(CF4D * lin[x] + CF4C * lin[x + 1] + CF4B * lin[x + 2] + CF4A * lin[x + 3]); + } + lin += c->stride; + lout += c->width << 1; + lout[-3] = CF(CF3A * lin[-1] + CF3B * lin[-2] + CF3C * lin[-3]); + lout[-2] = CF(CF3X * lin[-1] + CF3Y * lin[-2] + CF3Z * lin[-3]); + lout[-1] = CF(CF2A * lin[-1] + CF2B * lin[-2]); + } + c->width <<= 1; + c->stride = c->width; + njFreeMem(c->pixels); + c->pixels = out; +} + +NJ_INLINE void njUpsampleV(nj_component_t* c) { + + const int w = c->width, s1 = c->stride, s2 = s1 + s1; + unsigned char *out, *cin, *cout; + int x, y; + out = njAllocMem((c->width * c->height) << 1); + if (!out) njThrow(NJ_OUT_OF_MEM); + for (x = 0; x < w; ++x) { + cin = &c->pixels[x]; + cout = &out[x]; + *cout = CF(CF2A * cin[0] + CF2B * cin[s1]); cout += w; + *cout = CF(CF3X * cin[0] + CF3Y * cin[s1] + CF3Z * cin[s2]); cout += w; + *cout = CF(CF3A * cin[0] + CF3B * cin[s1] + CF3C * cin[s2]); cout += w; + cin += s1; + for (y = c->height - 3; y; --y) { + *cout = CF(CF4A * cin[-s1] + CF4B * cin[0] + CF4C * cin[s1] + CF4D * cin[s2]); cout += w; + *cout = CF(CF4D * cin[-s1] + CF4C * cin[0] + CF4B * cin[s1] + CF4A * cin[s2]); cout += w; + cin += s1; + } + cin += s1; + *cout = CF(CF3A * cin[0] + CF3B * cin[-s1] + CF3C * cin[-s2]); cout += w; + *cout = CF(CF3X * cin[0] + CF3Y * cin[-s1] + CF3Z * cin[-s2]); cout += w; + *cout = CF(CF2A * cin[0] + CF2B * cin[-s1]); + } + c->height <<= 1; + c->stride = c->width; + njFreeMem(c->pixels); + c->pixels = out; +} + +#else + +NJ_INLINE void njUpsample(nj_component_t* c) { + + int x, y, xshift = 0, yshift = 0; + unsigned char *out, *lin, *lout; + while (c->width < nj.width) { c->width <<= 1; ++xshift; } + while (c->height < nj.height) { c->height <<= 1; ++yshift; } + out = njAllocMem(c->width * c->height); // 放大后的尺寸 + if (!out) njThrow(NJ_OUT_OF_MEM); + lin = c->pixels; + lout = out; + for (y = 0; y < c->height; ++y) { + lin = &c->pixels[(y >> yshift) * c->stride]; + for (x = 0; x < c->width; ++x) + lout[x] = lin[x >> xshift]; + lout += c->width; + } + c->stride = c->width; + njFreeMem(c->pixels); + c->pixels = out; +} + +#endif + +NJ_INLINE void njConvert() { + int i; + nj_component_t* c; + for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) { // 如果需要的话就upsampling + #if NJ_CHROMA_FILTER + while ((c->width < nj.width) || (c->height < nj.height)) { + if (c->width < nj.width) njUpsampleH(c); + njCheckError(); + if (c->height < nj.height) njUpsampleV(c); + njCheckError(); + } + #else + if ((c->width < nj.width) || (c->height < nj.height)) + njUpsample(c); + #endif + if ((c->width < nj.width) || (c->height < nj.height)) njThrow(NJ_INTERNAL_ERR); + } + if (nj.ncomp == 3) { // SEE njGetImage() + // convert to RGB + int x, yy; + unsigned char *prgb = nj.rgb; + const unsigned char *py = nj.comp[0].pixels; + const unsigned char *pcb = nj.comp[1].pixels; + const unsigned char *pcr = nj.comp[2].pixels; + // 多余的数据(编/解码是对齐用的)会被丢弃吗? + for (yy = nj.height; yy; --yy) { // 列 + for (x = 0; x < nj.width; ++x) { // 行 + register int y = py[x] << 8; // 这是为什么? 色彩空间转换公式计算需要 + register int cb = pcb[x] - 128; // YCbCr的Cb和Cr一般都是有符号数,但是在JPEG当中都是无符号数 + register int cr = pcr[x] - 128; + *prgb++ = njClip((y + 359 * cr + 128) >> 8); // 色彩空间转换,YCbCr到RGB + *prgb++ = njClip((y - 88 * cb - 183 * cr + 128) >> 8); + *prgb++ = njClip((y + 454 * cb + 128) >> 8); + } + py += nj.comp[0].stride; // 移动YCbCr数据指针,每一行都是有stride的,所以当需要的数据都得到时,后面的就不管,直接丢弃,移动到下一行 + pcb += nj.comp[1].stride; + pcr += nj.comp[2].stride; + } + } else if (nj.comp[0].width != nj.comp[0].stride) { // 如果宽度和stride都一样,什么都不用做 + // grayscale -> only remove stride + unsigned char *pin = &nj.comp[0].pixels[nj.comp[0].stride]; + unsigned char *pout = &nj.comp[0].pixels[nj.comp[0].width]; + int y; + for (y = nj.comp[0].height - 1; y; --y) { + njCopyMem(pout, pin, nj.comp[0].width); + pin += nj.comp[0].stride; + pout += nj.comp[0].width; + } + nj.comp[0].stride = nj.comp[0].width; + } +} + +void njInit(void) { + njFillMem(&nj, 0, sizeof(nj_context_t)); // 初始化nj_context_t +} + +void njDone(void) { + int i; + for (i = 0; i < 3; ++i) + if (nj.comp[i].pixels) njFreeMem((void*) nj.comp[i].pixels); + if (nj.rgb) njFreeMem((void*) nj.rgb); + njInit(); +} + +nj_result_t njDecode(const void* jpeg, const int size) { + njDone(); + nj.pos = (const unsigned char*) jpeg; + nj.size = size & 0x7FFFFFFF; // ? + if (nj.size < 2) return NJ_NO_JPEG; + if ((nj.pos[0] ^ 0xFF) | (nj.pos[1] ^ 0xD8)) return NJ_NO_JPEG; // 不以0xFFD8打头(为什么要用异或来判断?) + njSkip(2); + while (!nj.error) { // 有“错误”的时候离开 + if ((nj.size < 2) || (nj.pos[0] != 0xFF)) return NJ_SYNTAX_ERROR; // 太小,或者不以0xFF打头 + njSkip(2); // 移动到标签的后面(长度字段的前面) + switch (nj.pos[-1]) { + case 0xC0: njDecodeSOF(); break; + case 0xC4: njDecodeDHT(); break; + case 0xDB: njDecodeDQT(); break; + case 0xDD: njDecodeDRI(); break; + case 0xDA: njDecodeScan(); break; + case 0xFE: njSkipMarker(); break; + default: + if ((nj.pos[-1] & 0xF0) == 0xE0) // JPG0和APP0字段,目前都忽略 + njSkipMarker(); + else + return NJ_UNSUPPORTED; + } + } + if (nj.error != __NJ_FINISHED) return nj.error; + nj.error = NJ_OK; + njConvert(); + return nj.error; +} + +int njGetWidth(void) { return nj.width; } +int njGetHeight(void) { return nj.height; } +int njIsColor(void) { return (nj.ncomp != 1); } +unsigned char* njGetImage(void) { return (nj.ncomp == 1) ? nj.comp[0].pixels : nj.rgb; } // 一/三个分量 +int njGetImageSize(void) { return nj.width * nj.height * nj.ncomp; } + +#endif // _NJ_INCLUDE_HEADER_ONLY \ No newline at end of file diff --git a/src/kernel/userland/nanojpeg.h b/src/kernel/userland/nanojpeg.h new file mode 100644 index 0000000..07bf056 --- /dev/null +++ b/src/kernel/userland/nanojpeg.h @@ -0,0 +1,10 @@ +// nanojpeg.h - Header for NanoJPEG decoder (freestanding kernel use) +#ifndef NANOJPEG_H +#define NANOJPEG_H + +// Include naojpeg.c in header-only mode to get the type/function declarations +#define _NJ_INCLUDE_HEADER_ONLY +#include "nanojpeg.c" +#undef _NJ_INCLUDE_HEADER_ONLY + +#endif // NANOJPEG_H diff --git a/src/kernel/userland/nanojpeg.o b/src/kernel/userland/nanojpeg.o new file mode 100644 index 0000000..b9c0a23 Binary files /dev/null and b/src/kernel/userland/nanojpeg.o differ diff --git a/src/kernel/userland/notepad.elf b/src/kernel/userland/notepad.elf index 6a39864..60ddd31 100755 Binary files a/src/kernel/userland/notepad.elf and b/src/kernel/userland/notepad.elf differ diff --git a/src/kernel/userland/paint.c b/src/kernel/userland/paint.c new file mode 100644 index 0000000..4676cb8 --- /dev/null +++ b/src/kernel/userland/paint.c @@ -0,0 +1,251 @@ +#include "libc/syscall.h" +#include "libc/libui.h" +#include "libc/stdlib.h" +#include +#include +#include + +#define CANVAS_W 300 +#define CANVAS_H 200 +#define PAINT_MAGIC 0x544E5042 // 'BPNT' + +#define COLOR_BLACK 0xFF000000 +#define COLOR_WHITE 0xFFFFFFFF +#define COLOR_RED 0xFFFF0000 +#define COLOR_APPLE_GREEN 0xFF4CD964 +#define COLOR_APPLE_BLUE 0xFF007AFF +#define COLOR_APPLE_YELLOW 0xFFFFCC00 + +#define COLOR_DARK_BG 0xFF121212 +#define COLOR_DARK_PANEL 0xFF202020 +#define COLOR_DARK_BORDER 0xFF404040 +#define COLOR_DARK_TEXT 0xFFE0E0E0 + +static uint32_t *canvas_buffer = NULL; +static uint32_t current_color = COLOR_BLACK; +static int last_mx = -1; +static int last_my = -1; +static char current_file_path[256] = "/Desktop/drawing.pnt"; + +static void paint_strcpy(char *dest, const char *src) { + while (*src) *dest++ = *src++; + *dest = 0; +} + +static void debug_print(const char *msg) { + sys_write(1, msg, 0); + int i = 0; + while (msg[i]) i++; + sys_write(1, msg, i); + sys_write(1, "\n", 1); +} + +static void paint_reset(void) { + if (canvas_buffer) { + for (int i = 0; i < CANVAS_W * CANVAS_H; i++) { + canvas_buffer[i] = COLOR_WHITE; + } + } +} + +static void paint_paint(ui_window_t win) { + int canvas_x = 60; + int canvas_y = 30; + + // Canvas Area with dark background and rounded corners (draw first so it's behind everything) + ui_draw_rounded_rect_filled(win, canvas_x - 2, canvas_y - 2, CANVAS_W + 4, CANVAS_H + 4, 4, COLOR_DARK_BG); + + // Toolbar area - dark mode + ui_draw_rounded_rect_filled(win, 10, 30, 40, 260 - 40, 6, COLOR_DARK_PANEL); + + // Color Palette with rounded corners + uint32_t colors[] = {COLOR_BLACK, COLOR_RED, COLOR_APPLE_GREEN, COLOR_APPLE_BLUE, COLOR_APPLE_YELLOW, COLOR_WHITE}; + for (int i = 0; i < 6; i++) { + int cy = 40 + (i * 25); + ui_draw_rounded_rect_filled(win, 15, cy, 30, 20, 3, colors[i]); + + // Highlight selected color with border + if (current_color == colors[i]) { + // Note: libui might not have draw_rounded_rect (hollow), so we just draw four lines simulating it + // or we use ui_draw_rect for hollow border + ui_draw_rect(win, 13, cy - 2, 34, 1, COLOR_DARK_TEXT); + ui_draw_rect(win, 13, cy - 2 + 24, 34, 1, COLOR_DARK_TEXT); + ui_draw_rect(win, 13, cy - 2, 1, 24, COLOR_DARK_TEXT); + ui_draw_rect(win, 13 + 34, cy - 2, 1, 24, COLOR_DARK_TEXT); + } + } + + // Toolbar Buttons - dark mode with rounded corners + ui_draw_rounded_rect_filled(win, 12, 260 - 65, 36, 20, 4, COLOR_DARK_BORDER); + ui_draw_string(win, 18, 260 - 58, "CLR", COLOR_DARK_TEXT); + + ui_draw_rounded_rect_filled(win, 12, 260 - 40, 36, 20, 4, COLOR_DARK_BORDER); + ui_draw_string(win, 18, 260 - 33, "SAV", COLOR_DARK_TEXT); + + // Draw canvas content + if (canvas_buffer) { + for (int y = 0; y < CANVAS_H; y++) { + for (int x = 0; x < CANVAS_W; x++) { + uint32_t color = canvas_buffer[y * CANVAS_W + x]; + ui_draw_rect(win, canvas_x + x, canvas_y + y, 1, 1, color); + } + } + } +} + +static void paint_put_brush(ui_window_t win, int cx, int cy) { + if (!canvas_buffer) return; + for (int dy = 0; dy < 2; dy++) { + for (int dx = 0; dx < 2; dx++) { + int px = cx + dx; + int py = cy + dy; + if (px >= 0 && px < CANVAS_W && py >= 0 && py < CANVAS_H) { + canvas_buffer[py * CANVAS_W + px] = current_color; + ui_draw_rect(win, 60 + px, 30 + py, 1, 1, current_color); + } + } + } + ui_mark_dirty(win, 60 + cx, 30 + cy, 2, 2); +} + +void paint_handle_mouse(ui_window_t win, int x, int y) { + int cx = x - 60; + int cy = y - 30; + + if (cx < 0 || cx >= CANVAS_W || cy < 0 || cy >= CANVAS_H) { + last_mx = -1; + return; + } + + if (last_mx == -1) { + paint_put_brush(win, cx, cy); + } else { + // Bresenham's line algorithm to fill gaps between points + int x0 = last_mx, y0 = last_my; + int x1 = cx, y1 = cy; + int dx = (x1 - x0 > 0) ? (x1 - x0) : (x0 - x1); + int dy = (y1 - y0 > 0) ? (y1 - y0) : (y0 - y1); + int sx = x0 < x1 ? 1 : -1; + int sy = y0 < y1 ? 1 : -1; + int err = dx - dy; + + while (1) { + paint_put_brush(win, x0, y0); + if (x0 == x1 && y0 == y1) break; + int e2 = 2 * err; + if (e2 > -dy) { err -= dy; x0 += sx; } + if (e2 < dx) { err += dx; y0 += sy; } + } + } + last_mx = cx; + last_my = cy; +} + +void paint_reset_last_pos(void) { + last_mx = -1; + last_my = -1; +} + +// Simple window message dialog wrapper using syscall +static void wm_show_message(const char *title, const char *msg) { + // Wait, userland doesn't have wm_show_message syscall available yet, or maybe it does? + // We didn't add it or GUI_EVENT doesn't support it directly. + // For now we do nothing, or just open a small window. +} + +static void paint_save(const char *path) { + int fd = sys_open(path, "w"); + if (fd >= 0) { + uint32_t header[3] = {PAINT_MAGIC, CANVAS_W, CANVAS_H}; + sys_write_fs(fd, (char*)header, sizeof(header)); + sys_write_fs(fd, (char*)canvas_buffer, CANVAS_W * CANVAS_H * sizeof(uint32_t)); + sys_close(fd); + wm_show_message("Paint", "Image saved."); + } +} + +void paint_load(const char *path) { + paint_strcpy(current_file_path, path); + int fd = sys_open(path, "r"); + if (fd >= 0) { + uint32_t header[3]; + if (sys_read(fd, (char*)header, sizeof(header)) == sizeof(header)) { + if (header[0] == PAINT_MAGIC) { + sys_read(fd, (char*)canvas_buffer, CANVAS_W * CANVAS_H * sizeof(uint32_t)); + } + } + sys_close(fd); + } +} + +static void paint_click(ui_window_t win, int x, int y) { + // Check Buttons + if (x >= 12 && x < 48) { + if (y >= 260 - 65 && y < 260 - 45) { + paint_reset(); + paint_paint(win); + ui_mark_dirty(win, 0, 0, 380, 260); + return; + } + if (y >= 260 - 40 && y < 260 - 20) { + paint_save(current_file_path); + return; + } + } + + // Check Palette + if (x >= 15 && x < 45) { + for (int i = 0; i < 6; i++) { + int cy = 40 + (i * 25); + if (y >= cy && y < cy + 20) { + uint32_t colors[] = {COLOR_BLACK, COLOR_RED, COLOR_APPLE_GREEN, COLOR_APPLE_BLUE, COLOR_APPLE_YELLOW, COLOR_WHITE}; + current_color = colors[i]; + paint_paint(win); + ui_mark_dirty(win, 0, 0, 380, 260); + return; + } + } + } + paint_handle_mouse(win, x, y); +} + +int main(int argc, char **argv) { + ui_window_t win = ui_window_create("Paint", 150, 100, 380, 260); + if (!win) return 1; + + canvas_buffer = malloc(CANVAS_W * CANVAS_H * sizeof(uint32_t)); + if (!canvas_buffer) return 1; + + paint_reset(); + + if (argc > 1) { + paint_load(argv[1]); + } + + gui_event_t ev; + while (1) { + if (ui_get_event(win, &ev)) { + if (ev.type == GUI_EVENT_PAINT) { + paint_paint(win); + ui_mark_dirty(win, 0, 0, 380, 260); + } else if (ev.type == GUI_EVENT_CLICK) { + paint_click(win, ev.arg1, ev.arg2); + paint_paint(win); + ui_mark_dirty(win, 0, 0, 380, 260); + } else if (ev.type == GUI_EVENT_MOUSE_DOWN) { + paint_handle_mouse(win, ev.arg1, ev.arg2); + } else if (ev.type == GUI_EVENT_MOUSE_UP) { + paint_reset_last_pos(); + } else if (ev.type == GUI_EVENT_MOUSE_MOVE) { + if (ev.arg3 & 0x01) { // Left button down + paint_handle_mouse(win, ev.arg1, ev.arg2); + } else { + paint_reset_last_pos(); + } + } else if (ev.type == GUI_EVENT_CLOSE) { + sys_exit(0); + } + } + } + return 0; +} \ No newline at end of file diff --git a/src/kernel/userland/paint.elf b/src/kernel/userland/paint.elf new file mode 100755 index 0000000..bf66151 Binary files /dev/null and b/src/kernel/userland/paint.elf differ diff --git a/src/kernel/userland/settings.c b/src/kernel/userland/settings.c new file mode 100644 index 0000000..3e581ef --- /dev/null +++ b/src/kernel/userland/settings.c @@ -0,0 +1,691 @@ +#include "libc/syscall.h" +#include "libc/libui.h" +#include + +#define COLOR_COFFEE 0xFF6B4423 +#define COLOR_TEAL 0xFF008080 +#define COLOR_GREEN 0xFF008000 +#define COLOR_BLUE_BG 0xFF000080 +#define COLOR_PURPLE 0xFF800080 +#define COLOR_GREY 0xFF454545 +#define COLOR_BLACK 0xFF000000 + +#define COLOR_DARK_PANEL 0xFF2D2D2D +#define COLOR_DARK_TEXT 0xFFE0E0E0 +#define COLOR_DARK_BORDER 0xFF404040 +#define COLOR_DKGRAY 0xFFAAAAAA +#define COLOR_DARK_BG 0xFF1E1E1E + +// Control panel state +#define VIEW_MAIN 0 +#define VIEW_WALLPAPER 1 +#define VIEW_NETWORK 2 +#define VIEW_DESKTOP 3 +#define VIEW_MOUSE 4 + +static int current_view = VIEW_MAIN; +static char rgb_r[4] = ""; +static char rgb_g[4] = ""; +static char rgb_b[4] = ""; +static int focused_field = -1; +static int input_cursor = 0; + +static char net_status[64] = ""; + +// Pattern buffers (128x128) +#define PATTERN_SIZE 128 +static uint32_t pattern_lumberjack[PATTERN_SIZE * PATTERN_SIZE]; +static uint32_t pattern_blue_diamond[PATTERN_SIZE * PATTERN_SIZE]; + +#define WALLPAPER_THUMB_W 100 +#define WALLPAPER_THUMB_H 60 +static uint32_t moon_thumb[WALLPAPER_THUMB_W * WALLPAPER_THUMB_H]; +static uint32_t mtn_thumb[WALLPAPER_THUMB_W * WALLPAPER_THUMB_H]; +static _Bool moon_thumb_valid = 0; +static _Bool mtn_thumb_valid = 0; + +static _Bool desktop_snap_to_grid = 1; +static _Bool desktop_auto_align = 1; +static int desktop_max_rows_per_col = 10; +static int desktop_max_cols = 10; +static int mouse_speed = 10; + +static void cli_itoa(int num, char *str) { + if (num == 0) { + str[0] = '0'; + str[1] = '\0'; + return; + } + int t = num; + int len = 0; + while (t > 0) { len++; t /= 10; } + str[len] = '\0'; + for (int i = len - 1; i >= 0; i--) { + str[i] = (num % 10) + '0'; + num /= 10; + } +} + +static void generate_lumberjack_pattern(void) { + uint32_t red = 0xFFDC143C; + uint32_t dark_grey = 0xFF404040; + uint32_t black = 0xFF000000; + int scale = 5; + + for (int y = 0; y < PATTERN_SIZE; y++) { + for (int x = 0; x < PATTERN_SIZE; x++) { + int cell_x = (x / scale) % 3; + int cell_y = (y / scale) % 3; + uint32_t color; + if (cell_x == 1 && cell_y == 1) { + color = black; + } else if (cell_x == 1 || cell_y == 1) { + color = dark_grey; + } else { + color = red; + } + pattern_lumberjack[y * PATTERN_SIZE + x] = color; + } + } +} + +static void generate_blue_diamond_pattern(void) { + uint32_t bg_color = 0xFFADD8E6; + uint32_t diamond_color = 0xFF0000CD; + + for (int y = 0; y < PATTERN_SIZE; y++) { + for (int x = 0; x < PATTERN_SIZE; x++) { + pattern_blue_diamond[y * PATTERN_SIZE + x] = bg_color; + } + } + for (int dy = -24; dy <= 24; dy++) { + for (int dx = -24; dx <= 24; dx++) { + int abs_dx = dx < 0 ? -dx : dx; + int abs_dy = dy < 0 ? -dy : dy; + if (abs_dx + abs_dy <= 24) { + int x1 = 32 + dx; + int y1 = 32 + dy; + if (x1 >= 0 && x1 < PATTERN_SIZE && y1 >= 0 && y1 < PATTERN_SIZE) { + pattern_blue_diamond[y1 * PATTERN_SIZE + x1] = diamond_color; + } + int x2 = 96 + dx; + int y2 = 96 + dy; + if (x2 >= 0 && x2 < PATTERN_SIZE && y2 >= 0 && y2 < PATTERN_SIZE) { + pattern_blue_diamond[y2 * PATTERN_SIZE + x2] = diamond_color; + } + } + } + } +} + +static uint32_t parse_rgb_separate(const char *r, const char *g, const char *b) { + int rv = 0, gv = 0, bv = 0; + for (int i = 0; r[i] && i < 3; i++) { + if (r[i] >= '0' && r[i] <= '9') rv = rv * 10 + (r[i] - '0'); + } + for (int i = 0; g[i] && i < 3; i++) { + if (g[i] >= '0' && g[i] <= '9') gv = gv * 10 + (g[i] - '0'); + } + for (int i = 0; b[i] && i < 3; i++) { + if (b[i] >= '0' && b[i] <= '9') bv = bv * 10 + (b[i] - '0'); + } + if (rv > 255) rv = 255; + if (gv > 255) gv = 255; + if (bv > 255) bv = 255; + return 0xFF000000 | (rv << 16) | (gv << 8) | bv; +} + +static void control_panel_paint_main(ui_window_t win) { + int offset_x = 8; + int offset_y = 21; + int win_w = 350; + + int item_y = 15; + int item_h = 60; + int item_spacing = 10; + + // Wallpaper + ui_draw_rounded_rect_filled(win, offset_x, offset_y + item_y, win_w - 16, item_h, 8, COLOR_DARK_PANEL); + ui_draw_rect(win, offset_x + 12, offset_y + item_y + 8, 40, 40, 0xFF87CEEB); + ui_draw_rect(win, offset_x + 12, offset_y + item_y + 28, 40, 20, 0xFF90EE90); + ui_draw_rect(win, offset_x + 24, offset_y + item_y + 22, 3, 6, 0xFF654321); + ui_draw_rect(win, offset_x + 21, offset_y + item_y + 18, 9, 8, 0xFF228B22); + ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Wallpaper", COLOR_DARK_TEXT); + ui_draw_string(win, offset_x + 60, offset_y + item_y + 35, "Choose wallpaper", COLOR_DKGRAY); + + // Network + item_y += item_h + item_spacing; + ui_draw_rounded_rect_filled(win, offset_x, offset_y + item_y, win_w - 16, item_h, 8, COLOR_DARK_PANEL); + ui_draw_rect(win, offset_x + 18, offset_y + item_y + 12, 24, 24, 0xFF4169E1); + ui_draw_rect(win, offset_x + 22, offset_y + item_y + 16, 16, 16, 0xFF87CEEB); + ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Network", COLOR_DARK_TEXT); + ui_draw_string(win, offset_x + 60, offset_y + item_y + 35, "Internet and connectivity", COLOR_DKGRAY); + + // Desktop + item_y += item_h + item_spacing; + ui_draw_rounded_rect_filled(win, offset_x, offset_y + item_y, win_w - 16, item_h, 8, COLOR_DARK_PANEL); + ui_draw_rect(win, offset_x + 12, offset_y + item_y + 10, 36, 8, 0xFFE0C060); + ui_draw_rect(win, offset_x + 12, offset_y + item_y + 18, 36, 22, 0xFFD4A574); + ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Desktop", COLOR_DARK_TEXT); + ui_draw_string(win, offset_x + 60, offset_y + item_y + 35, "Desktop alignment", COLOR_DKGRAY); + + // Mouse + item_y += item_h + item_spacing; + ui_draw_rounded_rect_filled(win, offset_x, offset_y + item_y, win_w - 16, item_h, 8, COLOR_DARK_PANEL); + ui_draw_rect(win, offset_x + 18, offset_y + item_y + 8, 20, 28, 0xFFD3D3D3); + ui_draw_rect(win, offset_x + 20, offset_y + item_y + 10, 16, 10, 0xFFB0B0B0); + ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Mouse", COLOR_DARK_TEXT); + ui_draw_string(win, offset_x + 60, offset_y + item_y + 35, "Pointer settings", COLOR_DKGRAY); +} + +static void control_panel_paint_wallpaper(ui_window_t win) { + int offset_x = 8; + int offset_y = 21; + + ui_draw_rounded_rect_filled(win, offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); + + ui_draw_string(win, offset_x, offset_y + 40, "Presets:", COLOR_DARK_TEXT); + + int button_y = offset_y + 65; + int button_x = offset_x; + + // Colors + ui_draw_rounded_rect_filled(win, button_x, button_y, 91, 25, 6, COLOR_DARK_PANEL); + ui_draw_rect(win, button_x + 8, button_y + 6, 18, 13, COLOR_COFFEE); + ui_draw_string(win, button_x + 35, button_y + 8, "Coffee", COLOR_DARK_TEXT); + + ui_draw_rounded_rect_filled(win, button_x + 100, button_y, 91, 25, 6, COLOR_DARK_PANEL); + ui_draw_rect(win, button_x + 108, button_y + 6, 18, 13, COLOR_TEAL); + ui_draw_string(win, button_x + 135, button_y + 8, "Teal", COLOR_DARK_TEXT); + + ui_draw_rounded_rect_filled(win, button_x + 200, button_y, 91, 25, 6, COLOR_DARK_PANEL); + ui_draw_rect(win, button_x + 208, button_y + 6, 18, 13, COLOR_GREEN); + ui_draw_string(win, button_x + 235, button_y + 8, "Green", COLOR_DARK_TEXT); + + button_y += 35; + ui_draw_rounded_rect_filled(win, button_x, button_y, 91, 25, 6, COLOR_DARK_PANEL); + ui_draw_rect(win, button_x + 8, button_y + 6, 18, 13, COLOR_BLUE_BG); + ui_draw_string(win, button_x + 35, button_y + 8, "Blue", COLOR_DARK_TEXT); + + ui_draw_rounded_rect_filled(win, button_x + 100, button_y, 91, 25, 6, COLOR_DARK_PANEL); + ui_draw_rect(win, button_x + 108, button_y + 6, 18, 13, COLOR_PURPLE); + ui_draw_string(win, button_x + 132, button_y + 8, "Purple", COLOR_DARK_TEXT); + + ui_draw_rounded_rect_filled(win, button_x + 200, button_y, 91, 25, 6, COLOR_DARK_PANEL); + ui_draw_rect(win, button_x + 208, button_y + 6, 18, 13, COLOR_GREY); + ui_draw_string(win, button_x + 235, button_y + 8, "Grey", COLOR_DARK_TEXT); + + // Patterns + button_y += 40; + ui_draw_string(win, offset_x, button_y, "Patterns:", COLOR_DARK_TEXT); + + button_y += 20; + ui_draw_rounded_rect_filled(win, button_x, button_y, 132, 25, 6, COLOR_DARK_PANEL); + for (int py = 0; py < 10; py++) { + for (int px = 0; px < 12; px++) { + int cell_x = px % 3; + int cell_y = py % 3; + uint32_t color = (cell_x == 1 && cell_y == 1) ? 0xFF000000 : + (cell_x == 1 || cell_y == 1) ? 0xFF404040 : 0xFFDC143C; + ui_draw_rect(win, button_x + 8 + px, button_y + 7 + py, 1, 1, color); + } + } + ui_draw_string(win, button_x + 28, button_y + 8, "Lumberjack", COLOR_DARK_TEXT); + + ui_draw_rounded_rect_filled(win, button_x + 145, button_y, 132, 25, 6, COLOR_DARK_PANEL); + for (int py = 0; py < 8; py++) { + for (int px = 0; px < 10; px++) { + int cx = px - 5; + int cy = py - 4; + int abs_cx = cx < 0 ? -cx : cx; + int abs_cy = cy < 0 ? -cy : cy; + uint32_t color = (abs_cx + abs_cy <= 3) ? 0xFF0000CD : 0xFFADD8E6; + ui_draw_rect(win, button_x + 153 + px, button_y + 8 + py, 1, 1, color); + } + } + ui_draw_string(win, button_x + 165, button_y + 8, "Blue Diamond", COLOR_DARK_TEXT); + + // Custom color + button_y += 40; + ui_draw_string(win, offset_x, button_y, "Custom color:", COLOR_DARK_TEXT); + button_y += 20; + + ui_draw_string(win, button_x, button_y + 4, "R:", COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, button_x + 25, button_y, 50, 18, 4, COLOR_DARK_PANEL); + ui_draw_string(win, button_x + 30, button_y + 4, rgb_r, (focused_field == 0) ? 0xFFFF6B6B : COLOR_DARK_TEXT); + if (focused_field == 0) { + int cursor_x = button_x + 30 + input_cursor * 8; + ui_draw_rect(win, cursor_x, button_y + 4, 1, 9, 0xFFFF6B6B); + } + + ui_draw_string(win, button_x + 90, button_y + 4, "G:", COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, button_x + 115, button_y, 50, 18, 4, COLOR_DARK_PANEL); + ui_draw_string(win, button_x + 120, button_y + 4, rgb_g, (focused_field == 1) ? 0xFF90EE90 : COLOR_DARK_TEXT); + if (focused_field == 1) { + int cursor_x = button_x + 120 + input_cursor * 8; + ui_draw_rect(win, cursor_x, button_y + 4, 1, 9, 0xFF90EE90); + } + + ui_draw_string(win, button_x + 180, button_y + 4, "B:", COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, button_x + 205, button_y, 50, 18, 4, COLOR_DARK_PANEL); + ui_draw_string(win, button_x + 210, button_y + 4, rgb_b, (focused_field == 2) ? 0xFF87CEEB : COLOR_DARK_TEXT); + if (focused_field == 2) { + int cursor_x = button_x + 210 + input_cursor * 8; + ui_draw_rect(win, cursor_x, button_y + 4, 1, 9, 0xFF87CEEB); + } + + ui_draw_rounded_rect_filled(win, button_x, button_y + 25, 70, 25, 6, COLOR_DARK_PANEL); + ui_draw_string(win, button_x + 18, button_y + 33, "Apply", COLOR_DARK_TEXT); + + // Wallpapers section + button_y += 60; + ui_draw_string(win, offset_x, button_y, "Wallpapers:", COLOR_DARK_TEXT); + button_y += 20; + + ui_draw_rounded_rect_filled(win, button_x, button_y, WALLPAPER_THUMB_W + 8, WALLPAPER_THUMB_H + 24, 6, COLOR_DARK_PANEL); + if (moon_thumb_valid) { + for (int ty = 0; ty < WALLPAPER_THUMB_H; ty++) { + for (int tx = 0; tx < WALLPAPER_THUMB_W; tx++) { + ui_draw_rect(win, button_x + 4 + tx, button_y + 4 + ty, 1, 1, moon_thumb[ty * WALLPAPER_THUMB_W + tx]); + } + } + } else { + ui_draw_string(win, button_x + 20, button_y + 30, "Error", 0xFFFF4444); + } + ui_draw_string(win, button_x + 30, button_y + WALLPAPER_THUMB_H + 8, "Moon", COLOR_DARK_TEXT); + + int thumb2_x = button_x + WALLPAPER_THUMB_W + 20; + ui_draw_rounded_rect_filled(win, thumb2_x, button_y, WALLPAPER_THUMB_W + 8, WALLPAPER_THUMB_H + 24, 6, COLOR_DARK_PANEL); + if (mtn_thumb_valid) { + for (int ty = 0; ty < WALLPAPER_THUMB_H; ty++) { + for (int tx = 0; tx < WALLPAPER_THUMB_W; tx++) { + ui_draw_rect(win, thumb2_x + 4 + tx, button_y + 4 + ty, 1, 1, mtn_thumb[ty * WALLPAPER_THUMB_W + tx]); + } + } + } else { + ui_draw_string(win, thumb2_x + 20, button_y + 30, "Error", 0xFFFF4444); + } + ui_draw_string(win, thumb2_x + 16, button_y + WALLPAPER_THUMB_H + 8, "Mountain", COLOR_DARK_TEXT); +} + +static void control_panel_paint_network(ui_window_t win) { + int offset_x = 8; + int offset_y = 21; + + ui_draw_rounded_rect_filled(win, offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); + + ui_draw_string(win, offset_x, offset_y + 40, "Network:", COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, offset_x, offset_y + 55, 140, 25, 6, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 30, offset_y + 63, "Init Network", COLOR_DARK_TEXT); + + if (net_status[0] != '\0') { + ui_draw_string(win, offset_x + 150, offset_y + 63, net_status, 0xFF90EE90); + } +} + +static void control_panel_paint_desktop(ui_window_t win) { + int offset_x = 8; + int offset_y = 21; + + ui_draw_rounded_rect_filled(win, offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); + ui_draw_string(win, offset_x, offset_y + 40, "Desktop Settings:", COLOR_DARK_TEXT); + + int section_y = offset_y + 65; + + ui_draw_rounded_rect_filled(win, offset_x, section_y, 16, 16, 3, COLOR_DARK_PANEL); + if (desktop_snap_to_grid) ui_draw_string(win, offset_x + 3, section_y + 1, "v", 0xFF90EE90); + ui_draw_string(win, offset_x + 25, section_y + 3, "Snap to Grid", COLOR_DARK_TEXT); + + section_y += 25; + ui_draw_rounded_rect_filled(win, offset_x, section_y, 16, 16, 3, COLOR_DARK_PANEL); + if (desktop_auto_align) ui_draw_string(win, offset_x + 3, section_y + 1, "v", 0xFF90EE90); + ui_draw_string(win, offset_x + 25, section_y + 3, "Auto Align Icons", COLOR_DARK_TEXT); + + section_y += 30; + ui_draw_string(win, offset_x, section_y + 3, "Apps per column:", COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, offset_x + 130, section_y, 20, 20, 4, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 135, section_y + 4, "-", COLOR_DARK_TEXT); + + char num[4]; + cli_itoa(desktop_max_rows_per_col, num); + ui_draw_string(win, offset_x + 160, section_y + 5, num, COLOR_DARK_TEXT); + + ui_draw_rounded_rect_filled(win, offset_x + 180, section_y, 20, 20, 4, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 186, section_y + 4, "+", COLOR_DARK_TEXT); + + section_y += 30; + ui_draw_string(win, offset_x, section_y + 3, "Columns:", COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, offset_x + 130, section_y, 20, 20, 4, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 135, section_y + 4, "-", COLOR_DARK_TEXT); + + char num_c[4]; + cli_itoa(desktop_max_cols, num_c); + ui_draw_string(win, offset_x + 160, section_y + 5, num_c, COLOR_DARK_TEXT); + + ui_draw_rounded_rect_filled(win, offset_x + 180, section_y, 20, 20, 4, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 186, section_y + 4, "+", COLOR_DARK_TEXT); +} + +static void control_panel_paint_mouse(ui_window_t win) { + int offset_x = 8; + int offset_y = 21; + + ui_draw_rounded_rect_filled(win, offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); + ui_draw_string(win, offset_x, offset_y + 40, "Mouse Settings:", COLOR_DARK_TEXT); + + int section_y = offset_y + 65; + ui_draw_string(win, offset_x, section_y, "Speed:", COLOR_DARK_TEXT); + + ui_draw_rounded_rect_filled(win, offset_x + 60, section_y + 8, 200, 8, 4, COLOR_DARK_PANEL); + + int knob_x = offset_x + 60 + (mouse_speed - 1) * 190 / 49; + ui_draw_rounded_rect_filled(win, knob_x, section_y + 2, 10, 14, 3, 0xFF4A90E2); + + ui_draw_string(win, offset_x + 270, section_y + 4, "x", COLOR_DARK_TEXT); + char speed_str[4]; + cli_itoa(mouse_speed, speed_str); + ui_draw_string(win, offset_x + 280, section_y + 4, speed_str, COLOR_DARK_TEXT); +} + +static void control_panel_paint(ui_window_t win) { + // Fill background + ui_draw_rect(win, 0, 20, 350, 480, COLOR_DARK_BG); + + if (current_view == VIEW_MAIN) { + control_panel_paint_main(win); + } else if (current_view == VIEW_WALLPAPER) { + control_panel_paint_wallpaper(win); + } else if (current_view == VIEW_NETWORK) { + control_panel_paint_network(win); + } else if (current_view == VIEW_DESKTOP) { + control_panel_paint_desktop(win); + } else if (current_view == VIEW_MOUSE) { + control_panel_paint_mouse(win); + } +} + +static void save_desktop_config(void) { + sys_system(4 /*SET_DESKTOP_PROP*/, 1, desktop_snap_to_grid, 0, 0); + sys_system(4, 2, desktop_auto_align, 0, 0); + sys_system(4, 3, desktop_max_rows_per_col, 0, 0); + sys_system(4, 4, desktop_max_cols, 0, 0); +} + +static void save_mouse_config(void) { + sys_system(5 /*SET_MOUSE_SPEED*/, mouse_speed, 0, 0, 0); +} + +static void fetch_kernel_state(void) { + desktop_snap_to_grid = sys_system(7 /*GET_DESKTOP_PROP*/, 1, 0, 0, 0); + desktop_auto_align = sys_system(7, 2, 0, 0, 0); + desktop_max_rows_per_col = sys_system(7, 3, 0, 0, 0); + desktop_max_cols = sys_system(7, 4, 0, 0, 0); + mouse_speed = sys_system(8 /*GET_MOUSE_SPEED*/, 0, 0, 0, 0); + + if (sys_system(9, 0, (uint64_t)moon_thumb, 0, 0) == 0) { + moon_thumb_valid = 1; + } + if (sys_system(9, 1, (uint64_t)mtn_thumb, 0, 0) == 0) { + mtn_thumb_valid = 1; + } +} + +static void control_panel_handle_click(int x, int y) { + int win_w = 350; + + if (current_view == VIEW_MAIN) { + int offset_x = 8; + int offset_y = 21; + int item_h = 60; + int item_spacing = 10; + + int item_y = offset_y + 15; + if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) { + current_view = VIEW_WALLPAPER; + focused_field = -1; + } + item_y += item_h + item_spacing; + if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) { + current_view = VIEW_NETWORK; + focused_field = -1; + } + item_y += item_h + item_spacing; + if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) { + current_view = VIEW_DESKTOP; + } + item_y += item_h + item_spacing; + if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) { + current_view = VIEW_MOUSE; + } + } else if (current_view == VIEW_WALLPAPER) { + int offset_x = 8; + int offset_y = 21; + int button_y = offset_y + 65; + int button_x = offset_x; + + if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { + current_view = VIEW_MAIN; + return; + } + + if (x >= button_x && x < button_x + 91 && y >= button_y && y < button_y + 25) { + sys_system(1, COLOR_COFFEE, 0, 0, 0); + return; + } + if (x >= button_x + 100 && x < button_x + 191 && y >= button_y && y < button_y + 25) { + sys_system(1, COLOR_TEAL, 0, 0, 0); + return; + } + if (x >= button_x + 200 && x < button_x + 291 && y >= button_y && y < button_y + 25) { + sys_system(1, COLOR_GREEN, 0, 0, 0); + return; + } + + button_y += 35; + if (x >= button_x && x < button_x + 91 && y >= button_y && y < button_y + 25) { + sys_system(1, COLOR_BLUE_BG, 0, 0, 0); + return; + } + if (x >= button_x + 100 && x < button_x + 191 && y >= button_y && y < button_y + 25) { + sys_system(1, COLOR_PURPLE, 0, 0, 0); + return; + } + if (x >= button_x + 200 && x < button_x + 291 && y >= button_y && y < button_y + 25) { + sys_system(1, COLOR_GREY, 0, 0, 0); + return; + } + + button_y += 60; // 40 + 20 + if (x >= button_x && x < button_x + 132 && y >= button_y && y < button_y + 25) { + sys_system(2, (uint64_t)pattern_lumberjack, 0, 0, 0); + return; + } + if (x >= button_x + 145 && x < button_x + 277 && y >= button_y && y < button_y + 25) { + sys_system(2, (uint64_t)pattern_blue_diamond, 0, 0, 0); + return; + } + + button_y += 60; + if (x >= button_x + 25 && x < button_x + 75 && y >= button_y && y < button_y + 18) { + if (focused_field != 0) rgb_r[0] = 0; + focused_field = 0; input_cursor = 0; return; + } + if (x >= button_x + 115 && x < button_x + 165 && y >= button_y && y < button_y + 18) { + if (focused_field != 1) rgb_g[0] = 0; + focused_field = 1; input_cursor = 0; return; + } + if (x >= button_x + 205 && x < button_x + 255 && y >= button_y && y < button_y + 18) { + if (focused_field != 2) rgb_b[0] = 0; + focused_field = 2; input_cursor = 0; return; + } + + if (x >= button_x && x < button_x + 70 && y >= button_y + 25 && y < button_y + 50) { + uint32_t cust = parse_rgb_separate(rgb_r, rgb_g, rgb_b); + sys_system(1, cust, 0, 0, 0); + return; + } + + button_y += 80; + if (x >= button_x && x < button_x + WALLPAPER_THUMB_W + 8 && y >= button_y && y < button_y + WALLPAPER_THUMB_H + 24) { + sys_system(3, 0, 0, 0, 0); + return; + } + int thumb2_x = button_x + WALLPAPER_THUMB_W + 20; + if (x >= thumb2_x && x < thumb2_x + WALLPAPER_THUMB_W + 8 && y >= button_y && y < button_y + WALLPAPER_THUMB_H + 24) { + sys_system(3, 1, 0, 0, 0); + return; + } + } else if (current_view == VIEW_NETWORK) { + int offset_x = 8; + int offset_y = 21; + + if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { + current_view = VIEW_MAIN; + focused_field = -1; + return; + } + + if (x >= offset_x && x < offset_x + 140 && y >= offset_y + 55 && y < offset_y + 80) { + if (sys_system(6, 0, 0, 0, 0) == 0) { + net_status[0] = 'I'; net_status[1] = 'n'; net_status[2] = 'i'; + net_status[3] = 't'; net_status[4] = 'e'; net_status[5] = 'd'; net_status[6] = 0; + } else { + net_status[0] = 'F'; net_status[1] = 'a'; net_status[2] = 'i'; + net_status[3] = 'l'; net_status[4] = 'e'; net_status[5] = 'd'; net_status[6] = 0; + } + } + } else if (current_view == VIEW_DESKTOP) { + int offset_x = 8; + int offset_y = 21; + + if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { + current_view = VIEW_MAIN; + return; + } + + int section_y = offset_y + 65; + if (x >= offset_x && x < offset_x + 16 && y >= section_y && y < section_y + 16) { + desktop_snap_to_grid = !desktop_snap_to_grid; + if (!desktop_snap_to_grid) desktop_auto_align = 0; + save_desktop_config(); + return; + } + + section_y += 25; + if (x >= offset_x && x < offset_x + 16 && y >= section_y && y < section_y + 16) { + desktop_auto_align = !desktop_auto_align; + if (desktop_auto_align) desktop_snap_to_grid = 1; + save_desktop_config(); + return; + } + + section_y += 25; + if (x >= offset_x + 130 && x < offset_x + 150 && y >= section_y && y < section_y + 20) { + if (desktop_max_rows_per_col > 1) { + desktop_max_rows_per_col--; + save_desktop_config(); + } + } + if (x >= offset_x + 180 && x < offset_x + 200 && y >= section_y && y < section_y + 20) { + if (desktop_max_rows_per_col < 15) desktop_max_rows_per_col++; + save_desktop_config(); + } + + section_y += 25; + if (x >= offset_x + 130 && x < offset_x + 150 && y >= section_y && y < section_y + 20) { + if (desktop_max_cols > 1) { + desktop_max_cols--; + save_desktop_config(); + } + } + if (x >= offset_x + 180 && x < offset_x + 200 && y >= section_y && y < section_y + 20) { + desktop_max_cols++; + save_desktop_config(); + } + } else if (current_view == VIEW_MOUSE) { + int offset_x = 8; + int offset_y = 21; + + if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { + current_view = VIEW_MAIN; + return; + } + + int section_y = offset_y + 65; + if (x >= offset_x + 60 && x <= offset_x + 260 && y >= section_y && y <= section_y + 20) { + int new_speed = 1 + (x - (offset_x + 60)) * 49 / 200; + if (new_speed < 1) new_speed = 1; + if (new_speed > 50) new_speed = 50; + mouse_speed = new_speed; + save_mouse_config(); + return; + } + } +} + +static void control_panel_handle_key(char c) { + if (focused_field < 0) return; + + if (current_view == VIEW_WALLPAPER) { + char *focused_buffer = NULL; + int max_len = 3; + + if (focused_field == 0) focused_buffer = rgb_r; + else if (focused_field == 1) focused_buffer = rgb_g; + else if (focused_field == 2) focused_buffer = rgb_b; + else return; + + if (c == '\b') { + if (input_cursor > 0) { + input_cursor--; + focused_buffer[input_cursor] = '\0'; + } + } else if (c >= '0' && c <= '9') { + if (input_cursor < max_len) { + focused_buffer[input_cursor] = c; + input_cursor++; + focused_buffer[input_cursor] = '\0'; + } + } else if (c == '\t') { + focused_field = (focused_field + 1) % 3; + input_cursor = 0; + } + } +} + +int main(int argc, char **argv) { + (void)argc; + (void)argv; + ui_window_t win = ui_window_create("Settings", 200, 150, 350, 500); + if (!win) return 1; + + generate_lumberjack_pattern(); + generate_blue_diamond_pattern(); + + fetch_kernel_state(); + + gui_event_t ev; + while (1) { + if (ui_get_event(win, &ev)) { + if (ev.type == GUI_EVENT_PAINT) { + control_panel_paint(win); + ui_mark_dirty(win, 0, 0, 350, 500); + } else if (ev.type == GUI_EVENT_CLICK) { + control_panel_handle_click(ev.arg1, ev.arg2); + control_panel_paint(win); + ui_mark_dirty(win, 0, 0, 350, 500); + } else if (ev.type == GUI_EVENT_KEY) { + control_panel_handle_key((char)ev.arg1); + control_panel_paint(win); + ui_mark_dirty(win, 0, 0, 350, 500); + } else if (ev.type == GUI_EVENT_CLOSE) { + sys_exit(0); + } + } + } + return 0; +} diff --git a/src/kernel/userland/settings.elf b/src/kernel/userland/settings.elf new file mode 100755 index 0000000..4858796 Binary files /dev/null and b/src/kernel/userland/settings.elf differ diff --git a/src/kernel/editor.c b/src/kernel/userland/txtedit.c similarity index 58% rename from src/kernel/editor.c rename to src/kernel/userland/txtedit.c index 86faf0f..db1b811 100644 --- a/src/kernel/editor.c +++ b/src/kernel/userland/txtedit.c @@ -1,11 +1,14 @@ -#include "editor.h" -#include "graphics.h" -#include "fat32.h" -#include "wm.h" -#include +#include "libc/syscall.h" +#include "libc/libui.h" #include -Window win_editor; +#define COLOR_DARK_PANEL 0xFF202020 +#define COLOR_DARK_TEXT 0xFFE0E0E0 +#define COLOR_DARK_BORDER 0xFF404040 +#define COLOR_RED 0xFFFF4444 +#define COLOR_DARK_BG 0xFF121212 +#define COLOR_DKGRAY 0xFF808080 +#define COLOR_WHITE 0xFFFFFFFF #define EDITOR_MAX_LINES 128 #define EDITOR_MAX_LINE_LEN 256 @@ -23,37 +26,18 @@ static int cursor_line = 0; static int cursor_col = 0; static int scroll_top = 0; static char open_filename[256] = ""; -static bool file_modified = false; +static _Bool file_modified = 0; -// === Helper Functions === - -static size_t editor_strlen(const char *str) { - size_t len = 0; - while (str[len]) len++; - return len; -} +static int win_w = 700; +static int win_h = 450; static void editor_strcpy(char *dest, const char *src) { while (*src) *dest++ = *src++; *dest = 0; } -static int editor_strcmp(const char *s1, const char *s2) { - while (*s1 && (*s1 == *s2)) { - s1++; - s2++; - } - return *(const unsigned char*)s1 - *(const unsigned char*)s2; -} - -// === Editor Logic === - -// Forward declaration -static void editor_ensure_cursor_visible(void); - static void editor_clear_all(void) { for (int i = 0; i < EDITOR_MAX_LINES; i++) { - // Zero out entire buffer to prevent ghost text from previous file for (int j = 0; j < EDITOR_MAX_LINE_LEN; j++) { lines[i].content[j] = 0; } @@ -64,37 +48,43 @@ static void editor_clear_all(void) { cursor_col = 0; scroll_top = 0; open_filename[0] = 0; - file_modified = false; + file_modified = 0; +} + +static void editor_ensure_cursor_visible(void) { + int visible_lines = 22; + if (cursor_line < scroll_top) { + scroll_top = cursor_line; + } + if (cursor_line >= scroll_top + visible_lines) { + scroll_top = cursor_line - visible_lines + 1; + } } void editor_open_file(const char *filename) { editor_clear_all(); editor_strcpy(open_filename, filename); - FAT32_FileHandle *fh = fat32_open(filename, "r"); - if (!fh) { - // New file - file_modified = false; + int fd = sys_open(filename, "r"); + if (fd < 0) { + file_modified = 0; return; } - // Read file content - char buffer[16384]; - int bytes_read = fat32_read(fh, buffer, sizeof(buffer)); - fat32_close(fh); + static char buffer[16384]; + int bytes_read = sys_read(fd, buffer, sizeof(buffer)); + sys_close(fd); if (bytes_read <= 0) { - file_modified = false; + file_modified = 0; return; } - // Parse into lines int line = 0; int col = 0; for (int i = 0; i < bytes_read && line < EDITOR_MAX_LINES; i++) { char ch = buffer[i]; - if (ch == '\n') { lines[line].content[col] = 0; lines[line].length = col; @@ -115,71 +105,54 @@ void editor_open_file(const char *filename) { } line_count = (line > 0) ? line : 1; - file_modified = false; + file_modified = 0; } static void editor_save_file(void) { - if (!open_filename[0]) { - // No filename set - return; - } + if (!open_filename[0]) return; - FAT32_FileHandle *fh = fat32_open(open_filename, "w"); - if (!fh) { - return; - } + int fd = sys_open(open_filename, "w"); + if (fd < 0) return; - // Write lines for (int i = 0; i < line_count; i++) { - fat32_write(fh, lines[i].content, lines[i].length); - fat32_write(fh, "\n", 1); + sys_write_fs(fd, lines[i].content, lines[i].length); + sys_write_fs(fd, "\n", 1); } - - fat32_close(fh); - file_modified = false; + sys_close(fd); + file_modified = 0; } -// Insert character at cursor position static void editor_insert_char(char ch) { if (cursor_line >= EDITOR_MAX_LINES) return; - EditorLine *line = &lines[cursor_line]; if (ch == '\n') { - // Split line - shift all lines below down first if (line_count >= EDITOR_MAX_LINES) return; - - // Shift all lines from cursor_line+1 onwards down by one position for (int j = line_count; j > cursor_line; j--) { lines[j] = lines[j - 1]; } line_count++; - // Clear the new line completely (zero entire buffer) for (int k = 0; k < EDITOR_MAX_LINE_LEN; k++) { lines[cursor_line + 1].content[k] = 0; } lines[cursor_line + 1].length = 0; - // Now split the current line at cursor position int current_len = lines[cursor_line].length; int new_len = current_len - cursor_col; - // Copy the second part to the new line for (int i = 0; i < new_len; i++) { lines[cursor_line + 1].content[i] = lines[cursor_line].content[cursor_col + i]; } lines[cursor_line + 1].content[new_len] = 0; lines[cursor_line + 1].length = new_len; - // Truncate current line lines[cursor_line].content[cursor_col] = 0; lines[cursor_line].length = cursor_col; cursor_line++; cursor_col = 0; } else if (ch == '\b') { - // Backspace if (cursor_col > 0) { for (int i = cursor_col - 1; i < line->length; i++) { line->content[i] = line->content[i + 1]; @@ -187,7 +160,6 @@ static void editor_insert_char(char ch) { line->length--; cursor_col--; } else if (cursor_line > 0) { - // Merge with previous line EditorLine *prev = &lines[cursor_line - 1]; int merge_point = prev->length; @@ -199,9 +171,8 @@ static void editor_insert_char(char ch) { prev->content[merge_point + i] = 0; prev->length = merge_point + i; - // Shift lines up - for (int i = cursor_line; i < line_count - 1; i++) { - lines[i] = lines[i + 1]; + for (int j = cursor_line; j < line_count - 1; j++) { + lines[j] = lines[j + 1]; } lines[line_count - 1].length = 0; lines[line_count - 1].content[0] = 0; @@ -211,9 +182,7 @@ static void editor_insert_char(char ch) { line_count--; } } else if (ch >= 32 && ch <= 126) { - // Regular character if (cursor_col < EDITOR_MAX_LINE_LEN - 1) { - // Shift characters right for (int i = line->length; i > cursor_col; i--) { line->content[i] = line->content[i - 1]; } @@ -222,60 +191,39 @@ static void editor_insert_char(char ch) { cursor_col++; } } - - file_modified = true; + file_modified = 1; editor_ensure_cursor_visible(); } -// Ensure cursor is visible by adjusting scroll position -static void editor_ensure_cursor_visible(void) { - int visible_lines = 22; // Allow ~24 lines to use available window space +static void editor_paint(ui_window_t win) { + int offset_x = 4; + int offset_y = 24; + int content_width = win_w - 8; + int content_height = win_h - 28; - // Scroll up if cursor is above visible area - if (cursor_line < scroll_top) { - scroll_top = cursor_line; - } + // Top content bar + ui_draw_rounded_rect_filled(win, offset_x, offset_y, content_width, 25, 6, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 10, offset_y + 6, "File", COLOR_DARK_TEXT); + ui_draw_string(win, offset_x + 55, offset_y + 6, open_filename, COLOR_DARK_TEXT); - // Scroll down if cursor is below visible area - if (cursor_line >= scroll_top + visible_lines) { - scroll_top = cursor_line - visible_lines + 1; - } -} - -// === Paint Function === - -static void editor_paint(Window *win) { - int offset_x = win->x + 4; - int offset_y = win->y + 24; - int content_width = win->w - 8; - int content_height = win->h - 28; - - // Top content bar (modern dark, rounded) - draw_rounded_rect_filled(offset_x, offset_y, content_width, 25, 6, COLOR_DARK_PANEL); - draw_string(offset_x + 10, offset_y + 6, "File", COLOR_DARK_TEXT); - draw_string(offset_x + 55, offset_y + 6, open_filename, COLOR_DARK_TEXT); - - // Save button (modern rounded) + // Save button int save_btn_x = offset_x + content_width - 80; int save_btn_y = offset_y + 3; - draw_rounded_rect_filled(save_btn_x, save_btn_y, 70, 20, 6, COLOR_DARK_BORDER); - draw_string(save_btn_x + 20, save_btn_y + 6, "Save", COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, save_btn_x, save_btn_y, 70, 20, 6, COLOR_DARK_BORDER); + ui_draw_string(win, save_btn_x + 20, save_btn_y + 6, "Save", COLOR_DARK_TEXT); - // Draw modification indicator if (file_modified) { - draw_string(offset_x + content_width - 200, offset_y + 5, "[Modified]", COLOR_RED); + ui_draw_string(win, offset_x + content_width - 200, offset_y + 5, "[Modified]", COLOR_RED); } - // Fill editor background - dark mode - draw_rect(win->x + 4, win->y + 54, win->w - 8, win->h - 58, COLOR_DARK_BG); + // Editor background + ui_draw_rect(win, 4, 54, win_w - 8, win_h - 58, COLOR_DARK_BG); - // Calculate available width for text (accounting for line numbers) int text_start_x = offset_x + 40; int available_width = content_width - 40; int max_chars_per_line = available_width / EDITOR_CHAR_WIDTH; if (max_chars_per_line < 1) max_chars_per_line = 1; - // Draw line numbers and content with word wrapping int display_line = 0; int visible_lines = (content_height - 55) / EDITOR_LINE_HEIGHT; int max_display_lines = visible_lines; @@ -284,9 +232,7 @@ static void editor_paint(Window *win) { while (line_idx < line_count && display_line < max_display_lines) { int display_y = offset_y + 35 + display_line * EDITOR_LINE_HEIGHT; - // Only draw line number for first wrapped line of this editor line if (display_line == 0 || line_idx < line_count) { - // Draw line number char line_num_str[16]; int temp = line_idx + 1; int str_len = 0; @@ -298,7 +244,6 @@ static void editor_paint(Window *win) { line_num_str[str_len++] = (temp % 10) + '0'; temp /= 10; } - // Reverse for (int j = 0; j < str_len / 2; j++) { char t = line_num_str[j]; line_num_str[j] = line_num_str[str_len - 1 - j]; @@ -306,21 +251,19 @@ static void editor_paint(Window *win) { } } line_num_str[str_len] = 0; - draw_string(offset_x + 4, display_y, line_num_str, COLOR_DKGRAY); + ui_draw_string(win, offset_x + 4, display_y, line_num_str, COLOR_DKGRAY); } - // Word-based text wrapping for this line const char *text = lines[line_idx].content; int text_len = lines[line_idx].length; int char_idx = 0; int local_display_line = 0; - bool first_pass = true; + _Bool first_pass = 1; while ((char_idx < text_len || (text_len == 0 && first_pass)) && display_line < max_display_lines) { - first_pass = false; + first_pass = 0; int current_display_y = offset_y + 35 + display_line * EDITOR_LINE_HEIGHT; - // Extract segment (up to max_chars_per_line) char segment[256]; int segment_len = 0; int segment_start = char_idx; @@ -330,7 +273,6 @@ static void editor_paint(Window *win) { } segment[segment_len] = 0; - // Word-based wrapping: find last space if we didn't reach end if (char_idx < text_len && segment_len > 0) { int last_space = -1; for (int i = segment_len - 1; i >= 0; i--) { @@ -339,51 +281,44 @@ static void editor_paint(Window *win) { break; } } - if (last_space > 0) { segment_len = last_space; segment[segment_len] = 0; char_idx = segment_start + last_space + 1; - // Skip additional spaces while (char_idx < text_len && text[char_idx] == ' ') { char_idx++; } } } - // Draw the text segment if (segment_len > 0) { - draw_string(text_start_x, current_display_y, segment, COLOR_DARK_TEXT); + ui_draw_string(win, text_start_x, current_display_y, segment, COLOR_DARK_TEXT); } - // Draw cursor if on this line and wrapped segment if (line_idx == cursor_line) { int segment_end = segment_start + segment_len; - bool draw_cursor = false; + _Bool draw_cursor = 0; if (cursor_col >= segment_start && cursor_col < segment_end) { - draw_cursor = true; + draw_cursor = 1; } else if (cursor_col == text_len && segment_end == text_len) { - draw_cursor = true; + draw_cursor = 1; } if (draw_cursor) { int cursor_x = text_start_x + ((cursor_col - segment_start) * EDITOR_CHAR_WIDTH); - draw_rect(cursor_x, current_display_y, 2, 10, COLOR_WHITE); + ui_draw_rect(win, cursor_x, current_display_y, 2, 10, COLOR_WHITE); } } display_line++; local_display_line++; - if (char_idx >= text_len) break; } - line_idx++; } - // Status bar at bottom (modern dark, rounded) int status_y = offset_y + content_height - 20; - draw_rounded_rect_filled(offset_x, status_y, content_width, 20, 6, COLOR_DARK_PANEL); - draw_string(offset_x + 10, status_y + 5, "Line: ", COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, offset_x, status_y, content_width, 20, 6, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 10, status_y + 5, "Line: ", COLOR_DARK_TEXT); char line_str[32]; int temp = cursor_line + 1; @@ -399,8 +334,8 @@ static void editor_paint(Window *win) { } line_str[idx] = 0; - draw_string(offset_x + 60, status_y + 5, line_str, COLOR_DARK_TEXT); - draw_string(offset_x + 100, status_y + 5, " Col: ", COLOR_DARK_TEXT); + ui_draw_string(win, offset_x + 60, status_y + 5, line_str, COLOR_DARK_TEXT); + ui_draw_string(win, offset_x + 100, status_y + 5, " Col: ", COLOR_DARK_TEXT); char col_str[32]; temp = cursor_col + 1; @@ -416,100 +351,81 @@ static void editor_paint(Window *win) { } col_str[idx] = 0; - draw_string(offset_x + 170, status_y + 5, col_str, COLOR_DARK_TEXT); + ui_draw_string(win, offset_x + 170, status_y + 5, col_str, COLOR_DARK_TEXT); } -// === Key Handler === - -static void editor_handle_key(Window *win, char c) { - // Arrow keys - UP - if (c == 17) { +static void editor_handle_key(char c) { + if (c == 17) { // UP if (cursor_line > 0) { cursor_line--; - if (cursor_col > (int)lines[cursor_line].length) { - cursor_col = lines[cursor_line].length; - } - if (cursor_line < scroll_top) { - scroll_top = cursor_line; - } + if (cursor_col > lines[cursor_line].length) cursor_col = lines[cursor_line].length; + if (cursor_line < scroll_top) scroll_top = cursor_line; } - return; - } - - // Arrow keys - DOWN - if (c == 18) { + } else if (c == 18) { // DOWN if (cursor_line < line_count - 1) { cursor_line++; - if (cursor_col > (int)lines[cursor_line].length) { - cursor_col = lines[cursor_line].length; - } + if (cursor_col > lines[cursor_line].length) cursor_col = lines[cursor_line].length; int visible_lines = 20; - if (cursor_line >= scroll_top + visible_lines) { - scroll_top = cursor_line - visible_lines + 1; - } + if (cursor_line >= scroll_top + visible_lines) scroll_top = cursor_line - visible_lines + 1; } - return; - } - - // Arrow keys - LEFT - if (c == 19) { + } else if (c == 19) { // LEFT if (cursor_col > 0) { cursor_col--; } else if (cursor_line > 0) { cursor_line--; cursor_col = lines[cursor_line].length; } - return; - } - - // Arrow keys - RIGHT - if (c == 20) { - if (cursor_col < (int)lines[cursor_line].length) { + } else if (c == 20) { // RIGHT + if (cursor_col < lines[cursor_line].length) { cursor_col++; } else if (cursor_line < line_count - 1) { cursor_line++; cursor_col = 0; } - return; + } else { + editor_insert_char(c); } - - // Regular character input - editor_insert_char(c); } -// === Click Handler === - -static void editor_handle_click(Window *win, int x, int y) { - // x and y are relative to window origin - int content_width = win->w - 8; - - // Check save button - position is at (4 + content_width - 80, 24 + 3) = (4 + w - 8 - 80, 27) - // Button dimensions: 70 wide, 20 tall +static void editor_handle_click(int x, int y) { + int content_width = win_w - 8; int button_x = 4 + content_width - 80; int button_y = 24 + 3; - if (x >= button_x && x < button_x + 70 && - y >= button_y && y < button_y + 20) { + if (x >= button_x && x < button_x + 70 && y >= button_y && y < button_y + 20) { editor_save_file(); - return; } } -// === Initialization === +int main(int argc, char **argv) { + ui_window_t win = ui_window_create("Text Editor", 100, 150, win_w, win_h); + if (!win) return 1; -void editor_init(void) { - win_editor.title = "Text Editor"; - win_editor.x = 100; - win_editor.y = 150; - win_editor.w = 700; - win_editor.h = 450; - win_editor.visible = false; - win_editor.focused = false; - win_editor.z_index = 0; - win_editor.paint = editor_paint; - win_editor.handle_key = editor_handle_key; - win_editor.handle_click = editor_handle_click; - win_editor.handle_right_click = NULL; - editor_clear_all(); + if (argc > 1) { + editor_open_file(argv[1]); + } else { + editor_strcpy(open_filename, "untitled.txt"); + } + + gui_event_t ev; + while (1) { + if (ui_get_event(win, &ev)) { + if (ev.type == GUI_EVENT_PAINT) { + editor_paint(win); + ui_mark_dirty(win, 0, 0, win_w, win_h); + } else if (ev.type == GUI_EVENT_CLICK) { + editor_handle_click(ev.arg1, ev.arg2); + editor_paint(win); + ui_mark_dirty(win, 0, 0, win_w, win_h); + } else if (ev.type == GUI_EVENT_KEY) { + editor_handle_key((char)ev.arg1); + editor_paint(win); + ui_mark_dirty(win, 0, 0, win_w, win_h); + } else if (ev.type == GUI_EVENT_CLOSE) { + sys_exit(0); + } + } + } + return 0; } diff --git a/src/kernel/userland/txtedit.elf b/src/kernel/userland/txtedit.elf new file mode 100755 index 0000000..f4ca530 Binary files /dev/null and b/src/kernel/userland/txtedit.elf differ diff --git a/src/kernel/userland/viewer.c b/src/kernel/userland/viewer.c new file mode 100644 index 0000000..a64d1cf --- /dev/null +++ b/src/kernel/userland/viewer.c @@ -0,0 +1,226 @@ +#include "nanojpeg.h" +#include "libc/syscall.h" +#include "libc/libui.h" +#include "libc/stdlib.h" +#include +#include +#include + +#define VIEWER_MAX_W 800 +#define VIEWER_MAX_H 600 + +static uint32_t *viewer_pixels = NULL; +static int viewer_img_w = 0; +static int viewer_img_h = 0; +static char viewer_title[64] = "Viewer"; +static bool viewer_has_image = false; +static char viewer_file_path[256]; + +static int win_w = 500; +static int win_h = 400; + +static void viewer_strcpy(char *dst, const char *src) { + while (*src) *dst++ = *src++; + *dst = 0; +} + +static int viewer_strlen(const char *s) { + int len = 0; + while (s[len]) len++; + return len; +} + +static void viewer_scale_rgb_to_argb(const unsigned char *rgb, int src_w, int src_h, + uint32_t *dst, int dst_w, int dst_h) { + for (int y = 0; y < dst_h; y++) { + int src_y = y * src_h / dst_h; + if (src_y >= src_h) src_y = src_h - 1; + for (int x = 0; x < dst_w; x++) { + int src_x = x * src_w / dst_w; + if (src_x >= src_w) src_x = src_w - 1; + int idx = (src_y * src_w + src_x) * 3; + unsigned char r = rgb[idx]; + unsigned char g = rgb[idx + 1]; + unsigned char b = rgb[idx + 2]; + dst[y * dst_w + x] = 0xFF000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; + } + } +} + +static void viewer_paint(ui_window_t win) { + int cx = 4; + int cy = 24; + int cw = win_w - 8; + int ch = win_h - 28; + + ui_draw_rect(win, cx, cy, cw, ch, 0xFF1A1A1A); + + if (!viewer_has_image) { + ui_draw_string(win, cx + 20, cy + ch / 2, "No image loaded", 0xFF888888); + return; + } + + int disp_w = viewer_img_w; + int disp_h = viewer_img_h; + + if (disp_w > cw - 8) { + disp_h = disp_h * (cw - 8) / disp_w; + disp_w = cw - 8; + } + if (disp_h > ch - 40) { + disp_w = disp_w * (ch - 40) / disp_h; + disp_h = ch - 40; + } + + int ox = cx + (cw - disp_w) / 2; + int oy = cy + (ch - disp_h - 30) / 2; + + for (int y = 0; y < disp_h; y++) { + int src_y = y * viewer_img_h / disp_h; + if (src_y >= viewer_img_h) src_y = viewer_img_h - 1; + for (int x = 0; x < disp_w; x++) { + int src_x = x * viewer_img_w / disp_w; + if (src_x >= viewer_img_w) src_x = viewer_img_w - 1; + uint32_t pixel = viewer_pixels[src_y * viewer_img_w + src_x]; + ui_draw_rect(win, ox + x, oy + y, 1, 1, pixel); + } + } + + int btn_w = 160; + int btn_h = 22; + int btn_x = cx + (cw - btn_w) / 2; + int btn_y = win_h - 30; + ui_draw_rounded_rect_filled(win, btn_x, btn_y, btn_w, btn_h, 6, 0xFF2D2D2D); + ui_draw_string(win, btn_x + 10, btn_y + 6, "Set as Wallpaper", 0xFFF0F0F0); +} + +static void viewer_handle_click(ui_window_t win, int x, int y) { + if (!viewer_has_image) return; + + int cx = 4; + int cw = win_w - 8; + int btn_w = 160; + int btn_x = cx + (cw - btn_w) / 2; + int btn_y = win_h - 30; + + if (x >= btn_x && x < btn_x + btn_w && y >= btn_y && y < btn_y + 22) { + // SYSTEM_CMD_SET_WALLPAPER is 3 based on syscall.c code + sys_system(3, (uint64_t)viewer_file_path, 0, 0, 0); + } +} + +void viewer_open_file(const char *path) { + int fd = sys_open(path, "r"); + if (fd < 0) return; + + // We can't use stat yet, so read chunks until EOF + // Alternatively, use a large buffer if sys_read handles large chunks. + int alloc_size = 2 * 1024 * 1024; + unsigned char *buf = malloc(alloc_size); + if (!buf) { + sys_close(fd); + return; + } + + int total_read = 0; + while (total_read < alloc_size) { + int chunk = sys_read(fd, (char*)buf + total_read, alloc_size - total_read); + if (chunk <= 0) break; + total_read += chunk; + } + sys_close(fd); + + if (total_read <= 0) { + free(buf); + return; + } + + njInit(); + nj_result_t result = njDecode(buf, total_read); + if (result != NJ_OK) { + njDone(); + free(buf); + return; + } + + int img_w = njGetWidth(); + int img_h = njGetHeight(); + unsigned char *rgb = njGetImage(); + + if (!rgb || img_w <= 0 || img_h <= 0) { + njDone(); + free(buf); + return; + } + + int fit_w = img_w; + int fit_h = img_h; + if (fit_w > VIEWER_MAX_W) { + fit_h = fit_h * VIEWER_MAX_W / fit_w; + fit_w = VIEWER_MAX_W; + } + if (fit_h > VIEWER_MAX_H) { + fit_w = fit_w * VIEWER_MAX_H / fit_h; + fit_h = VIEWER_MAX_H; + } + + viewer_pixels = malloc(fit_w * fit_h * sizeof(uint32_t)); + if (viewer_pixels) { + viewer_scale_rgb_to_argb(rgb, img_w, img_h, viewer_pixels, fit_w, fit_h); + viewer_img_w = fit_w; + viewer_img_h = fit_h; + viewer_has_image = true; + } + + njDone(); + free(buf); + + viewer_strcpy(viewer_file_path, path); + + const char *fname = path; + int plen = viewer_strlen(path); + for (int i = plen - 1; i >= 0; i--) { + if (path[i] == '/') { + fname = &path[i + 1]; + break; + } + } + + viewer_title[0] = 'V'; viewer_title[1] = 'i'; viewer_title[2] = 'e'; + viewer_title[3] = 'w'; viewer_title[4] = 'e'; viewer_title[5] = 'r'; + viewer_title[6] = ' '; viewer_title[7] = '-'; viewer_title[8] = ' '; + int ti = 9; + for (int i = 0; fname[i] && ti < 60; i++) { + viewer_title[ti++] = fname[i]; + } + viewer_title[ti] = 0; + + win_w = fit_w + 16; + if (win_w < 200) win_w = 200; + win_h = fit_h + 64; + if (win_h < 100) win_h = 100; +} + +int main(int argc, char **argv) { + if (argc > 1) { + viewer_open_file(argv[1]); + } + + ui_window_t win = ui_window_create(viewer_title, 100, 50, win_w, win_h); + if (!win) return 1; + + gui_event_t ev; + while (1) { + if (ui_get_event(win, &ev)) { + if (ev.type == GUI_EVENT_PAINT) { + viewer_paint(win); + ui_mark_dirty(win, 0, 0, win_w, win_h); + } else if (ev.type == GUI_EVENT_CLICK) { + viewer_handle_click(win, ev.arg1, ev.arg2); + } else if (ev.type == GUI_EVENT_CLOSE) { + sys_exit(0); + } + } + } + return 0; +} diff --git a/src/kernel/userland/viewer.elf b/src/kernel/userland/viewer.elf new file mode 100755 index 0000000..437573c Binary files /dev/null and b/src/kernel/userland/viewer.elf differ diff --git a/src/kernel/userland/viewer.o b/src/kernel/userland/viewer.o new file mode 100644 index 0000000..18e69a1 Binary files /dev/null and b/src/kernel/userland/viewer.o differ diff --git a/src/kernel/viewer.c b/src/kernel/viewer.c deleted file mode 100644 index 3c8eec8..0000000 --- a/src/kernel/viewer.c +++ /dev/null @@ -1,332 +0,0 @@ -// viewer.c - Image Viewer app for BoredOS -// Opens .jpg files and displays the decoded image in a window - -#include "viewer.h" -#include "nanojpeg.h" -#include "graphics.h" -#include "fat32.h" -#include "memory_manager.h" -#include "wallpaper.h" -#include "io.h" -#include - -Window win_viewer; - -// Viewer state -#define VIEWER_MAX_W 800 -#define VIEWER_MAX_H 600 -static uint32_t viewer_pixels[VIEWER_MAX_W * VIEWER_MAX_H]; -static int viewer_img_w = 0; -static int viewer_img_h = 0; -static char viewer_title[64] = "Viewer"; -static bool viewer_has_image = false; - -// Deferred open: click handler stores path, main loop decodes -static char viewer_pending_path[256]; -static volatile bool viewer_open_pending = false; - -// Store the file path for "Set as Wallpaper" -static char viewer_file_path[256]; - -// String helpers -static int viewer_strlen(const char *s) { - int len = 0; - while (s[len]) len++; - return len; -} - -static void viewer_strcpy(char *dst, const char *src) { - while (*src) *dst++ = *src++; - *dst = 0; -} - -// Simple nearest-neighbor scale from decoded RGB to ARGB pixel buffer -static void viewer_scale_rgb_to_argb(const unsigned char *rgb, int src_w, int src_h, - uint32_t *dst, int dst_w, int dst_h) { - for (int y = 0; y < dst_h; y++) { - int src_y = y * src_h / dst_h; - if (src_y >= src_h) src_y = src_h - 1; - for (int x = 0; x < dst_w; x++) { - int src_x = x * src_w / dst_w; - if (src_x >= src_w) src_x = src_w - 1; - int idx = (src_y * src_w + src_x) * 3; - unsigned char r = rgb[idx]; - unsigned char g = rgb[idx + 1]; - unsigned char b = rgb[idx + 2]; - dst[y * dst_w + x] = 0xFF000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; - } - } -} - -static void viewer_paint(Window *win) { - int cx = win->x + 4; - int cy = win->y + 24; - int cw = win->w - 8; - int ch = win->h - 28; - - draw_rect(cx, cy, cw, ch, 0xFF1A1A1A); - - if (!viewer_has_image) { - draw_string(cx + 20, cy + ch / 2, "No image loaded", 0xFF888888); - return; - } - - // Calculate display size (fit within window, keep aspect ratio) - int disp_w = viewer_img_w; - int disp_h = viewer_img_h; - - if (disp_w > cw - 8) { - disp_h = disp_h * (cw - 8) / disp_w; - disp_w = cw - 8; - } - if (disp_h > ch - 40) { - disp_w = disp_w * (ch - 40) / disp_h; - disp_h = ch - 40; - } - - // Center in window - int ox = cx + (cw - disp_w) / 2; - int oy = cy + (ch - disp_h - 30) / 2; - - // Draw the image pixel by pixel (nearest-neighbor from stored buffer) - for (int y = 0; y < disp_h; y++) { - int src_y = y * viewer_img_h / disp_h; - if (src_y >= viewer_img_h) src_y = viewer_img_h - 1; - for (int x = 0; x < disp_w; x++) { - int src_x = x * viewer_img_w / disp_w; - if (src_x >= viewer_img_w) src_x = viewer_img_w - 1; - uint32_t pixel = viewer_pixels[src_y * viewer_img_w + src_x]; - put_pixel(ox + x, oy + y, pixel); - } - } - - // Draw "Set as Wallpaper" button at the bottom - int btn_w = 160; - int btn_h = 22; - int btn_x = cx + (cw - btn_w) / 2; - int btn_y = win->y + win->h - 30; - draw_rounded_rect_filled(btn_x, btn_y, btn_w, btn_h, 6, 0xFF2D2D2D); - draw_string(btn_x + 10, btn_y + 6, "Set as Wallpaper", 0xFFF0F0F0); -} - -static void viewer_handle_click(Window *win, int x, int y) { - if (!viewer_has_image) return; - - int cx = 4; - int cw = win->w - 8; - - // Check "Set as Wallpaper" button - int btn_w = 160; - int btn_x = cx + (cw - btn_w) / 2; - int btn_y = win->h - 30; - if (x >= btn_x && x < btn_x + btn_w && y >= btn_y && y < btn_y + 22) { - // Queue wallpaper change from file (deferred to main loop) - wallpaper_request_set_from_file(viewer_file_path); - } -} - -static void viewer_handle_key(Window *win, char c) { - (void)win; - (void)c; -} - -// Simple serial output for debugging -static void v_serial_char(char c) { - while (!(inb(0x3F8 + 5) & 0x20)); - outb(0x3F8, c); -} -static void v_serial_str(const char *s) { while (*s) v_serial_char(*s++); } -static void v_serial_num(int n) { - if (n < 0) { v_serial_char('-'); n = -n; } - if (n >= 10) v_serial_num(n / 10); - v_serial_char('0' + (n % 10)); -} - -// Called from interrupt context - just queue the path for later processing -void viewer_open_file(const char *path) { - v_serial_str("[VIEWER] open_file queued: "); - v_serial_str(path); - v_serial_str("\n"); - viewer_strcpy(viewer_pending_path, path); - viewer_open_pending = true; -} - -// Process deferred viewer open (called from main loop, NOT interrupt context) -void viewer_process_pending(void) { - if (!viewer_open_pending) return; - viewer_open_pending = false; - - const char *path = viewer_pending_path; - v_serial_str("[VIEWER] process_pending: "); - v_serial_str(path); - v_serial_str("\n"); - - FAT32_FileHandle *fh = fat32_open(path, "r"); - if (!fh) { - v_serial_str("[VIEWER] fat32_open FAILED\n"); - return; - } - - uint32_t file_size = fh->size; - v_serial_str("[VIEWER] file_size="); - v_serial_num((int)file_size); - v_serial_str("\n"); - - if (file_size == 0 || file_size > 2 * 1024 * 1024) { - v_serial_str("[VIEWER] file too big or empty\n"); - fat32_close(fh); - return; - } - - unsigned char *buf = (unsigned char*)kmalloc(file_size); - if (!buf) { - v_serial_str("[VIEWER] kmalloc FAILED\n"); - fat32_close(fh); - return; - } - - int total_read = 0; - while (total_read < (int)file_size) { - int chunk = fat32_read(fh, buf + total_read, (int)file_size - total_read); - if (chunk <= 0) break; - total_read += chunk; - } - fat32_close(fh); - - v_serial_str("[VIEWER] read "); - v_serial_num(total_read); - v_serial_str(" bytes\n"); - - if (total_read <= 0) { - kfree(buf); - return; - } - - // Decode JPEG (now running in main loop, safe context) - njInit(); - nj_result_t result = njDecode(buf, total_read); - v_serial_str("[VIEWER] njDecode returned: "); - v_serial_num((int)result); - v_serial_str("\n"); - - if (result != NJ_OK) { - njDone(); - kfree(buf); - return; - } - - int img_w = njGetWidth(); - int img_h = njGetHeight(); - unsigned char *rgb = njGetImage(); - - v_serial_str("[VIEWER] decoded "); - v_serial_num(img_w); - v_serial_str("x"); - v_serial_num(img_h); - v_serial_str("\n"); - - if (!rgb || img_w <= 0 || img_h <= 0) { - njDone(); - kfree(buf); - return; - } - - // Scale to fit viewer buffer - int fit_w = img_w; - int fit_h = img_h; - if (fit_w > VIEWER_MAX_W) { - fit_h = fit_h * VIEWER_MAX_W / fit_w; - fit_w = VIEWER_MAX_W; - } - if (fit_h > VIEWER_MAX_H) { - fit_w = fit_w * VIEWER_MAX_H / fit_h; - fit_h = VIEWER_MAX_H; - } - - viewer_scale_rgb_to_argb(rgb, img_w, img_h, viewer_pixels, fit_w, fit_h); - viewer_img_w = fit_w; - viewer_img_h = fit_h; - viewer_has_image = true; - - njDone(); - kfree(buf); - - // Store the file path for "Set as Wallpaper" - viewer_strcpy(viewer_file_path, path); - - // Update title - extract filename from path - const char *fname = path; - int plen = viewer_strlen(path); - for (int i = plen - 1; i >= 0; i--) { - if (path[i] == '/') { - fname = &path[i + 1]; - break; - } - } - viewer_title[0] = 'V'; viewer_title[1] = 'i'; viewer_title[2] = 'e'; - viewer_title[3] = 'w'; viewer_title[4] = 'e'; viewer_title[5] = 'r'; - viewer_title[6] = ' '; viewer_title[7] = '-'; viewer_title[8] = ' '; - int ti = 9; - for (int i = 0; fname[i] && ti < 60; i++) { - viewer_title[ti++] = fname[i]; - } - viewer_title[ti] = 0; - - // Resize window to fit image - win_viewer.w = fit_w + 16; - if (win_viewer.w < 200) win_viewer.w = 200; - win_viewer.h = fit_h + 64; - if (win_viewer.h < 100) win_viewer.h = 100; - - // Reset position to ensure visibility - win_viewer.x = 100; - win_viewer.y = 50; - - v_serial_str("[VIEWER] window: x="); - v_serial_num(win_viewer.x); - v_serial_str(" y="); - v_serial_num(win_viewer.y); - v_serial_str(" w="); - v_serial_num(win_viewer.w); - v_serial_str(" h="); - v_serial_num(win_viewer.h); - v_serial_str(" fit="); - v_serial_num(fit_w); - v_serial_str("x"); - v_serial_num(fit_h); - v_serial_str("\n"); - - // Show and bring to front - win_viewer.visible = true; - wm_bring_to_front(&win_viewer); - - v_serial_str("[VIEWER] z_index="); - v_serial_num(win_viewer.z_index); - v_serial_str(" visible="); - v_serial_num(win_viewer.visible); - v_serial_str(" focused="); - v_serial_num(win_viewer.focused); - v_serial_str("\n"); - v_serial_str("[VIEWER] window shown!\n"); -} - -void viewer_init(void) { - win_viewer.title = viewer_title; - viewer_title[0] = 'V'; viewer_title[1] = 'i'; viewer_title[2] = 'e'; - viewer_title[3] = 'w'; viewer_title[4] = 'e'; viewer_title[5] = 'r'; - viewer_title[6] = 0; - win_viewer.x = 100; - win_viewer.y = 50; - win_viewer.w = 500; - win_viewer.h = 400; - win_viewer.visible = false; - win_viewer.paint = viewer_paint; - win_viewer.handle_click = viewer_handle_click; - win_viewer.handle_key = viewer_handle_key; - win_viewer.handle_right_click = NULL; - win_viewer.data = NULL; - // Window is registered directly in wm_init's all_windows array - v_serial_str("[VIEWER] init done, win_viewer paint="); - v_serial_num(win_viewer.paint != NULL); - v_serial_str("\n"); -} diff --git a/src/kernel/viewer.h b/src/kernel/viewer.h deleted file mode 100644 index 3a27817..0000000 --- a/src/kernel/viewer.h +++ /dev/null @@ -1,13 +0,0 @@ -// viewer.h - Image Viewer app for BoredOS -#ifndef VIEWER_H -#define VIEWER_H - -#include "wm.h" - -extern Window win_viewer; - -void viewer_init(void); -void viewer_open_file(const char *path); // Safe from interrupt context (deferred) -void viewer_process_pending(void); // Call from main loop only - -#endif // VIEWER_H diff --git a/src/kernel/wm.c b/src/kernel/wm.c index a96d049..2663d8d 100644 --- a/src/kernel/wm.c +++ b/src/kernel/wm.c @@ -3,22 +3,16 @@ #include "io.h" #include "cmd.h" #include "process.h" +#include "syscall.h" #include "cli_apps/cli_utils.h" #include "explorer.h" -#include "editor.h" -#include "markdown.h" #include #include - -#include "viewer.h" #include "wallpaper.h" -#include "control_panel.h" #include "about.h" -#include "minesweeper.h" #include "fat32.h" #include "nanojpeg.h" #include "memory_manager.h" -#include "paint.h" #include "disk.h" extern void serial_write(const char *str); @@ -152,7 +146,7 @@ static void refresh_desktop_icons(void) { // Find if this icon still exists in the file list int found_idx = -1; for (int j = 0; j < file_count; j++) { - if (!file_processed[j] && str_eq(desktop_icons[i].name, files[j].name) == 0) { + if (!file_processed[j] && str_eq(desktop_icons[i].name, files[j].name) != 0) { found_idx = j; break; } @@ -529,7 +523,7 @@ static int thumb_cache_next = 0; // Round-robin eviction static uint32_t* thumb_cache_lookup(const char *path) { for (int i = 0; i < THUMB_CACHE_SIZE; i++) { - if (thumb_cache[i].valid && str_eq(thumb_cache[i].path, path) == 0) { + if (thumb_cache[i].valid && str_eq(thumb_cache[i].path, path) != 0) { return thumb_cache[i].pixels; } } @@ -538,7 +532,7 @@ static uint32_t* thumb_cache_lookup(const char *path) { static bool thumb_cache_is_failed(const char *path) { for (int i = 0; i < THUMB_CACHE_SIZE; i++) { - if (thumb_cache[i].failed && str_eq(thumb_cache[i].path, path) == 0) { + if (thumb_cache[i].failed && str_eq(thumb_cache[i].path, path) != 0) { return true; } } @@ -619,10 +613,10 @@ void draw_image_icon(int x, int y, const char *label) { uint32_t *thumb = NULL; // Fast path: check hardcoded wallpaper names - if (str_eq(label, "moon.jpg") == 0) thumb = wallpaper_get_thumb(0); - else if (str_eq(label, "mountain.jpg") == 0) thumb = wallpaper_get_thumb(1); - else if (str_eq(label, "moon") == 0) thumb = wallpaper_get_thumb(0); - else if (str_eq(label, "mountain") == 0) thumb = wallpaper_get_thumb(1); + if (str_eq(label, "moon.jpg") != 0) thumb = wallpaper_get_thumb(0); + else if (str_eq(label, "mountain.jpg") != 0) thumb = wallpaper_get_thumb(1); + else if (str_eq(label, "moon") != 0) thumb = wallpaper_get_thumb(0); + else if (str_eq(label, "mountain") != 0) thumb = wallpaper_get_thumb(1); if (!thumb) { if (str_ends_with(label, "moon.jpg")) thumb = wallpaper_get_thumb(0); @@ -658,7 +652,7 @@ void draw_image_icon(int x, int y, const char *label) { // Draw thumbnail into icon - handle both 100x60 wallpaper thumbs and 48x48 dynamic thumbs bool is_wallpaper_thumb = false; if (str_ends_with(label, "moon.jpg") || str_ends_with(label, "mountain.jpg") || - str_eq(label, "moon") == 0 || str_eq(label, "mountain") == 0) { + str_eq(label, "moon") != 0 || str_eq(label, "mountain") != 0) { is_wallpaper_thumb = true; } int dst_w = 44, dst_h = 44; @@ -1567,7 +1561,9 @@ void wm_handle_click(int x, int y) { if (item == 0) { // About wm_bring_to_front(&win_about); } else if (item == 1) { // Settings - wm_bring_to_front(&win_control_panel); + Window *existing = wm_find_window_by_title("Settings"); + if (existing) wm_bring_to_front(existing); + else process_create_elf("/bin/settings.elf", NULL); } else if (item == 2) { // Shutdown cli_cmd_shutdown(NULL); } else if (item == 3) { // Restart @@ -1608,10 +1604,6 @@ void wm_handle_click(int x, int y) { // 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 @@ -1729,11 +1721,6 @@ void wm_handle_right_click(int x, int y) { // 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 @@ -1763,11 +1750,6 @@ void wm_handle_right_click(int x, int y) { } } 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; @@ -1855,7 +1837,9 @@ void wm_handle_right_click(int x, int y) { process_create_elf("/bin/notepad.elf", NULL); } } else if (str_starts_with(start_menu_pending_app, "Editor")) { - wm_bring_to_front(&win_editor); + Window *existing = wm_find_window_by_title("Txtedit"); + if (existing) wm_bring_to_front(existing); + else process_create_elf("/bin/txtedit.elf", NULL); } 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")) { @@ -1866,11 +1850,17 @@ void wm_handle_right_click(int x, int y) { process_create_elf("/bin/calculator.elf", NULL); } } else if (str_starts_with(start_menu_pending_app, "Minesweeper")) { - wm_bring_to_front(&win_minesweeper); + Window *existing = wm_find_window_by_title("Minesweeper"); + if (existing) wm_bring_to_front(existing); + else process_create_elf("/bin/minesweeper.elf", NULL); } else if (str_starts_with(start_menu_pending_app, "Settings")) { - wm_bring_to_front(&win_control_panel); + Window *existing = wm_find_window_by_title("Settings"); + if (existing) wm_bring_to_front(existing); + else process_create_elf("/bin/settings.elf", NULL); } else if (str_starts_with(start_menu_pending_app, "Paint")) { - wm_bring_to_front(&win_paint); + Window *existing = wm_find_window_by_title("Paint"); + if (existing) wm_bring_to_front(existing); + else process_create_elf("/bin/paint.elf", NULL); } 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")) { @@ -1896,9 +1886,9 @@ void wm_handle_right_click(int x, int y) { } else if (str_ends_with(icon->name, "Calculator.shortcut")) { process_create_elf("/bin/calculator.elf", NULL); handled = true; } else if (str_ends_with(icon->name, "Minesweeper.shortcut")) { - wm_bring_to_front(&win_minesweeper); handled = true; + process_create_elf("/bin/minesweeper.elf", NULL); handled = true; } else if (str_ends_with(icon->name, "Settings.shortcut")) { - wm_bring_to_front(&win_control_panel); handled = true; + process_create_elf("/bin/settings.elf", NULL); 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")) { @@ -1908,7 +1898,7 @@ void wm_handle_right_click(int x, int y) { } 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; + process_create_elf("/bin/paint.elf", NULL); handled = true; } if (!handled) { @@ -1927,8 +1917,7 @@ void wm_handle_right_click(int x, int y) { if (fat32_is_directory(buf)) { explorer_open_directory(buf); } else { - editor_open_file(buf); - wm_bring_to_front(&win_editor); + process_create_elf("/bin/txtedit.elf", buf); } pending_desktop_icon_click = -1; force_redraw = true; @@ -1948,16 +1937,13 @@ void wm_handle_right_click(int x, int y) { if (str_ends_with(icon->name, ".elf")) { process_create_elf(path, NULL); } else if (str_ends_with(icon->name, ".pnt")) { - paint_load(path); - wm_bring_to_front(&win_paint); + process_create_elf("/bin/paint.elf", path); } else if (str_ends_with(icon->name, ".md")) { - markdown_open_file(path); - wm_bring_to_front(&win_markdown); + process_create_elf("/bin/markdown.elf", path); } else if (str_ends_with(icon->name, ".jpg") || str_ends_with(icon->name, ".JPG")) { - viewer_open_file(path); + process_create_elf("/bin/viewer.elf", path); } else { - editor_open_file(path); - wm_bring_to_front(&win_editor); + process_create_elf("/bin/txtedit.elf", path); } } } @@ -2008,7 +1994,7 @@ void wm_handle_right_click(int x, int y) { 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 (str_eq(path, drag_file_path) != 0) continue; } if (rect_contains(desktop_icons[i].x + 20, desktop_icons[i].y, 40, 40, mx, my)) { @@ -2051,7 +2037,7 @@ void wm_handle_right_click(int x, int y) { if (desktop_auto_align && !msg_box_visible) { int new_idx = -1; for(int i=0; ivisible && rect_contains(win->x, win->y, win->w, win->h, mx, my)) { + if (win->z_index > topmost_z) { + topmost = win; + topmost_z = win->z_index; + } + } + } + if (topmost && topmost->data) { + syscall_send_mouse_down_event(topmost, mx - topmost->x, my - topmost->y); + } + } + + if (!left && prev_left) { + // Left button released - send MOUSE_UP event to topmost window + Window *topmost = 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) { + topmost = win; + topmost_z = win->z_index; + } + } + } + if (topmost && topmost->data) { + syscall_send_mouse_up_event(topmost, mx - topmost->x, my - topmost->y); + } + } + + if (dx != 0 || dy != 0) { + // Mouse moved - send MOUSE_MOVE event to topmost window + Window *topmost = 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) { + topmost = win; + topmost_z = win->z_index; + } + } + } + if (topmost && topmost->data) { + syscall_send_mouse_move_event(topmost, mx - topmost->x, my - topmost->y, buttons); + } + } + prev_left = left; prev_right = right; @@ -2290,13 +2331,7 @@ void wm_init(void) { cmd_init(); explorer_init(); - editor_init(); - markdown_init(); - control_panel_init(); about_init(); - minesweeper_init(); - paint_init(); - viewer_init(); wallpaper_init(); refresh_desktop_icons(); @@ -2304,24 +2339,12 @@ void wm_init(void) { // Initialize z-indices win_cmd.z_index = 0; win_explorer.z_index = 1; - win_editor.z_index = 2; - win_markdown.z_index = 3; - win_control_panel.z_index = 4; - win_about.z_index = 5; - win_minesweeper.z_index = 6; - win_paint.z_index = 7; - win_viewer.z_index = 8; + win_about.z_index = 2; all_windows[0] = &win_cmd; all_windows[1] = &win_explorer; - all_windows[2] = &win_editor; - all_windows[3] = &win_markdown; - all_windows[4] = &win_control_panel; - all_windows[5] = &win_about; - all_windows[6] = &win_minesweeper; - all_windows[7] = &win_paint; - all_windows[8] = &win_viewer; - window_count = 9; + all_windows[2] = &win_about; + window_count = 3; // Only show Explorer on desktop (initially hidden) win_explorer.visible = false; @@ -2330,11 +2353,7 @@ void wm_init(void) { // Rest are hidden initially win_cmd.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; }