blob: 83a2362fdd1fe554c4ac157629453b48f35872f5 [file] [log] [blame]
/*
* Copyright (c) 2002-2019, 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.
*
* https://opensource.org/licenses/BSD-3-Clause
*/
package jdk.internal.org.jline.terminal.impl.jna.win;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.function.Function;
import java.util.function.IntConsumer;
//import com.sun.jna.LastErrorException;
//import com.sun.jna.Pointer;
//import com.sun.jna.ptr.IntByReference;
import jdk.internal.org.jline.terminal.Cursor;
import jdk.internal.org.jline.terminal.Size;
import jdk.internal.org.jline.terminal.Terminal;
import jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal;
import jdk.internal.org.jline.utils.InfoCmp;
import jdk.internal.org.jline.utils.OSUtils;
public class JnaWinSysTerminal extends AbstractWindowsTerminal {
private static final Pointer consoleIn = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_INPUT_HANDLE);
private static final Pointer consoleOut = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_OUTPUT_HANDLE);
public static JnaWinSysTerminal createTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler, boolean paused, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
Writer writer;
if (ansiPassThrough) {
if (type == null) {
type = OSUtils.IS_CONEMU ? TYPE_WINDOWS_CONEMU : TYPE_WINDOWS;
}
writer = new JnaWinConsoleWriter(consoleOut);
} else {
IntByReference mode = new IntByReference();
Kernel32.INSTANCE.GetConsoleMode(consoleOut, mode);
try {
Kernel32.INSTANCE.SetConsoleMode(consoleOut, mode.getValue() | AbstractWindowsTerminal.ENABLE_VIRTUAL_TERMINAL_PROCESSING);
if (type == null) {
type = TYPE_WINDOWS_VTP;
}
writer = new JnaWinConsoleWriter(consoleOut);
} catch (LastErrorException e) {
if (OSUtils.IS_CONEMU) {
if (type == null) {
type = TYPE_WINDOWS_CONEMU;
}
writer = new JnaWinConsoleWriter(consoleOut);
} else {
if (type == null) {
type = TYPE_WINDOWS;
}
writer = new WindowsAnsiWriter(new BufferedWriter(new JnaWinConsoleWriter(consoleOut)), consoleOut);
}
}
}
JnaWinSysTerminal terminal = new JnaWinSysTerminal(writer, name, type, encoding, codepage, nativeSignals, signalHandler, inputStreamWrapper);
// Start input pump thread
if (!paused) {
terminal.resume();
}
return terminal;
}
JnaWinSysTerminal(Writer writer, String name, String type, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
super(writer, name, type, encoding, codepage, nativeSignals, signalHandler, inputStreamWrapper);
strings.put(InfoCmp.Capability.key_mouse, "\\E[M");
}
@Override
protected int getConsoleOutputCP() {
return Kernel32.INSTANCE.GetConsoleOutputCP();
}
@Override
protected int getConsoleMode() {
IntByReference mode = new IntByReference();
Kernel32.INSTANCE.GetConsoleMode(consoleIn, mode);
return mode.getValue();
}
@Override
protected void setConsoleMode(int mode) {
Kernel32.INSTANCE.SetConsoleMode(consoleIn, mode);
}
public Size getSize() {
Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO();
Kernel32.INSTANCE.GetConsoleScreenBufferInfo(consoleOut, info);
return new Size(info.windowWidth(), info.windowHeight());
}
public Size getBufferSize() {
Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO();
Kernel32.INSTANCE.GetConsoleScreenBufferInfo(consoleOut, info);
return new Size(info.dwSize.X, info.dwSize.Y);
}
protected boolean processConsoleInput() throws IOException {
Kernel32.INPUT_RECORD event = readConsoleInput(100);
if (event == null) {
return false;
}
switch (event.EventType) {
case Kernel32.INPUT_RECORD.KEY_EVENT:
processKeyEvent(event.Event.KeyEvent);
return true;
case Kernel32.INPUT_RECORD.WINDOW_BUFFER_SIZE_EVENT:
raise(Signal.WINCH);
return false;
case Kernel32.INPUT_RECORD.MOUSE_EVENT:
processMouseEvent(event.Event.MouseEvent);
return true;
case Kernel32.INPUT_RECORD.FOCUS_EVENT:
processFocusEvent(event.Event.FocusEvent.bSetFocus);
return true;
default:
// Skip event
return false;
}
}
private void processKeyEvent(Kernel32.KEY_EVENT_RECORD keyEvent) throws IOException {
processKeyEvent(keyEvent.bKeyDown, keyEvent.wVirtualKeyCode, keyEvent.uChar.UnicodeChar, keyEvent.dwControlKeyState);
}
private char[] focus = new char[] { '\033', '[', ' ' };
private void processFocusEvent(boolean hasFocus) throws IOException {
if (focusTracking) {
focus[2] = hasFocus ? 'I' : 'O';
slaveInputPipe.write(focus);
}
}
private char[] mouse = new char[] { '\033', '[', 'M', ' ', ' ', ' ' };
private void processMouseEvent(Kernel32.MOUSE_EVENT_RECORD mouseEvent) throws IOException {
int dwEventFlags = mouseEvent.dwEventFlags;
int dwButtonState = mouseEvent.dwButtonState;
if (tracking == MouseTracking.Off
|| tracking == MouseTracking.Normal && dwEventFlags == Kernel32.MOUSE_MOVED
|| tracking == MouseTracking.Button && dwEventFlags == Kernel32.MOUSE_MOVED && dwButtonState == 0) {
return;
}
int cb = 0;
dwEventFlags &= ~ Kernel32.DOUBLE_CLICK; // Treat double-clicks as normal
if (dwEventFlags == Kernel32.MOUSE_WHEELED) {
cb |= 64;
if ((dwButtonState >> 16) < 0) {
cb |= 1;
}
} else if (dwEventFlags == Kernel32.MOUSE_HWHEELED) {
return;
} else if ((dwButtonState & Kernel32.FROM_LEFT_1ST_BUTTON_PRESSED) != 0) {
cb |= 0x00;
} else if ((dwButtonState & Kernel32.RIGHTMOST_BUTTON_PRESSED) != 0) {
cb |= 0x01;
} else if ((dwButtonState & Kernel32.FROM_LEFT_2ND_BUTTON_PRESSED) != 0) {
cb |= 0x02;
} else {
cb |= 0x03;
}
int cx = mouseEvent.dwMousePosition.X;
int cy = mouseEvent.dwMousePosition.Y;
mouse[3] = (char) (' ' + cb);
mouse[4] = (char) (' ' + cx + 1);
mouse[5] = (char) (' ' + cy + 1);
slaveInputPipe.write(mouse);
}
private final Kernel32.INPUT_RECORD[] inputEvents = new Kernel32.INPUT_RECORD[1];
private final IntByReference eventsRead = new IntByReference();
private Kernel32.INPUT_RECORD readConsoleInput(int dwMilliseconds) throws IOException {
if (Kernel32.INSTANCE.WaitForSingleObject(consoleIn, dwMilliseconds) != 0) {
return null;
}
Kernel32.INSTANCE.ReadConsoleInput(consoleIn, inputEvents, 1, eventsRead);
if (eventsRead.getValue() == 1) {
return inputEvents[0];
} else {
return null;
}
}
@Override
public Cursor getCursorPosition(IntConsumer discarded) {
Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO();
Kernel32.INSTANCE.GetConsoleScreenBufferInfo(consoleOut, info);
return new Cursor(info.dwCursorPosition.X, info.dwCursorPosition.Y);
}
}