eXept Software AG Logo

Smalltalk/X Webserver

Documentation of class 'TerminalView':

Home

Documentation
www.exept.de
Everywhere
for:
[back]

Class: TerminalView


Inheritance:

   Object
   |
   +--GraphicsMedium
      |
      +--DisplaySurface
         |
         +--SimpleView
            |
            +--View
               |
               +--ListView
                  |
                  +--TextView
                     |
                     +--EditTextView
                        |
                        +--TextCollector
                           |
                           +--TerminalView
                              |
                              +--VT100TerminalView

Package:
stx:libwidg2
Category:
Views-TerminalViews
Version:
rev: 1.268 date: 2019/08/16 22:04:34
user: cg
file: TerminalView.st directory: libwidg2
module: stx stc-classLibrary: libwidg2
Author:
Claus Gittinger

Description:


I provide terminal functionality, by interpreting data
arriving from some stream (typically connected to a command interpreter 
via a pty, or another system via a modem) and sending my keyboard data to it.

I am abstract providing general functionality -
concrete terminal characteristics (i.e. escape sequences) are defined 
by concrete subclasses (see VT52TerminalView, VT100TerminalView).

Concrete applications are: 
    consoles (VT100TerminalView),
    telnet-views (see TelnetTool)
    editor wrappers (if you like emacs/vi)
    gdb terminal subviews (see GDBApplication)

Although my class protocol includes common startup protocol
(to open a terminalView with a shell or on the output of a command),
I can be used as a widget within an application (modem software).

Implementation notice: 
    some of my pty functionality and handling is being extracted to
    the separate TerminalSession class (which allows communicating with a
    program without having its output displayed).   
    So currently, some ugly code duplication is present.
    Once stable, code will be refactored.   
    For now, as terminalView is being used in
    some of our critical applications, this refactoring has not yet been done.

Line Editing mode:
 Cursor keys allow for th user to navigate through previously entered input (as in cmd.exe and bash).

 Special: <SHIFT>-cursor up goes back to the previously selected history line.
 (not to the previous line).
 For example, if the user types:
    a <RETURN>
    b <RETURN>
    c <RETURN>
    <CURSOR-UP> -> shows 'c'
    <CURSOR-UP> -> shows 'b'
    <CURSOR-UP> -> shows 'a'
    123 <RETURN> -> resends 'a123'
    <SHIFT-CURSOR-UP> -> shows 'a' again which is the previously selected history line(not a123)
    <CURSOR-DOWN> -> shows 'b'
    <RETURN> -> sends 'b'


[instance variables:]
    inStream                stream where keyboard input is
                            sent to (connected to shells or commands input)

    outStream               stream where the output of the
                            shell or command arrives
                            (read here and displayed in the view)

    readerProcess           process which reads commands    
                            output and sends it to the view

    lineEditMode            if on, do readLine-alike input history and editing

    signalControlKeys       if on, CTRL-C sends an interrupt (for Windows)

[class variables]:
    Debug := true           trace incoming characters
    Debug := false           
    DebugKeyboard := true   trace outgoing characters
    DebugKeyboard := false 


Related information:

    TelNetTool

Class protocol:

defaults
o  defaultIcon
This resource specification was automatically generated
by the ImageEditor of ST/X.

usage example(s):

     self defaultIcon inspect
     ImageEditor openOnClass:self andSelector:#defaultIcon
     Icon flushCachedIcons

o  defaultNumberOfColumns

o  defaultNumberOfLines

initialization
o  initialize
self initialize

opening
o  open
VT100TerminalView open
VT52TerminalView open
TerminalView open

o  openDummy
for testing purposes only - opens a dummy tty-view, which simply
echoes whatever is typed in

usage example(s):

     self openDummy

o  openOnCommand: aCommandString
start a command on a pseudo-TTY, open a terminalView on it
(i.e. this is kind of an xterm)

usage example(s):

     VT100TerminalView openOnCommand:'ls -l'

o  openOnCommand: aCommand in: aDirectory
start a shell on a pseudo-TTY, open a terminalView on it
(i.e. this is kind of an xterm)

usage example(s):

     TerminalView openOnCommand:'ls'
     TerminalView openOnCommand:'ls' in:'/etc'

o  openOnCommand: aCommandString in: aDirectory onExit: exitBlockOrNil
start a command on a pseudo-TTY, open a terminalView on its output
(i.e. this is kind of an xterm).
When the command finishes, evaluate aBlock.

usage example(s):

     VT100TerminalView openOnCommand:'ls -l' in:'/etc' onExit:[]
     VT100TerminalView openOnCommand:'ls -l' in:'/etc' onExit:[:vt | vt topView destroy]
     VT100TerminalView openOnCommand:'ls -l' in:'/etc' onExit:[:vt | Dialog information:'Shell terminated'. vt topView destroy ]

o  openOnCommand: aCommandString onExit: aBlock
start a command on a pseudo-TTY, open a terminalView on its output
(i.e. this is kind of an xterm).
When the command finishes, evaluate aBlock.

usage example(s):

     VT100TerminalView 
        openOnCommand:'ls -lR' 
        onExit:[:vt | 
            Dialog information:'Press OK to close'.
            vt topView close.
        ].

o  openOnInput: inStream output: outStream
open a terminalView on the given streams (which are typically some
kind of socket or pty).
Keys pressed are sent to inStream, text appearing
from outStream is displayed in the terminal view.
This can be used to implement things like rlogin
or telnet views (if connected to a modem, a com-program can also be
implemented this way).

o  openOnInput: inStream output: outStream in: aView
open a terminalView on the given streams
(which are typically some kind of socket or pty).
Keys pressed are sent to inStream, text appearing
from outStream is displayed in the terminal view.
This can be used to implement things like rlogin
or telnet views (if connected to a modem, a com-program can also be
implemented this way).

o  openShell
start a shell on a pseudo-TTY, open a terminalView on it
(i.e. this is kind of an xterm)

usage example(s):

     VT100TerminalView openShell

o  openShellIn: aDirectory
start a shell on a pseudo-TTY, open a terminalView on it
(i.e. this is kind of an xterm)

usage example(s):

     TerminalView openShellIn:'/etc'
     VT52TerminalView openShellIn:'/etc'
     VT100TerminalView openShellIn:'/etc'

o  openShellIn: aDirectory onExit: exitAction
start a shell on a pseudo-TTY, open a terminalView on it
(i.e. this is kind of an xterm)

usage example(s):

     TerminalView openShellIn:'/etc' onExit:[ Dialog information:'Shell terminated' ]

o  openWithAction: setupAction
open a terminalView, let it start its command via setupAction,
which gets the instantiated terminalView as argument.

usage example(s):

     TerminalView openWithAction:[:vt | vt startShellIn:'/etc']
     TerminalView openWithAction:[:vt | vt startCommand:'ls -l' in:'/etc'.  vt shellTerminateAction:[].]

o  openWithAction: setupAction onExit: exitBlockOrNil
open a terminalView, let it start its command via setupAction,
which gets the instantiated terminalView as argument.
The default exitAction is to destroy the topView.

usage example(s):

     TerminalView openWithAction:[:vt | vt startShellIn:'/etc']
     TerminalView openWithAction:[:vt | vt startCommand:'ls -l' in:'/etc'.  vt shellTerminateAction:[].]

queries
o  isVisualStartable
returns whether this application class can be started via #open


Instance protocol:

accessing
o  filterStream
get a filter stream if any; if not nil, it gets all incoming data via nextPutAll:.
Added to allow saving incoming data to a file, but can also be used to catch/filter/lookAt
incoming data by some other program

o  filterStream: aStream
set a filter stream; if not nil, it gets all incoming data via nextPutAll:.
Added to allow saving incoming data to a file, but can also be used to catch/filter/lookAt
incoming data by some other program

o  inStream
return the stream, which gets all input data (i.e. keyboard input)

o  inStream: something
set the stream, which gets all input data (i.e. keyboard input)

o  lineBufferHistory

o  lineBufferHistory: aCollection

o  lineBufferHistoryChanged
true if it changed since either set explicitly or since the terminal was opened

o  masterWindow: aTopView
if set, and a corresponding osCommand escape sequence is received,
that topView's title, icon or other attribute is changed.
Needed to support xterm's 'set window title' escape sequence

o  outStream
return the stream, which is used to present data in the view (i.e. shell output)

o  outStream: something
set the stream, which is used to present data in the view (i.e. shell output)

o  readerProcess

o  recorderStream: aWriteStream
set a recorder stream; if not nil, it gets all user input (keyboard) data via nextPut:.
Allows saving of user input to a file, for later replay of a session

o  shellDirectory: aPathname
the directory, in which the shell/command should be executed.
By default, the 'current' directory is used

o  shellTerminateAction: aBlock
set the block which is evaluated when the shell terminates.
Can be used to close down the application or perform any other cleanup action.
The default shows a dialog, that the shell/command has terminated

