(???)                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.