More optimizing.

This commit is contained in:
Scott Duensing 2026-03-14 20:27:59 -05:00
parent 54d9180d3e
commit e489f54ef0
17 changed files with 118 additions and 74 deletions

View file

@ -137,8 +137,6 @@ static void compositeAndFlush(AppContextT *ctx) {
RectT popIsect;
if (rectIntersect(dr, &popRect, &popIsect)) {
setClipRect(d, dr->x, dr->y, dr->w, dr->h);
// Find the window and menu
for (int32_t j = 0; j < ws->count; j++) {
if (ws->windows[j]->id == ctx->popup.windowId) {
@ -202,8 +200,6 @@ static void compositeAndFlush(AppContextT *ctx) {
RectT smIsect;
if (rectIntersect(dr, &smRect, &smIsect)) {
setClipRect(d, dr->x, dr->y, dr->w, dr->h);
BevelStyleT smBevel;
smBevel.highlight = ctx->colors.windowHighlight;
smBevel.shadow = ctx->colors.windowShadow;
@ -465,10 +461,10 @@ static void dispatchEvents(AppContextT *ctx) {
if (mx != ctx->prevMouseX || my != ctx->prevMouseY) {
dirtyCursorArea(ctx, ctx->prevMouseX, ctx->prevMouseY);
dirtyCursorArea(ctx, mx, my);
}
// Update cursor shape based on what the mouse is hovering over
updateCursorShape(ctx);
}
// Handle active drag
if (ctx->stack.dragWindow >= 0) {
@ -1380,9 +1376,16 @@ static void pollAnsiTermWidgetsWalk(AppContextT *ctx, WidgetT *w, WindowT *win)
static void pollKeyboard(AppContextT *ctx) {
__dpmi_regs r;
// Check if key is available (INT 16h, enhanced function 11h)
while (1) {
// Read shift state once per poll (INT 16h, AH=12h)
memset(&r, 0, sizeof(r));
r.x.ax = 0x1200;
__dpmi_int(0x16, &r);
int32_t shiftFlags = r.x.ax & 0xFF;
bool shiftHeld = (shiftFlags & 0x03) != 0; // left or right shift
// Process buffered keys
while (1) {
// Check if key is available (INT 16h, enhanced function 11h)
r.x.ax = 0x1100;
__dpmi_int(0x16, &r);
@ -1392,7 +1395,6 @@ static void pollKeyboard(AppContextT *ctx) {
}
// Read the key (INT 16h, enhanced function 10h)
memset(&r, 0, sizeof(r));
r.x.ax = 0x1000;
__dpmi_int(0x16, &r);
@ -1406,13 +1408,6 @@ static void pollKeyboard(AppContextT *ctx) {
ascii = 0;
}
// Read shift state (INT 16h, AH=12h — enhanced shift flags)
memset(&r, 0, sizeof(r));
r.x.ax = 0x1200;
__dpmi_int(0x16, &r);
int32_t shiftFlags = r.x.ax & 0xFF;
bool shiftHeld = (shiftFlags & 0x03) != 0; // left or right shift
// Alt+Tab / Shift+Alt+Tab — cycle windows
// Alt+Tab: scancode=0xA5, ascii=0x00
if (ascii == 0 && scancode == 0xA5) {

View file

@ -77,12 +77,15 @@ void dirtyListMerge(DirtyListT *dl) {
return;
}
// Single-pass O(N²): for each rect, try to merge it into an
// earlier rect. When a merge succeeds the merged rect may now
// overlap others, so restart the inner scan for that slot.
for (int32_t i = 0; i < dl->count; i++) {
bool merged = true;
while (merged) {
merged = false;
for (int32_t i = 0; i < dl->count; i++) {
for (int32_t j = i + 1; j < dl->count; j++) {
if (rectsOverlapOrAdjacent(&dl->rects[i], &dl->rects[j], DIRTY_MERGE_GAP)) {
rectUnion(&dl->rects[i], &dl->rects[j], &dl->rects[i]);
@ -104,15 +107,12 @@ void dirtyListMerge(DirtyListT *dl) {
void flushRect(DisplayT *d, const RectT *r) {
int32_t bpp = d->format.bytesPerPixel;
// Caller (compositeAndFlush) already clips to screen bounds
int32_t x = r->x;
int32_t y = r->y;
int32_t w = r->w;
int32_t h = r->h;
if (__builtin_expect(x < 0, 0)) { w += x; x = 0; }
if (__builtin_expect(y < 0, 0)) { h += y; y = 0; }
if (__builtin_expect(x + w > d->width, 0)) { w = d->width - x; }
if (__builtin_expect(y + h > d->height, 0)) { h = d->height - y; }
if (__builtin_expect(w <= 0 || h <= 0, 0)) { return; }
int32_t rowBytes = w * bpp;

View file

@ -576,6 +576,9 @@ int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *outY, int32_t *outH);
// Mark a widget (and ancestors) for relayout and repaint
void wgtInvalidate(WidgetT *w);
// Repaint only — skip measure/layout (use for visual-only changes)
void wgtInvalidatePaint(WidgetT *w);
// Set/get widget text (label, button, textInput, etc.)
void wgtSetText(WidgetT *w, const char *text);
const char *wgtGetText(const WidgetT *w);

View file

@ -1553,7 +1553,7 @@ void widgetAnsiTermOnKey(WidgetT *w, int32_t key, int32_t mod) {
if (ansiTermHasSelection(w)) {
ansiTermCopySelection(w);
ansiTermClearSelection(w);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1563,7 +1563,7 @@ void widgetAnsiTermOnKey(WidgetT *w, int32_t key, int32_t mod) {
// Ctrl+V: paste from clipboard to terminal
if (key == 0x16 && (mod & KEY_MOD_CTRL)) {
ansiTermPasteToComm(w);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1645,7 +1645,7 @@ void widgetAnsiTermOnKey(WidgetT *w, int32_t key, int32_t mod) {
w->as.ansiTerm.commWrite(w->as.ansiTerm.commCtx, buf, len);
}
wgtInvalidate(w);
wgtInvalidatePaint(w);
}
@ -1822,8 +1822,18 @@ void widgetAnsiTermPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
// Determine if viewing live terminal or scrollback
bool viewingLive = (w->as.ansiTerm.scrollPos == sbCount);
// Render character cells row by row using bulk renderer
// Render character cells row by row using bulk renderer.
// Only repaint rows marked dirty; 0xFFFFFFFF means all rows.
uint32_t dirty = w->as.ansiTerm.dirtyRows;
if (dirty == 0) {
dirty = 0xFFFFFFFF;
}
for (int32_t row = 0; row < rows; row++) {
if (row < 32 && !(dirty & (1U << row))) {
continue;
}
int32_t lineIndex = w->as.ansiTerm.scrollPos + row;
const uint8_t *lineData = ansiTermGetLine(w, lineIndex);
@ -1838,6 +1848,8 @@ void widgetAnsiTermPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
ansiTermPaintSelRow(w, d, ops, font, row, baseX, baseY);
}
w->as.ansiTerm.dirtyRows = 0;
// Draw scrollbar
int32_t sbX = baseX + cols * cellW;
int32_t sbY = baseY;

View file

@ -59,7 +59,7 @@ void widgetButtonOnKey(WidgetT *w, int32_t key, int32_t mod) {
if (key == ' ' || key == 0x0D) {
w->as.button.pressed = true;
sKeyPressedBtn = w;
wgtInvalidate(w);
wgtInvalidatePaint(w);
}
}

View file

@ -709,7 +709,7 @@ void widgetCanvasOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) {
hit->as.canvas.lastX = cx;
hit->as.canvas.lastY = cy;
wgtInvalidate(hit);
wgtInvalidatePaint(hit);
}

View file

@ -64,7 +64,7 @@ void widgetCheckboxOnKey(WidgetT *w, int32_t key, int32_t mod) {
w->onChange(w);
}
wgtInvalidate(w);
wgtInvalidatePaint(w);
}
}

View file

@ -154,7 +154,7 @@ void widgetComboBoxOnKey(WidgetT *w, int32_t key, int32_t mod) {
}
}
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -167,7 +167,7 @@ void widgetComboBoxOnKey(WidgetT *w, int32_t key, int32_t mod) {
}
}
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -194,7 +194,7 @@ void widgetComboBoxOnKey(WidgetT *w, int32_t key, int32_t mod) {
w->onChange(w);
}
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
}
@ -213,7 +213,7 @@ void widgetComboBoxOnKey(WidgetT *w, int32_t key, int32_t mod) {
w->as.comboBox.listScrollPos = w->as.comboBox.hoverIdx;
}
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -426,7 +426,7 @@ void widgetComboBoxPaintPopup(WidgetT *w, DisplayT *d, const BlitOpsT *ops, cons
rectFill(d, ops, popX + 2, iy, textW + TEXT_INPUT_PAD * 2, font->charHeight, ibg);
}
drawText(d, ops, font, textX, iy, items[idx], ifg, ibg, idx == hoverIdx);
drawText(d, ops, font, textX, iy, items[idx], ifg, ibg, false);
}
}

View file

@ -166,7 +166,7 @@ void widgetDropdownOnKey(WidgetT *w, int32_t key, int32_t mod) {
}
}
wgtInvalidate(w);
wgtInvalidatePaint(w);
}
@ -280,6 +280,6 @@ void widgetDropdownPaintPopup(WidgetT *w, DisplayT *d, const BlitOpsT *ops, cons
rectFill(d, ops, popX + 2, iy, textW + TEXT_INPUT_PAD * 2, font->charHeight, ibg);
}
drawText(d, ops, font, textX, iy, items[idx], ifg, ibg, idx == hoverIdx);
drawText(d, ops, font, textX, iy, items[idx], ifg, ibg, false);
}
}

View file

@ -192,21 +192,21 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
sDrawingCanvas->as.canvas.lastX = -1;
sDrawingCanvas->as.canvas.lastY = -1;
sDrawingCanvas = NULL;
wgtInvalidate(root);
wgtInvalidatePaint(root);
return;
}
// Handle canvas drawing (mouse move while pressed)
if (sDrawingCanvas && (buttons & 1)) {
widgetCanvasOnMouse(sDrawingCanvas, root, x, y);
wgtInvalidate(root);
wgtInvalidatePaint(root);
return;
}
// Handle slider drag release
if (sDragSlider && !(buttons & 1)) {
sDragSlider = NULL;
wgtInvalidate(root);
wgtInvalidatePaint(root);
return;
}
@ -242,7 +242,7 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
sDragSlider->onChange(sDragSlider);
}
wgtInvalidate(root);
wgtInvalidatePaint(root);
}
}
@ -272,7 +272,7 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
}
}
wgtInvalidate(sPressedButton);
wgtInvalidatePaint(sPressedButton);
sPressedButton = NULL;
return;
}
@ -302,7 +302,7 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
sPressedButton->as.button.pressed = over;
}
wgtInvalidate(sPressedButton);
wgtInvalidatePaint(sPressedButton);
}
return;
@ -376,7 +376,7 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
}
sOpenPopup = NULL;
wgtInvalidate(root);
wgtInvalidatePaint(root);
// Fall through to normal click handling
}

