Commit | Line | Data |
---|---|---|
a3b510ab NR |
1 | /* |
2 | * This file is part of lanterna (http://code.google.com/p/lanterna/). | |
3 | * | |
4 | * lanterna is free software: you can redistribute it and/or modify | |
5 | * it under the terms of the GNU Lesser General Public License as published by | |
6 | * the Free Software Foundation, either version 3 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU Lesser General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU Lesser General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | * | |
17 | * Copyright (C) 2010-2015 Martin | |
18 | */ | |
19 | package com.googlecode.lanterna.terminal.ansi; | |
20 | ||
21 | import com.googlecode.lanterna.TerminalSize; | |
22 | ||
23 | import java.io.*; | |
24 | import java.nio.charset.Charset; | |
25 | import java.util.ArrayList; | |
26 | import java.util.Arrays; | |
27 | import java.util.List; | |
28 | import java.util.regex.Matcher; | |
29 | import java.util.regex.Pattern; | |
30 | ||
31 | /** | |
32 | * This class extends UnixLikeTerminal and implements the Cygwin-specific implementations. This means, running a Java | |
33 | * application using Lanterna inside the Cygwin Terminal application. The standard Windows command prompt (cmd.exe) is | |
34 | * not supported by this class.<p> | |
35 | * <p> | |
36 | * <b>NOTE:</b> This class is experimental and does not fully work! Some of the operations, like disabling echo and | |
37 | * changing cbreak seems to be impossible to do without resorting to native code. Running "stty raw" before starting the | |
38 | * JVM will improve compatibility. | |
39 | * | |
40 | * @author Martin | |
41 | * @author Andreas | |
42 | */ | |
43 | public class CygwinTerminal extends UnixLikeTerminal { | |
44 | ||
45 | private static final Pattern STTY_SIZE_PATTERN = Pattern.compile(".*rows ([0-9]+);.*columns ([0-9]+);.*"); | |
46 | private static final String STTY_LOCATION = findProgram("stty.exe"); | |
47 | ||
48 | /** | |
49 | * Creates a new CygwinTerminal based off input and output streams and a character set to use | |
50 | * @param terminalInput Input stream to read input from | |
51 | * @param terminalOutput Output stream to write output to | |
52 | * @param terminalCharset Character set to use when writing to the output stream | |
53 | * @throws IOException If there was an I/O error when trying to initialize the class and setup the terminal | |
54 | */ | |
55 | public CygwinTerminal( | |
56 | InputStream terminalInput, | |
57 | OutputStream terminalOutput, | |
58 | Charset terminalCharset) throws IOException { | |
59 | super(terminalInput, terminalOutput, terminalCharset, | |
60 | CtrlCBehaviour.TRAP, null); | |
61 | ||
62 | //Make sure to set an initial size | |
63 | onResized(80, 24); | |
64 | ||
65 | saveSTTY(); | |
66 | setCBreak(true); | |
67 | setEcho(false); | |
68 | sttyMinimum1CharacterForRead(); | |
69 | setupShutdownHook(); | |
70 | } | |
71 | ||
72 | @Override | |
73 | public TerminalSize getTerminalSize() { | |
74 | try { | |
75 | String stty = exec(findSTTY(), "-F", getPseudoTerminalDevice(), "-a"); | |
76 | Matcher matcher = STTY_SIZE_PATTERN.matcher(stty); | |
77 | if(matcher.matches()) { | |
78 | return new TerminalSize(Integer.parseInt(matcher.group(2)), Integer.parseInt(matcher.group(1))); | |
79 | } | |
80 | else { | |
81 | return new TerminalSize(80, 24); | |
82 | } | |
83 | } | |
84 | catch(Throwable e) { | |
85 | return new TerminalSize(80, 24); | |
86 | } | |
87 | } | |
88 | ||
89 | @Override | |
90 | protected void sttyKeyEcho(final boolean enable) throws IOException { | |
91 | runSTTYCommand(enable ? "echo" : "-echo"); | |
92 | } | |
93 | ||
94 | @Override | |
95 | protected void sttyMinimum1CharacterForRead() throws IOException { | |
96 | runSTTYCommand("min", "1"); | |
97 | } | |
98 | ||
99 | @Override | |
100 | protected void sttyICanon(final boolean enable) throws IOException { | |
101 | runSTTYCommand(enable ? "icanon" : "cbreak"); | |
102 | } | |
103 | ||
104 | @Override | |
105 | protected String sttySave() throws IOException { | |
106 | return runSTTYCommand("-g").trim(); | |
107 | } | |
108 | ||
109 | @Override | |
110 | protected void sttyRestore(String tok) throws IOException { | |
111 | runSTTYCommand(tok); | |
112 | } | |
113 | ||
114 | protected String findSTTY() { | |
115 | return STTY_LOCATION; | |
116 | } | |
117 | ||
118 | private String runSTTYCommand(String... parameters) throws IOException { | |
119 | List<String> commandLine = new ArrayList<String>(Arrays.asList( | |
120 | findSTTY(), | |
121 | "-F", | |
122 | getPseudoTerminalDevice())); | |
123 | commandLine.addAll(Arrays.asList(parameters)); | |
124 | return exec(commandLine.toArray(new String[commandLine.size()])); | |
125 | } | |
126 | ||
127 | private String getPseudoTerminalDevice() { | |
128 | //This will only work if you only have one terminal window open, otherwise we'll need to figure out somehow | |
129 | //which pty to use, which could be very tricky... | |
130 | return "/dev/pty0"; | |
131 | } | |
132 | ||
133 | private static String findProgram(String programName) { | |
134 | String[] paths = System.getProperty("java.library.path").split(";"); | |
135 | for(String path : paths) { | |
136 | File shBin = new File(path, programName); | |
137 | if(shBin.exists()) { | |
138 | return shBin.getAbsolutePath(); | |
139 | } | |
140 | } | |
141 | return programName; | |
142 | } | |
143 | ||
144 | } |