From 0cede78932896beed1750920ed3d1fc6dfb9fc89 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Wed, 18 Mar 2026 00:45:35 -0500 Subject: [PATCH] Window movement and resizing clamped to prevent moving them entirely offscreen. --- dvx/dvxApp.c | 46 ++++++++++++++++++++++++++++++++++++++-------- dvx/dvxWm.c | 37 +++++++++++++++++++++++++++++++++++-- dvx/dvxWm.h | 2 +- 3 files changed, 74 insertions(+), 11 deletions(-) diff --git a/dvx/dvxApp.c b/dvx/dvxApp.c index 64a307d..ea07058 100644 --- a/dvx/dvxApp.c +++ b/dvx/dvxApp.c @@ -770,7 +770,7 @@ static void dispatchEvents(AppContextT *ctx) { // Handle active drag if (ctx->stack.dragWindow >= 0) { if (buttons & 1) { - wmDragMove(&ctx->stack, &ctx->dirty, mx, my); + wmDragMove(&ctx->stack, &ctx->dirty, mx, my, ctx->display.width, ctx->display.height); } else { wmDragEnd(&ctx->stack); } @@ -2782,21 +2782,39 @@ static void pollKeyboard(AppContextT *ctx) { } if (ctx->kbMoveResize.mode == KbModeMoveE) { + int32_t oldX = kbWin->x; + int32_t oldY = kbWin->y; + if (ascii == 0 && scancode == 0x48) { - dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h); kbWin->y -= KB_MOVE_STEP; - dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h); } else if (ascii == 0 && scancode == 0x50) { - dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h); kbWin->y += KB_MOVE_STEP; - dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h); } else if (ascii == 0 && scancode == 0x4B) { - dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h); kbWin->x -= KB_MOVE_STEP; - dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h); } else if (ascii == 0 && scancode == 0x4D) { - dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h); kbWin->x += KB_MOVE_STEP; + } + + // Clamp: keep title bar reachable + int32_t screenW = ctx->display.width; + int32_t screenH = ctx->display.height; + int32_t minVisible = 50; + + if (kbWin->y < 0) { + kbWin->y = 0; + } + if (kbWin->y + CHROME_BORDER_WIDTH + CHROME_TITLE_HEIGHT > screenH) { + kbWin->y = screenH - CHROME_BORDER_WIDTH - CHROME_TITLE_HEIGHT; + } + if (kbWin->x + kbWin->w < minVisible) { + kbWin->x = minVisible - kbWin->w; + } + if (kbWin->x > screenW - minVisible) { + kbWin->x = screenW - minVisible; + } + + if (kbWin->x != oldX || kbWin->y != oldY) { + dirtyListAdd(&ctx->dirty, oldX, oldY, kbWin->w, kbWin->h); dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h); } } else { @@ -2830,6 +2848,18 @@ static void pollKeyboard(AppContextT *ctx) { newH = kbWin->maxH; } + // Clamp to screen boundaries + int32_t screenW = ctx->display.width; + int32_t screenH = ctx->display.height; + + if (kbWin->x + newW > screenW) { + newW = screenW - kbWin->x; + } + + if (kbWin->y + newH > screenH) { + newH = screenH - kbWin->y; + } + if (newW != kbWin->w || newH != kbWin->h) { dirtyListAdd(&ctx->dirty, kbWin->x, kbWin->y, kbWin->w, kbWin->h); kbWin->w = newW; diff --git a/dvx/dvxWm.c b/dvx/dvxWm.c index 2320046..32398c6 100644 --- a/dvx/dvxWm.c +++ b/dvx/dvxWm.c @@ -1193,18 +1193,33 @@ void wmDragEnd(WindowStackT *stack) { // we don't need to ask the app to repaint during the drag, just blit from // its buffer at the new position. -void wmDragMove(WindowStackT *stack, DirtyListT *dl, int32_t mouseX, int32_t mouseY) { +void wmDragMove(WindowStackT *stack, DirtyListT *dl, int32_t mouseX, int32_t mouseY, int32_t screenW, int32_t screenH) { if (stack->dragWindow < 0 || stack->dragWindow >= stack->count) { return; } - WindowT *win = stack->windows[stack->dragWindow]; + WindowT *win = stack->windows[stack->dragWindow]; + int32_t minVisible = 50; dirtyListAdd(dl, win->x, win->y, win->w, win->h); win->x = mouseX - stack->dragOffX; win->y = mouseY - stack->dragOffY; + // Clamp: keep title bar reachable + if (win->y < 0) { + win->y = 0; + } + if (win->y + CHROME_BORDER_WIDTH + CHROME_TITLE_HEIGHT > screenH) { + win->y = screenH - CHROME_BORDER_WIDTH - CHROME_TITLE_HEIGHT; + } + if (win->x + win->w < minVisible) { + win->x = minVisible - win->w; + } + if (win->x > screenW - minVisible) { + win->x = screenW - minVisible; + } + dirtyListAdd(dl, win->x, win->y, win->w, win->h); } @@ -2022,6 +2037,11 @@ void wmResizeMove(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, int32_ newW = maxW; } + if (newX < 0) { + newW += newX; + newX = 0; + } + if (newW >= minW) { win->x = newX; win->w = newW; @@ -2036,6 +2056,10 @@ void wmResizeMove(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, int32_ newW = maxW; } + if (win->x + newW > d->width) { + newW = d->width - win->x; + } + if (newW >= minW) { win->w = newW; appliedX = true; @@ -2051,6 +2075,11 @@ void wmResizeMove(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, int32_ newH = maxH; } + if (newY < 0) { + newH += newY; + newY = 0; + } + if (newH >= minH) { win->y = newY; win->h = newH; @@ -2065,6 +2094,10 @@ void wmResizeMove(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, int32_ newH = maxH; } + if (win->y + newH > d->height) { + newH = d->height - win->y; + } + if (newH >= minH) { win->h = newH; appliedY = true; diff --git a/dvx/dvxWm.h b/dvx/dvxWm.h index f66c405..a5bd115 100644 --- a/dvx/dvxWm.h +++ b/dvx/dvxWm.h @@ -131,7 +131,7 @@ int32_t wmResizeEdgeHit(const WindowT *win, int32_t mx, int32_t my); // event while dragWindow is active. Dirties both the old and new window // positions. The drag offset (mouse position relative to window origin at // drag start) is applied so the window tracks the mouse smoothly. -void wmDragMove(WindowStackT *stack, DirtyListT *dl, int32_t mouseX, int32_t mouseY); +void wmDragMove(WindowStackT *stack, DirtyListT *dl, int32_t mouseX, int32_t mouseY, int32_t screenW, int32_t screenH); // Update window dimensions during an active resize. Enforces MIN_WINDOW_W/H // and optional maxW/maxH constraints. Reallocates the content buffer if the