author | jlahoda |
Wed, 20 Feb 2019 11:11:38 +0100 | |
changeset 53840 | 9a0fd1f82406 |
parent 52938 | 5ff7480c9e28 |
child 58903 | eeb1c0da2126 |
permissions | -rw-r--r-- |
52938 | 1 |
/* |
2 |
* Copyright (c) 2002-2018, 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.org.jline.terminal.impl; |
|
10 |
||
11 |
import jdk.internal.org.jline.terminal.Attributes; |
|
12 |
import jdk.internal.org.jline.terminal.Size; |
|
13 |
import jdk.internal.org.jline.utils.Curses; |
|
14 |
import jdk.internal.org.jline.utils.InfoCmp; |
|
15 |
import jdk.internal.org.jline.utils.Log; |
|
16 |
import jdk.internal.org.jline.utils.NonBlocking; |
|
17 |
import jdk.internal.org.jline.utils.NonBlockingInputStream; |
|
18 |
import jdk.internal.org.jline.utils.NonBlockingPumpReader; |
|
19 |
import jdk.internal.org.jline.utils.NonBlockingReader; |
|
20 |
import jdk.internal.org.jline.utils.ShutdownHooks; |
|
21 |
import jdk.internal.org.jline.utils.Signals; |
|
22 |
import jdk.internal.org.jline.utils.WriterOutputStream; |
|
23 |
||
24 |
import java.io.IOError; |
|
25 |
import java.io.IOException; |
|
26 |
import java.io.InputStream; |
|
27 |
import java.io.OutputStream; |
|
28 |
import java.io.PrintWriter; |
|
29 |
import java.io.StringWriter; |
|
30 |
import java.io.Writer; |
|
31 |
import java.nio.charset.Charset; |
|
32 |
import java.nio.charset.StandardCharsets; |
|
33 |
import java.util.HashMap; |
|
34 |
import java.util.Map; |
|
35 |
import java.util.function.Function; |
|
36 |
||
37 |
/** |
|
38 |
* The AbstractWindowsTerminal is used as the base class for windows terminal. |
|
39 |
* Due to windows limitations, mostly the missing support for ansi sequences, |
|
40 |
* the only way to create a correct terminal is to use the windows api to set |
|
41 |
* character attributes, move the cursor, erasing, etc... |
|
42 |
* |
|
43 |
* UTF-8 support is also lacking in windows and the code page supposed to |
|
44 |
* emulate UTF-8 is a bit broken. In order to work around this broken |
|
45 |
* code page, windows api WriteConsoleW is used directly. This means that |
|
46 |
* the writer() becomes the primary output, while the output() is bridged |
|
47 |
* to the writer() using a WriterOutputStream wrapper. |
|
48 |
*/ |
|
49 |
public abstract class AbstractWindowsTerminal extends AbstractTerminal { |
|
50 |
||
51 |
public static final String TYPE_WINDOWS = "windows"; |
|
52 |
public static final String TYPE_WINDOWS_256_COLOR = "windows-256color"; |
|
53 |
public static final String TYPE_WINDOWS_VTP = "windows-vtp"; |
|
54 |
||
55 |
public static final int ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; |
|
56 |
||
57 |
private static final int UTF8_CODE_PAGE = 65001; |
|
58 |
||
59 |
protected static final int ENABLE_PROCESSED_INPUT = 0x0001; |
|
60 |
protected static final int ENABLE_LINE_INPUT = 0x0002; |
|
61 |
protected static final int ENABLE_ECHO_INPUT = 0x0004; |
|
62 |
protected static final int ENABLE_WINDOW_INPUT = 0x0008; |
|
63 |
protected static final int ENABLE_MOUSE_INPUT = 0x0010; |
|
64 |
protected static final int ENABLE_INSERT_MODE = 0x0020; |
|
65 |
protected static final int ENABLE_QUICK_EDIT_MODE = 0x0040; |
|
66 |
||
67 |
protected final Writer slaveInputPipe; |
|
68 |
protected final InputStream input; |
|
69 |
protected final OutputStream output; |
|
70 |
protected final NonBlockingReader reader; |
|
71 |
protected final PrintWriter writer; |
|
72 |
protected final Map<Signal, Object> nativeHandlers = new HashMap<>(); |
|
73 |
protected final ShutdownHooks.Task closer; |
|
74 |
protected final Attributes attributes = new Attributes(); |
|
75 |
protected final int originalConsoleMode; |
|
76 |
||
77 |
protected final Object lock = new Object(); |
|
78 |
protected boolean paused = true; |
|
79 |
protected Thread pump; |
|
80 |
||
81 |
protected MouseTracking tracking = MouseTracking.Off; |
|
82 |
protected boolean focusTracking = false; |
|
83 |
private volatile boolean closing; |
|
84 |
||
85 |
public AbstractWindowsTerminal(Writer writer, String name, String type, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler, Function<InputStream, InputStream> inputStreamWrapper) throws IOException { |
|
86 |
super(name, type, selectCharset(encoding, codepage), signalHandler); |
|
87 |
NonBlockingPumpReader reader = NonBlocking.nonBlockingPumpReader(); |
|
88 |
this.slaveInputPipe = reader.getWriter(); |
|
89 |
this.input = inputStreamWrapper.apply(NonBlocking.nonBlockingStream(reader, encoding())); |
|
53840
9a0fd1f82406
8218287: jshell tool: input behavior unstable after 12-ea+24 on Windows
jlahoda
parents:
52938
diff
changeset
|
90 |
this.reader = NonBlocking.nonBlocking(name, input, encoding()); |
52938 | 91 |
this.writer = new PrintWriter(writer); |
92 |
this.output = new WriterOutputStream(writer, encoding()); |
|
93 |
parseInfoCmp(); |
|
94 |
// Attributes |
|
95 |
originalConsoleMode = getConsoleMode(); |
|
96 |
attributes.setLocalFlag(Attributes.LocalFlag.ISIG, true); |
|
97 |
attributes.setControlChar(Attributes.ControlChar.VINTR, ctrl('C')); |
|
98 |
attributes.setControlChar(Attributes.ControlChar.VEOF, ctrl('D')); |
|
99 |
attributes.setControlChar(Attributes.ControlChar.VSUSP, ctrl('Z')); |
|
100 |
// Handle signals |
|
101 |
if (nativeSignals) { |
|
102 |
for (final Signal signal : Signal.values()) { |
|
103 |
if (signalHandler == SignalHandler.SIG_DFL) { |
|
104 |
nativeHandlers.put(signal, Signals.registerDefault(signal.name())); |
|
105 |
} else { |
|
106 |
nativeHandlers.put(signal, Signals.register(signal.name(), () -> raise(signal))); |
|
107 |
} |
|
108 |
} |
|
109 |
} |
|
110 |
closer = this::close; |
|
111 |
ShutdownHooks.add(closer); |
|
112 |
// ConEMU extended fonts support |
|
113 |
if (TYPE_WINDOWS_256_COLOR.equals(getType()) |
|
114 |
&& !Boolean.getBoolean("org.jline.terminal.conemu.disable-activate")) { |
|
115 |
writer.write("\u001b[9999E"); |
|
116 |
writer.flush(); |
|
117 |
} |
|
118 |
} |
|
119 |
||
120 |
private static Charset selectCharset(Charset encoding, int codepage) { |
|
121 |
if (encoding != null) { |
|
122 |
return encoding; |
|
123 |
} |
|
124 |
||
125 |
if (codepage >= 0) { |
|
126 |
return getCodepageCharset(codepage); |
|
127 |
} |
|
128 |
||
129 |
// Use UTF-8 as default |
|
130 |
return StandardCharsets.UTF_8; |
|
131 |
} |
|
132 |
||
133 |
private static Charset getCodepageCharset(int codepage) { |
|
134 |
//http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html |
|
135 |
if (codepage == UTF8_CODE_PAGE) { |
|
136 |
return StandardCharsets.UTF_8; |
|
137 |
} |
|
138 |
String charsetMS = "ms" + codepage; |
|
139 |
if (Charset.isSupported(charsetMS)) { |
|
140 |
return Charset.forName(charsetMS); |
|
141 |
} |
|
142 |
String charsetCP = "cp" + codepage; |
|
143 |
if (Charset.isSupported(charsetCP)) { |
|
144 |
return Charset.forName(charsetCP); |
|
145 |
} |
|
146 |
return Charset.defaultCharset(); |
|
147 |
} |
|
148 |
||
149 |
@Override |
|
150 |
public SignalHandler handle(Signal signal, SignalHandler handler) { |
|
151 |
SignalHandler prev = super.handle(signal, handler); |
|
152 |
if (prev != handler) { |
|
153 |
if (handler == SignalHandler.SIG_DFL) { |
|
154 |
Signals.registerDefault(signal.name()); |
|
155 |
} else { |
|
156 |
Signals.register(signal.name(), () -> raise(signal)); |
|
157 |
} |
|
158 |
} |
|
159 |
return prev; |
|
160 |
} |
|
161 |
||
162 |
public NonBlockingReader reader() { |
|
163 |
return reader; |
|
164 |
} |
|
165 |
||
166 |
public PrintWriter writer() { |
|
167 |
return writer; |
|
168 |
} |
|
169 |
||
170 |
@Override |
|
171 |
public InputStream input() { |
|
172 |
return input; |
|
173 |
} |
|
174 |
||
175 |
@Override |
|
176 |
public OutputStream output() { |
|
177 |
return output; |
|
178 |
} |
|
179 |
||
180 |
public Attributes getAttributes() { |
|
181 |
int mode = getConsoleMode(); |
|
182 |
if ((mode & ENABLE_ECHO_INPUT) != 0) { |
|
183 |
attributes.setLocalFlag(Attributes.LocalFlag.ECHO, true); |
|
184 |
} |
|
185 |
if ((mode & ENABLE_LINE_INPUT) != 0) { |
|
186 |
attributes.setLocalFlag(Attributes.LocalFlag.ICANON, true); |
|
187 |
} |
|
188 |
return new Attributes(attributes); |
|
189 |
} |
|
190 |
||
191 |
public void setAttributes(Attributes attr) { |
|
192 |
attributes.copy(attr); |
|
193 |
updateConsoleMode(); |
|
194 |
} |
|
195 |
||
196 |
protected void updateConsoleMode() { |
|
197 |
int mode = ENABLE_WINDOW_INPUT; |
|
198 |
if (attributes.getLocalFlag(Attributes.LocalFlag.ECHO)) { |
|
199 |
mode |= ENABLE_ECHO_INPUT; |
|
200 |
} |
|
201 |
if (attributes.getLocalFlag(Attributes.LocalFlag.ICANON)) { |
|
202 |
mode |= ENABLE_LINE_INPUT; |
|
203 |
} |
|
204 |
if (tracking != MouseTracking.Off) { |
|
205 |
mode |= ENABLE_MOUSE_INPUT; |
|
206 |
} |
|
207 |
setConsoleMode(mode); |
|
208 |
} |
|
209 |
||
210 |
protected int ctrl(char key) { |
|
211 |
return (Character.toUpperCase(key) & 0x1f); |
|
212 |
} |
|
213 |
||
214 |
public void setSize(Size size) { |
|
215 |
throw new UnsupportedOperationException("Can not resize windows terminal"); |
|
216 |
} |
|
217 |
||
218 |
public void close() throws IOException { |
|
219 |
super.close(); |
|
220 |
closing = true; |
|
221 |
pump.interrupt(); |
|
222 |
ShutdownHooks.remove(closer); |
|
223 |
for (Map.Entry<Signal, Object> entry : nativeHandlers.entrySet()) { |
|
224 |
Signals.unregister(entry.getKey().name(), entry.getValue()); |
|
225 |
} |
|
226 |
reader.close(); |
|
227 |
writer.close(); |
|
228 |
setConsoleMode(originalConsoleMode); |
|
229 |
} |
|
230 |
||
231 |
static final int SHIFT_FLAG = 0x01; |
|
232 |
static final int ALT_FLAG = 0x02; |
|
233 |
static final int CTRL_FLAG = 0x04; |
|
234 |
||
235 |
static final int RIGHT_ALT_PRESSED = 0x0001; |
|
236 |
static final int LEFT_ALT_PRESSED = 0x0002; |
|
237 |
static final int RIGHT_CTRL_PRESSED = 0x0004; |
|
238 |
static final int LEFT_CTRL_PRESSED = 0x0008; |
|
239 |
static final int SHIFT_PRESSED = 0x0010; |
|
240 |
static final int NUMLOCK_ON = 0x0020; |
|
241 |
static final int SCROLLLOCK_ON = 0x0040; |
|
242 |
static final int CAPSLOCK_ON = 0x0080; |
|
243 |
||
244 |
protected void processKeyEvent(final boolean isKeyDown, final short virtualKeyCode, char ch, final int controlKeyState) throws IOException { |
|
245 |
final boolean isCtrl = (controlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) > 0; |
|
246 |
final boolean isAlt = (controlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) > 0; |
|
247 |
final boolean isShift = (controlKeyState & SHIFT_PRESSED) > 0; |
|
248 |
// key down event |
|
249 |
if (isKeyDown && ch != '\3') { |
|
250 |
// Pressing "Alt Gr" is translated to Alt-Ctrl, hence it has to be checked that Ctrl is _not_ pressed, |
|
251 |
// otherwise inserting of "Alt Gr" codes on non-US keyboards would yield errors |
|
252 |
if (ch != 0 |
|
253 |
&& (controlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED | SHIFT_PRESSED)) |
|
254 |
== (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)) { |
|
255 |
processInputChar(ch); |
|
256 |
} else { |
|
257 |
final String keySeq = getEscapeSequence(virtualKeyCode, (isCtrl ? CTRL_FLAG : 0) + (isAlt ? ALT_FLAG : 0) + (isShift ? SHIFT_FLAG : 0)); |
|
258 |
if (keySeq != null) { |
|
259 |
for (char c : keySeq.toCharArray()) { |
|
260 |
processInputChar(c); |
|
261 |
} |
|
262 |
return; |
|
263 |
} |
|
264 |
/* uchar value in Windows when CTRL is pressed: |
|
265 |
* 1). Ctrl + <0x41 to 0x5e> : uchar=<keyCode> - 'A' + 1 |
|
266 |
* 2). Ctrl + Backspace(0x08) : uchar=0x7f |
|
267 |
* 3). Ctrl + Enter(0x0d) : uchar=0x0a |
|
268 |
* 4). Ctrl + Space(0x20) : uchar=0x20 |
|
269 |
* 5). Ctrl + <Other key> : uchar=0 |
|
270 |
* 6). Ctrl + Alt + <Any key> : uchar=0 |
|
271 |
*/ |
|
272 |
if (ch > 0) { |
|
273 |
if (isAlt) { |
|
274 |
processInputChar('\033'); |
|
275 |
} |
|
276 |
if (isCtrl && ch != ' ' && ch != '\n' && ch != 0x7f) { |
|
277 |
processInputChar((char) (ch == '?' ? 0x7f : Character.toUpperCase(ch) & 0x1f)); |
|
278 |
} else if (isCtrl && ch == '\n') { |
|
279 |
//simulate Alt-Enter: |
|
280 |
processInputChar('\033'); |
|
281 |
processInputChar('\r'); |
|
282 |
} else { |
|
283 |
processInputChar(ch); |
|
284 |
} |
|
285 |
} else if (isCtrl) { //Handles the ctrl key events(uchar=0) |
|
286 |
if (virtualKeyCode >= 'A' && virtualKeyCode <= 'Z') { |
|
287 |
ch = (char) (virtualKeyCode - 0x40); |
|
288 |
} else if (virtualKeyCode == 191) { //? |
|
289 |
ch = 127; |
|
290 |
} |
|
291 |
if (ch > 0) { |
|
292 |
if (isAlt) { |
|
293 |
processInputChar('\033'); |
|
294 |
} |
|
295 |
processInputChar(ch); |
|
296 |
} |
|
297 |
} |
|
298 |
} |
|
299 |
} else if (isKeyDown && ch == '\3') { |
|
300 |
processInputChar('\3'); |
|
301 |
} |
|
302 |
// key up event |
|
303 |
else { |
|
304 |
// support ALT+NumPad input method |
|
305 |
if (virtualKeyCode == 0x12 /*VK_MENU ALT key*/ && ch > 0) { |
|
306 |
processInputChar(ch); // no such combination in Windows |
|
307 |
} |
|
308 |
} |
|
309 |
} |
|
310 |
||
311 |
protected String getEscapeSequence(short keyCode, int keyState) { |
|
312 |
// virtual keycodes: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx |
|
313 |
// TODO: numpad keys, modifiers |
|
314 |
String escapeSequence = null; |
|
315 |
switch (keyCode) { |
|
316 |
case 0x08: // VK_BACK BackSpace |
|
317 |
escapeSequence = (keyState & ALT_FLAG) > 0 ? "\\E^H" : getRawSequence(InfoCmp.Capability.key_backspace); |
|
318 |
break; |
|
319 |
case 0x09: |
|
320 |
escapeSequence = (keyState & SHIFT_FLAG) > 0 ? getRawSequence(InfoCmp.Capability.key_btab) : null; |
|
321 |
break; |
|
322 |
case 0x21: // VK_PRIOR PageUp |
|
323 |
escapeSequence = getRawSequence(InfoCmp.Capability.key_ppage); |
|
324 |
break; |
|
325 |
case 0x22: // VK_NEXT PageDown |
|
326 |
escapeSequence = getRawSequence(InfoCmp.Capability.key_npage); |
|
327 |
break; |
|
328 |
case 0x23: // VK_END |
|
329 |
escapeSequence = keyState > 0 ? "\\E[1;%p1%dF" : getRawSequence(InfoCmp.Capability.key_end); |
|
330 |
break; |
|
331 |
case 0x24: // VK_HOME |
|
332 |
escapeSequence = keyState > 0 ? "\\E[1;%p1%dH" : getRawSequence(InfoCmp.Capability.key_home); |
|
333 |
break; |
|
334 |
case 0x25: // VK_LEFT |
|
335 |
escapeSequence = keyState > 0 ? "\\E[1;%p1%dD" : getRawSequence(InfoCmp.Capability.key_left); |
|
336 |
break; |
|
337 |
case 0x26: // VK_UP |
|
338 |
escapeSequence = keyState > 0 ? "\\E[1;%p1%dA" : getRawSequence(InfoCmp.Capability.key_up); |
|
339 |
break; |
|
340 |
case 0x27: // VK_RIGHT |
|
341 |
escapeSequence = keyState > 0 ? "\\E[1;%p1%dC" : getRawSequence(InfoCmp.Capability.key_right); |
|
342 |
break; |
|
343 |
case 0x28: // VK_DOWN |
|
344 |
escapeSequence = keyState > 0 ? "\\E[1;%p1%dB" : getRawSequence(InfoCmp.Capability.key_down); |
|
345 |
break; |
|
346 |
case 0x2D: // VK_INSERT |
|
347 |
escapeSequence = getRawSequence(InfoCmp.Capability.key_ic); |
|
348 |
break; |
|
349 |
case 0x2E: // VK_DELETE |
|
350 |
escapeSequence = getRawSequence(InfoCmp.Capability.key_dc); |
|
351 |
break; |
|
352 |
case 0x70: // VK_F1 |
|
353 |
escapeSequence = keyState > 0 ? "\\E[1;%p1%dP" : getRawSequence(InfoCmp.Capability.key_f1); |
|
354 |
break; |
|
355 |
case 0x71: // VK_F2 |
|
356 |
escapeSequence = keyState > 0 ? "\\E[1;%p1%dQ" : getRawSequence(InfoCmp.Capability.key_f2); |
|
357 |
break; |
|
358 |
case 0x72: // VK_F3 |
|
359 |
escapeSequence = keyState > 0 ? "\\E[1;%p1%dR" : getRawSequence(InfoCmp.Capability.key_f3); |
|
360 |
break; |
|
361 |
case 0x73: // VK_F4 |
|
362 |
escapeSequence = keyState > 0 ? "\\E[1;%p1%dS" : getRawSequence(InfoCmp.Capability.key_f4); |
|
363 |
break; |
|
364 |
case 0x74: // VK_F5 |
|
365 |
escapeSequence = keyState > 0 ? "\\E[15;%p1%d~" : getRawSequence(InfoCmp.Capability.key_f5); |
|
366 |
break; |
|
367 |
case 0x75: // VK_F6 |
|
368 |
escapeSequence = keyState > 0 ? "\\E[17;%p1%d~" : getRawSequence(InfoCmp.Capability.key_f6); |
|
369 |
break; |
|
370 |
case 0x76: // VK_F7 |
|
371 |
escapeSequence = keyState > 0 ? "\\E[18;%p1%d~" : getRawSequence(InfoCmp.Capability.key_f7); |
|
372 |
break; |
|
373 |
case 0x77: // VK_F8 |
|
374 |
escapeSequence = keyState > 0 ? "\\E[19;%p1%d~" : getRawSequence(InfoCmp.Capability.key_f8); |
|
375 |
break; |
|
376 |
case 0x78: // VK_F9 |
|
377 |
escapeSequence = keyState > 0 ? "\\E[20;%p1%d~" : getRawSequence(InfoCmp.Capability.key_f9); |
|
378 |
break; |
|
379 |
case 0x79: // VK_F10 |
|
380 |
escapeSequence = keyState > 0 ? "\\E[21;%p1%d~" : getRawSequence(InfoCmp.Capability.key_f10); |
|
381 |
break; |
|
382 |
case 0x7A: // VK_F11 |
|
383 |
escapeSequence = keyState > 0 ? "\\E[23;%p1%d~" : getRawSequence(InfoCmp.Capability.key_f11); |
|
384 |
break; |
|
385 |
case 0x7B: // VK_F12 |
|
386 |
escapeSequence = keyState > 0 ? "\\E[24;%p1%d~" : getRawSequence(InfoCmp.Capability.key_f12); |
|
387 |
break; |
|
388 |
case 0x5D: // VK_CLOSE_BRACKET(Menu key) |
|
389 |
case 0x5B: // VK_OPEN_BRACKET(Window key) |
|
390 |
default: |
|
391 |
return null; |
|
392 |
} |
|
393 |
return Curses.tputs(escapeSequence, keyState + 1); |
|
394 |
} |
|
395 |
||
396 |
protected String getRawSequence(InfoCmp.Capability cap) { |
|
397 |
return strings.get(cap); |
|
398 |
} |
|
399 |
||
400 |
@Override |
|
401 |
public boolean hasFocusSupport() { |
|
402 |
return true; |
|
403 |
} |
|
404 |
||
405 |
@Override |
|
406 |
public boolean trackFocus(boolean tracking) { |
|
407 |
focusTracking = tracking; |
|
408 |
return true; |
|
409 |
} |
|
410 |
||
411 |
@Override |
|
412 |
public boolean canPauseResume() { |
|
413 |
return true; |
|
414 |
} |
|
415 |
||
416 |
@Override |
|
417 |
public void pause() { |
|
418 |
synchronized (lock) { |
|
419 |
paused = true; |
|
420 |
} |
|
421 |
} |
|
422 |
||
423 |
@Override |
|
424 |
public void pause(boolean wait) throws InterruptedException { |
|
425 |
Thread p; |
|
426 |
synchronized (lock) { |
|
427 |
paused = true; |
|
428 |
p = pump; |
|
429 |
} |
|
430 |
if (p != null) { |
|
431 |
p.interrupt(); |
|
432 |
p.join(); |
|
433 |
} |
|
434 |
} |
|
435 |
||
436 |
@Override |
|
437 |
public void resume() { |
|
438 |
synchronized (lock) { |
|
439 |
paused = false; |
|
440 |
if (pump == null) { |
|
441 |
pump = new Thread(this::pump, "WindowsStreamPump"); |
|
442 |
pump.setDaemon(true); |
|
443 |
pump.start(); |
|
444 |
} |
|
445 |
} |
|
446 |
} |
|
447 |
||
448 |
@Override |
|
449 |
public boolean paused() { |
|
450 |
synchronized (lock) { |
|
451 |
return paused; |
|
452 |
} |
|
453 |
} |
|
454 |
||
455 |
protected void pump() { |
|
456 |
try { |
|
457 |
while (!closing) { |
|
458 |
synchronized (lock) { |
|
459 |
if (paused) { |
|
460 |
pump = null; |
|
461 |
break; |
|
462 |
} |
|
463 |
} |
|
464 |
if (processConsoleInput()) { |
|
465 |
slaveInputPipe.flush(); |
|
466 |
} |
|
467 |
} |
|
468 |
} catch (IOException e) { |
|
469 |
if (!closing) { |
|
470 |
Log.warn("Error in WindowsStreamPump", e); |
|
471 |
try { |
|
472 |
close(); |
|
473 |
} catch (IOException e1) { |
|
474 |
Log.warn("Error closing terminal", e); |
|
475 |
} |
|
476 |
} |
|
477 |
} finally { |
|
478 |
synchronized (lock) { |
|
479 |
pump = null; |
|
480 |
} |
|
481 |
} |
|
482 |
} |
|
483 |
||
484 |
public void processInputChar(char c) throws IOException { |
|
485 |
if (attributes.getLocalFlag(Attributes.LocalFlag.ISIG)) { |
|
486 |
if (c == attributes.getControlChar(Attributes.ControlChar.VINTR)) { |
|
487 |
raise(Signal.INT); |
|
488 |
return; |
|
489 |
} else if (c == attributes.getControlChar(Attributes.ControlChar.VQUIT)) { |
|
490 |
raise(Signal.QUIT); |
|
491 |
return; |
|
492 |
} else if (c == attributes.getControlChar(Attributes.ControlChar.VSUSP)) { |
|
493 |
raise(Signal.TSTP); |
|
494 |
return; |
|
495 |
} else if (c == attributes.getControlChar(Attributes.ControlChar.VSTATUS)) { |
|
496 |
raise(Signal.INFO); |
|
497 |
} |
|
498 |
} |
|
499 |
if (c == '\r') { |
|
500 |
if (attributes.getInputFlag(Attributes.InputFlag.IGNCR)) { |
|
501 |
return; |
|
502 |
} |
|
503 |
if (attributes.getInputFlag(Attributes.InputFlag.ICRNL)) { |
|
504 |
c = '\n'; |
|
505 |
} |
|
506 |
} else if (c == '\n' && attributes.getInputFlag(Attributes.InputFlag.INLCR)) { |
|
507 |
c = '\r'; |
|
508 |
} |
|
509 |
// if (attributes.getLocalFlag(Attributes.LocalFlag.ECHO)) { |
|
510 |
// processOutputByte(c); |
|
511 |
// masterOutput.flush(); |
|
512 |
// } |
|
513 |
slaveInputPipe.write(c); |
|
514 |
} |
|
515 |
||
516 |
@Override |
|
517 |
public boolean trackMouse(MouseTracking tracking) { |
|
518 |
this.tracking = tracking; |
|
519 |
updateConsoleMode(); |
|
520 |
return true; |
|
521 |
} |
|
522 |
||
523 |
protected abstract int getConsoleOutputCP(); |
|
524 |
||
525 |
protected abstract int getConsoleMode(); |
|
526 |
||
527 |
protected abstract void setConsoleMode(int mode); |
|
528 |
||
529 |
/** |
|
530 |
* Read a single input event from the input buffer and process it. |
|
531 |
* |
|
532 |
* @return true if new input was generated from the event |
|
533 |
* @throws IOException if anything wrong happens |
|
534 |
*/ |
|
535 |
protected abstract boolean processConsoleInput() throws IOException; |
|
536 |
||
537 |
} |
|
538 |