425 lines
12 KiB
Java
425 lines
12 KiB
Java
/*
|
|
* 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();
|
|
}
|
|
}
|