diff --git a/video/cursor.h b/video/cursor.h index 5826007e..188f8714 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); @@ -37,6 +42,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 +69,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) { 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 +271,15 @@ void cursorDown() { // Move the active cursor up a line // -void cursorUp() { +void cursorUp(bool moveOnly) { if (cursorBehaviour.flipXY) { activeCursor->X += (cursorBehaviour.invertHorizontal ? fontW : -fontW); } else { activeCursor->Y += (cursorBehaviour.invertVertical ? fontH : -fontH); } + if (moveOnly) { + return; + } cursorScrollOrWrap(); } @@ -243,13 +323,8 @@ void cursorRight(bool scrollProtect) { // Move the active cursor to the top-left position in the viewport // void cursorHome(Point * cursor, Rect * viewport) { - 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 +333,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 +370,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 +393,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) { 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.