8187788: Disasble javax/net tests until JDK-8187786 is resolved
Reviewed-by: darcy
/*
* Copyright (c) 2002-2012, the original author or authors.
*
* This software is distributable under the BSD license. See the terms of the
* BSD license in the documentation provided with this software.
*
* http://www.opensource.org/licenses/bsd-license.php
*/
package jdk.internal.jline;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import jdk.internal.jline.internal.Configuration;
import jdk.internal.jline.internal.Log;
//import org.fusesource.jansi.internal.WindowsSupport;
//import org.fusesource.jansi.internal.Kernel32;
//import static org.fusesource.jansi.internal.Kernel32.*;
import static jdk.internal.jline.WindowsTerminal.ConsoleMode.ENABLE_ECHO_INPUT;
import static jdk.internal.jline.WindowsTerminal.ConsoleMode.ENABLE_LINE_INPUT;
import static jdk.internal.jline.WindowsTerminal.ConsoleMode.ENABLE_PROCESSED_INPUT;
import static jdk.internal.jline.WindowsTerminal.ConsoleMode.ENABLE_WINDOW_INPUT;
/**
* Terminal implementation for Microsoft Windows. Terminal initialization in
* {@link #init} is accomplished by extracting the
* <em>jline_<i>version</i>.dll</em>, saving it to the system temporary
* directoy (determined by the setting of the <em>java.io.tmpdir</em> System
* property), loading the library, and then calling the Win32 APIs <a
* href="http://msdn.microsoft.com/library/default.asp?
* url=/library/en-us/dllproc/base/setconsolemode.asp">SetConsoleMode</a> and
* <a href="http://msdn.microsoft.com/library/default.asp?
* url=/library/en-us/dllproc/base/getconsolemode.asp">GetConsoleMode</a> to
* disable character echoing.
* <p/>
* <p>
* By default, the {@link #wrapInIfNeeded(java.io.InputStream)} method will attempt
* to test to see if the specified {@link InputStream} is {@link System#in} or a wrapper
* around {@link FileDescriptor#in}, and if so, will bypass the character reading to
* directly invoke the readc() method in the JNI library. This is so the class
* can read special keys (like arrow keys) which are otherwise inaccessible via
* the {@link System#in} stream. Using JNI reading can be bypassed by setting
* the <code>jline.WindowsTerminal.directConsole</code> system property
* to <code>false</code>.
* </p>
*
* @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
* @since 2.0
*/
public class WindowsTerminal
extends TerminalSupport
{
public static final String DIRECT_CONSOLE = WindowsTerminal.class.getName() + ".directConsole";
public static final String ANSI = WindowsTerminal.class.getName() + ".ansi";
private boolean directConsole;
private int originalMode;
public WindowsTerminal() throws Exception {
super(true);
}
@Override
public void init() throws Exception {
super.init();
// setAnsiSupported(Configuration.getBoolean(ANSI, true));
setAnsiSupported(false);
//
// FIXME: Need a way to disable direct console and sysin detection muck
//
setDirectConsole(Configuration.getBoolean(DIRECT_CONSOLE, true));
this.originalMode = getConsoleMode();
setConsoleMode(originalMode & ~ENABLE_ECHO_INPUT.code);
setEchoEnabled(false);
}
/**
* Restore the original terminal configuration, which can be used when
* shutting down the console reader. The ConsoleReader cannot be
* used after calling this method.
*/
@Override
public void restore() throws Exception {
// restore the old console mode
setConsoleMode(originalMode);
super.restore();
}
@Override
public int getWidth() {
int w = getWindowsTerminalWidth();
return w < 1 ? DEFAULT_WIDTH : w;
}
@Override
public int getHeight() {
int h = getWindowsTerminalHeight();
return h < 1 ? DEFAULT_HEIGHT : h;
}
@Override
public void setEchoEnabled(final boolean enabled) {
// Must set these four modes at the same time to make it work fine.
if (enabled) {
setConsoleMode(getConsoleMode() |
ENABLE_ECHO_INPUT.code |
ENABLE_LINE_INPUT.code |
ENABLE_PROCESSED_INPUT.code |
ENABLE_WINDOW_INPUT.code);
}
else {
setConsoleMode(getConsoleMode() &
~(ENABLE_LINE_INPUT.code |
ENABLE_ECHO_INPUT.code |
ENABLE_PROCESSED_INPUT.code |
ENABLE_WINDOW_INPUT.code));
}
super.setEchoEnabled(enabled);
}
/**
* Whether or not to allow the use of the JNI console interaction.
*/
public void setDirectConsole(final boolean flag) {
this.directConsole = flag;
Log.debug("Direct console: ", flag);
}
/**
* Whether or not to allow the use of the JNI console interaction.
*/
public Boolean getDirectConsole() {
return directConsole;
}
@Override
public InputStream wrapInIfNeeded(InputStream in) throws IOException {
if (directConsole && isSystemIn(in)) {
return new InputStream() {
private byte[] buf = null;
int bufIdx = 0;
@Override
public int read() throws IOException {
while (buf == null || bufIdx == buf.length) {
buf = readConsoleInput();
bufIdx = 0;
}
int c = buf[bufIdx] & 0xFF;
bufIdx++;
return c;
}
};
} else {
return super.wrapInIfNeeded(in);
}
}
protected boolean isSystemIn(final InputStream in) throws IOException {
if (in == null) {
return false;
}
else if (in == System.in) {
return true;
}
else if (in instanceof FileInputStream && ((FileInputStream) in).getFD() == FileDescriptor.in) {
return true;
}
return false;
}
@Override
public String getOutputEncoding() {
int codepage = getConsoleOutputCodepage();
//http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html
String charsetMS = "ms" + codepage;
if (java.nio.charset.Charset.isSupported(charsetMS)) {
return charsetMS;
}
String charsetCP = "cp" + codepage;
if (java.nio.charset.Charset.isSupported(charsetCP)) {
return charsetCP;
}
Log.debug("can't figure out the Java Charset of this code page (" + codepage + ")...");
return super.getOutputEncoding();
}
//
// Original code:
//
// private int getConsoleMode() {
// return WindowsSupport.getConsoleMode();
// }
//
// private void setConsoleMode(int mode) {
// WindowsSupport.setConsoleMode(mode);
// }
//
// private byte[] readConsoleInput() {
// // XXX does how many events to read in one call matter?
// INPUT_RECORD[] events = null;
// try {
// events = WindowsSupport.readConsoleInput(1);
// } catch (IOException e) {
// Log.debug("read Windows console input error: ", e);
// }
// if (events == null) {
// return new byte[0];
// }
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < events.length; i++ ) {
// KEY_EVENT_RECORD keyEvent = events[i].keyEvent;
// //Log.trace(keyEvent.keyDown? "KEY_DOWN" : "KEY_UP", "key code:", keyEvent.keyCode, "char:", (long)keyEvent.uchar);
// if (keyEvent.keyDown) {
// if (keyEvent.uchar > 0) {
// // support some C1 control sequences: ALT + [@-_] (and [a-z]?) => ESC <ascii>
// // http://en.wikipedia.org/wiki/C0_and_C1_control_codes#C1_set
// final int altState = KEY_EVENT_RECORD.LEFT_ALT_PRESSED | KEY_EVENT_RECORD.RIGHT_ALT_PRESSED;
// // Pressing "Alt Gr" is translated to Alt-Ctrl, hence it has to be checked that Ctrl is _not_ pressed,
// // otherwise inserting of "Alt Gr" codes on non-US keyboards would yield errors
// final int ctrlState = KEY_EVENT_RECORD.LEFT_CTRL_PRESSED | KEY_EVENT_RECORD.RIGHT_CTRL_PRESSED;
// if (((keyEvent.uchar >= '@' && keyEvent.uchar <= '_') || (keyEvent.uchar >= 'a' && keyEvent.uchar <= 'z'))
// && ((keyEvent.controlKeyState & altState) != 0) && ((keyEvent.controlKeyState & ctrlState) == 0)) {
// sb.append('\u001B'); // ESC
// }
//
// sb.append(keyEvent.uchar);
// continue;
// }
// // virtual keycodes: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
// // just add support for basic editing keys (no control state, no numpad keys)
// String escapeSequence = null;
// switch (keyEvent.keyCode) {
// case 0x21: // VK_PRIOR PageUp
// escapeSequence = "\u001B[5~";
// break;
// case 0x22: // VK_NEXT PageDown
// escapeSequence = "\u001B[6~";
// break;
// case 0x23: // VK_END
// escapeSequence = "\u001B[4~";
// break;
// case 0x24: // VK_HOME
// escapeSequence = "\u001B[1~";
// break;
// case 0x25: // VK_LEFT
// escapeSequence = "\u001B[D";
// break;
// case 0x26: // VK_UP
// escapeSequence = "\u001B[A";
// break;
// case 0x27: // VK_RIGHT
// escapeSequence = "\u001B[C";
// break;
// case 0x28: // VK_DOWN
// escapeSequence = "\u001B[B";
// break;
// case 0x2D: // VK_INSERT
// escapeSequence = "\u001B[2~";
// break;
// case 0x2E: // VK_DELETE
// escapeSequence = "\u001B[3~";
// break;
// default:
// break;
// }
// if (escapeSequence != null) {
// for (int k = 0; k < keyEvent.repeatCount; k++) {
// sb.append(escapeSequence);
// }
// }
// } else {
// // key up event
// // support ALT+NumPad input method
// if (keyEvent.keyCode == 0x12/*VK_MENU ALT key*/ && keyEvent.uchar > 0) {
// sb.append(keyEvent.uchar);
// }
// }
// }
// return sb.toString().getBytes();
// }
//
// private int getConsoleOutputCodepage() {
// return Kernel32.GetConsoleOutputCP();
// }
//
// private int getWindowsTerminalWidth() {
// return WindowsSupport.getWindowsTerminalWidth();
// }
//
// private int getWindowsTerminalHeight() {
// return WindowsSupport.getWindowsTerminalHeight();
// }
//
// Native Bits
//
static {
System.loadLibrary("le");
initIDs();
}
private static native void initIDs();
protected native int getConsoleMode();
protected native void setConsoleMode(int mode);
private byte[] readConsoleInput() {
KEY_EVENT_RECORD keyEvent = readKeyEvent();
return convertKeys(keyEvent).getBytes();
}
public static String convertKeys(KEY_EVENT_RECORD keyEvent) {
if (keyEvent == null) {
return "";
}
StringBuilder sb = new StringBuilder();
if (keyEvent.keyDown) {
if (keyEvent.uchar > 0) {
// support some C1 control sequences: ALT + [@-_] (and [a-z]?) => ESC <ascii>
// http://en.wikipedia.org/wiki/C0_and_C1_control_codes#C1_set
final int altState = KEY_EVENT_RECORD.ALT_PRESSED;
// Pressing "Alt Gr" is translated to Alt-Ctrl, hence it has to be checked that Ctrl is _not_ pressed,
// otherwise inserting of "Alt Gr" codes on non-US keyboards would yield errors
final int ctrlState = KEY_EVENT_RECORD.CTRL_PRESSED;
boolean handled = false;
if ((keyEvent.controlKeyState & ctrlState) != 0) {
switch (keyEvent.keyCode) {
case 0x43: //Ctrl-C
sb.append("\003");
handled = true;
break;
}
}
if ((keyEvent.controlKeyState & KEY_EVENT_RECORD.SHIFT_PRESSED) != 0) {
switch (keyEvent.keyCode) {
case 0x09: //Shift-Tab
sb.append("\033\133\132");
handled = true;
break;
}
}
if (!handled) {
if (((keyEvent.uchar >= '@' && keyEvent.uchar <= '_') || (keyEvent.uchar >= 'a' && keyEvent.uchar <= 'z'))
&& ((keyEvent.controlKeyState & altState) != 0) && ((keyEvent.controlKeyState & ctrlState) == 0)) {
sb.append('\u001B'); // ESC
}
sb.append(keyEvent.uchar);
}
} else {
// virtual keycodes: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
// xterm escape codes: E. Moy, S. Gildea and T. Dickey, "XTerm Control Sequences":
// http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
// http://xorg.freedesktop.org/releases/X11R6.8.1/PDF/ctlseqs.pdf
// just add support for basic editing keys and function keys
String escapeSequence = null;
switch (keyEvent.keyCode) {
case 0x21: // VK_PRIOR PageUp
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[5~", "\u001B[5;%d~");
break;
case 0x22: // VK_NEXT PageDown
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[6~", "\u001B[6;%d~");
break;
case 0x23: // VK_END
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[4~", "\u001B[4;%d~");
break;
case 0x24: // VK_HOME
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[1~", "\u001B[1;%d~");
break;
case 0x25: // VK_LEFT
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[D", "\u001B[1;%dD");
break;
case 0x26: // VK_UP
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[A", "\u001B[1;%dA");
break;
case 0x27: // VK_RIGHT
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[C", "\u001B[1;%dC");
break;
case 0x28: // VK_DOWN
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[B", "\u001B[1;%dB");
break;
case 0x2D: // VK_INSERT
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[2~", "\u001B[2;%d~");
break;
case 0x2E: // VK_DELETE
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[3~", "\u001B[3;%d~");
break;
case 0x70: // VK_F1
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001BOP", "\u001BO%dP");
break;
case 0x71: // VK_F2
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001BOQ", "\u001BO%dQ");
break;
case 0x72: // VK_F3
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001BOR", "\u001BO%dR");
break;
case 0x73: // VK_F4
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001BOS", "\u001BO%dS");
break;
case 0x74: // VK_F5
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[15~", "\u001B[15;%d~");
break;
case 0x75: // VK_F6
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[17~", "\u001B[17;%d~");
break;
case 0x76: // VK_F7
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[18~", "\u001B[18;%d~");
break;
case 0x77: // VK_F8
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[19~", "\u001B[19;%d~");
break;
case 0x78: // VK_F9
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[20~", "\u001B[20;%d~");
break;
case 0x79: // VK_F10
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[21~", "\u001B[21;%d~");
break;
case 0x7A: // VK_F11
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[23~", "\u001B[23;%d~");
break;
case 0x7B: // VK_F12
escapeSequence = escapeSequence(keyEvent.controlKeyState, "\u001B[24~", "\u001B[24;%d~");
break;
default:
break;
}
if (escapeSequence != null) {
for (int k = 0; k < keyEvent.repeatCount; k++) {
sb.append(escapeSequence);
}
}
}
} else {
// key up event
// support ALT+NumPad input method
if (keyEvent.keyCode == 0x12/*VK_MENU ALT key*/ && keyEvent.uchar > 0) {
sb.append(keyEvent.uchar);
}
}
return sb.toString();
}
private static String escapeSequence(int controlKeyState, String noControlSequence, String withControlSequence) {
int controlNum = 1;
if ((controlKeyState & KEY_EVENT_RECORD.SHIFT_PRESSED) != 0) {
controlNum += 1;
}
if ((controlKeyState & KEY_EVENT_RECORD.ALT_PRESSED) != 0) {
controlNum += 2;
}
if ((controlKeyState & KEY_EVENT_RECORD.CTRL_PRESSED) != 0) {
controlNum += 4;
}
if (controlNum > 1) {
return String.format(withControlSequence, controlNum);
} else {
return noControlSequence;
}
}
private native KEY_EVENT_RECORD readKeyEvent();
public static class KEY_EVENT_RECORD {
public final static int ALT_PRESSED = 0x3;
public final static int CTRL_PRESSED = 0xC;
public final static int SHIFT_PRESSED = 0x10;
public final boolean keyDown;
public final char uchar;
public final int controlKeyState;
public final int keyCode;
public final int repeatCount;
public KEY_EVENT_RECORD(boolean keyDown, char uchar, int controlKeyState, int keyCode, int repeatCount) {
this.keyDown = keyDown;
this.uchar = uchar;
this.controlKeyState = controlKeyState;
this.keyCode = keyCode;
this.repeatCount = repeatCount;
}
}
private native int getConsoleOutputCodepage();
private native int getWindowsTerminalWidth();
private native int getWindowsTerminalHeight();
/**
* Console mode
* <p/>
* Constants copied <tt>wincon.h</tt>.
*/
public static enum ConsoleMode
{
/**
* The ReadFile or ReadConsole function returns only when a carriage return
* character is read. If this mode is disable, the functions return when one
* or more characters are available.
*/
ENABLE_LINE_INPUT(2),
/**
* Characters read by the ReadFile or ReadConsole function are written to
* the active screen buffer as they are read. This mode can be used only if
* the ENABLE_LINE_INPUT mode is also enabled.
*/
ENABLE_ECHO_INPUT(4),
/**
* CTRL+C is processed by the system and is not placed in the input buffer.
* If the input buffer is being read by ReadFile or ReadConsole, other
* control keys are processed by the system and are not returned in the
* ReadFile or ReadConsole buffer. If the ENABLE_LINE_INPUT mode is also
* enabled, backspace, carriage return, and linefeed characters are handled
* by the system.
*/
ENABLE_PROCESSED_INPUT(1),
/**
* User interactions that change the size of the console screen buffer are
* reported in the console's input buffee. Information about these events
* can be read from the input buffer by applications using
* theReadConsoleInput function, but not by those using ReadFile
* orReadConsole.
*/
ENABLE_WINDOW_INPUT(8),
/**
* If the mouse pointer is within the borders of the console window and the
* window has the keyboard focus, mouse events generated by mouse movement
* and button presses are placed in the input buffer. These events are
* discarded by ReadFile or ReadConsole, even when this mode is enabled.
*/
ENABLE_MOUSE_INPUT(16),
/**
* When enabled, text entered in a console window will be inserted at the
* current cursor location and all text following that location will not be
* overwritten. When disabled, all following text will be overwritten. An OR
* operation must be performed with this flag and the ENABLE_EXTENDED_FLAGS
* flag to enable this functionality.
*/
ENABLE_PROCESSED_OUTPUT(1),
/**
* This flag enables the user to use the mouse to select and edit text. To
* enable this option, use the OR to combine this flag with
* ENABLE_EXTENDED_FLAGS.
*/
ENABLE_WRAP_AT_EOL_OUTPUT(2),;
public final int code;
ConsoleMode(final int code) {
this.code = code;
}
}
}