src/jdk.internal.le/share/classes/jdk/internal/jline/WindowsTerminal.java
changeset 52974 ddbd9744a3d5
parent 52973 a659ccd1888d
parent 52961 d67b37917e82
child 52975 35e2bbea78b2
child 53179 760293737af0
equal deleted inserted replaced
52973:a659ccd1888d 52974:ddbd9744a3d5
     1 /*
       
     2  * Copyright (c) 2002-2016, the original author or authors.
       
     3  *
       
     4  * This software is distributable under the BSD license. See the terms of the
       
     5  * BSD license in the documentation provided with this software.
       
     6  *
       
     7  * http://www.opensource.org/licenses/bsd-license.php
       
     8  */
       
     9 package jdk.internal.jline;
       
    10 
       
    11 import java.io.FileDescriptor;
       
    12 import java.io.FileInputStream;
       
    13 import java.io.IOException;
       
    14 import java.io.InputStream;
       
    15 import java.io.OutputStream;
       
    16 
       
    17 import jdk.internal.jline.extra.AnsiInterpretingOutputStream;
       
    18 import jdk.internal.jline.extra.AnsiInterpretingOutputStream.BufferState;
       
    19 import jdk.internal.jline.extra.AnsiInterpretingOutputStream.Performer;
       
    20 import jdk.internal.jline.internal.Configuration;
       
    21 import jdk.internal.jline.internal.Log;
       
    22 //import org.fusesource.jansi.internal.WindowsSupport;
       
    23 //import org.fusesource.jansi.internal.Kernel32;
       
    24 //import static org.fusesource.jansi.internal.Kernel32.*;
       
    25 
       
    26 import static jdk.internal.jline.WindowsTerminal.ConsoleMode.ENABLE_ECHO_INPUT;
       
    27 import static jdk.internal.jline.WindowsTerminal.ConsoleMode.ENABLE_LINE_INPUT;
       
    28 import static jdk.internal.jline.WindowsTerminal.ConsoleMode.ENABLE_PROCESSED_INPUT;
       
    29 import static jdk.internal.jline.WindowsTerminal.ConsoleMode.ENABLE_WINDOW_INPUT;
       
    30 
       
    31 /**
       
    32  * Terminal implementation for Microsoft Windows. Terminal initialization in
       
    33  * {@link #init} is accomplished by extracting the
       
    34  * <em>jline_<i>version</i>.dll</em>, saving it to the system temporary
       
    35  * directoy (determined by the setting of the <em>java.io.tmpdir</em> System
       
    36  * property), loading the library, and then calling the Win32 APIs <a
       
    37  * href="http://msdn.microsoft.com/library/default.asp?
       
    38  * url=/library/en-us/dllproc/base/setconsolemode.asp">SetConsoleMode</a> and
       
    39  * <a href="http://msdn.microsoft.com/library/default.asp?
       
    40  * url=/library/en-us/dllproc/base/getconsolemode.asp">GetConsoleMode</a> to
       
    41  * disable character echoing.
       
    42  * <p/>
       
    43  * <p>
       
    44  * By default, the {@link #wrapInIfNeeded(java.io.InputStream)} method will attempt
       
    45  * to test to see if the specified {@link InputStream} is {@link System#in} or a wrapper
       
    46  * around {@link FileDescriptor#in}, and if so, will bypass the character reading to
       
    47  * directly invoke the readc() method in the JNI library. This is so the class
       
    48  * can read special keys (like arrow keys) which are otherwise inaccessible via
       
    49  * the {@link System#in} stream. Using JNI reading can be bypassed by setting
       
    50  * the <code>jline.WindowsTerminal.directConsole</code> system property
       
    51  * to <code>false</code>.
       
    52  * </p>
       
    53  *
       
    54  * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
       
    55  * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
       
    56  * @since 2.0
       
    57  */
       
    58 public class WindowsTerminal
       
    59     extends TerminalSupport
       
    60 {
       
    61     public static final String DIRECT_CONSOLE = WindowsTerminal.class.getName() + ".directConsole";
       
    62 
       
    63     public static final String ANSI = WindowsTerminal.class.getName() + ".ansi";
       
    64 
       
    65     private boolean directConsole;
       
    66 
       
    67     private int originalMode;
       
    68 
       
    69     public WindowsTerminal() throws Exception {
       
    70         super(true);
       
    71     }
       
    72 
       
    73     @Override
       
    74     public void init() throws Exception {
       
    75         super.init();
       
    76 
       
    77 //        setAnsiSupported(Configuration.getBoolean(ANSI, true));
       
    78         setAnsiSupported(true);
       
    79 
       
    80         //
       
    81         // FIXME: Need a way to disable direct console and sysin detection muck
       
    82         //
       
    83 
       
    84         setDirectConsole(Configuration.getBoolean(DIRECT_CONSOLE, true));
       
    85 
       
    86         this.originalMode = getConsoleMode();
       
    87         setConsoleMode(originalMode & ~ENABLE_ECHO_INPUT.code);
       
    88         setEchoEnabled(false);
       
    89     }
       
    90 
       
    91     /**
       
    92      * Restore the original terminal configuration, which can be used when
       
    93      * shutting down the console reader. The ConsoleReader cannot be
       
    94      * used after calling this method.
       
    95      */
       
    96     @Override
       
    97     public void restore() throws Exception {
       
    98         // restore the old console mode
       
    99         setConsoleMode(originalMode);
       
   100         super.restore();
       
   101     }
       
   102 
       
   103     @Override
       
   104     public int getWidth() {
       
   105         int w = getWindowsTerminalWidth();
       
   106         return w < 1 ? DEFAULT_WIDTH : w;
       
   107     }
       
   108 
       
   109     @Override
       
   110     public int getHeight() {
       
   111         int h = getWindowsTerminalHeight();
       
   112         return h < 1 ? DEFAULT_HEIGHT : h;
       
   113     }
       
   114 
       
   115     @Override
       
   116     public void setEchoEnabled(final boolean enabled) {
       
   117         // Must set these four modes at the same time to make it work fine.
       
   118         if (enabled) {
       
   119             setConsoleMode(getConsoleMode() |
       
   120                 ENABLE_ECHO_INPUT.code |
       
   121                 ENABLE_LINE_INPUT.code |
       
   122                 ENABLE_WINDOW_INPUT.code);
       
   123         }
       
   124         else {
       
   125             setConsoleMode(getConsoleMode() &
       
   126                 ~(ENABLE_LINE_INPUT.code |
       
   127                     ENABLE_ECHO_INPUT.code |
       
   128                     ENABLE_WINDOW_INPUT.code));
       
   129         }
       
   130         super.setEchoEnabled(enabled);
       
   131     }
       
   132 
       
   133     public void disableInterruptCharacter() {
       
   134         setConsoleMode(getConsoleMode() &
       
   135             ~(ENABLE_PROCESSED_INPUT.code));
       
   136     }
       
   137 
       
   138     public void enableInterruptCharacter() {
       
   139         setConsoleMode(getConsoleMode() |
       
   140             ENABLE_PROCESSED_INPUT.code);
       
   141     }
       
   142 
       
   143     /**
       
   144      * Whether or not to allow the use of the JNI console interaction.
       
   145      */
       
   146     public void setDirectConsole(final boolean flag) {
       
   147         this.directConsole = flag;
       
   148         Log.debug("Direct console: ", flag);
       
   149     }
       
   150 
       
   151     /**
       
   152      * Whether or not to allow the use of the JNI console interaction.
       
   153      */
       
   154     public Boolean getDirectConsole() {
       
   155         return directConsole;
       
   156     }
       
   157 
       
   158 
       
   159     @Override
       
   160     public InputStream wrapInIfNeeded(InputStream in) throws IOException {
       
   161         if (directConsole && isSystemIn(in)) {
       
   162             return new InputStream() {
       
   163                 private byte[] buf = null;
       
   164                 int bufIdx = 0;
       
   165 
       
   166                 @Override
       
   167                 public int read() throws IOException {
       
   168                     while (buf == null || bufIdx == buf.length) {
       
   169                         buf = readConsoleInput();
       
   170                         bufIdx = 0;
       
   171                     }
       
   172                     int c = buf[bufIdx] & 0xFF;
       
   173                     bufIdx++;
       
   174                     return c;
       
   175                 }
       
   176             };
       
   177         } else {
       
   178             return super.wrapInIfNeeded(in);
       
   179         }
       
   180     }
       
   181 
       
   182     protected boolean isSystemIn(final InputStream in) throws IOException {
       
   183         if (in == null) {
       
   184             return false;
       
   185         }
       
   186         else if (in == System.in) {
       
   187             return true;
       
   188         }
       
   189         else if (in instanceof FileInputStream && ((FileInputStream) in).getFD() == FileDescriptor.in) {
       
   190             return true;
       
   191         }
       
   192 
       
   193         return false;
       
   194     }
       
   195 
       
   196     @Override
       
   197     public OutputStream wrapOutIfNeeded(OutputStream out) {
       
   198         return new AnsiInterpretingOutputStream(getOutputEncoding(), out, new Performer() {
       
   199             @Override
       
   200             public BufferState getBufferState() throws IOException {
       
   201                 out.flush();
       
   202                 return WindowsTerminal.this.getBufferState();
       
   203             }
       
   204             @Override
       
   205             public void setCursorPosition(int cursorX, int cursorY) throws IOException {
       
   206                 out.flush();
       
   207                 WindowsTerminal.this.setCursorPosition(cursorX, cursorY);
       
   208             }
       
   209         });
       
   210     }
       
   211 
       
   212     @Override
       
   213     public String getOutputEncoding() {
       
   214         int codepage = getConsoleOutputCodepage();
       
   215         //http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html
       
   216         String charsetMS = "ms" + codepage;
       
   217         if (java.nio.charset.Charset.isSupported(charsetMS)) {
       
   218             return charsetMS;
       
   219         }
       
   220         String charsetCP = "cp" + codepage;
       
   221         if (java.nio.charset.Charset.isSupported(charsetCP)) {
       
   222             return charsetCP;
       
   223         }
       
   224         Log.debug("can't figure out the Java Charset of this code page (" + codepage + ")...");
       
   225         return super.getOutputEncoding();
       
   226     }
       
   227 
       
   228     //
       
   229     // Original code:
       
   230     //
       
   231 //    private int getConsoleMode() {
       
   232 //        return WindowsSupport.getConsoleMode();
       
   233 //    }
       
   234 //
       
   235 //    private void setConsoleMode(int mode) {
       
   236 //        WindowsSupport.setConsoleMode(mode);
       
   237 //    }
       
   238 //
       
   239 //    private byte[] readConsoleInput() {
       
   240 //        // XXX does how many events to read in one call matter?
       
   241 //        INPUT_RECORD[] events = null;
       
   242 //        try {
       
   243 //            events = WindowsSupport.readConsoleInput(1);
       
   244 //        } catch (IOException e) {
       
   245 //            Log.debug("read Windows console input error: ", e);
       
   246 //        }
       
   247 //        if (events == null) {
       
   248 //            return new byte[0];
       
   249 //        }
       
   250 //        StringBuilder sb = new StringBuilder();
       
   251 //        for (int i = 0; i < events.length; i++ ) {
       
   252 //            KEY_EVENT_RECORD keyEvent = events[i].keyEvent;
       
   253 //            //Log.trace(keyEvent.keyDown? "KEY_DOWN" : "KEY_UP", "key code:", keyEvent.keyCode, "char:", (long)keyEvent.uchar);
       
   254 //            if (keyEvent.keyDown) {
       
   255 //                if (keyEvent.uchar > 0) {
       
   256 //                    // support some C1 control sequences: ALT + [@-_] (and [a-z]?) => ESC <ascii>
       
   257 //                    // http://en.wikipedia.org/wiki/C0_and_C1_control_codes#C1_set
       
   258 //                    final int altState = KEY_EVENT_RECORD.LEFT_ALT_PRESSED | KEY_EVENT_RECORD.RIGHT_ALT_PRESSED;
       
   259 //                    // Pressing "Alt Gr" is translated to Alt-Ctrl, hence it has to be checked that Ctrl is _not_ pressed,
       
   260 //                    // otherwise inserting of "Alt Gr" codes on non-US keyboards would yield errors
       
   261 //                    final int ctrlState = KEY_EVENT_RECORD.LEFT_CTRL_PRESSED | KEY_EVENT_RECORD.RIGHT_CTRL_PRESSED;
       
   262 //                    if (((keyEvent.uchar >= '@' && keyEvent.uchar <= '_') || (keyEvent.uchar >= 'a' && keyEvent.uchar <= 'z'))
       
   263 //                        && ((keyEvent.controlKeyState & altState) != 0) && ((keyEvent.controlKeyState & ctrlState) == 0)) {
       
   264 //                        sb.append('\u001B'); // ESC
       
   265 //                    }
       
   266 //
       
   267 //                    sb.append(keyEvent.uchar);
       
   268 //                    continue;
       
   269 //                }
       
   270 //                // virtual keycodes: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
       
   271 //                // just add support for basic editing keys (no control state, no numpad keys)
       
   272 //                String escapeSequence = null;
       
   273 //                switch (keyEvent.keyCode) {
       
   274 //                case 0x21: // VK_PRIOR PageUp
       
   275 //                    escapeSequence = "\u001B[5~";
       
   276 //                    break;
       
   277 //                case 0x22: // VK_NEXT PageDown
       
   278 //                    escapeSequence = "\u001B[6~";
       
   279 //                    break;
       
   280 //                case 0x23: // VK_END
       
   281 //                    escapeSequence = "\u001B[4~";
       
   282 //                    break;
       
   283 //                case 0x24: // VK_HOME
       
   284 //                    escapeSequence = "\u001B[1~";
       
   285 //                    break;
       
   286 //                case 0x25: // VK_LEFT
       
   287 //                    escapeSequence = "\u001B[D";
       
   288 //                    break;
       
   289 //                case 0x26: // VK_UP
       
   290 //                    escapeSequence = "\u001B[A";
       
   291 //                    break;
       
   292 //                case 0x27: // VK_RIGHT
       
   293 //                    escapeSequence = "\u001B[C";
       
   294 //                    break;
       
   295 //                case 0x28: // VK_DOWN
       
   296 //                    escapeSequence = "\u001B[B";
       
   297 //                    break;
       
   298 //                case 0x2D: // VK_INSERT
       
   299 //                    escapeSequence = "\u001B[2~";
       
   300 //                    break;
       
   301 //                case 0x2E: // VK_DELETE
       
   302 //                    escapeSequence = "\u001B[3~";
       
   303 //                    break;
       
   304 //                default:
       
   305 //                    break;
       
   306 //                }
       
   307 //                if (escapeSequence != null) {
       
   308 //                    for (int k = 0; k < keyEvent.repeatCount; k++) {
       
   309 //                        sb.append(escapeSequence);
       
   310 //                    }
       
   311 //                }
       
   312 //            } else {
       
   313 //                // key up event
       
   314 //                // support ALT+NumPad input method
       
   315 //                if (keyEvent.keyCode == 0x12/*VK_MENU ALT key*/ && keyEvent.uchar > 0) {
       
   316 //                    sb.append(keyEvent.uchar);
       
   317 //                }
       
   318 //            }
       
   319 //        }
       
   320 //        return sb.toString().getBytes();
       
   321 //    }
       
   322 //
       
   323 //    private int getConsoleOutputCodepage() {
       
   324 //        return Kernel32.GetConsoleOutputCP();
       
   325 //    }
       
   326 //
       
   327 //    private int getWindowsTerminalWidth() {
       
   328 //        return WindowsSupport.getWindowsTerminalWidth();
       
   329 //    }
       
   330 //
       
   331 //    private int getWindowsTerminalHeight() {
       
   332 //        return WindowsSupport.getWindowsTerminalHeight();
       
   333 //    }
       
   334 
       
   335     //
       
   336     // Native Bits
       
   337     //
       
   338     static {
       
   339         System.loadLibrary("le");
       
   340         initIDs();
       
   341     }
       
   342 
       
   343     private static native void initIDs();
       
   344 
       
   345     protected native int getConsoleMode();
       
   346 
       
   347     protected native void setConsoleMode(int mode);
       
   348 
       
   349     private byte[] readConsoleInput() {
       
   350         KEY_EVENT_RECORD keyEvent = readKeyEvent();
       
   351 
       
   352         return convertKeys(keyEvent).getBytes();
       
   353     }
       
   354 
       
   355     public static String convertKeys(KEY_EVENT_RECORD keyEvent) {
       
   356         if (keyEvent == null) {
       
   357             return "";
       
   358         }
       
   359 
       
   360         StringBuilder sb = new StringBuilder();
       
   361 
       
   362         if (keyEvent.keyDown) {
       
   363             if (keyEvent.uchar > 0) {
       
   364                 // support some C1 control sequences: ALT + [@-_] (and [a-z]?) => ESC <ascii>
       
   365                 // http://en.wikipedia.org/wiki/C0_and_C1_control_codes#C1_set
       
   366                 final int altState = KEY_EVENT_RECORD.ALT_PRESSED;
       
   367                 // Pressing "Alt Gr" is translated to Alt-Ctrl, hence it has to be checked that Ctrl is _not_ pressed,
       
   368                 // otherwise inserting of "Alt Gr" codes on non-US keyboards would yield errors
       
   369                 final int ctrlState = KEY_EVENT_RECORD.CTRL_PRESSED;
       
   370 
       
   371                 boolean handled = false;
       
   372 
       
   373                 if ((keyEvent.controlKeyState & ctrlState) != 0) {
       
   374                     switch (keyEvent.keyCode) {
       
   375                         case 0x43: //Ctrl-C
       
   376                             sb.append("\003");
       
   377                             handled = true;
       
   378                             break;
       
   379                     }
       
   380                 }
       
   381 
       
   382                 if ((keyEvent.controlKeyState & KEY_EVENT_RECORD.SHIFT_PRESSED) != 0) {
       
   383                     switch (keyEvent.keyCode) {
       
   384                         case 0x09: //Shift-Tab
       
   385                             sb.append("\033\133\132");
       
   386                             handled = true;
       
   387                             break;
       
   388                     }
       
   389                 }
       
   390 
       
   391                 if (!handled) {
       
   392                     if (((keyEvent.uchar >= '@' && keyEvent.uchar <= '_') || (keyEvent.uchar >= 'a' && keyEvent.uchar <= 'z'))
       
   393                         && ((keyEvent.controlKeyState & altState) != 0) && ((keyEvent.controlKeyState & ctrlState) == 0)) {
       
   394                         sb.append('\u001B'); // ESC
       
   395                     }
       
   396 
       
   397                     sb.append(keyEvent.uchar);
       
   398                 }
       
   399             } else {
       
   400                 // virtual keycodes: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
       
   401                 // xterm escape codes: E. Moy, S. Gildea and T. Dickey, "XTerm Control Sequences":
       
   402                 // http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
       
   403                 // http://xorg.freedesktop.org/releases/X11R6.8.1/PDF/ctlseqs.pdf
       
   404                 // just add support for basic editing keys and function keys
       
   405                 String escapeSequence = null;
       
   406                 switch (keyEvent.keyCode) {
       
   407                 case 0x21: // VK_PRIOR PageUp
       
   408                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[5~", "\u001B[5;%d~");
       
   409                     break;
       
   410                 case 0x22: // VK_NEXT PageDown
       
   411                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[6~", "\u001B[6;%d~");
       
   412                     break;
       
   413                 case 0x23: // VK_END
       
   414                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[4~", "\u001B[4;%d~");
       
   415                     break;
       
   416                 case 0x24: // VK_HOME
       
   417                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[1~", "\u001B[1;%d~");
       
   418                     break;
       
   419                 case 0x25: // VK_LEFT
       
   420                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[D", "\u001B[1;%dD");
       
   421                     break;
       
   422                 case 0x26: // VK_UP
       
   423                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[A", "\u001B[1;%dA");
       
   424                     break;
       
   425                 case 0x27: // VK_RIGHT
       
   426                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[C", "\u001B[1;%dC");
       
   427                     break;
       
   428                 case 0x28: // VK_DOWN
       
   429                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[B", "\u001B[1;%dB");
       
   430                     break;
       
   431                 case 0x2D: // VK_INSERT
       
   432                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[2~", "\u001B[2;%d~");
       
   433                     break;
       
   434                 case 0x2E: // VK_DELETE
       
   435                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[3~", "\u001B[3;%d~");
       
   436                     break;
       
   437                 case 0x70: // VK_F1
       
   438                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001BOP", "\u001BO%dP");
       
   439                     break;
       
   440                 case 0x71: // VK_F2
       
   441                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001BOQ", "\u001BO%dQ");
       
   442                     break;
       
   443                 case 0x72: // VK_F3
       
   444                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001BOR", "\u001BO%dR");
       
   445                     break;
       
   446                 case 0x73: // VK_F4
       
   447                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001BOS", "\u001BO%dS");
       
   448                     break;
       
   449                 case 0x74: // VK_F5
       
   450                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[15~", "\u001B[15;%d~");
       
   451                     break;
       
   452                 case 0x75: // VK_F6
       
   453                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[17~", "\u001B[17;%d~");
       
   454                     break;
       
   455                 case 0x76: // VK_F7
       
   456                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[18~", "\u001B[18;%d~");
       
   457                     break;
       
   458                 case 0x77: // VK_F8
       
   459                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[19~", "\u001B[19;%d~");
       
   460                     break;
       
   461                 case 0x78: // VK_F9
       
   462                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[20~", "\u001B[20;%d~");
       
   463                     break;
       
   464                 case 0x79: // VK_F10
       
   465                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[21~", "\u001B[21;%d~");
       
   466                     break;
       
   467                 case 0x7A: // VK_F11
       
   468                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[23~", "\u001B[23;%d~");
       
   469                     break;
       
   470                 case 0x7B: // VK_F12
       
   471                     escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[24~", "\u001B[24;%d~");
       
   472                     break;
       
   473                 default:
       
   474                     break;
       
   475                 }
       
   476                 if (escapeSequence != null) {
       
   477                     for (int k = 0; k < keyEvent.repeatCount; k++) {
       
   478                         sb.append(escapeSequence);
       
   479                     }
       
   480                 }
       
   481             }
       
   482         } else {
       
   483             // key up event
       
   484             // support ALT+NumPad input method
       
   485             if (keyEvent.keyCode == 0x12/*VK_MENU ALT key*/ && keyEvent.uchar > 0) {
       
   486                 sb.append(keyEvent.uchar);
       
   487             }
       
   488         }
       
   489         return sb.toString();
       
   490     }
       
   491 
       
   492     private static String escapeSequence(int controlKeyState, String noControlSequence, String withControlSequence) {
       
   493         int controlNum = 1;
       
   494 
       
   495         if ((controlKeyState & KEY_EVENT_RECORD.SHIFT_PRESSED) != 0) {
       
   496             controlNum += 1;
       
   497         }
       
   498 
       
   499         if ((controlKeyState & KEY_EVENT_RECORD.ALT_PRESSED) != 0) {
       
   500             controlNum += 2;
       
   501         }
       
   502 
       
   503         if ((controlKeyState & KEY_EVENT_RECORD.CTRL_PRESSED) != 0) {
       
   504             controlNum += 4;
       
   505         }
       
   506 
       
   507         if (controlNum > 1) {
       
   508             return String.format(withControlSequence, controlNum);
       
   509         } else {
       
   510             return noControlSequence;
       
   511         }
       
   512     }
       
   513 
       
   514     private native KEY_EVENT_RECORD readKeyEvent();
       
   515 
       
   516     public static class KEY_EVENT_RECORD {
       
   517         public final static int ALT_PRESSED = 0x3;
       
   518         public final static int CTRL_PRESSED = 0xC;
       
   519         public final static int SHIFT_PRESSED = 0x10;
       
   520         public final boolean keyDown;
       
   521         public final char uchar;
       
   522         public final int controlKeyState;
       
   523         public final int keyCode;
       
   524         public final int repeatCount;
       
   525 
       
   526         public KEY_EVENT_RECORD(boolean keyDown, char uchar, int controlKeyState, int keyCode, int repeatCount) {
       
   527             this.keyDown = keyDown;
       
   528             this.uchar = uchar;
       
   529             this.controlKeyState = controlKeyState;
       
   530             this.keyCode = keyCode;
       
   531             this.repeatCount = repeatCount;
       
   532         }
       
   533 
       
   534     }
       
   535 
       
   536     private native int getConsoleOutputCodepage();
       
   537 
       
   538     private native int getWindowsTerminalWidth();
       
   539 
       
   540     private native int getWindowsTerminalHeight();
       
   541 
       
   542     private native BufferState getBufferState();
       
   543 
       
   544     private native void setCursorPosition(int x, int y);
       
   545 
       
   546     /**
       
   547      * Console mode
       
   548      * <p/>
       
   549      * Constants copied <tt>wincon.h</tt>.
       
   550      */
       
   551     public static enum ConsoleMode
       
   552     {
       
   553         /**
       
   554          * The ReadFile or ReadConsole function returns only when a carriage return
       
   555          * character is read. If this mode is disable, the functions return when one
       
   556          * or more characters are available.
       
   557          */
       
   558         ENABLE_LINE_INPUT(2),
       
   559 
       
   560         /**
       
   561          * Characters read by the ReadFile or ReadConsole function are written to
       
   562          * the active screen buffer as they are read. This mode can be used only if
       
   563          * the ENABLE_LINE_INPUT mode is also enabled.
       
   564          */
       
   565         ENABLE_ECHO_INPUT(4),
       
   566 
       
   567         /**
       
   568          * CTRL+C is processed by the system and is not placed in the input buffer.
       
   569          * If the input buffer is being read by ReadFile or ReadConsole, other
       
   570          * control keys are processed by the system and are not returned in the
       
   571          * ReadFile or ReadConsole buffer. If the ENABLE_LINE_INPUT mode is also
       
   572          * enabled, backspace, carriage return, and linefeed characters are handled
       
   573          * by the system.
       
   574          */
       
   575         ENABLE_PROCESSED_INPUT(1),
       
   576 
       
   577         /**
       
   578          * User interactions that change the size of the console screen buffer are
       
   579          * reported in the console's input buffee. Information about these events
       
   580          * can be read from the input buffer by applications using
       
   581          * theReadConsoleInput function, but not by those using ReadFile
       
   582          * orReadConsole.
       
   583          */
       
   584         ENABLE_WINDOW_INPUT(8),
       
   585 
       
   586         /**
       
   587          * If the mouse pointer is within the borders of the console window and the
       
   588          * window has the keyboard focus, mouse events generated by mouse movement
       
   589          * and button presses are placed in the input buffer. These events are
       
   590          * discarded by ReadFile or ReadConsole, even when this mode is enabled.
       
   591          */
       
   592         ENABLE_MOUSE_INPUT(16),
       
   593 
       
   594         /**
       
   595          * When enabled, text entered in a console window will be inserted at the
       
   596          * current cursor location and all text following that location will not be
       
   597          * overwritten. When disabled, all following text will be overwritten. An OR
       
   598          * operation must be performed with this flag and the ENABLE_EXTENDED_FLAGS
       
   599          * flag to enable this functionality.
       
   600          */
       
   601         ENABLE_PROCESSED_OUTPUT(1),
       
   602 
       
   603         /**
       
   604          * This flag enables the user to use the mouse to select and edit text. To
       
   605          * enable this option, use the OR to combine this flag with
       
   606          * ENABLE_EXTENDED_FLAGS.
       
   607          */
       
   608         ENABLE_WRAP_AT_EOL_OUTPUT(2),;
       
   609 
       
   610         public final int code;
       
   611 
       
   612         ConsoleMode(final int code) {
       
   613             this.code = code;
       
   614         }
       
   615     }
       
   616 
       
   617 }