mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
FS fix
This commit is contained in:
parent
f9d84d434e
commit
8c6d751254
25 changed files with 237 additions and 166 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
16
Makefile
16
Makefile
|
|
@ -32,7 +32,7 @@ LDFLAGS = -m elf_x86_64 -nostdlib -static -pie --no-dynamic-linker \
|
||||||
NASMFLAGS = -f elf64
|
NASMFLAGS = -f elf64
|
||||||
|
|
||||||
# Limine Version
|
# Limine Version
|
||||||
LIMINE_VERSION = 10.8.2
|
LIMINE_VERSION = 7.0.0
|
||||||
LIMINE_URL_BASE = https://github.com/limine-bootloader/limine/raw/v$(LIMINE_VERSION)
|
LIMINE_URL_BASE = https://github.com/limine-bootloader/limine/raw/v$(LIMINE_VERSION)
|
||||||
|
|
||||||
.PHONY: all clean run limine-setup
|
.PHONY: all clean run limine-setup
|
||||||
|
|
@ -86,25 +86,26 @@ $(KERNEL_ELF): $(OBJ_FILES)
|
||||||
$(MAKE) -C $(SRC_DIR)/userland
|
$(MAKE) -C $(SRC_DIR)/userland
|
||||||
|
|
||||||
# Create ISO
|
# Create ISO
|
||||||
$(ISO_IMAGE): $(KERNEL_ELF) limine.conf limine-setup
|
$(ISO_IMAGE): $(KERNEL_ELF) limine.cfg limine-setup
|
||||||
rm -rf $(ISO_DIR)
|
rm -rf $(ISO_DIR)
|
||||||
mkdir -p $(ISO_DIR)
|
mkdir -p $(ISO_DIR)
|
||||||
mkdir -p $(ISO_DIR)/EFI/BOOT
|
mkdir -p $(ISO_DIR)/EFI/BOOT
|
||||||
|
|
||||||
# Copy Kernel and Config
|
# Copy Kernel and Config
|
||||||
cp $(KERNEL_ELF) $(ISO_DIR)/
|
cp $(KERNEL_ELF) $(ISO_DIR)/
|
||||||
# Build ISO limine.conf natively with modules
|
# Build ISO limine.cfg natively with modules
|
||||||
cp limine.conf $(ISO_DIR)/
|
cp limine.cfg $(ISO_DIR)/
|
||||||
mkdir -p $(ISO_DIR)/bin
|
mkdir -p $(ISO_DIR)/bin
|
||||||
@for f in $(SRC_DIR)/userland/*.elf; do \
|
@for f in $(SRC_DIR)/userland/*.elf; do \
|
||||||
if [ -f "$$f" ]; then \
|
if [ -f "$$f" ]; then \
|
||||||
basename=$$(basename "$$f"); \
|
basename=$$(basename "$$f"); \
|
||||||
cp "$$f" $(ISO_DIR)/bin/; \
|
cp "$$f" $(ISO_DIR)/bin/; \
|
||||||
echo " module_path: boot():/bin/$$basename" >> $(ISO_DIR)/limine.conf; \
|
echo " MODULE_PATH=boot:///bin/$$basename" >> $(ISO_DIR)/limine.cfg; \
|
||||||
fi \
|
fi \
|
||||||
done
|
done
|
||||||
|
|
||||||
# Copy README
|
# Copy README
|
||||||
|
@if [ -f README.md ]; then cp README.md $(ISO_DIR)/; fi
|
||||||
|
|
||||||
# Copy Wallpaper (if it exists)
|
# Copy Wallpaper (if it exists)
|
||||||
@if [ -f src/kernel/wallpaper.ppm ]; then cp src/kernel/wallpaper.ppm $(ISO_DIR)/; fi
|
@if [ -f src/kernel/wallpaper.ppm ]; then cp src/kernel/wallpaper.ppm $(ISO_DIR)/; fi
|
||||||
|
|
@ -133,11 +134,8 @@ clean:
|
||||||
rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE)
|
rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE)
|
||||||
|
|
||||||
run: $(ISO_IMAGE)
|
run: $(ISO_IMAGE)
|
||||||
dd if=/dev/zero of=disk.img bs=1M count=64
|
|
||||||
mformat -i disk.img -F ::
|
|
||||||
qemu-system-x86_64 -m 2G -serial stdio -cdrom $< -boot d \
|
qemu-system-x86_64 -m 2G -serial stdio -cdrom $< -boot d \
|
||||||
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
|
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
|
||||||
-netdev user,id=net0,hostfwd=udp::12345-:12345 -device e1000,netdev=net0 \
|
-netdev user,id=net0,hostfwd=udp::12345-:12345 -device e1000,netdev=net0 \
|
||||||
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
|
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
|
||||||
-drive file=disk.img,format=raw \
|
|
||||||
-no-reboot -d int -D qemu-debug.log
|
|
||||||
|
|
|
||||||
BIN
boredos.iso
BIN
boredos.iso
Binary file not shown.
BIN
build/cmd.o
BIN
build/cmd.o
Binary file not shown.
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/idt.o
BIN
build/idt.o
Binary file not shown.
BIN
build/main.o
BIN
build/main.o
Binary file not shown.
Binary file not shown.
BIN
build/platform.o
BIN
build/platform.o
Binary file not shown.
BIN
disk.img
BIN
disk.img
Binary file not shown.
2
limine
2
limine
|
|
@ -1 +1 @@
|
||||||
Subproject commit 38ff2c855aabb92e4cfa2cc7ef0c8af665ecba94
|
Subproject commit 36783ec43af4db4841a1e80770355fd1e98f091b
|
||||||
8
limine.cfg
Normal file
8
limine.cfg
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
TIMEOUT=3
|
||||||
|
TERM_PALETTE=ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff
|
||||||
|
TERM_BACKGROUND=00000000
|
||||||
|
WALLPAPER=splash.jpg
|
||||||
|
|
||||||
|
:BoredOS
|
||||||
|
PROTOCOL=limine
|
||||||
|
KERNEL_PATH=boot:///boredos.elf
|
||||||
11
limine.conf
11
limine.conf
|
|
@ -1,11 +0,0 @@
|
||||||
timeout: 3
|
|
||||||
|
|
||||||
# Graphics mode and Splash screen
|
|
||||||
term_palette: ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff
|
|
||||||
term_background: #00000000
|
|
||||||
wallpaper: boot():/splash.jpg
|
|
||||||
|
|
||||||
/BoredOS
|
|
||||||
protocol: limine
|
|
||||||
|
|
||||||
kernel_path: boot():/boredos.elf
|
|
||||||
|
|
@ -731,50 +731,6 @@ static const CommandEntry commands[] = {
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper to build full path with cmd window's drive context
|
|
||||||
static void cmd_build_full_path(const char *relative_path, char *full_path) {
|
|
||||||
if (!cmd_state) {
|
|
||||||
if (relative_path[0]) {
|
|
||||||
cmd_strcpy(full_path, relative_path);
|
|
||||||
} else {
|
|
||||||
full_path[0] = 'A';
|
|
||||||
full_path[1] = ':';
|
|
||||||
full_path[2] = '/';
|
|
||||||
full_path[3] = 0;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If path already has drive letter, use it as-is
|
|
||||||
if (relative_path && relative_path[1] == ':') {
|
|
||||||
cmd_strcpy(full_path, relative_path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build path with cmd_state's drive and directory
|
|
||||||
int i = 0;
|
|
||||||
full_path[i++] = cmd_state->current_drive;
|
|
||||||
full_path[i++] = ':';
|
|
||||||
|
|
||||||
// Add current directory
|
|
||||||
const char *dir = cmd_state->current_dir;
|
|
||||||
while (*dir && i < 509) {
|
|
||||||
full_path[i++] = *dir++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add path argument
|
|
||||||
if (relative_path && relative_path[0]) {
|
|
||||||
if (i > 2 && full_path[i-1] != '/') {
|
|
||||||
full_path[i++] = '/';
|
|
||||||
}
|
|
||||||
const char *p = relative_path;
|
|
||||||
while (*p && i < 509) {
|
|
||||||
full_path[i++] = *p++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
full_path[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to sync cmd window directory after cd
|
// Helper to sync cmd window directory after cd
|
||||||
static void cmd_update_dir(const char *path) {
|
static void cmd_update_dir(const char *path) {
|
||||||
|
|
|
||||||
|
|
@ -93,11 +93,12 @@ static int ata_identify(uint16_t port_base, bool slave) {
|
||||||
if (status == 0) return 0;
|
if (status == 0) return 0;
|
||||||
|
|
||||||
// Wait until BSY clears
|
// Wait until BSY clears
|
||||||
while (inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) {
|
int timeout = 10000;
|
||||||
// Simple timeout could be added here
|
while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0) {
|
||||||
status = inb(port_base + ATA_REG_STATUS);
|
status = inb(port_base + ATA_REG_STATUS);
|
||||||
if (status == 0) return 0; // Check again
|
if (status == 0) return 0; // Check again
|
||||||
}
|
}
|
||||||
|
if (timeout <= 0) return 0; // Hardware didn't respond
|
||||||
|
|
||||||
// Check for error
|
// Check for error
|
||||||
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) {
|
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) {
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ static int dropdown_menu_item_height = 25;
|
||||||
#define CONTEXT_MENU_ITEM_HEIGHT 25
|
#define CONTEXT_MENU_ITEM_HEIGHT 25
|
||||||
|
|
||||||
// Clipboard state
|
// Clipboard state
|
||||||
static char clipboard_path[256] = "";
|
static char clipboard_path[FAT32_MAX_PATH] = "";
|
||||||
static int clipboard_action = 0; // 0=None, 1=Copy, 2=Cut
|
static int clipboard_action = 0; // 0=None, 1=Copy, 2=Cut
|
||||||
#define FILE_CONTEXT_ITEMS 2 // "Open with Text Editor" and "Open with Markdown Viewer"
|
#define FILE_CONTEXT_ITEMS 2 // "Open with Text Editor" and "Open with Markdown Viewer"
|
||||||
|
|
||||||
|
|
@ -259,7 +259,7 @@ static void dialog_confirm_create_file(Window *win) {
|
||||||
|
|
||||||
if (!check_desktop_limit_explorer(win)) return;
|
if (!check_desktop_limit_explorer(win)) return;
|
||||||
|
|
||||||
char full_path[256];
|
char full_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(full_path, state->dialog_creation_path);
|
explorer_strcpy(full_path, state->dialog_creation_path);
|
||||||
if (full_path[explorer_strlen(full_path) - 1] != '/') {
|
if (full_path[explorer_strlen(full_path) - 1] != '/') {
|
||||||
explorer_strcat(full_path, "/");
|
explorer_strcat(full_path, "/");
|
||||||
|
|
@ -283,7 +283,7 @@ static void dialog_confirm_create_file(Window *win) {
|
||||||
|
|
||||||
static void dialog_force_create_file(Window *win) {
|
static void dialog_force_create_file(Window *win) {
|
||||||
ExplorerState *state = (ExplorerState*)win->data;
|
ExplorerState *state = (ExplorerState*)win->data;
|
||||||
char full_path[256];
|
char full_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(full_path, state->dialog_creation_path);
|
explorer_strcpy(full_path, state->dialog_creation_path);
|
||||||
if (full_path[explorer_strlen(full_path) - 1] != '/') {
|
if (full_path[explorer_strlen(full_path) - 1] != '/') {
|
||||||
explorer_strcat(full_path, "/");
|
explorer_strcat(full_path, "/");
|
||||||
|
|
@ -304,7 +304,7 @@ static void dialog_confirm_create_folder(Window *win) {
|
||||||
|
|
||||||
if (!check_desktop_limit_explorer(win)) return;
|
if (!check_desktop_limit_explorer(win)) return;
|
||||||
|
|
||||||
char full_path[256];
|
char full_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(full_path, state->dialog_creation_path);
|
explorer_strcpy(full_path, state->dialog_creation_path);
|
||||||
if (full_path[explorer_strlen(full_path) - 1] != '/') {
|
if (full_path[explorer_strlen(full_path) - 1] != '/') {
|
||||||
explorer_strcat(full_path, "/");
|
explorer_strcat(full_path, "/");
|
||||||
|
|
@ -331,7 +331,7 @@ bool explorer_delete_permanently(const char *path) {
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
if (explorer_strcmp(entries[i].name, ".") == 0 || explorer_strcmp(entries[i].name, "..") == 0) continue;
|
if (explorer_strcmp(entries[i].name, ".") == 0 || explorer_strcmp(entries[i].name, "..") == 0) continue;
|
||||||
|
|
||||||
char child_path[256];
|
char child_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(child_path, path);
|
explorer_strcpy(child_path, path);
|
||||||
if (child_path[explorer_strlen(child_path) - 1] != '/') {
|
if (child_path[explorer_strlen(child_path) - 1] != '/') {
|
||||||
explorer_strcat(child_path, "/");
|
explorer_strcat(child_path, "/");
|
||||||
|
|
@ -379,13 +379,13 @@ bool explorer_delete_recursive(const char *path) {
|
||||||
drive_prefix[0] = path[0];
|
drive_prefix[0] = path[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
char dest_path[256];
|
char dest_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(dest_path, drive_prefix);
|
explorer_strcpy(dest_path, drive_prefix);
|
||||||
explorer_strcat(dest_path, "/RecycleBin/");
|
explorer_strcat(dest_path, "/RecycleBin/");
|
||||||
explorer_strcat(dest_path, filename);
|
explorer_strcat(dest_path, filename);
|
||||||
|
|
||||||
// Save origin
|
// Save origin
|
||||||
char origin_path[256];
|
char origin_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(origin_path, dest_path);
|
explorer_strcpy(origin_path, dest_path);
|
||||||
explorer_strcat(origin_path, ".origin");
|
explorer_strcat(origin_path, ".origin");
|
||||||
FAT32_FileHandle *fh = fat32_open(origin_path, "w");
|
FAT32_FileHandle *fh = fat32_open(origin_path, "w");
|
||||||
|
|
@ -446,7 +446,7 @@ static bool explorer_copy_recursive(const char *src_path, const char *dest_path)
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
if (explorer_strcmp(files[i].name, ".") == 0 || explorer_strcmp(files[i].name, "..") == 0) continue;
|
if (explorer_strcmp(files[i].name, ".") == 0 || explorer_strcmp(files[i].name, "..") == 0) continue;
|
||||||
|
|
||||||
char s_sub[256], d_sub[256];
|
char s_sub[FAT32_MAX_PATH], d_sub[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(s_sub, src_path);
|
explorer_strcpy(s_sub, src_path);
|
||||||
if (s_sub[explorer_strlen(s_sub)-1] != '/') explorer_strcat(s_sub, "/");
|
if (s_sub[explorer_strlen(s_sub)-1] != '/') explorer_strcat(s_sub, "/");
|
||||||
explorer_strcat(s_sub, files[i].name);
|
explorer_strcat(s_sub, files[i].name);
|
||||||
|
|
@ -482,7 +482,7 @@ static bool explorer_copy_recursive(const char *src_path, const char *dest_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void explorer_copy_file_internal(const char *src_path, const char *dest_dir) {
|
static void explorer_copy_file_internal(const char *src_path, const char *dest_dir) {
|
||||||
char filename[256];
|
char filename[FAT32_MAX_FILENAME];
|
||||||
int len = explorer_strlen(src_path);
|
int len = explorer_strlen(src_path);
|
||||||
int i = len - 1;
|
int i = len - 1;
|
||||||
while (i >= 0 && src_path[i] != '/') i--;
|
while (i >= 0 && src_path[i] != '/') i--;
|
||||||
|
|
@ -490,7 +490,7 @@ static void explorer_copy_file_internal(const char *src_path, const char *dest_d
|
||||||
for (int k = i + 1; k < len; k++) filename[j++] = src_path[k];
|
for (int k = i + 1; k < len; k++) filename[j++] = src_path[k];
|
||||||
filename[j] = 0;
|
filename[j] = 0;
|
||||||
|
|
||||||
char dest_path[256];
|
char dest_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(dest_path, dest_dir);
|
explorer_strcpy(dest_path, dest_dir);
|
||||||
if (dest_path[explorer_strlen(dest_path) - 1] != '/') {
|
if (dest_path[explorer_strlen(dest_path) - 1] != '/') {
|
||||||
explorer_strcat(dest_path, "/");
|
explorer_strcat(dest_path, "/");
|
||||||
|
|
@ -565,7 +565,7 @@ void explorer_create_shortcut(Window *win, const char *target_path) {
|
||||||
for (int k = i + 1; k < len; k++) filename[j++] = target_path[k];
|
for (int k = i + 1; k < len; k++) filename[j++] = target_path[k];
|
||||||
filename[j] = 0;
|
filename[j] = 0;
|
||||||
|
|
||||||
char shortcut_path[256];
|
char shortcut_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(shortcut_path, state->current_path);
|
explorer_strcpy(shortcut_path, state->current_path);
|
||||||
if (shortcut_path[explorer_strlen(shortcut_path)-1] != '/') explorer_strcat(shortcut_path, "/");
|
if (shortcut_path[explorer_strlen(shortcut_path)-1] != '/') explorer_strcat(shortcut_path, "/");
|
||||||
explorer_strcat(shortcut_path, filename);
|
explorer_strcat(shortcut_path, filename);
|
||||||
|
|
@ -649,7 +649,7 @@ static int explorer_build_context_menu(Window *win, ExplorerContextItem *items_o
|
||||||
// === Explorer Logic ===
|
// === Explorer Logic ===
|
||||||
|
|
||||||
static uint32_t explorer_get_folder_color(const char *folder_path) {
|
static uint32_t explorer_get_folder_color(const char *folder_path) {
|
||||||
char color_file_path[256];
|
char color_file_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(color_file_path, folder_path);
|
explorer_strcpy(color_file_path, folder_path);
|
||||||
if (color_file_path[explorer_strlen(color_file_path) - 1] != '/') {
|
if (color_file_path[explorer_strlen(color_file_path) - 1] != '/') {
|
||||||
explorer_strcat(color_file_path, "/");
|
explorer_strcat(color_file_path, "/");
|
||||||
|
|
@ -669,7 +669,7 @@ static uint32_t explorer_get_folder_color(const char *folder_path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void explorer_set_folder_color(const char *folder_path, uint32_t color) {
|
static void explorer_set_folder_color(const char *folder_path, uint32_t color) {
|
||||||
char color_file_path[256];
|
char color_file_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(color_file_path, folder_path);
|
explorer_strcpy(color_file_path, folder_path);
|
||||||
if (color_file_path[explorer_strlen(color_file_path) - 1] != '/') {
|
if (color_file_path[explorer_strlen(color_file_path) - 1] != '/') {
|
||||||
explorer_strcat(color_file_path, "/");
|
explorer_strcat(color_file_path, "/");
|
||||||
|
|
@ -687,19 +687,19 @@ static void explorer_restore_file(Window *win, int item_idx) {
|
||||||
ExplorerState *state = (ExplorerState*)win->data;
|
ExplorerState *state = (ExplorerState*)win->data;
|
||||||
if (item_idx < 0 || item_idx >= state->item_count) return;
|
if (item_idx < 0 || item_idx >= state->item_count) return;
|
||||||
|
|
||||||
char recycle_path[256];
|
char recycle_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(recycle_path, state->current_path);
|
explorer_strcpy(recycle_path, state->current_path);
|
||||||
if (recycle_path[explorer_strlen(recycle_path) - 1] != '/') explorer_strcat(recycle_path, "/");
|
if (recycle_path[explorer_strlen(recycle_path) - 1] != '/') explorer_strcat(recycle_path, "/");
|
||||||
explorer_strcat(recycle_path, state->items[item_idx].name);
|
explorer_strcat(recycle_path, state->items[item_idx].name);
|
||||||
|
|
||||||
char origin_file_path[256];
|
char origin_file_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(origin_file_path, recycle_path);
|
explorer_strcpy(origin_file_path, recycle_path);
|
||||||
explorer_strcat(origin_file_path, ".origin");
|
explorer_strcat(origin_file_path, ".origin");
|
||||||
|
|
||||||
char original_path[256] = {0};
|
char original_path[FAT32_MAX_PATH] = {0};
|
||||||
FAT32_FileHandle *fh = fat32_open(origin_file_path, "r");
|
FAT32_FileHandle *fh = fat32_open(origin_file_path, "r");
|
||||||
if (fh) {
|
if (fh) {
|
||||||
int len = fat32_read(fh, original_path, 255);
|
int len = fat32_read(fh, original_path, FAT32_MAX_PATH - 1);
|
||||||
if (len > 0) original_path[len] = 0;
|
if (len > 0) original_path[len] = 0;
|
||||||
fat32_close(fh);
|
fat32_close(fh);
|
||||||
}
|
}
|
||||||
|
|
@ -725,7 +725,7 @@ static void explorer_load_directory(Window *win, const char *path) {
|
||||||
int count = fat32_list_directory(path, entries, EXPLORER_MAX_FILES);
|
int count = fat32_list_directory(path, entries, EXPLORER_MAX_FILES);
|
||||||
|
|
||||||
state->item_count = 0;
|
state->item_count = 0;
|
||||||
for (int i = 0; i < count && i < EXPLORER_MAX_FILES; i++) {
|
for (int i = 0; i < count && state->item_count < EXPLORER_MAX_FILES; i++) {
|
||||||
// Skip .color files
|
// Skip .color files
|
||||||
if (explorer_strcmp(entries[i].name, ".color") == 0) {
|
if (explorer_strcmp(entries[i].name, ".color") == 0) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -741,8 +741,8 @@ static void explorer_load_directory(Window *win, const char *path) {
|
||||||
state->items[state->item_count].size = entries[i].size;
|
state->items[state->item_count].size = entries[i].size;
|
||||||
|
|
||||||
if (state->items[state->item_count].is_directory) {
|
if (state->items[state->item_count].is_directory) {
|
||||||
char subfolder_path[256];
|
char subfolder_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(subfolder_path, state->current_path);
|
explorer_strcpy(subfolder_path, path);
|
||||||
if (subfolder_path[explorer_strlen(subfolder_path) - 1] != '/') {
|
if (subfolder_path[explorer_strlen(subfolder_path) - 1] != '/') {
|
||||||
explorer_strcat(subfolder_path, "/");
|
explorer_strcat(subfolder_path, "/");
|
||||||
}
|
}
|
||||||
|
|
@ -763,7 +763,7 @@ static void explorer_load_directory(Window *win, const char *path) {
|
||||||
|
|
||||||
static void explorer_navigate_to(Window *win, const char *dirname) {
|
static void explorer_navigate_to(Window *win, const char *dirname) {
|
||||||
ExplorerState *state = (ExplorerState*)win->data;
|
ExplorerState *state = (ExplorerState*)win->data;
|
||||||
char new_path[256];
|
char new_path[FAT32_MAX_PATH];
|
||||||
|
|
||||||
if (explorer_strcmp(dirname, "..") == 0) {
|
if (explorer_strcmp(dirname, "..") == 0) {
|
||||||
// Go to parent directory
|
// Go to parent directory
|
||||||
|
|
@ -827,7 +827,7 @@ static void explorer_open_item(Window *win, int index) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char full_path[256];
|
char full_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(full_path, state->current_path);
|
explorer_strcpy(full_path, state->current_path);
|
||||||
if (full_path[explorer_strlen(full_path) - 1] != '/') {
|
if (full_path[explorer_strlen(full_path) - 1] != '/') {
|
||||||
explorer_strcat(full_path, "/");
|
explorer_strcat(full_path, "/");
|
||||||
|
|
@ -863,7 +863,7 @@ static void explorer_open_item(Window *win, int index) {
|
||||||
// Generic shortcut
|
// Generic shortcut
|
||||||
FAT32_FileHandle *fh = fat32_open(full_path, "r");
|
FAT32_FileHandle *fh = fat32_open(full_path, "r");
|
||||||
if (fh) {
|
if (fh) {
|
||||||
char buf[256];
|
char buf[FAT32_MAX_PATH];
|
||||||
int len = fat32_read(fh, buf, 255);
|
int len = fat32_read(fh, buf, 255);
|
||||||
fat32_close(fh);
|
fat32_close(fh);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
|
|
@ -898,7 +898,7 @@ static void explorer_draw_file_icon(int x, int y, bool is_dir, uint32_t color, c
|
||||||
draw_paint_icon(x + 5, y + 5, "");
|
draw_paint_icon(x + 5, y + 5, "");
|
||||||
} else if (explorer_str_ends_with(filename, ".jpg") || explorer_str_ends_with(filename, ".JPG")) {
|
} else if (explorer_str_ends_with(filename, ".jpg") || explorer_str_ends_with(filename, ".JPG")) {
|
||||||
// Build full path for thumbnail loading
|
// Build full path for thumbnail loading
|
||||||
char full_path[256];
|
char full_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(full_path, current_path);
|
explorer_strcpy(full_path, current_path);
|
||||||
if (full_path[explorer_strlen(full_path) - 1] != '/') explorer_strcat(full_path, "/");
|
if (full_path[explorer_strlen(full_path) - 1] != '/') explorer_strcat(full_path, "/");
|
||||||
explorer_strcat(full_path, filename);
|
explorer_strcat(full_path, filename);
|
||||||
|
|
@ -922,7 +922,7 @@ static void explorer_paint(Window *win) {
|
||||||
draw_rect(offset_x, offset_y, win->w - 8, win->h - 28, COLOR_DARK_BG);
|
draw_rect(offset_x, offset_y, win->w - 8, win->h - 28, COLOR_DARK_BG);
|
||||||
|
|
||||||
// Draw Drive Button (modern rounded style)
|
// Draw Drive Button (modern rounded style)
|
||||||
char drive_label[8];
|
char drive_label[9]; // 8 chars + null
|
||||||
// Extract drive from the window's current_path instead of using global current_drive
|
// Extract drive from the window's current_path instead of using global current_drive
|
||||||
char current_drv = 'A';
|
char current_drv = 'A';
|
||||||
if (state->current_path[0] && state->current_path[1] == ':') {
|
if (state->current_path[0] && state->current_path[1] == ':') {
|
||||||
|
|
@ -939,6 +939,7 @@ static void explorer_paint(Window *win) {
|
||||||
drive_label[5] = 'v';
|
drive_label[5] = 'v';
|
||||||
drive_label[6] = ' ';
|
drive_label[6] = ' ';
|
||||||
drive_label[7] = ']';
|
drive_label[7] = ']';
|
||||||
|
drive_label[8] = 0;
|
||||||
|
|
||||||
// Button at x+4, y+3, w=60 (rounded)
|
// Button at x+4, y+3, w=60 (rounded)
|
||||||
draw_rounded_rect_filled(win->x + 4, offset_y + 3, 60, 22, 5, COLOR_DARK_PANEL);
|
draw_rounded_rect_filled(win->x + 4, offset_y + 3, 60, 22, 5, COLOR_DARK_PANEL);
|
||||||
|
|
@ -1209,7 +1210,7 @@ static void explorer_paint(Window *win) {
|
||||||
if (state->file_context_menu_visible) {
|
if (state->file_context_menu_visible) {
|
||||||
// Convert window-relative coordinates to screen coordinates for drawing
|
// Convert window-relative coordinates to screen coordinates for drawing
|
||||||
int menu_screen_x = win->x + state->file_context_menu_x;
|
int menu_screen_x = win->x + state->file_context_menu_x;
|
||||||
int menu_screen_y = win->y + state->file_context_menu_y;
|
int menu_screen_y = win->y + state->file_context_menu_y + 20; // Adjusted for title bar (20px)
|
||||||
|
|
||||||
ExplorerContextItem menu_items[20];
|
ExplorerContextItem menu_items[20];
|
||||||
int count = explorer_build_context_menu(win, menu_items);
|
int count = explorer_build_context_menu(win, menu_items);
|
||||||
|
|
@ -1250,7 +1251,7 @@ static void explorer_handle_click(Window *win, int x, int y) {
|
||||||
// Handle dialog clicks
|
// Handle dialog clicks
|
||||||
if (state->dialog_state == DIALOG_CREATE_FILE || state->dialog_state == DIALOG_CREATE_FOLDER) {
|
if (state->dialog_state == DIALOG_CREATE_FILE || state->dialog_state == DIALOG_CREATE_FOLDER) {
|
||||||
int dlg_x = win->w / 2 - 150;
|
int dlg_x = win->w / 2 - 150;
|
||||||
int dlg_y = win->h / 2 - 60;
|
int dlg_y = win->h / 2 - 80;
|
||||||
|
|
||||||
// Create button
|
// Create button
|
||||||
if (x >= dlg_x + 50 && x < dlg_x + 130 &&
|
if (x >= dlg_x + 50 && x < dlg_x + 130 &&
|
||||||
|
|
@ -1279,9 +1280,10 @@ static void explorer_handle_click(Window *win, int x, int y) {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
return; // Clicked inside dialog but not on button - consume click
|
||||||
} else if (state->dialog_state == DIALOG_DELETE_CONFIRM) {
|
} else if (state->dialog_state == DIALOG_DELETE_CONFIRM) {
|
||||||
int dlg_x = win->w / 2 - 150;
|
int dlg_x = win->w / 2 - 150;
|
||||||
int dlg_y = win->h / 2 - 60;
|
int dlg_y = win->h / 2 - 80; // Adjusted for title bar offset
|
||||||
|
|
||||||
// Delete button
|
// Delete button
|
||||||
if (x >= dlg_x + 50 && x < dlg_x + 130 &&
|
if (x >= dlg_x + 50 && x < dlg_x + 130 &&
|
||||||
|
|
@ -1296,9 +1298,10 @@ static void explorer_handle_click(Window *win, int x, int y) {
|
||||||
dialog_close(win);
|
dialog_close(win);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
return; // Consume click
|
||||||
} else if (state->dialog_state == DIALOG_REPLACE_CONFIRM) {
|
} else if (state->dialog_state == DIALOG_REPLACE_CONFIRM) {
|
||||||
int dlg_x = win->w / 2 - 150;
|
int dlg_x = win->w / 2 - 150;
|
||||||
int dlg_y = win->h / 2 - 60;
|
int dlg_y = win->h / 2 - 80;
|
||||||
|
|
||||||
if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 70 && y < dlg_y + 95) {
|
if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 70 && y < dlg_y + 95) {
|
||||||
dialog_confirm_replace(win);
|
dialog_confirm_replace(win);
|
||||||
|
|
@ -1309,9 +1312,10 @@ static void explorer_handle_click(Window *win, int x, int y) {
|
||||||
dialog_close(win);
|
dialog_close(win);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
} else if (state->dialog_state == DIALOG_REPLACE_MOVE_CONFIRM) {
|
} else if (state->dialog_state == DIALOG_REPLACE_MOVE_CONFIRM) {
|
||||||
int dlg_x = win->w / 2 - 150;
|
int dlg_x = win->w / 2 - 150;
|
||||||
int dlg_y = win->h / 2 - 60;
|
int dlg_y = win->h / 2 - 80;
|
||||||
|
|
||||||
if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 70 && y < dlg_y + 95) {
|
if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 70 && y < dlg_y + 95) {
|
||||||
dialog_confirm_replace_move(win);
|
dialog_confirm_replace_move(win);
|
||||||
|
|
@ -1322,9 +1326,10 @@ static void explorer_handle_click(Window *win, int x, int y) {
|
||||||
dialog_close(win);
|
dialog_close(win);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
} else if (state->dialog_state == DIALOG_CREATE_REPLACE_CONFIRM) {
|
} else if (state->dialog_state == DIALOG_CREATE_REPLACE_CONFIRM) {
|
||||||
int dlg_x = win->w / 2 - 150;
|
int dlg_x = win->w / 2 - 150;
|
||||||
int dlg_y = win->h / 2 - 60;
|
int dlg_y = win->h / 2 - 80;
|
||||||
|
|
||||||
if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 70 && y < dlg_y + 95) {
|
if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 70 && y < dlg_y + 95) {
|
||||||
dialog_force_create_file(win);
|
dialog_force_create_file(win);
|
||||||
|
|
@ -1335,20 +1340,22 @@ static void explorer_handle_click(Window *win, int x, int y) {
|
||||||
dialog_close(win);
|
dialog_close(win);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
} else if (state->dialog_state == DIALOG_ERROR) {
|
} else if (state->dialog_state == DIALOG_ERROR) {
|
||||||
int dlg_x = win->w / 2 - 150;
|
int dlg_x = win->w / 2 - 150;
|
||||||
int dlg_y = win->h / 2 - 60;
|
int dlg_y = win->h / 2 - 80;
|
||||||
|
|
||||||
if (x >= dlg_x + 110 && x < dlg_x + 190 && y >= dlg_y + 70 && y < dlg_y + 95) {
|
if (x >= dlg_x + 110 && x < dlg_x + 190 && y >= dlg_y + 70 && y < dlg_y + 95) {
|
||||||
dialog_close(win);
|
dialog_close(win);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
} else if (state->dialog_state == DIALOG_RENAME) {
|
} else if (state->dialog_state == DIALOG_RENAME) {
|
||||||
int dlg_x = win->w / 2 - 150;
|
int dlg_x = win->w / 2 - 150;
|
||||||
int dlg_y = win->h / 2 - 60;
|
int dlg_y = win->h / 2 - 80;
|
||||||
|
|
||||||
if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 65 && y < dlg_y + 90) {
|
if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 65 && y < dlg_y + 90) {
|
||||||
char new_path[256];
|
char new_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(new_path, state->current_path);
|
explorer_strcpy(new_path, state->current_path);
|
||||||
if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/");
|
if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/");
|
||||||
explorer_strcat(new_path, state->dialog_input);
|
explorer_strcat(new_path, state->dialog_input);
|
||||||
|
|
@ -1362,6 +1369,15 @@ static void explorer_handle_click(Window *win, int x, int y) {
|
||||||
dialog_close(win);
|
dialog_close(win);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (x >= dlg_x + 10 && x < dlg_x + 290 && y >= dlg_y + 35 && y < dlg_y + 55) {
|
||||||
|
state->dialog_input_cursor = (x - dlg_x - 15) / 8;
|
||||||
|
if (state->dialog_input_cursor > (int)explorer_strlen(state->dialog_input)) {
|
||||||
|
state->dialog_input_cursor = explorer_strlen(state->dialog_input);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return; // Consume click
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Drive Menu Selection
|
// Handle Drive Menu Selection
|
||||||
|
|
@ -1525,7 +1541,7 @@ static void explorer_handle_key(Window *win, char c) {
|
||||||
} else if (state->dialog_state == DIALOG_CREATE_FOLDER) {
|
} else if (state->dialog_state == DIALOG_CREATE_FOLDER) {
|
||||||
dialog_confirm_create_folder(win);
|
dialog_confirm_create_folder(win);
|
||||||
} else if (state->dialog_state == DIALOG_RENAME) {
|
} else if (state->dialog_state == DIALOG_RENAME) {
|
||||||
char new_path[256];
|
char new_path[FAT32_MAX_PATH];
|
||||||
explorer_strcpy(new_path, state->current_path);
|
explorer_strcpy(new_path, state->current_path);
|
||||||
if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/");
|
if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/");
|
||||||
explorer_strcat(new_path, state->dialog_input);
|
explorer_strcat(new_path, state->dialog_input);
|
||||||
|
|
@ -1725,7 +1741,7 @@ static void explorer_handle_file_context_menu_click(Window *win, int x, int y) {
|
||||||
if (clicked_action == 0) return;
|
if (clicked_action == 0) return;
|
||||||
|
|
||||||
// Execute Action
|
// Execute Action
|
||||||
char full_path[256];
|
char full_path[FAT32_MAX_PATH];
|
||||||
if (state->file_context_menu_item >= 0) {
|
if (state->file_context_menu_item >= 0) {
|
||||||
explorer_strcpy(full_path, state->current_path);
|
explorer_strcpy(full_path, state->current_path);
|
||||||
if (full_path[explorer_strlen(full_path) - 1] != '/') explorer_strcat(full_path, "/");
|
if (full_path[explorer_strlen(full_path) - 1] != '/') explorer_strcat(full_path, "/");
|
||||||
|
|
@ -1943,6 +1959,8 @@ Window* explorer_create_window(const char *path) {
|
||||||
|
|
||||||
Window *win = (Window*)kmalloc(sizeof(Window));
|
Window *win = (Window*)kmalloc(sizeof(Window));
|
||||||
ExplorerState *state = (ExplorerState*)kmalloc(sizeof(ExplorerState));
|
ExplorerState *state = (ExplorerState*)kmalloc(sizeof(ExplorerState));
|
||||||
|
if (!state || !win) return NULL;
|
||||||
|
mem_memset(state, 0, sizeof(ExplorerState));
|
||||||
|
|
||||||
win->title = "Files";
|
win->title = "Files";
|
||||||
win->x = 300 + (explorer_win_count * 30);
|
win->x = 300 + (explorer_win_count * 30);
|
||||||
|
|
@ -1962,9 +1980,6 @@ Window* explorer_create_window(const char *path) {
|
||||||
state->last_clicked_item = -1;
|
state->last_clicked_item = -1;
|
||||||
state->explorer_scroll_row = 0;
|
state->explorer_scroll_row = 0;
|
||||||
state->dialog_state = DIALOG_NONE;
|
state->dialog_state = DIALOG_NONE;
|
||||||
state->dropdown_menu_visible = false;
|
|
||||||
state->drive_menu_visible = false;
|
|
||||||
state->file_context_menu_visible = false;
|
|
||||||
|
|
||||||
explorer_wins[explorer_win_count++] = win;
|
explorer_wins[explorer_win_count++] = win;
|
||||||
wm_add_window(win);
|
wm_add_window(win);
|
||||||
|
|
@ -1977,6 +1992,9 @@ Window* explorer_create_window(const char *path) {
|
||||||
|
|
||||||
void explorer_init(void) {
|
void explorer_init(void) {
|
||||||
ExplorerState *state = (ExplorerState*)kmalloc(sizeof(ExplorerState));
|
ExplorerState *state = (ExplorerState*)kmalloc(sizeof(ExplorerState));
|
||||||
|
if (!state) return;
|
||||||
|
mem_memset(state, 0, sizeof(ExplorerState));
|
||||||
|
|
||||||
win_explorer.title = "Files";
|
win_explorer.title = "Files";
|
||||||
win_explorer.x = 300;
|
win_explorer.x = 300;
|
||||||
win_explorer.y = 100;
|
win_explorer.y = 100;
|
||||||
|
|
@ -1991,7 +2009,11 @@ void explorer_init(void) {
|
||||||
win_explorer.handle_right_click = explorer_handle_right_click;
|
win_explorer.handle_right_click = explorer_handle_right_click;
|
||||||
win_explorer.data = state;
|
win_explorer.data = state;
|
||||||
|
|
||||||
state->drive_menu_visible = false;
|
state->selected_item = -1;
|
||||||
|
state->last_clicked_item = -1;
|
||||||
|
state->explorer_scroll_row = 0;
|
||||||
|
state->dialog_state = DIALOG_NONE;
|
||||||
|
|
||||||
explorer_wins[explorer_win_count++] = &win_explorer;
|
explorer_wins[explorer_win_count++] = &win_explorer;
|
||||||
explorer_load_directory(&win_explorer, "A:/");
|
explorer_load_directory(&win_explorer, "A:/");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#define EXPLORER_H
|
#define EXPLORER_H
|
||||||
|
|
||||||
#include "wm.h"
|
#include "wm.h"
|
||||||
|
#include "fat32.h"
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
// External windows references (for opening other apps)
|
// External windows references (for opening other apps)
|
||||||
|
|
@ -15,7 +16,7 @@ extern Window win_markdown;
|
||||||
#define DIALOG_INPUT_MAX 256
|
#define DIALOG_INPUT_MAX 256
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char name[256];
|
char name[FAT32_MAX_FILENAME];
|
||||||
bool is_directory;
|
bool is_directory;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t color;
|
uint32_t color;
|
||||||
|
|
@ -25,7 +26,7 @@ typedef struct {
|
||||||
ExplorerItem items[EXPLORER_MAX_FILES];
|
ExplorerItem items[EXPLORER_MAX_FILES];
|
||||||
int item_count;
|
int item_count;
|
||||||
int selected_item;
|
int selected_item;
|
||||||
char current_path[256];
|
char current_path[FAT32_MAX_PATH];
|
||||||
int last_clicked_item;
|
int last_clicked_item;
|
||||||
uint32_t last_click_time;
|
uint32_t last_click_time;
|
||||||
int explorer_scroll_row;
|
int explorer_scroll_row;
|
||||||
|
|
@ -34,11 +35,11 @@ typedef struct {
|
||||||
int dialog_state;
|
int dialog_state;
|
||||||
char dialog_input[DIALOG_INPUT_MAX];
|
char dialog_input[DIALOG_INPUT_MAX];
|
||||||
int dialog_input_cursor;
|
int dialog_input_cursor;
|
||||||
char dialog_target_path[256];
|
char dialog_target_path[FAT32_MAX_PATH];
|
||||||
bool dialog_target_is_dir;
|
bool dialog_target_is_dir;
|
||||||
char dialog_dest_dir[256];
|
char dialog_dest_dir[FAT32_MAX_PATH];
|
||||||
char dialog_creation_path[256];
|
char dialog_creation_path[FAT32_MAX_PATH];
|
||||||
char dialog_move_src[256];
|
char dialog_move_src[FAT32_MAX_PATH];
|
||||||
|
|
||||||
// Dropdown menu state
|
// Dropdown menu state
|
||||||
bool dropdown_menu_visible;
|
bool dropdown_menu_visible;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include "disk.h"
|
#include "disk.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include "wm.h"
|
||||||
|
|
||||||
|
|
||||||
#define MAX_FILES 256
|
#define MAX_FILES 256
|
||||||
|
|
@ -87,14 +88,8 @@ static void fs_strcat(char *dest, const char *src) {
|
||||||
fs_strcpy(dest, src);
|
fs_strcpy(dest, src);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool fs_ends_with(const char *str, const char *suffix) {
|
|
||||||
int str_len = fs_strlen(str);
|
|
||||||
int suffix_len = fs_strlen(suffix);
|
|
||||||
if (suffix_len > str_len) return false;
|
|
||||||
return fs_strcmp(str + str_len - suffix_len, suffix) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool fs_starts_with(const char *str, const char *prefix) {
|
bool fs_starts_with(const char *str, const char *prefix) {
|
||||||
while (*prefix) {
|
while (*prefix) {
|
||||||
if (*prefix++ != *str++) return false;
|
if (*prefix++ != *str++) return false;
|
||||||
}
|
}
|
||||||
|
|
@ -147,8 +142,6 @@ static char parse_drive_from_path(const char **path_ptr) {
|
||||||
|
|
||||||
// Normalize path (remove .., ., etc)
|
// Normalize path (remove .., ., etc)
|
||||||
void fat32_normalize_path(const char *path, char *normalized) {
|
void fat32_normalize_path(const char *path, char *normalized) {
|
||||||
|
|
||||||
|
|
||||||
char temp[FAT32_MAX_PATH];
|
char temp[FAT32_MAX_PATH];
|
||||||
int temp_len = 0;
|
int temp_len = 0;
|
||||||
const char *p = path;
|
const char *p = path;
|
||||||
|
|
@ -167,9 +160,9 @@ void fat32_normalize_path(const char *path, char *normalized) {
|
||||||
while (p[i]) {
|
while (p[i]) {
|
||||||
while (p[i] == '/') i++;
|
while (p[i] == '/') i++;
|
||||||
if (!p[i]) break;
|
if (!p[i]) break;
|
||||||
char component[256];
|
char component[FAT32_MAX_FILENAME];
|
||||||
int j = 0;
|
int j = 0;
|
||||||
while (p[i] && p[i] != '/' && j < 255) {
|
while (p[i] && p[i] != '/' && j < (FAT32_MAX_FILENAME - 1)) {
|
||||||
component[j++] = p[i++];
|
component[j++] = p[i++];
|
||||||
}
|
}
|
||||||
component[j] = 0;
|
component[j] = 0;
|
||||||
|
|
@ -183,12 +176,15 @@ void fat32_normalize_path(const char *path, char *normalized) {
|
||||||
temp[temp_len] = 0;
|
temp[temp_len] = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (temp[temp_len - 1] != '/') {
|
int comp_len = fs_strlen(component);
|
||||||
temp[temp_len++] = '/';
|
if (temp_len + comp_len + 2 < FAT32_MAX_PATH) {
|
||||||
temp[temp_len] = 0;
|
if (temp[temp_len - 1] != '/') {
|
||||||
|
temp[temp_len++] = '/';
|
||||||
|
temp[temp_len] = 0;
|
||||||
|
}
|
||||||
|
fs_strcat(temp, component);
|
||||||
|
temp_len = fs_strlen(temp);
|
||||||
}
|
}
|
||||||
fs_strcat(temp, component);
|
|
||||||
temp_len = fs_strlen(temp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (temp_len > 1 && temp[temp_len - 1] == '/') temp[--temp_len] = 0;
|
if (temp_len > 1 && temp[temp_len - 1] == '/') temp[--temp_len] = 0;
|
||||||
|
|
@ -368,6 +364,7 @@ static int ramfs_write(FAT32_FileHandle *handle, const void *buffer, int size) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wm_notify_fs_change();
|
||||||
return bytes_written;
|
return bytes_written;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1137,6 +1134,17 @@ static int realfs_list_directory(char drive, const char *path, FAT32_FileInfo *e
|
||||||
// === Public API (Dispatch) ===
|
// === Public API (Dispatch) ===
|
||||||
|
|
||||||
void fat32_init(void) {
|
void fat32_init(void) {
|
||||||
|
// Explicitly zero out all structures for RAMFS safety
|
||||||
|
for (int i = 0; i < MAX_FILES; i++) {
|
||||||
|
files[i].used = false;
|
||||||
|
files[i].full_path[0] = 0;
|
||||||
|
files[i].filename[0] = 0;
|
||||||
|
files[i].parent_path[0] = 0;
|
||||||
|
files[i].size = 0;
|
||||||
|
files[i].attributes = 0;
|
||||||
|
files[i].start_cluster = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize FAT table for RAMFS
|
// Initialize FAT table for RAMFS
|
||||||
for (int i = 0; i < MAX_CLUSTERS; i++) {
|
for (int i = 0; i < MAX_CLUSTERS; i++) {
|
||||||
fat_table[i] = 0;
|
fat_table[i] = 0;
|
||||||
|
|
@ -1144,6 +1152,13 @@ void fat32_init(void) {
|
||||||
fat_table[0] = 0xFFFFFFF8;
|
fat_table[0] = 0xFFFFFFF8;
|
||||||
fat_table[1] = 0xFFFFFFFF;
|
fat_table[1] = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
// Zero out cluster data
|
||||||
|
for (int i = 0; i < MAX_CLUSTERS; i++) {
|
||||||
|
for (int j = 0; j < FAT32_CLUSTER_SIZE; j++) {
|
||||||
|
cluster_data[i][j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create root directory entry for RAMFS
|
// Create root directory entry for RAMFS
|
||||||
FileEntry *root = ramfs_find_free_entry();
|
FileEntry *root = ramfs_find_free_entry();
|
||||||
if (root) {
|
if (root) {
|
||||||
|
|
@ -1353,6 +1368,7 @@ bool fat32_mkdir(const char *path) {
|
||||||
entry->size = 0;
|
entry->size = 0;
|
||||||
entry->attributes = ATTR_DIRECTORY;
|
entry->attributes = ATTR_DIRECTORY;
|
||||||
|
|
||||||
|
wm_notify_fs_change();
|
||||||
asm volatile("push %0; popfq" : : "r"(rflags));
|
asm volatile("push %0; popfq" : : "r"(rflags));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1372,6 +1388,7 @@ bool fat32_rmdir(const char *path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
entry->used = false;
|
entry->used = false;
|
||||||
|
wm_notify_fs_change();
|
||||||
asm volatile("push %0; popfq" : : "r"(rflags));
|
asm volatile("push %0; popfq" : : "r"(rflags));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1397,6 +1414,7 @@ bool fat32_delete(const char *path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
entry->used = false;
|
entry->used = false;
|
||||||
|
wm_notify_fs_change();
|
||||||
result = true;
|
result = true;
|
||||||
} else {
|
} else {
|
||||||
// Real FAT32 deletion
|
// Real FAT32 deletion
|
||||||
|
|
@ -1445,7 +1463,7 @@ bool fat32_rename(const char *old_path, const char *new_path) {
|
||||||
// Check destination
|
// Check destination
|
||||||
if (ramfs_find_file(new_path)) { asm volatile("push %0; popfq" : : "r"(rflags)); return false; }
|
if (ramfs_find_file(new_path)) { asm volatile("push %0; popfq" : : "r"(rflags)); return false; }
|
||||||
|
|
||||||
int old_len = fs_strlen(old_path);
|
size_t old_len = fs_strlen(old_path);
|
||||||
// Logic from original rename...
|
// Logic from original rename...
|
||||||
for (int i = 0; i < MAX_FILES; i++) {
|
for (int i = 0; i < MAX_FILES; i++) {
|
||||||
if (!files[i].used) continue;
|
if (!files[i].used) continue;
|
||||||
|
|
@ -1472,6 +1490,7 @@ bool fat32_rename(const char *old_path, const char *new_path) {
|
||||||
fs_strcat(files[i].parent_path, suffix);
|
fs_strcat(files[i].parent_path, suffix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wm_notify_fs_change();
|
||||||
asm volatile("push %0; popfq" : : "r"(rflags));
|
asm volatile("push %0; popfq" : : "r"(rflags));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1516,16 +1535,14 @@ int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entr
|
||||||
if (drive == 'A') {
|
if (drive == 'A') {
|
||||||
char normalized[FAT32_MAX_PATH];
|
char normalized[FAT32_MAX_PATH];
|
||||||
fat32_normalize_path(p, normalized);
|
fat32_normalize_path(p, normalized);
|
||||||
FileEntry *dir = ramfs_find_file(normalized);
|
|
||||||
if (dir && (dir->attributes & ATTR_DIRECTORY)) {
|
for (int i = 0; i < MAX_FILES && count < max_entries; i++) {
|
||||||
for (int i = 0; i < MAX_FILES && count < max_entries; i++) {
|
if (files[i].used && fs_strcmp(files[i].parent_path, normalized) == 0) {
|
||||||
if (files[i].used && fs_strcmp(files[i].parent_path, normalized) == 0) {
|
fs_strcpy(entries[count].name, files[i].filename);
|
||||||
fs_strcpy(entries[count].name, files[i].filename);
|
entries[count].size = files[i].size;
|
||||||
entries[count].size = files[i].size;
|
entries[count].is_directory = (files[i].attributes & ATTR_DIRECTORY) != 0;
|
||||||
entries[count].is_directory = (files[i].attributes & ATTR_DIRECTORY) != 0;
|
entries[count].start_cluster = files[i].start_cluster;
|
||||||
entries[count].start_cluster = files[i].start_cluster;
|
count++;
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ char fat32_get_current_drive(void);
|
||||||
|
|
||||||
// Utilities
|
// Utilities
|
||||||
void fat32_normalize_path(const char *path, char *normalized);
|
void fat32_normalize_path(const char *path, char *normalized);
|
||||||
|
bool fs_starts_with(const char *str, const char *prefix);
|
||||||
|
|
||||||
// Desktop Limit
|
// Desktop Limit
|
||||||
void fat32_set_desktop_limit(int limit);
|
void fat32_set_desktop_limit(int limit);
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,19 @@ void serial_write(const char *str) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void serial_write_num(uint32_t n) {
|
||||||
|
if (n >= 10) serial_write_num(n / 10);
|
||||||
|
while ((inb(0x3F8 + 5) & 0x20) == 0);
|
||||||
|
outb(0x3F8, '0' + (n % 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
void serial_write_hex(uint64_t n) {
|
||||||
|
char *hex = "0123456789ABCDEF";
|
||||||
|
if (n >= 16) serial_write_hex(n / 16);
|
||||||
|
while ((inb(0x3F8 + 5) & 0x20) == 0);
|
||||||
|
outb(0x3F8, hex[n % 16]);
|
||||||
|
}
|
||||||
|
|
||||||
// Kernel Entry Point
|
// Kernel Entry Point
|
||||||
void kmain(void) {
|
void kmain(void) {
|
||||||
init_serial();
|
init_serial();
|
||||||
|
|
@ -83,11 +96,38 @@ void kmain(void) {
|
||||||
platform_init();
|
platform_init();
|
||||||
serial_write("[DEBUG] platform_init OK\n");
|
serial_write("[DEBUG] platform_init OK\n");
|
||||||
|
|
||||||
|
extern uint64_t hhdm_offset;
|
||||||
|
extern uint64_t kernel_phys_base;
|
||||||
|
extern uint64_t kernel_virt_base;
|
||||||
|
|
||||||
|
serial_write("[DEBUG] HHDM Offset: 0x");
|
||||||
|
serial_write_hex(hhdm_offset);
|
||||||
|
serial_write("\n");
|
||||||
|
serial_write("[DEBUG] Kernel Phys: 0x");
|
||||||
|
serial_write_hex(kernel_phys_base);
|
||||||
|
serial_write("\n");
|
||||||
|
serial_write("[DEBUG] Kernel Virt: 0x");
|
||||||
|
serial_write_hex(kernel_virt_base);
|
||||||
|
serial_write("\n");
|
||||||
|
|
||||||
uint64_t heap_phys_addr = 0;
|
uint64_t heap_phys_addr = 0;
|
||||||
size_t heap_size = 0;
|
size_t heap_size = 0;
|
||||||
if (memmap_request.response != NULL) {
|
if (memmap_request.response != NULL) {
|
||||||
|
serial_write("[DEBUG] Memory Map entries: ");
|
||||||
|
serial_write_num(memmap_request.response->entry_count);
|
||||||
|
serial_write("\n");
|
||||||
for (uint64_t i = 0; i < memmap_request.response->entry_count; i++) {
|
for (uint64_t i = 0; i < memmap_request.response->entry_count; i++) {
|
||||||
struct limine_memmap_entry *entry = memmap_request.response->entries[i];
|
struct limine_memmap_entry *entry = memmap_request.response->entries[i];
|
||||||
|
serial_write("[DEBUG] Map entry ");
|
||||||
|
serial_write_num(i);
|
||||||
|
serial_write(": base=");
|
||||||
|
serial_write_hex(entry->base);
|
||||||
|
serial_write(" len=");
|
||||||
|
serial_write_hex(entry->length);
|
||||||
|
serial_write(" type=");
|
||||||
|
serial_write_num(entry->type);
|
||||||
|
serial_write("\n");
|
||||||
|
|
||||||
if (entry->type == LIMINE_MEMMAP_USABLE) {
|
if (entry->type == LIMINE_MEMMAP_USABLE) {
|
||||||
if (entry->length > heap_size) {
|
if (entry->length > heap_size) {
|
||||||
heap_size = entry->length;
|
heap_size = entry->length;
|
||||||
|
|
@ -100,10 +140,15 @@ void kmain(void) {
|
||||||
if (heap_size > 512 * 1024 * 1024) heap_size = 512 * 1024 * 1024;
|
if (heap_size > 512 * 1024 * 1024) heap_size = 512 * 1024 * 1024;
|
||||||
|
|
||||||
if (heap_phys_addr != 0) {
|
if (heap_phys_addr != 0) {
|
||||||
|
serial_write("[DEBUG] Selected heap base (Phys): 0x");
|
||||||
|
serial_write_hex(heap_phys_addr);
|
||||||
|
serial_write(", Size: ");
|
||||||
|
serial_write_num(heap_size / 1024 / 1024);
|
||||||
|
serial_write(" MB\n");
|
||||||
memory_manager_init_at((void*)p2v(heap_phys_addr), heap_size);
|
memory_manager_init_at((void*)p2v(heap_phys_addr), heap_size);
|
||||||
serial_write("[DEBUG] memory_manager_init OK\n");
|
serial_write("[DEBUG] memory_manager_init OK\n");
|
||||||
} else {
|
} else {
|
||||||
serial_write("[DEBUG] ERROR: No usable memory for heap!\n");
|
serial_write("[DEBUG] ERROR: No usable memory for heap! Check Limine memmap.\n");
|
||||||
hcf();
|
hcf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,26 +180,51 @@ void kmain(void) {
|
||||||
serial_write("[DEBUG] Skipping user mode test, proceeding with normal boot.\n");
|
serial_write("[DEBUG] Skipping user mode test, proceeding with normal boot.\n");
|
||||||
|
|
||||||
fat32_init();
|
fat32_init();
|
||||||
if (module_request.response != NULL) {
|
serial_write("[DEBUG] fat32_init OK\n");
|
||||||
|
fat32_mkdir("/bin");
|
||||||
|
serial_write("[DEBUG] /bin directory created/checked\n");
|
||||||
|
|
||||||
|
if (module_request.response == NULL) {
|
||||||
|
serial_write("[DEBUG] ERROR: Limine Module Response is NULL!\n");
|
||||||
|
} else {
|
||||||
|
serial_write("[DEBUG] Limine Module Response found. Count: ");
|
||||||
|
serial_write_num(module_request.response->module_count);
|
||||||
|
serial_write("\n");
|
||||||
for (uint64_t i = 0; i < module_request.response->module_count; i++) {
|
for (uint64_t i = 0; i < module_request.response->module_count; i++) {
|
||||||
struct limine_file *mod = module_request.response->modules[i];
|
struct limine_file *mod = module_request.response->modules[i];
|
||||||
|
|
||||||
FAT32_FileHandle *fh = fat32_open(mod->path, "w");
|
serial_write("[DEBUG] Found module: ");
|
||||||
|
serial_write(mod->path);
|
||||||
|
serial_write(" adr=0x");
|
||||||
|
serial_write_hex((uint64_t)mod->address);
|
||||||
|
serial_write(" size=");
|
||||||
|
serial_write_num(mod->size);
|
||||||
|
serial_write("\n");
|
||||||
|
|
||||||
|
const char *clean_path = mod->path;
|
||||||
|
// Strip boot():/ or boot:/// prefixes common in different Limine versions
|
||||||
|
if (fs_starts_with(clean_path, "boot():")) clean_path += 7;
|
||||||
|
else if (fs_starts_with(clean_path, "boot:///")) clean_path += 8;
|
||||||
|
|
||||||
|
serial_write("[DEBUG] Stripped module path: ");
|
||||||
|
serial_write(clean_path);
|
||||||
|
serial_write("\n");
|
||||||
|
|
||||||
|
FAT32_FileHandle *fh = fat32_open(clean_path, "w");
|
||||||
if (fh && fh->valid) {
|
if (fh && fh->valid) {
|
||||||
fat32_write(fh, mod->address, mod->size);
|
int written = fat32_write(fh, mod->address, mod->size);
|
||||||
fat32_close(fh);
|
fat32_close(fh);
|
||||||
serial_write("[DEBUG] Limine Module loaded into RAMFS: ");
|
serial_write("[DEBUG] Module successfully copied to RAMFS. Written bytes: ");
|
||||||
serial_write(mod->path);
|
serial_write_num(written);
|
||||||
|
serial_write("\n");
|
||||||
|
} else {
|
||||||
|
serial_write("[DEBUG] ERROR: Failed to create file in RAMFS for module: ");
|
||||||
|
serial_write(clean_path);
|
||||||
serial_write("\n");
|
serial_write("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ENABLE_USER_TEST = 1;
|
|
||||||
#ifdef ENABLE_USER_TEST
|
|
||||||
process_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
asm("cli");
|
asm("cli");
|
||||||
ps2_init();
|
ps2_init();
|
||||||
asm("sti");
|
asm("sti");
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ static volatile struct limine_kernel_address_request kernel_addr_request __attri
|
||||||
.revision = 0,
|
.revision = 0,
|
||||||
.response = NULL
|
.response = NULL
|
||||||
};
|
};
|
||||||
static uint64_t hhdm_offset = 0;
|
uint64_t hhdm_offset = 0;
|
||||||
static uint64_t kernel_phys_base = 0;
|
uint64_t kernel_phys_base = 0;
|
||||||
static uint64_t kernel_virt_base = 0;
|
uint64_t kernel_virt_base = 0;
|
||||||
void platform_init(void) {
|
void platform_init(void) {
|
||||||
if (hhdm_request.response) { hhdm_offset = hhdm_request.response->offset; }
|
if (hhdm_request.response) { hhdm_offset = hhdm_request.response->offset; }
|
||||||
if (kernel_addr_request.response) {
|
if (kernel_addr_request.response) {
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ static int drag_offset_y = 0;
|
||||||
|
|
||||||
// File Dragging State
|
// File Dragging State
|
||||||
bool is_dragging_file = false;
|
bool is_dragging_file = false;
|
||||||
static char drag_file_path[256];
|
static char drag_file_path[FAT32_MAX_PATH];
|
||||||
static int drag_icon_type = 0;
|
static int drag_icon_type = 0;
|
||||||
static int drag_start_x = 0;
|
static int drag_start_x = 0;
|
||||||
static int drag_start_y = 0;
|
static int drag_start_y = 0;
|
||||||
|
|
@ -83,6 +83,8 @@ static bool cursor_visible = true;
|
||||||
static int last_cursor_x = 400;
|
static int last_cursor_x = 400;
|
||||||
static int last_cursor_y = 300;
|
static int last_cursor_y = 300;
|
||||||
|
|
||||||
|
static bool periodic_refresh_pending = false;
|
||||||
|
|
||||||
// --- Desktop State ---
|
// --- Desktop State ---
|
||||||
#define MAX_DESKTOP_ICONS 32
|
#define MAX_DESKTOP_ICONS 32
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -509,7 +511,7 @@ void draw_elf_icon(int x, int y, const char *label) {
|
||||||
#define THUMB_CACHE_SIZE 8
|
#define THUMB_CACHE_SIZE 8
|
||||||
#define THUMB_PIXELS (48 * 48)
|
#define THUMB_PIXELS (48 * 48)
|
||||||
static struct {
|
static struct {
|
||||||
char path[256];
|
char path[FAT32_MAX_PATH];
|
||||||
uint32_t pixels[THUMB_PIXELS];
|
uint32_t pixels[THUMB_PIXELS];
|
||||||
bool valid;
|
bool valid;
|
||||||
bool failed; // Mark as failed so we don't retry
|
bool failed; // Mark as failed so we don't retry
|
||||||
|
|
@ -518,7 +520,7 @@ static int thumb_cache_next = 0; // Round-robin eviction
|
||||||
|
|
||||||
// Deferred Thumbnail Request Queue
|
// Deferred Thumbnail Request Queue
|
||||||
#define THUMB_QUEUE_SIZE 16
|
#define THUMB_QUEUE_SIZE 16
|
||||||
static char thumb_request_queue[THUMB_QUEUE_SIZE][256];
|
static char thumb_request_queue[THUMB_QUEUE_SIZE][FAT32_MAX_PATH];
|
||||||
static int thumb_queue_head = 0;
|
static int thumb_queue_head = 0;
|
||||||
static int thumb_queue_tail = 0;
|
static int thumb_queue_tail = 0;
|
||||||
|
|
||||||
|
|
@ -2318,6 +2320,15 @@ void wm_handle_key(char c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void wm_process_input(void) {
|
void wm_process_input(void) {
|
||||||
|
if (periodic_refresh_pending) {
|
||||||
|
if (!is_dragging && !is_dragging_file) {
|
||||||
|
refresh_desktop_icons();
|
||||||
|
explorer_refresh_all();
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
periodic_refresh_pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t rflags;
|
uint64_t rflags;
|
||||||
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
|
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
|
||||||
while (key_head != key_tail) {
|
while (key_head != key_tail) {
|
||||||
|
|
@ -2399,17 +2410,9 @@ void wm_timer_tick(void) {
|
||||||
timer_ticks++;
|
timer_ticks++;
|
||||||
|
|
||||||
if (!is_dragging && !is_dragging_file) {
|
if (!is_dragging && !is_dragging_file) {
|
||||||
desktop_refresh_timer++;
|
// Periodic refresh removed - now triggered by FS events
|
||||||
if (desktop_refresh_timer >= 300) {
|
|
||||||
refresh_desktop_icons();
|
|
||||||
explorer_refresh_all();
|
|
||||||
desktop_refresh_timer = 0;
|
|
||||||
force_redraw = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static uint8_t last_second = 0xFF;
|
static uint8_t last_second = 0xFF;
|
||||||
|
|
||||||
outb(0x70, 0x00);
|
outb(0x70, 0x00);
|
||||||
|
|
@ -2432,3 +2435,7 @@ void wm_timer_tick(void) {
|
||||||
graphics_clear_dirty();
|
graphics_clear_dirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wm_notify_fs_change(void) {
|
||||||
|
periodic_refresh_pending = true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ void wm_timer_tick(void);
|
||||||
uint32_t wm_get_ticks(void);
|
uint32_t wm_get_ticks(void);
|
||||||
int wm_get_desktop_icon_count(void);
|
int wm_get_desktop_icon_count(void);
|
||||||
void wm_show_message(const char *title, const char *message);
|
void wm_show_message(const char *title, const char *message);
|
||||||
|
void wm_notify_fs_change(void);
|
||||||
|
|
||||||
// Hook for external rendering (e.g. VM overlay)
|
// Hook for external rendering (e.g. VM overlay)
|
||||||
extern void (*wm_custom_paint_hook)(void);
|
extern void (*wm_custom_paint_hook)(void);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue