mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 10:26:59 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36d7137969 | ||
|
|
e939d50be6 | ||
|
|
9a4b7b05ff | ||
|
|
23972f8951 |
@@ -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!
|
||||
|
||||
BIN
brewos.iso
BIN
brewos.iso
Binary file not shown.
BIN
build/about.o
BIN
build/about.o
Binary file not shown.
BIN
build/brewos.elf
BIN
build/brewos.elf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/cmd.o
BIN
build/cmd.o
Binary file not shown.
Binary file not shown.
BIN
build/editor.o
BIN
build/editor.o
Binary file not shown.
BIN
build/explorer.o
BIN
build/explorer.o
Binary file not shown.
BIN
build/fat32.o
BIN
build/fat32.o
Binary file not shown.
BIN
build/graphics.o
BIN
build/graphics.o
Binary file not shown.
BIN
build/markdown.o
BIN
build/markdown.o
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/notepad.o
BIN
build/notepad.o
Binary file not shown.
BIN
build/paint.o
BIN
build/paint.o
Binary file not shown.
BIN
build/tcp.o
BIN
build/tcp.o
Binary file not shown.
BIN
build/wm.o
BIN
build/wm.o
Binary file not shown.
@@ -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.
@@ -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
2
limine
Submodule limine updated: 9e0258b727...efd130dbb6
@@ -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
|
||||
|
||||
@@ -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 : {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
char parsed_content[256];
|
||||
MDLineType type;
|
||||
int indent;
|
||||
md_parse_line(raw_line, parsed_content, &type, &indent);
|
||||
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);
|
||||
md_strcpy(lines[line].content, parsed_content);
|
||||
lines[line].length = md_strlen(parsed_content);
|
||||
lines[line].type = type;
|
||||
lines[line].indent_level = indent;
|
||||
line++;
|
||||
}
|
||||
}
|
||||
|
||||
// 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,16 +251,24 @@ 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;
|
||||
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++;
|
||||
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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
|
||||
419
src/kernel/wm.c
419
src/kernel/wm.c
@@ -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;
|
||||
break;
|
||||
// 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
|
||||
|
||||
int j = 0;
|
||||
while (label[start2 + j] && j < 8) {
|
||||
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; }
|
||||
if (split != -1) {
|
||||
for (int i = 0; i < split; i++) line1[i] = label[i];
|
||||
int start2 = (label[split] == ' ') ? split + 1 : split;
|
||||
int j = 0;
|
||||
while (label[start2 + j] && j < 10) {
|
||||
line2[j] = label[start2 + j];
|
||||
j++;
|
||||
}
|
||||
if (label[start2 + j] != 0) {
|
||||
int t = (j > 8) ? 8 : j;
|
||||
line2[t] = '.'; line2[t+1] = '.'; line2[t+2] = 0;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 10; i++) line1[i] = label[i];
|
||||
int j = 0;
|
||||
while (label[10 + j] && j < 10) {
|
||||
line2[j] = label[10 + j];
|
||||
j++;
|
||||
}
|
||||
if (label[10 + j] != 0) {
|
||||
int t = (j > 8) ? 8 : j;
|
||||
line2[t] = '.'; line2[t+1] = '.'; line2[t+2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 + 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, "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", 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
|
||||
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 (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/");
|
||||
|
||||
if (drag_file_path[0] != ':') {
|
||||
char target_path[256];
|
||||
bool is_dir;
|
||||
// Check if dropped on a folder inside explorer
|
||||
if (explorer_get_file_at(mx, my, target_path, &is_dir) && is_dir) {
|
||||
explorer_import_file_to(drag_file_path, target_path);
|
||||
} else {
|
||||
// Dropped in current dir
|
||||
explorer_import_file(drag_file_path);
|
||||
// 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 (from_desktop) {
|
||||
}
|
||||
|
||||
if (drop_win) {
|
||||
char target_path[256];
|
||||
bool is_dir;
|
||||
// Check if dropped on a folder inside this explorer
|
||||
if (explorer_get_file_at(mx, my, target_path, &is_dir) && is_dir) {
|
||||
explorer_import_file_to(drop_win, drag_file_path, target_path);
|
||||
} else {
|
||||
// Dropped in current dir of this explorer
|
||||
explorer_import_file(drop_win, drag_file_path);
|
||||
}
|
||||
|
||||
if (str_starts_with(drag_file_path, "/Desktop/")) {
|
||||
refresh_desktop_icons();
|
||||
}
|
||||
} else {
|
||||
@@ -1396,27 +1476,52 @@ 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--;
|
||||
s++;
|
||||
int d = 0; while(drag_file_path[s] && d < 63) filename[d++] = drag_file_path[s++];
|
||||
filename[d] = 0;
|
||||
char filename[64];
|
||||
int len = 0; while(drag_file_path[len]) len++;
|
||||
int s = len - 1; while(s >= 0 && drag_file_path[s] != '/') s--;
|
||||
s++;
|
||||
int d = 0; while(drag_file_path[s] && d < 63) filename[d++] = drag_file_path[s++];
|
||||
filename[d] = 0;
|
||||
|
||||
if (desktop_auto_align && !msg_box_visible) {
|
||||
int new_idx = -1;
|
||||
for(int i=0; i<desktop_icon_count; 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} 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') {
|
||||
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 (desktop_dialog_state == 8) { // Rename
|
||||
char old_path[128] = "/Desktop/";
|
||||
char new_path[128] = "/Desktop/";
|
||||
int p=9; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0;
|
||||
p=9; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0;
|
||||
if (fat32_rename(old_path, new_path)) {
|
||||
refresh_desktop_icons();
|
||||
explorer_refresh_all();
|
||||
}
|
||||
} else if (desktop_dialog_state == 1 || desktop_dialog_state == 2) { // Create File/Folder
|
||||
if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) {
|
||||
wm_show_message("Error", "Desktop is full!");
|
||||
} else if (desktop_dialog_input[0] != 0) {
|
||||
char path[128] = "/Desktop/";
|
||||
int p=9; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0;
|
||||
if (desktop_dialog_state == 1) {
|
||||
FAT32_FileHandle *fh = fat32_open(path, "w");
|
||||
if (fh) fat32_close(fh);
|
||||
} else {
|
||||
fat32_mkdir(path);
|
||||
}
|
||||
refresh_desktop_icons();
|
||||
explorer_refresh_all();
|
||||
}
|
||||
}
|
||||
desktop_dialog_state = 0;
|
||||
} else if (c == 27) {
|
||||
desktop_dialog_state = 0;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user