jurgen_defurne <jurgen.defurne@pandora.be> wrote:
+---------------
| My problem is that I want to emulate a keyboard, not a terminal, in
| CL, sending and interpreting each character typed on the keyboard. One
| solution I have found is to use stty, but this interferes with using
| e.g. CLISP on the command line. I know that CLISP e.g. has separate
| extensions to deal with the issue, but then my code gets less portable
| across compilers. I haven't found a similar extension in SBCL ...
+---------------
The non-portability is simply something you're going to have to
live with, since TTY handling isn't part of the ANSI standard.
Feature expressions should be able to encapsulate the non-portable
buts easily enough, though. I don't know what facilities CLISP
provides, but here's what I use in CMUCL when running on FreeBSD:
(use-package :alien)
(use-package :unix)
(defun read-char-no-echo-cbreak (&optional (stream *query-io*))
(with-alien ((old (struct termios))
(new (struct termios)))
(let ((e0 (unix-tcgetattr 0 old))
(e1 (unix-tcgetattr 0 new))
(bits (logior tty-icanon tty-echo tty-echoe
tty-echok tty-echonl)))
(declare (ignorable e0 e1))
(unwind-protect
(progn
(setf (slot new 'c-lflag) (logandc2 (slot old 'c-lflag) bits))
(setf (deref (slot new 'c-cc) vmin) 1)
(setf (deref (slot new 'c-cc) vtime) 0)
(unix-tcsetattr 0 tcsadrain new)
(read-char stream))
(unix-tcsetattr 0 tcsadrain old)))))
Depending on your exact application, you might want to change that
READ-CHAR near the end to a READ-CHAR-NO-HANG [and also change the
function name], but for a character-at-a-time command-style program
such as an editor, pager, mail reader, or non-realtime game, the
READ-CHAR should be fine.
[Also, if you're going to be doing this a lot you'll probably
want to cache the "new" and "old" TERMIOS structs in globals
to reduce consing.]
-Rob
-----
Rob Warnock <rpw3@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607