]> git.argeo.org Git - lgpl/argeo-commons.git/blob - rcp/org.argeo.swt.minidesktop/src/org/argeo/minidesktop/MiniTerminal.java
Improve BND usage in Makefile
[lgpl/argeo-commons.git] / rcp / org.argeo.swt.minidesktop / src / org / argeo / minidesktop / MiniTerminal.java
1 package org.argeo.minidesktop;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStreamReader;
6 import java.io.OutputStream;
7 import java.net.InetAddress;
8 import java.net.UnknownHostException;
9 import java.nio.charset.Charset;
10 import java.nio.charset.StandardCharsets;
11 import java.nio.file.Files;
12 import java.nio.file.Path;
13 import java.nio.file.Paths;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.List;
17 import java.util.StringTokenizer;
18
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.events.KeyEvent;
21 import org.eclipse.swt.events.KeyListener;
22 import org.eclipse.swt.events.PaintEvent;
23 import org.eclipse.swt.events.PaintListener;
24 import org.eclipse.swt.graphics.Font;
25 import org.eclipse.swt.graphics.GC;
26 import org.eclipse.swt.graphics.Point;
27 import org.eclipse.swt.layout.GridData;
28 import org.eclipse.swt.layout.GridLayout;
29 import org.eclipse.swt.widgets.Canvas;
30 import org.eclipse.swt.widgets.Caret;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.Display;
33 import org.eclipse.swt.widgets.Shell;
34
35 public class MiniTerminal implements KeyListener, PaintListener {
36
37 private Canvas area;
38 private Caret caret;
39
40 private StringBuffer buf = new StringBuffer("");
41 private StringBuffer userInput = new StringBuffer("");
42 private List<String> history = new ArrayList<>();
43
44 private Point charExtent = null;
45 private int charsPerLine = 0;
46 private String[] lines = new String[0];
47 private List<String> logicalLines = new ArrayList<>();
48
49 private Font mono;
50 private Charset charset;
51
52 private Path currentDir;
53 private Path homeDir;
54 private String host = "localhost";
55 private String username;
56
57 // Sub process
58 private Process process;
59 private boolean running = false;
60 private OutputStream stdIn = null;
61
62 private Thread readOut;
63
64 public MiniTerminal(Composite parent, String url) {
65 this(parent);
66 setPath(url);
67 }
68
69 public MiniTerminal(Composite parent) {
70 charset = StandardCharsets.UTF_8;
71
72 Display display = parent.getDisplay();
73 // Linux-specific
74 mono = new Font(display, "Monospace", 10, SWT.NONE);
75
76 parent.setLayout(new GridLayout());
77 area = new Canvas(parent, SWT.NONE);
78 area.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
79 caret = new Caret(area, SWT.NONE);
80 area.setCaret(caret);
81
82 area.addKeyListener(this);
83 area.addPaintListener(this);
84
85 username = System.getProperty("user.name");
86 try {
87 host = InetAddress.getLocalHost().getHostName();
88 if (host.indexOf('.') > 0)
89 host = host.substring(0, host.indexOf('.'));
90 } catch (UnknownHostException e) {
91 host = "localhost";
92 }
93 homeDir = Paths.get(System.getProperty("user.home"));
94 currentDir = homeDir;
95
96 buf = new StringBuffer(prompt());
97 }
98
99 @Override
100 public void keyPressed(KeyEvent e) {
101 }
102
103 @Override
104 public void keyReleased(KeyEvent e) {
105 if (e.keyLocation != 0)
106 return;// weird characters
107 // System.out.println(e.character);
108 if (e.keyCode == 0xd) {// return
109 markLogicalLine();
110 if (!running)
111 processUserInput();
112 // buf.append(prompt());
113 } else if (e.keyCode == 0x8) {// delete
114 if (userInput.length() == 0)
115 return;
116 userInput.setLength(userInput.length() - 1);
117 if (!running && buf.length() > 0)
118 buf.setLength(buf.length() - 1);
119 } else if (e.stateMask == 0x40000 && e.keyCode == 0x63) {// Ctrl+C
120 if (process != null)
121 process.destroy();
122 } else if (e.stateMask == 0x40000 && e.keyCode == 0xdf) {// Ctrl+\
123 if (process != null) {
124 process.destroyForcibly();
125 }
126 } else {
127 // if (!running)
128 buf.append(e.character);
129 userInput.append(e.character);
130 }
131
132 if (area.isDisposed())
133 return;
134 area.redraw();
135 // System.out.println("Append " + e);
136
137 if (running) {
138 if (stdIn != null) {
139 try {
140 stdIn.write(Character.toString(e.character).getBytes(charset));
141 } catch (IOException e1) {
142 // TODO Auto-generated catch block
143 e1.printStackTrace();
144 }
145 }
146 }
147 }
148
149 protected String prompt() {
150 String fileName = currentDir.equals(homeDir) ? "~" : currentDir.getFileName().toString();
151 String end = username.equals("root") ? "]# " : "]$ ";
152 return "[" + username + "@" + host + " " + fileName + end;
153 }
154
155 private void displayPrompt() {
156 buf.append(prompt() + userInput);
157 }
158
159 protected void markLogicalLine() {
160 String str = buf.toString().trim();
161 logicalLines.add(str);
162 buf = new StringBuffer("");
163 }
164
165 private void processUserInput() {
166 String cmd = userInput.toString();
167 userInput = new StringBuffer("");
168 processUserInput(cmd);
169 history.add(cmd);
170 }
171
172 protected void processUserInput(String input) {
173 try {
174 StringTokenizer st = new StringTokenizer(input);
175 List<String> args = new ArrayList<>();
176 while (st.hasMoreTokens())
177 args.add(st.nextToken());
178 if (args.size() == 0) {
179 displayPrompt();
180 return;
181 }
182
183 // change directory
184 if (args.get(0).equals("cd")) {
185 if (args.size() == 1) {
186 setPath(homeDir);
187 } else {
188 Path newPath = currentDir.resolve(args.get(1));
189 if (!Files.exists(newPath) || !Files.isDirectory(newPath)) {
190 println(newPath + ": No such file or directory");
191 return;
192 }
193 setPath(newPath);
194 }
195 displayPrompt();
196 return;
197 }
198 // show current directory
199 else if (args.get(0).equals("pwd")) {
200 println(currentDir);
201 displayPrompt();
202 return;
203 }
204 // exit
205 else if (args.get(0).equals("exit")) {
206 println("logout");
207 exitCalled();
208 return;
209 }
210
211 ProcessBuilder pb = new ProcessBuilder(args);
212 pb.redirectErrorStream(true);
213 pb.directory(currentDir.toFile());
214 // Process process = Runtime.getRuntime().exec(input, null, currentPath.toFile());
215 process = pb.start();
216
217 stdIn = process.getOutputStream();
218 readOut = new Thread("MiniTerminal read out") {
219 @Override
220 public void run() {
221 running = true;
222 try (BufferedReader in = new BufferedReader(
223 new InputStreamReader(process.getInputStream(), charset))) {
224 String line = null;
225 while ((line = in.readLine()) != null) {
226 println(line);
227 }
228 } catch (IOException e) {
229 println(e.getMessage());
230 }
231 stdIn = null;
232 displayPrompt();
233 running = false;
234 readOut = null;
235 process = null;
236 }
237 };
238 readOut.start();
239 } catch (IOException e) {
240 println(e.getMessage());
241 displayPrompt();
242 }
243 }
244
245 protected int linesForLogicalLine(char[] line) {
246 return line.length / charsPerLine + 1;
247 }
248
249 protected void println(Object line) {
250 buf.append(line);
251 markLogicalLine();
252 }
253
254 protected void refreshLines(int charPerLine, int nbrOfLines) {
255 if (lines.length != nbrOfLines) {
256 lines = new String[nbrOfLines];
257 Arrays.fill(lines, null);
258 }
259 if (this.charsPerLine != charPerLine)
260 this.charsPerLine = charPerLine;
261
262 int currentLine = nbrOfLines - 1;
263 // current line
264 if (buf.length() > 0) {
265 lines[currentLine] = buf.toString();
266 } else {
267 lines[currentLine] = "";
268 }
269 currentLine--;
270
271 logicalLines: for (int i = logicalLines.size() - 1; i >= 0; i--) {
272 char[] logicalLine = logicalLines.get(i).toCharArray();
273 int linesNeeded = linesForLogicalLine(logicalLine);
274 for (int j = linesNeeded - 1; j >= 0; j--) {
275 int from = j * charPerLine;
276 int to = j == linesNeeded - 1 ? from + charPerLine : Math.min(from + charPerLine, logicalLine.length);
277 lines[currentLine] = new String(Arrays.copyOfRange(logicalLine, from, to));
278 // System.out.println("Set line " + currentLine + " to : " + lines[currentLine]);
279 currentLine--;
280 if (currentLine < 0)
281 break logicalLines;
282 }
283 }
284 }
285
286 @Override
287 public void paintControl(PaintEvent e) {
288 GC gc = e.gc;
289 gc.setFont(mono);
290 if (charExtent == null)
291 charExtent = gc.textExtent("a");
292
293 Point areaSize = area.getSize();
294 int charPerLine = areaSize.x / charExtent.x;
295 int nbrOfLines = areaSize.y / charExtent.y;
296 refreshLines(charPerLine, nbrOfLines);
297
298 for (int i = 0; i < lines.length; i++) {
299 String line = lines[i];
300 if (line != null)
301 gc.drawString(line, 0, i * charExtent.y);
302 }
303 // String toDraw = buf.toString();
304 // gc.drawString(toDraw, 0, 0);
305 // area.setCaret(caret);
306 }
307
308 protected void exitCalled() {
309
310 }
311
312 public void setPath(String path) {
313 this.currentDir = Paths.get(path);
314 }
315
316 public void setPath(Path path) {
317 this.currentDir = path;
318 }
319
320 public static void main(String[] args) {
321 Display display = Display.getCurrent() == null ? new Display() : Display.getCurrent();
322 Shell shell = new Shell(display, SWT.SHELL_TRIM);
323
324 String url = args.length > 0 ? args[0] : System.getProperty("user.home");
325 new MiniTerminal(shell, url) {
326
327 @Override
328 protected void exitCalled() {
329 shell.dispose();
330 System.exit(0);
331 }
332 };
333
334 shell.open();
335 shell.setSize(new Point(800, 480));
336 while (!shell.isDisposed()) {
337 if (!display.readAndDispatch())
338 display.sleep();
339 }
340 }
341
342 }