import 'package:dartterm/colors.dart'; import 'package:dartterm/cp437.dart'; import 'package:dartterm/fonts.dart'; import 'package:dartterm/terminal_painter.dart'; import 'package:flutter/material.dart'; class Terminal { static const int width = 88; static const int height = 50; static const int nTiles = width * height; late List tiles; Terminal() { tiles = [for (var i = 0; i < nTiles; i += 1) Tile.empty()]; } CustomPaint toWidget(BuildContext context) { var scalingFactor = MediaQuery.devicePixelRatioOf(context); return CustomPaint(painter: TerminalCustomPainter(this, scalingFactor)); } (int, int)? toXY(int i) { if (i < 0 || i > nTiles) { return null; } return (i % width, i ~/ width); } int? fromXY(int x, int y) { if (x < 0 || x >= width) { return null; } if (y < 0 || y >= height) { return null; } return x + y * width; } } class Cursor { final Terminal t; int x, y; Color? _bg, _fg; Font font = Font.normal; // TODO: Clip Cursor({required this.t, required this.x, required this.y}); void clear() { t.tiles = [for (var i = 0; i < nTiles; i += 1) Tile.empty()]; } void putc(int c) { for (var dx = 0; dx < font.cellWidth; dx += 1) { for (var dy = 0; dy < font.cellHeight; dy += 1) { var i = t.fromXY(x + dx, y + dy); if (i != null) { final (sourceCx, sourceCy) = ( (c % font.nCellsW) * font.cellWidth + dx, (c ~/ font.nCellsW) * font.cellHeight + dy ); t.tiles[i].content = Content(font.imageName, sourceCx, sourceCy); final (bg, fg) = (_bg, _fg); if (bg != null) { t.tiles[i].bg = bg; } if (fg != null) { t.tiles[i].fg = fg; } } } } } void puts(String s) { var startX = x; for (final c in toCp437String(s)) { if (c == 10) { // newline x = startX; y += font.cellHeight; continue; } if (c == 13) { // carriage return x = startX; continue; } putc(c); x += font.cellWidth; } } Cursor fg(Color c) { _fg = c; return this; } Cursor bg(Color c) { _bg = c; return this; } Cursor small() { font = Font.small; return this; } Cursor normal() { font = Font.normal; return this; } Cursor big() { font = Font.big; return this; } } class Clip {} class Tile { Content? content; Color bg; Color fg; Tile(this.content, this.bg, this.fg); static Tile empty() => Tile(null, Palette.defaultBg, Palette.defaultFg); } class Content { final String sourceImage; final int sourceCx; final int sourceCy; const Content(this.sourceImage, this.sourceCx, this.sourceCy); } // reexports Terminal _terminal = Terminal(); const int width = Terminal.width; const int height = Terminal.height; const int nTiles = Terminal.nTiles; Widget toWidget(BuildContext context) { return _terminal.toWidget(context); } void clear() { at(0, 0).clear(); } Cursor at(int x, int y) { return Cursor(t: _terminal, x: x, y: y); }