4 Commits

Author SHA1 Message Date
Chris
36d7137969 1.44 2026-02-09 21:00:57 +01:00
Chris
e939d50be6 1.44 Beta
[BUG FIX UPDATE]
Updates until 1.50 will be bug fix updates as 1.50 will be a full release.
Bug fixes for 1.44:
-Stopped the crashing when moving stuff onto the desktop
-Fixed codeblock formatting in the markdown viewer.
2026-02-09 21:00:32 +01:00
Chris
9a4b7b05ff 1.43 Beta 2026-02-09 09:44:37 +01:00
Chris
23972f8951 1.42
Paint added and other bug fixes
2026-02-08 23:17:27 +01:00
49 changed files with 1072 additions and 737 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -1,4 +1,4 @@
# Brew OS 1.42 Beta
# Brew OS 1.44 Beta
BrewOS is now in a Beta stage as i have brought over all apps from brewkernel and have made the DE a lot more usable and stable.
## Brewkernel is now BrewOS!

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.

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,4 @@
# Brew OS 1.42 Beta
# Brew OS 1.44 Beta
BrewOS is now in a Beta stage as i have brought over all apps from brewkernel and have made the DE a lot more usable and stable.
## Brewkernel is now BrewOS!

Binary file not shown.

View File

@@ -1,12 +1,8 @@
# Timeout in seconds that Limine will wait before automatically booting.
TIMEOUT=3
# The entry name that will be displayed in the boot menu.
:BrewOS
# We use the Limine boot protocol.
PROTOCOL=limine
# Path to the kernel to boot. boot:/// represents the partition on which limine.cfg is located.
KERNEL_PATH=boot:///brewos.elf
#FRAMEBUFFER_WIDTH=1280

2
limine

Submodule limine updated: 9e0258b727...efd130dbb6

View File

@@ -1,12 +1,8 @@
# Timeout in seconds that Limine will wait before automatically booting.
TIMEOUT=3
# The entry name that will be displayed in the boot menu.
:BrewOS
# We use the Limine boot protocol.
PROTOCOL=limine
# Path to the kernel to boot. boot:/// represents the partition on which limine.cfg is located.
KERNEL_PATH=boot:///brewos.elf
#FRAMEBUFFER_WIDTH=1280

View File

