mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
V1.63 bug fixes
This commit is contained in:
parent
6e2f0d8c1a
commit
cc950974e8
27 changed files with 348 additions and 69 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -18,3 +18,4 @@ limine 2/Makefile
|
||||||
limine 2/limine
|
limine 2/limine
|
||||||
limine 2/limine.dSYM/Contents/Resources/DWARF/limine
|
limine 2/limine.dSYM/Contents/Resources/DWARF/limine
|
||||||
limine 2/limine.exe
|
limine 2/limine.exe
|
||||||
|
build/*
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# BoredOS 1.51
|
# BoredOS
|
||||||
BoredOS has now exited Beta stage and is "stable enough" to be put out as a "stable" product.
|
BoredOS has now exited Beta stage and is "stable enough" to be put out as a "stable" product.
|
||||||
|
|
||||||
<img src="bored.svg" width="200" /> </br>
|
<img src="bored.svg" width="200" /> </br>
|
||||||
|
|
@ -9,6 +9,7 @@ It features a DE (and WM), a FAT32 filesystem, customizable UI and much much mor
|
||||||
*this screenshot might be outdated*
|
*this screenshot might be outdated*
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
- JPG image support
|
||||||
- Disk manager
|
- Disk manager
|
||||||
- Drag and drop mouse centered UI
|
- Drag and drop mouse centered UI
|
||||||
- Customizable UI
|
- Customizable UI
|
||||||
|
|
|
||||||
BIN
boredos.iso
BIN
boredos.iso
Binary file not shown.
BIN
build/about.o
BIN
build/about.o
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/explorer.o
BIN
build/explorer.o
Binary file not shown.
BIN
build/fat32.o
BIN
build/fat32.o
Binary file not shown.
BIN
build/wm.o
BIN
build/wm.o
Binary file not shown.
BIN
disk.img
BIN
disk.img
Binary file not shown.
|
|
@ -1,4 +1,4 @@
|
||||||
# BoredOS 1.51
|
# BoredOS
|
||||||
BoredOS has now exited Beta stage and is "stable enough" to be put out as a "stable" product.
|
BoredOS has now exited Beta stage and is "stable enough" to be put out as a "stable" product.
|
||||||
|
|
||||||
<img src="bored.svg" width="200" /> </br>
|
<img src="bored.svg" width="200" /> </br>
|
||||||
|
|
@ -9,6 +9,7 @@ It features a DE (and WM), a FAT32 filesystem, customizable UI and much much mor
|
||||||
*this screenshot might be outdated*
|
*this screenshot might be outdated*
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
- JPG image support
|
||||||
- Disk manager
|
- Disk manager
|
||||||
- Drag and drop mouse centered UI
|
- Drag and drop mouse centered UI
|
||||||
- Customizable UI
|
- Customizable UI
|
||||||
|
|
|
||||||
Binary file not shown.
BIN
screenshot.jpg
BIN
screenshot.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 897 KiB After Width: | Height: | Size: 216 KiB |
BIN
src/.DS_Store
vendored
BIN
src/.DS_Store
vendored
Binary file not shown.
BIN
src/kernel/.DS_Store
vendored
BIN
src/kernel/.DS_Store
vendored
Binary file not shown.
|
|
@ -14,8 +14,8 @@ static void about_paint(Window *win) {
|
||||||
|
|
||||||
// Version info
|
// Version info
|
||||||
draw_string(offset_x, offset_y + 105, "BoredOS 'Panda'", COLOR_WHITE);
|
draw_string(offset_x, offset_y + 105, "BoredOS 'Panda'", COLOR_WHITE);
|
||||||
draw_string(offset_x, offset_y + 120, "BoredOS Version 1.62", COLOR_WHITE);
|
draw_string(offset_x, offset_y + 120, "BoredOS Version 1.63", COLOR_WHITE);
|
||||||
draw_string(offset_x, offset_y + 135, "Kernel Version 2.5.2", COLOR_WHITE);
|
draw_string(offset_x, offset_y + 135, "Kernel Version 2.5.3", COLOR_WHITE);
|
||||||
|
|
||||||
// Copyright
|
// Copyright
|
||||||
draw_string(offset_x, offset_y + 150, "(C) 2026 boreddevnl.", COLOR_WHITE);
|
draw_string(offset_x, offset_y + 150, "(C) 2026 boreddevnl.", COLOR_WHITE);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
void cli_cmd_boredver(char *args) {
|
void cli_cmd_boredver(char *args) {
|
||||||
(void)args;
|
(void)args;
|
||||||
cli_write("BoredOS v1.62\n");
|
cli_write("BoredOS v1.63\n");
|
||||||
cli_write("BoredOS Kernel V2.5.2\n");
|
cli_write("BoredOS Kernel V2.5.3\n");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ typedef struct Disk {
|
||||||
DiskType type;
|
DiskType type;
|
||||||
bool is_fat32;
|
bool is_fat32;
|
||||||
char name[32];
|
char name[32];
|
||||||
|
uint32_t partition_lba_offset; // LBA offset of FAT32 partition (0 for raw)
|
||||||
|
|
||||||
// Function pointers for driver operations
|
// Function pointers for driver operations
|
||||||
int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer);
|
int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer);
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,7 @@ void disk_manager_init(void) {
|
||||||
ramdisk->read_sector = ramdisk_read;
|
ramdisk->read_sector = ramdisk_read;
|
||||||
ramdisk->write_sector = ramdisk_write;
|
ramdisk->write_sector = ramdisk_write;
|
||||||
ramdisk->driver_data = NULL;
|
ramdisk->driver_data = NULL;
|
||||||
|
ramdisk->partition_lba_offset = 0;
|
||||||
|
|
||||||
disk_register(ramdisk);
|
disk_register(ramdisk);
|
||||||
}
|
}
|
||||||
|
|
@ -243,25 +244,95 @@ Disk* disk_get_by_index(int index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Check for FAT32 Signature in MBR/VBR
|
// === MBR Partition Table Structures ===
|
||||||
static bool check_fat32_signature(Disk *disk) {
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t status; // 0x80 = bootable, 0x00 = inactive
|
||||||
|
uint8_t chs_first[3]; // CHS of first sector
|
||||||
|
uint8_t type; // Partition type
|
||||||
|
uint8_t chs_last[3]; // CHS of last sector
|
||||||
|
uint32_t lba_start; // LBA of first sector
|
||||||
|
uint32_t sector_count; // Number of sectors
|
||||||
|
} __attribute__((packed)) MBR_PartitionEntry;
|
||||||
|
|
||||||
|
// FAT32 partition type codes
|
||||||
|
#define PART_TYPE_FAT32 0x0B
|
||||||
|
#define PART_TYPE_FAT32_LBA 0x0C
|
||||||
|
|
||||||
|
// Check if sector contains a valid FAT32 BPB (Volume Boot Record)
|
||||||
|
static bool is_fat32_bpb(const uint8_t *sector) {
|
||||||
|
// Must have 0xAA55 boot signature
|
||||||
|
if (sector[510] != 0x55 || sector[511] != 0xAA) return false;
|
||||||
|
|
||||||
|
// Check for FAT32 filesystem string at offset 82
|
||||||
|
// "FAT32 " in the fs_type field of the BPB
|
||||||
|
if (sector[82] == 'F' && sector[83] == 'A' && sector[84] == 'T' &&
|
||||||
|
sector[85] == '3' && sector[86] == '2') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also accept if bytes_per_sector is 512 and sectors_per_fat_16 is 0
|
||||||
|
// (FAT32 always has sectors_per_fat_16 == 0)
|
||||||
|
uint16_t bps = *(uint16_t*)§or[11];
|
||||||
|
uint16_t spf16 = *(uint16_t*)§or[22];
|
||||||
|
uint32_t spf32 = *(uint32_t*)§or[36];
|
||||||
|
if (bps == 512 && spf16 == 0 && spf32 > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse MBR partition table and find a FAT32 partition.
|
||||||
|
// Sets disk->partition_lba_offset and returns true if found.
|
||||||
|
static bool detect_fat32_partition(Disk *disk) {
|
||||||
uint8_t *buffer = (uint8_t*)kmalloc(512);
|
uint8_t *buffer = (uint8_t*)kmalloc(512);
|
||||||
if (!buffer) return false;
|
if (!buffer) return false;
|
||||||
|
|
||||||
// Read Sector 0
|
// Read sector 0 (MBR or raw BPB)
|
||||||
if (disk->read_sector(disk, 0, buffer) != 0) {
|
if (disk->read_sector(disk, 0, buffer) != 0) {
|
||||||
kfree(buffer);
|
kfree(buffer);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check boot signature 0x55 0xAA at offset 510
|
// Must have 0xAA55 boot signature
|
||||||
if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
|
if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
|
||||||
kfree(buffer);
|
kfree(buffer);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check MBR partition table entries (4 entries at offset 446)
|
||||||
|
MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446];
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (partitions[i].type == PART_TYPE_FAT32 ||
|
||||||
|
partitions[i].type == PART_TYPE_FAT32_LBA) {
|
||||||
|
|
||||||
|
uint32_t part_lba = partitions[i].lba_start;
|
||||||
|
|
||||||
|
// Read the partition's first sector to verify it's a valid FAT32 BPB
|
||||||
|
uint8_t *pbuf = (uint8_t*)kmalloc(512);
|
||||||
|
if (!pbuf) { kfree(buffer); return false; }
|
||||||
|
|
||||||
|
if (disk->read_sector(disk, part_lba, pbuf) == 0 && is_fat32_bpb(pbuf)) {
|
||||||
|
disk->partition_lba_offset = part_lba;
|
||||||
|
kfree(pbuf);
|
||||||
kfree(buffer);
|
kfree(buffer);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
kfree(pbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: check if sector 0 itself is a raw FAT32 BPB (no partition table)
|
||||||
|
if (is_fat32_bpb(buffer)) {
|
||||||
|
disk->partition_lba_offset = 0;
|
||||||
|
kfree(buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(buffer);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
|
static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
|
||||||
|
|
@ -279,13 +350,13 @@ static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
|
||||||
new_disk->read_sector = ata_read_sector;
|
new_disk->read_sector = ata_read_sector;
|
||||||
new_disk->write_sector = ata_write_sector;
|
new_disk->write_sector = ata_write_sector;
|
||||||
new_disk->driver_data = data;
|
new_disk->driver_data = data;
|
||||||
|
new_disk->partition_lba_offset = 0;
|
||||||
|
|
||||||
// Check filesystem
|
// Detect FAT32 (with MBR partition support)
|
||||||
if (check_fat32_signature(new_disk)) {
|
if (detect_fat32_partition(new_disk)) {
|
||||||
new_disk->is_fat32 = true;
|
new_disk->is_fat32 = true;
|
||||||
disk_register(new_disk);
|
disk_register(new_disk);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
kfree(data);
|
kfree(data);
|
||||||
kfree(new_disk);
|
kfree(new_disk);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -366,7 +366,14 @@ bool explorer_delete_permanently(const char *path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool explorer_delete_recursive(const char *path) {
|
bool explorer_delete_recursive(const char *path) {
|
||||||
if (explorer_str_starts_with(path, "/RecycleBin")) {
|
// Check if path is on an external drive (not A:)
|
||||||
|
bool is_external = false;
|
||||||
|
if (path[0] && path[1] == ':' && path[0] != 'A' && path[0] != 'a') {
|
||||||
|
is_external = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_external || explorer_str_starts_with(path, "/RecycleBin")) {
|
||||||
|
// External drives have no RecycleBin — delete permanently
|
||||||
return explorer_delete_permanently(path);
|
return explorer_delete_permanently(path);
|
||||||
} else {
|
} else {
|
||||||
// Move to Recycle Bin
|
// Move to Recycle Bin
|
||||||
|
|
@ -885,7 +892,7 @@ static void explorer_open_item(Window *win, int index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw a simple file icon
|
// Draw a simple file icon
|
||||||
static void explorer_draw_file_icon(int x, int y, bool is_dir, uint32_t color, const char *filename) {
|
static void explorer_draw_file_icon(int x, int y, bool is_dir, uint32_t color, const char *filename, const char *current_path) {
|
||||||
if (is_dir) {
|
if (is_dir) {
|
||||||
if (explorer_strcmp(filename, "RecycleBin") == 0) draw_recycle_bin_icon(x + 5, y + 5, "");
|
if (explorer_strcmp(filename, "RecycleBin") == 0) draw_recycle_bin_icon(x + 5, y + 5, "");
|
||||||
else draw_folder_icon(x + 5, y + 5, "");
|
else draw_folder_icon(x + 5, y + 5, "");
|
||||||
|
|
@ -903,7 +910,12 @@ static void explorer_draw_file_icon(int x, int y, bool is_dir, uint32_t color, c
|
||||||
} else if (explorer_str_ends_with(filename, ".pnt")) {
|
} else if (explorer_str_ends_with(filename, ".pnt")) {
|
||||||
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")) {
|
||||||
draw_image_icon(x + 5, y + 5, filename);
|
// Build full path for thumbnail loading
|
||||||
|
char full_path[256];
|
||||||
|
explorer_strcpy(full_path, current_path);
|
||||||
|
if (full_path[explorer_strlen(full_path) - 1] != '/') explorer_strcat(full_path, "/");
|
||||||
|
explorer_strcat(full_path, filename);
|
||||||
|
draw_image_icon(x + 5, y + 5, full_path);
|
||||||
} else {
|
} else {
|
||||||
draw_document_icon(x + 5, y + 5, "");
|
draw_document_icon(x + 5, y + 5, "");
|
||||||
}
|
}
|
||||||
|
|
@ -989,7 +1001,7 @@ static void explorer_paint(Window *win) {
|
||||||
draw_rounded_rect_filled(item_x, item_y, EXPLORER_ITEM_WIDTH, EXPLORER_ITEM_HEIGHT, 6, bg_color);
|
draw_rounded_rect_filled(item_x, item_y, EXPLORER_ITEM_WIDTH, EXPLORER_ITEM_HEIGHT, 6, bg_color);
|
||||||
|
|
||||||
// Draw icon (larger area)
|
// Draw icon (larger area)
|
||||||
explorer_draw_file_icon(item_x + 5, item_y + 5, state->items[i].is_directory, state->items[i].color, state->items[i].name);
|
explorer_draw_file_icon(item_x + 5, item_y + 5, state->items[i].is_directory, state->items[i].color, state->items[i].name, state->current_path);
|
||||||
|
|
||||||
// Draw name using intelligent wrapping
|
// Draw name using intelligent wrapping
|
||||||
const char *display_name = state->items[i].name;
|
const char *display_name = state->items[i].name;
|
||||||
|
|
@ -1098,91 +1110,96 @@ static void explorer_paint(Window *win) {
|
||||||
int dlg_x = win->x + win->w / 2 - 150;
|
int dlg_x = win->x + win->w / 2 - 150;
|
||||||
int dlg_y = win->y + win->h / 2 - 60;
|
int dlg_y = win->y + win->h / 2 - 60;
|
||||||
|
|
||||||
// Dialog background
|
// Dialog background (modern dark, rounded)
|
||||||
draw_rect(dlg_x - 5, dlg_y - 5, 310, 120, COLOR_LTGRAY);
|
draw_rounded_rect_filled(dlg_x, dlg_y, 300, 110, 8, COLOR_DARK_PANEL);
|
||||||
draw_bevel_rect(dlg_x, dlg_y, 300, 110, true);
|
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
const char *title = state->dialog_target_is_dir ? "Delete Folder?" : "Delete File?";
|
const char *title = state->dialog_target_is_dir ? "Delete Folder?" : "Delete File?";
|
||||||
draw_string(dlg_x + 10, dlg_y + 10, title, COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 10, title, COLOR_WHITE);
|
||||||
|
|
||||||
// Message
|
// Message
|
||||||
if (explorer_str_starts_with(state->current_path, "/RecycleBin")) {
|
if (explorer_str_starts_with(state->current_path, "/RecycleBin")) {
|
||||||
draw_string(dlg_x + 10, dlg_y + 35, "This action cannot be undone.", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 35, "This action cannot be undone.", 0xFFAAAAAA);
|
||||||
draw_string(dlg_x + 10, dlg_y + 48, "Delete forever?", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 48, "Delete forever?", 0xFFAAAAAA);
|
||||||
} else {
|
} else {
|
||||||
draw_string(dlg_x + 10, dlg_y + 35, "This file will be moved to", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 35, "This file will be moved to", 0xFFAAAAAA);
|
||||||
draw_string(dlg_x + 10, dlg_y + 45, "the recycle bin.", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 45, "the recycle bin.", 0xFFAAAAAA);
|
||||||
}
|
}
|
||||||
// Buttons
|
// Buttons (rounded, delete button red-tinted)
|
||||||
draw_button(dlg_x + 50, dlg_y + 65, 80, 25, "Delete", false);
|
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, 0xFF8B2020);
|
||||||
draw_button(dlg_x + 170, dlg_y + 65, 80, 25, "Cancel", false);
|
draw_string(dlg_x + 68, dlg_y + 72, "Delete", COLOR_WHITE);
|
||||||
|
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER);
|
||||||
|
draw_string(dlg_x + 185, dlg_y + 72, "Cancel", COLOR_WHITE);
|
||||||
} else if (state->dialog_state == DIALOG_REPLACE_CONFIRM) {
|
} else if (state->dialog_state == DIALOG_REPLACE_CONFIRM) {
|
||||||
int dlg_x = win->x + win->w / 2 - 150;
|
int dlg_x = win->x + win->w / 2 - 150;
|
||||||
int dlg_y = win->y + win->h / 2 - 60;
|
int dlg_y = win->y + win->h / 2 - 60;
|
||||||
|
|
||||||
// Dialog background
|
// Dialog background (modern dark, rounded)
|
||||||
draw_rect(dlg_x - 5, dlg_y - 5, 310, 120, COLOR_LTGRAY);
|
draw_rounded_rect_filled(dlg_x, dlg_y, 300, 110, 8, COLOR_DARK_PANEL);
|
||||||
draw_bevel_rect(dlg_x, dlg_y, 300, 110, true);
|
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
draw_string(dlg_x + 10, dlg_y + 10, "File Exists", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 10, "File Exists", COLOR_WHITE);
|
||||||
|
|
||||||
// Message
|
// Message
|
||||||
draw_string(dlg_x + 10, dlg_y + 35, "Replace existing file?", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 35, "Replace existing file?", 0xFFAAAAAA);
|
||||||
draw_string(dlg_x + 10, dlg_y + 48, "This cannot be undone.", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 48, "This cannot be undone.", 0xFFAAAAAA);
|
||||||
|
|
||||||
// Buttons
|
// Buttons (rounded)
|
||||||
draw_button(dlg_x + 50, dlg_y + 70, 80, 25, "Replace", false);
|
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER);
|
||||||
draw_button(dlg_x + 170, dlg_y + 70, 80, 25, "Cancel", false);
|
draw_string(dlg_x + 63, dlg_y + 77, "Replace", COLOR_WHITE);
|
||||||
|
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER);
|
||||||
|
draw_string(dlg_x + 185, dlg_y + 77, "Cancel", COLOR_WHITE);
|
||||||
} else if (state->dialog_state == DIALOG_REPLACE_MOVE_CONFIRM) {
|
} else if (state->dialog_state == DIALOG_REPLACE_MOVE_CONFIRM) {
|
||||||
int dlg_x = win->x + win->w / 2 - 150;
|
int dlg_x = win->x + win->w / 2 - 150;
|
||||||
int dlg_y = win->y + win->h / 2 - 60;
|
int dlg_y = win->y + win->h / 2 - 60;
|
||||||
|
|
||||||
// Dialog background
|
// Dialog background (modern dark, rounded)
|
||||||
draw_rect(dlg_x - 5, dlg_y - 5, 310, 120, COLOR_LTGRAY);
|
draw_rounded_rect_filled(dlg_x, dlg_y, 300, 110, 8, COLOR_DARK_PANEL);
|
||||||
draw_bevel_rect(dlg_x, dlg_y, 300, 110, true);
|
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
draw_string(dlg_x + 10, dlg_y + 10, "File Exists", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 10, "File Exists", COLOR_WHITE);
|
||||||
|
|
||||||
// Message
|
// Message
|
||||||
draw_string(dlg_x + 10, dlg_y + 35, "Replace existing file?", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 35, "Replace existing file?", 0xFFAAAAAA);
|
||||||
draw_string(dlg_x + 10, dlg_y + 48, "This cannot be undone.", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 48, "This cannot be undone.", 0xFFAAAAAA);
|
||||||
|
|
||||||
// Buttons
|
// Buttons (rounded)
|
||||||
draw_button(dlg_x + 50, dlg_y + 70, 80, 25, "Replace", false);
|
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER);
|
||||||
draw_button(dlg_x + 170, dlg_y + 70, 80, 25, "Cancel", false);
|
draw_string(dlg_x + 63, dlg_y + 77, "Replace", COLOR_WHITE);
|
||||||
|
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER);
|
||||||
|
draw_string(dlg_x + 185, dlg_y + 77, "Cancel", COLOR_WHITE);
|
||||||
} else if (state->dialog_state == DIALOG_CREATE_REPLACE_CONFIRM) {
|
} else if (state->dialog_state == DIALOG_CREATE_REPLACE_CONFIRM) {
|
||||||
int dlg_x = win->x + win->w / 2 - 150;
|
int dlg_x = win->x + win->w / 2 - 150;
|
||||||
int dlg_y = win->y + win->h / 2 - 60;
|
int dlg_y = win->y + win->h / 2 - 60;
|
||||||
|
|
||||||
// Dialog background
|
// Dialog background (modern dark, rounded)
|
||||||
draw_rect(dlg_x - 5, dlg_y - 5, 310, 120, COLOR_LTGRAY);
|
draw_rounded_rect_filled(dlg_x, dlg_y, 300, 110, 8, COLOR_DARK_PANEL);
|
||||||
draw_bevel_rect(dlg_x, dlg_y, 300, 110, true);
|
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
draw_string(dlg_x + 10, dlg_y + 10, "File Exists", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 10, "File Exists", COLOR_WHITE);
|
||||||
|
|
||||||
// Message
|
// Message
|
||||||
draw_string(dlg_x + 10, dlg_y + 35, "Overwrite existing file?", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 35, "Overwrite existing file?", 0xFFAAAAAA);
|
||||||
draw_string(dlg_x + 10, dlg_y + 48, "This cannot be undone.", COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 48, "This cannot be undone.", 0xFFAAAAAA);
|
||||||
|
|
||||||
// Buttons
|
// Buttons (rounded)
|
||||||
draw_button(dlg_x + 50, dlg_y + 70, 80, 25, "Overwrite", false);
|
draw_rounded_rect_filled(dlg_x + 50, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER);
|
||||||
draw_button(dlg_x + 170, dlg_y + 70, 80, 25, "Cancel", false);
|
draw_string(dlg_x + 57, dlg_y + 77, "Overwrite", COLOR_WHITE);
|
||||||
|
draw_rounded_rect_filled(dlg_x + 170, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER);
|
||||||
|
draw_string(dlg_x + 185, dlg_y + 77, "Cancel", COLOR_WHITE);
|
||||||
} else if (state->dialog_state == DIALOG_ERROR) {
|
} else if (state->dialog_state == DIALOG_ERROR) {
|
||||||
int dlg_x = win->x + win->w / 2 - 150;
|
int dlg_x = win->x + win->w / 2 - 150;
|
||||||
int dlg_y = win->y + win->h / 2 - 60;
|
int dlg_y = win->y + win->h / 2 - 60;
|
||||||
|
|
||||||
draw_rect(dlg_x - 5, dlg_y - 5, 310, 120, COLOR_LTGRAY);
|
// Dialog background (modern dark, rounded)
|
||||||
draw_bevel_rect(dlg_x, dlg_y, 300, 110, true);
|
draw_rounded_rect_filled(dlg_x, dlg_y, 300, 110, 8, COLOR_DARK_PANEL);
|
||||||
|
|
||||||
draw_string(dlg_x + 10, dlg_y + 10, "Error", COLOR_RED);
|
draw_string(dlg_x + 10, dlg_y + 10, "Error", 0xFFFF6B6B);
|
||||||
draw_string(dlg_x + 10, dlg_y + 40, state->dialog_input, COLOR_BLACK);
|
draw_string(dlg_x + 10, dlg_y + 40, state->dialog_input, 0xFFAAAAAA);
|
||||||
|
|
||||||
// OK Button
|
// OK Button (rounded)
|
||||||
draw_button(dlg_x + 110, dlg_y + 70, 80, 25, "OK", false);
|
draw_rounded_rect_filled(dlg_x + 110, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER);
|
||||||
|
draw_string(dlg_x + 138, dlg_y + 77, "OK", COLOR_WHITE);
|
||||||
} else if (state->dialog_state == DIALOG_RENAME) {
|
} else if (state->dialog_state == DIALOG_RENAME) {
|
||||||
int dlg_x = win->x + win->w / 2 - 150;
|
int dlg_x = win->x + win->w / 2 - 150;
|
||||||
int dlg_y = win->y + win->h / 2 - 60;
|
int dlg_y = win->y + win->h / 2 - 60;
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ typedef struct {
|
||||||
uint32_t root_cluster;
|
uint32_t root_cluster;
|
||||||
uint32_t fat_size; // sectors
|
uint32_t fat_size; // sectors
|
||||||
uint32_t total_sectors;
|
uint32_t total_sectors;
|
||||||
|
uint32_t partition_offset; // LBA offset of partition start
|
||||||
bool mounted;
|
bool mounted;
|
||||||
} FAT32_Volume;
|
} FAT32_Volume;
|
||||||
|
|
||||||
|
|
@ -51,6 +52,17 @@ static FAT32_Volume volumes[26]; // A-Z
|
||||||
|
|
||||||
// === Helper Functions (Shared) ===
|
// === Helper Functions (Shared) ===
|
||||||
|
|
||||||
|
// Serial debug output
|
||||||
|
static void fs_serial_char(char c) {
|
||||||
|
while (!(inb(0x3F8 + 5) & 0x20));
|
||||||
|
outb(0x3F8, c);
|
||||||
|
}
|
||||||
|
static void fs_serial_str(const char *s) { while (*s) fs_serial_char(*s++); }
|
||||||
|
static void fs_serial_num(uint32_t n) {
|
||||||
|
if (n >= 10) fs_serial_num(n / 10);
|
||||||
|
fs_serial_char('0' + (n % 10));
|
||||||
|
}
|
||||||
|
|
||||||
static size_t fs_strlen(const char *str) {
|
static size_t fs_strlen(const char *str) {
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
while (str[len]) len++;
|
while (str[len]) len++;
|
||||||
|
|
@ -373,31 +385,49 @@ static bool realfs_mount(char drive) {
|
||||||
Disk *disk = disk_get_by_letter(drive);
|
Disk *disk = disk_get_by_letter(drive);
|
||||||
if (!disk) return false;
|
if (!disk) return false;
|
||||||
|
|
||||||
|
// Use partition LBA offset from disk (set during MBR parsing)
|
||||||
|
uint32_t part_offset = disk->partition_lba_offset;
|
||||||
|
|
||||||
uint8_t *sect0 = (uint8_t*)kmalloc(512);
|
uint8_t *sect0 = (uint8_t*)kmalloc(512);
|
||||||
if (!sect0) return false;
|
if (!sect0) return false;
|
||||||
|
|
||||||
if (disk->read_sector(disk, 0, sect0) != 0) {
|
// Read BPB from partition start (sector 0 for raw, partition LBA for MBR)
|
||||||
|
if (disk->read_sector(disk, part_offset, sect0) != 0) {
|
||||||
kfree(sect0);
|
kfree(sect0);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FAT32_BootSector *bpb = (FAT32_BootSector*)sect0;
|
FAT32_BootSector *bpb = (FAT32_BootSector*)sect0;
|
||||||
|
|
||||||
// Simple verification (could be more robust)
|
|
||||||
if (bpb->boot_signature_value != 0xAA55) {
|
if (bpb->boot_signature_value != 0xAA55) {
|
||||||
kfree(sect0);
|
kfree(sect0);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
volumes[idx].disk = disk;
|
volumes[idx].disk = disk;
|
||||||
volumes[idx].fat_begin_lba = bpb->reserved_sectors;
|
volumes[idx].partition_offset = part_offset;
|
||||||
volumes[idx].cluster_begin_lba = bpb->reserved_sectors + (bpb->num_fats * bpb->sectors_per_fat_32);
|
volumes[idx].fat_begin_lba = part_offset + bpb->reserved_sectors;
|
||||||
|
volumes[idx].cluster_begin_lba = part_offset + bpb->reserved_sectors + (bpb->num_fats * bpb->sectors_per_fat_32);
|
||||||
volumes[idx].sectors_per_cluster = bpb->sectors_per_cluster;
|
volumes[idx].sectors_per_cluster = bpb->sectors_per_cluster;
|
||||||
volumes[idx].root_cluster = bpb->root_cluster;
|
volumes[idx].root_cluster = bpb->root_cluster;
|
||||||
volumes[idx].fat_size = bpb->sectors_per_fat_32;
|
volumes[idx].fat_size = bpb->sectors_per_fat_32;
|
||||||
volumes[idx].total_sectors = bpb->total_sectors_32;
|
volumes[idx].total_sectors = bpb->total_sectors_32;
|
||||||
volumes[idx].mounted = true;
|
volumes[idx].mounted = true;
|
||||||
|
|
||||||
|
fs_serial_str("[FAT32] mounted drive ");
|
||||||
|
fs_serial_char(drive);
|
||||||
|
fs_serial_str(": part_offset=");
|
||||||
|
fs_serial_num(part_offset);
|
||||||
|
fs_serial_str(" fat_lba=");
|
||||||
|
fs_serial_num(volumes[idx].fat_begin_lba);
|
||||||
|
fs_serial_str(" cluster_lba=");
|
||||||
|
fs_serial_num(volumes[idx].cluster_begin_lba);
|
||||||
|
fs_serial_str(" spc=");
|
||||||
|
fs_serial_num(volumes[idx].sectors_per_cluster);
|
||||||
|
fs_serial_str(" root_cl=");
|
||||||
|
fs_serial_num(volumes[idx].root_cluster);
|
||||||
|
fs_serial_str("\n");
|
||||||
|
|
||||||
kfree(sect0);
|
kfree(sect0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -488,6 +518,14 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m
|
||||||
const char *p = path;
|
const char *p = path;
|
||||||
if (*p == '/') p++;
|
if (*p == '/') p++;
|
||||||
|
|
||||||
|
fs_serial_str("[FAT32] realfs_open drive=");
|
||||||
|
fs_serial_char(drive);
|
||||||
|
fs_serial_str(" path='");
|
||||||
|
fs_serial_str(path);
|
||||||
|
fs_serial_str("' mode=");
|
||||||
|
fs_serial_char(mode[0]);
|
||||||
|
fs_serial_str("\n");
|
||||||
|
|
||||||
if (*p == 0) {
|
if (*p == 0) {
|
||||||
// Root dir
|
// Root dir
|
||||||
if (mode[0] == 'w') return NULL; // Cannot write to root as file
|
if (mode[0] == 'w') return NULL; // Cannot write to root as file
|
||||||
|
|
@ -535,6 +573,8 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m
|
||||||
for (int e = 0; e < entries_per_cluster; e++) {
|
for (int e = 0; e < entries_per_cluster; e++) {
|
||||||
if (entry[e].filename[0] == 0) break; // End of dir
|
if (entry[e].filename[0] == 0) break; // End of dir
|
||||||
if (entry[e].filename[0] == 0xE5) continue; // Deleted
|
if (entry[e].filename[0] == 0xE5) continue; // Deleted
|
||||||
|
if (entry[e].attributes == 0x0F) continue; // LFN entry
|
||||||
|
if (entry[e].attributes & ATTR_VOLUME_ID) continue; // Volume label
|
||||||
|
|
||||||
// Compare name (simplistic 8.3 matching)
|
// Compare name (simplistic 8.3 matching)
|
||||||
char name[12];
|
char name[12];
|
||||||
|
|
@ -558,6 +598,13 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
uint32_t cluster = (entry[e].start_cluster_high << 16) | entry[e].start_cluster_low;
|
uint32_t cluster = (entry[e].start_cluster_high << 16) | entry[e].start_cluster_low;
|
||||||
|
fs_serial_str("[FAT32] MATCH '");
|
||||||
|
fs_serial_str(name);
|
||||||
|
fs_serial_str("' cluster=");
|
||||||
|
fs_serial_num(cluster);
|
||||||
|
fs_serial_str(" size=");
|
||||||
|
fs_serial_num(entry[e].file_size);
|
||||||
|
fs_serial_str("\n");
|
||||||
|
|
||||||
uint32_t lba = vol->cluster_begin_lba + (search_cluster - 2) * vol->sectors_per_cluster;
|
uint32_t lba = vol->cluster_begin_lba + (search_cluster - 2) * vol->sectors_per_cluster;
|
||||||
int sect_in_cluster = (e * 32) / 512;
|
int sect_in_cluster = (e * 32) / 512;
|
||||||
|
|
@ -922,6 +969,8 @@ static bool realfs_delete(char drive, const char *path) {
|
||||||
for (int e = 0; e < entries_per_cluster; e++) {
|
for (int e = 0; e < entries_per_cluster; e++) {
|
||||||
if (entry[e].filename[0] == 0) break;
|
if (entry[e].filename[0] == 0) break;
|
||||||
if (entry[e].filename[0] == 0xE5) continue;
|
if (entry[e].filename[0] == 0xE5) continue;
|
||||||
|
if (entry[e].attributes == 0x0F) continue; // Skip LFN entries
|
||||||
|
if (entry[e].attributes & ATTR_VOLUME_ID) continue; // Skip volume label
|
||||||
|
|
||||||
// Format name and compare
|
// Format name and compare
|
||||||
char name[12];
|
char name[12];
|
||||||
|
|
@ -1062,6 +1111,8 @@ static int realfs_list_directory(char drive, const char *path, FAT32_FileInfo *e
|
||||||
for (int e = 0; e < entries_per_cluster && count < max_entries; e++) {
|
for (int e = 0; e < entries_per_cluster && count < max_entries; e++) {
|
||||||
if (entry[e].filename[0] == 0) break;
|
if (entry[e].filename[0] == 0) break;
|
||||||
if (entry[e].filename[0] == 0xE5) continue;
|
if (entry[e].filename[0] == 0xE5) continue;
|
||||||
|
if (entry[e].attributes == 0x0F) continue; // Skip LFN entries
|
||||||
|
if (entry[e].attributes & ATTR_VOLUME_ID) continue; // Skip volume label
|
||||||
|
|
||||||
// Format name
|
// Format name
|
||||||
char name[13];
|
char name[13];
|
||||||
|
|
|
||||||
BIN
src/kernel/images/.DS_Store
vendored
BIN
src/kernel/images/.DS_Store
vendored
Binary file not shown.
BIN
src/kernel/images/wallpapers/.DS_Store
vendored
BIN
src/kernel/images/wallpapers/.DS_Store
vendored
Binary file not shown.
138
src/kernel/wm.c
138
src/kernel/wm.c
|
|
@ -16,6 +16,7 @@
|
||||||
#include "about.h"
|
#include "about.h"
|
||||||
#include "minesweeper.h"
|
#include "minesweeper.h"
|
||||||
#include "fat32.h"
|
#include "fat32.h"
|
||||||
|
#include "nanojpeg.h"
|
||||||
#include "memory_manager.h"
|
#include "memory_manager.h"
|
||||||
#include "paint.h"
|
#include "paint.h"
|
||||||
#include "disk.h"
|
#include "disk.h"
|
||||||
|
|
@ -474,12 +475,109 @@ void draw_document_icon(int x, int y, const char *label) {
|
||||||
draw_icon_label(x, y, label);
|
draw_icon_label(x, y, label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === Dynamic thumbnail cache for JPG explorer icons ===
|
||||||
|
#define THUMB_CACHE_SIZE 8
|
||||||
|
#define THUMB_PIXELS (48 * 48)
|
||||||
|
static struct {
|
||||||
|
char path[256];
|
||||||
|
uint32_t pixels[THUMB_PIXELS];
|
||||||
|
bool valid;
|
||||||
|
bool failed; // Mark as failed so we don't retry
|
||||||
|
} thumb_cache[THUMB_CACHE_SIZE];
|
||||||
|
static int thumb_cache_next = 0; // Round-robin eviction
|
||||||
|
|
||||||
|
static uint32_t* thumb_cache_lookup(const char *path) {
|
||||||
|
for (int i = 0; i < THUMB_CACHE_SIZE; i++) {
|
||||||
|
if (thumb_cache[i].valid && str_eq(thumb_cache[i].path, path) == 0) {
|
||||||
|
return thumb_cache[i].pixels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool thumb_cache_is_failed(const char *path) {
|
||||||
|
for (int i = 0; i < THUMB_CACHE_SIZE; i++) {
|
||||||
|
if (thumb_cache[i].failed && str_eq(thumb_cache[i].path, path) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t* thumb_cache_decode(const char *path) {
|
||||||
|
// Open and read the JPG file
|
||||||
|
FAT32_FileHandle *fh = fat32_open(path, "r");
|
||||||
|
if (!fh) return NULL;
|
||||||
|
|
||||||
|
uint32_t file_size = fh->size;
|
||||||
|
if (file_size == 0 || file_size > 2 * 1024 * 1024) {
|
||||||
|
fat32_close(fh);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *buf = (unsigned char*)kmalloc(file_size);
|
||||||
|
if (!buf) { fat32_close(fh); return NULL; }
|
||||||
|
|
||||||
|
int total = 0;
|
||||||
|
while (total < (int)file_size) {
|
||||||
|
int chunk = fat32_read(fh, buf + total, (int)file_size - total);
|
||||||
|
if (chunk <= 0) break;
|
||||||
|
total += chunk;
|
||||||
|
}
|
||||||
|
fat32_close(fh);
|
||||||
|
|
||||||
|
if (total <= 0) { kfree(buf); return NULL; }
|
||||||
|
|
||||||
|
// Decode JPEG
|
||||||
|
njInit();
|
||||||
|
if (njDecode(buf, total) != NJ_OK) {
|
||||||
|
njDone();
|
||||||
|
kfree(buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int img_w = njGetWidth();
|
||||||
|
int img_h = njGetHeight();
|
||||||
|
unsigned char *img = njGetImage();
|
||||||
|
|
||||||
|
// Store in cache — downscale to 48x48
|
||||||
|
int slot = thumb_cache_next;
|
||||||
|
thumb_cache_next = (thumb_cache_next + 1) % THUMB_CACHE_SIZE;
|
||||||
|
|
||||||
|
// Copy path
|
||||||
|
int p = 0;
|
||||||
|
while (path[p] && p < 255) { thumb_cache[slot].path[p] = path[p]; p++; }
|
||||||
|
thumb_cache[slot].path[p] = 0;
|
||||||
|
|
||||||
|
// Downscale image to 48x48 with aspect-fill
|
||||||
|
for (int ty = 0; ty < 48; ty++) {
|
||||||
|
for (int tx = 0; tx < 48; tx++) {
|
||||||
|
int sx = tx * img_w / 48;
|
||||||
|
int sy = ty * img_h / 48;
|
||||||
|
if (sx >= img_w) sx = img_w - 1;
|
||||||
|
if (sy >= img_h) sy = img_h - 1;
|
||||||
|
int idx = (sy * img_w + sx) * 3;
|
||||||
|
uint32_t r = img[idx], g = img[idx+1], b = img[idx+2];
|
||||||
|
thumb_cache[slot].pixels[ty * 48 + tx] = 0xFF000000 | (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thumb_cache[slot].valid = true;
|
||||||
|
thumb_cache[slot].failed = false;
|
||||||
|
|
||||||
|
njDone();
|
||||||
|
kfree(buf);
|
||||||
|
|
||||||
|
return thumb_cache[slot].pixels;
|
||||||
|
}
|
||||||
|
|
||||||
void draw_image_icon(int x, int y, const char *label) {
|
void draw_image_icon(int x, int y, const char *label) {
|
||||||
uint32_t icon_buf[48 * 48];
|
uint32_t icon_buf[48 * 48];
|
||||||
for (int i = 0; i < 48 * 48; i++) icon_buf[i] = 0xFFFF00FF;
|
for (int i = 0; i < 48 * 48; i++) icon_buf[i] = 0xFFFF00FF;
|
||||||
graphics_set_render_target(icon_buf, 48, 48);
|
graphics_set_render_target(icon_buf, 48, 48);
|
||||||
|
|
||||||
uint32_t *thumb = NULL;
|
uint32_t *thumb = NULL;
|
||||||
|
// Fast path: check hardcoded wallpaper names
|
||||||
if (str_eq(label, "moon.jpg") == 0) thumb = wallpaper_get_thumb(0);
|
if (str_eq(label, "moon.jpg") == 0) thumb = wallpaper_get_thumb(0);
|
||||||
else if (str_eq(label, "mountain.jpg") == 0) thumb = wallpaper_get_thumb(1);
|
else if (str_eq(label, "mountain.jpg") == 0) thumb = wallpaper_get_thumb(1);
|
||||||
else if (str_eq(label, "moon") == 0) thumb = wallpaper_get_thumb(0);
|
else if (str_eq(label, "moon") == 0) thumb = wallpaper_get_thumb(0);
|
||||||
|
|
@ -490,15 +588,52 @@ void draw_image_icon(int x, int y, const char *label) {
|
||||||
else if (str_ends_with(label, "mountain.jpg")) thumb = wallpaper_get_thumb(1);
|
else if (str_ends_with(label, "mountain.jpg")) thumb = wallpaper_get_thumb(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dynamic path: try thumbnail cache for any JPG file path
|
||||||
|
if (!thumb && !thumb_cache_is_failed(label)) {
|
||||||
|
thumb = thumb_cache_lookup(label);
|
||||||
|
if (!thumb) {
|
||||||
|
// Try to decode and cache
|
||||||
|
graphics_set_render_target(NULL, 0, 0); // Restore before file I/O
|
||||||
|
thumb = thumb_cache_decode(label);
|
||||||
|
if (!thumb) {
|
||||||
|
// Mark as failed so we don't retry every frame
|
||||||
|
int slot = thumb_cache_next;
|
||||||
|
int p = 0;
|
||||||
|
while (label[p] && p < 255) { thumb_cache[slot].path[p] = label[p]; p++; }
|
||||||
|
thumb_cache[slot].path[p] = 0;
|
||||||
|
thumb_cache[slot].valid = false;
|
||||||
|
thumb_cache[slot].failed = true;
|
||||||
|
thumb_cache_next = (thumb_cache_next + 1) % THUMB_CACHE_SIZE;
|
||||||
|
}
|
||||||
|
// Re-set render target for icon drawing
|
||||||
|
for (int i = 0; i < 48 * 48; i++) icon_buf[i] = 0xFFFF00FF;
|
||||||
|
graphics_set_render_target(icon_buf, 48, 48);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (thumb) {
|
if (thumb) {
|
||||||
// White border
|
// White border
|
||||||
draw_rounded_rect_filled(0, 0, 48, 48, 4, 0xFFFFFFFF);
|
draw_rounded_rect_filled(0, 0, 48, 48, 4, 0xFFFFFFFF);
|
||||||
|
// Draw thumbnail into icon - handle both 100x60 wallpaper thumbs and 48x48 dynamic thumbs
|
||||||
|
bool is_wallpaper_thumb = false;
|
||||||
|
if (str_ends_with(label, "moon.jpg") || str_ends_with(label, "mountain.jpg") ||
|
||||||
|
str_eq(label, "moon") == 0 || str_eq(label, "mountain") == 0) {
|
||||||
|
is_wallpaper_thumb = true;
|
||||||
|
}
|
||||||
int dst_w = 44, dst_h = 44;
|
int dst_w = 44, dst_h = 44;
|
||||||
for (int ty = 0; ty < dst_h; ty++) {
|
for (int ty = 0; ty < dst_h; ty++) {
|
||||||
for (int tx = 0; tx < dst_w; tx++) {
|
for (int tx = 0; tx < dst_w; tx++) {
|
||||||
|
uint32_t pixel;
|
||||||
|
if (is_wallpaper_thumb) {
|
||||||
int sx = tx * 100 / dst_w;
|
int sx = tx * 100 / dst_w;
|
||||||
int sy = ty * 60 / dst_h;
|
int sy = ty * 60 / dst_h;
|
||||||
put_pixel(2 + tx, 2 + ty, thumb[sy * 100 + sx]);
|
pixel = thumb[sy * 100 + sx];
|
||||||
|
} else {
|
||||||
|
int sx = tx * 48 / dst_w;
|
||||||
|
int sy = ty * 48 / dst_h;
|
||||||
|
pixel = thumb[sy * 48 + sx];
|
||||||
|
}
|
||||||
|
put_pixel(2 + tx, 2 + ty, pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -969,6 +1104,7 @@ void wm_paint(void) {
|
||||||
if (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name);
|
if (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name);
|
||||||
else if (str_ends_with(icon->name, ".jpg") || str_ends_with(icon->name, ".JPG")) {
|
else if (str_ends_with(icon->name, ".jpg") || str_ends_with(icon->name, ".JPG")) {
|
||||||
draw_image_icon(icon->x, icon->y, icon->name);
|
draw_image_icon(icon->x, icon->y, icon->name);
|
||||||
|
draw_icon_label(icon->x, icon->y, icon->name);
|
||||||
}
|
}
|
||||||
else draw_document_icon(icon->x, icon->y, icon->name);
|
else draw_document_icon(icon->x, icon->y, icon->name);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue