(???) A Rebuttal of an Article Involving the CL-CHARMS Common Lisp Library (TN3) he following article is a rebuttal of the following article: (HTM) ttp://turtleware.eu/posts/cl-charms-crash-course.html (TN3) his article can also serve as a ``crash course'' in using my ACUTE-TERMINAL-CONTROL. (IMG) recommend reading that article first, as the structure here mimicks the structure there. This is (???) n article made with the intent of thoroughly defeating the notion that linking to a C library for (SND) omething so simple and benign as sending control codes to a terminal device is anything resembling (???) n adequate or sensible act. I will do this by showing, step-by-step, how my ACUTE-TERMINAL-CONTROL (???) ommon Lisp library, written entirely in Common Lisp, is an entirely better mechanism for this than (???) rapping Ncurses in a Common Lisp facade. Risking memory leaks shouldn't be necessary to control a (???) erminal. (TN3) he only necessary software you need for this is a Common Lisp implementation that supports a (SND) even-bit character set (ASCII is fine and likely supported.), followed by CL-ECMA-48 (This mplements the ECMA-48 standard and ACUTE-TERMINAL-CONTROL uses it.), followed lastly by (???) CUTE-TERMINAL-CONTROL itself. I won't be involving SWANK in this, as that would unnecessarily (???) omplicate the matter. If you ever package a piece of software like this that manipulates a (???) erminal device, you likely won't be using SWANK anyway. (TN3) o start, this is a basic program which will clear the terminal screen; display ``Hello world!'' (???) eginning at location 10,10; and wait until the ``q'' key is pressed before returning: (???) defun hello-world () (???) (disable-system-echoing) (???) (disable-system-buffering) (???) (erase) (???) (setf (cursor) '(10 . 10)) (???) (write-string "Hello world!") (???) (finish-output) (???) (loop (if (char= #\q (read-char)) (???) (return))) (???) (values)) (???) ow, compare this with the HELLO-WORLD in the other article. In order to hide Ncurses nitialization and whatnot, since it's a C library, it's generally required to use a (???) L-CHARMS:WITH-CURSES macro in order to obfuscate this. CL-CHARMS also enforces a window (???) bstraction and it's apparently necessary to repeatedly write the same output in a loop; I'm gladly (???) nfamiliar with Ncurses and so don't know why this would ever be the case. Regardless, there's no (???) ontinuous computation and so no need to use SLEEP to decrease it; note how standard Common Lisp (???) unctions can be used to interact with the terminal, rather than specialized such functions; this (PNG) rogram simply communicates with *STANDARD-OUTPUT* and *STANDARD-INPUT*. (???) ow, before continuing, I also find it necessary to point out my distaste for the functions I've (???) ritten which control system echoing and system buffering. Not only are these arguably rather (???) alformed concepts, but they are entirely impossible to implement in standard Common Lisp, due to (???) he design of the systems that impose this. I strongly urge every programmer to, instead, (???) anipulate these settings as necessary before the program is even entered. It's likely that a (???) ommon Lisp program packaged for use would be begun by a program more amicable to the native system; (???) nder POSIX, I use ``s=$(stty -g); stty raw -echo'' for this and later restore the previous settings (???) pon exit; my Common Lisp stays free of these concerns and it is merely the relatively short and (???) nimportant native program that must capitulate to any considerations of the underlying environment. (TN3) he lower level interface ACUTE-TERMINAL-CONTROL uses is CL-ECMA-48, which implements the ECMA-48 (SND) tandard. Rather than reading a ``HOWTO'', you should read the document, which is an actual (SND) tandard that can be relied upon and with a canonical defining document. Furthermore, by (???) amiliarizing yourself with this, you'll understand how simple it truly is to build an interface (???) ith a suitable terminal; it's not some Herculean task requiring gargantuan libraries after all. (???) CUTE-TERMINAL-CONTROL defines no function for drawing windows; in Common Lisp, it's easy enough to (DOC) efine this by one's self. Follows is a trivial definition; as an optimization, this will be (DOC) efined in terms of CL-ECMA-48, sans the use of CURSOR to obtain the current coordinates: (???) defun draw-window-border (width height &aux (cursor (cursor))) (???) (or cursor (error "The stream doesn't seem to correspond to a terminal device.")) (???) (assert (> width 1) (width)) (???) (assert (> height 1) (height)) (???) (write-char #\+) (???) (if (> width 2) (???) (write-char #\-)) (???) (if (> width 3) (???) (repeat (- width 3))) (???) (write-char #\+) (???) (if (> height 2) (???) (dotimes (h (- height 2)) (declare (ignorable h)) (???) (next-line) (???) (cursor-character-absolute (car cursor)) (???) (write-char #\|) (???) (cursor-character-absolute (+ -1 width (car cursor))) (???) (write-char #\|))) (???) (next-line) (???) (cursor-character-absolute (car cursor)) (???) (write-char #\+) (???) (if (> width 2) (???) (write-char #\-)) (???) (if (> width 3) (???) (repeat (- width 3))) (???) (write-char #\+) (???) (values)) (TN3) here are clearly ways this could be improved or specialized to the specific program, but this is (SND) ufficient to illustrate the concept and how simple it is. It also demonstrates optimization (???) pportunities that are lost at higher levels, such as using the REPEAT control function rather than (???) epeatedly writing a single character or creating a string all of one character to write. This (???) unction requires system echoing and system buffering to be disabled in order to work properly. (TN3) here's no need to have a START-COLORS function, as this is both an ugly initialization function and (???) terminal should ignore control functions it can't fulfill; so, we won't be returning C error (???) odes, either. Similarly, color codes are defined by integers, rather than C definitions. The (???) curses abstraction of color pairs is also unnecessary. Colors are handled in (???) CUTE-TERMINAL-CONTROL with the (SETF BACKGROUND) and (SETF FOREGROUND) functions using named (SND) ymbols; for colors outside of the ECMA-48 standard, ISO 8613-6 colors may be used. (???) ere is a function similar to DRAW-WINDOW-BORDER that instead draws a blank colored rectangle: (???) defun draw-window-background (width height color &aux (cursor (cursor))) (???) (or cursor (error "The stream doesn't seem to correspond to a terminal device.")) (???) (assert (> width 0) (width)) (???) (assert (> height 0) (height)) (???) (setf (background) color) (???) (dotimes (h height) (declare (ignorable h)) (???) (write-char #\space) (???) (repeat (1- width)) (???) (next-line) (???) (cursor-character-absolute (car cursor))) (???) (values)) (???) ollows is a ``pretty'' hello world, elaborated on from earlier: (???) defun pretty-hello-world () (???) (disable-system-echoing) (???) (disable-system-buffering) (???) (erase) (???) (setf (cursor) '(10 . 10)) (???) (draw-window-background 50 15 :blue) (???) (setf (foreground) :white (???) (background) :blue) (???) (setf (cursor) '(10 . 10)) (???) (write-string "Hello world!") (???) (setf (foreground) :red (???) (background) :black) (???) (setf (cursor) '(11 . 10)) (???) (write-string "Hello world!") (???) (setf (foreground) :default (???) (background) :default) (???) (finish-output) (???) (loop (if (char= #\q (read-char)) (???) (return))) (???) (values)) (???) ow here is an ``amazing'' hello world: (???) defun amazing-hello-world (&aux (foreground '#1=(:white :blue . #1#)) (???) (background '#2=(:black :red . #2#))) (???) (disable-system-echoing) (???) (disable-system-buffering) (???) (erase) (???) (setf (cursor) '(10 . 10)) (???) (draw-window-background 50 15 :blue) (???) (setf (cursor) '(10 . 10)) (???) (loop (setf (foreground) (car foreground) (???) (background) (car background) (???) foreground (cdr foreground) (???) background (cdr background)) (???) (write-string "Hello world!") (???) (cursor-character-absolute 10) (???) (force-output) (???) (if (eql #\q (read-char-no-hang)) (???) (return)) (???) (sleep 1)) (???) (setf (foreground) :default (???) (background) :default) (???) (values)) (???) ow, the obvious issue is a lack of responsiveness caused by the SLEEP. The obvious solution is to ntroduce multiple threads of program execution. Since this doesn't use Ncurses, it doesn't matter (???) hat Ncurses isn't ``thread-safe''. It's simple to use BORDEAUX-THREADS here; due to the API (DOC) escription, a special variable will be used: (???) defparameter improved-amazing-hello-world-thread (bt:current-thread)) (???) defun improved-amazing-hello-world (&aux (foreground '#1=(:white :blue . #1#)) (???) (background '#2=(:black :red . #2#))) (???) (bt:make-thread (lambda () (???) (loop (if (eql #\q (read-char)) (???) (bt:interrupt-thread improved-amazing-hello-world-thread (???) (lambda () (throw 'end nil))))))) (???) (disable-system-echoing) (???) (disable-system-buffering) (???) (erase) (???) (setf (cursor) '(10 . 10)) (???) (draw-window-background 50 15 :blue) (???) (setf (cursor) '(10 . 10)) (???) (catch 'end (loop (setf (foreground) (car foreground) (???) (background) (car background) (???) foreground (cdr foreground) (???) background (cdr background)) (???) (write-string "Hello world!") (???) (cursor-character-absolute 10) (???) (force-output) (???) (sleep 1))) (???) (setf (foreground) :default (???) (background) :default) (???) (values)) (???) ow, I will not bother with reconstructing a simpler form of the following code in that other (???) rticle; part of the function of this article is showing that requiring Ncurses, and immediately (???) aking your otherwise nice Common Lisp perfectly unportable and dependent on C, is ridiculous for (SND) uch basic programming tasks. Now, part of writing a new terminal program is, firstly, (???) nderstanding that this is technology from the same era as Common Lisp and, like Common Lisp, was (DOC) esigned with some foresight that keeps it nice and useful in modern times; however, it will nevitably shape the interface you present and a suitably complex interface would be better suited (???) y a true graphical interface; you're better off creating a unique interface, rather than (SND) hoehorning buttons and whatnot into the model. Secondly and unlike Common Lisp, the terminal (SND) tandards are enforced and progressed rather solely by the implementors of the terminals and the (???) term developers in particular have slowly piled on poorly designed interfaces that must be worked (???) round in order to use certain features, because they had little sense of aesthetics. No terminal (IMG) 'm aware of properly parses ISO 8613-6 colors, as an example, and require an invalid sequence to be (SND) ent instead. (IMG) will now discuss comparable mouse functionality. This isn't hard either; it's simply poor design (???) n the part of the xterm experts that makes it a bother; there's four mouse APIs: two of which are (???) roken and not recommended; one which is still broken, but recommended, and the only option in some (???) ases; and one which is recommended, the least broken, and the only one that should actually behave (???) orrectly in all circumstances on modern terminals with higher resolutions. (???) relevant enabling function definition is this ENABLE-MOUSE-REPORTING; compare it to START-MOUSE (???) rom the other article; this merely uses the SET-MODE control function with parameters dictated by (???) term's interfaces: (???) defun enable-mouse-reporting (&optional (stream *standard-output*) &aux (*standard-output* stream)) (???) "Enable mouse reporting events." (???) (set-mode '(#\? 9 1000 1006))) (IMG) put a good effort to make the majority of ACUTE-TERMINAL-CONTROL functions SETF functions, as this s a nice and familiar interface for Common Lisp. For reading in higher level events, it's not (???) ecessary to call any initialization function; I merely employ the READ-EVENT and READ-EVENT-NO-HANG (???) unctions. Through these two simple functions, higher level events can be recognized and handled. (TN3) he ACUTE-TERMINAL-CONTROL documentation is much more comprehensive. (???) ow, in conclusion, I've demonstrated that defining terminal interfaces is a rather simple task. (TN3) he only reason one would employ an entire C library for something so simple is pure laziness. I (???) onsider myself to be a lazy programmer and, being too lazy to read the Ncurses documentation and (DOC) eal with CFFI errors, I defined my own simple interface for much of the same thing. The CL-ECMA-48 (???) nd ACUTE-TERMINAL-CONTROL source code, together, is less than one thousand lines of Common Lisp and s well documented. So ends my rebuttal of that CL-CHARMS Crash Course.