Porting of apps to userspace

This commit is contained in:
boreddevnl 2026-02-26 20:07:21 +01:00
parent c2ead0d6a7
commit 786eac0345
84 changed files with 2887 additions and 2392 deletions

BIN
.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
disk.img

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
iso_root/bin/markdown.elf Executable file

Binary file not shown.

BIN
iso_root/bin/minesweeper.elf Executable file

Binary file not shown.

Binary file not shown.

BIN
iso_root/bin/paint.elf Executable file

Binary file not shown.

BIN
iso_root/bin/settings.elf Executable file

Binary file not shown.

BIN
iso_root/bin/txtedit.elf Executable file

Binary file not shown.

BIN
iso_root/bin/viewer.elf Executable file

Binary file not shown.

Binary file not shown.

View file

@ -8,4 +8,10 @@ timeout: 3
module_path: boot():/bin/calculator.elf module_path: boot():/bin/calculator.elf
module_path: boot():/bin/crash.elf module_path: boot():/bin/crash.elf
module_path: boot():/bin/hello.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/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

View file

@ -7,15 +7,11 @@ extern kmain
bits 64 bits 64
_start: _start:
; Ensure interrupts are disabled
cli cli
; (Limine guarantees 16-byte alignment)
; Call the C kernel entry point
call kmain call kmain
; Halt if kmain returns
hlt hlt
.loop: .loop:
jmp .loop jmp .loop

View file

@ -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 <filename>\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");
}

View file

@ -513,7 +513,7 @@ static void internal_cmd_txtedit(char *args) {
i++; i++;
} }
// Extract filepath (args now includes drive prefix from cmd_exec_single) // Extract filepath
int j = 0; int j = 0;
while (args && args[i] && args[i] != ' ' && args[i] != '\t' && j < 255) { while (args && args[i] && args[i] != ' ' && args[i] != '\t' && j < 255) {
filepath[j++] = args[i++]; filepath[j++] = args[i++];
@ -523,50 +523,19 @@ static void internal_cmd_txtedit(char *args) {
// If no filepath provided, show usage // If no filepath provided, show usage
if (j == 0) { if (j == 0) {
cmd_write("Usage: txtedit <filename>\n"); cmd_write("Usage: txtedit <filename>\n");
cmd_write("Example: txtedit myfile.txt\n");
cmd_write(" txtedit /document.txt\n");
return; return;
} }
// Normalize the path (filepath already includes drive from cmd_exec_single) // Normalize the path
char normalized_path[256]; char normalized_path[256];
fat32_normalize_path(filepath, normalized_path); 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("Opening: ");
cmd_write(normalized_path); cmd_write(normalized_path);
cmd_write("\n"); 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) { static void internal_cmd_ls(char *args) {

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

View file

@ -4,29 +4,17 @@
#include "disk.h" #include "disk.h"
#include "wm.h" #include "wm.h"
#include "memory_manager.h" #include "memory_manager.h"
#include "editor.h"
#include "markdown.h"
#include "cmd.h" #include "cmd.h"
#include "process.h" #include "process.h"
#include "minesweeper.h"
#include "viewer.h"
#include "control_panel.h"
#include "about.h" #include "about.h"
#include "paint.h"
#include <stdbool.h>
#include <stddef.h>
// === File Explorer State ===
Window win_explorer;
#define EXPLORER_MAX_FILES 64
#define EXPLORER_ITEM_HEIGHT 80 #define EXPLORER_ITEM_HEIGHT 80
#define EXPLORER_ITEM_WIDTH 120 #define EXPLORER_ITEM_WIDTH 120
#define EXPLORER_COLS 4 #define EXPLORER_COLS 4
#define EXPLORER_ROWS 4 #define EXPLORER_ROWS 4
#define EXPLORER_PADDING 15 #define EXPLORER_PADDING 15
Window win_explorer;
// Dialog states // Dialog states
#define DIALOG_NONE 0 #define DIALOG_NONE 0
#define DIALOG_CREATE_FILE 1 #define DIALOG_CREATE_FILE 1
@ -819,16 +807,13 @@ static void explorer_open_target(const char *path) {
if (explorer_str_ends_with(path, ".elf")) { if (explorer_str_ends_with(path, ".elf")) {
process_create_elf(path, NULL); process_create_elf(path, NULL);
} else if (explorer_is_markdown_file(path)) { } else if (explorer_is_markdown_file(path)) {
wm_bring_to_front(&win_markdown); process_create_elf("A:/bin/markdown.elf", path);
markdown_open_file(path);
} else if (explorer_str_ends_with(path, ".pnt")) { } else if (explorer_str_ends_with(path, ".pnt")) {
paint_load(path); process_create_elf("A:/bin/paint.elf", path);
wm_bring_to_front(&win_paint);
} else if (explorer_str_ends_with(path, ".jpg") || explorer_str_ends_with(path, ".JPG")) { } 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 { } else {
wm_bring_to_front(&win_editor); process_create_elf("A:/bin/txtedit.elf", path);
editor_open_file(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) { } else if (explorer_strcmp(state->items[index].name, "Terminal.shortcut") == 0) {
target = &win_cmd; cmd_reset(); target = &win_cmd; cmd_reset();
} else if (explorer_strcmp(state->items[index].name, "Minesweeper.shortcut") == 0) { } else if (explorer_strcmp(state->items[index].name, "Minesweeper.shortcut") == 0) {
target = &win_minesweeper; process_create_elf("/bin/minesweeper.elf", NULL); return;
} else if (explorer_strcmp(state->items[index].name, "Control Panel.shortcut") == 0) { } else if (explorer_strcmp(state->items[index].name, "Control Panel.shortcut") == 0 || explorer_strcmp(state->items[index].name, "Settings.shortcut") == 0) {
target = &win_control_panel; process_create_elf("/bin/settings.elf", NULL); return;
} else if (explorer_strcmp(state->items[index].name, "About.shortcut") == 0) { } else if (explorer_strcmp(state->items[index].name, "About.shortcut") == 0) {
target = &win_about; target = &win_about;
} else if (explorer_strcmp(state->items[index].name, "Explorer.shortcut") == 0) { } 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); state->dialog_input_cursor = explorer_strlen(state->dialog_input);
explorer_strcpy(state->dialog_target_path, full_path); explorer_strcpy(state->dialog_target_path, full_path);
} else if (clicked_action == 110) { // Open with Text Editor } else if (clicked_action == 110) { // Open with Text Editor
win_editor.visible = true; win_editor.focused = true; process_create_elf("A:/bin/txtedit.elf", full_path);
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);
} else if (clicked_action == ACTION_RESTORE) { } else if (clicked_action == ACTION_RESTORE) {
explorer_restore_file(win, state->file_context_menu_item); explorer_restore_file(win, state->file_context_menu_item);
} else if (clicked_action == ACTION_CREATE_SHORTCUT) { } else if (clicked_action == ACTION_CREATE_SHORTCUT) {
@ -2024,6 +1999,6 @@ void explorer_reset(void) {
ExplorerState *state = (ExplorerState*)win_explorer.data; ExplorerState *state = (ExplorerState*)win_explorer.data;
// Reset explorer to root directory on close/reopen // Reset explorer to root directory on close/reopen
explorer_load_directory(&win_explorer, "A:/"); explorer_load_directory(&win_explorer, "A:/");
win_explorer.focused = false;
state->explorer_scroll_row = 0; state->explorer_scroll_row = 0;
win_explorer.focused = false;
} }

View file

@ -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 if (entry[e].attributes & ATTR_VOLUME_ID) continue; // Volume label
// Compare name (simplistic 8.3 matching) // Compare name (simplistic 8.3 matching)
char name[12]; char name[13];
int n = 0; int n = 0;
for (int k = 0; k < 8 && entry[e].filename[k] != ' '; k++) name[n++] = entry[e].filename[k]; for (int k = 0; k < 8 && entry[e].filename[k] != ' '; k++) name[n++] = entry[e].filename[k];
if (entry[e].extension[0] != ' ') { 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 if (entry[e].attributes & ATTR_VOLUME_ID) continue; // Skip volume label
// Format name and compare // Format name and compare
char name[12]; char name[13];
int n = 0; int n = 0;
for (int k = 0; k < 8 && entry[e].filename[k] != ' '; k++) name[n++] = entry[e].filename[k]; for (int k = 0; k < 8 && entry[e].filename[k] != ' '; k++) name[n++] = entry[e].filename[k];
if (entry[e].extension[0] != ' ') { if (entry[e].extension[0] != ' ') {

View file

@ -4,34 +4,27 @@
#include "io.h" #include "io.h"
static struct limine_framebuffer *g_fb = NULL; 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 #define PATTERN_SIZE 128
static uint32_t g_bg_pattern[PATTERN_SIZE * PATTERN_SIZE]; static uint32_t g_bg_pattern[PATTERN_SIZE * PATTERN_SIZE];
static bool g_use_pattern = false; static bool g_use_pattern = false;
// Wallpaper image support
static uint32_t *g_bg_image = NULL; static uint32_t *g_bg_image = NULL;
static int g_bg_image_w = 0; static int g_bg_image_w = 0;
static int g_bg_image_h = 0; static int g_bg_image_h = 0;
static bool g_use_image = false; static bool g_use_image = false;
// Dirty rectangle tracking
static DirtyRect g_dirty = {0, 0, 0, 0, false}; 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_WIDTH 2048
#define MAX_FB_HEIGHT 2048 #define MAX_FB_HEIGHT 2048
static uint32_t g_back_buffer[MAX_FB_WIDTH * MAX_FB_HEIGHT] __attribute__((aligned(4096))); 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 int g_clip_x = 0, g_clip_y = 0, g_clip_w = 0, g_clip_h = 0;
static bool g_clip_enabled = false; static bool g_clip_enabled = false;
// Custom Render Target
static uint32_t *g_render_target = NULL; static uint32_t *g_render_target = NULL;
static int g_rt_width = 0; static int g_rt_width = 0;
static int g_rt_height = 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, y + radius, radius, h - 2*radius, color);
draw_rect(x + w - radius, 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 (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)); 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); 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); 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); 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); 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); 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; unsigned char uc = (unsigned char)c;
if (uc > 127) return; 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 (g_clip_enabled && !g_render_target) {
if (x + 8 <= g_clip_x || x >= g_clip_x + g_clip_w || 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) { y + 8 <= g_clip_y || y >= g_clip_y + g_clip_h) {

View file

@ -14,11 +14,15 @@
#define GUI_EVENT_RIGHT_CLICK 3 #define GUI_EVENT_RIGHT_CLICK 3
#define GUI_EVENT_CLOSE 4 #define GUI_EVENT_CLOSE 4
#define GUI_EVENT_KEY 5 #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 { typedef struct {
int type; int type;
int arg1; // For click: x int arg1; // For click: x
int arg2; // For click: y int arg2; // For click: y
int arg3; // For click: button state
} gui_event_t; } gui_event_t;
#endif #endif

View file

@ -15,7 +15,6 @@
#include "memory_manager.h" #include "memory_manager.h"
#include "platform.h" #include "platform.h"
#include "wallpaper.h" #include "wallpaper.h"
#include "viewer.h"
// --- Limine Requests --- // --- Limine Requests ---
__attribute__((used, section(".requests"))) __attribute__((used, section(".requests")))
@ -183,7 +182,6 @@ void kmain(void) {
while (1) { while (1) {
wm_process_input(); wm_process_input();
wallpaper_process_pending(); wallpaper_process_pending();
viewer_process_pending();
asm("hlt"); asm("hlt");
} }
} }

