2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 Kevin Lamonte
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.util
.ResourceBundle
;
33 import jexer
.menu
.TMenu
;
34 import jexer
.event
.TKeypressEvent
;
35 import jexer
.event
.TMenuEvent
;
36 import jexer
.event
.TMouseEvent
;
37 import jexer
.event
.TResizeEvent
;
38 import static jexer
.TCommand
.*;
39 import static jexer
.TKeypress
.*;
42 * TTerminalWindow exposes a ECMA-48 / ANSI X3.64 style terminal in a window.
44 public class TTerminalWindow
extends TScrollableWindow
{
49 private static final ResourceBundle i18n
= ResourceBundle
.getBundle(TTerminalWindow
.class.getName());
51 // ------------------------------------------------------------------------
52 // Variables --------------------------------------------------------------
53 // ------------------------------------------------------------------------
58 private TTerminalWidget terminal
;
61 * If true, close the window when the shell exits.
63 private boolean closeOnExit
= false;
65 // ------------------------------------------------------------------------
66 // Constructors -----------------------------------------------------------
67 // ------------------------------------------------------------------------
70 * Public constructor spawns a custom command line.
72 * @param application TApplication that manages this window
73 * @param x column relative to parent
74 * @param y row relative to parent
75 * @param commandLine the command line to execute
77 public TTerminalWindow(final TApplication application
, final int x
,
78 final int y
, final String commandLine
) {
80 this(application
, x
, y
, RESIZABLE
, commandLine
.split("\\s+"),
81 System
.getProperty("jexer.TTerminal.closeOnExit",
82 "false").equals("true"));
86 * Public constructor spawns a custom command line.
88 * @param application TApplication that manages this window
89 * @param x column relative to parent
90 * @param y row relative to parent
91 * @param commandLine the command line to execute
92 * @param closeOnExit if true, close the window when the command exits
94 public TTerminalWindow(final TApplication application
, final int x
,
95 final int y
, final String commandLine
, final boolean closeOnExit
) {
97 this(application
, x
, y
, RESIZABLE
, commandLine
.split("\\s+"),
102 * Public constructor spawns a custom command line.
104 * @param application TApplication that manages this window
105 * @param x column relative to parent
106 * @param y row relative to parent
107 * @param flags mask of CENTERED, MODAL, or RESIZABLE
108 * @param command the command line to execute
110 public TTerminalWindow(final TApplication application
, final int x
,
111 final int y
, final int flags
, final String
[] command
) {
113 this(application
, x
, y
, flags
, command
,
114 System
.getProperty("jexer.TTerminal.closeOnExit",
115 "false").equals("true"));
119 * Public constructor spawns a custom command line.
121 * @param application TApplication that manages this window
122 * @param x column relative to parent
123 * @param y row relative to parent
124 * @param flags mask of CENTERED, MODAL, or RESIZABLE
125 * @param command the command line to execute
126 * @param closeOnExit if true, close the window when the command exits
128 public TTerminalWindow(final TApplication application
, final int x
,
129 final int y
, final int flags
, final String
[] command
,
130 final boolean closeOnExit
) {
132 super(application
, i18n
.getString("windowTitle"), x
, y
,
133 80 + 2, 24 + 2, flags
);
135 // Require at least one line for the display.
136 setMinimumWindowHeight(3);
138 this.closeOnExit
= closeOnExit
;
139 vScroller
= new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
141 // Claim the keystrokes the emulator will need.
145 TStatusBar statusBar
= newStatusBar(i18n
.getString("statusBarRunning"));
146 statusBar
.addShortcutKeypress(kbF1
, cmHelp
,
147 i18n
.getString("statusBarHelp"));
148 statusBar
.addShortcutKeypress(kbF10
, cmMenu
,
149 i18n
.getString("statusBarMenu"));
152 terminal
= new TTerminalWidget(this, 0, 0, command
, new TAction() {
160 * Public constructor spawns a shell.
162 * @param application TApplication that manages this window
163 * @param x column relative to parent
164 * @param y row relative to parent
165 * @param flags mask of CENTERED, MODAL, or RESIZABLE
167 public TTerminalWindow(final TApplication application
, final int x
,
168 final int y
, final int flags
) {
170 this(application
, x
, y
, flags
,
171 System
.getProperty("jexer.TTerminal.closeOnExit",
172 "false").equals("true"));
177 * Public constructor spawns a shell.
179 * @param application TApplication that manages this window
180 * @param x column relative to parent
181 * @param y row relative to parent
182 * @param flags mask of CENTERED, MODAL, or RESIZABLE
183 * @param closeOnExit if true, close the window when the shell exits
185 public TTerminalWindow(final TApplication application
, final int x
,
186 final int y
, final int flags
, final boolean closeOnExit
) {
188 super(application
, i18n
.getString("windowTitle"), x
, y
,
189 80 + 2, 24 + 2, flags
);
191 // Require at least one line for the display.
192 setMinimumWindowHeight(3);
194 this.closeOnExit
= closeOnExit
;
195 vScroller
= new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
197 // Claim the keystrokes the emulator will need.
201 TStatusBar statusBar
= newStatusBar(i18n
.getString("statusBarRunning"));
202 statusBar
.addShortcutKeypress(kbF1
, cmHelp
,
203 i18n
.getString("statusBarHelp"));
204 statusBar
.addShortcutKeypress(kbF10
, cmMenu
,
205 i18n
.getString("statusBarMenu"));
208 terminal
= new TTerminalWidget(this, 0, 0, new TAction() {
215 // ------------------------------------------------------------------------
216 // TScrollableWindow ------------------------------------------------------
217 // ------------------------------------------------------------------------
220 * Draw the display buffer.
224 if (terminal
!= null) {
225 setTitle(terminal
.getTitle());
232 * Handle window/screen resize events.
234 * @param resize resize event
237 public void onResize(final TResizeEvent resize
) {
238 if (resize
.getType() == TResizeEvent
.Type
.WIDGET
) {
239 if (terminal
!= null) {
240 terminal
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
241 getWidth() - 2, getHeight() - 2));
244 // Resize the scroll bars
252 * Resize scrollbars for a new width/height.
255 public void reflowData() {
256 // Vertical scrollbar
257 if (terminal
!= null) {
258 terminal
.reflowData();
259 setTopValue(terminal
.getTopValue());
260 setBottomValue(terminal
.getBottomValue());
261 setVerticalBigChange(terminal
.getVerticalBigChange());
262 setVerticalValue(terminal
.getVerticalValue());
269 * @param keypress keystroke event
272 public void onKeypress(final TKeypressEvent keypress
) {
273 if ((terminal
!= null)
274 && (terminal
.isReading())
275 && (!inKeyboardResize
)
277 terminal
.onKeypress(keypress
);
279 super.onKeypress(keypress
);
284 * Handle mouse press events.
286 * @param mouse mouse button press event
289 public void onMouseDown(final TMouseEvent mouse
) {
290 if (inWindowMove
|| inWindowResize
) {
291 // TWindow needs to deal with this.
292 super.onMouseDown(mouse
);
296 super.onMouseDown(mouse
);
300 * Handle mouse release events.
302 * @param mouse mouse button release event
305 public void onMouseUp(final TMouseEvent mouse
) {
306 if (inWindowMove
|| inWindowResize
) {
307 // TWindow needs to deal with this.
308 super.onMouseUp(mouse
);
312 super.onMouseUp(mouse
);
314 if (mouse
.isMouse1() && mouseOnVerticalScroller(mouse
)) {
315 // Clicked on vertical scrollbar
316 if (terminal
!= null) {
317 terminal
.setVerticalValue(getVerticalValue());
323 * Handle mouse motion events.
325 * @param mouse mouse motion event
328 public void onMouseMotion(final TMouseEvent mouse
) {
329 if (inWindowMove
|| inWindowResize
) {
330 // TWindow needs to deal with this.
331 super.onMouseMotion(mouse
);
335 super.onMouseMotion(mouse
);
337 if (mouse
.isMouse1() && mouseOnVerticalScroller(mouse
)) {
338 // Clicked/dragged on vertical scrollbar
339 if (terminal
!= null) {
340 terminal
.setVerticalValue(getVerticalValue());
346 * Get this window's help topic to load.
348 * @return the topic name
351 public String
getHelpTopic() {
352 return "Terminal Window";
355 // ------------------------------------------------------------------------
356 // TTerminalWindow --------------------------------------------------------
357 // ------------------------------------------------------------------------
360 * Returns true if this window does not want the application-wide mouse
361 * cursor drawn over it.
363 * @return true if this window does not want the application-wide mouse
364 * cursor drawn over it
367 public boolean hasHiddenMouse() {
368 if (terminal
!= null) {
369 return terminal
.hasHiddenMouse();
375 * Claim the keystrokes the emulator will need.
377 private void addShortcutKeys() {
378 addShortcutKeypress(kbCtrlA
);
379 addShortcutKeypress(kbCtrlB
);
380 addShortcutKeypress(kbCtrlC
);
381 addShortcutKeypress(kbCtrlD
);
382 addShortcutKeypress(kbCtrlE
);
383 addShortcutKeypress(kbCtrlF
);
384 addShortcutKeypress(kbCtrlG
);
385 addShortcutKeypress(kbCtrlH
);
386 addShortcutKeypress(kbCtrlU
);
387 addShortcutKeypress(kbCtrlJ
);
388 addShortcutKeypress(kbCtrlK
);
389 addShortcutKeypress(kbCtrlL
);
390 addShortcutKeypress(kbCtrlM
);
391 addShortcutKeypress(kbCtrlN
);
392 addShortcutKeypress(kbCtrlO
);
393 addShortcutKeypress(kbCtrlP
);
394 addShortcutKeypress(kbCtrlQ
);
395 addShortcutKeypress(kbCtrlR
);
396 addShortcutKeypress(kbCtrlS
);
397 addShortcutKeypress(kbCtrlT
);
398 addShortcutKeypress(kbCtrlU
);
399 addShortcutKeypress(kbCtrlV
);
400 addShortcutKeypress(kbCtrlW
);
401 addShortcutKeypress(kbCtrlX
);
402 addShortcutKeypress(kbCtrlY
);
403 addShortcutKeypress(kbCtrlZ
);
404 addShortcutKeypress(kbF1
);
405 addShortcutKeypress(kbF2
);
406 addShortcutKeypress(kbF3
);
407 addShortcutKeypress(kbF4
);
408 addShortcutKeypress(kbF5
);
409 addShortcutKeypress(kbF6
);
410 addShortcutKeypress(kbF7
);
411 addShortcutKeypress(kbF8
);
412 addShortcutKeypress(kbF9
);
413 addShortcutKeypress(kbF10
);
414 addShortcutKeypress(kbF11
);
415 addShortcutKeypress(kbF12
);
416 addShortcutKeypress(kbAltA
);
417 addShortcutKeypress(kbAltB
);
418 addShortcutKeypress(kbAltC
);
419 addShortcutKeypress(kbAltD
);
420 addShortcutKeypress(kbAltE
);
421 addShortcutKeypress(kbAltF
);
422 addShortcutKeypress(kbAltG
);
423 addShortcutKeypress(kbAltH
);
424 addShortcutKeypress(kbAltU
);
425 addShortcutKeypress(kbAltJ
);
426 addShortcutKeypress(kbAltK
);
427 addShortcutKeypress(kbAltL
);
428 addShortcutKeypress(kbAltM
);
429 addShortcutKeypress(kbAltN
);
430 addShortcutKeypress(kbAltO
);
431 addShortcutKeypress(kbAltP
);
432 addShortcutKeypress(kbAltQ
);
433 addShortcutKeypress(kbAltR
);
434 addShortcutKeypress(kbAltS
);
435 addShortcutKeypress(kbAltT
);
436 addShortcutKeypress(kbAltU
);
437 addShortcutKeypress(kbAltV
);
438 addShortcutKeypress(kbAltW
);
439 addShortcutKeypress(kbAltX
);
440 addShortcutKeypress(kbAltY
);
441 addShortcutKeypress(kbAltZ
);
445 * Hook for subclasses to be notified of the shell termination.
447 public void onShellExit() {
451 clearShortcutKeypresses();
452 getApplication().postEvent(new TMenuEvent(TMenu
.MID_REPAINT
));
456 * Wait for a period of time to get output from the launched process.
458 * @param millis millis to wait for, or 0 to wait forever
459 * @return true if the launched process has emitted something
461 public boolean waitForOutput(final int millis
) {
462 if (terminal
== null) {
465 return terminal
.waitForOutput(millis
);
469 * Get the exit value for the emulator.
473 public int getExitValue() {
474 if (terminal
== null) {
477 return terminal
.getExitValue();