[Optimization update]
This update greatly optimizes screen redraws saving system resources and making the system run a LOT smoother through QEMU.
This commit is contained in:
Chris 2026-02-10 18:55:43 +01:00
parent 36d7137969
commit 90f86d63dc
11 changed files with 131 additions and 74 deletions

View file

@ -119,4 +119,4 @@ run: $(ISO_IMAGE)
qemu-system-x86_64 -m 2G -serial stdio -cdrom $(ISO_IMAGE) -boot d \ qemu-system-x86_64 -m 2G -serial stdio -cdrom $(ISO_IMAGE) -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=1280 -global VGA.yres=800 -vga std -global VGA.xres=1920 -global VGA.yres=1080

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1056,7 +1056,7 @@ static void create_test_files(void) {
const char *content = const char *content =
"# All compiled C files in this directory are openable from any other directory by typing in the name of the compiled file by typing in the name of the compiled file.\n\n" "# All compiled C files in this directory are openable from any other directory by typing in the name of the compiled file by typing in the name of the compiled file.\n\n"
"The c file 'wordofgod.c' contains a C program similar to one in TempleOS, which Terry A. Davis (RIP) saw as 'words from god' telling him what to do with his kernel.\n" "The c file 'wordofgod.c' contains a C program similar to one in TempleOS, which Terry A. Davis (RIP) saw as 'words from god' telling him what to do with his kernel.\n"
"I made this file as a tribute to him, as he also inspired me to create this project in '24. If you want to run it you simply do cc (or compc) wordgod.c and then run ./wordgod \n"; "I made this file as a tribute to him, as he also inspired me to create this project in '24. If you want to run it you simply do cc (or compc) wordofgod.c and then run ./wordofgod \n";
fat32_write(fh, (void *)content, cmd_strlen(content)); fat32_write(fh, (void *)content, cmd_strlen(content));
fat32_close(fh); fat32_close(fh);
} }
@ -1093,16 +1093,16 @@ static void create_test_files(void) {
"poke(l+40,\"day \");poke(l+44,\"night \");poke(l+48,\"waters \");poke(l+52,\"firmament \");poke(l+56,\"evening \");poke(l+60,\"morning \");poke(l+64,\"land \");poke(l+68,\"seas \");poke(l+72,\"grass \");poke(l+76,\"herb \");", "poke(l+40,\"day \");poke(l+44,\"night \");poke(l+48,\"waters \");poke(l+52,\"firmament \");poke(l+56,\"evening \");poke(l+60,\"morning \");poke(l+64,\"land \");poke(l+68,\"seas \");poke(l+72,\"grass \");poke(l+76,\"herb \");",
"poke(l+80,\"seed \");poke(l+84,\"fruit \");poke(l+88,\"tree \");poke(l+92,\"sun \");poke(l+96,\"moon \");poke(l+100,\"stars \");poke(l+104,\"signs \");poke(l+108,\"seasons \");poke(l+112,\"days \");poke(l+116,\"years \");", "poke(l+80,\"seed \");poke(l+84,\"fruit \");poke(l+88,\"tree \");poke(l+92,\"sun \");poke(l+96,\"moon \");poke(l+100,\"stars \");poke(l+104,\"signs \");poke(l+108,\"seasons \");poke(l+112,\"days \");poke(l+116,\"years \");",
"poke(l+120,\"creature \");poke(l+124,\"life \");poke(l+128,\"fowl \");poke(l+132,\"whales \");poke(l+136,\"cattle \");poke(l+140,\"creeping \");poke(l+144,\"beast \");poke(l+148,\"man \");poke(l+152,\"image \");poke(l+156,\"likeness \");", "poke(l+120,\"creature \");poke(l+124,\"life \");poke(l+128,\"fowl \");poke(l+132,\"whales \");poke(l+136,\"cattle \");poke(l+140,\"creeping \");poke(l+144,\"beast \");poke(l+148,\"man \");poke(l+152,\"image \");poke(l+156,\"likeness \");",
"poke(l+160,\"dominion \");poke(l+164,\"fish \");poke(l+168,\"air \");poke(l+172,\"every \");poke(l+176,\"CIA \");poke(l+180,\"meat \");poke(l+184,\"holy \");poke(l+188,\"rest \");poke(l+192,\"dust \");poke(l+196,\"breath \");", "poke(l+160,\"dominion \");poke(l+164,\"fish \");poke(l+168,\"air \");poke(l+172,\"every \");poke(l+176,\"CIA \");poke(l+180,\"Epstein Files \");poke(l+184,\"holy \");poke(l+188,\"rest \");poke(l+192,\"dust \");poke(l+196,\"breath \");",
"poke(l+200,\"soul \");poke(l+204,\"garden \");poke(l+208,\"east \");poke(l+212,\"Eden \");poke(l+216,\"ground \");poke(l+220,\"sight \");poke(l+224,\"good \");poke(l+228,\"evil \");poke(l+232,\"river \");poke(l+236,\"gold \");", "poke(l+200,\"soul \");poke(l+204,\"garden \");poke(l+208,\"east \");poke(l+212,\"Eden \");poke(l+216,\"ground \");poke(l+220,\"sight \");poke(l+224,\"good \");poke(l+228,\"evil \");poke(l+232,\"river \");poke(l+236,\"gold \");",
"poke(l+240,\"stone \");poke(l+244,\"woman \");poke(l+248,\"wife \");poke(l+252,\"flesh \");poke(l+256,\"bone \");poke(l+260,\"naked \");poke(l+264,\"serpent \");poke(l+268,\"subtle \");poke(l+272,\"eat \");poke(l+276,\"eyes \");", "poke(l+240,\"stone \");poke(l+244,\"woman \");poke(l+248,\"wife \");poke(l+252,\"flesh \");poke(l+256,\"bone \");poke(l+260,\"naked \");poke(l+264,\"serpent \");poke(l+268,\"subtle \");poke(l+272,\"eat \");poke(l+276,\"eyes \");",
"poke(l+280,\"wise \");poke(l+284,\"cool \");poke(l+288,\"voice \");poke(l+292,\"fear \");poke(l+296,\"hid \");poke(l+300,\"cursed \");poke(l+304,\"belly \");poke(l+308,\"enmity \");poke(l+312,\"sorrow \");poke(l+316,\"conception \");", "poke(l+280,\"wise \");poke(l+284,\"cool \");poke(l+288,\"voice \");poke(l+292,\"fear \");poke(l+296,\"hid \");poke(l+300,\"cursed \");poke(l+304,\"belly \");poke(l+308,\"enmity \");poke(l+312,\"sorrow \");poke(l+316,\"conception \");",
"poke(l+320,\"children \");poke(l+324,\"desire \");poke(l+328,\"husband \");poke(l+332,\"thorns \");poke(l+336,\"thistles \");poke(l+340,\"sweat \");poke(l+344,\"bread \");poke(l+348,\"mother \");poke(l+352,\"skin \");poke(l+356,\"coats \");", "poke(l+320,\"children \");poke(l+324,\"desire \");poke(l+328,\"husband \");poke(l+332,\"lava \");poke(l+336,\"thistles \");poke(l+340,\"sweat \");poke(l+344,\"bread \");poke(l+348,\"mother \");poke(l+352,\"skin \");poke(l+356,\"coats \");",
"poke(l+360,\"cherubims \");poke(l+364,\"sword \");poke(l+368,\"gate \");poke(l+372,\"offering \");poke(l+376,\"respect \");poke(l+380,\"sin \");poke(l+384,\"door \");poke(l+388,\"blood \");poke(l+392,\"brother \");poke(l+396,\"keeper \");", "poke(l+360,\"cherubims \");poke(l+364,\"sword \");poke(l+368,\"gate \");poke(l+372,\"offering \");poke(l+376,\"obsidian \");poke(l+380,\"sin \");poke(l+384,\"door \");poke(l+388,\"blood \");poke(l+392,\"brother \");poke(l+396,\"keeper \");",
"poke(l+400,\"voice \");poke(l+404,\"heard \");poke(l+408,\"walking \");poke(l+412,\"cool \");poke(l+416,\"day \");poke(l+420,\"where \");poke(l+424,\"art \");poke(l+428,\"thou \");poke(l+432,\"told \");poke(l+436,\"thee \");", "poke(l+400,\"voice \");poke(l+404,\"heard \");poke(l+408,\"walking \");poke(l+412,\"cool \");poke(l+416,\"day \");poke(l+420,\"where \");poke(l+424,\"art \");poke(l+428,\"thou \");poke(l+432,\"told \");poke(l+436,\"thee \");",
"poke(l+440,\"hast \");poke(l+444,\"eaten \");poke(l+448,\"tree \");poke(l+452,\"whereof \");poke(l+456,\"commanded \");poke(l+460,\"shouldest \");poke(l+464,\"not \");poke(l+468,\"eat \");poke(l+472,\"gave \");poke(l+476,\"me \");", "poke(l+440,\"hast \");poke(l+444,\"eaten \");poke(l+448,\"tree \");poke(l+452,\"minecraft \");poke(l+456,\"commanded \");poke(l+460,\"shouldest \");poke(l+464,\"not \");poke(l+468,\"eat \");poke(l+472,\"gave \");poke(l+476,\"me \");",
"poke(l+480,\"beguiled \");poke(l+484,\"belly \");poke(l+488,\"go \");poke(l+492,\"dust \");poke(l+496,\"shalt \");poke(l+500,\"eat \");poke(l+504,\"days \");poke(l+508,\"life \");poke(l+512,\"put \");poke(l+516,\"enmity \");", "poke(l+480,\"beguiled \");poke(l+484,\"belly \");poke(l+488,\"go \");poke(l+492,\"dust \");poke(l+496,\"shalt \");poke(l+500,\"eat \");poke(l+504,\"days \");poke(l+508,\"life \");poke(l+512,\"put \");poke(l+516,\"enmity \");",
"poke(l+520,\"between \");poke(l+524,\"seed \");poke(l+528,\"bruise \");poke(l+532,\"head \");poke(l+536,\"heel \");poke(l+540,\"multiply \");poke(l+544,\"sorrow \");poke(l+548,\"conception \");poke(l+552,\"forth \");poke(l+556,\"children \");", "poke(l+520,\"between \");poke(l+524,\"seed \");poke(l+528,\"ICE \");poke(l+532,\"Detainment Facility \");poke(l+536,\"heel \");poke(l+540,\"multiply \");poke(l+544,\"sorrow \");poke(l+548,\"conception \");poke(l+552,\"forth \");poke(l+556,\"children \");",
"poke(l+560,\"desire \");poke(l+564,\"rule \");poke(l+568,\"over \");poke(l+572,\"sake \");poke(l+576,\"sweat \");poke(l+580,\"face \");poke(l+584,\"till \");poke(l+588,\"return \");poke(l+592,\"ground \");poke(l+596,\"taken \");", "poke(l+560,\"desire \");poke(l+564,\"rule \");poke(l+568,\"over \");poke(l+572,\"sake \");poke(l+576,\"sweat \");poke(l+580,\"face \");poke(l+584,\"till \");poke(l+588,\"return \");poke(l+592,\"ground \");poke(l+596,\"taken \");",
"poke(l+600,\"mother \");poke(l+604,\"living \");poke(l+608,\"coats \");poke(l+612,\"skins \");poke(l+616,\"clothed \");poke(l+620,\"become \");poke(l+624,\"one \");poke(l+628,\"us \");poke(l+632,\"know \");poke(l+636,\"good \");", "poke(l+600,\"mother \");poke(l+604,\"living \");poke(l+608,\"coats \");poke(l+612,\"skins \");poke(l+616,\"clothed \");poke(l+620,\"become \");poke(l+624,\"one \");poke(l+628,\"us \");poke(l+632,\"know \");poke(l+636,\"good \");",
"poke(l+640,\"evil \");poke(l+644,\"lest \");poke(l+648,\"put \");poke(l+652,\"hand \");poke(l+656,\"take \");poke(l+660,\"live \");poke(l+664,\"ever \");poke(l+668,\"sent \");poke(l+672,\"garden \");poke(l+676,\"eden \");", "poke(l+640,\"evil \");poke(l+644,\"lest \");poke(l+648,\"put \");poke(l+652,\"hand \");poke(l+656,\"take \");poke(l+660,\"live \");poke(l+664,\"ever \");poke(l+668,\"sent \");poke(l+672,\"garden \");poke(l+676,\"eden \");",

View file

@ -134,9 +134,29 @@ void put_pixel(int x, int y, uint32_t color) {
} }
void draw_rect(int x, int y, int w, int h, uint32_t color) { void draw_rect(int x, int y, int w, int h, uint32_t color) {
for (int i = 0; i < h; i++) { if (!g_fb) return;
for (int j = 0; j < w; j++) {
put_pixel(x + j, y + i, color); int x1 = x, y1 = y, x2 = x + w, y2 = y + h;
if (g_clip_enabled) {
if (x1 < g_clip_x) x1 = g_clip_x;
if (y1 < g_clip_y) y1 = g_clip_y;
if (x2 > g_clip_x + g_clip_w) x2 = g_clip_x + g_clip_w;
if (y2 > g_clip_y + g_clip_h) y2 = g_clip_y + g_clip_h;
}
if (x1 < 0) x1 = 0;
if (y1 < 0) y1 = 0;
if (x2 > (int)g_fb->width) x2 = g_fb->width;
if (y2 > (int)g_fb->height) y2 = g_fb->height;
if (x1 >= x2 || y1 >= y2) return;
for (int i = y1; i < y2; i++) {
uint32_t *row = &g_back_buffer[i * g_fb->width + x1];
int len = x2 - x1;
for (int j = 0; j < len; j++) {
row[j] = color;
} }
} }
} }
@ -144,6 +164,15 @@ void draw_rect(int x, int y, int w, int h, uint32_t color) {
void draw_char(int x, int y, char c, uint32_t color) { void draw_char(int x, int y, char c, uint32_t color) {
unsigned char uc = (unsigned char)c; unsigned char uc = (unsigned char)c;
if (uc > 127) return; if (uc > 127) return;
// Fast rejection: if the character is entirely outside the clipping/dirty rect, skip it
if (g_clip_enabled) {
if (x + 8 <= g_clip_x || x >= g_clip_x + g_clip_w ||
y + 8 <= g_clip_y || y >= g_clip_y + g_clip_h) {
return;
}
}
const uint8_t *glyph = font8x8_basic[uc]; const uint8_t *glyph = font8x8_basic[uc];
for (int row = 0; row < 8; row++) { for (int row = 0; row < 8; row++) {
@ -174,13 +203,18 @@ void draw_desktop_background(void) {
if (!g_fb) return; if (!g_fb) return;
if (g_use_pattern) { if (g_use_pattern) {
// Draw tiled pattern // Optimized tiled pattern: only draw within the clipping/dirty rect
for (int y = 0; y < (int)g_fb->height; y++) { int x1 = 0, y1 = 0, x2 = g_fb->width, y2 = g_fb->height;
for (int x = 0; x < (int)g_fb->width; x++) { if (g_clip_enabled) {
int px = x % PATTERN_SIZE; x1 = g_clip_x; y1 = g_clip_y;
int py = y % PATTERN_SIZE; x2 = g_clip_x + g_clip_w; y2 = g_clip_y + g_clip_h;
uint32_t color = g_bg_pattern[py * PATTERN_SIZE + px]; }
put_pixel(x, y, color);
for (int y = y1; y < y2; y++) {
uint32_t *row = &g_back_buffer[y * g_fb->width + x1];
int py = y % PATTERN_SIZE;
for (int x = x1; x < x2; x++) {
*row++ = g_bg_pattern[py * PATTERN_SIZE + (x % PATTERN_SIZE)];
} }
} }
} else { } else {
@ -214,20 +248,27 @@ void graphics_clear_back_buffer(uint32_t color) {
} }
void graphics_flip_buffer(void) { void graphics_flip_buffer(void) {
if (!g_fb) return; if (!g_fb || !g_dirty.active) return;
// Copy back buffer to framebuffer int x = g_dirty.x;
uint32_t *src = g_back_buffer; int y = g_dirty.y;
uint8_t *dst = (uint8_t *)g_fb->address; int w = g_dirty.w;
int h = g_dirty.h;
for (int y = 0; y < (int)g_fb->height; y++) {
// Copy one scanline if (x < 0) { w += x; x = 0; }
uint32_t *dst_row = (uint32_t *)dst; if (y < 0) { h += y; y = 0; }
for (int x = 0; x < (int)g_fb->width; x++) { if (x + w > (int)g_fb->width) w = g_fb->width - x;
dst_row[x] = src[x]; if (y + h > (int)g_fb->height) h = g_fb->height - y;
if (w <= 0 || h <= 0) return;
for (int i = 0; i < h; i++) {
int curr_y = y + i;
uint32_t *src_row = &g_back_buffer[curr_y * g_fb->width + x];
uint32_t *dst_row = (uint32_t *)((uint8_t *)g_fb->address + curr_y * g_fb->pitch) + x;
for (int j = 0; j < w; j++) {
dst_row[j] = src_row[j];
} }
src += g_fb->width;
dst += g_fb->pitch;
} }
} }

View file

@ -88,8 +88,8 @@ static int desktop_icon_count = 0;
// Desktop Settings // Desktop Settings
bool desktop_snap_to_grid = true; bool desktop_snap_to_grid = true;
bool desktop_auto_align = true; bool desktop_auto_align = true;
int desktop_max_rows_per_col = 9; int desktop_max_rows_per_col = 13;
int desktop_max_cols = 15; int desktop_max_cols = 23;
// Helper to check if string ends with suffix // Helper to check if string ends with suffix
static bool str_ends_with(const char *str, const char *suffix) { static bool str_ends_with(const char *str, const char *suffix) {
@ -697,20 +697,26 @@ void wm_paint(void) {
int sw = get_screen_width(); int sw = get_screen_width();
int sh = get_screen_height(); int sh = get_screen_height();
// Ensure no stale clipping state interferes with the new frame
graphics_clear_clipping();
// First, erase the old cursor (before redrawing anything) DirtyRect dirty = graphics_get_dirty_rect();
if (cursor_visible) { if (dirty.active) {
erase_cursor(last_cursor_x, last_cursor_y); graphics_set_clipping(dirty.x, dirty.y, dirty.w, dirty.h);
} else {
graphics_clear_clipping();
} }
// 1. Desktop // 1. Desktop
draw_desktop_background(); draw_desktop_background();
// Draw Desktop Icons // Draw Desktop Icons
for (int i = 0; i < desktop_icon_count; i++) { for (int i = 0; i < desktop_icon_count; i++) {
DesktopIcon *icon = &desktop_icons[i]; DesktopIcon *icon = &desktop_icons[i];
if (dirty.active) {
if (icon->x + 80 <= dirty.x || icon->x >= dirty.x + dirty.w ||
icon->y + 80 <= dirty.y || icon->y >= dirty.y + dirty.h) {
continue;
}
}
if (icon->type == 1) draw_folder_icon(icon->x, icon->y, icon->name); if (icon->type == 1) draw_folder_icon(icon->x, icon->y, icon->name);
else if (icon->type == 2) { else if (icon->type == 2) {
// App icon - strip .app for display // App icon - strip .app for display
@ -765,7 +771,15 @@ void wm_paint(void) {
// Draw windows in z-order (lowest first) // Draw windows in z-order (lowest first)
for (int i = 0; i < window_count; i++) { for (int i = 0; i < window_count; i++) {
draw_window(sorted_windows[i]); Window *win = sorted_windows[i];
if (!win->visible) continue;
if (dirty.active) {
if (win->x + win->w <= dirty.x || win->x >= dirty.x + dirty.w ||
win->y + win->h <= dirty.y || win->y >= dirty.y + dirty.h) {
continue;
}
}
draw_window(win);
} }
// 4. Taskbar // 4. Taskbar
@ -917,6 +931,7 @@ void wm_bring_to_front(Window *win) {
win->visible = true; win->visible = true;
win->focused = true; win->focused = true;
win->z_index = max_z + 1; win->z_index = max_z + 1;
force_redraw = true;
} }
void wm_add_window(Window *win) { void wm_add_window(Window *win) {
@ -989,7 +1004,6 @@ void wm_handle_click(int x, int y) {
} }
refresh_desktop_icons(); refresh_desktop_icons();
// If auto-align is OFF and we pasted to the background, place at click location
if (!desktop_auto_align && desktop_icon_count > old_count && desktop_menu_target_icon == -1) { if (!desktop_auto_align && desktop_icon_count > old_count && desktop_menu_target_icon == -1) {
int new_idx = desktop_icon_count - 1; int new_idx = desktop_icon_count - 1;
desktop_icons[new_idx].x = desktop_menu_x - 20; desktop_icons[new_idx].x = desktop_menu_x - 20;
@ -1292,7 +1306,7 @@ void wm_handle_right_click(int x, int y) {
DesktopIcon *icon = &desktop_icons[i]; DesktopIcon *icon = &desktop_icons[i];
is_dragging_file = true; is_dragging_file = true;
drag_icon_type = icon->type; drag_icon_type = icon->type;
pending_desktop_icon_click = -1; // Cancel pending click since we are dragging pending_desktop_icon_click = -1;
drag_icon_orig_x = icon->x; drag_icon_orig_x = icon->x;
drag_icon_orig_y = icon->y; drag_icon_orig_y = icon->y;
// Construct path // Construct path
@ -1312,7 +1326,6 @@ void wm_handle_right_click(int x, int y) {
for (int w = 0; w < window_count; w++) { for (int w = 0; w < window_count; w++) {
Window *win = all_windows[w]; Window *win = all_windows[w];
if (win->visible && rect_contains(win->x, win->y, win->w, win->h, drag_start_x, drag_start_y)) { if (win->visible && rect_contains(win->x, win->y, win->w, win->h, drag_start_x, drag_start_y)) {
// This is a bit of a hack, but we check if it's an explorer window
if (str_starts_with(win->title, "File Explorer")) { if (str_starts_with(win->title, "File Explorer")) {
drag_src_win = win; drag_src_win = win;
explorer_clear_click_state(win); explorer_clear_click_state(win);
@ -1369,48 +1382,52 @@ void wm_handle_right_click(int x, int y) {
int i = pending_desktop_icon_click; int i = pending_desktop_icon_click;
if (i < desktop_icon_count) { if (i < desktop_icon_count) {
DesktopIcon *icon = &desktop_icons[i]; DesktopIcon *icon = &desktop_icons[i];
bool handled = false;
if (icon->type == 2) { // App Shortcut if (icon->type == 2) { // App Shortcut
// Check name to launch app // Check name to launch app
if (str_ends_with(icon->name, "Notepad.shortcut")) { if (str_ends_with(icon->name, "Notepad.shortcut")) {
notepad_reset(); wm_bring_to_front(&win_notepad); notepad_reset(); wm_bring_to_front(&win_notepad); handled = true;
} else if (str_ends_with(icon->name, "Calculator.shortcut")) { } else if (str_ends_with(icon->name, "Calculator.shortcut")) {
wm_bring_to_front(&win_calculator); wm_bring_to_front(&win_calculator); handled = true;
} else if (str_ends_with(icon->name, "Minesweeper.shortcut")) { } else if (str_ends_with(icon->name, "Minesweeper.shortcut")) {
wm_bring_to_front(&win_minesweeper); wm_bring_to_front(&win_minesweeper); handled = true;
} else if (str_ends_with(icon->name, "Control Panel.shortcut")) { } else if (str_ends_with(icon->name, "Control Panel.shortcut")) {
wm_bring_to_front(&win_control_panel); wm_bring_to_front(&win_control_panel); handled = true;
} else if (str_ends_with(icon->name, "Terminal.shortcut")) { } else if (str_ends_with(icon->name, "Terminal.shortcut")) {
wm_bring_to_front(&win_cmd); wm_bring_to_front(&win_cmd); handled = true;
} else if (str_ends_with(icon->name, "About.shortcut")) { } else if (str_ends_with(icon->name, "About.shortcut")) {
wm_bring_to_front(&win_about); wm_bring_to_front(&win_about); handled = true;
} else if (str_ends_with(icon->name, "Explorer.shortcut")) { } else if (str_ends_with(icon->name, "Explorer.shortcut")) {
explorer_open_directory("/"); explorer_open_directory("/"); handled = true;
} else if (str_ends_with(icon->name, "Recycle Bin.shortcut")) { } else if (str_ends_with(icon->name, "Recycle Bin.shortcut")) {
explorer_open_directory("/RecycleBin"); explorer_open_directory("/RecycleBin"); handled = true;
} else if (str_ends_with(icon->name, "Paint.shortcut")) { } else if (str_ends_with(icon->name, "Paint.shortcut")) {
wm_bring_to_front(&win_paint); wm_bring_to_front(&win_paint); handled = true;
} }
// Generic Shortcut Handling if (!handled) {
char path[128] = "/Desktop/"; // Generic Shortcut Handling
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0; char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
if (str_ends_with(icon->name, ".shortcut") && !str_starts_with(icon->name, "Recycle Bin")) {
FAT32_FileHandle *fh = fat32_open(path, "r"); if (str_ends_with(icon->name, ".shortcut") && !str_starts_with(icon->name, "Recycle Bin")) {
if (fh) { FAT32_FileHandle *fh = fat32_open(path, "r");
char buf[256]; if (fh) {
int len = fat32_read(fh, buf, 255); char buf[256];
fat32_close(fh); int len = fat32_read(fh, buf, 255);
if (len > 0) { fat32_close(fh);
buf[len] = 0; if (len > 0) {
if (fat32_is_directory(buf)) { buf[len] = 0;
explorer_open_directory(buf); if (fat32_is_directory(buf)) {
} else { explorer_open_directory(buf);
editor_open_file(buf); } else {
wm_bring_to_front(&win_editor); editor_open_file(buf);
wm_bring_to_front(&win_editor);
}
pending_desktop_icon_click = -1;
force_redraw = true;
return;
} }
pending_desktop_icon_click = -1;
return;
} }
} }
} }
@ -1626,7 +1643,6 @@ void wm_handle_right_click(int x, int y) {
if (dx < 0) dx = -dx; if (dx < 0) dx = -dx;
if (dy < 0) dy = -dy; if (dy < 0) dy = -dy;
if (dx < 35 && dy < 35) { if (dx < 35 && dy < 35) {
// Collision with non-folder (or we would have handled it)
// Revert position // Revert position
desktop_icons[dragged_idx].x = drag_icon_orig_x; desktop_icons[dragged_idx].x = drag_icon_orig_x;
desktop_icons[dragged_idx].y = drag_icon_orig_y; desktop_icons[dragged_idx].y = drag_icon_orig_y;
@ -1825,11 +1841,11 @@ uint32_t wm_get_ticks(void) {
void wm_timer_tick(void) { void wm_timer_tick(void) {
timer_ticks++; timer_ticks++;
// Auto-refresh desktop every second (approx 60 ticks) // Auto-refresh desktop every 5 seconds to save CPU in QEMU
// But NOT if we are currently dragging something, to avoid state conflicts // But NOT if the user is dragging a window or file.
if (!is_dragging && !is_dragging_file) { if (!is_dragging && !is_dragging_file) {
desktop_refresh_timer++; desktop_refresh_timer++;
if (desktop_refresh_timer >= 60) { if (desktop_refresh_timer >= 300) {
refresh_desktop_icons(); refresh_desktop_icons();
explorer_refresh_all(); explorer_refresh_all();
desktop_refresh_timer = 0; desktop_refresh_timer = 0;