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