[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 \
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
-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 =
"# 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;
// 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;
uint32_t color = g_bg_pattern[py * PATTERN_SIZE + px];
put_pixel(x, y, color);
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;
if (!g_fb || !g_dirty.active) return;
// Copy back buffer to framebuffer
uint32_t *src = g_back_buffer;
uint8_t *dst = (uint8_t *)g_fb->address;
int x = g_dirty.x;
int y = g_dirty.y;
int w = g_dirty.w;
int h = g_dirty.h;
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 (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,12 +697,12 @@ 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
@ -711,6 +711,12 @@ void wm_paint(void) {
// 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,28 +1382,30 @@ 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;
}
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;
@ -1410,10 +1425,12 @@ void wm_handle_right_click(int x, int y) {
wm_bring_to_front(&win_editor);
}
pending_desktop_icon_click = -1;
force_redraw = true;
return;
}
}
}
}
} else if (icon->type == 1) { // Folder
char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
@ -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;