simplify screen, rewrite gno console.

git-svn-id: svn://qnap.local/TwoTerm/branches/fix-gno-scrolling-region@3162 5590a31f-7b70-45f8-8c82-aa3a8e5f4507
This commit is contained in:
Kelvin Sherlock 2017-02-16 00:26:45 +00:00
parent 29776f8366
commit 0291b21733
7 changed files with 324 additions and 910 deletions

View File

@ -16,7 +16,7 @@
@interface GNOConsole : NSObject <Emulator>
{
unsigned cs;
TextPort _textPort;
context _context;
Screen::CursorType _cursorType;
int _scratch[4];

View File

@ -123,6 +123,16 @@
action nop {}
action forward {
if (cursor.x > window.maxX()-1) {
cursor.x = window.minX();
if (cursor.y >= window.maxY()-1) {
screen->scrollUp(window);
} else cursor.y++;
}
}
arg1 = any ${ _scratch[0] = ((fc & 0x7f) - 32); };
arg2 = any ${ _scratch[1] = ((fc & 0x7f) - 32); };
arg3 = any ${ _scratch[2] = ((fc & 0x7f) - 32); };
@ -161,7 +171,7 @@
if (_scratch[3] <= _scratch[2]) _scratch[3] = 24;
_textPort.frame = iRect(
window = iRect(
iPoint(_scratch[0], _scratch[2]),
iPoint(_scratch[1], _scratch[3])
);
@ -170,11 +180,9 @@
// move the cursor to the top left
// gnome clamps the horizontal, doesn't adjust the vertical.
//screen->setCursor(&_textPort, iPoint(0,0));
int x = screen->x();
if (x < _scratch[0]) x = _scratch[0];
if (x >= _scratch[1]) x = _scratch[1] - 1;
screen->setX(x);
if (cursor.x < _scratch[0]) cursor.x = _scratch[0];
if (cursor.x >= _scratch[1]) cursor.x = _scratch[1] - 1;
}
| 0x04 $nop
@ -198,54 +206,70 @@
| 0x08 ${
// CTRL('H'):
int x = screen->x();
if (x != 0 && x != _textPort.frame.minX())
screen->decrementX(&_textPort);
//screen->decrementX(true);
if (cursor.x == window.minX()) {
cursor.x = window.maxX()-1;
// go up, possibly scrolling.
if (cursor.y != window.minY()) cursor.y--;
}
else cursor.x--;
}
| 0x09 ${
// CTRL('I'):
// tab
screen->tabTo(&_textPort, (_textPort.cursor.x + 8) & ~0x07);
//screen->tabTo((screen->x() + 8) & ~0x07);
}
cursor.x = (cursor.x + 8) & ~ 0x07;
} $forward
| 0x0a ${
// CTRL('J'):
// down 1 line.
screen->lineFeed(&_textPort);
if (cursor.y >= window.maxY()-1) {
screen->scrollUp(window);
} else cursor.y++;
}
| 0x0b ${
// CTRL('K'):
// clear to end of screen
screen->erase(&_textPort, Screen::EraseAfterCursor);
iRect tmp;
tmp.origin = cursor;
tmp.size = iSize(window.size.width - cursor.x, 1);
screen->eraseRect(tmp);
tmp = window;
tmp.origin.y = cursor.y+1;
tmp.size.height -= cursor.y+1;
screen->eraseRect(tmp);
}
| 0x0c ${
// CTRL('L'):
// clear screen, go home.
screen->erase(&_textPort, Screen::EraseAll);
screen->setCursor(&_textPort, 0, 0);
screen->eraseRect(window);
cursor = window.origin;
}
| 0x0d ${
// CTRL('M'):
// move to left edge.
screen->setX(&_textPort, 0);
cursor.x = window.minX();
}
| 0x0e ${
// CTRL('N'):
// normal text.
screen->clearFlagBit(Screen::FlagInverse);
_context.clearFlagBit(Screen::FlagInverse);
}
| 0x0f ${
// CTRL('O'):
// inverse text.
screen->setFlagBit(Screen::FlagInverse);
_context.setFlagBit(Screen::FlagInverse);
}
| 0x10 $nop
@ -253,73 +277,84 @@
| 0x11 ${
// CTRL('Q'):
// insert line.
// TODO -- verify textPort
screen->insertLine(&_textPort, _textPort.cursor.y);
iRect tmp(iPoint(window.minX(), cursor.y), window.bottomRight());
screen->scrollDown(tmp);
}
| 0x12 ${
// CTRL('R'):
// delete line
// TODO -- verify textPort
screen->deleteLine(&_textPort, _textPort.cursor.y);
iRect tmp(iPoint(window.minX(), cursor.y), window.bottomRight());
screen->scrollUp(tmp);
}
| 0x13 $nop
| 0x14 $nop
| 0x15 ${
// CTRL('U'):
// right arrow.
screen->incrementX(&_textPort);
}
| 0x15 $forward ${
// CTRL('U'):
// right arrow.
cursor.x++;
} $forward
| 0x16 ${
// CTRL('V'):
// scroll down 1 line.
screen->insertLine(&_textPort, 0);
screen->scrollDown(window);
}
| 0x17 ${
// CTRL('W'):
// scroll up 1 line.
screen->deleteLine(&_textPort, 0);
screen->scrollUp(window);
}
| 0x18 ${
// CTRL('X'):
//mouse text off
screen->clearFlagBit(Screen::FlagMouseText);
_context.clearFlagBit(Screen::FlagMouseText);
}
| 0x19 ${
// CTRL('Y'):
// cursor home
screen->setCursor(&_textPort, 0, 0);
cursor.x = 0;
cursor.y = 0;
}
| 0x1a ${
// CTRL('Z'):
// clear entire line
screen->erase(&_textPort, Screen::EraseLineAll);
iRect tmp;
tmp.origin = iPoint(window.origin.x, cursor.y);
tmp.size = iSize(window.size.width, 1);
screen->eraseRect(tmp);
}
| 0x1b ${
// CTRL('['):
// mouse text on
// inverse must also be on.
screen->setFlagBit(Screen::FlagMouseText);
_context.setFlagBit(Screen::FlagMouseText);
}
| 0x1c ${
// CTRL('\\'):
// move cursor 1 character to the right
screen->incrementX(&_textPort);
cursor.x++;
}
| 0x1d ${
// CTRL(']'):
// clear to end of line.
screen->erase(&_textPort, Screen::EraseLineAfterCursor);
iRect tmp;
tmp.origin = _context.cursor;
tmp.size = iSize(window.size.width - cursor.x, 1);
screen->eraseRect(tmp);
}
| 0x1e arg1 arg2 ${
@ -328,20 +363,20 @@
// goto xy does not respect the text window.
if (_scratch[0] >= 80) _scratch[0] = 0;
if (_scratch[1] >= 24) _scratch[1] = 0;
iPoint dca(_scratch[0], _scratch[1]);
screen->setCursor(&_textPort, dca);
cursor = iPoint(_scratch[0], _scratch[1]);
}
| 0x1f ${
// CTRL('_'):
// move up 1 line
screen->decrementY(&_textPort);
if (cursor.y != window.minY()) cursor.y--;
}
| 0x20 .. 0x7f ${
screen->putc(&_textPort, fc);
| 0x20 .. 0x7f $forward ${
screen->putc(fc, _context);
cursor.x++;
}
| 0x80 .. 0xff $nop
)* $err{ fgoto main; };
@ -381,13 +416,10 @@
%%write init;
_textPort.frame = iRect(0, 0, 80, 24);
_textPort.cursor = iPoint(0,0);
_context.flags = 0;
_context.window = iRect(0, 0, 80, 24);
_context.cursor = iPoint(0,0);
_textPort.scroll = true;
_textPort.advanceCursor = true;
_textPort.leftMargin = TextPort::MarginWrap;
_textPort.rightMargin = TextPort::MarginWrap;
_cursorType = Screen::CursorTypeUnderscore;
@ -429,8 +461,13 @@
const uint8_t *p = data;
const uint8_t *pe = data + length;
iPoint &cursor = _context.cursor;
iRect &window = _context.window;
%%write exec;
if (cursor.x == 80) screen->setCursor(iPoint(79, cursor.y));
else screen->setCursor(cursor);
}

