Automatically use sixel if it's available

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-20 18:19:19 +01:00
parent 2598218a4d
commit 45e5302eac
3 changed files with 31 additions and 14 deletions

View File

@@ -25,10 +25,10 @@ class CliGameLoop {
'CliGameLoop requires a CliInput instance.', 'CliGameLoop requires a CliInput instance.',
), ),
primaryRenderer = AsciiRenderer( primaryRenderer = SixelRenderer(),
secondaryRenderer = AsciiRenderer(
mode: AsciiRendererMode.terminalAnsi, mode: AsciiRendererMode.terminalAnsi,
), ) {
secondaryRenderer = SixelRenderer() {
_renderer = primaryRenderer; _renderer = primaryRenderer;
} }
@@ -44,6 +44,7 @@ class CliGameLoop {
StreamSubscription<List<int>>? _stdinSubscription; StreamSubscription<List<int>>? _stdinSubscription;
Timer? _timer; Timer? _timer;
bool _isRunning = false; bool _isRunning = false;
bool _isSixelAvailable = false;
Duration _lastTick = Duration.zero; Duration _lastTick = Duration.zero;
/// Starts terminal probing, enters raw input mode, and begins the frame timer. /// Starts terminal probing, enters raw input mode, and begins the frame timer.
@@ -57,6 +58,11 @@ class CliGameLoop {
sixel.isSixelSupported = await SixelRenderer.checkTerminalSixelSupport( sixel.isSixelSupported = await SixelRenderer.checkTerminalSixelSupport(
inputStream: _stdinStream, inputStream: _stdinStream,
); );
_isSixelAvailable = sixel.isSixelSupported;
_renderer = _isSixelAvailable ? primaryRenderer : secondaryRenderer;
} else {
_isSixelAvailable = false;
_renderer = secondaryRenderer;
} }
if (stdin.hasTerminal) { if (stdin.hasTerminal) {
@@ -118,6 +124,9 @@ class CliGameLoop {
} }
if (input.matchesRendererToggleShortcut(bytes)) { if (input.matchesRendererToggleShortcut(bytes)) {
if (!_isSixelAvailable) {
return;
}
// Allow dynamic renderer-switch bindings configured on the CLI input. // Allow dynamic renderer-switch bindings configured on the CLI input.
_renderer = identical(_renderer, secondaryRenderer) _renderer = identical(_renderer, secondaryRenderer)
? primaryRenderer ? primaryRenderer
@@ -204,6 +213,9 @@ class CliGameLoop {
final int safeCols = cols > 1 ? cols - 1 : cols; final int safeCols = cols > 1 ? cols - 1 : cols;
final String hint = _buildShortcutHintText(); final String hint = _buildShortcutHintText();
if (hint.isEmpty) {
return;
}
final String visible = hint.length > safeCols final String visible = hint.length > safeCols
? hint.substring(0, safeCols) ? hint.substring(0, safeCols)
: hint; : hint;
@@ -214,6 +226,14 @@ class CliGameLoop {
} }
String _buildShortcutHintText() { String _buildShortcutHintText() {
if (!_isSixelAvailable) {
if (_renderer is AsciiRenderer) {
final AsciiRenderer ascii = _renderer as AsciiRenderer;
return '<${input.asciiThemeCycleKeyLabel}> ${ascii.activeTheme.name}';
}
return '';
}
final String rendererMode = _renderer is SixelRenderer ? 'sixel' : 'ascii'; final String rendererMode = _renderer is SixelRenderer ? 'sixel' : 'ascii';
final String rendererHint = final String rendererHint =
'<${input.rendererToggleKeyLabel}> $rendererMode'; '<${input.rendererToggleKeyLabel}> $rendererMode';

View File

@@ -1327,7 +1327,7 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
} }
// 1. Pull Retro Colors // 1. Pull Retro Colors
final int vgaStatusBarBlue = ColorPalette.vga32Bit[153]; final int hudBackground = ColorPalette.vga32Bit[0];
final int vgaPanelDark = ColorPalette.vga32Bit[0]; final int vgaPanelDark = ColorPalette.vga32Bit[0];
final int white = ColorPalette.vga32Bit[15]; final int white = ColorPalette.vga32Bit[15];
final int yellow = ColorPalette.vga32Bit[11]; final int yellow = ColorPalette.vga32Bit[11];
@@ -1364,7 +1364,7 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
viewHeight * 2, viewHeight * 2,
projectionWidth, projectionWidth,
hudRows * 2, hudRows * 2,
vgaStatusBarBlue, hudBackground,
); );
_fillTerminalRect( _fillTerminalRect(
projectionOffsetX, projectionOffsetX,
@@ -1380,7 +1380,7 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
width, width,
height - viewHeight, height - viewHeight,
' ', ' ',
vgaStatusBarBlue, hudBackground,
); );
_writeString(0, viewHeight, "" * width, white); _writeString(0, viewHeight, "" * width, white);
} }
@@ -1481,7 +1481,7 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
} }
void _drawMinimalHud(WolfEngine engine) { void _drawMinimalHud(WolfEngine engine) {
final int vgaStatusBarBlue = ColorPalette.vga32Bit[153]; final int hudBackground = ColorPalette.vga32Bit[0];
final int white = ColorPalette.vga32Bit[15]; final int white = ColorPalette.vga32Bit[15];
final int red = ColorPalette.vga32Bit[4]; final int red = ColorPalette.vga32Bit[4];
@@ -1492,7 +1492,7 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
viewHeight * 2, viewHeight * 2,
projectionWidth, projectionWidth,
hudRows * 2, hudRows * 2,
vgaStatusBarBlue, hudBackground,
); );
_fillTerminalRect( _fillTerminalRect(
projectionOffsetX, projectionOffsetX,
@@ -1502,7 +1502,7 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
white, white,
); );
} else { } else {
_fillRect(0, viewHeight, width, hudRows, ' ', vgaStatusBarBlue); _fillRect(0, viewHeight, width, hudRows, ' ', hudBackground);
_writeString(0, viewHeight, "" * width, white); _writeString(0, viewHeight, "" * width, white);
} }
@@ -1527,7 +1527,7 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
} }
final int startX = drawStartX + ((drawWidth - clipped.length) ~/ 2); final int startX = drawStartX + ((drawWidth - clipped.length) ~/ 2);
_writeString(startX, lineY, clipped, healthColor, vgaStatusBarBlue); _writeString(startX, lineY, clipped, healthColor, hudBackground);
} }
void _drawFullVgaHud(WolfEngine engine) { void _drawFullVgaHud(WolfEngine engine) {

View File

@@ -34,7 +34,6 @@ class SixelRenderer extends CliRendererBackend<String> {
static const int _maxRenderHeight = 240; static const int _maxRenderHeight = 240;
static const int _menuFooterBottomMargin = 1; static const int _menuFooterBottomMargin = 1;
static const int _headerHeadingY = 24; static const int _headerHeadingY = 24;
static const String _terminalTealBackground = '\x1b[48;2;0;150;136m';
late Uint8List _screen; late Uint8List _screen;
List<int>? _mainMenuBandFirstColumn; List<int>? _mainMenuBandFirstColumn;
@@ -877,9 +876,7 @@ class SixelRenderer extends CliRendererBackend<String> {
return _renderNotSupportedMessage(); return _renderNotSupportedMessage();
} }
final String clearPrefix = _needsBackgroundClear final String clearPrefix = _needsBackgroundClear ? '\x1b[0m\x1b[2J' : '';
? '$_terminalTealBackground\x1b[2J\x1b[0m'
: '';
_needsBackgroundClear = false; _needsBackgroundClear = false;
return '$clearPrefix\x1b[${_offsetRows + 1};${_offsetColumns + 1}H${toSixelString()}'; return '$clearPrefix\x1b[${_offsetRows + 1};${_offsetColumns + 1}H${toSixelString()}';
} }