View file

@ -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

View file

@ -1,10 +0,0 @@
#ifndef MINESWEEPER_H
#define MINESWEEPER_H
#include "wm.h"
extern Window win_minesweeper;
void minesweeper_init(void);
#endif

View file

@ -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;
}
}
}

View file

@ -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

View file

@ -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_head = 0;
new_proc->gui_event_tail = 0; new_proc->gui_event_tail = 0;
new_proc->ui_window = NULL; new_proc->ui_window = NULL;
new_proc->heap_start = 0x20000000; // 512MB mark
new_proc->heap_end = 0x20000000;
// 2. Load ELF executable // 2. Load ELF executable
uint64_t entry_point = elf_load(filepath, new_proc->pml4_phys); 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 // Drop event if queue is full
if (next_tail == proc->gui_event_head) { if (next_tail == proc->gui_event_head) {
extern void serial_write(const char *str); extern void serial_write(const char *str);
serial_write("PROC: GUI event queue full, dropping event!\n");
return; return;
} }
proc->gui_events[proc->gui_event_tail] = *ev; proc->gui_events[proc->gui_event_tail] = *ev;

View file

@ -30,6 +30,9 @@ typedef struct process {
int gui_event_tail; int gui_event_tail;
void *ui_window; // Pointer to the active Window void *ui_window; // Pointer to the active Window
uint64_t heap_start;
uint64_t heap_end;
void *fds[MAX_PROCESS_FDS]; void *fds[MAX_PROCESS_FDS];
struct process *next; struct process *next;

View file

@ -5,7 +5,8 @@
#include "process.h" #include "process.h"
#include "wm.h" #include "wm.h"
#include "fat32.h" #include "fat32.h"
#include "fat32.h" #include "paging.h"
#include "platform.h"
// Read MSR // Read MSR
static inline uint64_t rdmsr(uint32_t 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); 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) { static void user_window_key(Window *win, char c) {
process_t *proc = (process_t *)win->data; process_t *proc = (process_t *)win->data;
if (!proc) return; 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 // Set callbacks
win->paint = user_window_paint; win->paint = user_window_paint;
win->handle_click = user_window_click; win->handle_click = user_window_click;
win->handle_right_click = user_window_right_click;
win->handle_close = user_window_close; win->handle_close = user_window_close;
win->handle_key = user_window_key; win->handle_key = user_window_key;
win->handle_right_click = NULL;
proc->ui_window = win; proc->ui_window = win;
wm_add_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); extern void serial_write(const char *str);
serial_write((const char *)arg2); serial_write((const char *)arg2);
return 0; 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; return 0;

View file

@ -3,6 +3,9 @@
#include <stdint.h> #include <stdint.h>
// Forward declarations
typedef struct Window Window;
// MSRs used for syscalls in x86_64 // MSRs used for syscalls in x86_64
#define MSR_EFER 0xC0000080 #define MSR_EFER 0xC0000080
#define MSR_STAR 0xC0000081 #define MSR_STAR 0xC0000081
@ -32,4 +35,9 @@
void syscall_init(void); 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); 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 #endif // SYSCALL_H

View file