accessing-behavior
o  disableLineEditMode

o  enableLineEditMode

o  filterOnly: aBoolean
if true, any output from the program is ONLY
sent to the filterStream (if any), not to the window.
Can be used to divert output to a file, without showing
it (eg. for mass-data recording)

o  ignoreOutput: aBoolean
if true, any output from the program is ignored
(not processed at all).
Can be used to temporarily disable processing
(for example via a button) when huge mass-output is
coming which we want to ignore.

o  inputTranslateBackspaceToDelete
translating backspace to delete on user input

o  inputTranslateBackspaceToDelete: aBoolean

o  inputTranslateCRToNL
translating <RETURN> to <NL> on user input

o  inputTranslateCRToNL: aBoolean
translate <RETURN> to <NL> on user input

o  lineEditMode: aBoolean
if true, I do some limited line editing for the user's input;
the user can use cursor keys to navigate in the input history and
reissue a previously entered input line.
Special: <SHIFT>-cursor up goes back to the previously selected history line.
(not to the previous line).
For example, if the user types:
a <RETURN>
b <RETURN>
c <RETURN>
<CURSOR-UP> -> shows 'c'
<CURSOR-UP> -> shows 'b'
<CURSOR-UP> -> shows 'a'
123 <RETURN> -> resends 'a123'
<SHIFT-CURSOR-UP> -> shows 'a' again which is the previously selected history line(not a123)
<CURSOR-DOWN> -> shows 'b'
<RETURN> -> sends 'b'

o  localEcho: aBoolean
enable/disable local echo

o  noColors
the noColors boolean disables color changes (from color rendition escape sequences)

o  noColors: aBoolean
the noColors boolean disables color changes (from color rendition escape sequences)

