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