/* * This file is part of ComputerCraft - http://www.computercraft.info * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ package dan200.computercraft.core.terminal; import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.Palette; import net.minecraft.nbt.CompoundNBT; import net.minecraft.network.PacketBuffer; import javax.annotation.Nonnull; public class Terminal { private static final String base16 = "0123456789abcdef"; private int cursorX = 0; private int cursorY = 0; private boolean cursorBlink = false; private int cursorColour = 0; private int cursorBackgroundColour = 15; private int width; private int height; private TextBuffer[] text; private TextBuffer[] textColour; private TextBuffer[] backgroundColour; private final Palette palette = new Palette(); private final Runnable onChanged; public Terminal( int width, int height ) { this( width, height, null ); } public Terminal( int width, int height, Runnable changedCallback ) { this.width = width; this.height = height; onChanged = changedCallback; text = new TextBuffer[height]; textColour = new TextBuffer[height]; backgroundColour = new TextBuffer[height]; for( int i = 0; i < this.height; i++ ) { text[i] = new TextBuffer( ' ', this.width ); textColour[i] = new TextBuffer( base16.charAt( cursorColour ), this.width ); backgroundColour[i] = new TextBuffer( base16.charAt( cursorBackgroundColour ), this.width ); } } public synchronized void reset() { cursorColour = 0; cursorBackgroundColour = 15; cursorX = 0; cursorY = 0; cursorBlink = false; clear(); setChanged(); palette.resetColours(); } public int getWidth() { return width; } public int getHeight() { return height; } public synchronized void resize( int width, int height ) { if( width == this.width && height == this.height ) { return; } int oldHeight = this.height; int oldWidth = this.width; TextBuffer[] oldText = text; TextBuffer[] oldTextColour = textColour; TextBuffer[] oldBackgroundColour = backgroundColour; this.width = width; this.height = height; text = new TextBuffer[height]; textColour = new TextBuffer[height]; backgroundColour = new TextBuffer[height]; for( int i = 0; i < this.height; i++ ) { if( i >= oldHeight ) { text[i] = new TextBuffer( ' ', this.width ); textColour[i] = new TextBuffer( base16.charAt( cursorColour ), this.width ); backgroundColour[i] = new TextBuffer( base16.charAt( cursorBackgroundColour ), this.width ); } else if( this.width == oldWidth ) { text[i] = oldText[i]; textColour[i] = oldTextColour[i]; backgroundColour[i] = oldBackgroundColour[i]; } else { text[i] = new TextBuffer( ' ', this.width ); textColour[i] = new TextBuffer( base16.charAt( cursorColour ), this.width ); backgroundColour[i] = new TextBuffer( base16.charAt( cursorBackgroundColour ), this.width ); text[i].write( oldText[i] ); textColour[i].write( oldTextColour[i] ); backgroundColour[i].write( oldBackgroundColour[i] ); } } setChanged(); } public void setCursorPos( int x, int y ) { if( cursorX != x || cursorY != y ) { cursorX = x; cursorY = y; setChanged(); } } public void setCursorBlink( boolean blink ) { if( cursorBlink != blink ) { cursorBlink = blink; setChanged(); } } public void setTextColour( int colour ) { if( cursorColour != colour ) { cursorColour = colour; setChanged(); } } public void setBackgroundColour( int colour ) { if( cursorBackgroundColour != colour ) { cursorBackgroundColour = colour; setChanged(); } } public int getCursorX() { return cursorX; } public int getCursorY() { return cursorY; } public boolean getCursorBlink() { return cursorBlink; } public int getTextColour() { return cursorColour; } public int getBackgroundColour() { return cursorBackgroundColour; } @Nonnull public Palette getPalette() { return palette; } public synchronized void blit( String text, String textColour, String backgroundColour ) { int x = cursorX; int y = cursorY; if( y >= 0 && y < height ) { this.text[y].write( text, x ); this.textColour[y].write( textColour, x ); this.backgroundColour[y].write( backgroundColour, x ); setChanged(); } } public synchronized void write( String text ) { int x = cursorX; int y = cursorY; if( y >= 0 && y < height ) { this.text[y].write( text, x ); textColour[y].fill( base16.charAt( cursorColour ), x, x + text.length() ); backgroundColour[y].fill( base16.charAt( cursorBackgroundColour ), x, x + text.length() ); setChanged(); } } public synchronized void scroll( int yDiff ) { if( yDiff != 0 ) { TextBuffer[] newText = new TextBuffer[height]; TextBuffer[] newTextColour = new TextBuffer[height]; TextBuffer[] newBackgroundColour = new TextBuffer[height]; for( int y = 0; y < height; y++ ) { int oldY = y + yDiff; if( oldY >= 0 && oldY < height ) { newText[y] = text[oldY]; newTextColour[y] = textColour[oldY]; newBackgroundColour[y] = backgroundColour[oldY]; } else { newText[y] = new TextBuffer( ' ', width ); newTextColour[y] = new TextBuffer( base16.charAt( cursorColour ), width ); newBackgroundColour[y] = new TextBuffer( base16.charAt( cursorBackgroundColour ), width ); } } text = newText; textColour = newTextColour; backgroundColour = newBackgroundColour; setChanged(); } } public synchronized void clear() { for( int y = 0; y < height; y++ ) { text[y].fill( ' ' ); textColour[y].fill( base16.charAt( cursorColour ) ); backgroundColour[y].fill( base16.charAt( cursorBackgroundColour ) ); } setChanged(); } public synchronized void clearLine() { int y = cursorY; if( y >= 0 && y < height ) { text[y].fill( ' ' ); textColour[y].fill( base16.charAt( cursorColour ) ); backgroundColour[y].fill( base16.charAt( cursorBackgroundColour ) ); setChanged(); } } public synchronized TextBuffer getLine( int y ) { if( y >= 0 && y < height ) { return text[y]; } return null; } public synchronized void setLine( int y, String text, String textColour, String backgroundColour ) { this.text[y].write( text ); this.textColour[y].write( textColour ); this.backgroundColour[y].write( backgroundColour ); setChanged(); } public synchronized TextBuffer getTextColourLine( int y ) { if( y >= 0 && y < height ) { return textColour[y]; } return null; } public synchronized TextBuffer getBackgroundColourLine( int y ) { if( y >= 0 && y < height ) { return backgroundColour[y]; } return null; } public final void setChanged() { if( onChanged != null ) onChanged.run(); } public synchronized void write( PacketBuffer buffer ) { buffer.writeInt( cursorX ); buffer.writeInt( cursorY ); buffer.writeBoolean( cursorBlink ); buffer.writeByte( cursorBackgroundColour << 4 | cursorColour ); for( int y = 0; y < height; y++ ) { TextBuffer text = this.text[y]; TextBuffer textColour = this.textColour[y]; TextBuffer backColour = backgroundColour[y]; for( int x = 0; x < width; x++ ) { buffer.writeByte( text.charAt( x ) & 0xFF ); buffer.writeByte( getColour( backColour.charAt( x ), Colour.BLACK ) << 4 | getColour( textColour.charAt( x ), Colour.WHITE ) ); } } palette.write( buffer ); } public synchronized void read( PacketBuffer buffer ) { cursorX = buffer.readInt(); cursorY = buffer.readInt(); cursorBlink = buffer.readBoolean(); byte cursorColour = buffer.readByte(); cursorBackgroundColour = (cursorColour >> 4) & 0xF; this.cursorColour = cursorColour & 0xF; for( int y = 0; y < height; y++ ) { TextBuffer text = this.text[y]; TextBuffer textColour = this.textColour[y]; TextBuffer backColour = backgroundColour[y]; for( int x = 0; x < width; x++ ) { text.setChar( x, (char) (buffer.readByte() & 0xFF) ); byte colour = buffer.readByte(); backColour.setChar( x, base16.charAt( (colour >> 4) & 0xF ) ); textColour.setChar( x, base16.charAt( colour & 0xF ) ); } } palette.read( buffer ); setChanged(); } public synchronized CompoundNBT writeToNBT( CompoundNBT nbt ) { nbt.putInt( "term_cursorX", cursorX ); nbt.putInt( "term_cursorY", cursorY ); nbt.putBoolean( "term_cursorBlink", cursorBlink ); nbt.putInt( "term_textColour", cursorColour ); nbt.putInt( "term_bgColour", cursorBackgroundColour ); for( int n = 0; n < height; n++ ) { nbt.putString( "term_text_" + n, text[n].toString() ); nbt.putString( "term_textColour_" + n, textColour[n].toString() ); nbt.putString( "term_textBgColour_" + n, backgroundColour[n].toString() ); } palette.writeToNBT( nbt ); return nbt; } public synchronized void readFromNBT( CompoundNBT nbt ) { cursorX = nbt.getInt( "term_cursorX" ); cursorY = nbt.getInt( "term_cursorY" ); cursorBlink = nbt.getBoolean( "term_cursorBlink" ); cursorColour = nbt.getInt( "term_textColour" ); cursorBackgroundColour = nbt.getInt( "term_bgColour" ); for( int n = 0; n < height; n++ ) { text[n].fill( ' ' ); if( nbt.contains( "term_text_" + n ) ) { text[n].write( nbt.getString( "term_text_" + n ) ); } textColour[n].fill( base16.charAt( cursorColour ) ); if( nbt.contains( "term_textColour_" + n ) ) { textColour[n].write( nbt.getString( "term_textColour_" + n ) ); } backgroundColour[n].fill( base16.charAt( cursorBackgroundColour ) ); if( nbt.contains( "term_textBgColour_" + n ) ) { backgroundColour[n].write( nbt.getString( "term_textBgColour_" + n ) ); } } palette.readFromNBT( nbt ); setChanged(); } public static int getColour( char c, Colour def ) { if( c >= '0' && c <= '9' ) return c - '0'; if( c >= 'a' && c <= 'f' ) return c - 'a' + 10; return 15 - def.ordinal(); } }