@@ -1,14 +1,10 @@
/* Tell the linker that we want an x86_64 ELF64 output file */
OUTPUT_FORMAT(elf64-x86-64)
OUTPUT_ARCH(i386:x86-64)
/* We want the symbol _start to be our entry point */
ENTRY(_start)
/* Define the memory layout for the kernel */
SECTIONS
{
/* We want to be loaded in the upper half of memory */
. = 0xffffffff80000000;
.text : {

View File

@@ -14,9 +14,6 @@ Window win_about;
#define COLOR_CYAN_LOGO 0xFF368DF7
static void about_paint(Window *win) {
// Background
draw_rect(win->x + 4, win->y + 24, win->w - 8, win->h - 28, COLOR_LTGRAY);
int offset_x = win->x + 15;
int offset_y = win->y + 35;
@@ -35,7 +32,7 @@ static void about_paint(Window *win) {
// Version info
draw_string(offset_x, offset_y + 105, "BrewOS", COLOR_BLACK);
draw_string(offset_x, offset_y + 120, "BrewOS Version 1.42", COLOR_BLACK);
draw_string(offset_x, offset_y + 120, "BrewOS Version 1.44", COLOR_BLACK);
draw_string(offset_x, offset_y + 135, "Kernel Version 2.3.1", COLOR_BLACK);
// Copyright

View File

@@ -11,7 +11,6 @@ _start:
; Ensure interrupts are disabled
cli
; Setup stack is handled by Limine, but we can re-align if paranoid
; (Limine guarantees 16-byte alignment)
; Call the C kernel entry point

View File

@@ -60,7 +60,6 @@ static void fixed_to_str(long long n, char *buf) {
}
frac_buf[f_idx] = 0;
// If we have a fraction, add it to buffer (reversed first)
if (f_idx > 0) {
for (int i = f_idx - 1; i >= 0; i--) {
temp[pos++] = frac_buf[i];
@@ -247,7 +246,7 @@ static void calculator_click(Window *win, int x, int y) {
}
update_display(win);
wm_paint(); // Request repaint
wm_mark_dirty(win->x, win->y, win->w, win->h);
return;
}
}

View File

@@ -2,6 +2,6 @@
void cli_cmd_brewver(char *args) {
(void)args;
cli_write("BrewOS v1.42 Beta\n");
cli_write("BrewOS v1.44 Beta\n");
cli_write("BrewOS Kernel V2.3.1 Beta\n");
}

View File

@@ -140,7 +140,6 @@ void cli_cmd_udptest(char *args){
void cli_cmd_msgrc(char *args) {
(void)args;
// Reset message count since we are viewing them
cmd_reset_msg_count();
FAT32_FileHandle *fh = fat32_open("messages", "r");

View File

@@ -800,7 +800,7 @@ static void cmd_paint(Window *win) {
int offset_y = win->y + 24;
// Fill background
draw_rect(offset_x, offset_y, win->w - 8, win->h - 28, COLOR_BLACK);
draw_rect(win->x + 4, win->y + 24, win->w - 8, win->h - 28, COLOR_BLACK);
int start_y = offset_y + 4;
int start_x = offset_x + 4;
@@ -957,7 +957,7 @@ static void create_test_files(void) {
fh = fat32_open("README.md", "w");
if (fh) {
const char *content =
"# Brew OS 1.40 Beta\n\n"
"# Brew OS 1.44 Beta\n\n"
"BrewOS is now in a Beta stage as i have brought over all apps from brewkernel and have made the DE a lot more usable and stable.\n"
"## Brewkernel is now BrewOS!\n"
"Brewkernel will from now on be deprecated as it's core became too messy. I have built a less bloated kernel and wrote a DE above it, which is why it is now an OS instead of a kernel (in my opinion).\n\n"

View File

@@ -265,7 +265,7 @@ static void editor_paint(Window *win) {
}
// Fill editor background
draw_rect(offset_x, offset_y + 30, content_width, content_height - 55, COLOR_WHITE);
draw_rect(win->x + 4, win->y + 54, win->w - 8, win->h - 58, COLOR_WHITE);
// Calculate available width for text (accounting for line numbers)
int text_start_x = offset_x + 40;

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,9 @@
#define EXPLORER_H
#include "wm.h"
#include <stddef.h>
// External windows references (for opening other apps)
extern Window win_explorer;
extern Window win_editor;
extern Window win_cmd;
@@ -10,27 +12,73 @@ extern Window win_notepad;
extern Window win_calculator;
extern Window win_markdown;
#define EXPLORER_MAX_FILES 64
#define DIALOG_INPUT_MAX 256
typedef struct {
char name[256];
bool is_directory;
uint32_t size;
uint32_t color;
} ExplorerItem;
typedef struct {
ExplorerItem items[EXPLORER_MAX_FILES];
int item_count;
int selected_item;
char current_path[256];
int last_clicked_item;
uint32_t last_click_time;
int explorer_scroll_row;
// Dialog state
int dialog_state;
char dialog_input[DIALOG_INPUT_MAX];
int dialog_input_cursor;
char dialog_target_path[256];
bool dialog_target_is_dir;
char dialog_dest_dir[256];
char dialog_creation_path[256];
char dialog_move_src[256];
// Dropdown menu state
bool dropdown_menu_visible;
// File context menu state
bool file_context_menu_visible;
int file_context_menu_x;
int file_context_menu_y;
int file_context_menu_item;
} ExplorerState;
void explorer_init(void);
void explorer_reset(void);
void explorer_refresh(void);
void explorer_clear_click_state(void);
void explorer_open_directory(const char *path); // Creates a NEW window
// Drag and Drop support
// This now needs to find WHICH explorer window is under the mouse
bool explorer_get_file_at(int screen_x, int screen_y, char *out_path, bool *is_dir);
void explorer_import_file(const char *source_path);
void explorer_import_file_to(const char *source_path, const char *dest_dir);
void explorer_import_file(Window *win, const char *source_path); // To focused or default
void explorer_import_file_to(Window *win, const char *source_path, const char *dest_dir);
void explorer_refresh(Window *win);
void explorer_refresh_all(void);
void explorer_clear_click_state(Window *win);
// Clipboard
// String Helpers
size_t explorer_strlen(const char *str);
void explorer_strcpy(char *dest, const char *src);
void explorer_strcat(char *dest, const char *src);
// Clipboard (System-wide)
void explorer_clipboard_copy(const char *path);
void explorer_clipboard_cut(const char *path);
void explorer_clipboard_paste(const char *dest_dir);
void explorer_clipboard_paste(Window *win, const char *dest_dir);
bool explorer_clipboard_has_content(void);
// File Operations
bool explorer_delete_permanently(const char *path);
bool explorer_delete_recursive(const char *path);
void explorer_create_shortcut(const char *target_path);
void explorer_open_directory(const char *path);
void explorer_create_shortcut(Window *win, const char *target_path);
#endif

View File

@@ -1,4 +1,6 @@
#include "fat32.h"
#include "memory_manager.h"
#include "io.h"
#include <stdbool.h>
#include <stddef.h>
@@ -116,6 +118,9 @@ static void extract_parent_path(const char *path, char *parent) {
// Normalize path (remove .., ., etc)
void fat32_normalize_path(const char *path, char *normalized) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
char temp[FAT32_MAX_PATH];
int temp_len = 0;
@@ -173,6 +178,7 @@ void fat32_normalize_path(const char *path, char *normalized) {
}
fs_strcpy(normalized, temp);
asm volatile("push %0; popfq" : : "r"(rflags));
}
// Find file entry by path
@@ -237,8 +243,10 @@ static bool check_desktop_limit(const char *normalized_path) {
}
// Count files in /Desktop
FAT32_FileInfo info[256]; // Temp buffer
FAT32_FileInfo *info = (FAT32_FileInfo*)kmalloc(256 * sizeof(FAT32_FileInfo));
if (!info) return true;
int count = fat32_list_directory("/Desktop", info, 256);
kfree(info);
if (count >= desktop_file_limit) return false;
}
return true;
@@ -276,6 +284,9 @@ void fat32_set_desktop_limit(int limit) {
}
FAT32_FileHandle* fat32_open(const char *path, const char *mode) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
char normalized[FAT32_MAX_PATH];
fat32_normalize_path(path, normalized);
@@ -284,24 +295,32 @@ FAT32_FileHandle* fat32_open(const char *path, const char *mode) {
if (mode[0] == 'r') {
// Read mode
if (!entry || (entry->attributes & ATTR_DIRECTORY)) {
asm volatile("push %0; popfq" : : "r"(rflags));
return NULL; // File not found or is directory
}
} else if (mode[0] == 'w' || (mode[0] == 'a')) {
// Write/append mode - create if not exists
if (!entry) {
if (!check_desktop_limit(normalized)) {
asm volatile("push %0; popfq" : : "r"(rflags));
return NULL;
}
entry = find_free_entry();
if (!entry) return NULL;
if (!entry) {
asm volatile("push %0; popfq" : : "r"(rflags));
return NULL;
}
entry->used = true;
fs_strcpy(entry->full_path, normalized);
extract_filename(normalized, entry->filename);
extract_parent_path(normalized, entry->parent_path);
entry->start_cluster = allocate_cluster();
if (!entry->start_cluster) return NULL;
if (!entry->start_cluster) {
asm volatile("push %0; popfq" : : "r"(rflags));
return NULL;
}
entry->size = 0;
entry->attributes = 0; // Regular file
}
@@ -313,7 +332,10 @@ FAT32_FileHandle* fat32_open(const char *path, const char *mode) {
// Find free handle
FAT32_FileHandle *handle = find_free_handle();
if (!handle) return NULL;
if (!handle) {
asm volatile("push %0; popfq" : : "r"(rflags));
return NULL;
}
handle->valid = true;
handle->cluster = entry->start_cluster;
@@ -341,17 +363,24 @@ FAT32_FileHandle* fat32_open(const char *path, const char *mode) {
handle->cluster = current_cluster;
}
asm volatile("push %0; popfq" : : "r"(rflags));
return handle;
}
void fat32_close(FAT32_FileHandle *handle) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
if (handle) {
handle->valid = false;
}
asm volatile("push %0; popfq" : : "r"(rflags));
}
int fat32_read(FAT32_FileHandle *handle, void *buffer, int size) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
if (!handle || !handle->valid || handle->mode != 0) {
asm volatile("push %0; popfq" : : "r"(rflags));
return -1;
}
@@ -388,11 +417,15 @@ int fat32_read(FAT32_FileHandle *handle, void *buffer, int size) {
}
}
asm volatile("push %0; popfq" : : "r"(rflags));
return bytes_read;
}
int fat32_write(FAT32_FileHandle *handle, const void *buffer, int size) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
if (!handle || !handle->valid || (handle->mode != 1 && handle->mode != 2)) {
asm volatile("push %0; popfq" : : "r"(rflags));
return -1;
}
@@ -404,7 +437,10 @@ int fat32_write(FAT32_FileHandle *handle, const void *buffer, int size) {
uint32_t next = fat_table[handle->cluster];
if (next >= 0xFFFFFFF8) {
next = allocate_cluster();
if (!next) return 0;
if (!next) {
asm volatile("push %0; popfq" : : "r"(rflags));
return 0;
}
fat_table[handle->cluster] = next;
}
handle->cluster = next;
@@ -451,11 +487,15 @@ int fat32_write(FAT32_FileHandle *handle, const void *buffer, int size) {
}
}
asm volatile("push %0; popfq" : : "r"(rflags));
return bytes_written;
}
int fat32_seek(FAT32_FileHandle *handle, int offset, int whence) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
if (!handle || !handle->valid) {
asm volatile("push %0; popfq" : : "r"(rflags));
return -1;
}
@@ -474,23 +514,31 @@ int fat32_seek(FAT32_FileHandle *handle, int offset, int whence) {
}
handle->position = new_position;
asm volatile("push %0; popfq" : : "r"(rflags));
return new_position;
}
bool fat32_mkdir(const char *path) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
char normalized[FAT32_MAX_PATH];
fat32_normalize_path(path, normalized);
if (find_file(normalized)) {
asm volatile("push %0; popfq" : : "r"(rflags));
return false; // Already exists
}
if (!check_desktop_limit(normalized)) {
asm volatile("push %0; popfq" : : "r"(rflags));
return false;
}
FileEntry *entry = find_free_entry();
if (!entry) return false;
if (!entry) {
asm volatile("push %0; popfq" : : "r"(rflags));
return false;
}
entry->used = true;
fs_strcpy(entry->full_path, normalized);
@@ -500,43 +548,58 @@ bool fat32_mkdir(const char *path) {
entry->size = 0;
entry->attributes = ATTR_DIRECTORY;
asm volatile("push %0; popfq" : : "r"(rflags));
return true;
}
bool fat32_rmdir(const char *path) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
char normalized[FAT32_MAX_PATH];
fat32_normalize_path(path, normalized);
FileEntry *entry = find_file(normalized);
if (!entry || !(entry->attributes & ATTR_DIRECTORY)) {
asm volatile("push %0; popfq" : : "r"(rflags));
return false;
}
entry->used = false;
asm volatile("push %0; popfq" : : "r"(rflags));
return true;
}
bool fat32_delete(const char *path) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
char normalized[FAT32_MAX_PATH];
fat32_normalize_path(path, normalized);
FileEntry *entry = find_file(normalized);
if (!entry || (entry->attributes & ATTR_DIRECTORY)) {
asm volatile("push %0; popfq" : : "r"(rflags));
return false;
}
entry->used = false;
asm volatile("push %0; popfq" : : "r"(rflags));
return true;
}
bool fat32_exists(const char *path) {
return find_file(path) != NULL;
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
bool res = find_file(path) != NULL;
asm volatile("push %0; popfq" : : "r"(rflags));
return res;
}
bool fat32_rename(const char *old_path, const char *new_path) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
FileEntry *entry = find_file(old_path);
if (!entry) return false;
if (find_file(new_path)) return false; // Destination exists
if (!entry) { asm volatile("push %0; popfq" : : "r"(rflags)); return false; }
if (find_file(new_path)) { asm volatile("push %0; popfq" : : "r"(rflags)); return false; } // Destination exists
int old_len = fs_strlen(old_path);
@@ -572,20 +635,28 @@ bool fat32_rename(const char *old_path, const char *new_path) {
fs_strcat(files[i].parent_path, suffix);
}
}
asm volatile("push %0; popfq" : : "r"(rflags));
return true;
}
bool fat32_is_directory(const char *path) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
FileEntry *entry = find_file(path);
return entry && (entry->attributes & ATTR_DIRECTORY);
bool res = entry && (entry->attributes & ATTR_DIRECTORY);
asm volatile("push %0; popfq" : : "r"(rflags));
return res;
}
int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entries) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
char normalized[FAT32_MAX_PATH];
fat32_normalize_path(path, normalized);
FileEntry *dir = find_file(normalized);
if (!dir || !(dir->attributes & ATTR_DIRECTORY)) {
asm volatile("push %0; popfq" : : "r"(rflags));
return 0; // Not a directory
}
@@ -600,23 +671,30 @@ int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entr
}
}
asm volatile("push %0; popfq" : : "r"(rflags));
return count;
}
bool fat32_chdir(const char *path) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
char normalized[FAT32_MAX_PATH];
fat32_normalize_path(path, normalized);
FileEntry *entry = find_file(normalized);
if (!entry || !(entry->attributes & ATTR_DIRECTORY)) {
asm volatile("push %0; popfq" : : "r"(rflags));
return false;
}
fs_strcpy(current_dir, normalized);
asm volatile("push %0; popfq" : : "r"(rflags));
return true;
}
void fat32_get_current_dir(char *buffer, int size) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
int len = fs_strlen(current_dir);
if (len >= size) len = size - 1;
@@ -624,4 +702,5 @@ void fat32_get_current_dir(char *buffer, int size) {
buffer[i] = current_dir[i];
}
buffer[len] = 0;
asm volatile("push %0; popfq" : : "r"(rflags));
}

