Skip to content

Commit

Permalink
Merge tag 'v2.7.1' into emulator-console8
Browse files Browse the repository at this point in the history
  • Loading branch information
tomm committed Mar 30, 2024
2 parents a4a9ff0 + 3c6c7a9 commit 2b51448
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 84 deletions.
234 changes: 161 additions & 73 deletions video/cursor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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
//
Expand All @@ -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;
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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)
Expand All @@ -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;
}
Expand All @@ -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);
}

Expand All @@ -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);
}
Expand Down
Loading

0 comments on commit 2b51448

Please sign in to comment.