diff --git a/Emulators/GNOConsole.h b/Emulators/GNOConsole.h index 79174f1..a8cac06 100644 --- a/Emulators/GNOConsole.h +++ b/Emulators/GNOConsole.h @@ -16,7 +16,7 @@ @interface GNOConsole : NSObject { unsigned cs; - TextPort _textPort; + context _context; Screen::CursorType _cursorType; int _scratch[4]; diff --git a/Emulators/GNOConsole.mm.ragel b/Emulators/GNOConsole.mm.ragel index b6e8eb4..0dfc6b4 100644 --- a/Emulators/GNOConsole.mm.ragel +++ b/Emulators/GNOConsole.mm.ragel @@ -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); } diff --git a/Emulators/GSOSConsole.h b/Emulators/GSOSConsole.h index 36671eb..586c507 100644 --- a/Emulators/GSOSConsole.h +++ b/Emulators/GSOSConsole.h @@ -16,6 +16,7 @@ unsigned cs; TextPort _textPort; + iRect _window; std::vector _tpStack; diff --git a/TermWindowController.mm b/TermWindowController.mm index a819f96..0fb16f8 100644 --- a/TermWindowController.mm +++ b/TermWindowController.mm @@ -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]; diff --git a/Views/EmulatorView.mm b/Views/EmulatorView.mm index cfb1160..3e601f6 100644 --- a/Views/EmulatorView.mm +++ b/Views/EmulatorView.mm @@ -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]; } diff --git a/cpp/Screen.cpp b/cpp/Screen.cpp index faf8988..a7d161c 100644 --- a/cpp/Screen.cpp +++ b/cpp/Screen.cpp @@ -11,35 +11,17 @@ #include -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 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 +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 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 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 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); diff --git a/cpp/Screen.h b/cpp/Screen.h index 35ee30f..68f0db9 100644 --- a/cpp/Screen.h +++ b/cpp/Screen.h @@ -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 _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::iterator CharInfoIterator; + typedef std::vector::iterator CharInfoIterator; typedef std::vector::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