View File

@@ -4,7 +4,6 @@
#include <stdint.h>
// Minimal 8x8 font for ASCII 32-127
// Derived from standard VGA font
static const uint8_t font8x8_basic[128][8] = {
// 0-31 Control chars (empty)
{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0},

View File

@@ -1,6 +1,7 @@
#include <stddef.h>
#include "graphics.h"
#include "font.h"
#include "io.h"
static struct limine_framebuffer *g_fb = NULL;
static uint32_t g_bg_color = 0xFF696969; // Dark gray background
@@ -69,6 +70,9 @@ static void merge_dirty_rect(int x, int y, int w, int h) {
}
void graphics_mark_dirty(int x, int y, int w, int h) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
// Clamp to screen bounds
if (x < 0) {
w += x;
@@ -85,9 +89,13 @@ void graphics_mark_dirty(int x, int y, int w, int h) {
h = get_screen_height() - y;
}
if (w <= 0 || h <= 0) return;
if (w <= 0 || h <= 0) {
asm volatile("push %0; popfq" : : "r"(rflags));
return;
}
merge_dirty_rect(x, y, w, h);
asm volatile("push %0; popfq" : : "r"(rflags));
}
void graphics_mark_screen_dirty(void) {
@@ -103,7 +111,10 @@ DirtyRect graphics_get_dirty_rect(void) {
}
void graphics_clear_dirty(void) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
g_dirty.active = false;
asm volatile("push %0; popfq" : : "r"(rflags));
}
void put_pixel(int x, int y, uint32_t color) {

View File

@@ -11,10 +11,7 @@ send_eoi:
push rax
mov al, 0x20
out 0x20, al ; Master PIC
; If IRQ > 7, send to Slave too (Mouse is IRQ 12)
; We'll handle this in the specific wrappers or C code.
; Actually, simpler to do EOI in C or here.
; Let's just do it in C.
pop rax
ret

View File

@@ -116,10 +116,6 @@ static void md_parse_line(const char *raw_line, char *output, MDLineType *type,
*type = MD_LINE_BLOCKQUOTE;
i++;
if (raw_line[i] == ' ') i++;
} else if (raw_line[i] == '`') {
// Code block
*type = MD_LINE_CODE;
i++;
}
// Parse inline formatting and copy content
@@ -215,6 +211,7 @@ void markdown_open_file(const char *filename) {
int line = 0;
int col = 0;
char raw_line[256] = "";
bool in_code_block = false;
for (int i = 0; i < bytes_read && line < MD_MAX_LINES; i++) {
char ch = buffer[i];
@@ -222,19 +219,28 @@ void markdown_open_file(const char *filename) {
if (ch == '\n') {
raw_line[col] = 0;
// Parse the raw line
if (raw_line[0] == '`' && raw_line[1] == '`' && raw_line[2] == '`') {
in_code_block = !in_code_block;
} else {
if (in_code_block) {
md_strcpy(lines[line].content, raw_line);
lines[line].length = md_strlen(raw_line);
lines[line].type = MD_LINE_CODE;
lines[line].indent_level = 0;
line++;
} else {
char parsed_content[256];
MDLineType type;
int indent;
md_parse_line(raw_line, parsed_content, &type, &indent);
// Store parsed line
md_strcpy(lines[line].content, parsed_content);
lines[line].length = md_strlen(parsed_content);
lines[line].type = type;
lines[line].indent_level = indent;
line++;
}
}
col = 0;
raw_line[0] = 0;
} else if (col < 255) {
@@ -245,17 +251,25 @@ void markdown_open_file(const char *filename) {
// Handle last line if no trailing newline
if (col > 0 && line < MD_MAX_LINES) {
raw_line[col] = 0;
if (raw_line[0] == '`' && raw_line[1] == '`' && raw_line[2] == '`') {
} else if (in_code_block) {
md_strcpy(lines[line].content, raw_line);
lines[line].length = md_strlen(raw_line);
lines[line].type = MD_LINE_CODE;
lines[line].indent_level = 0;
line++;
} else {
char parsed_content[256];
MDLineType type;
int indent;
md_parse_line(raw_line, parsed_content, &type, &indent);
md_strcpy(lines[line].content, parsed_content);
lines[line].length = md_strlen(parsed_content);
lines[line].type = type;
lines[line].indent_level = indent;
line++;
}
}
line_count = line;
}
@@ -293,7 +307,7 @@ static void md_paint(Window *win) {
int max_display_lines = usable_content_height / MD_LINE_HEIGHT;
// Draw content background
draw_rect(offset_x, content_start_y, content_width - 20, usable_content_height, COLOR_WHITE);
draw_rect(win->x + 4, content_start_y, win->w - 24, usable_content_height, COLOR_WHITE);
@@ -331,7 +345,7 @@ static void md_paint(Window *win) {
text_color = 0xFF808080; // Gray
break;
case MD_LINE_CODE:
text_color = 0xFF800000; // Dark red
text_color = COLOR_WHITE;
break;
default:
text_color = COLOR_BLACK;
@@ -371,7 +385,6 @@ static void md_paint(Window *win) {
}
line_segment[segment_len] = 0;
// Word-based wrapping: if we didn't reach end of string, find last space
if (char_idx < text_len && segment_len > 0) {
// Look for the last space in the segment
int last_space = -1;
@@ -382,7 +395,6 @@ static void md_paint(Window *win) {
}
}
// If we found a space, break there
if (last_space > 0) {
segment_len = last_space;
line_segment[segment_len] = 0;
@@ -395,6 +407,10 @@ static void md_paint(Window *win) {
}
}
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);
}
// Draw special elements for first wrapped line of this markdown line
if (local_display_line == 0) {
switch (line->type) {
@@ -414,10 +430,6 @@ static void md_paint(Window *win) {
// Draw left border
draw_rect(x_offset - 4, line_y, 2, line_height, 0xFF404080);
break;
case MD_LINE_CODE:
// Draw background for code
draw_rect(x_offset - 2, line_y, (max_chars_per_line * MD_CHAR_WIDTH) + 4, line_height, 0xFFF0F0F0);
break;
default:
break;
}
@@ -438,8 +450,7 @@ static void md_paint(Window *win) {
if (char_idx >= text_len) break;
}
// Move display line forward by the actual number of wrapped lines created
// Each wrapped line uses one MD_LINE_HEIGHT worth of space
display_line += wrapped_line_count;
i++;
@@ -451,8 +462,7 @@ static void md_paint(Window *win) {
static void md_handle_key(Window *win, char c) {
(void)win; // Suppress unused warning
// Handle scrolling with arrow keys and W/S
// 17 = UP arrow, 18 = DOWN arrow (from ps2 keyboard mapping)
if (c == 'w' || c == 'W' || c == 17) { // Page up or UP arrow
scroll_top -= 3;
if (scroll_top < 0) scroll_top = 0;

View File

@@ -148,23 +148,30 @@ void* kmalloc(size_t size) {
memory_manager_init();
}
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
if (size == 0 || size > memory_pool_size) {
asm volatile("push %0; popfq" : : "r"(rflags));
return NULL;
}
// Check if we can allocate
if (total_allocated + size > memory_pool_size) {
asm volatile("push %0; popfq" : : "r"(rflags));
return NULL;
}
// Find free space
void *ptr = find_free_space(size);
if (ptr == NULL) {
asm volatile("push %0; popfq" : : "r"(rflags));
return NULL;
}
// Add block entry
if (block_count >= MAX_ALLOCATIONS) {
asm volatile("push %0; popfq" : : "r"(rflags));
return NULL;
}
@@ -185,6 +192,7 @@ void* kmalloc(size_t size) {
// Clear memory
mem_memset(ptr, 0, size);
asm volatile("push %0; popfq" : : "r"(rflags));
return ptr;
}
@@ -193,6 +201,9 @@ void kfree(void *ptr) {
return;
}
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
// Find and free the block
for (int i = 0; i < block_count; i++) {
if (block_list[i].allocated && block_list[i].address == ptr) {
@@ -204,9 +215,11 @@ void kfree(void *ptr) {
block_list[j] = block_list[j + 1];
}
block_count--;
asm volatile("push %0; popfq" : : "r"(rflags));
return;
}
}
asm volatile("push %0; popfq" : : "r"(rflags));
}
void* krealloc(void *ptr, size_t new_size) {

View File

@@ -146,7 +146,7 @@ static void minesweeper_right_click(Window *win, int x, int y) {
int cell_y = (y - grid_start_y) / CELL_SIZE;
flag_cell(cell_x, cell_y);
wm_paint();
wm_mark_dirty(win->x, win->y, win->w, win->h);
}
}
@@ -211,7 +211,7 @@ static void minesweeper_click(Window *win, int x, int y) {
if (x >= grid_start_x && x < grid_start_x + 90 &&
y >= btn_y && y < btn_y + 24) {
init_game();
wm_paint();
wm_mark_dirty(win->x, win->y, win->w, win->h);
return;
}
@@ -226,7 +226,7 @@ static void minesweeper_click(Window *win, int x, int y) {
reveal_cell(cell_x, cell_y);
wm_paint();
wm_mark_dirty(win->x, win->y, win->w, win->h);
}
}

View File

@@ -24,6 +24,9 @@ static void notepad_ensure_cursor_visible(Window *win) {
}
static void notepad_paint(Window *win) {
// Explicitly draw white background for text
draw_rect(win->x + 4, win->y + 24, win->w - 8, win->h - 28, COLOR_WHITE);
int visual_line = 0;
int current_x = win->x + 8;
int current_y = win->y + 30;

View File

@@ -21,9 +21,6 @@ static void paint_strcpy(char *dest, const char *src) {
}
static void paint_paint(Window *win) {
// Background
draw_rect(win->x + 4, win->y + 24, win->w - 8, win->h - 28, COLOR_LTGRAY);
// Toolbar area
draw_rect(win->x + 10, win->y + 30, 40, win->h - 40, COLOR_GRAY);
draw_bevel_rect(win->x + 10, win->y + 30, 40, win->h - 40, true);

View File

@@ -195,7 +195,6 @@ void tcp_close(tcp_socket_t *sock) {
int tcp_read(tcp_socket_t *sock, char *buffer, int max_len) {
if (!sock) return 0;
// Simple copy of what we have
int count = 0;
for (int i = 0; i < sock->rx_pos && i < max_len; i++) {
buffer[i] = sock->rx_buffer[i];

View File

@@ -51,16 +51,17 @@ static int drag_offset_x = 0;
static int drag_offset_y = 0;
// File Dragging State
static bool is_dragging_file = false;
bool is_dragging_file = false;
static char drag_file_path[256];
static int drag_icon_type = 0; // 0=File, 1=Folder, 2=App
static int drag_start_x = 0;
static int drag_start_y = 0;
static int drag_icon_orig_x = 0;
static int drag_icon_orig_y = 0;
static Window *drag_src_win = NULL;
// Windows array for z-order management
static Window *all_windows[10];
static Window *all_windows[32];
static int window_count = 0;
// Redraw system
@@ -79,7 +80,6 @@ typedef struct {
char name[64];
int x, y;
int type; // 0=File, 1=Folder, 2=App
bool selected;
} DesktopIcon;
static DesktopIcon desktop_icons[MAX_DESKTOP_ICONS];
@@ -165,8 +165,6 @@ static void refresh_desktop_icons(void) {
if (files[i].is_directory) dest->type = 1;
else if (str_ends_with(dest->name, ".shortcut")) dest->type = 2;
else dest->type = 0;
dest->selected = false;
dest->x = -1; // Mark as new for layout
dest->y = -1;
@@ -276,49 +274,55 @@ void wm_show_message(const char *title, const char *message) {
}
static void draw_icon_label(int x, int y, const char *label) {
char line1[10] = {0};
char line2[10] = {0};
char line1[11] = {0}; // 10 chars + null
char line2[11] = {0}; // 10 chars + null
int len = 0; while(label[len]) len++;
if (len <= 8) {
int i=0; while(i<len) { line1[i] = label[i]; i++; }
line1[i] = 0;
if (len <= 10) {
for (int i = 0; i < len; i++) line1[i] = label[i];
} else {
int split = 8;
// Smart wrap: look for separator in first 8 chars (backwards)
// Prioritize keeping extension together (e.g. SEVENCH.MD -> split at 7)
int best_split = -1;
for (int i = 7; i >= 1; i--) {
if (label[i] == ' ' || label[i] == '.') {
best_split = i;
// Dot-based wrap: keep extension together if prefix fits
int dot_pos = -1;
for (int i = len - 1; i >= 0; i--) {
if (label[i] == '.') { dot_pos = i; break; }
}
int split = -1;
if (dot_pos != -1 && dot_pos > 0 && dot_pos <= 10) {
split = dot_pos;
} else {
// Word-based wrap: look for space in the first 11 characters
for (int i = 10; i >= 0; i--) {
if (label[i] == ' ') {
split = i;
break;
}
}
if (best_split != -1) {
split = best_split;
}
// Copy line 1
int i;
for (i = 0; i < split; i++) line1[i] = label[i];
line1[i] = 0;
// Copy line 2
int start2 = split;
if (label[split] == ' ') start2++; // Skip space at start of line 2
if (split != -1) {
for (int i = 0; i < split; i++) line1[i] = label[i];
int start2 = (label[split] == ' ') ? split + 1 : split;
int j = 0;
while (label[start2 + j] && j < 8) {
while (label[start2 + j] && j < 10) {
line2[j] = label[start2 + j];
j++;
}
line2[j] = 0;
// Truncate with .. if longer than 16 (or if line 2 overflows)
if (label[start2 + j] != 0) {
if (j > 6) { line2[6] = '.'; line2[7] = '.'; line2[8] = 0; }
else { line2[j++] = '.'; line2[j++] = '.'; line2[j] = 0; }
int t = (j > 8) ? 8 : j;
line2[t] = '.'; line2[t+1] = '.'; line2[t+2] = 0;
}
} else {
for (int i = 0; i < 10; i++) line1[i] = label[i];
int j = 0;
while (label[10 + j] && j < 10) {
line2[j] = label[10 + j];
j++;
}
if (label[10 + j] != 0) {
int t = (j > 8) ? 8 : j;
line2[t] = '.'; line2[t+1] = '.'; line2[t+2] = 0;
}
}
}
@@ -338,7 +342,6 @@ static void draw_icon_label(int x, int y, const char *label) {
// --- Drawing Helpers ---
// Draw a bevelled box (Win 3.1 style)
void draw_bevel_rect(int x, int y, int w, int h, bool sunken) {
draw_rect(x, y, w, h, COLOR_GRAY);
@@ -377,16 +380,14 @@ void draw_coffee_cup(int x, int y, int size) {
draw_rect(x + cup_w - 2, y + 2, 1, cup_h - 3, COLOR_BLACK); // Right
draw_rect(x + 1, y + cup_h - 1, cup_w - 2, 1, COLOR_BLACK); // Bottom
// Rounded bottom corners
draw_rect(x + 1, y + cup_h - 1, 1, 1, COLOR_LTGRAY);
draw_rect(x + cup_w - 2, y + cup_h - 1, 1, 1, COLOR_LTGRAY);
// Handle - much bigger (on the right side, pointing inward)
draw_rect(x + cup_w, y + 3, 2, 8, COLOR_BLACK);
draw_rect(x + cup_w - 2, y + 3, 2, 1, COLOR_BLACK);
draw_rect(x + cup_w - 2, y + 10, 2, 1, COLOR_BLACK);
// Coffee liquid inside - rainbow Apple logo stripes (blue, green, yellow, red, purple, blue)
int stripe_height = (cup_h - 5) / 6;
int coffee_y = y + 4;
draw_rect(x + 2, coffee_y, cup_w - 4, stripe_height, COLOR_APPLE_BLUE);
@@ -601,7 +602,7 @@ void draw_window(Window *win) {
draw_button(win->x + win->w - 20, win->y + 5, 14, 14, "X", false);
// Client Area
draw_rect(win->x + 4, win->y + 24, win->w - 8, win->h - 28, COLOR_WHITE);
draw_rect(win->x + 4, win->y + 24, win->w - 8, win->h - 28, COLOR_LTGRAY);
if (win->paint) {
win->paint(win);
@@ -696,6 +697,9 @@ void wm_paint(void) {
int sw = get_screen_width();
int sh = get_screen_height();
// Ensure no stale clipping state interferes with the new frame
graphics_clear_clipping();
// First, erase the old cursor (before redrawing anything)
if (cursor_visible) {
erase_cursor(last_cursor_x, last_cursor_y);
@@ -734,14 +738,17 @@ void wm_paint(void) {
else if (str_ends_with(icon->name, ".md")) {
draw_document_icon(icon->x, icon->y, icon->name);
draw_string(icon->x + 31, icon->y + 2, "MD", COLOR_BLACK);
} else if (str_ends_with(icon->name, ".c") || str_ends_with(icon->name, ".C")) {
draw_document_icon(icon->x, icon->y, icon->name);
draw_string(icon->x + 31, icon->y + 2, "C", COLOR_APPLE_BLUE);
}
else draw_document_icon(icon->x, icon->y, icon->name);
}
}
// 3. Windows - sort by z-index and draw
// Simple bubble sort by z-index (5 windows max)
Window *sorted_windows[6];
// Simple bubble sort by z-index
Window *sorted_windows[32];
for (int i = 0; i < window_count; i++) {
sorted_windows[i] = all_windows[i];
}
@@ -803,30 +810,32 @@ void wm_paint(void) {
// Desktop Context Menu
if (desktop_menu_visible) {
int menu_w = 140;
int menu_h = 125; // 5 items * 25
int item_h = 25;
int menu_h = (desktop_menu_target_icon != -1) ? 125 : 75;
draw_rect(desktop_menu_x, desktop_menu_y, menu_w, menu_h, COLOR_LTGRAY);
draw_bevel_rect(desktop_menu_x, desktop_menu_y, menu_w, menu_h, true);
bool can_cut_copy = (desktop_menu_target_icon != -1);
bool can_paste = explorer_clipboard_has_content();
// If target is a file (not folder), paste is disabled
if (desktop_menu_target_icon != -1) {
bool can_paste = explorer_clipboard_has_content();
DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon];
if (icon->type != 1) can_paste = false; // 1 is folder
}
int item_h = 25;
draw_string(desktop_menu_x + 5, desktop_menu_y + 5, "Cut", can_cut_copy ? COLOR_BLACK : COLOR_DKGRAY);
draw_string(desktop_menu_x + 5, desktop_menu_y + 5 + item_h, "Copy", can_cut_copy ? COLOR_BLACK : COLOR_DKGRAY);
draw_string(desktop_menu_x + 5, desktop_menu_y + 5, "Cut", COLOR_BLACK);
draw_string(desktop_menu_x + 5, desktop_menu_y + 5 + item_h, "Copy", COLOR_BLACK);
draw_string(desktop_menu_x + 5, desktop_menu_y + 5 + item_h * 2, "Paste", can_paste ? COLOR_BLACK : COLOR_DKGRAY);
draw_string(desktop_menu_x + 5, desktop_menu_y + 5 + item_h * 3, "Delete", can_cut_copy ? COLOR_RED : COLOR_DKGRAY);
draw_string(desktop_menu_x + 5, desktop_menu_y + 5 + item_h * 4, "Rename", can_cut_copy ? COLOR_BLACK : COLOR_DKGRAY);
draw_string(desktop_menu_x + 5, desktop_menu_y + 5 + item_h * 3, "Delete", COLOR_RED);
draw_string(desktop_menu_x + 5, desktop_menu_y + 5 + item_h * 4, "Rename", COLOR_BLACK);
} else {
bool can_paste = explorer_clipboard_has_content();
draw_string(desktop_menu_x + 5, desktop_menu_y + 5, "New File", COLOR_BLACK);
draw_string(desktop_menu_x + 5, desktop_menu_y + 5 + item_h, "New Folder", COLOR_BLACK);
draw_string(desktop_menu_x + 5, desktop_menu_y + 5 + item_h * 2, "Paste", can_paste ? COLOR_BLACK : COLOR_DKGRAY);
}
}
// Desktop Rename Dialog
if (desktop_dialog_state == 8) {
// Desktop Dialogs
if (desktop_dialog_state != 0) {
int dlg_w = 300; int dlg_h = 110;
int dlg_x = (sw - dlg_w) / 2;
int dlg_y = (sh - dlg_h) / 2;
@@ -834,13 +843,18 @@ void wm_paint(void) {
draw_rect(dlg_x - 5, dlg_y - 5, dlg_w + 10, dlg_h + 10, COLOR_LTGRAY);
draw_bevel_rect(dlg_x, dlg_y, dlg_w, dlg_h, true);
draw_string(dlg_x + 10, dlg_y + 10, "Rename", COLOR_BLACK);
const char *title = "Rename";
const char *btn_text = "Rename";
if (desktop_dialog_state == 1) { title = "Create New File"; btn_text = "Create"; }
else if (desktop_dialog_state == 2) { title = "Create New Folder"; btn_text = "Create"; }
draw_string(dlg_x + 10, dlg_y + 10, title, COLOR_BLACK);
draw_bevel_rect(dlg_x + 10, dlg_y + 35, 280, 20, false);
draw_string(dlg_x + 15, dlg_y + 40, desktop_dialog_input, COLOR_BLACK);
// Cursor
draw_rect(dlg_x + 15 + desktop_dialog_cursor * 8, dlg_y + 39, 2, 12, COLOR_BLACK);
draw_button(dlg_x + 50, dlg_y + 65, 80, 25, "Rename", false);
draw_button(dlg_x + 50, dlg_y + 65, 80, 25, btn_text, false);
draw_button(dlg_x + 170, dlg_y + 65, 80, 25, "Cancel", false);
}
@@ -888,7 +902,7 @@ bool rect_contains(int x, int y, int w, int h, int px, int py) {
return px >= x && px < x + w && py >= y && py < y + h;
}
static void wm_bring_to_front(Window *win) {
void wm_bring_to_front(Window *win) {
// Clear focus from all windows
for (int i = 0; i < window_count; i++) {
all_windows[i]->focused = false;
@@ -905,6 +919,12 @@ static void wm_bring_to_front(Window *win) {
win->z_index = max_z + 1;
}
void wm_add_window(Window *win) {
if (window_count < 32) {
all_windows[window_count++] = win;
}
}
void wm_handle_click(int x, int y) {
int sh = get_screen_height();
int sw = get_screen_width();
@@ -924,7 +944,8 @@ void wm_handle_click(int x, int y) {
// Handle Desktop Context Menu Click
if (desktop_menu_visible) {
int menu_w = 140;
int menu_h = 125;
int menu_h = (desktop_menu_target_icon != -1) ? 125 : 75;
if (rect_contains(desktop_menu_x, desktop_menu_y, menu_w, menu_h, x, y)) {
int rel_y = y - desktop_menu_y;
int item = rel_y / 25;
@@ -939,6 +960,14 @@ void wm_handle_click(int x, int y) {
char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_clipboard_copy(path);
} else if (item == 0 && desktop_menu_target_icon == -1) { // New File
desktop_dialog_state = 1;
desktop_dialog_input[0] = 0;
desktop_dialog_cursor = 0;
} else if (item == 1 && desktop_menu_target_icon == -1) { // New Folder
desktop_dialog_state = 2;
desktop_dialog_input[0] = 0;
desktop_dialog_cursor = 0;
} else if (item == 2) { // Paste
bool can_paste = explorer_clipboard_has_content();
if (desktop_menu_target_icon != -1) {
@@ -947,17 +976,32 @@ void wm_handle_click(int x, int y) {
}
if (can_paste) {
int old_count = desktop_icon_count;
if (desktop_menu_target_icon != -1 && desktop_icons[desktop_menu_target_icon].type == 1) {
// Paste into folder
char path[128] = "/Desktop/";
DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon];
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_clipboard_paste(path);
explorer_clipboard_paste(&win_explorer, path);
} else {
// Paste to desktop
explorer_clipboard_paste("/Desktop");
explorer_clipboard_paste(&win_explorer, "/Desktop");
}
refresh_desktop_icons();
// If auto-align is OFF and we pasted to the background, place at click location
if (!desktop_auto_align && desktop_icon_count > old_count && desktop_menu_target_icon == -1) {
int new_idx = desktop_icon_count - 1;
desktop_icons[new_idx].x = desktop_menu_x - 20;
desktop_icons[new_idx].y = desktop_menu_y - 20;
if (desktop_snap_to_grid) {
int col = (desktop_icons[new_idx].x - 20 + 40) / 80;
int row = (desktop_icons[new_idx].y - 20 + 40) / 80;
if (col < 0) col = 0; if (row < 0) row = 0;
desktop_icons[new_idx].x = 20 + col * 80;
desktop_icons[new_idx].y = 20 + row * 80;
}
}
}
}
else if (item == 3 && desktop_menu_target_icon != -1) { // Delete
@@ -984,15 +1028,35 @@ void wm_handle_click(int x, int y) {
}
// Handle Desktop Dialog Clicks
if (desktop_dialog_state == 8) {
if (desktop_dialog_state != 0) {
int dlg_x = (sw - 300) / 2; int dlg_y = (sh - 110) / 2;
if (rect_contains(dlg_x + 50, dlg_y + 65, 80, 25, x, y)) { // Rename
if (rect_contains(dlg_x + 50, dlg_y + 65, 80, 25, x, y)) { // Confirm
if (desktop_dialog_state == 8) { // Rename
char old_path[128] = "/Desktop/";
char new_path[128] = "/Desktop/";
int p=9; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0;
p=9; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0;
if (fat32_rename(old_path, new_path)) refresh_desktop_icons();
if (fat32_rename(old_path, new_path)) {
refresh_desktop_icons();
explorer_refresh_all();
}
} else if (desktop_dialog_state == 1 || desktop_dialog_state == 2) { // Create File/Folder
if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) {
wm_show_message("Error", "Desktop is full!");
} else if (desktop_dialog_input[0] != 0) {
char path[128] = "/Desktop/";
int p=9; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0;
if (desktop_dialog_state == 1) {
FAT32_FileHandle *fh = fat32_open(path, "w");
if (fh) fat32_close(fh);
} else {
fat32_mkdir(path);
}
refresh_desktop_icons();
explorer_refresh_all();
}
}
desktop_dialog_state = 0;
force_redraw = true;
return;
@@ -1242,7 +1306,19 @@ void wm_handle_right_click(int x, int y) {
if (explorer_get_file_at(drag_start_x, drag_start_y, drag_file_path, &is_dir)) {
is_dragging_file = true;
drag_icon_type = is_dir ? 1 : 0;
explorer_clear_click_state();
drag_src_win = NULL;
// Find which explorer window was clicked to clear its state
for (int w = 0; w < window_count; w++) {
Window *win = all_windows[w];
if (win->visible && rect_contains(win->x, win->y, win->w, win->h, drag_start_x, drag_start_y)) {
// This is a bit of a hack, but we check if it's an explorer window
if (str_starts_with(win->title, "File Explorer")) {
drag_src_win = win;
explorer_clear_click_state(win);
}
}
}
}
}
@@ -1260,7 +1336,7 @@ void wm_handle_right_click(int x, int y) {
if (start_menu_pending_app) {
// Launch App
if (str_starts_with(start_menu_pending_app, "Explorer")) {
explorer_reset(); wm_bring_to_front(&win_explorer);
explorer_open_directory("/");
} else if (str_starts_with(start_menu_pending_app, "Notepad")) {
wm_bring_to_front(&win_notepad);
} else if (str_starts_with(start_menu_pending_app, "Editor")) {
@@ -1308,9 +1384,9 @@ void wm_handle_right_click(int x, int y) {
} else if (str_ends_with(icon->name, "About.shortcut")) {
wm_bring_to_front(&win_about);
} else if (str_ends_with(icon->name, "Explorer.shortcut")) {
explorer_reset(); wm_bring_to_front(&win_explorer);
explorer_open_directory("/");
} else if (str_ends_with(icon->name, "Recycle Bin.shortcut")) {
explorer_open_directory("/RecycleBin"); wm_bring_to_front(&win_explorer);
explorer_open_directory("/RecycleBin");
} else if (str_ends_with(icon->name, "Paint.shortcut")) {
wm_bring_to_front(&win_paint);
}
@@ -1329,7 +1405,6 @@ void wm_handle_right_click(int x, int y) {
buf[len] = 0;
if (fat32_is_directory(buf)) {
explorer_open_directory(buf);
wm_bring_to_front(&win_explorer);
} else {
editor_open_file(buf);
wm_bring_to_front(&win_editor);
@@ -1343,7 +1418,6 @@ void wm_handle_right_click(int x, int y) {
char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_open_directory(path);
wm_bring_to_front(&win_explorer);
} else { // File
char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
@@ -1366,25 +1440,31 @@ void wm_handle_right_click(int x, int y) {
if (is_dragging_file) {
// Drop logic
// Check drop target
if (win_explorer.visible && rect_contains(win_explorer.x, win_explorer.y, win_explorer.w, win_explorer.h, mx, my)) {
// Dropped on Explorer
// If source was desktop, we need to refresh desktop to remove the icon
bool from_desktop = str_starts_with(drag_file_path, "/Desktop/");
// Check drop target - iterate through all windows to find if dropped on an Explorer
Window *drop_win = NULL;
int topmost_z = -1;
for (int w = 0; w < window_count; w++) {
Window *win = all_windows[w];
if (win->visible && rect_contains(win->x, win->y, win->w, win->h, mx, my)) {
if (win->z_index > topmost_z && str_starts_with(win->title, "File Explorer")) {
drop_win = win;
topmost_z = win->z_index;
}
}
}
if (drag_file_path[0] != ':') {
if (drop_win) {
char target_path[256];
bool is_dir;
// Check if dropped on a folder inside explorer
// Check if dropped on a folder inside this explorer
if (explorer_get_file_at(mx, my, target_path, &is_dir) && is_dir) {
explorer_import_file_to(drag_file_path, target_path);
explorer_import_file_to(drop_win, drag_file_path, target_path);
} else {
// Dropped in current dir
explorer_import_file(drag_file_path);
// Dropped in current dir of this explorer
explorer_import_file(drop_win, drag_file_path);
}
}
if (from_desktop) {
if (str_starts_with(drag_file_path, "/Desktop/")) {
refresh_desktop_icons();
}
} else {
@@ -1396,20 +1476,44 @@ void wm_handle_right_click(int x, int y) {
// If source was NOT desktop, move to desktop
// Check if path starts with /Desktop/
bool from_desktop = (drag_file_path[0]=='/' && drag_file_path[1]=='D' && drag_file_path[2]=='e');
if (!from_desktop) {
bool dropped_on_target = false;
for (int i = 0; i < desktop_icon_count; i++) {
if (from_desktop) {
char path[128] = "/Desktop/";
int p=9; int n=0; while(desktop_icons[i].name[n]) path[p++] = desktop_icons[i].name[n++]; path[p]=0;
if (str_eq(path, drag_file_path) == 0) continue;
}
if (rect_contains(desktop_icons[i].x + 20, desktop_icons[i].y, 40, 40, mx, my)) {
if (desktop_icons[i].type == 1) {
char target_path[256] = "/Desktop/";
int p=9; int n=0; while(desktop_icons[i].name[n]) target_path[p++] = desktop_icons[i].name[n++]; target_path[p]=0;
explorer_import_file_to(&win_explorer, drag_file_path, target_path);
refresh_desktop_icons();
dropped_on_target = true;
break;
} else if (desktop_icons[i].type == 2 && str_starts_with(desktop_icons[i].name, "Recycle Bin")) {
explorer_import_file_to(&win_explorer, drag_file_path, "/RecycleBin");
refresh_desktop_icons();
dropped_on_target = true;
break;
} else {
dropped_on_target = true;
break;
}
}
}
if (!dropped_on_target && !from_desktop) {
// Dragged from Explorer to Desktop
// Check limit first
if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) {
wm_show_message("Error", "Desktop is full!");
} else {
explorer_import_file_to(drag_file_path, "/Desktop");
refresh_desktop_icons();
explorer_import_file_to(&win_explorer, drag_file_path, "/Desktop");
}
// Handle insertion at specific position
if (desktop_auto_align && !msg_box_visible) {
// Find the newly added icon (it will be at the end)
// Extract filename from drag_file_path
char filename[64];
int len = 0; while(drag_file_path[len]) len++;
int s = len - 1; while(s >= 0 && drag_file_path[s] != '/') s--;
@@ -1417,6 +1521,7 @@ void wm_handle_right_click(int x, int y) {
int d = 0; while(drag_file_path[s] && d < 63) filename[d++] = drag_file_path[s++];
filename[d] = 0;
if (desktop_auto_align && !msg_box_visible) {
int new_idx = -1;
for(int i=0; i<desktop_icon_count; i++) {
if (str_eq(desktop_icons[i].name, filename) == 0) {
@@ -1441,8 +1546,23 @@ void wm_handle_right_click(int x, int y) {
refresh_desktop_icons(); // Re-apply layout
}
} else if (!desktop_auto_align && !msg_box_visible) {
for(int i=0; i<desktop_icon_count; i++) {
if (str_eq(desktop_icons[i].name, filename) == 0) {
desktop_icons[i].x = mx - 20;
desktop_icons[i].y = my - 20;
if (desktop_snap_to_grid) {
int col = (desktop_icons[i].x - 20 + 40) / 80;
int row = (desktop_icons[i].y - 20 + 40) / 80;
if (col < 0) col = 0; if (row < 0) row = 0;
desktop_icons[i].x = 20 + col * 80;
desktop_icons[i].y = 20 + row * 80;
}
} else {
break;
}
}
}
} else if (!dropped_on_target) {
// Moved within desktop
// Find which icon was dragged
int dragged_idx = -1;
@@ -1456,32 +1576,7 @@ void wm_handle_right_click(int x, int y) {
}
if (dragged_idx != -1) {
// Check for drop on folder (Priority over placement)
bool dropped_on_folder = false;
for (int i = 0; i < desktop_icon_count; i++) {
if (i == dragged_idx) continue;
if (desktop_icons[i].type == 1) { // Folder
// Check if mouse is over this folder icon
if (rect_contains(desktop_icons[i].x + 20, desktop_icons[i].y, 40, 40, mx, my)) {
char target_path[256] = "/Desktop/";
int p=9; int n=0; while(desktop_icons[i].name[n]) target_path[p++] = desktop_icons[i].name[n++]; target_path[p]=0;
explorer_import_file_to(drag_file_path, target_path);
refresh_desktop_icons();
dropped_on_folder = true;
break;
}
} else if (desktop_icons[i].type == 2 && str_starts_with(desktop_icons[i].name, "Recycle Bin")) {
if (rect_contains(desktop_icons[i].x + 20, desktop_icons[i].y, 40, 40, mx, my)) {
// Move to Recycle Bin
explorer_import_file_to(drag_file_path, "/RecycleBin");
refresh_desktop_icons();
dropped_on_folder = true;
break;
}
}
}
if (!dropped_on_folder && desktop_auto_align) {
if (desktop_auto_align) {
int cell_h = 80;
int rel_y = my - 20;
if (rel_y < 0) rel_y = 0;
@@ -1509,7 +1604,7 @@ void wm_handle_right_click(int x, int y) {
}
desktop_icons[target_idx] = temp;
refresh_desktop_icons(); // Re-applies layout
} else if (!dropped_on_folder) {
} else {
desktop_icons[dragged_idx].x = mx - 20;
desktop_icons[dragged_idx].y = my - 20;
if (desktop_snap_to_grid) {
@@ -1571,14 +1666,34 @@ static volatile int key_head = 0;
static volatile int key_tail = 0;
static void wm_dispatch_key(char c) {
if (desktop_dialog_state == 8) {
if (desktop_dialog_state != 0) {
int len = 0; while(desktop_dialog_input[len]) len++;
if (c == '\n') {
if (desktop_dialog_state == 8) { // Rename
char old_path[128] = "/Desktop/";
char new_path[128] = "/Desktop/";
int p=9; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0;
p=9; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0;
if (fat32_rename(old_path, new_path)) refresh_desktop_icons();
if (fat32_rename(old_path, new_path)) {
refresh_desktop_icons();
explorer_refresh_all();
}
} else if (desktop_dialog_state == 1 || desktop_dialog_state == 2) { // Create File/Folder
if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) {
wm_show_message("Error", "Desktop is full!");
} else if (desktop_dialog_input[0] != 0) {
char path[128] = "/Desktop/";
int p=9; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0;
if (desktop_dialog_state == 1) {
FAT32_FileHandle *fh = fat32_open(path, "w");
if (fh) fat32_close(fh);
} else {
fat32_mkdir(path);
}
refresh_desktop_icons();
explorer_refresh_all();
}
}
desktop_dialog_state = 0;
} else if (c == 27) {
desktop_dialog_state = 0;
@@ -1601,13 +1716,12 @@ static void wm_dispatch_key(char c) {
}
Window *target = NULL;
if (win_notepad.focused && win_notepad.visible) target = &win_notepad;
else if (win_cmd.focused && win_cmd.visible) target = &win_cmd;
else if (win_calculator.focused && win_calculator.visible) target = &win_calculator;
else if (win_explorer.focused && win_explorer.visible) target = &win_explorer;
else if (win_editor.focused && win_editor.visible) target = &win_editor;
else if (win_markdown.focused && win_markdown.visible) target = &win_markdown;
else if (win_control_panel.focused && win_control_panel.visible) target = &win_control_panel;
for (int i = 0; i < window_count; i++) {
if (all_windows[i]->focused && all_windows[i]->visible) {
target = all_windows[i];
break;
}
}
if (!target) return;
@@ -1717,6 +1831,7 @@ void wm_timer_tick(void) {
desktop_refresh_timer++;
if (desktop_refresh_timer >= 60) {
refresh_desktop_icons();
explorer_refresh_all();
desktop_refresh_timer = 0;
force_redraw = true;
}

View File

@@ -33,6 +33,7 @@ struct Window {
int cursor_pos;
bool focused;
int z_index; // Layering depth (higher = on top)
void *data; // Per-window private data
// Callbacks
void (*paint)(Window *win);
@@ -47,6 +48,8 @@ void wm_handle_key(char c);
void wm_handle_click(int x, int y);
void wm_handle_right_click(int x, int y);
void wm_process_input(void);
void wm_add_window(Window *win);
void wm_bring_to_front(Window *win);
// Redraw system
void wm_mark_dirty(int x, int y, int w, int h);