1 Introduction
1.1 Demo
1.2 Simple Example
2 Interface
2.1 charterm Object
charterm?
2.2 Opening and Closing
current-charterm
open-charterm
close-charterm
with-charterm
2.3 Information
charterm-screen-size
2.4 Video
2.4.1 Cursor
charterm-cursor
charterm-newline
2.4.2 Displaying
charterm-display
2.4.3 Video Attributes
charterm-normal
charterm-inverse
charterm-bold
charterm-underline
charterm-blink
2.4.4 Clearing
charterm-clear-screen
charterm-clear-line
charterm-clear-line-left
charterm-clear-line-right
2.4.5 Line Insert and Delete
charterm-insert-line
charterm-delete-line
charterm-bell
2.5 Keyboard
charterm-byte-ready?
charterm-read-key
3 Misc.
charterm-demo
4 Known Issues
5 History
6 Legal
Version: 1:0

charterm: Character-cell Terminal Interface in Racket

Neil Van Dyke

 (require (planet neil/charterm:1:0))

1 Introduction

The charterm package provides a Racket interface for character-cell video display terminals on Unix-like systems – both terminal emulators like xterm, and some older hardware terminals (even the venerable DEC VT100). Currently, it implements a subset of xterm’s features.
This package could be built upon to implement a status/management console for a Racket-based server process (perhaps run from an SSH session, or perhaps in screen), a lightweight user interface for a systems tool, a command-line REPL, a text editor, and, most importantly, a Rogue-like application.
The charterm package does not include any native code in the Racket process, such as through the Racket FFI or C extensions. It is implemented in pure Racket code except for briefly calling out to /bin/stty at startup time and shutdown time.
Fun fact: “charterm” is short for “Character Terminal,” not for “Chart? Erm...” For doing charts, see the PLoT library by Neil Toronto.

1.1 Demo

For a demonstration, the following command, run from a terminal, should install the charterm package (if not already installed), and run the demo:
  racket -p neil/charterm -l racket -e "(charterm-demo)"
Note: Although charterm-demo includes an editable text field, as proof of concept, the current version of charterm does not provide editable text fields as reusable functionality.

1.2 Simple Example

Here’s your first charterm program:
#lang racket/base
 
(require (planet neil/charterm:1))
 
(with-charterm
 (charterm-clear-screen)
 (charterm-cursor 10 5)
 (charterm-display "Hello, ")
 (charterm-bold)
 (charterm-display "you")
 (charterm-normal)
 (charterm-display ".")
 (charterm-cursor 1 1)
 (charterm-display "Press a key...")
 (let ((key (charterm-read-key)))
   (charterm-cursor 1 1)
   (charterm-clear-line)
   (printf "You pressed: ~S\r\n" key)))
Now you’re living the dream of the ’70s.

2 Interface

2.1 charterm Object

(charterm? x)  boolean?
  x : any/c
Predicate for whether or not x is a charterm.

2.2 Opening and Closing

