/* * This file is part of lanterna (http://code.google.com/p/lanterna/). * * lanterna is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Copyright (C) 2010-2012 Martin */ package com.googlecode.lanterna.terminal.text; import com.googlecode.lanterna.input.CommonProfile; import com.googlecode.lanterna.terminal.TerminalSize; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; /** * A common ANSI text terminal implementation * @see <a href="http://en.wikipedia.org/wiki/ANSI_escape_code">Wikipedia</a> * @author Martin */ public abstract class ANSITerminal extends StreamBasedTerminal { ANSITerminal(InputStream terminalInput, OutputStream terminalOutput, Charset terminalCharset) { super(terminalInput, terminalOutput, terminalCharset); addInputProfile(new CommonProfile()); } private void CSI() { writeToTerminal((byte)0x1b, (byte)'['); } @Deprecated @Override public TerminalSize queryTerminalSize() { synchronized(writerMutex) { saveCursorPosition(); moveCursor(5000, 5000); reportPosition(); restoreCursorPosition(); } return getLastKnownSize(); } @Override public TerminalSize getTerminalSize() { queryTerminalSize(); return waitForTerminalSizeReport(1000); //Wait 1 second for the terminal size report to come, is this reasonable? } @Override public void applyBackgroundColor(Color color) { synchronized(writerMutex) { CSI(); writeToTerminal((byte)'4', (byte)((color.getIndex() + "").charAt(0)), (byte)'m'); } } @Override public void applyBackgroundColor(int r, int g, int b) { if(r < 0 || r > 255) throw new IllegalArgumentException("applyForegroundColor: r is outside of valid range (0-255)"); if(g < 0 || g > 255) throw new IllegalArgumentException("applyForegroundColor: g is outside of valid range (0-255)"); if(b < 0 || b > 255) throw new IllegalArgumentException("applyForegroundColor: b is outside of valid range (0-255)"); synchronized(writerMutex) { CSI(); String asString = "48;2;" + r + ";" + g + ";" + b + "m"; for(int i = 0; i < asString.length(); i++) writeToTerminal((byte)asString.charAt(i)); } } @Override public void applyBackgroundColor(int index) { if(index < 0 || index > 255) throw new IllegalArgumentException("applyBackgroundColor: index is outside of valid range (0-255)"); synchronized(writerMutex) { CSI(); String asString = "48;5;" + index + "m"; for(int i = 0; i < asString.length(); i++) writeToTerminal((byte)asString.charAt(i)); } } @Override public void applyForegroundColor(Color color) { synchronized(writerMutex) { CSI(); writeToTerminal((byte)'3', (byte)((color.getIndex() + "").charAt(0)), (byte)'m'); } } @Override public void applyForegroundColor(int r, int g, int b) { if(r < 0 || r > 255) throw new IllegalArgumentException("applyForegroundColor: r is outside of valid range (0-255)"); if(g < 0 || g > 255) throw new IllegalArgumentException("applyForegroundColor: g is outside of valid range (0-255)"); if(b < 0 || b > 255) throw new IllegalArgumentException("applyForegroundColor: b is outside of valid range (0-255)"); synchronized(writerMutex) { CSI(); String asString = "38;2;" + r + ";" + g + ";" + b + "m"; for(int i = 0; i < asString.length(); i++) writeToTerminal((byte)asString.charAt(i)); } } @Override public void applyForegroundColor(int index) { if(index < 0 || index > 255) throw new IllegalArgumentException("applyForegroundColor: index is outside of valid range (0-255)"); synchronized(writerMutex) { CSI(); String asString = "38;5;" + index + "m"; for(int i = 0; i < asString.length(); i++) writeToTerminal((byte)asString.charAt(i)); } } @Override public void applySGR(SGR... options) { synchronized(writerMutex) { CSI(); int index = 0; for(SGR sgr: options) { switch(sgr) { case ENTER_BOLD: writeToTerminal((byte)'1'); break; case ENTER_REVERSE: writeToTerminal((byte)'7'); break; case ENTER_UNDERLINE: writeToTerminal((byte)'4'); break; case EXIT_BOLD: writeToTerminal((byte)'2', (byte)'2'); break; case EXIT_REVERSE: writeToTerminal((byte)'2', (byte)'7'); break; case EXIT_UNDERLINE: writeToTerminal((byte)'2', (byte)'4'); break; case ENTER_BLINK: writeToTerminal((byte)'5'); break; case RESET_ALL: writeToTerminal((byte)'0'); break; } if(index++ < options.length - 1) writeToTerminal((byte)';'); } writeToTerminal((byte)'m'); } } @Override public void clearScreen() { synchronized(writerMutex) { CSI(); writeToTerminal((byte)'2', (byte)'J'); } } @Override public void enterPrivateMode() { synchronized(writerMutex) { CSI(); writeToTerminal((byte)'?', (byte)'1', (byte)'0', (byte)'4', (byte)'9', (byte)'h'); } } @Override public void exitPrivateMode() { synchronized(writerMutex) { applySGR(SGR.RESET_ALL); setCursorVisible(true); CSI(); writeToTerminal((byte)'?', (byte)'1', (byte)'0', (byte)'4', (byte)'9', (byte)'l'); } } /** * Enables or disables keyboard echo, meaning the immediate output of the * characters you type on your keyboard. If your users are going to interact * with this application through the keyboard, you probably want to disable * echo mode. * @param echoOn true if keyboard input will immediately echo, false if it's hidden * @throws LanternaException */ public abstract void setEcho(boolean echoOn); /** * Enabling cbreak mode will allow you to read user input immediately as the * user enters the characters, as opposed to reading the data in lines as * the user presses enter. If you want your program to respond to user input * by the keyboard, you probably want to enable cbreak mode. * @see <a href="http://en.wikipedia.org/wiki/POSIX_terminal_interface">POSIX terminal interface</a> * @param cbreakOn * @throws LanternaException */ public abstract void setCBreak(boolean cbreakOn); @Override public void moveCursor(int x, int y) { synchronized(writerMutex) { CSI(); writeToTerminal(((y+1) + "").getBytes()); writeToTerminal((byte)';'); writeToTerminal(((x+1) + "").getBytes()); writeToTerminal((byte)'H'); } } @Override public void setCursorVisible(boolean visible) { synchronized(writerMutex) { CSI(); writeToTerminal((byte)'?'); writeToTerminal((byte)'2'); writeToTerminal((byte)'5'); if(visible) writeToTerminal((byte)'h'); else writeToTerminal((byte)'l'); } } /** * Synchronize with writerMutex externally!!! */ protected void reportPosition() { CSI(); writeToTerminal("6n".getBytes()); } /** * Synchronize with writerMutex externally!!! */ protected void restoreCursorPosition() { CSI(); writeToTerminal("u".getBytes()); } /** * Synchronize with writerMutex externally!!! */ protected void saveCursorPosition() { CSI(); writeToTerminal("s".getBytes()); } }