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