(current-charterm)  (or/c #f charterm?)
(current-charterm ct)  void?
  ct : (or/c #f charterm?)
This parameter provides the default charterm for most of the other procedures. It is usually set automatically by call-with-charterm, with-charterm, open-charterm, and close-charterm.
(open-charterm [#:tty tty    
  #:current? current?])  charterm?
  tty : (or/c #f path-string?) = #f
  current? : boolean? = #t
Returns an open charterm object, by opening I/O ports on the terminal device at tty (or, if #f, file "/dev/tty"), and setting raw mode and disabling echo (via "/bin/stty"). If current? is true, the current-charterm parameter is also set to this object.
(close-charterm [#:charterm ct])  void?
  ct : charterm? = (current-charterm)
Closes ct by closing the I/O ports, and undoing open-charterm’s changes via "/bin/stty". If current-charterm is set to ct, then that parameter will be changed to #f for good measure. You might wish to use with-charterm instead of worrying about calling close-charterm directly.
Note: If you exit your Racket process without properly closing the charterm, your terminal may be left in a crazy state. You can fix it with the command:
  stty sane
If only ex-lovers could be fixed as easily as ex-processes.
(with-charterm expr? ...)
Opens a charterm and evaluates the body expressions in sequence with current-charterm set appropriately. When control jumps out of the body, in a manner of speaking, the charterm is closed.

2.3 Information

(charterm-screen-size [#:charterm ct])
  
(or/c #f exact-nonnegative-integer?)
(or/c #f exact-nonnegative-integer?)
  ct : charterm? = (current-charterm)
Attempts to get the screen size, in character columns and rows. If unable to get a value, then #f is returned for the value.
If you find this returning (#f, #f), then (80, 24) might be a good fallback.

2.4 Video

2.4.1 Cursor

(charterm-cursor x y [#:charterm ct])  void?
  x : exact-positive-integer?
  y : exact-positive-integer?
  ct : charterm? = (current-charterm)
Positions the cursor at column x, row y, with the upper-left character cell being (1, 1).
(charterm-newline [#:charterm ct])  void?
  ct : charterm? = (current-charterm)
Sends a newline to the terminal. This is typically a CR-LF sequence.

2.4.2 Displaying

(charterm-display [#:charterm ct    
  #:width width    
  #:pad pad    
  #:truncate truncate]    
  arg ...)  void?
  ct : charterm? = (current-charterm)
  width : (or/c #f exact-positive-integer?) = #f
  pad : (or/c 'width boolean?) = 'width
  truncate : (or/c 'width boolean?) = 'width
  arg : any/c
Displays each arg on the terminal, as if formatted by display, with the exception that unprintable or non-ASCII characters might not be displayed. (The exact behavior of what is permitted is expected to change in a later version of charterm, so avoid trying to send your own control sequences or using newlines, making assumptions about non-ASCII, etc.)
If width is a number, then pad and truncate specify whether or not to pad with spaces or truncate the output, respectively, to width characters. When pad or width is 'width, that is a convenience meaning “true if, and only if, width is not #f.”

2.4.3 Video Attributes

(charterm-normal [#:charterm ct])  void?
  ct : charterm? = (current-charterm)
(charterm-inverse [#:charterm ct])  void?
  ct : charterm? = (current-charterm)
(charterm-bold [#:charterm ct])  void?
  ct : charterm? = (current-charterm)
(charterm-underline [#:charterm ct])  void?
  ct : charterm? = (current-charterm)
(charterm-blink [#:charterm ct])  void?
  ct : charterm? = (current-charterm)
Sets the video attributes for subsequent writes to the terminal. In this version of charterm, each is mutually-exclusive, so, for example, setting bold clears inverse. Note that not all terminals support all of these.

2.4.4 Clearing

(charterm-clear-screen [#:charterm ct])  void?
  ct : charterm? = (current-charterm)
Clears the screen, including first setting the video attributes to normal, and positioning the cursor at (1, 1).
(charterm-clear-line [#:charterm ct])  void?
  ct : charterm? = (current-charterm)
(charterm-clear-line-left [#:charterm ct])  void?
  ct : charterm? = (current-charterm)
(charterm-clear-line-right [#:charterm ct])  void?
  ct : charterm? = (current-charterm)
Clears text from the line with the cursor, or part of the line with the cursor.

2.4.5 Line Insert and Delete

(charterm-insert-line [count #:charterm ct])  void?
  count : exact-positive-integer? = 1
  ct : charterm? = (current-charterm)
Inserts count blank lines at cursor. Note that not all terminals support this.
(charterm-delete-line [count #:charterm ct])  void?
  count : exact-positive-integer? = 1
  ct : charterm? = (current-charterm)
Deletes count blank lines at cursor. Note that not all terminals support this.
(charterm-bell [#:charterm ct])  void?
  ct : charterm? = (current-charterm)
Rings the terminal bell. This bell ringing might manifest as a beep, a flash of the screen, or nothing.

2.5 Keyboard

(charterm-byte-ready? [#:charterm ct])  boolean?
  ct : charterm? = (current-charterm)
Returns true/false for whether at least one byte is ready for reading (either in a buffer or on the port) from ct. Note that, since some keys are encoded as multiple bytes, just because this procedure returns true doesn’t mean that charterm-read-key won’t block temporarily because it sees part of a potential multiple-byte key encoding.
(charterm-read-key [#:charterm ct    
  #:timeout timeout])  (or #f char? symbol?)
  ct : charterm? = (current-charterm)
  timeout : (or/c #f positive?) = #f
Reads a key from ct, blocking indefinitely or until sometime after timeout seconds has been reached, if timeout is non-#f. If timeout is reached, #f is returned.
Many keys are returned as characters, especially ones that correspond to printable characters. For example, the unshifted “q” key is returned as character #\q. Other keys are returned as symbols, such as 'return, 'esc, 'f1, 'shift-f12, 'right, and many others.
Since some keys are sent as ambiguous sequences, charterm-read-key employs separate timeouts internally, such as to disambuate the Esc key (byte sequence 27) from what on some terminals would be the F10 key (bytes sequence 27, 91, 50, 49, 126).

3 Misc.

(charterm-demo [#:tty tty])  void?
  tty : (or/c #f path-string?) = #f
This procedure runs a demonstration program using charterm. Specifically, it reports what keys you pressed, while letting you edit a text field, and while displaying a clock. The clock is updated roughly once per second, and is not updated during heavy keyboard input, such as when typing fast. The demo responds to changing terminal sizes, such as when an xterm is window is resized. It also displays the determined terminal size, and some small tests of the #:width argument to charterm-display. Exit the demo by pressing the Esc key.

4 Known Issues

5 History

6 Legal

Copyright 2012 Neil Van Dyke. This program is Free Software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See http://www.gnu.org/licenses/ for details. For other licenses and consulting, please contact the author.