Skip to content

Commit 4082090

Browse files
committed
fix for GRAILS-9144 "Interactive mode on Windows is broken"
1 parent e72418e commit 4082090

File tree

2 files changed

+95
-12
lines changed

2 files changed

+95
-12
lines changed

grails-bootstrap/src/main/groovy/grails/build/logging/GrailsConsole.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -175,20 +175,12 @@ protected ConsoleReader createConsoleReader() throws IOException {
175175
* is controlled by the jline.terminal system property.
176176
*/
177177
protected Terminal createTerminal() {
178-
@SuppressWarnings("hiding") Terminal terminal;
179178
if (isWindows()) {
180-
terminal = new WindowsTerminal() {
181-
@Override
182-
public boolean isANSISupported() {
183-
return true;
184-
}
185-
};
186179
try {
187-
terminal.initializeTerminal();
188-
terminal.enableEcho();
189-
fixCtrlC();
190-
} catch (Exception e) {
191-
terminal = new UnsupportedTerminal();
180+
return PatchedJLineWindowsTerminal.setupTerminal(reader);
181+
}
182+
catch (Exception ex) {
183+
error(ex);
192184
}
193185
}
194186
else {
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package grails.build.logging;
2+
3+
import jline.ConsoleReader;
4+
import jline.Terminal;
5+
import jline.WindowsTerminal;
6+
7+
import java.awt.event.ActionEvent;
8+
import java.awt.event.ActionListener;
9+
import java.lang.reflect.Field;
10+
import java.lang.reflect.InvocationTargetException;
11+
import java.lang.reflect.Method;
12+
13+
public final class PatchedJLineWindowsTerminal extends WindowsTerminal {
14+
private static final int ENABLE_PROCESSED_INPUT = 1;
15+
private static final int ENABLE_WINDOW_INPUT = 8;
16+
private final ConsoleReader reader;
17+
18+
public PatchedJLineWindowsTerminal(ConsoleReader reader) {
19+
super();
20+
this.reader = reader;
21+
}
22+
23+
public static Terminal setupTerminal(ConsoleReader reader) throws Exception {
24+
final Terminal terminal = new PatchedJLineWindowsTerminal(reader);
25+
terminal.initializeTerminal();
26+
return terminal;
27+
}
28+
29+
@Override
30+
public void initializeTerminal() throws Exception {
31+
super.initializeTerminal();
32+
setConsoleModeHacked(getConsoleModeHacked() | ENABLE_PROCESSED_INPUT | ENABLE_WINDOW_INPUT);
33+
fixCtrlC(reader);
34+
}
35+
36+
@Override
37+
public boolean isANSISupported() {
38+
return true;
39+
}
40+
41+
private Integer getConsoleModeHacked() {
42+
return (Integer) invokePrivateMethod(WindowsTerminal.class, this, "getConsoleMode", null);
43+
}
44+
45+
private void setConsoleModeHacked(Integer mode) {
46+
invokePrivateMethod(WindowsTerminal.class, this, "setConsoleMode", new Object[]{mode});
47+
}
48+
49+
private void fixCtrlC(ConsoleReader reader) {
50+
if (reader == null) {
51+
return;
52+
}
53+
54+
// hack to workaround JLine bug - see https://issues.apache.org/jira/browse/GERONIMO-3978 for source of fix
55+
try {
56+
Field f = ConsoleReader.class.getDeclaredField("keybindings");
57+
f.setAccessible(true);
58+
short[] keybindings = (short[]) f.get(reader);
59+
if (keybindings[3] == Terminal.INSERT) {
60+
keybindings[3] = Terminal.CTRL_C;
61+
}
62+
} catch (Exception ignored) {
63+
// shouldn't happen
64+
}
65+
66+
// CTRL+C before interactive mode is running
67+
reader.addTriggeredAction(Terminal.CTRL_C, new ActionListener() {
68+
public void actionPerformed(ActionEvent e) {
69+
System.exit(0);
70+
}
71+
});
72+
}
73+
74+
private Object invokePrivateMethod(Class clazz, Object o, String methodName, Object[] params) {
75+
final Method methods[] = clazz.getDeclaredMethods();
76+
for (int i = 0; i < methods.length; ++i) {
77+
if (methodName.equals(methods[i].getName())) {
78+
try {
79+
methods[i].setAccessible(true);
80+
return methods[i].invoke(o, params);
81+
} catch (IllegalAccessException ex) {
82+
throw new RuntimeException("IllegalAccessException accessing " + methodName);
83+
} catch (InvocationTargetException ite) {
84+
throw new RuntimeException("InvocationTargetException accessing " + methodName);
85+
}
86+
}
87+
}
88+
throw new RuntimeException("Method '" + methodName + "' not found");
89+
}
90+
91+
}

0 commit comments

Comments
 (0)