| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- import 'dart:math' show max, min;
- import 'package:xterm/buffer/buffer_line.dart';
- import 'package:xterm/buffer/cell.dart';
- import 'package:xterm/buffer/cell_attr.dart';
- import 'package:xterm/terminal/charset.dart';
- import 'package:xterm/terminal/terminal.dart';
- import 'package:xterm/utli/scroll_range.dart';
- import 'package:xterm/utli/unicode_v11.dart';
- class Buffer {
- Buffer(this.terminal) {
- resetVerticalMargins();
- }
- final Terminal terminal;
- final lines = <BufferLine>[];
- final charset = Charset();
- int _cursorX = 0;
- int _cursorY = 0;
- int _savedCursorX;
- int _savedCursorY;
- int _scrollLinesFromBottom = 0;
- int _marginTop;
- int _marginBottom;
- CellAttr _savedCellAttr;
- int get cursorX => _cursorX.clamp(0, terminal.viewWidth - 1);
- int get cursorY => _cursorY;
- int get marginTop => _marginTop;
- int get marginBottom => _marginBottom;
- void write(String text) {
- for (var char in text.runes) {
- writeChar(char);
- }
- }
- void writeChar(int codePoint) {
- codePoint = charset.translate(codePoint);
- final cellWidth = unicodeV11.wcwidth(codePoint);
- if (_cursorX >= terminal.viewWidth) {
- newLine();
- setCursorX(0);
- }
- final line = currentLine;
- while (line.length <= _cursorX) {
- line.add(Cell());
- }
- final cell = line.getCell(_cursorX);
- cell.setCodePoint(codePoint);
- cell.setWidth(cellWidth);
- cell.setAttr(terminal.cellAttr.value);
- if (_cursorX < terminal.viewWidth) {
- _cursorX++;
- }
- if (cellWidth == 2) {
- writeChar(0);
- }
- }
- BufferLine getViewLine(int index) {
- if (index > terminal.viewHeight) {
- return lines.last;
- }
- while (index >= lines.length) {
- final newLine = BufferLine();
- lines.add(newLine);
- }
- return lines[convertViewLineToRawLine(index)];
- }
- BufferLine get currentLine {
- return getViewLine(_cursorY);
- }
- int get height {
- return lines.length;
- }
- int convertViewLineToRawLine(int viewLine) {
- if (terminal.viewHeight > height) {
- return viewLine;
- }
- return viewLine + (height - terminal.viewHeight);
- }
- int convertRawLineToViewLine(int rawLine) {
- if (terminal.viewHeight > height) {
- return rawLine;
- }
- return rawLine - (height - terminal.viewHeight);
- }
- void newLine() {
- if (terminal.lineFeed == false) {
- setCursorX(0);
- }
- index();
- }
- void carriageReturn() {
- setCursorX(0);
- }
- void backspace() {
- if (_cursorX == 0 && currentLine.isWrapped) {
- movePosition(terminal.viewWidth - 1, -1);
- } else if (_cursorX == terminal.viewWidth) {
- movePosition(-2, 0);
- } else {
- movePosition(-1, 0);
- }
- }
- List<BufferLine> getVisibleLines() {
- final result = <BufferLine>[];
- for (var i = height - terminal.viewHeight; i < height; i++) {
- final y = i - scrollOffsetFromBottom;
- if (y >= 0 && y < height) {
- result.add(lines[y]);
- }
- }
- return result;
- }
- void eraseDisplayFromCursor() {
- eraseLineFromCursor();
- for (var i = _cursorY + 1; i < terminal.viewHeight; i++) {
- getViewLine(i).erase(terminal.cellAttr.value, 0, terminal.viewWidth);
- }
- }
- void eraseDisplayToCursor() {
- eraseLineToCursor();
- for (var i = 0; i < _cursorY; i++) {
- getViewLine(i).erase(terminal.cellAttr.value, 0, terminal.viewWidth);
- }
- }
- void eraseDisplay() {
- for (var i = 0; i < terminal.viewHeight; i++) {
- final line = getViewLine(i);
- line.erase(terminal.cellAttr.value, 0, terminal.viewWidth);
- }
- }
- void eraseLineFromCursor() {
- currentLine.erase(terminal.cellAttr.value, _cursorX, terminal.viewWidth);
- }
- void eraseLineToCursor() {
- currentLine.erase(terminal.cellAttr.value, 0, _cursorX);
- }
- void eraseLine() {
- currentLine.erase(terminal.cellAttr.value, 0, terminal.viewWidth);
- }
- void eraseCharacters(int count) {
- final start = _cursorX;
- for (var i = start; i < start + count; i++) {
- if (i >= currentLine.length) {
- currentLine.add(Cell(attr: terminal.cellAttr.value));
- } else {
- currentLine.getCell(i).erase(terminal.cellAttr.value);
- }
- }
- }
- ScrollRange getAreaScrollRange() {
- var top = convertViewLineToRawLine(_marginTop);
- var bottom = convertViewLineToRawLine(_marginBottom) + 1;
- if (bottom > lines.length) {
- bottom = lines.length;
- }
- return ScrollRange(top, bottom);
- }
- void areaScrollDown(int lines) {
- final scrollRange = getAreaScrollRange();
- for (var i = scrollRange.bottom; i > scrollRange.top;) {
- i--;
- if (i >= scrollRange.top + lines) {
- this.lines[i] = this.lines[i - lines];
- } else {
- this.lines[i] = BufferLine();
- }
- }
- }
- void areaScrollUp(int lines) {
- final scrollRange = getAreaScrollRange();
- for (var i = scrollRange.top; i < scrollRange.bottom; i++) {
- if (i + lines < scrollRange.bottom) {
- this.lines[i] = this.lines[i + lines];
- } else {
- this.lines[i] = BufferLine();
- }
- }
- }
- /// https://vt100.net/docs/vt100-ug/chapter3.html#IND
- void index() {
- if (isInScrollableRegion) {
- if (_cursorY < _marginBottom) {
- moveCursorY(1);
- } else {
- areaScrollUp(1);
- }
- return;
- }
- if (_cursorY >= terminal.viewHeight - 1) {
- lines.add(BufferLine());
- if (terminal.maxLines != null && lines.length > terminal.maxLines) {
- lines.removeRange(0, lines.length - terminal.maxLines);
- }
- } else {
- moveCursorY(1);
- }
- }
- /// https://vt100.net/docs/vt100-ug/chapter3.html#RI
- void reverseIndex() {
- if (_cursorY == _marginTop) {
- areaScrollDown(1);
- } else if (_cursorY > 0) {
- moveCursorY(-1);
- }
- }
- Cell getCell(int col, int row) {
- final rawRow = convertViewLineToRawLine(row);
- return getRawCell(col, rawRow);
- }
- Cell getRawCell(int col, int rawRow) {
- if (col < 0 || rawRow < 0 || rawRow >= lines.length) {
- return null;
- }
- final line = lines[rawRow];
- if (col >= line.length) {
- return null;
- }
- return line.getCell(col);
- }
- Cell getCellUnderCursor() {
- return getCell(cursorX, cursorY);
- }
- void cursorGoForward() {
- setCursorX(_cursorX + 1);
- terminal.refresh();
- }
- void setCursorX(int cursorX) {
- _cursorX = cursorX.clamp(0, terminal.viewWidth - 1);
- terminal.refresh();
- }
- void setCursorY(int cursorY) {
- _cursorY = cursorY.clamp(0, terminal.viewHeight - 1);
- terminal.refresh();
- }
- void moveCursorX(int offset) {
- setCursorX(_cursorX + offset);
- }
- void moveCursorY(int offset) {
- setCursorY(_cursorY + offset);
- }
- void setPosition(int cursorX, int cursorY) {
- var maxLine = terminal.viewHeight - 1;
- if (terminal.originMode) {
- cursorY += _marginTop;
- maxLine = _marginBottom;
- }
- _cursorX = cursorX.clamp(0, terminal.viewWidth - 1);
- _cursorY = cursorY.clamp(0, maxLine);
- }
- void movePosition(int offsetX, int offsetY) {
- final cursorX = _cursorX + offsetX;
- final cursorY = _cursorY + offsetY;
- setPosition(cursorX, cursorY);
- }
- int get scrollOffsetFromBottom {
- return _scrollLinesFromBottom;
- }
- int get scrollOffsetFromTop {
- return terminal.invisibleHeight - scrollOffsetFromBottom;
- }
- void setScrollOffsetFromBottom(int offset) {
- if (height < terminal.viewHeight) return;
- final maxOffset = height - terminal.viewHeight;
- _scrollLinesFromBottom = offset.clamp(0, maxOffset);
- terminal.refresh();
- }
- void setScrollOffsetFromTop(int offset) {
- final bottomOffset = terminal.invisibleHeight - offset;
- setScrollOffsetFromBottom(bottomOffset);
- }
- void screenScrollUp(int lines) {
- setScrollOffsetFromBottom(scrollOffsetFromBottom + lines);
- }
- void screenScrollDown(int lines) {
- setScrollOffsetFromBottom(scrollOffsetFromBottom - lines);
- }
- void saveCursor() {
- _savedCellAttr = terminal.cellAttr.value;
- _savedCursorX = _cursorX;
- _savedCursorY = _cursorY;
- charset.save();
- }
- void restoreCursor() {
- if (_savedCellAttr != null) {
- terminal.cellAttr.use(_savedCellAttr);
- }
- if (_savedCursorX != null) {
- _cursorX = _savedCursorX;
- }
- if (_savedCursorY != null) {
- _cursorY = _savedCursorY;
- }
- charset.restore();
- }
- void setVerticalMargins(int top, int bottom) {
- _marginTop = top.clamp(0, terminal.viewHeight - 1);
- _marginBottom = bottom.clamp(0, terminal.viewHeight - 1);
- _marginTop = min(_marginTop, _marginBottom);
- _marginBottom = max(_marginTop, _marginBottom);
- }
- bool get hasScrollableRegion {
- return _marginTop > 0 || _marginBottom < (terminal.viewHeight - 1);
- }
- bool get isInScrollableRegion {
- return hasScrollableRegion &&
- _cursorY >= _marginTop &&
- _cursorY <= _marginBottom;
- }
- void resetVerticalMargins() {
- setVerticalMargins(0, terminal.viewHeight - 1);
- }
- void deleteChars(int count) {
- final start = _cursorX.clamp(0, currentLine.length);
- final end = min(_cursorX + count, currentLine.length);
- currentLine.removeRange(start, end);
- }
- void clearScrollback() {
- if (lines.length <= terminal.viewHeight) {
- return;
- }
- lines.removeRange(0, lines.length - terminal.viewHeight);
- }
- void clear() {
- lines.clear();
- }
- void insertBlankCharacters(int count) {
- for (var i = 0; i < count; i++) {
- final cell = Cell(attr: terminal.cellAttr.value);
- currentLine.insert(_cursorX + i, cell);
- }
- }
- void insertLines(int count) {
- if (hasScrollableRegion && !isInScrollableRegion) {
- return;
- }
- setCursorX(0);
- for (var i = 0; i < count; i++) {
- insertLine();
- }
- }
- void insertLine() {
- if (!isInScrollableRegion) {
- final index = convertViewLineToRawLine(_cursorX);
- final newLine = BufferLine();
- lines.insert(index, newLine);
- if (terminal.maxLines != null && lines.length > terminal.maxLines) {
- lines.removeRange(0, lines.length - terminal.maxLines);
- }
- } else {
- final bottom = convertViewLineToRawLine(marginBottom);
- final movedLines = lines.getRange(_cursorY, bottom - 1);
- lines.setRange(_cursorY + 1, bottom, movedLines);
- final newLine = BufferLine();
- lines[_cursorY] = newLine;
- }
- }
- void deleteLines(int count) {
- if (hasScrollableRegion && !isInScrollableRegion) {
- return;
- }
- setCursorX(0);
- for (var i = 0; i < count; i++) {
- deleteLine();
- }
- }
- void deleteLine() {
- final index = convertViewLineToRawLine(_cursorX);
- if (index >= height) {
- return;
- }
- lines.removeAt(index);
- }
- }
|