52938
|
1 |
/*
|
58903
|
2 |
* Copyright (c) 2002-2019, the original author or authors.
|
52938
|
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 |
*
|
58903
|
7 |
* https://opensource.org/licenses/BSD-3-Clause
|
52938
|
8 |
*/
|
|
9 |
package jdk.internal.org.jline.terminal.impl.jna.win;
|
|
10 |
|
|
11 |
import java.io.BufferedWriter;
|
|
12 |
import java.io.IOException;
|
|
13 |
import java.io.InputStream;
|
|
14 |
import java.io.Writer;
|
|
15 |
import java.nio.charset.Charset;
|
|
16 |
import java.util.function.Function;
|
|
17 |
import java.util.function.IntConsumer;
|
|
18 |
|
|
19 |
//import com.sun.jna.LastErrorException;
|
|
20 |
//import com.sun.jna.Pointer;
|
|
21 |
//import com.sun.jna.ptr.IntByReference;
|
|
22 |
|
|
23 |
import jdk.internal.org.jline.terminal.Cursor;
|
|
24 |
import jdk.internal.org.jline.terminal.Size;
|
|
25 |
import jdk.internal.org.jline.terminal.Terminal;
|
|
26 |
import jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal;
|
|
27 |
import jdk.internal.org.jline.utils.InfoCmp;
|
|
28 |
import jdk.internal.org.jline.utils.OSUtils;
|
|
29 |
|
|
30 |
public class JnaWinSysTerminal extends AbstractWindowsTerminal {
|
|
31 |
|
|
32 |
private static final Pointer consoleIn = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_INPUT_HANDLE);
|
|
33 |
private static final Pointer consoleOut = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_OUTPUT_HANDLE);
|
|
34 |
|
|
35 |
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 {
|
|
36 |
Writer writer;
|
|
37 |
if (ansiPassThrough) {
|
|
38 |
if (type == null) {
|
58903
|
39 |
type = OSUtils.IS_CONEMU ? TYPE_WINDOWS_CONEMU : TYPE_WINDOWS;
|
52938
|
40 |
}
|
|
41 |
writer = new JnaWinConsoleWriter(consoleOut);
|
|
42 |
} else {
|
|
43 |
IntByReference mode = new IntByReference();
|
|
44 |
Kernel32.INSTANCE.GetConsoleMode(consoleOut, mode);
|
|
45 |
try {
|
|
46 |
Kernel32.INSTANCE.SetConsoleMode(consoleOut, mode.getValue() | AbstractWindowsTerminal.ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
|
47 |
if (type == null) {
|
|
48 |
type = TYPE_WINDOWS_VTP;
|
|
49 |
}
|
|
50 |
writer = new JnaWinConsoleWriter(consoleOut);
|
|
51 |
} catch (LastErrorException e) {
|
|
52 |
if (OSUtils.IS_CONEMU) {
|
|
53 |
if (type == null) {
|
58903
|
54 |
type = TYPE_WINDOWS_CONEMU;
|
52938
|
55 |
}
|
|
56 |
writer = new JnaWinConsoleWriter(consoleOut);
|
|
57 |
} else {
|
|
58 |
if (type == null) {
|
|
59 |
type = TYPE_WINDOWS;
|
|
60 |
}
|
|
61 |
writer = new WindowsAnsiWriter(new BufferedWriter(new JnaWinConsoleWriter(consoleOut)), consoleOut);
|
|
62 |
}
|
|
63 |
}
|
|
64 |
}
|
|
65 |
JnaWinSysTerminal terminal = new JnaWinSysTerminal(writer, name, type, encoding, codepage, nativeSignals, signalHandler, inputStreamWrapper);
|
|
66 |
// Start input pump thread
|
|
67 |
if (!paused) {
|
|
68 |
terminal.resume();
|
|
69 |
}
|
|
70 |
return terminal;
|
|
71 |
}
|
|
72 |
|
|
73 |
JnaWinSysTerminal(Writer writer, String name, String type, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler, Function<InputStream, InputStream> inputStreamWrapper) throws IOException {
|
|
74 |
super(writer, name, type, encoding, codepage, nativeSignals, signalHandler, inputStreamWrapper);
|
|
75 |
strings.put(InfoCmp.Capability.key_mouse, "\\E[M");
|
|
76 |
}
|
|
77 |
|
|
78 |
@Override
|
|
79 |
protected int getConsoleOutputCP() {
|
|
80 |
return Kernel32.INSTANCE.GetConsoleOutputCP();
|
|
81 |
}
|
|
82 |
|
|
83 |
@Override
|
|
84 |
protected int getConsoleMode() {
|
|
85 |
IntByReference mode = new IntByReference();
|
|
86 |
Kernel32.INSTANCE.GetConsoleMode(consoleIn, mode);
|
|
87 |
return mode.getValue();
|
|
88 |
}
|
|
89 |
|
|
90 |
@Override
|
|
91 |
protected void setConsoleMode(int mode) {
|
|
92 |
Kernel32.INSTANCE.SetConsoleMode(consoleIn, mode);
|
|
93 |
}
|
|
94 |
|
|
95 |
public Size getSize() {
|
|
96 |
Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO();
|
|
97 |
Kernel32.INSTANCE.GetConsoleScreenBufferInfo(consoleOut, info);
|
58903
|
98 |
return new Size(info.windowWidth(), info.windowHeight());
|
|
99 |
}
|
|
100 |
|
|
101 |
public Size getBufferSize() {
|
|
102 |
Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO();
|
|
103 |
Kernel32.INSTANCE.GetConsoleScreenBufferInfo(consoleOut, info);
|
52938
|
104 |
return new Size(info.dwSize.X, info.dwSize.Y);
|
|
105 |
}
|
|
106 |
|
|
107 |
protected boolean processConsoleInput() throws IOException {
|
|
108 |
Kernel32.INPUT_RECORD event = readConsoleInput(100);
|
|
109 |
if (event == null) {
|
|
110 |
return false;
|
|
111 |
}
|
|
112 |
|
|
113 |
switch (event.EventType) {
|
|
114 |
case Kernel32.INPUT_RECORD.KEY_EVENT:
|
|
115 |
processKeyEvent(event.Event.KeyEvent);
|
|
116 |
return true;
|
|
117 |
case Kernel32.INPUT_RECORD.WINDOW_BUFFER_SIZE_EVENT:
|
|
118 |
raise(Signal.WINCH);
|
|
119 |
return false;
|
|
120 |
case Kernel32.INPUT_RECORD.MOUSE_EVENT:
|
|
121 |
processMouseEvent(event.Event.MouseEvent);
|
|
122 |
return true;
|
|
123 |
case Kernel32.INPUT_RECORD.FOCUS_EVENT:
|
|
124 |
processFocusEvent(event.Event.FocusEvent.bSetFocus);
|
|
125 |
return true;
|
|
126 |
default:
|
|
127 |
// Skip event
|
|
128 |
return false;
|
|
129 |
}
|
|
130 |
}
|
|
131 |
|
|
132 |
private void processKeyEvent(Kernel32.KEY_EVENT_RECORD keyEvent) throws IOException {
|
|
133 |
processKeyEvent(keyEvent.bKeyDown, keyEvent.wVirtualKeyCode, keyEvent.uChar.UnicodeChar, keyEvent.dwControlKeyState);
|
|
134 |
}
|
|
135 |
|
|
136 |
private char[] focus = new char[] { '\033', '[', ' ' };
|
|
137 |
|
|
138 |
private void processFocusEvent(boolean hasFocus) throws IOException {
|
|
139 |
if (focusTracking) {
|
|
140 |
focus[2] = hasFocus ? 'I' : 'O';
|
|
141 |
slaveInputPipe.write(focus);
|
|
142 |
}
|
|
143 |
}
|
|
144 |
|
|
145 |
private char[] mouse = new char[] { '\033', '[', 'M', ' ', ' ', ' ' };
|
|
146 |
|
|
147 |
private void processMouseEvent(Kernel32.MOUSE_EVENT_RECORD mouseEvent) throws IOException {
|
|
148 |
int dwEventFlags = mouseEvent.dwEventFlags;
|
|
149 |
int dwButtonState = mouseEvent.dwButtonState;
|
|
150 |
if (tracking == MouseTracking.Off
|
|
151 |
|| tracking == MouseTracking.Normal && dwEventFlags == Kernel32.MOUSE_MOVED
|
|
152 |
|| tracking == MouseTracking.Button && dwEventFlags == Kernel32.MOUSE_MOVED && dwButtonState == 0) {
|
|
153 |
return;
|
|
154 |
}
|
|
155 |
int cb = 0;
|
|
156 |
dwEventFlags &= ~ Kernel32.DOUBLE_CLICK; // Treat double-clicks as normal
|
|
157 |
if (dwEventFlags == Kernel32.MOUSE_WHEELED) {
|
|
158 |
cb |= 64;
|
|
159 |
if ((dwButtonState >> 16) < 0) {
|
|
160 |
cb |= 1;
|
|
161 |
}
|
|
162 |
} else if (dwEventFlags == Kernel32.MOUSE_HWHEELED) {
|
|
163 |
return;
|
|
164 |
} else if ((dwButtonState & Kernel32.FROM_LEFT_1ST_BUTTON_PRESSED) != 0) {
|
|
165 |
cb |= 0x00;
|
|
166 |
} else if ((dwButtonState & Kernel32.RIGHTMOST_BUTTON_PRESSED) != 0) {
|
|
167 |
cb |= 0x01;
|
|
168 |
} else if ((dwButtonState & Kernel32.FROM_LEFT_2ND_BUTTON_PRESSED) != 0) {
|
|
169 |
cb |= 0x02;
|
|
170 |
} else {
|
|
171 |
cb |= 0x03;
|
|
172 |
}
|
|
173 |
int cx = mouseEvent.dwMousePosition.X;
|
|
174 |
int cy = mouseEvent.dwMousePosition.Y;
|
|
175 |
mouse[3] = (char) (' ' + cb);
|
|
176 |
mouse[4] = (char) (' ' + cx + 1);
|
|
177 |
mouse[5] = (char) (' ' + cy + 1);
|
|
178 |
slaveInputPipe.write(mouse);
|
|
179 |
}
|
|
180 |
|
|
181 |
private final Kernel32.INPUT_RECORD[] inputEvents = new Kernel32.INPUT_RECORD[1];
|
|
182 |
private final IntByReference eventsRead = new IntByReference();
|
|
183 |
|
|
184 |
private Kernel32.INPUT_RECORD readConsoleInput(int dwMilliseconds) throws IOException {
|
|
185 |
if (Kernel32.INSTANCE.WaitForSingleObject(consoleIn, dwMilliseconds) != 0) {
|
|
186 |
return null;
|
|
187 |
}
|
|
188 |
Kernel32.INSTANCE.ReadConsoleInput(consoleIn, inputEvents, 1, eventsRead);
|
|
189 |
if (eventsRead.getValue() == 1) {
|
|
190 |
return inputEvents[0];
|
|
191 |
} else {
|
|
192 |
return null;
|
|
193 |
}
|
|
194 |
}
|
|
195 |
|
|
196 |
@Override
|
|
197 |
public Cursor getCursorPosition(IntConsumer discarded) {
|
|
198 |
Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO();
|
|
199 |
Kernel32.INSTANCE.GetConsoleScreenBufferInfo(consoleOut, info);
|
|
200 |
return new Cursor(info.dwCursorPosition.X, info.dwCursorPosition.Y);
|
|
201 |
}
|
|
202 |
|
|
203 |
}
|