The ACUTE-TERMINAL-CONTROL library was created to serve my needs when writing an interactive program that creates a more complex terminal interface. In order to clean the source of the aforementioned program a tad, I decided to split the functionality into a library. I also have a large disgust with the use of Curses varieties in Common Lisp programs, as this unnecessarily and substantially complicates using such a program. Risking memory leaks shouldn't be necessary to control a terminal. This library is named as if an illness, but can also be read as fast control of a terminal device. This library is licensed under the GNU Affero General Public License version three. This library uses the CL-ECMA-48 library to generate its controlling sequences and so currently only targets terminals supporting the ECMA-48 control functions used. This library primarily acts as an abstraction over CL-ECMA-48. While this ensures that the control functions are optimized, as CL-ECMA-48 performs this optimization, it is not possible at this level of abstraction to necessarily send the absolutely optimal combination of control functions; this will be elaborated upon later. It is expected that this library may eventually provide an abstraction over multiple lower level libraries in order to support more terminal control sequences and whatnot. This library necessarily has no limit on expanding functionality and the current functionality will now be listed; currently, every function has a lambda list ending in an optional stream argument, defaulting to *STANDARD-OUTPUT* and so that will be omitted in the listings: CURSOR () (SETF CURSOR) (CONS) The CURSOR function returns the current location of the cursor corresponding to the stream as a CONS cell containing the line number in the CAR and the character number in the CDR or NIL if this cannot be determined. This function uses the DEVICE STATUS REPORT control function with an argument of six to obtain an ACTIVE POSITION REPORT control function that is then parsed. This necessarily calls FINISH-OUTPUT and CLEAR-INPUT in order to make way for this. In the case that *STANDARD-OUTPUT* is the stream, *STANDARD-INPUT* is implicitly used for this input; otherwise, the stream should be a TWO-WAY-STREAM. This function will wait for input to be available for less than one half of a second, if it's not immediately available, before returning NIL. If the input does not immediately correspond to an ACTIVE POSITION REPORT or the end of file is reached, this function will return NIL. The (SETF CURSOR) function requires a CONS argument of the form returned by CURSOR. This function uses the CURSOR POSITION control function to reposition the cursor according to its argument, returning that same CONS. DIMENSIONS () The DIMENSIONS function returns the current dimensions of the terminal corresponding to the stream in a form suitable for a call to CURSOR or NIL if this cannot be determined. Due to a hack, this function can be implemented with only ECMA-48 control functions. The current cursor position is saved, the cursor is positioned well beyond the maximum coordinates, currently to 999,999 in both respects, the now current cursor location is obtained and saved, and the previous cursor position is reinstated, returning the other cursor location obtained and hence the dimensions of the terminal. It is expected that this function, having a portable implementation, may also be implemented specially for some implementations supporting it; in such a case, CURSOR would not necessarily be called and so FINISH-OUTPUT and CLEAR-INPUT not necessarily called; it is expected that either the caller should not expect these two functions to necessarily be called or that other implementations will call these two functions regardless, but this is not yet decided nor other implementations present. A return value of NIL is only returned if either of the two calls to CURSOR return NIL and so those same conditions apply. (SETF FOREGROUND) (COLOR) (SETF BACKGROUND) (COLOR) The (SETF FOREGROUND) and (SETF BACKGROUND) functions make use of the SELECT GRAPHIC RENDITION control function to set the foreground and background color, respectively. The available colors are :BLACK, :RED, :GREEN, :YELLOW, :BLUE, :MAGENTA, :CYAN, :WHITE, and :DEFAULT. Additionally, ISO 8613-6 colors may be specified by :TRANSPARENT, a proper list beginning with :RGB or :CMY followed by three integers (An example is (:RGB 128 128 128) for a nice grey.), or a proper list beginning with :CMYK followed by four integers. Currently, all tested terminals misinterpret ISO 8613-6 colors and so a malformed sequence is emitted, as otherwise the color will be parsed incorrectly. The (SETF FOREGROUND) and (SETF BACKGROUND) functions represent an inefficiency with this level of abstraction, compared to the lower level of abstraction provided by CL-ECMA-48. The following code sets the foreground and background to their default values for *STANDARD-OUTPUT* and a character representation is shown of what is sent: (SETF (FOREGROUND) :DEFAULT (BACKGROUND) :DEFAULT) ESCAPE [ 3 9 m ESCAPE [ 4 9 m The following code using CL-ECMA-48 sets the foreground and background to their default values for *STANDARD-OUTPUT* and a character representation is shown of what is sent: (SGR '(39 49)) ESCAPE [ 3 9 ; 4 9 m It is clear that the level of abstraction provided by CL-ECMA-48 results in two less characters when these operations are combined. The general rarity of this, the mere two character inefficiency, and the greater level of abstraction provided by ACUTE-TERMINAL-CONTROL is considered acceptable over this. SCROLL (KEYWORD &OPTIONAL (COUNT 1)) The SCROLL function causes text to scroll in one of the four possible directions designated by KEYWORD with values of :UP, :DOWN, :LEFT, or :RIGHT by COUNT. This function uses and abstracts over the SCROLL UP, SCROLL DOWN, SCROLL LEFT, and SCROLL RIGHT control functions. ERASE () The ERASE function clears the visible contents corresponding to STREAM; as an extension, it will also attempt to clear the contents of the ``scrollback'' if that is supported. This function uses the ERASE IN PAGE control functions with values of two (standard) and three (an extension) to achieve this. The extension is emitted first in the case it is unsupported. RESET () The RESET function resets the terminal to its default state, using the RESET TO INITIAL STATE control function. It is intended that this will primarily be used to erase the contents of the screen and reposition the cursor to the origin, in a single operation. SOUND () The SOUND function causes the terminal to emit a beep, perhaps flash, or otherwise gain the attention of the operator. It does this with the BELL control function. INSERT-MODE () REPLACE-MODE () The INSERT-MODE and REPLACE-MODE functions use the SET MODE and RESET MODE control functions, both with values of four, in order to manipulate INSERTION REPLACEMENT MODE; while modes are deprecated, they are still supported, currently. The INSERT-MODE function has new graphical characters inserted, shifting the remaining contents. The REPLACE-MODE function has new graphical characters overwrite the current contents. The following functions will be shown the same, but have *STANDARD-INPUT* as the default argument, rather than *STANDARD-OUTPUT*. READ-EVENT () READ-EVENT-NO-HANG () The READ-EVENT and READ-EVENT-NO-HANG functions are used to extract more meaningful and higher level input from the STREAM argument. If the CHARACTER input is deemed normal, the character itself will be returned and the STREAM will be put in a state as if only that CHARACTER had been read. Currently, xterm ``X10'' and ``SGR'' mouse reporting is supported. Due to poor design, the ``X10'' mode will likely fail if the mouse coordinates in either dimension are greater than ninety five, but certainly if greater than two hundred and twenty three, as the design of this interface doesn't accomodate dimensions greater than this. In the case of any such mouse reporting failure, NIL is returned. Mouse release reports are treated as mouse click reports. A mouse event is reported as a dotted list of the form (:MOUSE Z X Y . W) in which Z is the number corresponding to the mouse button used, limited to one to three for xterm, X is the column position (X-coordinate), Y is the row position (Y-coordinate) calculated from the origin, and W is a list containing modifier keys used and NIL otherwise; modifier key support is in place for :SHIFT, :CONTROL, and :META. In the case of a ``function key'' press, a CONS of the form (:FUNCTION . N) will be returned, in which N is the number of the function key pressed. In the case of holding the meta key while pressing a key, a CONS of the form (:META . C) will be returned, in which C is the character entered. Using the ``arrow keys'' will result in corresponding results of :UP, :DOWN, :LEFT, or :RIGHT. Use of the ``Page Up'' key will result in :PAGE-UP, whereas ``Page Down'' will result in :PAGE-DOWN. Using the ``Home'' key will result in :START, whereas ``End'' will result in :END. The ``Delete'' key is made to correspond to #\Rubout, if this is available. Other input types denoted by single keywords will be considered, but are being chosen carefully to avoid prefering a poor platform. The two functions are identical, sans READ-EVENT using READ-CHAR for input and READ-EVENT-NO-HANG using READ-CHAR-NO-HANG; thus, one will wait for input and one will expect input to already be present. This is the only function which does not properly parse the NULL control function in all possible instances. ENABLE-SYSTEM-BUFFERING () DISABLE-SYSTEM-BUFFERING () ENABLE-SYSTEM-ECHOING () DISABLE-SYSTEM-ECHOING () The ENABLE-SYSTEM-BUFFERING, DISABLE-SYSTEM-BUFFERING, ENABLE-SYSTEM-ECHOING, and DISABLE-SYSTEM-ECHOING functions control system buffering and system echoing. It is strongly recommended that these functions be avoided in all cases, as they're inherently unportable and their very semantics are tied to poor operating systems. That is, the very notion of system buffering is poor and stupid. After calling these functions, no Common Lisp input routines, such as READ-CHAR or READ-LINE can be assumed to behave as before. On a normally buffered implementation, READ-CHAR will require multiple key presses, but not under a normally unbuffered implementation. On a normally buffered implementation, READ-LINE may not perform any line editing and may be implemented in a way to benefit from the buffering which will break when that is not present, but under a normally unbuffered implementation will likely provide some line editing functionality that will in turn break under system buffering. The distinction is itself poor. System echoing, in comparison, is still a poor idea, but admittedly significantly less so and should not impact the semantics of a Common Lisp program, being tied only to how the contents of the terminal appear with respect to input. Regardless, it should still be avoided due to its unportable nature. Currently, only Allegro Common Lisp, CMU Common Lisp, Steel Bank Common Lisp, and Embeddable Common Lisp are fully supported with respect to these four functions, with partial support currently in Clozure Common Lisp. It seems to be the case that POPLOG and CLISP only provide fundamentally incompatible mechanisms for similar functionality and so can't be supported. As I don't agree with the notion that these functions should be able to fail, they use IGNORE-ERROR around the implementation-specific code. The system buffering is still being tested with termios flags and doesn't work quite correctly, yet. Currently supported functionality is as follows: the ability to recall the cursor position; the ability to arbitrarily reposition the cursor; the ability to recall the terminal dimensions; the ability to change the foreground and background colors; control over whether insertion or overwriting is the input mode; the ability to have the terminal beep or flash or whatnot; and extra key and mouse support. It is also supported to have control over system input echoing and control over whether input is buffered by the system and so whether all reading will require, say, the press of the enter key. However, the addition of this functionality is very much resented and should only be used if truly nothing else will work; it is recommended that this idiotic system design be worked around outside of the Common Lisp, such as by a program that launches the Common Lisp implementation to launch the Common Lisp program. This is, by far, the simplest and best way to do this, if the Common Lisp program doesn't require the ability to change this behavior while running, which is usually the case; even then, it should still be done this way. Copyright (C) 2018,2019 Prince Trippy Verbatim copying and redistribution of this document is permitted provided this notice is preserved. .