View File

@ -16,6 +16,7 @@
unsigned cs;
TextPort _textPort;
iRect _window;
std::vector<TextPort> _tpStack;

View File

@ -11,8 +11,8 @@
#import "CurveView.h"
#import "EmulatorWindow.h"
#import "VT52.h"
#import "PTSE.h"
//#import "VT52.h"
//#import "PTSE.h"
#import "Defaults.h"
@ -309,7 +309,8 @@
klass = [_parameters objectForKey: kClass];
if (!klass || ![klass conformsToProtocol: @protocol(Emulator)])
{
klass = [VT52 class];
klass = Nil;
//klass = [VT52 class];
}
o = [_parameters objectForKey: kForegroundColor];

View File

@ -101,7 +101,17 @@
}
else
{
unsigned char c;
switch (cursorType) {
default:
case Screen::CursorTypeUnderscore: c = '_'; break;
case Screen::CursorTypePipe: c = '|'; break;
case Screen::CursorTypeBlock: c = 0x80; break;
}
[_cursorImg release];
_cursorImg = [[_charGen imageForCharacter: c] retain];
[self startCursorTimer];
}
@ -341,7 +351,7 @@
{
NSRect charRect = NSMakeRect(_paddingLeft + x * _charWidth, _paddingTop + y *_charHeight, _charWidth, _charHeight);
//NSImage *img;
CharInfo ci = _screen.getc(x, y);
char_info ci = _screen.getc(iPoint(x, y));
unsigned flag = ci.flag;
uint8_t c = ci.c;
@ -374,7 +384,14 @@
std::swap(currentBack, currentFront);
}
}
if (_cursorType == Screen::CursorTypeBlock && _cursorOn && _screen.cursor() == iPoint(x, y)) {
std::swap(currentBack, currentFront);
}
if (currentBack != _backgroundColor)
{
[currentBack setFill];
@ -417,14 +434,18 @@
// cursor.
iPoint cursor = _screen.cursor();
if (_cursorOn && iRect(minX, minY, maxX - minX, maxY - minY).contains(cursor))
if (_cursorOn && iRect(minX, minY, maxX - minX, maxY - minY).contains(cursor) && _cursorType != Screen::CursorTypeBlock)
{
NSRect charRect = NSMakeRect(_paddingLeft + cursor.x * _charWidth, _paddingTop + cursor.y *_charHeight, _charWidth, _charHeight);
[_foregroundColor setFill];
NSCompositingOperation op = NSCompositingOperationCopy;
//if (_cursorType == Screen::CursorTypeBlock) op = NSCompositingOperationXOR;
[_cursorImg drawInRect: charRect
fromRect: NSZeroRect operation: NSCompositeCopy
fromRect: NSZeroRect
operation: op
fraction: 1.0
respectFlipped: YES
hints: nil];
@ -514,6 +535,7 @@
//[self stopCursorTimer];
//_screen.setCursorType(Screen::CursorTypeNone);
#if 0
_screen.beginUpdate();
_screen.setX(0);
@ -524,11 +546,11 @@
_screen.putc(*cp);
}
updateRect = _screen.endUpdate();
[self invalidateIRect: updateRect];
#endif
//[_emulator writeLine: @"[Process completed]"];
@ -940,6 +962,7 @@ void ViewScreen::setSize(unsigned width, unsigned height, bool resizeView)
void ViewScreen::setCursorType(CursorType cursorType)
{
Screen::setCursorType(cursorType);
[_view setCursorType: cursorType];
}

View File

@ -11,35 +11,17 @@
#include <algorithm>
iPoint TextPort::absoluteCursor() const
{
return iPoint(frame.origin.x + cursor.x, frame.origin.y + cursor.y);
}
Screen::Screen(unsigned height, unsigned width)
{
_port.frame = iRect(0, 0, width, height);
_port.rightMargin = TextPort::MarginTruncate;
_port.rightMargin = TextPort::MarginTruncate;
_frame = iRect(0, 0, width, height);
_port.advanceCursor = true;
_port.scroll = true;
_flag = 0;
_screen.resize(height);
for (ScreenIterator iter = _screen.begin(); iter != _screen.end(); ++iter)
{
iter->resize(width);
}
for (auto &line : _screen) line.resize(width);
}
Screen::~Screen()
@ -69,13 +51,12 @@ iRect Screen::endUpdate()
_updates.push_back(_updateCursor);
}
for (UpdateIterator iter = _updates.begin(); iter != _updates.end(); ++iter)
{
maxX = std::max(maxX, iter->x);
maxY = std::max(maxY, iter->y);
for (auto &point : _updates) {
maxX = std::max(maxX, point.x);
maxY = std::max(maxY, point.y);
minX = std::min(minX, iter->x);
minY = std::min(minY, iter->y);
minX = std::min(minX, point.x);
minY = std::min(minY, point.y);
}
_lock.unlock();
@ -83,726 +64,213 @@ iRect Screen::endUpdate()
return iRect(iPoint(minX, minY), iSize(maxX + 1 - minX, maxY + 1 - minY));
}
void Screen::setFlag(uint8_t flag)
void Screen::putc(uint8_t c, const context &ctx)
{
_flag = flag;
}
void Screen::setFlagBit(uint8_t bit)
{
_flag |= bit;
}
void Screen::clearFlagBit(uint8_t bit)
{
_flag &= ~bit;
}
void Screen::putc(TextPort *textPort, uint8_t c)
{
putc(textPort, c, _flag);
}
void Screen::putc(TextPort *textPort, uint8_t c, uint8_t flag)
{
/*
* textport must be valid.
* cursor must be within textport.
*/
if (!textPort) textPort = &_port;
iPoint cursor = textPort->absoluteCursor();
// right margin is a special case.
if (textPort->cursor.x == textPort->frame.width() -1)
{
if (textPort->rightMargin == TextPort::MarginTruncate) return;
if (textPort->rightMargin == TextPort::MarginOverwrite)
{
_updates.push_back(cursor);
_screen[cursor.y][cursor.x] = CharInfo(c, flag);
return;
}
//if (textPort->rightMargin == TextPort::MarginWrap)
}
auto cursor = ctx.cursor;
if (!_frame.contains(cursor)) return;
_updates.push_back(cursor);
_screen[cursor.y][cursor.x] = CharInfo(c, flag);
if (textPort->advanceCursor)
{
incrementX(textPort);
}
_screen[cursor.y][cursor.x] = char_info(c, ctx.flags);
}
#pragma mark -
#pragma mark Cursor manipulation.
/*
* sets cursor.x within the textport.
* if x is outside the textport and clampX is true, it will be clamped to 0/width-1
* if x is outside the textport and clampX is false, x will not be updated.
*
* returns the new cursor.x
*/
int Screen::setX(TextPort *textPort, int x)
{
// honors clampX.
if (!textPort) textPort = &_port;
bool clamp = textPort->clampX;
if (x < 0)
{
if (clamp) textPort->cursor.x = 0;
}
else if (x >= textPort->frame.width())
{
if (clamp) textPort->cursor.x = textPort->frame.width() - 1;
}
else
{
textPort->cursor.x = x;
}
if (textPort != &_port) _port.cursor = textPort->absoluteCursor();
return textPort->cursor.x;
}
/*
* sets cursor.y within the textport.
* if y is outside the textport and clampY is true, it will be clamped to 0/height-1
* if y is outside the textport and clampY is false, y will not be updated.
*
* returns the new cursor.y
*/
int Screen::setY(TextPort *textPort, int y)
{
// honors clampY.
if (!textPort) textPort = &_port;
bool clamp = textPort->clampY;
if (y < 0)
{
if (clamp) textPort->cursor.y = 0;
}
else if (y >= textPort->frame.height())
{
if (clamp) textPort->cursor.y = textPort->frame.height() - 1;
}
else
{
textPort->cursor.y = y;
}
if (textPort != &_port) _port.cursor = textPort->absoluteCursor();
return textPort->cursor.y;
}
/*
* increments cursor.x within the textport.
* if rightMargin wraps, it will set x = 0 and incrementY (which may scroll)
* if rightMargin does not wrap, it will not be updated.
*
* returns the new cursor.x
*/
int Screen::incrementX(TextPort *textPort)
{
// honors wrap, scroll.
if (!textPort) textPort = &_port;
if (textPort->cursor.x == textPort->frame.width() - 1)
{
if (textPort->rightMargin == TextPort::MarginWrap)
{
textPort->cursor.x = 0;
incrementY(textPort);
}
}
else
{
textPort->cursor.x++;
}
if (textPort != &_port) _port.cursor = textPort->absoluteCursor();
return textPort->cursor.x;
}
/*
* decrements cursor.x within the textport.
* if leftMargin wraps, it will set x = width - 1 and decrementY (which may scroll)
* if leftMargin does not wrap, it will not be updated.
*
* returns the new cursor.x
*/
int Screen::decrementX(TextPort *textPort)
{
// honors wrap, scroll.
if (!textPort) textPort = &_port;
if (textPort->cursor.x == 0)
{
if (textPort->leftMargin == TextPort::MarginWrap)
{
textPort->cursor.x = textPort->frame.width() - 1;
decrementY(textPort);
}
}
else
{
textPort->cursor.x--;
}
if (textPort != &_port) _port.cursor = textPort->absoluteCursor();
return textPort->cursor.x;
}
/*
* increment cursor.y
* this is similar to lineFeed, except that it honors the scroll flag
* at the bottom of the screen.
* returns the new cursor.y
*/
int Screen::incrementY(TextPort *textPort)
{
// similar to linefeed, but honors scroll.
if (!textPort) textPort = &_port;
if (textPort->scroll)
return lineFeed(textPort);
if (textPort->cursor.y < textPort->frame.height() - 1)
return lineFeed(textPort);
return textPort->cursor.y;
}
/*
* decrement cursor.y
* this is similar to revereseLineFeed, except that it honors the scroll flag
* at the top of the screen.
* returns the new cursor.y
*/
int Screen::decrementY(TextPort *textPort)
{
// similar to reverseLineFeed, but will not scroll.
if (!textPort) textPort = &_port;
if (!textPort) textPort = &_port;
if (textPort->scroll)
return reverseLineFeed(textPort);
if (textPort->cursor.y > 0)
return reverseLineFeed(textPort);
return textPort->cursor.y;
}
void Screen::setCursor(TextPort *textPort,iPoint point)
{
setX(textPort, point.x);
setY(textPort, point.y);
}
void Screen::setCursor(TextPort *textPort, int x, int y)
{
setX(textPort, x);
setY(textPort, y);
}
#pragma mark -
#pragma mark Erase
void Screen::erase(EraseRegion region)
void Screen::eraseScreen()
{
CharInfoIterator ciIter;
ScreenIterator screenIter;
if (region == EraseAll)
{
ScreenIterator end = _screen.end();
for (screenIter = _screen.begin(); screenIter < end; ++screenIter)
{
std::fill(screenIter->begin(), screenIter->end(), CharInfo(0,0));
}
_updates.push_back(iPoint(0,0));
_updates.push_back(iPoint(width() - 1, height() - 1));
return;
}
// TODO -- be smart and check if cursor is at x = 0 (or x = _width - 1)
if (region == EraseBeforeCursor)
{
ScreenIterator end = _screen.begin() + y() - 1;
for (screenIter = _screen.begin(); screenIter < end; ++screenIter)
{
std::fill(screenIter->begin(), screenIter->end(), CharInfo(0,0));
}
_updates.push_back(iPoint(0,0));
_updates.push_back(iPoint(width() - 1, y()));
region = EraseLineBeforeCursor;
}
if (region == EraseAfterCursor)
{
ScreenIterator end = _screen.end();
for (screenIter = _screen.begin() + y() + 1; screenIter < end; ++screenIter)
{
std::fill(screenIter->begin(), screenIter->end(), CharInfo(0,0));
}
_updates.push_back(iPoint(0, y() + 1));
_updates.push_back(iPoint(width() - 1, height() - 1));
region = EraseLineAfterCursor;
}
if (region == EraseLineAll)
{
std::fill(_screen[y()].begin(), _screen[y()].end(), CharInfo(0,0));
_updates.push_back(iPoint(0, y()));
_updates.push_back(iPoint(width() - 1, y()));
return;
}
if (region == EraseLineBeforeCursor)
{
std::fill(_screen[y()].begin(), _screen[y()].begin() + x(), CharInfo(0,0));
_updates.push_back(iPoint(0, y()));
_updates.push_back(cursor());
return;
}
if (region == EraseLineAfterCursor)
{
std::fill(_screen[y()].begin() + x(), _screen[y()].end(), CharInfo(0,0));
_updates.push_back(cursor());
_updates.push_back(iPoint(width() - 1, y()));
return;
for (auto &line : _screen) {
std::fill(line.begin(), line.end(), char_info());
}
_updates.push_back(iPoint(0,0));
_updates.push_back(iPoint(width() - 1, height() - 1));
}
void Screen::erase(TextPort* textPort, EraseRegion region)
{
if (!textPort) textPort = &_port;
iRect frame = textPort->frame;
iPoint cursor = textPort->absoluteCursor();
void Screen::eraseRect(iRect rect) {
rect = rect.intersection(_frame);
if (!rect.valid()) return;
if (rect == _frame) return eraseScreen();
auto yIter = _screen.begin() + rect.origin.y;
auto yEnd = yIter + rect.size.height;
if (region == EraseAll)
{
//erase the current screen
while (yIter != yEnd) {
auto &line = *yIter++;
auto xIter = line.begin() + rect.origin.x;
auto xEnd = xIter + rect.size.width;
ScreenIterator begin = _screen.begin() + frame.minY();
ScreenIterator end = _screen.begin() + frame.maxY();
for (ScreenIterator iter = begin; iter != end; ++iter)
{
CharInfoIterator begin = iter->begin() + frame.minX();
CharInfoIterator end = iter->begin() + frame.maxX();
std::fill(begin, end, CharInfo(0, 0));
}
_updates.push_back(frame.origin);
_updates.push_back(iPoint(frame.maxX() - 1, frame.maxY() - 1));
return;
std::fill(xIter, xEnd, char_info());
}
if (region == EraseLineAll)
{
// erase the current line.
ScreenIterator iter = _screen.begin() + cursor.y;
CharInfoIterator begin = iter->begin() + frame.minX();
CharInfoIterator end = iter->begin() + frame.maxX();
std::fill(begin, end, CharInfo(0, 0));
_updates.push_back(iPoint(frame.minX(), cursor.y));
_updates.push_back(iPoint(frame.maxX() - 1, cursor.y));
return;
}
if (region == EraseBeforeCursor)
{
// erase everything before the cursor
// part 1 -- erase all lines prior to the current line.
ScreenIterator begin = _screen.begin() + frame.minY();
ScreenIterator end = _screen.begin() + cursor.y;
for (ScreenIterator iter = begin; iter != end; ++iter)
{
CharInfoIterator begin = iter->begin() + frame.minX();
CharInfoIterator end = iter->begin() + frame.maxX();
std::fill(begin, end, CharInfo(0, 0));
}
_updates.push_back(frame.origin);
_updates.push_back(iPoint(frame.maxX() - 1, cursor.y - 1));
// handle rest below.
region = EraseLineBeforeCursor;
}
if (region == EraseAfterCursor)
{
// erase everything after the cursor
// part 1 -- erase all lines after the current line.
ScreenIterator begin = _screen.begin() + cursor.y + 1;
ScreenIterator end = _screen.begin() + frame.maxY();
if (begin < end)
{
for (ScreenIterator iter = begin; iter != end; ++iter)
{
CharInfoIterator begin = iter->begin() + frame.minX();
CharInfoIterator end = iter->begin() + frame.maxX();
std::fill(begin, end, CharInfo(0, 0));
}
_updates.push_back(iPoint(cursor.x, cursor.y + 1));
_updates.push_back(iPoint(frame.maxX() - 1, frame.maxY() - 1));
}
region = EraseLineAfterCursor;
}
if (region == EraseLineBeforeCursor)
{
// erase the current line, before the cursor.
ScreenIterator iter = _screen.begin() + cursor.y;
CharInfoIterator begin = iter->begin() + frame.minX();
CharInfoIterator end = iter->begin() + cursor.x;
std::fill(begin, end, CharInfo(0, 0));
_updates.push_back(iPoint(frame.minX(), cursor.y));
_updates.push_back(iPoint(cursor.x - 1, cursor.y));
return;
}
if (region == EraseLineAfterCursor)
{
// erase the current line, after the cursor.
ScreenIterator iter = _screen.begin() + cursor.y;
CharInfoIterator begin = iter->begin() + cursor.x;
CharInfoIterator end = iter->begin() + frame.maxX();
std::fill(begin, end, CharInfo(0, 0));
_updates.push_back(iPoint(cursor.x, cursor.y));
_updates.push_back(iPoint(frame.maxX() - 1, cursor.y));
return;
}
}
void Screen::eraseRect(iRect rect)
{
unsigned maxX = std::min(width(), rect.maxX());
unsigned maxY = std::min(height(), rect.maxY());
CharInfo clear;
for (unsigned y = rect.minY(); y < maxY; ++y)
{
for (unsigned x = rect.minX(); x < maxX; ++x)
{
_screen[y][x] = clear;
}
}
_updates.push_back(rect.origin);
_updates.push_back(iPoint(maxX - 1, maxY - 1));
_updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1));
}
void Screen::lineFeed()
template< class BidirIt1, class BidirIt2, class FX >
BidirIt2 copy_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last, FX fx)
{
// moves the screen up one row, inserting a blank line at the bottom.
if (y() == height() - 1)
{
deleteLine(0);
}
else
{
_port.cursor.y++;
}
}
/*
* perform a line feed. This increments Y. If Y was at the bottom of the
* textPort, the textPort scrolls.
*
*/
int Screen::lineFeed(TextPort *textPort)
{
if (!textPort)
{
lineFeed();
return y();
}
if (textPort->cursor.y == textPort->frame.height() - 1)
{
deleteLine(textPort, 0);
}
else
{
textPort->cursor.y++;
if (textPort != &_port) _port.cursor = textPort->absoluteCursor();
}
return textPort->cursor.y;
}
/*
* perform a reverse line feed. This increments Y. If Y was at the top of the
* textPort, the textPort scrolls.
*
*/
int Screen::reverseLineFeed(TextPort *textPort)
{
if (!textPort)
{
reverseLineFeed();
return y();
}
if (textPort->cursor.y == 0)
{
insertLine(textPort, 0);
}
else
{
textPort->cursor.y--;
if (textPort != &_port) _port.cursor = textPort->absoluteCursor();
}
return textPort->cursor.y;
}
void Screen::reverseLineFeed()
{
// moves the cursor down one row, inserting a blank line at the top.
if (y() == 0)
{
insertLine(0);
}
else
{
_port.cursor.y--;
}
}
void Screen::insertLine(unsigned line)
{
if (line >= height()) return;
if (line == height() - 1)
{
_screen.back().clear();
_screen.back().resize(width());
}
else
{
std::vector<CharInfo> newLine;
ScreenIterator iter;
while (first != last) {
_screen.pop_back();
iter = _screen.insert(_screen.begin() + line, newLine);
iter->resize(width());
fx(*(--last), *(--d_last));
}
return d_last;
}
_updates.push_back(iPoint(0, line));
template<class InputIt, class OutputIt, class FX>
OutputIt copy_forward(InputIt first, InputIt last, OutputIt d_first, FX fx)
{
while (first != last) {
fx( *first, *d_first);
++first;
++d_first;
}
return d_first;
}
void Screen::scrollUp()
{
// save the first line (to avoid allocation/deallocation)
std::vector<char_info> tmp;
std::swap(tmp, _screen.front());
std::fill(tmp.begin(), tmp.end(), char_info());
auto iter = std::move(_screen.begin() + 1, _screen.end(), _screen.begin());
*iter = std::move(tmp);
_updates.push_back(iPoint(0,0));
_updates.push_back(iPoint(width() - 1, height() - 1));
}
// line is relative to the textView.
// textView has been constrained.
void Screen::insertLine(TextPort *textPort, int line)
void Screen::scrollUp(iRect rect)
{
CharInfo ci;
rect = rect.intersection(_frame);
if (!rect.valid()) return;
if (rect == _frame) return scrollUp();
if (!textPort) return insertLine(line);
auto src = _screen.begin() + rect.minY()+1;
auto dest = _screen.begin() + rect.minY();
auto end = _screen.begin() + rect.maxY();
iRect frame(textPort->frame);
int minY = frame.minY();
int maxY = frame.maxY();
int minX = frame.minX();
int maxX = frame.maxX();
if (line < 0) return;
if (line >= frame.height()) return;
// move all subsequent lines forward by 1.
for (int y = frame.height() - 2; y >= line; --y)
{
CharInfoIterator iter;
iter = _screen[minY + y].begin();
std::copy(iter +minX, iter + maxX, _screen[minY + y + 1].begin() + minX);
}
// clear the line.
std::fill(_screen[minY + line].begin() + minX, _screen[minY + line].begin() + maxX, ci);
// set the update region.
_updates.push_back(iPoint(minX, minY + line));
_updates.push_back(iPoint(maxX - 1, maxY - 1));
auto iter = copy_forward(src, end, dest, [=](const auto &src, auto &dest){
std::copy(src.begin() + rect.minX(), src.begin() + rect.maxX(), dest.begin());
});
}
void Screen::deleteLine(unsigned line)
{
if (line >= height()) return;
if (line == height() - 1)
{
_screen.back().clear();
}
else
{
std::vector<CharInfo> newLine;
_screen.erase(_screen.begin() + line);
_screen.push_back(newLine);
}
_screen.back().resize(width());
std::fill(iter->begin() + rect.minX(), iter->begin() + rect.maxX(), char_info());
_updates.push_back(iPoint(0, line));
_updates.push_back(iPoint(width() - 1, height() - 1));
_updates.push_back(rect.origin);
_updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1));
}
void Screen::deleteLine(TextPort *textPort, int line)
void Screen::scrollDown()
{
CharInfo ci;
// save the first line (to avoid allocation/deallocation)
std::vector<char_info> tmp;
std::swap(tmp, _screen.back());
std::fill(tmp.begin(), tmp.end(), char_info());
if (!textPort) return deleteLine(line);
auto iter = std::move_backward(_screen.begin(), _screen.end()-1, _screen.end());
iRect frame(textPort->frame);
--iter;
*iter = std::move(tmp);
int minY = frame.minY();
int maxY = frame.maxY();
int minX = frame.minX();
int maxX = frame.maxX();
if (line < 0) return;
if (line >= frame.height()) return;
// move all subsequent lines back by 1.
for (int y = line; y < frame.height() - 1; ++y)
{
CharInfoIterator iter;
CharInfoIterator end;
iter = _screen[minY + y + 1].begin();
std::copy(iter + minX, iter + maxX, _screen[minY + y].begin() + minX);
}
// clear the last line.
std::fill(_screen[maxY - 1].begin() + minX, _screen[maxY - 1].begin() + maxX, ci);
// set the update region.
_updates.push_back(iPoint(minX, minY + line));
_updates.push_back(iPoint(maxX - 1, maxY - 1));
_updates.push_back(iPoint(0,0));
_updates.push_back(iPoint(width() - 1, height() - 1));
}
void Screen::scrollDown(iRect rect)
{
rect = rect.intersection(_frame);
if (!rect.valid()) return;
if (rect == _frame) return scrollDown();
auto src = _screen.begin() + rect.minY();
auto end = _screen.begin() + rect.maxY()-1;
auto dest = _screen.begin() + rect.maxY();
auto iter = copy_backward(src, end, dest, [=](const auto &src, auto &dest) {
std::copy(src.begin() + rect.minX(), src.begin() + rect.maxX(), dest.begin());
});
--iter;
std::fill(iter->begin() + rect.minX(), iter->begin() + rect.maxX(), char_info());
_updates.push_back(rect.origin);
_updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1));
}
#if 0
void Screen::scrollDown(iRect rect)
{
rect = rect.intersection(_frame);
if (!rect.valid()) return;
if (rect == _frame) return scrollDown();
auto src = _screen.begin() + rect.maxY()-1;
auto dest = _screen.begin() + rect.maxY();
auto end = _screen.begin() + rect.minY();
while (src != end) {
--src;
--dest;
std::copy(src->begin() + rect.minX(), src->begin() + rect.maxX(), dest->begin() + rect.minX());
}
auto &line = _screen[rect.minY()];
std::fill(line.begin() + rect.minX(), line.begin() + rect.maxX(), char_info());
_updates.push_back(rect.origin);
_updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1));
}
#endif
void Screen::setSize(unsigned w, unsigned h)
{
@ -810,49 +278,15 @@ void Screen::setSize(unsigned w, unsigned h)
if ((height() == h) && (width() == w)) return;
if (height() < h)
{
_screen.resize(h);
_screen.resize(h);
for (auto &line : _screen) {
line.resize(w);
}
else if (height() > h)
{
unsigned count = height() - h;
int y = _port.cursor.y;
int maxY = height() - 1;
// 1. erase from the bottom, up to the cursor (if blank)
// 2. erase lines from the top.
while (count && maxY > y)
{
// todo -- check if blank...
_screen.pop_back();
--count;
--maxY;
}
// erase lines from the top.
if (count)
_screen.erase(_screen.begin(), _screen.begin() + count);
}
//if (_width != _width || _height != height)
{
ScreenIterator iter;
for (iter = _screen.begin(); iter != _screen.end(); ++iter)
{
iter->resize(w);
}
_frame.size = iSize(w, h);
}
_port.frame.size = iSize(w, h);
if (_port.cursor.y >= h) _port.cursor.y = h - 1;
if (_port.cursor.x >= w) _port.cursor.x = w - 1;
if (_cursor.y >= h) _cursor.y = h - 1;
if (_cursor.x >= w) _cursor.x = w - 1;
//fprintf(stderr, "setSize(%u, %u)\n", width, height);

View File

@ -17,45 +17,27 @@
typedef struct CharInfo {
typedef struct char_info {
CharInfo() = default;
CharInfo(uint8_t cc, uint8_t ff) : c(cc), flag(ff) {}
char_info() = default;
char_info(uint8_t cc, uint8_t ff) : c(cc), flag(ff) {}
uint8_t c = 0;
uint8_t flag = 0;
} CharInfo;
} char_info;
typedef struct TextPort {
enum MarginBehavior {
MarginTruncate,
MarginWrap,
MarginOverwrite
};
iRect frame;
typedef struct context {
uint8_t flags = 0;
iRect window;
iPoint cursor;
void setFlagBit(unsigned x) { flags |= x; }
void clearFlagBit(unsigned x) { flags &= ~x; }
} context;
MarginBehavior leftMargin = MarginTruncate;
MarginBehavior rightMargin = MarginTruncate;
bool advanceCursor = true;
bool scroll = true;
// clamp setCursor calls.
bool clampX = true;
bool clampY = true;
iPoint absoluteCursor() const;
} TextPort;
class Screen {
@ -72,7 +54,7 @@ public:
static const unsigned FlagSelected = 0x8000;
/*
enum EraseRegion {
EraseAll,
EraseBeforeCursor,
@ -82,7 +64,7 @@ public:
EraseLineBeforeCursor,
EraseLineAfterCursor
};
*/
enum CursorType {
CursorTypeNone,
CursorTypeUnderscore,
@ -101,85 +83,38 @@ public:
int y() const;
iPoint cursor() const;
uint8_t flag() const;
int height() const;
int width() const;
int incrementX(bool clamp = true);
int decrementX(bool clamp = true);
int incrementY(bool clamp = true);
int decrementY(bool clamp = true);
void setX(int x, bool clamp = true);
void setY(int y, bool clamp = true);
void setCursor(iPoint point, bool clampX = true, bool clampY = true);
void setCursor(int x, int y, bool clampX = true, bool clampY = true);
void setCursor(iPoint point);
int setX(TextPort *textPort, int x);
int setY(TextPort *textPort, int y);
void putc(uint8_t c, const context &);
char_info getc(iPoint p) const;
int incrementX(TextPort *textPort);
int incrementY(TextPort *textPort);
int decrementX(TextPort *textPort);
int decrementY(TextPort *textPort);
void setCursor(TextPort *textPort, iPoint point);
void setCursor(TextPort *textPort, int x, int y);
void setFlag(uint8_t flag);
void setFlagBit(uint8_t bit);
void clearFlagBit(uint8_t bit);
void putc(uint8_t c, bool incrementX = true);
void putc(TextPort *textPort, uint8_t c);
void putc(TextPort *textPort, uint8_t c, uint8_t flags);
CharInfo getc(int x, int y) const;
void deletec();
void insertc(uint8_t c);
void tabTo(unsigned x);
void tabTo(TextPort *textPort, unsigned x);
void erase(EraseRegion);
void erase(TextPort *, EraseRegion);
void eraseLine();
void eraseScreen();
void eraseRect(iRect rect);
void lineFeed();
int lineFeed(TextPort *textPort);
void reverseLineFeed();
int reverseLineFeed(TextPort *textPort);
void scrollUp();
void scrollUp(iRect window);
void scrollDown();
void scrollDown(iRect window);
void deleteLine(unsigned line);
void insertLine(unsigned line);
void insertLine(TextPort *textPort, int line);
void deleteLine(TextPort *textPort, int line);
//void deletec();
//void insertc(uint8_t c);
void insertc(TextPort *textPort, uint8_t c);
void deletec(TextPort *textPort);
void beginUpdate();
iRect endUpdate();
@ -187,36 +122,32 @@ public:
void lock();
void unlock();
void setTextPort(const TextPort& textPort);
virtual void setSize(unsigned width, unsigned height);
virtual void setCursorType(CursorType cursor);
CursorType cursorType() const;
private:
TextPort _port;
uint8_t _flag;
iRect _frame;
iPoint _cursor;
CursorType _cursorType;
Lock _lock;
std::vector< std::vector< CharInfo > > _screen;
std::vector< std::vector< char_info > > _screen;
std::vector<iPoint> _updates;
iPoint _updateCursor;
typedef std::vector< std::vector< CharInfo > >::iterator ScreenIterator;
typedef std::vector< std::vector< CharInfo > >::reverse_iterator ReverseScreenIterator;
typedef std::vector< std::vector< char_info > >::iterator ScreenIterator;
typedef std::vector< std::vector< char_info > >::reverse_iterator ReverseScreenIterator;
typedef std::vector<CharInfo>::iterator CharInfoIterator;
typedef std::vector<char_info>::iterator CharInfoIterator;
typedef std::vector<iPoint>::iterator UpdateIterator;
};
@ -224,23 +155,19 @@ private:
inline int Screen::x() const
{
return _port.cursor.x;
return _cursor.x;
}
inline int Screen::y() const
{
return _port.cursor.y;
return _cursor.y;
}
inline iPoint Screen::cursor() const
{
return _port.cursor;
return _cursor;
}
inline uint8_t Screen::flag() const
{
return _flag;
}
inline Screen::CursorType Screen::cursorType() const
{
@ -249,24 +176,17 @@ inline Screen::CursorType Screen::cursorType() const
inline int Screen::height() const
{
return _port.frame.size.height;
return _frame.size.height;
}
inline int Screen::width() const
{
return _port.frame.size.width;
return _frame.size.width;
}
inline void Screen::setCursor(iPoint point, bool clampX, bool clampY)
inline void Screen::setCursor(iPoint point)
{
setX(point.x, clampX);
setY(point.y, clampY);
}
inline void Screen::setCursor(int x, int y, bool clampX, bool clampY)
{
setX(x, clampX);
setY(y, clampY);
_cursor = point;
}
@ -281,12 +201,10 @@ inline void Screen::unlock()
}
inline CharInfo Screen::getc(int x, int y) const
inline char_info Screen::getc(iPoint p) const
{
if (x < 0 || y < 0) return CharInfo();
if (x >= width() || y >= height()) return CharInfo(0,0);
return _screen[y][x];
if (_frame.contains(p)) return _screen[p.y][p.x];
return char_info();
}
#endif