View file

@ -141,7 +141,7 @@ void widgetImageOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
(void)vx;
(void)vy;
w->as.image.pressed = true;
wgtInvalidate(w);
wgtInvalidatePaint(w);
if (w->onClick) {
w->onClick(w);

View file

@ -77,7 +77,7 @@ void widgetImageButtonOnKey(WidgetT *w, int32_t key, int32_t mod) {
if (key == ' ' || key == 0x0D) {
w->as.imageButton.pressed = true;
sKeyPressedBtn = w;
wgtInvalidate(w);
wgtInvalidatePaint(w);
}
}

View file

@ -156,7 +156,7 @@ void widgetListBoxOnKey(WidgetT *w, int32_t key, int32_t mod) {
w->onChange(w);
}
wgtInvalidate(w);
wgtInvalidatePaint(w);
}
@ -292,7 +292,7 @@ void widgetListBoxPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitm
rectFill(d, ops, w->x + LISTBOX_BORDER, iy, contentW, font->charHeight, ibg);
}
drawText(d, ops, font, innerX, iy, w->as.listBox.items[idx], ifg, ibg, idx == w->as.listBox.selectedIdx);
drawText(d, ops, font, innerX, iy, w->as.listBox.items[idx], ifg, ibg, false);
}
// Draw scrollbar

