001 /** 002 * jline - Java console input library 003 * Copyright (c) 2002,2003 Marc Prud'hommeaux mwp1@cornell.edu 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library; if not, write to the Free Software 017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018 */ 019 package jline; 020 021 import java.io.*; 022 import java.util.*; 023 024 // TODO: handle arrow keys, which might require completely implementing the 025 // console input reading in the .dll. For example, see: 026 // http://cvs.sourceforge.net/viewcvs.py/lifelines/lifelines/ 027 // win32/mycurses.c?rev=1.28 028 029 /** 030 * <p> 031 * Terminal implementation for Microsoft Windows. Terminal initialization 032 * in {@link #initializeTerminal} is accomplished by extracting the 033 * <em>jline_<i>version</i>.dll</em>, saving it to the system temporary 034 * directoy (determined by the setting of the <em>java.io.tmpdir</em> 035 * System property), loading the library, and then calling the Win32 APIs 036 * <a href="http://msdn.microsoft.com/library/default.asp? 037 * url=/library/en-us/dllproc/base/setconsolemode.asp">SetConsoleMode</a> 038 * and 039 * <a href="http://msdn.microsoft.com/library/default.asp? 040 * url=/library/en-us/dllproc/base/getconsolemode.asp">GetConsoleMode</a> 041 * to disable character echoing. 042 * </p> 043 * 044 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> 045 */ 046 public class WindowsTerminal 047 extends Terminal 048 { 049 // constants copied from wincon.h 050 051 /** 052 * The ReadFile or ReadConsole function returns only when 053 * a carriage return character is read. If this mode is disable, 054 * the functions return when one or more characters are 055 * available. 056 */ 057 private static final int ENABLE_LINE_INPUT = 2; 058 059 060 /** 061 * Characters read by the ReadFile or ReadConsole function 062 * are written to the active screen buffer as they are read. 063 * This mode can be used only if the ENABLE_LINE_INPUT mode 064 * is also enabled. 065 */ 066 private static final int ENABLE_ECHO_INPUT = 4; 067 068 069 /** 070 * CTRL+C is processed by the system and is not placed 071 * in the input buffer. If the input buffer is being read 072 * by ReadFile or ReadConsole, other control keys are processed 073 * by the system and are not returned in the ReadFile or ReadConsole 074 * buffer. If the ENABLE_LINE_INPUT mode is also enabled, 075 * backspace, carriage return, and linefeed characters are 076 * handled by the system. 077 */ 078 private static final int ENABLE_PROCESSED_INPUT = 1; 079 080 081 /** 082 * User interactions that change the size of the console 083 * screen buffer are reported in the console's input buffee. 084 * Information about these events can be read from the input 085 * buffer by applications using theReadConsoleInput function, 086 * but not by those using ReadFile orReadConsole. 087 */ 088 private static final int ENABLE_WINDOW_INPUT = 8; 089 090 091 /** 092 * If the mouse pointer is within the borders of the console 093 * window and the window has the keyboard focus, mouse events 094 * generated by mouse movement and button presses are placed 095 * in the input buffer. These events are discarded by ReadFile 096 * or ReadConsole, even when this mode is enabled. 097 */ 098 private static final int ENABLE_MOUSE_INPUT = 16; 099 100 101 /** 102 * When enabled, text entered in a console window will 103 * be inserted at the current cursor location and all text 104 * following that location will not be overwritten. When disabled, 105 * all following text will be overwritten. An OR operation 106 * must be performed with this flag and the ENABLE_EXTENDED_FLAGS 107 * flag to enable this functionality. 108 */ 109 private static final int ENABLE_PROCESSED_OUTPUT = 1; 110 111 112 /** 113 * This flag enables the user to use the mouse to select 114 * and edit text. To enable this option, use the OR to combine 115 * this flag with ENABLE_EXTENDED_FLAGS. 116 */ 117 private static final int ENABLE_WRAP_AT_EOL_OUTPUT = 2; 118 119 120 121 private native int getConsoleMode (); 122 123 private native void setConsoleMode (final int mode); 124 125 126 public void initializeTerminal () 127 throws Exception 128 { 129 loadLibrary ("jline"); 130 131 final int originalMode = getConsoleMode (); 132 133 setConsoleMode (originalMode & ~ENABLE_ECHO_INPUT); 134 135 // set the console to raw mode 136 int newMode = originalMode 137 & ~(ENABLE_LINE_INPUT 138 | ENABLE_ECHO_INPUT 139 | ENABLE_PROCESSED_INPUT 140 | ENABLE_WINDOW_INPUT); 141 setConsoleMode (newMode); 142 143 // at exit, restore the original tty configuration (for JDK 1.3+) 144 try 145 { 146 Runtime.getRuntime ().addShutdownHook (new Thread () 147 { 148 public void start () 149 { 150 // restore the old console mode 151 setConsoleMode (originalMode); 152 } 153 }); 154 } 155 catch (AbstractMethodError ame) 156 { 157 // JDK 1.3+ only method. Bummer. 158 consumeException (ame); 159 } 160 } 161 162 163 private void loadLibrary (final String name) 164 throws IOException 165 { 166 // store the DLL in the temporary directory for the System 167 String version = getClass ().getPackage ().getImplementationVersion (); 168 if (version == null) 169 version = ""; 170 version = version.replace ('.', '_'); 171 172 File f = new File (System.getProperty ("java.io.tmpdir"), 173 name + "_" + version + ".dll"); 174 boolean exists = f.isFile (); // check if it already exists 175 176 // extract the embedded jline.dll file from the jar and save 177 // it to the current directory 178 InputStream in = new BufferedInputStream (getClass () 179 .getResourceAsStream (name + ".dll")); 180 181 try 182 { 183 OutputStream fout = new BufferedOutputStream ( 184 new FileOutputStream (f)); 185 byte[] bytes = new byte [1024 * 10]; 186 for (int n = 0; n != -1; n = in.read (bytes)) 187 fout.write (bytes, 0, n); 188 189 fout.close (); 190 } 191 catch (IOException ioe) 192 { 193 // We might get an IOException trying to overwrite an existing 194 // jline.dll file if there is another process using the DLL. 195 // If this happens, ignore errors. 196 if (!exists) 197 throw ioe; 198 } 199 200 // try to clean up the DLL after the JVM exits 201 f.deleteOnExit (); 202 203 // now actually load the DLL 204 System.load (f.getAbsolutePath ()); 205 } 206 207 208 public boolean isSupported () 209 { 210 return true; 211 } 212 213 214 public boolean getEcho () 215 { 216 return false; 217 } 218 219 220 /** 221 * Unsupported; return the default. 222 * 223 * @see Terminal#getTerminalWidth 224 */ 225 public int getTerminalWidth () 226 { 227 return 80; 228 } 229 230 231 /** 232 * Unsupported; return the default. 233 * 234 * @see Terminal#getTerminalHeight 235 */ 236 public int getTerminalHeight () 237 { 238 return 24; 239 } 240 241 242 /** 243 * No-op for exceptions we want to silently consume. 244 */ 245 private void consumeException (final Throwable e) 246 { 247 } 248 } 249