[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

@@ -1056,7 +1056,7 @@ static void create_test_files(void) {
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"
"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_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+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+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+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+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+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+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,\"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+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+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+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 \");",

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) {
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
put_pixel(x + j, y + i, color);
if (!g_fb) return;
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) {
unsigned char uc = (unsigned char)c;
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];
for (int row = 0; row < 8; row++) {
@@ -174,13 +203,18 @@ void draw_desktop_background(void) {
if (!g_fb) return;
if (g_use_pattern) {
// Draw tiled pattern
for (int y = 0; y < (int)g_fb->height; y++) {
for (int x = 0; x < (int)g_fb->width; x++) {
int px = x % PATTERN_SIZE;
int py = y % PATTERN_SIZE;
uint32_t color = g_bg_pattern[py * PATTERN_SIZE + px];
put_pixel(x, y, color);
// Optimized tiled pattern: only draw within the clipping/dirty rect
int x1 = 0, y1 = 0, x2 = g_fb->width, y2 = g_fb->height;
if (g_clip_enabled) {
x1 = g_clip_x; y1 = g_clip_y;
x2 = g_clip_x + g_clip_w; y2 = g_clip_y + g_clip_h;
}
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 {
@@ -214,20 +248,27 @@ void graphics_clear_back_buffer(uint32_t color) {
}
void graphics_flip_buffer(void) {
if (!g_fb) return;
// Copy back buffer to framebuffer
uint32_t *src = g_back_buffer;
uint8_t *dst = (uint8_t *)g_fb->address;
for (int y = 0; y < (int)g_fb->height; y++) {
// Copy one scanline
uint32_t *dst_row = (uint32_t *)dst;
for (int x = 0; x < (int)g_fb->width; x++) {
dst_row[x] = src[x];
if (!g_fb || !g_dirty.active) return;
int x = g_dirty.x;
int y = g_dirty.y;
int w = g_dirty.w;
int h = g_dirty.h;
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
if (x + w > (int)g_fb->width) w = g_fb->width - 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
bool desktop_snap_to_grid = true;
bool desktop_auto_align = true;
int desktop_max_rows_per_col = 9;
int desktop_max_cols = 15;
int desktop_max_rows_per_col = 13;
int desktop_max_cols = 23;
// Helper to check if string ends with 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 sh = get_screen_height();
// Ensure no stale clipping state interferes with the new frame
graphics_clear_clipping();
// First, erase the old cursor (before redrawing anything)
if (cursor_visible) {
erase_cursor(last_cursor_x, last_cursor_y);
DirtyRect dirty = graphics_get_dirty_rect();
if (dirty.active) {
graphics_set_clipping(dirty.x, dirty.y, dirty.w, dirty.h);
} else {
graphics_clear_clipping();
}
// 1. Desktop
draw_desktop_background();
// Draw Desktop Icons
for (int i = 0; i < desktop_icon_count; 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);
else if (icon->type == 2) {
// App icon - strip .app for display
@@ -765,7 +771,15 @@ void wm_paint(void) {
// Draw windows in z-order (lowest first)
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
@@ -917,6 +931,7 @@ void wm_bring_to_front(Window *win) {
win->visible = true;
win->focused = true;
win->z_index = max_z + 1;
force_redraw = true;
}
void wm_add_window(Window *win) {
@@ -989,7 +1004,6 @@ void wm_handle_click(int x, int y) {
}
refresh_desktop_icons();
// If auto-align is OFF and we pasted to the background, place at click location
if (!desktop_auto_align && desktop_icon_count > old_count && desktop_menu_target_icon == -1) {
int new_idx = desktop_icon_count - 1;
desktop_icons[new_idx].x = desktop_menu_x - 20;
@@ -1292,7 +1306,7 @@ void wm_handle_right_click(int x, int y) {
DesktopIcon *icon = &desktop_icons[i];
is_dragging_file = true;
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_y = icon->y;
// Construct path
@@ -1312,7 +1326,6 @@ void wm_handle_right_click(int x, int y) {
for (int w = 0; w < window_count; w++) {
Window *win = all_windows[w];
if (win->visible && rect_contains(win->x, win->y, win->w, win->h, drag_start_x, drag_start_y)) {
// This is a bit of a hack, but we check if it's an explorer window
if (str_starts_with(win->title, "File Explorer")) {
drag_src_win = win;
explorer_clear_click_state(win);
@@ -1369,48 +1382,52 @@ void wm_handle_right_click(int x, int y) {
int i = pending_desktop_icon_click;
if (i < desktop_icon_count) {
DesktopIcon *icon = &desktop_icons[i];
bool handled = false;
if (icon->type == 2) { // App Shortcut
// Check name to launch app
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")) {
wm_bring_to_front(&win_calculator);
wm_bring_to_front(&win_calculator); handled = true;
} 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")) {
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")) {
wm_bring_to_front(&win_cmd);
wm_bring_to_front(&win_cmd); handled = true;
} 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")) {
explorer_open_directory("/");
explorer_open_directory("/"); handled = true;
} 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")) {
wm_bring_to_front(&win_paint);
wm_bring_to_front(&win_paint); handled = true;
}
// Generic Shortcut Handling
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 (fh) {
char buf[256];
int len = fat32_read(fh, buf, 255);
fat32_close(fh);
if (len > 0) {
buf[len] = 0;
if (fat32_is_directory(buf)) {
explorer_open_directory(buf);
} else {
editor_open_file(buf);
wm_bring_to_front(&win_editor);
if (!handled) {
// Generic Shortcut Handling
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 (fh) {
char buf[256];
int len = fat32_read(fh, buf, 255);
fat32_close(fh);
if (len > 0) {
buf[len] = 0;
if (fat32_is_directory(buf)) {
explorer_open_directory(buf);
} else {
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 (dy < 0) dy = -dy;
if (dx < 35 && dy < 35) {
// Collision with non-folder (or we would have handled it)
// Revert position
desktop_icons[dragged_idx].x = drag_icon_orig_x;
desktop_icons[dragged_idx].y = drag_icon_orig_y;
@@ -1825,11 +1841,11 @@ uint32_t wm_get_ticks(void) {
void wm_timer_tick(void) {
timer_ticks++;
// Auto-refresh desktop every second (approx 60 ticks)
// But NOT if we are currently dragging something, to avoid state conflicts
// Auto-refresh desktop every 5 seconds to save CPU in QEMU
// But NOT if the user is dragging a window or file.
if (!is_dragging && !is_dragging_file) {
desktop_refresh_timer++;
if (desktop_refresh_timer >= 60) {
if (desktop_refresh_timer >= 300) {
refresh_desktop_icons();
explorer_refresh_all();
desktop_refresh_timer = 0;