View file

@ -250,6 +250,40 @@ void wgtInvalidate(WidgetT *w) {
}
// ============================================================
// wgtInvalidatePaint
// ============================================================
//
// Lightweight repaint — skips measure/layout/scrollbar management.
// Use when only visual state changed (slider value, cursor blink,
// selection highlight, checkbox toggle) but widget sizes are stable.
void wgtInvalidatePaint(WidgetT *w) {
if (!w || !w->window) {
return;
}
WidgetT *root = w;
while (root->parent) {
root = root->parent;
}
AppContextT *ctx = (AppContextT *)root->userData;
if (!ctx) {
return;
}
// Repaint without measure/layout
RectT fullRect = {0, 0, w->window->contentW, w->window->contentH};
widgetOnPaint(w->window, &fullRect);
w->window->contentDirty = true;
dvxInvalidateWindow(ctx, w->window);
}
// ============================================================
// wgtPaint
// ============================================================

View file

@ -92,7 +92,7 @@ void widgetRadioOnKey(WidgetT *w, int32_t key, int32_t mod) {
}
}
wgtInvalidate(w);
wgtInvalidatePaint(w);
} else if (key == (0x50 | 0x100) || key == (0x4D | 0x100)) {
// Down or Right — next radio in group
if (w->parent && w->parent->type == WidgetRadioGroupE) {
@ -115,7 +115,7 @@ void widgetRadioOnKey(WidgetT *w, int32_t key, int32_t mod) {
next->parent->onChange(next->parent);
}
wgtInvalidate(next);
wgtInvalidatePaint(next);
}
}
} else if (key == (0x48 | 0x100) || key == (0x4B | 0x100)) {
@ -139,7 +139,7 @@ void widgetRadioOnKey(WidgetT *w, int32_t key, int32_t mod) {
prev->parent->onChange(prev->parent);
}
wgtInvalidate(prev);
wgtInvalidatePaint(prev);
}
}
}