@ -8,7 +8,7 @@ LDFLAGS = -m elf_x86_64 -nostdlib -static -no-pie -Ttext=0x40000000 --no-dynamic
LIBC_SOURCES = $(wildcard libc/*.c) LIBC_SOURCES = $(wildcard libc/*.c)
LIBC_OBJS = $(LIBC_SOURCES:.c=.o) crt0.o 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_OBJS = $(APP_SOURCES:.c=.o)
APP_ELFS = $(APP_SOURCES:.c=.elf) APP_ELFS = $(APP_SOURCES:.c=.elf)
@ -23,6 +23,9 @@ libc/%.o: libc/%.c
%.o: %.c %.o: %.c
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
viewer.elf: $(LIBC_OBJS) viewer.o nanojpeg.o
$(LD) $(LDFLAGS) $^ -o $@
%.elf: $(LIBC_OBJS) %.o %.elf: $(LIBC_OBJS) %.o
$(LD) $(LDFLAGS) $^ -o $@ $(LD) $(LDFLAGS) $^ -o $@

View file

@ -4,7 +4,6 @@
#define SCALE 1000000LL #define SCALE 1000000LL
// Dark Mode Colors mapping to libui
#define COLOR_DARK_BG 0xFF1E1E1E #define COLOR_DARK_BG 0xFF1E1E1E
#define COLOR_DARK_PANEL 0xFF2D2D2D #define COLOR_DARK_PANEL 0xFF2D2D2D
#define COLOR_DARK_TEXT 0xFFF0F0F0 #define COLOR_DARK_TEXT 0xFFF0F0F0

Binary file not shown.

View file

@ -4,7 +4,7 @@ int main() {
const char* msg = "Attempting to crash via null dereference...\n"; const char* msg = "Attempting to crash via null dereference...\n";
sys_write(1, msg, 45); 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; volatile int* p = (int*)0;
*p = 123; *p = 123;

Binary file not shown.

Binary file not shown.

View file

@ -20,10 +20,15 @@
#define GUI_EVENT_CLOSE 4 #define GUI_EVENT_CLOSE 4
#define GUI_EVENT_KEY 5 #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 { typedef struct {
int type; int type;
int arg1; // For click: x int arg1; // For click: x
int arg2; // For click: y int arg2; // For click: y
int arg3; // For click: button state
} gui_event_t; } gui_event_t;
// Window Handle // Window Handle

Binary file not shown.

View file

@ -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;
}

View file

@ -0,0 +1,14 @@
#ifndef STDLIB_H
#define STDLIB_H
#include <stddef.h>
#include <stdint.h>
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

Binary file not shown.

View file

@ -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); 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) { int sys_open(const char *path, const char *mode) {
return (int)syscall3(SYS_FS, FS_CMD_OPEN, (uint64_t)path, (uint64_t)mode); return (int)syscall3(SYS_FS, FS_CMD_OPEN, (uint64_t)path, (uint64_t)mode);
} }

View file

@ -8,6 +8,8 @@
#define SYS_WRITE 1 #define SYS_WRITE 1
#define SYS_GUI 3 #define SYS_GUI 3
#define SYS_FS 4 #define SYS_FS 4
#define SYS_SYSTEM 5
#define SYS_SBRK 9
// FS Commands // FS Commands
#define FS_CMD_OPEN 1 #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 // Public API
void sys_exit(int status); void sys_exit(int status);
int sys_write(int fd, const char *buf, int len); 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 // FS API
int sys_open(const char *path, const char *mode); int sys_open(const char *path, const char *mode);

Binary file not shown.

View file

@ -1,20 +1,19 @@
#include "markdown.h" #include "libc/syscall.h"
#include "graphics.h" #include "libc/libui.h"
#include "fat32.h"
#include "wm.h"
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
// === Markdown Viewer State ===
Window win_markdown;
#define MD_MAX_CONTENT 16384 #define MD_MAX_CONTENT 16384
#define MD_MAX_LINES 256 #define MD_MAX_LINES 256
#define MD_CHAR_WIDTH 8 #define MD_CHAR_WIDTH 8
#define MD_LINE_HEIGHT 16 #define MD_LINE_HEIGHT 16
#define MD_CONTENT_Y 40
#define MD_PADDING_X 12 #define COLOR_DARK_PANEL 0xFF202020
#define MD_CONTENT_WIDTH 400 #define COLOR_DARK_BG 0xFF121212
#define COLOR_DARK_TEXT 0xFFE0E0E0
#define COLOR_DARK_TITLEBAR 0xFF303030
#define COLOR_BLACK 0xFF000000
typedef enum { typedef enum {
MD_LINE_NORMAL, MD_LINE_NORMAL,
@ -40,7 +39,8 @@ static int line_count = 0;
static int scroll_top = 0; static int scroll_top = 0;
static char open_filename[256] = ""; 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) { static size_t md_strlen(const char *str) {
size_t len = 0; size_t len = 0;
@ -63,64 +63,42 @@ static int md_strncpy(char *dest, const char *src, int n) {
return i; 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) { static void md_parse_line(const char *raw_line, char *output, MDLineType *type, int *indent) {
int i = 0; int i = 0;
int out_idx = 0; int out_idx = 0;
*indent = 0; *indent = 0;
*type = MD_LINE_NORMAL; *type = MD_LINE_NORMAL;
// Skip leading whitespace and count indentation
while (raw_line[i] == ' ' || raw_line[i] == '\t') { while (raw_line[i] == ' ' || raw_line[i] == '\t') {
if (raw_line[i] == '\t') *indent += 2; if (raw_line[i] == '\t') *indent += 2;
else *indent += 1; else *indent += 1;
i++; i++;
} }
// Detect line type
if (raw_line[i] == '#') { if (raw_line[i] == '#') {
// Heading
int hash_count = 0; int hash_count = 0;
while (raw_line[i] == '#') { while (raw_line[i] == '#') {
hash_count++; hash_count++;
i++; i++;
} }
// Skip space after hashes
if (raw_line[i] == ' ') i++; if (raw_line[i] == ' ') i++;
if (hash_count == 1) *type = MD_LINE_HEADING1; if (hash_count == 1) *type = MD_LINE_HEADING1;
else if (hash_count == 2) *type = MD_LINE_HEADING2; else if (hash_count == 2) *type = MD_LINE_HEADING2;
else if (hash_count <= 6) *type = MD_LINE_HEADING3; else if (hash_count <= 6) *type = MD_LINE_HEADING3;
} else if (raw_line[i] == '-' || raw_line[i] == '*') { } 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')) { if ((raw_line[i] == '-' || raw_line[i] == '*') && (raw_line[i+1] == ' ' || raw_line[i+1] == '\t')) {
*type = MD_LINE_LIST; *type = MD_LINE_LIST;
i += 2; // Skip '- ' or '* ' i += 2;
while (raw_line[i] == ' ' || raw_line[i] == '\t') i++; // Skip extra spaces while (raw_line[i] == ' ' || raw_line[i] == '\t') i++;
} }
} else if (raw_line[i] == '>') { } else if (raw_line[i] == '>') {
// Blockquote
*type = MD_LINE_BLOCKQUOTE; *type = MD_LINE_BLOCKQUOTE;
i++; i++;
if (raw_line[i] == ' ') i++; if (raw_line[i] == ' ') i++;
} }
// Parse inline formatting and copy content
while (raw_line[i] && out_idx < 255) { while (raw_line[i] && out_idx < 255) {
// Handle bold **text**
if (raw_line[i] == '*' && raw_line[i+1] == '*') { if (raw_line[i] == '*' && raw_line[i+1] == '*') {
i += 2; i += 2;
while (raw_line[i] && !(raw_line[i] == '*' && raw_line[i+1] == '*') && out_idx < 255) { 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; if (raw_line[i] == '*' && raw_line[i+1] == '*') i += 2;
continue; continue;
} }
// Handle italic *text* or _text_
if ((raw_line[i] == '*' || raw_line[i] == '_') && out_idx > 0 && raw_line[i-1] != '\\') { if ((raw_line[i] == '*' || raw_line[i] == '_') && out_idx > 0 && raw_line[i-1] != '\\') {
char delim = raw_line[i]; char delim = raw_line[i];
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++; if (raw_line[i] == delim) i++;
continue; continue;
} }
// Handle inline code `code`
if (raw_line[i] == '`') { if (raw_line[i] == '`') {
i++; i++;
while (raw_line[i] && raw_line[i] != '`' && out_idx < 255) { 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++; if (raw_line[i] == '`') i++;
continue; continue;
} }
// Handle links [text](url) - keep only text
if (raw_line[i] == '[') { if (raw_line[i] == '[') {
i++; i++;
while (raw_line[i] && raw_line[i] != ']' && out_idx < 255) { while (raw_line[i] && raw_line[i] != ']' && out_idx < 255) {
output[out_idx++] = raw_line[i++]; output[out_idx++] = raw_line[i++];
} }
if (raw_line[i] == ']') i++; if (raw_line[i] == ']') i++;
// Skip (url)
if (raw_line[i] == '(') { if (raw_line[i] == '(') {
while (raw_line[i] && raw_line[i] != ')') i++; while (raw_line[i] && raw_line[i] != ')') i++;
if (raw_line[i] == ')') i++; if (raw_line[i] == ')') i++;
} }
continue; continue;
} }
output[out_idx++] = raw_line[i++]; output[out_idx++] = raw_line[i++];
} }
output[out_idx] = 0; output[out_idx] = 0;
} }
// Clear all markdown lines
static void md_clear_all(void) { static void md_clear_all(void) {
for (int i = 0; i < MD_MAX_LINES; i++) { for (int i = 0; i < MD_MAX_LINES; i++) {
lines[i].content[0] = 0; lines[i].content[0] = 0;
@ -185,29 +153,20 @@ static void md_clear_all(void) {
open_filename[0] = 0; open_filename[0] = 0;
} }
// Load and parse markdown file
void markdown_open_file(const char *filename) { void markdown_open_file(const char *filename) {
md_clear_all(); md_clear_all();
md_strcpy(open_filename, filename); md_strcpy(open_filename, filename);
FAT32_FileHandle *fh = fat32_open(filename, "r"); int fd = sys_open(filename, "r");
if (!fh) { if (fd < 0) return;
// File not found
return;
}
// Read file content static char buffer[MD_MAX_CONTENT];
char buffer[MD_MAX_CONTENT]; int bytes_read = sys_read(fd, buffer, sizeof(buffer) - 1);
int bytes_read = fat32_read(fh, buffer, sizeof(buffer) - 1); sys_close(fd);
fat32_close(fh);
if (bytes_read <= 0) {
return;
}
if (bytes_read <= 0) return;
buffer[bytes_read] = 0; buffer[bytes_read] = 0;
// Parse into markdown lines
int line = 0; int line = 0;
int col = 0; int col = 0;
char raw_line[256] = ""; 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++) { for (int i = 0; i < bytes_read && line < MD_MAX_LINES; i++) {
char ch = buffer[i]; char ch = buffer[i];
if (ch == '\n') { if (ch == '\n') {
raw_line[col] = 0; raw_line[col] = 0;
if (raw_line[0] == '`' && raw_line[1] == '`' && raw_line[2] == '`') { if (raw_line[0] == '`' && raw_line[1] == '`' && raw_line[2] == '`') {
in_code_block = !in_code_block; in_code_block = !in_code_block;
} else { } else {
@ -240,7 +197,6 @@ void markdown_open_file(const char *filename) {
line++; line++;
} }
} }
col = 0; col = 0;
raw_line[0] = 0; raw_line[0] = 0;
} else if (col < 255) { } 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) { if (col > 0 && line < MD_MAX_LINES) {
raw_line[col] = 0; raw_line[col] = 0;
if (raw_line[0] == '`' && raw_line[1] == '`' && raw_line[2] == '`') { if (raw_line[0] == '`' && raw_line[1] == '`' && raw_line[2] == '`') {
@ -270,48 +225,38 @@ void markdown_open_file(const char *filename) {
line++; line++;
} }
} }
line_count = line; line_count = line;
} }
// === Paint Function === 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);
// Helper to draw text with emphasis (bold effect by overlaying) ui_draw_string(win, x + 1, y, text, color);
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_paint(Window *win) { static void md_paint(ui_window_t win) {
int offset_x = win->x + 4; int offset_x = 4;
int offset_y = win->y + 24; int offset_y = 24;
int content_width = win->w - 8; int content_width = win_w - 8;
int content_height = win->h - 28; int content_height = win_h - 28;
// Draw filename bar below title (dark mode) ui_draw_rounded_rect_filled(win, offset_x, offset_y, content_width, 20, 6, COLOR_DARK_PANEL);
draw_rounded_rect_filled(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);
draw_string(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_string(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_x_up = offset_x + content_width - 50;
int btn_y = offset_y + 2; int btn_y = offset_y + 2;
draw_rounded_rect_filled(btn_x_up, btn_y, 20, 16, 4, COLOR_DARK_TITLEBAR); ui_draw_rounded_rect_filled(win, btn_x_up, btn_y, 20, 16, 4, COLOR_DARK_TITLEBAR);
draw_string(btn_x_up + 6, btn_y, "^", COLOR_DARK_TEXT); ui_draw_string(win, 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); ui_draw_rounded_rect_filled(win, 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_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_y = offset_y + 24;
int content_start_x = offset_x + 4; 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 usable_content_height = content_height - 28;
int max_display_lines = usable_content_height / MD_LINE_HEIGHT; int max_display_lines = usable_content_height / MD_LINE_HEIGHT;
// Draw content background (dark mode) ui_draw_rounded_rect_filled(win, 4, content_start_y, win_w - 24, usable_content_height, 6, COLOR_DARK_BG);
draw_rounded_rect_filled(win->x + 4, content_start_y, win->w - 24, usable_content_height, 6, COLOR_DARK_BG);
int display_line = 0; int display_line = 0;
int i = scroll_top; int i = scroll_top;
@ -319,7 +264,6 @@ static void md_paint(Window *win) {
while (i < line_count && display_line < max_display_lines) { while (i < line_count && display_line < max_display_lines) {
MDLine *line = &lines[i]; MDLine *line = &lines[i];
// Determine spacing and text properties based on heading level (dark mode colors)
int line_height = MD_LINE_HEIGHT; int line_height = MD_LINE_HEIGHT;
int extra_spacing = 0; int extra_spacing = 0;
uint32_t text_color = COLOR_DARK_TEXT; uint32_t text_color = COLOR_DARK_TEXT;
@ -327,148 +271,117 @@ static void md_paint(Window *win) {
switch (line->type) { switch (line->type) {
case MD_LINE_HEADING1: case MD_LINE_HEADING1:
line_height = MD_LINE_HEIGHT * 2; // Double height line_height = MD_LINE_HEIGHT * 2;
text_color = 0xFF87CEEB; // Light blue text_color = 0xFF87CEEB;
use_bold = true; use_bold = true;
extra_spacing = 4; extra_spacing = 4;
break; break;
case MD_LINE_HEADING2: case MD_LINE_HEADING2:
line_height = MD_LINE_HEIGHT + 6; // 1.5x height line_height = MD_LINE_HEIGHT + 6;
text_color = 0xFF4A90E2; // Medium blue text_color = 0xFF4A90E2;
use_bold = true; use_bold = true;
extra_spacing = 2; extra_spacing = 2;
break; break;
case MD_LINE_HEADING3: case MD_LINE_HEADING3:
line_height = MD_LINE_HEIGHT + 2; // Slightly larger line_height = MD_LINE_HEIGHT + 2;
text_color = 0xFF87CEEB; // Light blue text_color = 0xFF87CEEB;
use_bold = false; use_bold = false;
break; break;
case MD_LINE_BLOCKQUOTE: case MD_LINE_BLOCKQUOTE:
text_color = 0xFFA0A0A0; // Light gray text_color = 0xFFA0A0A0;
break; break;
case MD_LINE_CODE: case MD_LINE_CODE:
text_color = 0xFF90EE90; // Light green for code text_color = 0xFF90EE90;
break; break;
default: default:
text_color = COLOR_DARK_TEXT; text_color = COLOR_DARK_TEXT;
break; break;
} }
// Check if this heading will fit on the screen if (display_line + (line_height / MD_LINE_HEIGHT) > max_display_lines) break;
if (display_line + (line_height / MD_LINE_HEIGHT) > max_display_lines) {
break; // Stop rendering if heading won't fit
}
// Adjust X position based on indentation
int x_offset = content_start_x + (line->indent_level * 4); int x_offset = content_start_x + (line->indent_level * 4);
int available_width = usable_content_width - (line->indent_level * 4); int available_width = usable_content_width - (line->indent_level * 4);
int max_chars_per_line = available_width / MD_CHAR_WIDTH; int max_chars_per_line = available_width / MD_CHAR_WIDTH;
if (max_chars_per_line < 1) max_chars_per_line = 1; if (max_chars_per_line < 1) max_chars_per_line = 1;
// Handle line wrapping (word-based)
const char *text = line->content; const char *text = line->content;
int text_len = line->length; int text_len = line->length;
int char_idx = 0; int char_idx = 0;
int local_display_line = 0; int local_display_line = 0;
int wrapped_line_count = 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); 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]; char line_segment[256];
int segment_len = 0; int segment_len = 0;
int segment_start = char_idx; 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) { while (char_idx < text_len && segment_len < max_chars_per_line) {
line_segment[segment_len++] = text[char_idx++]; line_segment[segment_len++] = text[char_idx++];
} }
line_segment[segment_len] = 0; line_segment[segment_len] = 0;
if (char_idx < text_len && segment_len > 0) { if (char_idx < text_len && segment_len > 0) {
// Look for the last space in the segment
int last_space = -1; int last_space = -1;
for (int i = segment_len - 1; i >= 0; i--) { for (int j = segment_len - 1; j >= 0; j--) {
if (line_segment[i] == ' ') { if (line_segment[j] == ' ') {
last_space = i; last_space = j; break;
break;
} }
} }
if (last_space > 0) { if (last_space > 0) {
segment_len = last_space; segment_len = last_space;
line_segment[segment_len] = 0; line_segment[segment_len] = 0;
// Backtrack char_idx to position after the space
char_idx = segment_start + last_space + 1; 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) { 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) { if (local_display_line == 0) {
switch (line->type) { switch (line->type) {
case MD_LINE_LIST: case MD_LINE_LIST:
// Draw bullet point ui_draw_rect(win, x_offset, line_y + MD_LINE_HEIGHT/2 - 1, 2, 2, COLOR_BLACK);
draw_rect(x_offset, line_y + MD_LINE_HEIGHT/2 - 1, 2, 2, COLOR_BLACK);
x_offset += 12; x_offset += 12;
// Redraw segment without leading space
if (segment_len > 0 && line_segment[0] == ' ') { if (segment_len > 0 && line_segment[0] == ' ') {
for (int j = 0; j < segment_len - 1; j++) { for (int j = 0; j < segment_len - 1; j++) line_segment[j] = line_segment[j + 1];
line_segment[j] = line_segment[j + 1];
}
segment_len--; segment_len--;
} }
break; break;
case MD_LINE_BLOCKQUOTE: case MD_LINE_BLOCKQUOTE:
// Draw left border ui_draw_rect(win, x_offset - 4, line_y, 2, line_height, 0xFF404080);
draw_rect(x_offset - 4, line_y, 2, line_height, 0xFF404080);
break;
default:
break; break;
default: break;
} }
} }
// Draw the text segment with appropriate styling
if (segment_len > 0) { if (segment_len > 0) {
if (use_bold) { 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 { } 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++; local_display_line++;
wrapped_line_count++; wrapped_line_count++;
if (char_idx >= text_len) break; if (char_idx >= text_len) break;
} }
display_line += (wrapped_line_count > 0 ? wrapped_line_count : 1);
display_line += wrapped_line_count;
i++; i++;
} }
} }
// === Input Handling === static void md_handle_key(char c) {
if (c == 'w' || c == 'W' || c == 17) {
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
scroll_top -= 3; scroll_top -= 3;
if (scroll_top < 0) scroll_top = 0; 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; scroll_top += 3;
int max_scroll = line_count - 10; int max_scroll = line_count - 10;
if (scroll_top > max_scroll) scroll_top = max_scroll; 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) { static void md_handle_click(int x, int y) {
// x and y are relative to window origin int content_width = win_w - 8;
int content_width = win->w - 8;
// Top right up button: 4 + content_width - 50, 24 + 2, 20x16
int btn_x_up = 4 + content_width - 50; int btn_x_up = 4 + content_width - 50;
int btn_y = 24 + 2; int btn_y = 24 + 2;
if (x >= btn_x_up && x < btn_x_up + 20 && y >= btn_y && y < btn_y + 16) { if (x >= btn_x_up && x < btn_x_up + 20 && y >= btn_y && y < btn_y + 16) {
// Scroll up
scroll_top -= 3; scroll_top -= 3;
if (scroll_top < 0) scroll_top = 0; if (scroll_top < 0) scroll_top = 0;
return; return;
} }
// Top right down button: 4 + content_width - 50 + 24, 24 + 2, 20x16
int btn_x_down_top = 4 + content_width - 50 + 24; 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) { if (x >= btn_x_down_top && x < btn_x_down_top + 20 && y >= btn_y && y < btn_y + 16) {
// Scroll down
scroll_top += 3; scroll_top += 3;
int max_scroll = line_count - 10; int max_scroll = line_count - 10;
if (scroll_top > max_scroll) scroll_top = max_scroll; 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);
void markdown_init(void) { if (!win) return 1;
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(); 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;
} }

BIN
src/kernel/userland/markdown.elf Executable file

Binary file not shown.

View file

@ -1,10 +1,23 @@
#include "minesweeper.h" #include "libc/syscall.h"
#include "graphics.h" #include "libc/libui.h"
#include "wm.h"
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
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 // Game constants
#define GRID_WIDTH 10 #define GRID_WIDTH 10
@ -73,9 +86,6 @@ static void init_game(void) {
revealed_count = 0; revealed_count = 0;
} }
static void reveal_cell(int x, int y);
// Flood fill for empty cells
static void flood_fill(int x, int y) { static void flood_fill(int x, int y) {
if (x < 0 || x >= GRID_WIDTH || y < 0 || y >= GRID_HEIGHT) return; if (x < 0 || x >= GRID_WIDTH || y < 0 || y >= GRID_HEIGHT) return;
if (revealed[y][x] || flagged[y][x]) 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]; flagged[y][x] = !flagged[y][x];
} }
static void minesweeper_right_click(Window *win, int x, int y) { static void minesweeper_paint(ui_window_t win) {
// x and y are relative to window content (0,0 is top-left of window) int win_w = 240, win_h = 340;
int grid_start_x = 10;
int grid_start_y = 50;
// 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 // 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 // Game status
if (game_over) { 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) { } 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 { } else {
draw_string(win->x + 10, win->y + 36, "", COLOR_DARK_TEXT); ui_draw_string(win, 10, 36, "", COLOR_DARK_TEXT);
} }
// Draw grid // Draw grid
int grid_start_x = win->x + 10; int grid_start_x = 10;
int grid_start_y = win->y + 56; int grid_start_y = 56;
for (int y = 0; y < GRID_HEIGHT; y++) { for (int y = 0; y < GRID_HEIGHT; y++) {
for (int x = 0; x < GRID_WIDTH; x++) { for (int x = 0; x < GRID_WIDTH; x++) {
@ -174,45 +167,42 @@ static void minesweeper_paint(Window *win) {
if (revealed[y][x]) { if (revealed[y][x]) {
// Revealed cell - dark mode // 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) { if (grid[y][x] == -1) {
// Mine // 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) { } else if (grid[y][x] > 0) {
// Number // Number
char num[2] = { '0' + grid[y][x], 0 }; 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 { } else {
// Unrevealed cell - raised/button look // Unrevealed cell
draw_rounded_rect_filled(px, py, CELL_SIZE, CELL_SIZE, 2, COLOR_DARK_BORDER); ui_draw_rounded_rect_filled(win, px, py, CELL_SIZE, CELL_SIZE, 2, COLOR_DARK_BORDER);
if (flagged[y][x]) { 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; 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); ui_draw_rounded_rect_filled(win, 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_string(win, grid_start_x + 6, btn_y + 8, "New Game", COLOR_DARK_TEXT);
} }
static void minesweeper_click(Window *win, int x, int y) { static void minesweeper_handle_click(ui_window_t win, int x, int y, int button) {
// x and y are relative to window content (0,0 is top-left of window)
int grid_start_x = 10; 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; int btn_y = grid_start_y + GRID_HEIGHT * CELL_SIZE + 10;
// Check "New Game" button // 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) { y >= btn_y && y < btn_y + 24) {
init_game(); init_game();
wm_mark_dirty(win->x, win->y, win->w, win->h);
return; 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_x = (x - grid_start_x) / CELL_SIZE;
int cell_y = (y - grid_start_y) / CELL_SIZE; int cell_y = (y - grid_start_y) / CELL_SIZE;
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); reveal_cell(cell_x, cell_y);
}
wm_mark_dirty(win->x, win->y, win->w, win->h);
} }
} }
void minesweeper_init(void) { int main(int argc, char **argv) {
win_minesweeper.title = "Minesweeper"; (void)argc;
win_minesweeper.x = 250; (void)argv;
win_minesweeper.y = 100; ui_window_t win = ui_window_create("Minesweeper", 250, 100, 240, 340);
win_minesweeper.w = 240; if (!win) return 1;
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 // Use current time or something for seed? No syscall for time right now.
random_seed = 987654321;
init_game(); 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;
} }

Binary file not shown.

View file

@ -0,0 +1,967 @@
// NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder
// version 1.3 (2012-03-05)
// by Martin J. Fiedler <martin.fiedler@gmx.net>
//
// 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 <stdio.h>
#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 <input.jpg> [<output.ppm>]\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 <stddef.h> // 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 <windows.h>
#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; // YCb和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

View file

@ -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

Binary file not shown.

Binary file not shown.

251
src/kernel/userland/paint.c Normal file
View file

@ -0,0 +1,251 @@
#include "libc/syscall.h"
#include "libc/libui.h"
#include "libc/stdlib.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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;
}

BIN
src/kernel/userland/paint.elf Executable file

Binary file not shown.

View file

@ -0,0 +1,691 @@
#include "libc/syscall.h"
#include "libc/libui.h"
#include <stddef.h>
#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;
}

BIN
src/kernel/userland/settings.elf Executable file

Binary file not shown.

View file

@ -1,11 +1,14 @@
#include "editor.h" #include "libc/syscall.h"
#include "graphics.h" #include "libc/libui.h"
#include "fat32.h"
#include "wm.h"
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
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_LINES 128
#define EDITOR_MAX_LINE_LEN 256 #define EDITOR_MAX_LINE_LEN 256
@ -23,37 +26,18 @@ static int cursor_line = 0;
static int cursor_col = 0; static int cursor_col = 0;
static int scroll_top = 0; static int scroll_top = 0;
static char open_filename[256] = ""; static char open_filename[256] = "";
static bool file_modified = false; static _Bool file_modified = 0;
// === Helper Functions === static int win_w = 700;
static int win_h = 450;
static size_t editor_strlen(const char *str) {
size_t len = 0;
while (str[len]) len++;
return len;
}
static void editor_strcpy(char *dest, const char *src) { static void editor_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++; while (*src) *dest++ = *src++;
*dest = 0; *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) { static void editor_clear_all(void) {
for (int i = 0; i < EDITOR_MAX_LINES; i++) { 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++) { for (int j = 0; j < EDITOR_MAX_LINE_LEN; j++) {
lines[i].content[j] = 0; lines[i].content[j] = 0;
} }
@ -64,37 +48,43 @@ static void editor_clear_all(void) {
cursor_col = 0; cursor_col = 0;
scroll_top = 0; scroll_top = 0;
open_filename[0] = 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) { void editor_open_file(const char *filename) {
editor_clear_all(); editor_clear_all();
editor_strcpy(open_filename, filename); editor_strcpy(open_filename, filename);
FAT32_FileHandle *fh = fat32_open(filename, "r"); int fd = sys_open(filename, "r");
if (!fh) { if (fd < 0) {
// New file file_modified = 0;
file_modified = false;
return; return;
} }
// Read file content static char buffer[16384];
char buffer[16384]; int bytes_read = sys_read(fd, buffer, sizeof(buffer));
int bytes_read = fat32_read(fh, buffer, sizeof(buffer)); sys_close(fd);
fat32_close(fh);
if (bytes_read <= 0) { if (bytes_read <= 0) {
file_modified = false; file_modified = 0;
return; return;
} }
// Parse into lines
int line = 0; int line = 0;
int col = 0; int col = 0;
for (int i = 0; i < bytes_read && line < EDITOR_MAX_LINES; i++) { for (int i = 0; i < bytes_read && line < EDITOR_MAX_LINES; i++) {
char ch = buffer[i]; char ch = buffer[i];
if (ch == '\n') { if (ch == '\n') {
lines[line].content[col] = 0; lines[line].content[col] = 0;
lines[line].length = col; lines[line].length = col;
@ -115,71 +105,54 @@ void editor_open_file(const char *filename) {
} }
line_count = (line > 0) ? line : 1; line_count = (line > 0) ? line : 1;
file_modified = false; file_modified = 0;
} }
static void editor_save_file(void) { static void editor_save_file(void) {
if (!open_filename[0]) { if (!open_filename[0]) return;
// No filename set
return;
}
FAT32_FileHandle *fh = fat32_open(open_filename, "w"); int fd = sys_open(open_filename, "w");
if (!fh) { if (fd < 0) return;
return;
}
// Write lines
for (int i = 0; i < line_count; i++) { for (int i = 0; i < line_count; i++) {
fat32_write(fh, lines[i].content, lines[i].length); sys_write_fs(fd, lines[i].content, lines[i].length);
fat32_write(fh, "\n", 1); sys_write_fs(fd, "\n", 1);
} }
sys_close(fd);
fat32_close(fh); file_modified = 0;
file_modified = false;
} }
// Insert character at cursor position
static void editor_insert_char(char ch) { static void editor_insert_char(char ch) {
if (cursor_line >= EDITOR_MAX_LINES) return; if (cursor_line >= EDITOR_MAX_LINES) return;
EditorLine *line = &lines[cursor_line]; EditorLine *line = &lines[cursor_line];
if (ch == '\n') { if (ch == '\n') {
// Split line - shift all lines below down first
if (line_count >= EDITOR_MAX_LINES) return; 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--) { for (int j = line_count; j > cursor_line; j--) {
lines[j] = lines[j - 1]; lines[j] = lines[j - 1];
} }
line_count++; line_count++;
// Clear the new line completely (zero entire buffer)
for (int k = 0; k < EDITOR_MAX_LINE_LEN; k++) { for (int k = 0; k < EDITOR_MAX_LINE_LEN; k++) {
lines[cursor_line + 1].content[k] = 0; lines[cursor_line + 1].content[k] = 0;
} }
lines[cursor_line + 1].length = 0; lines[cursor_line + 1].length = 0;
// Now split the current line at cursor position
int current_len = lines[cursor_line].length; int current_len = lines[cursor_line].length;
int new_len = current_len - cursor_col; int new_len = current_len - cursor_col;
// Copy the second part to the new line
for (int i = 0; i < new_len; i++) { 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[i] = lines[cursor_line].content[cursor_col + i];
} }
lines[cursor_line + 1].content[new_len] = 0; lines[cursor_line + 1].content[new_len] = 0;
lines[cursor_line + 1].length = new_len; lines[cursor_line + 1].length = new_len;
// Truncate current line
lines[cursor_line].content[cursor_col] = 0; lines[cursor_line].content[cursor_col] = 0;
lines[cursor_line].length = cursor_col; lines[cursor_line].length = cursor_col;
cursor_line++; cursor_line++;
cursor_col = 0; cursor_col = 0;
} else if (ch == '\b') { } else if (ch == '\b') {
// Backspace
if (cursor_col > 0) { if (cursor_col > 0) {
for (int i = cursor_col - 1; i < line->length; i++) { for (int i = cursor_col - 1; i < line->length; i++) {
line->content[i] = line->content[i + 1]; line->content[i] = line->content[i + 1];
@ -187,7 +160,6 @@ static void editor_insert_char(char ch) {
line->length--; line->length--;
cursor_col--; cursor_col--;
} else if (cursor_line > 0) { } else if (cursor_line > 0) {
// Merge with previous line
EditorLine *prev = &lines[cursor_line - 1]; EditorLine *prev = &lines[cursor_line - 1];
int merge_point = prev->length; int merge_point = prev->length;
@ -199,9 +171,8 @@ static void editor_insert_char(char ch) {
prev->content[merge_point + i] = 0; prev->content[merge_point + i] = 0;
prev->length = merge_point + i; prev->length = merge_point + i;
// Shift lines up for (int j = cursor_line; j < line_count - 1; j++) {
for (int i = cursor_line; i < line_count - 1; i++) { lines[j] = lines[j + 1];
lines[i] = lines[i + 1];
} }
lines[line_count - 1].length = 0; lines[line_count - 1].length = 0;
lines[line_count - 1].content[0] = 0; lines[line_count - 1].content[0] = 0;
@ -211,9 +182,7 @@ static void editor_insert_char(char ch) {
line_count--; line_count--;
} }
} else if (ch >= 32 && ch <= 126) { } else if (ch >= 32 && ch <= 126) {
// Regular character
if (cursor_col < EDITOR_MAX_LINE_LEN - 1) { if (cursor_col < EDITOR_MAX_LINE_LEN - 1) {
// Shift characters right
for (int i = line->length; i > cursor_col; i--) { for (int i = line->length; i > cursor_col; i--) {
line->content[i] = line->content[i - 1]; line->content[i] = line->content[i - 1];
} }
@ -222,60 +191,39 @@ static void editor_insert_char(char ch) {
cursor_col++; cursor_col++;
} }
} }
file_modified = 1;
file_modified = true;
editor_ensure_cursor_visible(); editor_ensure_cursor_visible();
} }
// Ensure cursor is visible by adjusting scroll position static void editor_paint(ui_window_t win) {
static void editor_ensure_cursor_visible(void) { int offset_x = 4;
int visible_lines = 22; // Allow ~24 lines to use available window space int offset_y = 24;
int content_width = win_w - 8;
int content_height = win_h - 28;
// Scroll up if cursor is above visible area // Top content bar
if (cursor_line < scroll_top) { ui_draw_rounded_rect_filled(win, offset_x, offset_y, content_width, 25, 6, COLOR_DARK_PANEL);
scroll_top = cursor_line; 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 // Save button
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)
int save_btn_x = offset_x + content_width - 80; int save_btn_x = offset_x + content_width - 80;
int save_btn_y = offset_y + 3; int save_btn_y = offset_y + 3;
draw_rounded_rect_filled(save_btn_x, save_btn_y, 70, 20, 6, COLOR_DARK_BORDER); ui_draw_rounded_rect_filled(win, 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_string(win, save_btn_x + 20, save_btn_y + 6, "Save", COLOR_DARK_TEXT);
// Draw modification indicator
if (file_modified) { 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 // Editor background
draw_rect(win->x + 4, win->y + 54, win->w - 8, win->h - 58, COLOR_DARK_BG); 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 text_start_x = offset_x + 40;
int available_width = content_width - 40; int available_width = content_width - 40;
int max_chars_per_line = available_width / EDITOR_CHAR_WIDTH; int max_chars_per_line = available_width / EDITOR_CHAR_WIDTH;
if (max_chars_per_line < 1) max_chars_per_line = 1; if (max_chars_per_line < 1) max_chars_per_line = 1;
// Draw line numbers and content with word wrapping
int display_line = 0; int display_line = 0;
int visible_lines = (content_height - 55) / EDITOR_LINE_HEIGHT; int visible_lines = (content_height - 55) / EDITOR_LINE_HEIGHT;
int max_display_lines = visible_lines; 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) { while (line_idx < line_count && display_line < max_display_lines) {
int display_y = offset_y + 35 + display_line * EDITOR_LINE_HEIGHT; 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) { if (display_line == 0 || line_idx < line_count) {
// Draw line number
char line_num_str[16]; char line_num_str[16];
int temp = line_idx + 1; int temp = line_idx + 1;
int str_len = 0; int str_len = 0;
@ -298,7 +244,6 @@ static void editor_paint(Window *win) {
line_num_str[str_len++] = (temp % 10) + '0'; line_num_str[str_len++] = (temp % 10) + '0';
temp /= 10; temp /= 10;
} }
// Reverse
for (int j = 0; j < str_len / 2; j++) { for (int j = 0; j < str_len / 2; j++) {
char t = line_num_str[j]; char t = line_num_str[j];
line_num_str[j] = line_num_str[str_len - 1 - 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; 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; const char *text = lines[line_idx].content;
int text_len = lines[line_idx].length; int text_len = lines[line_idx].length;
int char_idx = 0; int char_idx = 0;
int local_display_line = 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) { 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; int current_display_y = offset_y + 35 + display_line * EDITOR_LINE_HEIGHT;
// Extract segment (up to max_chars_per_line)
char segment[256]; char segment[256];
int segment_len = 0; int segment_len = 0;
int segment_start = char_idx; int segment_start = char_idx;
@ -330,7 +273,6 @@ static void editor_paint(Window *win) {
} }
segment[segment_len] = 0; segment[segment_len] = 0;
// Word-based wrapping: find last space if we didn't reach end
if (char_idx < text_len && segment_len > 0) { if (char_idx < text_len && segment_len > 0) {
int last_space = -1; int last_space = -1;
for (int i = segment_len - 1; i >= 0; i--) { for (int i = segment_len - 1; i >= 0; i--) {
@ -339,51 +281,44 @@ static void editor_paint(Window *win) {
break; break;
} }
} }
if (last_space > 0) { if (last_space > 0) {
segment_len = last_space; segment_len = last_space;
segment[segment_len] = 0; segment[segment_len] = 0;
char_idx = segment_start + last_space + 1; char_idx = segment_start + last_space + 1;
// Skip additional spaces
while (char_idx < text_len && text[char_idx] == ' ') { while (char_idx < text_len && text[char_idx] == ' ') {
char_idx++; char_idx++;
} }
} }
} }
// Draw the text segment
if (segment_len > 0) { 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) { if (line_idx == cursor_line) {
int segment_end = segment_start + segment_len; int segment_end = segment_start + segment_len;
bool draw_cursor = false; _Bool draw_cursor = 0;
if (cursor_col >= segment_start && cursor_col < segment_end) { 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) { } else if (cursor_col == text_len && segment_end == text_len) {
draw_cursor = true; draw_cursor = 1;
} }
if (draw_cursor) { if (draw_cursor) {
int cursor_x = text_start_x + ((cursor_col - segment_start) * EDITOR_CHAR_WIDTH); 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++; display_line++;
local_display_line++; local_display_line++;
if (char_idx >= text_len) break; if (char_idx >= text_len) break;
} }
line_idx++; line_idx++;
} }
// Status bar at bottom (modern dark, rounded)
int status_y = offset_y + content_height - 20; int status_y = offset_y + content_height - 20;
draw_rounded_rect_filled(offset_x, status_y, content_width, 20, 6, COLOR_DARK_PANEL); ui_draw_rounded_rect_filled(win, 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_string(win, offset_x + 10, status_y + 5, "Line: ", COLOR_DARK_TEXT);
char line_str[32]; char line_str[32];
int temp = cursor_line + 1; int temp = cursor_line + 1;
@ -399,8 +334,8 @@ static void editor_paint(Window *win) {
} }
line_str[idx] = 0; line_str[idx] = 0;
draw_string(offset_x + 60, status_y + 5, line_str, COLOR_DARK_TEXT); ui_draw_string(win, 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 + 100, status_y + 5, " Col: ", COLOR_DARK_TEXT);
char col_str[32]; char col_str[32];
temp = cursor_col + 1; temp = cursor_col + 1;
@ -416,100 +351,81 @@ static void editor_paint(Window *win) {
} }
col_str[idx] = 0; 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(char c) {
if (c == 17) { // UP
static void editor_handle_key(Window *win, char c) {
// Arrow keys - UP
if (c == 17) {
if (cursor_line > 0) { if (cursor_line > 0) {
cursor_line--; cursor_line--;
if (cursor_col > (int)lines[cursor_line].length) { if (cursor_col > lines[cursor_line].length) cursor_col = lines[cursor_line].length;
cursor_col = lines[cursor_line].length; if (cursor_line < scroll_top) scroll_top = cursor_line;
} }
if (cursor_line < scroll_top) { } else if (c == 18) { // DOWN
scroll_top = cursor_line;
}
}
return;
}
// Arrow keys - DOWN
if (c == 18) {
if (cursor_line < line_count - 1) { if (cursor_line < line_count - 1) {
cursor_line++; cursor_line++;
if (cursor_col > (int)lines[cursor_line].length) { if (cursor_col > lines[cursor_line].length) cursor_col = lines[cursor_line].length;
cursor_col = lines[cursor_line].length;
}
int visible_lines = 20; int visible_lines = 20;
if (cursor_line >= scroll_top + visible_lines) { if (cursor_line >= scroll_top + visible_lines) scroll_top = cursor_line - visible_lines + 1;
scroll_top = cursor_line - visible_lines + 1;
} }
} } else if (c == 19) { // LEFT
return;
}
// Arrow keys - LEFT
if (c == 19) {
if (cursor_col > 0) { if (cursor_col > 0) {
cursor_col--; cursor_col--;
} else if (cursor_line > 0) { } else if (cursor_line > 0) {
cursor_line--; cursor_line--;
cursor_col = lines[cursor_line].length; cursor_col = lines[cursor_line].length;
} }
return; } else if (c == 20) { // RIGHT
} if (cursor_col < lines[cursor_line].length) {
// Arrow keys - RIGHT
if (c == 20) {
if (cursor_col < (int)lines[cursor_line].length) {
cursor_col++; cursor_col++;
} else if (cursor_line < line_count - 1) { } else if (cursor_line < line_count - 1) {
cursor_line++; cursor_line++;
cursor_col = 0; cursor_col = 0;
} }
return; } else {
}
// Regular character input
editor_insert_char(c); editor_insert_char(c);
}
} }
// === Click Handler === static void editor_handle_click(int x, int y) {
int content_width = win_w - 8;
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
int button_x = 4 + content_width - 80; int button_x = 4 + content_width - 80;
int button_y = 24 + 3; int button_y = 24 + 3;
if (x >= button_x && x < button_x + 70 && if (x >= button_x && x < button_x + 70 && y >= button_y && y < button_y + 20) {
y >= button_y && y < button_y + 20) {
editor_save_file(); 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);
void editor_init(void) { if (!win) return 1;
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(); 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;
} }

BIN
src/kernel/userland/txtedit.elf Executable file

Binary file not shown.

View file

@ -0,0 +1,226 @@
#include "nanojpeg.h"
#include "libc/syscall.h"
#include "libc/libui.h"
#include "libc/stdlib.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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;
}

BIN
src/kernel/userland/viewer.elf Executable file

Binary file not shown.

Binary file not shown.

View file

@ -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 <stddef.h>
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");
}

View file

@ -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

View file

@ -3,22 +3,16 @@
#include "io.h" #include "io.h"
#include "cmd.h" #include "cmd.h"
#include "process.h" #include "process.h"
#include "syscall.h"
#include "cli_apps/cli_utils.h" #include "cli_apps/cli_utils.h"
#include "explorer.h" #include "explorer.h"
#include "editor.h"
#include "markdown.h"
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include "viewer.h"
#include "wallpaper.h" #include "wallpaper.h"
#include "control_panel.h"
#include "about.h" #include "about.h"
#include "minesweeper.h"
#include "fat32.h" #include "fat32.h"
#include "nanojpeg.h" #include "nanojpeg.h"
#include "memory_manager.h" #include "memory_manager.h"
#include "paint.h"
#include "disk.h" #include "disk.h"
extern void serial_write(const char *str); 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 // Find if this icon still exists in the file list
int found_idx = -1; int found_idx = -1;
for (int j = 0; j < file_count; j++) { 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; found_idx = j;
break; break;
} }
@ -529,7 +523,7 @@ static int thumb_cache_next = 0; // Round-robin eviction
static uint32_t* thumb_cache_lookup(const char *path) { static uint32_t* thumb_cache_lookup(const char *path) {
for (int i = 0; i < THUMB_CACHE_SIZE; i++) { 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; 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) { static bool thumb_cache_is_failed(const char *path) {
for (int i = 0; i < THUMB_CACHE_SIZE; i++) { 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; return true;
} }
} }
@ -619,10 +613,10 @@ void draw_image_icon(int x, int y, const char *label) {
uint32_t *thumb = NULL; uint32_t *thumb = NULL;
// Fast path: check hardcoded wallpaper names // Fast path: check hardcoded wallpaper names
if (str_eq(label, "moon.jpg") == 0) thumb = wallpaper_get_thumb(0); 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, "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, "moon") != 0) thumb = wallpaper_get_thumb(0);
else if (str_eq(label, "mountain") == 0) thumb = wallpaper_get_thumb(1); else if (str_eq(label, "mountain") != 0) thumb = wallpaper_get_thumb(1);
if (!thumb) { if (!thumb) {
if (str_ends_with(label, "moon.jpg")) thumb = wallpaper_get_thumb(0); 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 // Draw thumbnail into icon - handle both 100x60 wallpaper thumbs and 48x48 dynamic thumbs
bool is_wallpaper_thumb = false; bool is_wallpaper_thumb = false;
if (str_ends_with(label, "moon.jpg") || str_ends_with(label, "mountain.jpg") || 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; is_wallpaper_thumb = true;
} }
int dst_w = 44, dst_h = 44; int dst_w = 44, dst_h = 44;
@ -1567,7 +1561,9 @@ void wm_handle_click(int x, int y) {
if (item == 0) { // About if (item == 0) { // About
wm_bring_to_front(&win_about); wm_bring_to_front(&win_about);
} else if (item == 1) { // Settings } 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 } else if (item == 2) { // Shutdown
cli_cmd_shutdown(NULL); cli_cmd_shutdown(NULL);
} else if (item == 3) { // Restart } else if (item == 3) { // Restart
@ -1608,10 +1604,6 @@ void wm_handle_click(int x, int y) {
// Reset window state on close // Reset window state on close
if (topmost == &win_explorer) { if (topmost == &win_explorer) {
explorer_reset(); explorer_reset();
} else if (topmost == &win_control_panel) {
control_panel_reset();
} else if (topmost == &win_paint) {
paint_reset();
} }
} else if (y < topmost->y + 30) { } else if (y < topmost->y + 30) {
// Dragging the title bar // Dragging the title bar
@ -1729,11 +1721,6 @@ void wm_handle_right_click(int x, int y) {
// Mouse Down // Mouse Down
drag_start_x = mx; drag_start_x = mx;
drag_start_y = my; drag_start_y = my;
if (win_paint.focused && win_paint.visible) {
paint_reset_last_pos();
}
// Check Dock for app clicks (bottom of screen, floating) // Check Dock for app clicks (bottom of screen, floating)
int dock_h = 60; int dock_h = 60;
int dock_y = sh - dock_h - 6; // Float above bottom 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) { } else if (right && !prev_right) {
wm_handle_right_click(mx, my); 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) { } else if (left && is_dragging && drag_window) {
drag_window->x = mx - drag_offset_x; drag_window->x = mx - drag_offset_x;
drag_window->y = my - drag_offset_y; 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); process_create_elf("/bin/notepad.elf", NULL);
} }
} else if (str_starts_with(start_menu_pending_app, "Editor")) { } 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")) { } else if (str_starts_with(start_menu_pending_app, "Terminal")) {
cmd_reset(); wm_bring_to_front(&win_cmd); cmd_reset(); wm_bring_to_front(&win_cmd);
} else if (str_starts_with(start_menu_pending_app, "Calculator")) { } 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); process_create_elf("/bin/calculator.elf", NULL);
} }
} else if (str_starts_with(start_menu_pending_app, "Minesweeper")) { } 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")) { } 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")) { } 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")) { } else if (str_starts_with(start_menu_pending_app, "About")) {
wm_bring_to_front(&win_about); wm_bring_to_front(&win_about);
} else if (str_starts_with(start_menu_pending_app, "Shutdown")) { } 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")) { } else if (str_ends_with(icon->name, "Calculator.shortcut")) {
process_create_elf("/bin/calculator.elf", NULL); handled = true; process_create_elf("/bin/calculator.elf", NULL); handled = true;
} else if (str_ends_with(icon->name, "Minesweeper.shortcut")) { } 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")) { } 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")) { } else if (str_ends_with(icon->name, "Terminal.shortcut")) {
wm_bring_to_front(&win_cmd); handled = true; wm_bring_to_front(&win_cmd); handled = true;
} else if (str_ends_with(icon->name, "About.shortcut")) { } 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")) { } else if (str_ends_with(icon->name, "Recycle Bin.shortcut")) {
explorer_open_directory("/RecycleBin"); handled = true; explorer_open_directory("/RecycleBin"); handled = true;
} else if (str_ends_with(icon->name, "Paint.shortcut")) { } 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) { if (!handled) {
@ -1927,8 +1917,7 @@ void wm_handle_right_click(int x, int y) {
if (fat32_is_directory(buf)) { if (fat32_is_directory(buf)) {
explorer_open_directory(buf); explorer_open_directory(buf);
} else { } else {
editor_open_file(buf); process_create_elf("/bin/txtedit.elf", buf);
wm_bring_to_front(&win_editor);
} }
pending_desktop_icon_click = -1; pending_desktop_icon_click = -1;
force_redraw = true; force_redraw = true;
@ -1948,16 +1937,13 @@ void wm_handle_right_click(int x, int y) {
if (str_ends_with(icon->name, ".elf")) { if (str_ends_with(icon->name, ".elf")) {
process_create_elf(path, NULL); process_create_elf(path, NULL);
} else if (str_ends_with(icon->name, ".pnt")) { } else if (str_ends_with(icon->name, ".pnt")) {
paint_load(path); process_create_elf("/bin/paint.elf", path);
wm_bring_to_front(&win_paint);
} else if (str_ends_with(icon->name, ".md")) { } else if (str_ends_with(icon->name, ".md")) {
markdown_open_file(path); process_create_elf("/bin/markdown.elf", path);
wm_bring_to_front(&win_markdown);
} else if (str_ends_with(icon->name, ".jpg") || str_ends_with(icon->name, ".JPG")) { } 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 { } else {
editor_open_file(path); process_create_elf("/bin/txtedit.elf", path);
wm_bring_to_front(&win_editor);
} }
} }
} }
@ -2008,7 +1994,7 @@ void wm_handle_right_click(int x, int y) {
if (from_desktop) { if (from_desktop) {
char path[128] = "/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; 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)) { 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) { if (desktop_auto_align && !msg_box_visible) {
int new_idx = -1; int new_idx = -1;
for(int i=0; i<desktop_icon_count; i++) { for(int i=0; i<desktop_icon_count; i++) {
if (str_eq(desktop_icons[i].name, filename) == 0) { if (str_eq(desktop_icons[i].name, filename) != 0) {
new_idx = i; new_idx = i;
break; break;
} }
@ -2075,7 +2061,7 @@ void wm_handle_right_click(int x, int y) {
} }
} else if (!desktop_auto_align && !msg_box_visible) { } else if (!desktop_auto_align && !msg_box_visible) {
for(int i=0; i<desktop_icon_count; i++) { for(int i=0; i<desktop_icon_count; i++) {
if (str_eq(desktop_icons[i].name, filename) == 0) { if (str_eq(desktop_icons[i].name, filename) != 0) {
desktop_icons[i].x = mx - 20; desktop_icons[i].x = mx - 20;
desktop_icons[i].y = my - 20; desktop_icons[i].y = my - 20;
if (desktop_snap_to_grid) { if (desktop_snap_to_grid) {
@ -2096,7 +2082,7 @@ void wm_handle_right_click(int x, int y) {
for(int i=0; i<desktop_icon_count; i++) { for(int i=0; i<desktop_icon_count; i++) {
char path[128] = "/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; 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) { if (str_eq(path, drag_file_path) != 0) {
dragged_idx = i; dragged_idx = i;
break; break;
} }
@ -2173,6 +2159,61 @@ void wm_handle_right_click(int x, int y) {
force_redraw = true; force_redraw = true;
} }
// Send mouse events to userland windows
if (left && !prev_left) {
// Left button pressed - send MOUSE_DOWN 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_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_left = left;
prev_right = right; prev_right = right;
@ -2290,13 +2331,7 @@ void wm_init(void) {
cmd_init(); cmd_init();
explorer_init(); explorer_init();
editor_init();
markdown_init();
control_panel_init();
about_init(); about_init();
minesweeper_init();
paint_init();
viewer_init();
wallpaper_init(); wallpaper_init();
refresh_desktop_icons(); refresh_desktop_icons();
@ -2304,24 +2339,12 @@ void wm_init(void) {
// Initialize z-indices // Initialize z-indices
win_cmd.z_index = 0; win_cmd.z_index = 0;
win_explorer.z_index = 1; win_explorer.z_index = 1;
win_editor.z_index = 2; win_about.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;
all_windows[0] = &win_cmd; all_windows[0] = &win_cmd;
all_windows[1] = &win_explorer; all_windows[1] = &win_explorer;
all_windows[2] = &win_editor; all_windows[2] = &win_about;
all_windows[3] = &win_markdown; window_count = 3;
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;
// Only show Explorer on desktop (initially hidden) // Only show Explorer on desktop (initially hidden)
win_explorer.visible = false; win_explorer.visible = false;
@ -2330,11 +2353,7 @@ void wm_init(void) {
// Rest are hidden initially // Rest are hidden initially
win_cmd.visible = false; win_cmd.visible = false;
win_editor.visible = false;
win_markdown.visible = false;
win_control_panel.visible = false;
win_about.visible = false; win_about.visible = false;
win_minesweeper.visible = false;
force_redraw = true; force_redraw = true;
} }