Some optimizations.

This commit is contained in:
Scott Duensing 2026-03-10 00:37:35 -05:00
parent 7a1777ebfc
commit 73f7e37ba6
5 changed files with 152 additions and 108 deletions

View file

@ -155,7 +155,7 @@ static void compositeAndFlush(AppContextT *ctx) {
drawCursorAt(ctx, ctx->mouseX, ctx->mouseY); drawCursorAt(ctx, ctx->mouseX, ctx->mouseY);
// 5. Flush this dirty rect to LFB // 5. Flush this dirty rect to LFB
flushRect(d, ops, dr); flushRect(d, dr);
} }
resetClipRect(d); resetClipRect(d);

View file

@ -4,6 +4,8 @@
#include <string.h> #include <string.h>
#define DIRTY_MERGE_GAP 4
// ============================================================ // ============================================================
// Prototypes // Prototypes
// ============================================================ // ============================================================
@ -82,7 +84,7 @@ void dirtyListMerge(DirtyListT *dl) {
for (int32_t i = 0; i < dl->count; i++) { for (int32_t i = 0; i < dl->count; i++) {
for (int32_t j = i + 1; j < dl->count; j++) { for (int32_t j = i + 1; j < dl->count; j++) {
if (rectsOverlapOrAdjacent(&dl->rects[i], &dl->rects[j], 4)) { if (rectsOverlapOrAdjacent(&dl->rects[i], &dl->rects[j], DIRTY_MERGE_GAP)) {
rectUnion(&dl->rects[i], &dl->rects[j], &dl->rects[i]); rectUnion(&dl->rects[i], &dl->rects[j], &dl->rects[i]);
dl->rects[j] = dl->rects[dl->count - 1]; dl->rects[j] = dl->rects[dl->count - 1];
dl->count--; dl->count--;
@ -99,9 +101,7 @@ void dirtyListMerge(DirtyListT *dl) {
// flushRect // flushRect
// ============================================================ // ============================================================
void flushRect(DisplayT *d, const BlitOpsT *ops, const RectT *r) { void flushRect(DisplayT *d, const RectT *r) {
(void)ops;
int32_t bpp = d->format.bytesPerPixel; int32_t bpp = d->format.bytesPerPixel;
int32_t x = r->x; int32_t x = r->x;

View file

@ -17,7 +17,7 @@ void dirtyListMerge(DirtyListT *dl);
void dirtyListClear(DirtyListT *dl); void dirtyListClear(DirtyListT *dl);
// Flush a single rectangle from backbuffer to LFB // Flush a single rectangle from backbuffer to LFB
void flushRect(DisplayT *d, const BlitOpsT *ops, const RectT *r); void flushRect(DisplayT *d, const RectT *r);
// Intersect two rectangles, returns true if intersection is non-empty // Intersect two rectangles, returns true if intersection is non-empty
bool rectIntersect(const RectT *a, const RectT *b, RectT *result); bool rectIntersect(const RectT *a, const RectT *b, RectT *result);

View file

@ -368,6 +368,7 @@ int32_t videoInit(DisplayT *d, int32_t requestedW, int32_t requestedH, int32_t p
if (!d->backBuf) { if (!d->backBuf) {
fprintf(stderr, "VBE: Failed to allocate %lu byte backbuffer\n", (unsigned long)fbSize); fprintf(stderr, "VBE: Failed to allocate %lu byte backbuffer\n", (unsigned long)fbSize);
__djgpp_nearptr_disable();
return -1; return -1;
} }
@ -381,6 +382,7 @@ int32_t videoInit(DisplayT *d, int32_t requestedW, int32_t requestedH, int32_t p
fprintf(stderr, "VBE: Failed to allocate palette\n"); fprintf(stderr, "VBE: Failed to allocate palette\n");
free(d->backBuf); free(d->backBuf);
d->backBuf = NULL; d->backBuf = NULL;
__djgpp_nearptr_disable();
return -1; return -1;
} }

View file

@ -10,11 +10,37 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
// ============================================================
// Constants
// ============================================================
#define GADGET_PAD 2
#define GADGET_INSET 2 // inset from title bar edges
// ============================================================
// Title bar gadget geometry
// ============================================================
typedef struct {
int32_t titleX;
int32_t titleY;
int32_t titleW;
int32_t titleH;
int32_t gadgetS; // gadget square size
int32_t gadgetY; // gadget Y position
int32_t closeX; // close gadget X
int32_t minX; // minimize gadget X
int32_t maxX; // maximize gadget X (-1 if not resizable)
int32_t textLeftEdge; // left edge of title text area
int32_t textRightEdge; // right edge of title text area
} TitleGeomT;
// ============================================================ // ============================================================
// Prototypes // Prototypes
// ============================================================ // ============================================================
static void computeMenuBarPositions(WindowT *win, const BitmapFontT *font); static void computeMenuBarPositions(WindowT *win, const BitmapFontT *font);
static void computeTitleGeom(const WindowT *win, TitleGeomT *g);
static void drawBorderFrame(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t x, int32_t y, int32_t w, int32_t h); static void drawBorderFrame(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t x, int32_t y, int32_t w, int32_t h);
static void drawMenuBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, WindowT *win); static void drawMenuBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, WindowT *win);
static void drawResizeBreaks(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, WindowT *win); static void drawResizeBreaks(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, WindowT *win);
@ -50,6 +76,42 @@ static void computeMenuBarPositions(WindowT *win, const BitmapFontT *font) {
} }
// ============================================================
// computeTitleGeom
// ============================================================
//
// Compute title bar gadget positions. Used by both drawTitleBar()
// and wmHitTest() to keep geometry in sync.
static void computeTitleGeom(const WindowT *win, TitleGeomT *g) {
g->titleX = win->x + CHROME_BORDER_WIDTH;
g->titleY = win->y + CHROME_BORDER_WIDTH;
g->titleW = win->w - CHROME_BORDER_WIDTH * 2;
g->titleH = CHROME_TITLE_HEIGHT;
g->gadgetS = g->titleH - GADGET_INSET * 2;
g->gadgetY = g->titleY + GADGET_INSET;
// Close gadget on the left
g->closeX = g->titleX + GADGET_PAD;
// Rightmost gadget position (decremented as gadgets are placed)
int32_t rightX = g->titleX + g->titleW - GADGET_PAD - g->gadgetS;
if (win->resizable) {
g->maxX = rightX;
rightX -= g->gadgetS + GADGET_PAD;
} else {
g->maxX = -1;
}
g->minX = rightX;
// Text area between close gadget and minimize gadget
g->textLeftEdge = g->closeX + g->gadgetS + GADGET_PAD;
g->textRightEdge = g->minX - GADGET_PAD;
}
// ============================================================ // ============================================================
// drawBorderFrame // drawBorderFrame
// ============================================================ // ============================================================
@ -201,34 +263,46 @@ static void drawResizeBreaks(DisplayT *d, const BlitOpsT *ops, const ColorScheme
// ============================================================ // ============================================================
static void drawScaledRect(DisplayT *d, int32_t dstX, int32_t dstY, int32_t dstW, int32_t dstH, const uint8_t *src, int32_t srcW, int32_t srcH, int32_t srcPitch, int32_t bpp) { static void drawScaledRect(DisplayT *d, int32_t dstX, int32_t dstY, int32_t dstW, int32_t dstH, const uint8_t *src, int32_t srcW, int32_t srcH, int32_t srcPitch, int32_t bpp) {
for (int32_t dy = 0; dy < dstH; dy++) { // Pre-compute visible row/col range (clip against display clip rect)
int32_t sy = (dy * srcH) / dstH; int32_t rowStart = 0;
int32_t screenY = dstY + dy; int32_t rowEnd = dstH;
int32_t colStart = 0;
int32_t colEnd = dstW;
if (screenY < d->clipY || screenY >= d->clipY + d->clipH) { if (dstY < d->clipY) { rowStart = d->clipY - dstY; }
continue; if (dstY + dstH > d->clipY + d->clipH) { rowEnd = d->clipY + d->clipH - dstY; }
} if (dstX < d->clipX) { colStart = d->clipX - dstX; }
if (dstX + dstW > d->clipX + d->clipW) { colEnd = d->clipX + d->clipW - dstX; }
uint8_t *dstRow = d->backBuf + screenY * d->pitch; if (rowStart >= rowEnd || colStart >= colEnd) {
return;
}
// Pre-compute source X lookup table (one division per column instead of per pixel)
int32_t srcXTab[ICON_SIZE];
int32_t visibleCols = colEnd - colStart;
for (int32_t dx = 0; dx < visibleCols; dx++) {
srcXTab[dx] = ((colStart + dx) * srcW) / dstW * bpp;
}
// Blit with pre-computed lookups — no per-pixel divisions or clip checks
for (int32_t dy = rowStart; dy < rowEnd; dy++) {
int32_t sy = (dy * srcH) / dstH;
uint8_t *dstRow = d->backBuf + (dstY + dy) * d->pitch + (dstX + colStart) * bpp;
const uint8_t *srcRow = src + sy * srcPitch; const uint8_t *srcRow = src + sy * srcPitch;
for (int32_t dx = 0; dx < dstW; dx++) { if (bpp == 1) {
int32_t sx = (dx * srcW) / dstW; for (int32_t dx = 0; dx < visibleCols; dx++) {
int32_t screenX = dstX + dx; dstRow[dx] = srcRow[srcXTab[dx]];
if (screenX < d->clipX || screenX >= d->clipX + d->clipW) {
continue;
} }
} else if (bpp == 2) {
const uint8_t *srcPx = srcRow + sx * bpp; for (int32_t dx = 0; dx < visibleCols; dx++) {
uint8_t *dstPx = dstRow + screenX * bpp; *(uint16_t *)(dstRow + dx * 2) = *(const uint16_t *)(srcRow + srcXTab[dx]);
}
if (bpp == 1) { } else {
*dstPx = *srcPx; for (int32_t dx = 0; dx < visibleCols; dx++) {
} else if (bpp == 2) { *(uint32_t *)(dstRow + dx * 4) = *(const uint32_t *)(srcRow + srcXTab[dx]);
*(uint16_t *)dstPx = *(const uint16_t *)srcPx;
} else {
*(uint32_t *)dstPx = *(const uint32_t *)srcPx;
} }
} }
} }
@ -345,45 +419,30 @@ static void drawScrollbarArrow(DisplayT *d, const BlitOpsT *ops, const ColorSche
// ============================================================ // ============================================================
static void drawTitleBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, WindowT *win) { static void drawTitleBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, WindowT *win) {
int32_t titleX = win->x + CHROME_BORDER_WIDTH; TitleGeomT g;
int32_t titleY = win->y + CHROME_BORDER_WIDTH; computeTitleGeom(win, &g);
int32_t titleW = win->w - CHROME_BORDER_WIDTH * 2;
int32_t titleH = CHROME_TITLE_HEIGHT;
uint32_t bg = win->focused ? colors->activeTitleBg : colors->inactiveTitleBg; uint32_t bg = win->focused ? colors->activeTitleBg : colors->inactiveTitleBg;
uint32_t fg = win->focused ? colors->activeTitleFg : colors->inactiveTitleFg; uint32_t fg = win->focused ? colors->activeTitleFg : colors->inactiveTitleFg;
// Fill title bar background // Fill title bar background
rectFill(d, ops, titleX, titleY, titleW, titleH, bg); rectFill(d, ops, g.titleX, g.titleY, g.titleW, g.titleH, bg);
// GEOS Motif gadget sizing
int32_t gadgetS = titleH - 4; // gadget size, fits within title bar
int32_t gadgetY = titleY + 2;
int32_t gadgetPad = 2;
// Close gadget on the LEFT (system menu / close) // Close gadget on the LEFT (system menu / close)
int32_t closeX = titleX + gadgetPad; drawTitleGadget(d, ops, colors, g.closeX, g.gadgetY, g.gadgetS);
drawTitleGadget(d, ops, colors, closeX, gadgetY, gadgetS);
// Horizontal bar icon inside close gadget // Horizontal bar icon inside close gadget
drawHLine(d, ops, closeX + 3, gadgetY + gadgetS / 2, drawHLine(d, ops, g.closeX + 3, g.gadgetY + g.gadgetS / 2,
gadgetS - 6, colors->contentFg); g.gadgetS - 6, colors->contentFg);
// Right edge of title text area if (g.maxX >= 0) {
int32_t textRightEdge = titleX + titleW - gadgetPad;
// Rightmost gadget position
int32_t rightGadgetX = titleX + titleW - gadgetPad - gadgetS;
if (win->resizable) {
// Maximize/restore gadget on the far RIGHT // Maximize/restore gadget on the far RIGHT
int32_t maxX = rightGadgetX; drawTitleGadget(d, ops, colors, g.maxX, g.gadgetY, g.gadgetS);
drawTitleGadget(d, ops, colors, maxX, gadgetY, gadgetS);
if (win->maximized) { if (win->maximized) {
// Restore icon: larger box outline filling more of the gadget // Restore icon: larger box outline filling more of the gadget
int32_t bx = maxX + 2; int32_t bx = g.maxX + 2;
int32_t by = gadgetY + 2; int32_t by = g.gadgetY + 2;
int32_t bs = gadgetS - 4; int32_t bs = g.gadgetS - 4;
rectFill(d, ops, bx, by, bs, bs, colors->buttonFace); rectFill(d, ops, bx, by, bs, bs, colors->buttonFace);
drawHLine(d, ops, bx, by, bs, colors->contentFg); drawHLine(d, ops, bx, by, bs, colors->contentFg);
drawHLine(d, ops, bx, by + 1, bs, colors->contentFg); drawHLine(d, ops, bx, by + 1, bs, colors->contentFg);
@ -392,30 +451,24 @@ static void drawTitleBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fo
drawVLine(d, ops, bx + bs - 1, by, bs, colors->contentFg); drawVLine(d, ops, bx + bs - 1, by, bs, colors->contentFg);
} else { } else {
// Maximize icon: small box outline // Maximize icon: small box outline
rectFill(d, ops, maxX + 3, gadgetY + 3, rectFill(d, ops, g.maxX + 3, g.gadgetY + 3,
gadgetS - 6, gadgetS - 6, colors->buttonFace); g.gadgetS - 6, g.gadgetS - 6, colors->buttonFace);
drawHLine(d, ops, maxX + 3, gadgetY + 3, gadgetS - 6, colors->contentFg); drawHLine(d, ops, g.maxX + 3, g.gadgetY + 3, g.gadgetS - 6, colors->contentFg);
drawHLine(d, ops, maxX + 3, gadgetY + gadgetS - 4, gadgetS - 6, colors->contentFg); drawHLine(d, ops, g.maxX + 3, g.gadgetY + g.gadgetS - 4, g.gadgetS - 6, colors->contentFg);
drawVLine(d, ops, maxX + 3, gadgetY + 3, gadgetS - 6, colors->contentFg); drawVLine(d, ops, g.maxX + 3, g.gadgetY + 3, g.gadgetS - 6, colors->contentFg);
drawVLine(d, ops, maxX + gadgetS - 4, gadgetY + 3, gadgetS - 6, colors->contentFg); drawVLine(d, ops, g.maxX + g.gadgetS - 4, g.gadgetY + 3, g.gadgetS - 6, colors->contentFg);
} }
rightGadgetX -= gadgetS + gadgetPad;
} }
// Minimize gadget (always present, to the left of maximize or on the far right) // Minimize gadget (always present)
int32_t minX = rightGadgetX; drawTitleGadget(d, ops, colors, g.minX, g.gadgetY, g.gadgetS);
drawTitleGadget(d, ops, colors, minX, gadgetY, gadgetS);
// Small square centered in minimize gadget // Small square centered in minimize gadget
int32_t miniSize = 4; int32_t miniSize = 4;
rectFill(d, ops, minX + (gadgetS - miniSize) / 2, gadgetY + (gadgetS - miniSize) / 2, rectFill(d, ops, g.minX + (g.gadgetS - miniSize) / 2, g.gadgetY + (g.gadgetS - miniSize) / 2,
miniSize, miniSize, colors->contentFg); miniSize, miniSize, colors->contentFg);
textRightEdge = minX - gadgetPad; // Title text — centered between close gadget and minimize, truncated to fit
int32_t availW = g.textRightEdge - g.textLeftEdge;
// Title text — centered between close gadget and right edge, truncated to fit
int32_t textLeftEdge = closeX + gadgetS + gadgetPad;
int32_t availW = textRightEdge - textLeftEdge;
if (availW > 0) { if (availW > 0) {
int32_t maxChars = availW / font->charWidth; int32_t maxChars = availW / font->charWidth;
@ -426,15 +479,16 @@ static void drawTitleBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fo
} }
if (len > 0) { if (len > 0) {
int32_t textW = len * font->charWidth; // Build truncated title for drawText
int32_t textX = textLeftEdge + (availW - textW) / 2; char truncTitle[MAX_TITLE_LEN];
int32_t textY = titleY + (titleH - font->charHeight) / 2; memcpy(truncTitle, win->title, len);
truncTitle[len] = '\0';
// Draw truncated title character by character int32_t textW = len * font->charWidth;
for (int32_t i = 0; i < len; i++) { int32_t textX = g.textLeftEdge + (availW - textW) / 2;
drawChar(d, ops, font, textX + i * font->charWidth, textY, int32_t textY = g.titleY + (g.titleH - font->charHeight) / 2;
win->title[i], fg, bg, true);
} drawText(d, ops, font, textX, textY, truncTitle, fg, bg, true);
} }
} }
} }
@ -464,13 +518,13 @@ static int32_t scrollbarThumbInfo(const ScrollbarT *sb, int32_t *thumbPos, int32
return trackLen; return trackLen;
} }
*thumbSize = (sb->pageSize * trackLen) / (range + sb->pageSize); *thumbSize = (int32_t)(((int64_t)sb->pageSize * trackLen) / (range + sb->pageSize));
if (*thumbSize < SCROLLBAR_WIDTH) { if (*thumbSize < SCROLLBAR_WIDTH) {
*thumbSize = SCROLLBAR_WIDTH; *thumbSize = SCROLLBAR_WIDTH;
} }
*thumbPos = ((sb->value - sb->min) * (trackLen - *thumbSize)) / range; *thumbPos = (int32_t)(((int64_t)(sb->value - sb->min) * (trackLen - *thumbSize)) / range);
return trackLen; return trackLen;
} }
@ -944,46 +998,34 @@ int32_t wmHitTest(const WindowStackT *stack, int32_t mx, int32_t my, int32_t *hi
continue; continue;
} }
// Title bar gadget geometry (must match drawTitleBar) TitleGeomT g;
int32_t titleX = win->x + CHROME_BORDER_WIDTH; computeTitleGeom(win, &g);
int32_t titleY = win->y + CHROME_BORDER_WIDTH;
int32_t titleW = win->w - CHROME_BORDER_WIDTH * 2;
int32_t gadgetS = CHROME_TITLE_HEIGHT - 4;
int32_t gadgetY = titleY + 2;
int32_t gadgetP = 2;
// Close gadget (top-left) // Close gadget (top-left)
int32_t closeX = titleX + gadgetP; if (mx >= g.closeX && mx < g.closeX + g.gadgetS &&
if (mx >= closeX && mx < closeX + gadgetS && my >= g.gadgetY && my < g.gadgetY + g.gadgetS) {
my >= gadgetY && my < gadgetY + gadgetS) {
*hitPart = 2; *hitPart = 2;
return i; return i;
} }
// Rightmost gadget position // Maximize gadget (resizable windows only)
int32_t rightGadgetX = titleX + titleW - gadgetP - gadgetS; if (g.maxX >= 0 &&
mx >= g.maxX && mx < g.maxX + g.gadgetS &&
// Maximize gadget only on resizable windows my >= g.gadgetY && my < g.gadgetY + g.gadgetS) {
if (win->resizable) { *hitPart = 8;
if (mx >= rightGadgetX && mx < rightGadgetX + gadgetS && return i;
my >= gadgetY && my < gadgetY + gadgetS) {
*hitPart = 8;
return i;
}
rightGadgetX -= gadgetS + gadgetP;
} }
// Minimize gadget (always present) // Minimize gadget (always present)
if (mx >= rightGadgetX && mx < rightGadgetX + gadgetS && if (mx >= g.minX && mx < g.minX + g.gadgetS &&
my >= gadgetY && my < gadgetY + gadgetS) { my >= g.gadgetY && my < g.gadgetY + g.gadgetS) {
*hitPart = 7; *hitPart = 7;
return i; return i;
} }
// Title bar (drag area — between gadgets) // Title bar (drag area — between gadgets)
if (my >= titleY && my < titleY + CHROME_TITLE_HEIGHT && if (my >= g.titleY && my < g.titleY + CHROME_TITLE_HEIGHT &&
mx >= titleX && mx < titleX + titleW) { mx >= g.titleX && mx < g.titleX + g.titleW) {
*hitPart = 1; *hitPart = 1;
return i; return i;
} }