View file

@ -119,7 +119,7 @@ void widgetSliderOnKey(WidgetT *w, int32_t key, int32_t mod) {
w->onChange(w);
}
wgtInvalidate(w);
wgtInvalidatePaint(w);
}

View file

@ -697,7 +697,7 @@ done:
}
}
wgtInvalidate(w);
wgtInvalidatePaint(w);
}
@ -948,7 +948,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
textAreaOffToRowCol(buf, *pLen, pRow, pCol);
w->as.textArea.desiredCol = *pCol;
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -996,7 +996,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
}
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1023,7 +1023,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
}
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1067,7 +1067,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
}
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1105,7 +1105,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
}
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1144,7 +1144,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
}
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1181,7 +1181,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
}
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1197,7 +1197,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
w->as.textArea.desiredCol = *pCol;
SEL_END();
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1213,7 +1213,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
w->as.textArea.desiredCol = *pCol;
SEL_END();
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1229,7 +1229,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
SEL_END();
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1245,7 +1245,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
SEL_END();
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1256,7 +1256,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
w->as.textArea.desiredCol = 0;
SEL_END();
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1267,7 +1267,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
w->as.textArea.desiredCol = *pCol;
SEL_END();
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1284,7 +1284,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
*pCol = w->as.textArea.desiredCol < lineL ? w->as.textArea.desiredCol : lineL;
SEL_END();
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1301,7 +1301,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
*pCol = w->as.textArea.desiredCol < lineL ? w->as.textArea.desiredCol : lineL;
SEL_END();
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1313,7 +1313,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
w->as.textArea.desiredCol = 0;
SEL_END();
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1324,7 +1324,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
w->as.textArea.desiredCol = *pCol;
SEL_END();
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -1361,7 +1361,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
}
textAreaEnsureVisible(w, visRows, visCols);
wgtInvalidate(w);
wgtInvalidatePaint(w);
return;
}
@ -2520,5 +2520,5 @@ adjustScroll:
}
}
wgtInvalidate(w);
wgtInvalidatePaint(w);
}