o  sendControlKeys
there is some conflict with control key handling, for keys such as CTRL-c:
it is both a shortcut (eg. Copy) and sometimes required in the terminal (interrupt key).
For this, we look if there is a current selection, and if so, always treat it as a
shortcut (and NOT sending it to the terminal's program).
Otherwise, if there is no selection, look at the 'sendControlKeys' boolean.
If it is set (which is the default), then send it to the terminal, otherwise perform the editor op.
Thus, an application containing me can offer a menu function (or toggle),
to control this behavior on the UI level.

o  sendControlKeys: aBoolean
there is some conflict with control key handling, for keys such as CTRL-c:
it is both a shortcut (eg. Copy) and sometimes required in the terminal (interrupt key).
For this, we look if there is a current selection, and if so, always treat it as a
shortcut (and NOT sending it to the terminal's program).
Otherwise, if there is no selection, look at the 'sendControlKeys' boolean.
If it is set (which is the default), then send it to the terminal, otherwise perform the editor op.
Thus, an application containing me can offer a menu function (or toggle),
to control this behavior on the UI level.

o  signalControlKeys
if true (default on Windows), CTRL-C sends an interrupt to
the program. Otherwise, it is sent as a character (0x03)

o  signalControlKeys: aBoolean
if true (default on Windows), CTRL-C sends an interrupt to
the program. Otherwise, it is sent as a character (0x03)

o  translateNLToCRNL
translate NL to CRNL on output

o  translateNLToCRNL: aBoolean
translate NL to CRNL on output

cursor handling
o  cursorDown: n
(comment from inherited method)
move cursor down by n lines; scroll if at end of visible text

o  cursorMovementAllowed
return true, if the user may move the cursor around
(via button-click, or cursor-key with selection).
Here false is returned - the cursor is only moved by
cursor positioning escape sequences arriving from the
stream.

o  numberOfTerminalCols

o  numberOfTerminalColumns

o  numberOfTerminalLines
be careful - this is NOT called numberOfLines,

o  restoreCursor

o  saveCursor

o  validateCursorCol: col inLine: line
check of col is a valid cursor position; return a new col-nr if not.
Here, the linelength is enforced

defaults
o  anyKeyCodes

event handling
o  computeNumberOfLinesShown
(comment from inherited method)
recompute the number of visible lines

o  defineWindowSize
self halt.

o  keyPress: aKey x: x y: y
somewhat complicated, since some characters

o  keyPressInLineEditMode: aKey
readline alike line editing.
cursorUp/down select a previous line from the history (unix shell bahevior).
shift cursorUp selects a previous selected history line from the history (windows shell bahevior).
cursor left/right/^A/^E/backspace position/edit inside that line.
Return true, if the character was processed,
false if not. Then, the caller should proceed as usual.

o  shellTerminated
shell has terminated

o  shouldProcessInputInLineEditMode
should input be processed in the readline edit mode?

o  sizeChanged: how
(comment from inherited method)
make certain, cursor is visible after the sizechange

functions
o  autoMargin: aBoolean
set/clear autowrap at end of line (not yet fully implemented).
This may come from a CSI sequence, or set programmatically.

o  doBackspace

o  doClearDisplay
clear everything

o  doClearEntireLine
clear the cursor line. cursor position remains unchanged

o  doClearEntireScreen
clear everything

o  doClearFromBeginningOfLine
clear from beginning of line to the cursorPosition

o  doClearFromBeginningOfScreen
clear from beginning of the screen to the cursorPosition

o  doClearToEndOfLine
clear from the cursorPosition to the end of the line

o  doClearToEndOfScreen
clear from the cursorPosition to the end of the screen

o  doCursorDown: n
move the cursor down by n lines

o  doCursorHome
move the cursor to the home position

o  doCursorLeft: n
move the cursor to the left by n columns

o  doCursorNewLine
move the cursor down to the next line (col remains unchanged)

o  doCursorReturn
move the cursor down and left to the beginning to the next line

o  doCursorRight: n
move the cursor to the right by n columns

o  doCursorUp: n
move the cursor up by n lines

initialization & release
o  closeDownShell
shut down my shell process.

o  closeDownShellAndStopReaderProcess
shut down my shell process and stop the background reader thread.

o  closeStreams

o  escapeSequences: codes
setup my escape sequences

o  flushInput
flush any leftover input-processing events

o  initStyle
self foregroundColor:Color green.

o  initialize
currently unused.

o  initializeKeyboardMap
setup my own keyboardMap, where control-keys
(and some Cmd-keys) are not translated.

o  initializeKeyboardSequences

o  initializeShellTerminateAction
may be removed with shellTerminateAction:...

o  keyboardMap
return my own, private keyboard map.
This has control keys removed and
those will be passed unchanged to the shell

o  reinitialize
reinit after a snapIn.
this is invoked (by the system thread) after a snapShot image restart

o  release
release myself - shut down the shell, stop the reader thread.

initialization-shell
o  basicStartCommand: aCommand in: aDirectory
start a command on a pseudo terminal. If the command arg is nil,
a shell is started. If aDirectory is not nil, the command is
executed in that directory.
Also fork a reader process, to read the shells output and
tell me, whenever something arrives

o  startCommand: aCommand
start a command on a pseudo terminal. If the command arg is nil,
a shell is started. The command is started in the current directory.
Also fork a reader process, to read the shells output and
tell me, whenever something arrives

o  startCommand: aCommand in: aDirectory
start a command on a pseudo terminal. If the command arg is nil,
a shell is started. If aDirectory is not nil, the command is
executed in that directory.
Also fork a reader process, to read the shells output and
tell me, whenever something arrives

o  startShell
start a shell on a pseudo terminal in the current directory.
Also fork a reader process, to read the shells output and
tell me, whenever something arrives

o  startShellIn: aDirectory
start a shell on a pseudo terminal in some directory.
Also fork a reader process, to read the shells output and
tell me, whenever something arrives

menu
o  doClear
reset the scroll-range etc, clear the text buffer

o  doReset
reset the scroll-range;
may have to reset more in the future (current font-set; color; etc)

o  doSendInterrupt
send an INT-signal to the shell (UNIX only)

o  doSendKillSignal
send a KILL-signal to the shell (UNIX only)

o  doSendTerminateSignal
send a TERM-signal to the shell (UNIX only)

o  doSetLineLimit
ask for the lineLimit (the number of buffered lines)

o  editMenu
return the view's middleButtonMenu

o  openFileBrowserOnIt
open a fileBrowser on the selected fileName

o  sendInterruptSignal
send an INT-signal to the shell (UNIX only)

o  sendKillSignal
send a KILL-signal to the shell (UNIX only)

o  sendTerminateSignal
send a TERM-signal to the shell (UNIX only)

o  setGreenDisplayMode

o  setNormalDisplayMode

o  setRedDisplayMode

o  setReverseDisplayMode

o  startSaveAs
start saving all received data to some file

o  stopFilter
stop saving/printing of received data

misc
o  debugPrintCharacter: aCharacter as: what

o  removeTrailingBlankLines

processing-input
o  doNothing
private - end of an ignored escape-sequence

o  endOfSequence
private - reset state-machine at end of escape-sequence

o  nextPut: char
process a character (i.e. the shell's output)

o  nextPutAll: aString
self processInput:aString n:aString size

o  processInput: buffer n: count
actually: output coming from the connected application (aka input to me);
the stuff coming from the application is a mix of plain text and CSI escape sequences.
If we process each character individually, things are trivial, but slow.
Therefore, we collect big chunks of non-escaped text and buffer them to make
use of the inherited buffered output optimizations (see TextCollector).
Thus, we collect until an escape sequence is encountered, and flush the buffered stuff then,
proceed in single-character mode (processState0:) until the sequence is finished, and continue
collecting.
This makes this terminalView's speed usable and actually competitive with some existing
console applications.
BTW: It is *much* faster than the MSWindows command.com window,
- so much for the 'slow' Smalltalk ;-)

o  sync
(comment from inherited method)
send all buffered drawing to the device and wait until the device responds

o  waitForOutputToDrain
give terminalView a chance to

queries
o  computePreferredExtent
return my preferred extent - this is computed from my numberOfLines,
numberOfCols and font size

o  isKeyboardConsumer
return true, if the receiver is a keyboard consumer;
Always return true here

o  terminalType
returns a string describing this terminal (usually, this is
passed down to the shell as TERM environment variable).
Here, 'dump' is returned.

reader process
o  readAnyAvailableData
read data from the stream,
and sends me #processInput:n: events if something arrived.
Returns the amount of data read.

o  readerProcessLoop
look for the command's output,
and send me #processInput:n: events whenever something arrives.

o  startReaderProcessNow
Start a reader process, which looks for the commands output,
and sends me #processInput:n: events whenever something arrives.

o  startReaderProcessWhenVisible
Start a reader process, which looks for the commands output,
and sends me #processInput:n: events whenever something arrives.

o  startReaderProcessWhenVisible: whenVisible
Start a reader process, which looks for the commands output,
and sends me #processInput:n: events whenever something arrives.

o  stopReaderProcess
stop the background reader thread

searching
o  startPositionForSearchBackward

o  startPositionForSearchForward

selection handling
o  autoMoveCursorToEndOfSelection
Redefined to return false since the cursor should
not be affected by selecting

o  copySelection
the inherited code would leave the cursor behin the selection;
we always want it to stay at the end.
However: cursorToEndOfText ignores trailing spaces!

o  paste: someText
paste - redefined to send the chars to the shell instead
of pasting into the view

o  selection
if it is a line-wise collection, return multiple lines;

usage example(s):

a full-line (3xclick selection)

sending
o  send: aString
send aString to the underlying program's stdinput

o  sendCR: aString
send aString followed by a return to the underlying program's stdinput

o  sendCharacter: aCharacter
send a single character to the underlying program's stdin

o  sendLine: aString

o  sendLineEnd
OperatingSystem isMSDOSlike ifTrue:[


Demonstration:


    VT52TerminalView open


    VT100TerminalView open


    VT52TerminalView openShell


    VT100TerminalView openShell


    VT100TerminalView openOnCommand:'ls -l'


    VT100TerminalView openOnCommand:'dir'


Examples:


start a shell in the current directory:
  TerminalView openShell
start a shell in a given directory:
  TerminalView openShellIn:(OperatingSystem getHomeDirectory)
start another program current directory:
  TerminalView openOnCommand:'ls -l'
start another program in some given directory:
  TerminalView openOnCommand:'ls' in:'/etc'
specify how to react when the shell terminates:
  TerminalView openOnCommand:'ls' in:'/etc' onExit:[:vt | vt topView destroy]
  TerminalView openOnCommand:'ls' in:'/etc' onExit:[:vt | Dialog information:'finished'. vt topView destroy]
special low level usage: no shell command, interact with the user myself (i.e. read the user's input, and intepret it myself):
  |terminal in out inputLine|

  in := InternalPipeStream new.
  out := InternalPipeStream new.

  terminal := TerminalView openOnInput:in output:out.
  terminal localEcho:true.
  terminal inputTranslateCRToNL:true.
  terminal translateNLToCRNL:true.

  out nextPutLine:'Hello world - please type at me:'.

  [
      inputLine := in nextLine asString.
      inputLine ~= '#exit' ifTrue:[
          out nextPutLine:(Compiler evaluate:inputLine) printString.
      ].
      (inputLine = '#exit') or:[ terminal isOpen not ]
  ] whileFalse.

  terminal topView destroy.


ST/X 7.2.0.0; WebServer 1.670 at bd0aa1f87cdd.unknown:8081; Tue, 23 Apr 2024 20:33:16 GMT