/* This file is part of "MidpSSH". * Copyright (c) 2005 Karl von Randow. * * --LICENSE NOTICE-- * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * --LICENSE NOTICE-- * */ package terminal; import gui.Activatable; import gui.MainMenu; import gui.session.SpecialMenu; import java.io.InputStream; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Font; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Image; import javax.microedition.lcdui.TextBox; import javax.microedition.lcdui.TextField; import app.Settings; import app.session.Session; /** * Class that acts as terminal. It can basicly draw input from emulation (see * variable "buffer"), execute and store actions defined by user. */ public class Terminal extends Canvas implements Activatable, CommandListener { private static final int MODE_DISCONNECTED = 0; private static final int MODE_CONNECTED = 1; //#ifndef nocursororscroll private static final int MODE_CURSOR = 2; private static final int MODE_SCROLL = 3; //#endif //#ifndef notyping private static final int MODE_TYPING = 4; //#endif private static int commandPriority = 1; private static final Command textEnterCommand = new Command("Enter", Command.OK, commandPriority++); private static final Command textTypeCommand = new Command("Type", Command.OK, commandPriority++); // Have this separate back command as a Command.ITEM so that it will show first in the menu on // the phone, so that you know you're in typing mode private static final Command backMainCommand = new Command( "Back", Command.ITEM, commandPriority++ ); private static final Command textInputCommand = new Command( "Input", Command.ITEM, commandPriority++ ); private static final Command typeCommand = new Command( "Type", Command.ITEM, commandPriority++ ); //#ifndef nomacros private static final Command macrosCommand = new Command( "Macros", Command.ITEM, commandPriority++ ); //#endif private static final Command tabCommand = new Command( "TAB", Command.ITEM, commandPriority++ ); private static final Command spaceCommand = new Command( "SPACE", Command.ITEM, commandPriority++ ); private static final Command enterCommand = new Command( "ENTER", Command.ITEM, commandPriority++ ); private static final Command escCommand = new Command( "ESC", Command.ITEM, commandPriority++ ); //private static final Command backspaceCommand = new Command( "BACKSPACE", Command.ITEM, commandPriority++ ); private static final Command ctrlCommand = new Command( "CTRL", Command.ITEM, commandPriority++ ); private static final Command altCommand = new Command( "ALT", Command.ITEM, commandPriority++ ); private static final Command shiftCommand = new Command( "SHIFT", Command.ITEM, commandPriority++ ); //#ifndef nospecialmenu private static final Command specialCommand = new Command( "Special", Command.ITEM, commandPriority++ ); //#endif //#ifndef nocursororscroll private static final Command cursorCommand = new Command( "Cursor", Command.ITEM, commandPriority++ ); private static final Command scrollCommand = new Command( "Scroll", Command.ITEM, commandPriority++ ); //#endif private static final Command backCommand = new Command( "Back", Command.BACK, commandPriority++ ); //#ifndef noinstructions private static final Command showBindingsCommand = new Command( "Show Key Bindings", Command.ITEM, commandPriority++ ); //#endif //private static final Command settingsCommand = new Command( "Settings", Command.ITEM, commandPriority++ ); private static final Command disconnectCommand = new Command( "Disconnect", Command.ITEM, commandPriority++ ); private static final Command closeCommand = new Command( "Close", Command.STOP, commandPriority++ ); private static final Command[] commandsDisconnected = new Command[] { closeCommand }; private static final Command[] commandsConnected = new Command[] { textInputCommand, //#ifndef notyping typeCommand, //#endif //#ifndef nomacros macrosCommand, //#endif tabCommand, spaceCommand, enterCommand, escCommand, //backspaceCommand, ctrlCommand, altCommand, shiftCommand, //#ifndef nospecialmenu specialCommand, //#endif //#ifndef nocursororscroll cursorCommand, scrollCommand, //#endif //#ifndef noinstructions showBindingsCommand, //#endif disconnectCommand }; //#ifndef nocursororscroll private static final Command[] commandsCursor = new Command[] { backCommand }; //#endif //#ifndef notyping private static final Command[] commandsTyping = new Command[] { backMainCommand, backCommand, textInputCommand, //#ifndef nomacros macrosCommand, //#endif tabCommand, spaceCommand, enterCommand, escCommand, //backspaceCommand, ctrlCommand, altCommand, shiftCommand, //#ifndef nospecialmenu specialCommand, //#endif //#ifndef nocursororscroll cursorCommand, scrollCommand, //#endif disconnectCommand }; //#endif private static int [] bindingKeys = new int[] { Canvas.KEY_NUM1, Canvas.KEY_NUM2, Canvas.KEY_NUM3, Canvas.KEY_NUM4, Canvas.KEY_NUM5, Canvas.KEY_NUM6, Canvas.KEY_NUM7, Canvas.KEY_NUM8, Canvas.KEY_NUM9, Canvas.KEY_STAR, Canvas.KEY_NUM0, Canvas.KEY_POUND }; private Session session; private TextBox inputDialog; //#ifndef nospecialmenu private SpecialMenu menuSpecialKeys; //#endif private TextBox controlKeyDialog, altKeyDialog, shiftKeyDialog; private Command[] currentCommands; private int mode; /** * @param buffer */ public Terminal( VT320 buffer, Session session ) { this.buffer = buffer; buffer.setDisplay( this ); if ( MainMenu.useColors ) { fgcolor = color[7]; bgcolor = color[0]; } //#ifdef midp2 rotated = Settings.terminalRotated; //#else rotated = Settings.ROT_NORMAL; //#endif initFont(); //#ifdef midp2 /* Full screen mode moved below initFont() to avoid hang on Siemens phones, thanks DarkBear */ if ( Settings.terminalFullscreen ) { setFullScreenMode( true ); } //#endif top = 0; left = 0; this.session = session; changeMode( MODE_DISCONNECTED ); setCommandListener( this ); // Settings if ( MainMenu.useColors ) { bgcolor = Settings.bgcolor; /* If specified fgcolor is white then use default fgcolor, which is our off white */ if (Settings.fgcolor != 0xffffff) { fgcolor = Settings.fgcolor; } } sizeChanged(); } //#ifdef midp2 /* (non-Javadoc) * @see javax.microedition.lcdui.Displayable#sizeChanged(int, int) */ protected void sizeChanged(int w, int h) { super.sizeChanged(w, h); sizeChanged(); } //#endif protected void sizeChanged() { width = getWidth(); height = getHeight(); if ( rotated != Settings.ROT_NORMAL ) { width = getHeight(); height = getWidth(); } cols = width / fontWidth; rows = height / fontHeight; backingStore = Image.createImage( width, height ); int virtualCols = cols; int virtualRows = rows; if ( Settings.terminalCols != 0 ) { virtualCols = Settings.terminalCols; } if ( Settings.terminalRows != 0 ) { virtualRows = Settings.terminalRows; } //System.out.println( "ROWS " + virtualRows + " COLS " + virtualCols ); buffer.setScreenSize( virtualCols, virtualRows ); } public void connected() { changeMode( MODE_CONNECTED ); } public void disconnected() { changeMode( MODE_DISCONNECTED ); } protected void changeMode( int mode ) { this.mode = mode; switch ( mode ) { case MODE_DISCONNECTED: changeCurrentCommands( commandsDisconnected ); break; case MODE_CONNECTED: changeCurrentCommands( commandsConnected ); break; //#ifndef nocursororscroll case MODE_CURSOR: case MODE_SCROLL: changeCurrentCommands( commandsCursor ); break; //#endif //#ifndef notyping case MODE_TYPING: changeCurrentCommands( commandsTyping ); break; //#endif } } protected void changeCurrentCommands( Command[] commands ) { if ( currentCommands != null ) { for ( int i = 0; i < currentCommands.length; i++ ) { removeCommand( currentCommands[i] ); } } for ( int i = 0; i < commands.length; i++ ) { addCommand( commands[i] ); } this.currentCommands = commands; } /* * (non-Javadoc) * * @see gui.Activatable#activate() */ public void activate() { MainMenu.setDisplay( this ); } public void activate( Activatable back ) { activate(); } public void commandAction( Command command, Displayable displayable ) { if (displayable == inputDialog) { if (command != backCommand) { commandBuffer.setLength( 0 ); commandBuffer.append(inputDialog.getString()); if ( command == textEnterCommand ) { commandBuffer.append( '\n' ); } if ( command == tabCommand ) { commandBuffer.append( '\t' ); } session.typeString( commandBuffer.toString() ); inputDialog.setString( "" ); } activate(); } else if (displayable == controlKeyDialog) { handleModifierDialog(command, controlKeyDialog, VT320.KEY_CONTROL); } else if (displayable == altKeyDialog) { handleModifierDialog(command, altKeyDialog, VT320.KEY_ALT); } else if (displayable == shiftKeyDialog) { handleModifierDialog(command, shiftKeyDialog, VT320.KEY_SHIFT); } else if (displayable != this) { /* A message form or something, just come back to this screen */ activate(); } else if ( command == disconnectCommand || command == closeCommand ) { doDisconnect(); } else if ( command == textInputCommand ) { doTextInput(null); } //#ifndef nomacros else if ( command == macrosCommand ) { MainMenu.doMacros(this); } //#endif else if ( command == tabCommand ) { buffer.keyTyped( 0, '\t', 0 ); } else if ( command == spaceCommand ) { buffer.keyTyped( 0, ' ', 0 ); } else if ( command == enterCommand ) { buffer.keyTyped( 0, '\n', 0 ); } else if ( command == escCommand ) { buffer.keyTyped( 0, (char) 27, 0 ); } /*else if ( command == backspaceCommand ) { buffer.keyPressed( VT320.VK_BACK_SPACE, 0 ); }*/ else if ( command == ctrlCommand ) { doControlKeyInput(); } else if ( command == altCommand ) { doAltKeyInput(); } else if ( command == shiftCommand ) { doShiftKeyInput(); } //#ifndef nocursororscroll else if ( command == cursorCommand ) { doCursor(); } else if ( command == scrollCommand ) { doScroll(); } //#endif //#ifndef notyping else if ( command == typeCommand ) { doTyping(); } //#endif //#ifndef nospecialmenu else if ( command == specialCommand ) { if ( menuSpecialKeys == null ) { menuSpecialKeys = new SpecialMenu(); } menuSpecialKeys.activate( this ); } //#endif else if ( command == backCommand || command == backMainCommand ) { changeMode( MODE_CONNECTED ); } //#ifndef noinstructions else if ( command == showBindingsCommand ) { doShowBindings(); } //#endif } private StringBuffer commandBuffer = new StringBuffer(); private void handleModifierDialog(Command command, TextBox box, int modifier) { if (command != backCommand) { String str = box.getString(); for ( int i = 0; i < str.length(); i++ ) { session.typeChar( str.charAt( i ), modifier ); } box.setString(""); } activate(); } protected void keyPressed( int keycode ) { switch ( mode ) { case MODE_CONNECTED: keyPressedConnected( keycode ); break; //#ifndef nocursororscroll case MODE_CURSOR: keyPressedCursor( keycode ); break; case MODE_SCROLL: keyPressedScroll( keycode ); break; //#endif //#ifndef notyping case MODE_TYPING: keyPressedTyping( keycode ); break; //#endif } } protected void keyReleased( int keycode ) { switch ( mode ) { case MODE_CONNECTED: keyReleasedConnected( keycode ); break; //#ifndef notyping case MODE_TYPING: keyReleasedTyping( keycode ); break; //#endif } } //#ifndef nocursororscroll protected void keyRepeated( int keycode ) { switch ( mode ) { case MODE_CURSOR: keyPressedCursor( keycode ); break; case MODE_SCROLL: keyPressedScroll( keycode ); break; } } //#endif protected boolean handleGameAction( int keycode ) { int gameAction = getGameAction( keycode ); if ( gameAction != 0 ) { switch ( gameAction ) { case Canvas.UP: buffer.keyPressed( VT320.VK_UP, VT320.KEY_ACTION ); return true; case Canvas.DOWN: buffer.keyPressed( VT320.VK_DOWN, VT320.KEY_ACTION ); return true; case Canvas.LEFT: buffer.keyPressed( VT320.VK_LEFT, VT320.KEY_ACTION ); return true; case Canvas.RIGHT: buffer.keyPressed( VT320.VK_RIGHT, VT320.KEY_ACTION ); return true; } } return false; } protected void keyPressedConnected( int keycode ) { // If a game action is used, allow it to operate the cursor even when not in cursor mode boolean handled = false; for ( int i = 0; i < bindingKeys.length; i++ ) { if ( bindingKeys[i] == keycode ) { handled = true; } } if ( keycode == KEY_BACKSPACE || keycode == 13 ) { handled = true; } if ( !handled ) { if ( handleGameAction( keycode ) ) return; } } protected void keyReleasedConnected( int keycode ) { int index = -1; for ( int i = 0; i < bindingKeys.length; i++ ) { if ( bindingKeys[i] == keycode ) { index = i; break; } } if ( index >= 0 && index < commandsConnected.length ) { commandAction( commandsConnected[index], this ); } else { if ( keycode == KEY_BACKSPACE ) { // Backspace buffer.keyPressed( VT320.VK_BACK_SPACE, 0 ); } else if (keycode == 13) { // Enter buffer.keyTyped( 0, '\n', 0 ); } } } private static final int KEY_BACKSPACE = -8; // Keycode for clear on sony //#ifndef notyping private static final int KEY_SHIFT = 137; // Keycode for shift on blackberry private boolean typingShift; protected void keyPressedTyping( int keycode ) { if ( keycode == KEY_SHIFT ) { typingShift = true; } // If a game action is used, allow it to operate the cursor even when not in cursor mode // But need to make sure it's not a character that we might accept for typing if ( keycode == 8 || keycode == KEY_BACKSPACE || keycode == 10 || keycode == 13 || keycode == KEY_SHIFT || ( keycode >= 32 && keycode < 128 ) ) { // NOOP in keyPressedTyping } else { if ( handleGameAction( keycode ) ) return; } } protected void keyReleasedTyping( int keycode ) { /* Debug typing */ // buffer.putString("KEY" + keycode + " "); if ( keycode == 8 || keycode == KEY_BACKSPACE ) { // Backspace buffer.keyPressed( VT320.VK_BACK_SPACE, 0 ); } else if ( keycode == 10 || keycode == 13 ) { //buffer.keyTyped( keycode, (char) keycode, 0 ); buffer.keyTyped( 0, '\n', 0 ); } else if ( keycode == KEY_SHIFT ) { typingShift = false; } else if ( keycode > 0 && keycode < 32) { buffer.keyTyped( keycode, (char)keycode, 0); } else if ( keycode >= 32 && keycode < 128 ) { char c = (char) keycode; if ( typingShift ) { c = shiftChar( c ); } // Don't pass through the keycode, as we don't want the terminal to do any keycode mapping // we just care about the char buffer.keyTyped( 0, c, 0 ); } } private char shiftChar( char c ) { if ( c >= 'a' && c <= 'z' ) { return (char) ( c - 'a' + 'A' ); } else { switch ( c ) { case '0': return ')'; case '1': return '!'; case '2': return '@'; case '3': return '#'; case '4': return '$'; case '5': return '%'; case '6': return '^'; case '7': return '&'; case '8': return '*'; case '9': return '('; default: return c; } } } //#endif //#ifndef nocursororscroll private int gameKeysToNumeric( int keycode ) { // Convert game actions to keys int gameAction = getGameAction( keycode ); switch ( gameAction ) { case Canvas.UP: keycode = Canvas.KEY_NUM2; break; case Canvas.DOWN: keycode = Canvas.KEY_NUM8; break; case Canvas.LEFT: keycode = Canvas.KEY_NUM4; break; case Canvas.RIGHT: keycode = Canvas.KEY_NUM6; break; } return keycode; } protected void keyPressedCursor( int keycode ) { keycode = gameKeysToNumeric( keycode ); switch ( keycode ) { case Canvas.KEY_NUM2: buffer.keyPressed( VT320.VK_UP, VT320.KEY_ACTION ); break; case Canvas.KEY_NUM8: case Canvas.KEY_NUM0: buffer.keyPressed( VT320.VK_DOWN, VT320.KEY_ACTION ); break; case Canvas.KEY_NUM4: buffer.keyPressed( VT320.VK_LEFT, VT320.KEY_ACTION ); break; case Canvas.KEY_NUM6: buffer.keyPressed( VT320.VK_RIGHT, VT320.KEY_ACTION ); break; case Canvas.KEY_NUM1: keyPressedCursor( Canvas.UP ); keyPressedCursor( Canvas.LEFT ); break; case Canvas.KEY_NUM3: keyPressedCursor( Canvas.UP ); keyPressedCursor( Canvas.RIGHT ); break; case Canvas.KEY_NUM7: case Canvas.KEY_STAR: keyPressedCursor( Canvas.DOWN ); keyPressedCursor( Canvas.LEFT ); break; case Canvas.KEY_NUM9: case Canvas.KEY_POUND: keyPressedCursor( Canvas.DOWN ); keyPressedCursor( Canvas.RIGHT ); break; } } protected void keyPressedScroll( int keycode ) { keycode = gameKeysToNumeric( keycode ); switch ( keycode ) { case Canvas.KEY_NUM2: if ( top > 0 ) { top--; } redraw(); break; case Canvas.KEY_NUM8: case Canvas.KEY_NUM0: if ( top + rows < buffer.height ) { top++; } redraw(); break; case Canvas.KEY_NUM4: if ( left > 0 ) { left--; } redraw(); break; case Canvas.KEY_NUM6: if ( left + cols < buffer.width ) { left++; } redraw(); break; case Canvas.KEY_NUM1: keyPressedScroll( Canvas.UP ); keyPressedScroll( Canvas.LEFT ); break; case Canvas.KEY_NUM3: keyPressedScroll( Canvas.UP ); keyPressedScroll( Canvas.RIGHT ); break; case Canvas.KEY_NUM7: case Canvas.KEY_STAR: keyPressedScroll( Canvas.DOWN ); keyPressedScroll( Canvas.LEFT ); break; case Canvas.KEY_NUM9: case Canvas.KEY_POUND: keyPressedScroll( Canvas.DOWN ); keyPressedScroll( Canvas.RIGHT ); break; } } //#endif private void doDisconnect() { session.disconnect(); session.goMainMenu(); } public void doTextInput(String text) { if ( inputDialog == null ) { inputDialog = new TextBox( "Input", "", 255, TextField.ANY ); inputDialog.addCommand( textEnterCommand ); inputDialog.addCommand( typeCommand ); inputDialog.addCommand( tabCommand ); inputDialog.addCommand( backCommand ); //#ifdef midp2 if (!Settings.predictiveText) { inputDialog.setConstraints(TextField.ANY | TextField.NON_PREDICTIVE); } //#endif inputDialog.setCommandListener(this); } if (text != null) { inputDialog.setString(text); } MainMenu.setDisplay(inputDialog); } private TextBox makeModifierInputDialog(String title) { TextBox box = new TextBox( title, "", 10, TextField.ANY ); box.addCommand(textTypeCommand); box.addCommand(backCommand); //#ifdef midp2 if (!Settings.predictiveText) { box.setConstraints(TextField.ANY | TextField.NON_PREDICTIVE); } //#endif box.setCommandListener(this); return box; } private void doControlKeyInput() { if ( controlKeyDialog == null ) { controlKeyDialog = makeModifierInputDialog("CTRL"); //new ModifierInputDialog( "Control Keys", VT320.KEY_CONTROL ); } MainMenu.setDisplay(controlKeyDialog); } private void doAltKeyInput() { if ( altKeyDialog == null ) { altKeyDialog = makeModifierInputDialog("ALT"); //new ModifierInputDialog( "Alt Keys", VT320.KEY_ALT ); } MainMenu.setDisplay(altKeyDialog); } private void doShiftKeyInput() { if ( shiftKeyDialog == null ) { shiftKeyDialog = makeModifierInputDialog("SHIFT"); //new ModifierInputDialog( "Shift Keys", VT320.KEY_SHIFT ); } MainMenu.setDisplay(shiftKeyDialog); } //#ifndef nocursororscroll public void doCursor() { changeMode( MODE_CURSOR ); } public void doScroll() { changeMode( MODE_SCROLL ); } //#endif //#ifndef notyping public void doTyping() { changeMode( MODE_TYPING ); } //#endif //#ifndef noinstructions private void doShowBindings() { StringBuffer str = new StringBuffer(); if ( currentCommands != null ) { for ( int i = 0; i < bindingKeys.length && i < currentCommands.length; i++ ) { int keycode = bindingKeys[i]; Command comm = currentCommands[i]; String keyName = getKeyName( keycode ); str.append( keyName ); str.append( ": " ); str.append( comm.getLabel() ); str.append( "\n" ); } } MainMenu.showMessage("Key Bindings", str.toString(), this); } //#endif /** the VDU buffer */ protected VT320 buffer; /** first top and left character in buffer, that is displayed */ protected int top, left; protected int width, height; private int fontWidth, fontHeight; protected int rotated; /** display size in characters */ public int rows, cols; private Image backingStore = null; public int fgcolor = 0x000000; public int bgcolor = 0xffffff; /** A list of colors used for representation of the display */ private static final int color[] = { // black, red, green, yellow 0x000000, 0xcc0000, 0x00cc00, 0xcccc00, // blue, magenta, cyan, white 0x0000cc, 0xcc00cc, 0x00cccc, 0xcccccc }; private static final int boldcolor[] = { // black, red, green, yellow 0x333333, 0xff0000, 0x00ff00, 0xffff00, // blue, magenta, cyan, white 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff }; private static final int lowcolor[] = { // black, red, green, yellow 0x000000, 0x990000, 0x009900, 0x999900, // blue, magenta, cyan, white 0x000099, 0x990099, 0x009999, 0x999999 }; //#ifndef nopaintsync private Object paintMutex = new Object(); //#endif protected void paint( Graphics g ) { // Erase display g.setColor( bgcolor ); g.fillRect( 0, 0, getWidth(), getHeight() ); // Draw terminal image //#ifndef nopaintsync synchronized ( paintMutex ) { //#endif // Redraw backing store if necessary redrawBackingStore(); /* * Note the y coord is offset by 1 because without the offset it sometimes fails to * draw on my SonyEricsson K700i. This is seen in width - 1 on the two rotated image widths * and the y coord of 1 in drawImage. */ Image image; switch ( rotated ) { //#ifdef midp2 case Settings.ROT_270: // g.drawRegion( backingStore, 0, 0, width - 1, height, javax.microedition.lcdui.game.Sprite.TRANS_ROT270, 0, 1, Graphics.TOP | Graphics.LEFT ); image = Image.createImage(backingStore, 0, 0, width - 1, height, javax.microedition.lcdui.game.Sprite.TRANS_ROT270); break; case Settings.ROT_90: // g.drawRegion( backingStore, 0, 0, width - 1, height, javax.microedition.lcdui.game.Sprite.TRANS_ROT90, 0, 1, Graphics.TOP | Graphics.LEFT ); image = Image.createImage(backingStore, 0, 0, width - 1, height, javax.microedition.lcdui.game.Sprite.TRANS_ROT90); break; //#endif default: image = backingStore; break; } g.drawImage( image, 0, 1, Graphics.TOP | Graphics.LEFT ); //#ifndef nopaintsync } //#endif } private boolean invalid = true; public void redraw() { //#ifndef nopaintsync synchronized ( paintMutex ) { //#endif invalid = true; repaint(); //#ifndef nopaintsync } //#endif } protected void redrawBackingStore() { // Only redraw if we've been marked as invalid by a call to redraw // The idea is that if multiple calls to redraw occur before the call to // paint then we save // time not redrawing our backingStore each time if ( invalid ) { //long st = System.currentTimeMillis(); Graphics g = backingStore.getGraphics(); g.setColor( bgcolor ); g.fillRect( 0, 0, width, height ); for ( int l = top; l < buffer.height && l < ( top + rows ); l++ ) { if ( !buffer.update[0] && !buffer.update[l + 1] ) { continue; } buffer.update[l + 1] = false; for ( int c = left; c < buffer.width && c < ( left + cols ); c++ ) { int addr = 0; int currAttr = buffer.charAttributes[buffer.windowBase + l][c]; int fg = fgcolor; int bg = bgcolor; if (MainMenu.useColors) { int fgcolorindex = ( ( currAttr & VT320.COLOR_FG ) >> 4 ) - 1; if ( fgcolorindex >= 0 && fgcolorindex < 8 ) { /* Colour index 8 is invalid, 9 means use default */ if ( (currAttr & VT320.BOLD) != 0) { fg = boldcolor[fgcolorindex]; } else if (( currAttr & VT320.LOW ) != 0) { fg = lowcolor[fgcolorindex]; } else { fg = color[fgcolorindex]; } } int bgcolorindex = ( ( currAttr & VT320.COLOR_BG ) >> 8 ) - 1; if ( bgcolorindex >= 0 && bgcolorindex < 8) { /* Colour index 8 is invalid, 9 means use default */ bg = color[bgcolorindex]; } if ( ( currAttr & VT320.INVERT ) != 0 ) { int swapc = bg; bg = fg; fg = swapc; } } // determine the maximum of characters we can print in one // go while ( ( c + addr < buffer.width ) && ( ( buffer.charArray[buffer.windowBase + l][c + addr] < ' ' ) || ( buffer.charAttributes[buffer.windowBase + l][c + addr] == currAttr ) ) ) { if ( buffer.charArray[buffer.windowBase + l][c + addr] < ' ' ) { buffer.charArray[buffer.windowBase + l][c + addr] = ' '; buffer.charAttributes[buffer.windowBase + l][c + addr] = 0; continue; } addr++; } // clear the part of the screen we want to change (fill // rectangle) g.setColor( bg ); g.fillRect( ( c - left ) * fontWidth, ( l - top ) * fontHeight, addr * fontWidth, fontHeight ); g.setColor( fg ); // draw the characters drawChars( g, fg, bg, buffer.charArray[buffer.windowBase + l], c, addr, ( c - left ) * fontWidth, ( l - top ) * fontHeight ); c += addr - 1; } } // draw cursor if ( buffer.showcursor && ( buffer.screenBase + buffer.cursorY >= buffer.windowBase && buffer.screenBase + buffer.cursorY < buffer.windowBase + buffer.height ) ) { g.setColor( fgcolor ); g.fillRect( ( buffer.cursorX - left ) * fontWidth, ( buffer.cursorY - top + buffer.screenBase - buffer.windowBase ) * fontHeight, fontWidth, fontHeight ); } invalid = false; //System.out.println("REDRAW " + (System.currentTimeMillis() - st)); } } //#ifndef nofonts private LCDFont lcdfont; private int prevfg = -1, prevbg = -1; //#endif private void initFont() { //#ifdef nofonts initInternalFont(); //#else //#ifdef midp2 String lcdFontFile = null; //#endif switch ( fontMode ) { case Settings.FONT_NORMAL: initInternalFont(); break; case Settings.FONT_DEVICE: initSystemFont( Font.SIZE_SMALL ); break; //#ifdef midp2 case 2: lcdFontFile = "/font3x6lcd.png"; break; case 3: lcdFontFile = "/font4x6lcd.png"; break; case 4: lcdFontFile = "/font4x7lcd.png"; break; case 5: lcdFontFile = "/font5x9lcd.png"; break; case 6: lcdFontFile = "/font8x13lcd.png"; break; //#endif } //#ifdef midp2 if (lcdFontFile != null) { boolean BGR = Settings.lcdFontMode != 0; if (rotated != Settings.ROT_NORMAL) { lcdFontFile = "/font4x6lcdV.png"; if (rotated == Settings.ROT_270) { BGR = !BGR; } } lcdfont = new LCDFont(lcdFontFile, BGR); fontWidth = lcdfont.fontWidth; fontHeight = lcdfont.fontHeight; } //#endif //#endif } private void initInternalFont() { fontWidth = 4; fontHeight = 6; fontData = new int[128][]; try { InputStream in = getClass().getResourceAsStream( FONT_RESOURCE ); for ( int i = 33; i < 128; i++ ) { int b = in.read(); int l = ( b & 3 ) + 2; // length could be 1,2,3 or 4; this is // len+1 fontData[i] = new int[l]; // one more for template fontData[i][0] = ( b >> 2 ) - 32; // draw this template // System.out.println("--- ascii "+i +"---" ); // System.out.println("header "+b ); // System.out.println("len "+(l-1) ); // System.out.println("template "+ data[i][0] ); for ( int j = 1; j < l; j++ ) { fontData[i][j] = in.read(); // System.out.println("data["+j+"]" + data[i][j] ); } } in.close(); } catch ( Exception e ) { //e.printStackTrace(); } } private void initSystemFont( int size ) { font = Font.getFont( Font.FACE_MONOSPACE, Font.STYLE_PLAIN, size ); fontHeight = font.getHeight(); fontWidth = font.charWidth( 'W' ); } protected void drawChars( Graphics g, int fg, int bg, char[] chars, int offset, int length, int x, int y ) { //#ifndef nofonts if ( fontMode == Settings.FONT_NORMAL ) { //#endif for ( int i = offset; i < offset + length; i++ ) { drawChar( g, chars[i], x, y ); x += fontWidth; } //#ifndef nofonts } else if (fontMode == Settings.FONT_DEVICE) { g.setFont( font ); for ( int i = offset; i < offset + length; i++ ) { g.drawChar( chars[i], x, y, Graphics.TOP|Graphics.LEFT); x += fontWidth; } } //#ifdef midp2 else { /* Change colour */ if (fg != prevfg || bg != prevbg) { lcdfont.setColor(fg, bg); prevfg = fg; prevbg = bg; } /* Draw chars */ for ( int i = offset; i < offset + length; i++ ) { lcdfont.drawChar( g, chars[i], x, y ); x += fontWidth; } } //#endif //#endif } private void drawChar( Graphics g, char c, int x, int y ) { if ( c >= fontData.length || fontData[c] == null ) return; for ( int j = 1; j < fontData[c].length; j++ ) { int x1 = fontData[c][j] & 3; int y1 = ( fontData[c][j] & 12 ) >> 2; int x2 = ( fontData[c][j] & 48 ) >> 4; int y2 = ( fontData[c][j] & 192 ) >> 6; if ( x1 == 3 ) { x1 = y1; y1 = 4; } if ( x2 == 3 ) { x2 = y2; y2 = 4; } // System.out.println( "char " + c + " x1=" + x1 + " y1="+ (4-y1) +" // x2=" + x2 + " y2="+ (4-y2)); g.drawLine( x + x1, y + y1, x + x2, y + y2 ); } if ( fontData[c][0] != 0 ) drawChar( g, (char) ( c + fontData[c][0] ), x, y ); // draw template } private int fontMode = Settings.fontMode; private Font font; private int[][] fontData; private static final String FONT_RESOURCE = "/font"; }