From d7d20ccda9caa5072bc32b8f726ffac3a41cb45f Mon Sep 17 00:00:00 2001 From: Steve Sims Date: Sat, 30 Mar 2024 14:59:56 +0000 Subject: [PATCH 1/3] cursor and scrolling fixes allows teletext mode to work again resolves issues with cursor movement and scrolling when certain combinations of cursor behaviour direction bits are set cursor behaviour better copes with odd font sizes NB teletext mode will currently _not_ work when x/y are swapped, or vertical is inverted - all scrolling inside the teletext mode support code is _currently_ interpreted as a scroll upwards --- video/cursor.h | 229 ++++++++++++++++++++++++++++++++--------------- video/graphics.h | 22 ++--- 2 files changed, 168 insertions(+), 83 deletions(-) diff --git a/video/cursor.h b/video/cursor.h index 86fd0c77..198250cd 100644 --- a/video/cursor.h +++ b/video/cursor.h @@ -37,6 +37,8 @@ CursorBehaviour cursorBehaviour; // New cursor behavior bool pagedMode = false; // Is output paged or not? Set by VDU 14 and 15 uint8_t pagedModeCount = 0; // Scroll counter for paged mode +void cursorDown(bool moveOnly); +void cursorUp(bool moveOnly); // Render a cursor at the current screen position // @@ -62,111 +64,181 @@ inline void setCursorBehaviour(uint8_t setting, uint8_t mask = 0xFF) { cursorBehaviour.value = (cursorBehaviour.value & mask) ^ setting; } +// Adjustments to ensure cursor position sits at the nearest character boundary +int getXAdjustment() { + return activeViewport->width() % fontW; +} + +int getYAdjustment() { + return activeViewport->height() % fontH; +} + +Point getNormalisedCursorPosition(Point * cursor = activeCursor) { + Point p; + if (cursorBehaviour.flipXY) { + if (cursorBehaviour.invertHorizontal) { + p.X = activeViewport->Y2 - cursor->Y; + } else { + p.X = cursor->Y - activeViewport->Y1; + } + if (cursorBehaviour.invertVertical) { + p.Y = activeViewport->X2 - cursor->X; + } else { + p.Y = cursor->X - activeViewport->X1; + } + } else { + if (cursorBehaviour.invertHorizontal) { + p.X = activeViewport->X2 - cursor->X; + } else { + p.X = cursor->X - activeViewport->X1; + } + if (cursorBehaviour.invertVertical) { + p.Y = activeViewport->Y2 - cursor->Y; + } else { + p.Y = cursor->Y - activeViewport->Y1; + } + } + return p; +} + +int getNormalisedViewportWidth() { + if (cursorBehaviour.flipXY) { + return activeViewport->height() - getYAdjustment() - (fontH - 1); + } + return activeViewport->width() - getXAdjustment(); +} + +int getNormalisedViewportHeight() { + if (cursorBehaviour.flipXY) { + return activeViewport->width() - getXAdjustment() - (fontW - 1); + } + return activeViewport->height() - getYAdjustment() - (fontH - 1); +} + +bool cursorIsOffRight() { + return getNormalisedCursorPosition().X >= getNormalisedViewportWidth(); +} + +bool cursorIsOffLeft() { + return getNormalisedCursorPosition().X < 0; +} + +bool cursorIsOffTop() { + return getNormalisedCursorPosition().Y < 0; +} + +bool cursorIsOffBottom() { + return getNormalisedCursorPosition().Y >= getNormalisedViewportHeight(); +} + // Move the active cursor to the leftmost position in the viewport // -void cursorCR() { +void cursorCR(Rect * viewport = activeViewport) { if (cursorBehaviour.flipXY) { - activeCursor->Y = cursorBehaviour.invertVertical ? (activeViewport->Y2 + 1 - fontH) : activeViewport->Y1; + activeCursor->Y = cursorBehaviour.invertVertical ? (viewport->Y2 + 1 - fontH - getYAdjustment()) : viewport->Y1; } else { - activeCursor->X = cursorBehaviour.invertHorizontal ? (activeViewport->X2 + 1 - fontW) : activeViewport->X1; + activeCursor->X = cursorBehaviour.invertHorizontal ? (viewport->X2 + 1 - fontW - getXAdjustment()) : viewport->X1; } } -bool cursorIsOffRight() { +void cursorEndRow(Rect * viewport = activeViewport) { if (cursorBehaviour.flipXY) { - if (cursorBehaviour.invertHorizontal) { - return activeCursor->Y < activeViewport->Y1; - } - return activeCursor->Y >= activeViewport->Y2; + activeCursor->Y = cursorBehaviour.invertVertical ? viewport->Y1 : (viewport->Y2 + 1 - fontH - getYAdjustment()); + } else { + activeCursor->X = cursorBehaviour.invertHorizontal ? viewport->X1 : (viewport->X2 + 1 - fontW - getXAdjustment()); + } +} + +void cursorTop(Rect * viewport = activeViewport) { + if (cursorBehaviour.flipXY) { + activeCursor->X = cursorBehaviour.invertHorizontal ? (viewport->X2 + 1 - fontW - getXAdjustment()) : viewport->X1; + } else { + activeCursor->Y = cursorBehaviour.invertVertical ? (viewport->Y2 + 1 - fontH - getYAdjustment()) : viewport->Y1; } - if (cursorBehaviour.invertHorizontal) { - return activeCursor->X < activeViewport->X1; +} + +void cursorEndCol(Rect * viewport = activeViewport) { + if (cursorBehaviour.flipXY) { + activeCursor->X = cursorBehaviour.invertHorizontal ? viewport->X1 : (viewport->X2 + 1 - fontW - getXAdjustment()); + } else { + activeCursor->Y = cursorBehaviour.invertVertical ? viewport->Y1 : (viewport->Y2 + 1 - fontH - getYAdjustment()); } - return activeCursor->X >= activeViewport->X2; } +// Check if the cursor is off the edge of the viewport and take appropriate action +// returns true if the cursor wrapped, false if no action was taken or the screen scrolled bool cursorScrollOrWrap() { - bool outsideY = activeCursor->Y < activeViewport->Y1 || activeCursor->Y >= activeViewport->Y2; - bool outsideX = activeCursor->X < activeViewport->X1 || activeCursor->X >= activeViewport->X2; - if (!outsideX && !outsideY) { + bool offLeft = cursorIsOffLeft(); + bool offRight = cursorIsOffRight(); + bool offTop = cursorIsOffTop(); + bool offBottom = cursorIsOffBottom(); + if (!offLeft && !offRight && !offTop && !offBottom) { // cursor within current viewport, so do nothing return false; } if (textCursorActive() && !cursorBehaviour.yWrap) { // text cursor, scrolling for our Y direction is enabled - if (cursorBehaviour.flipXY && outsideX || !cursorBehaviour.flipXY && outsideY) { - // we are outside of our cursor vertical direction - // scroll in appropriate direction by 1 character (movement amount 0) - // our direction will be a 6 or 7, depending on whether we're outside the "top" or "bottom" - if (outsideX) { - if (activeCursor->X < activeViewport->X1) { - scrollRegion(activeViewport, cursorBehaviour.invertVertical ? 7 : 6, 0); - activeCursor->X = activeViewport->X1; - } else { - scrollRegion(activeViewport, cursorBehaviour.invertVertical ? 6 : 7, 0); - activeCursor->X = activeViewport->X2 + 1 - fontW; - } - } else { - if (activeCursor->Y < activeViewport->Y1) { - scrollRegion(activeViewport, cursorBehaviour.invertHorizontal ? 7 : 6, 0); - activeCursor->Y = activeViewport->Y1; - } else { - scrollRegion(activeViewport, cursorBehaviour.invertHorizontal ? 6 : 7, 0); - activeCursor->Y = activeViewport->Y2 + 1 - fontH; - } - } + if (offTop) { + // scroll screen down by 1 line + scrollRegion(activeViewport, 6, 0); + // move cursor down until it's within the viewport + do { + cursorDown(true); + } while (cursorIsOffTop()); + return false; + } + if (offBottom) { + // scroll screen up by 1 line + scrollRegion(activeViewport, 7, 0); + // move cursor up until it's within the viewport + do { + cursorUp(false); + } while (cursorIsOffBottom()); return false; } } // if we get here we have a graphics cursor, or text cursor with wrap enabled - if (!textCursorActive() && cursorBehaviour.grNoSpecialActions) { return false; } - // here we just wrap everything - // TODO these don't work properly if our viewport isn't a multiple of fontW or fontH - // NB this will only be a potential problem if we're using a graphics cursor with a restricted viewport - // or potentially if we're using an oddly sized font - if (activeCursor->X < activeViewport->X1) { - activeCursor->X = activeViewport->X2 + 1 - fontW; + // if we've reached here, we're wrapping, so move cursor to the opposite edge + if (offLeft) { + cursorEndRow(); } - if (activeCursor->X >= activeViewport->X2) { - activeCursor->X = activeViewport->X1; + if (offRight) { + cursorCR(); } - if (activeCursor->Y < activeViewport->Y1) { - activeCursor->Y = activeViewport->Y2 + 1 - fontH; + if (offTop) { + cursorEndCol(); } - if (activeCursor->Y >= activeViewport->Y2) { - activeCursor->Y = activeViewport->Y1; + if (offBottom) { + cursorTop(); } - - // alignment to a multiple of fontW can go _something_ like this - // but needs to bear in mind that our axis might be inverted - // int16_t xOffset = (activeCursor->X - activeViewport->X1) % fontW; - // activeCursor->X = activeCursor->X + xOffset; - return true; } // Move the active cursor down a line // -void cursorDown() { +void cursorDown(bool moveOnly = false) { if (cursorBehaviour.flipXY) { activeCursor->X += (cursorBehaviour.invertHorizontal ? -fontW : fontW); } else { activeCursor->Y += (cursorBehaviour.invertVertical ? -fontH : fontH); } + if (moveOnly) { + return; + } // // handle paging if we need to // if (textCursorActive() && pagedMode) { pagedModeCount++; if (pagedModeCount >= ( - cursorBehaviour.flipXY ? (activeViewport->X2 - activeViewport->X1 + 1) / fontW - : (activeViewport->Y2 - activeViewport->Y1 + 1) / fontH + cursorBehaviour.flipXY ? (activeViewport->width()) / fontW + : (activeViewport->height()) / fontH ) ) { pagedModeCount = 0; @@ -194,12 +266,15 @@ void cursorDown() { // Move the active cursor up a line // -void cursorUp() { +void cursorUp(bool moveOnly = false) { if (cursorBehaviour.flipXY) { activeCursor->X += (cursorBehaviour.invertHorizontal ? fontW : -fontW); } else { activeCursor->Y += (cursorBehaviour.invertVertical ? fontH : -fontH); } + if (moveOnly) { + return; + } cursorScrollOrWrap(); } @@ -243,13 +318,8 @@ void cursorRight(bool scrollProtect = false) { // Move the active cursor to the top-left position in the viewport // void cursorHome(Point * cursor = activeCursor, Rect * viewport = activeViewport) { - if (cursorBehaviour.flipXY) { - cursor->X = cursorBehaviour.invertHorizontal ? (viewport->X2 + 1 - fontW) : viewport->X1; - cursor->Y = cursorBehaviour.invertVertical ? (viewport->Y2 + 1 - fontH) : viewport->Y1; - } else { - cursor->X = cursorBehaviour.invertHorizontal ? (viewport->X2 + 1 - fontW) : viewport->X1; - cursor->Y = cursorBehaviour.invertVertical ? (viewport->Y2 + 1 - fontH) : viewport->Y1; - } + cursorCR(viewport); + cursorTop(viewport); } // TAB(x,y) @@ -258,28 +328,32 @@ void cursorTab(uint8_t x, uint8_t y) { int xPos, yPos; if (cursorBehaviour.flipXY) { if (cursorBehaviour.invertHorizontal) { - xPos = activeViewport->X2 - ((y + 1) * fontW); + xPos = activeViewport->X2 - ((y + 1) * fontW) - getXAdjustment(); } else { xPos = activeViewport->X1 + (y * fontW); } if (cursorBehaviour.invertVertical) { - yPos = activeViewport->Y2 - ((x + 1) * fontH); + yPos = activeViewport->Y2 - ((x + 1) * fontH) - getYAdjustment(); } else { yPos = activeViewport->Y1 + (x * fontH); } } else { if (cursorBehaviour.invertHorizontal) { - xPos = activeViewport->X2 - ((x + 1) * fontW); + xPos = activeViewport->X2 - ((x + 1) * fontW) - getXAdjustment(); } else { xPos = activeViewport->X1 + (x * fontW); } if (cursorBehaviour.invertVertical) { - yPos = activeViewport->Y2 - ((y + 1) * fontH); + yPos = activeViewport->Y2 - ((y + 1) * fontH) - getYAdjustment(); } else { yPos = activeViewport->Y1 + (y * fontH); } } - if (activeViewport->X1 <= xPos && xPos < activeViewport->X2 && activeViewport->Y1 <= yPos && yPos < activeViewport->Y2) { + if (activeViewport->X1 <= xPos + && xPos < activeViewport->X2 - getXAdjustment() + && activeViewport->Y1 <= yPos + && yPos < activeViewport->Y2 - getYAdjustment() + ) { activeCursor->X = xPos; activeCursor->Y = yPos; } @@ -291,11 +365,20 @@ void setPagedMode(bool mode = pagedMode) { } // Reset basic cursor control +// used when changing screen modes // void resetCursor() { setActiveCursor(getTextCursor()); + // visual cursor appearance reset cursorEnabled = true; - cursorBehaviour.value = 0; + cursorFlashing = true; + cursorFlashRate = CURSOR_PHASE; + cursorVStart = 0; + cursorVEnd = fontH - 1; + cursorHStart = 0; + cursorHEnd = fontW - 1; + // cursor behaviour however is _not_ reset here + cursorHome(); setPagedMode(false); } @@ -305,9 +388,9 @@ inline void enableCursor(bool enable = true) { void ensureCursorInViewport(Rect viewport) { if (textCursor.X < viewport.X1 - || textCursor.X > viewport.X2 + || textCursor.X > viewport.X2 - getXAdjustment() || textCursor.Y < viewport.Y1 - || textCursor.Y > viewport.Y2 + || textCursor.Y > viewport.Y2 - getYAdjustment() ) { cursorHome(&textCursor, &viewport); } diff --git a/video/graphics.h b/video/graphics.h index 4df29715..c51e0c25 100644 --- a/video/graphics.h +++ b/video/graphics.h @@ -691,9 +691,14 @@ void setClippingRect(Rect rect) { void drawCursor(Point p) { if (textCursorActive()) { if (cursorHStart < fontW && cursorHStart <= cursorHEnd && cursorVStart < fontH && cursorVStart <= cursorVEnd) { - canvas->setBrushColor(tfg); canvas->setPaintOptions(cpo); + canvas->setBrushColor(tbg); canvas->fillRectangle(p.X + cursorHStart, p.Y + cursorVStart, p.X + std::min(((int)cursorHEnd), fontW - 1), p.Y + std::min(((int)cursorVEnd), fontH - 1)); + canvas->setBrushColor(tfg); + canvas->fillRectangle(p.X + cursorHStart, p.Y + cursorVStart, p.X + std::min(((int)cursorHEnd), fontW - 1), p.Y + std::min(((int)cursorVEnd), fontH - 1)); + if (ttxtMode) { + canvas->setPaintOptions(tpo); + } } } } @@ -880,10 +885,6 @@ int8_t change_mode(uint8_t mode) { rectangularPixels = ((float)canvasW / (float)canvasH) > 2; fontW = canvas->getFontInfo()->width; fontH = canvas->getFontInfo()->height; - cursorVStart = 0; - cursorVEnd = fontH - 1; - cursorHStart = 0; - cursorHEnd = fontW - 1; viewportReset(); setOrigin(0,0); pushPoint(0,0); @@ -891,7 +892,6 @@ int8_t change_mode(uint8_t mode) { pushPoint(0,0); moveTo(); resetCursor(); - cursorHome(); if (isDoubleBuffered()) { switchBuffer(); cls(false); @@ -928,12 +928,14 @@ void setLegacyModes(bool legacy) { void scrollRegion(Rect * region, uint8_t direction, int16_t movement) { canvas->setScrollingRegion(region->X1, region->Y1, region->X2, region->Y2); + canvas->setPenColor(tbg); + canvas->setBrushColor(tbg); + canvas->setPaintOptions(tpo); if (ttxtMode) { - if (direction == 3) ttxt_instance.scroll(); + if (direction & 3 == 3) { + ttxt_instance.scroll(); + } } else { - canvas->setPenColor(tbg); - canvas->setBrushColor(tbg); - canvas->setPaintOptions(tpo); auto moveX = 0; auto moveY = 0; switch (direction) { From 2d87c188ec0aa010b78c41d9362674214da793e3 Mon Sep 17 00:00:00 2001 From: Steve Sims Date: Sat, 30 Mar 2024 17:46:18 +0000 Subject: [PATCH 2/3] bump version to 2.7.1 --- video/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/video/version.h b/video/version.h index b0bfe13b..4591b422 100644 --- a/video/version.h +++ b/video/version.h @@ -3,7 +3,7 @@ #define VERSION_MAJOR 2 #define VERSION_MINOR 7 -#define VERSION_PATCH 0 +#define VERSION_PATCH 1 #define VERSION_CANDIDATE 0 // Optional #define VERSION_TYPE "Release" // RC, Alpha, Beta, etc. From 31173261327d8633e943e149b5b889c8817103da Mon Sep 17 00:00:00 2001 From: Steve Sims Date: Sat, 30 Mar 2024 17:50:29 +0000 Subject: [PATCH 3/3] add missing declarations to fix build --- video/cursor.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/video/cursor.h b/video/cursor.h index 198250cd..ef4ca031 100644 --- a/video/cursor.h +++ b/video/cursor.h @@ -11,6 +11,11 @@ extern uint8_t fontW; extern uint8_t fontH; +extern uint8_t cursorVStart; +extern uint8_t cursorVEnd; +extern uint8_t cursorHStart; +extern uint8_t cursorHEnd; + extern void drawCursor(Point p); extern void scrollRegion(Rect * r, uint8_t direction, int16_t amount);