From: Bigos on
On Nov 15, 1:56 am, r...(a)rpw3.org (Rob Warnock) wrote:
> Bigos  <ruby.obj...(a)googlemail.com> wrote:
>
> +---------------
> | r...(a)rpw3.org (Rob Warnock) wrote:
> ...[how to do raw/cbreak input from within CMCUL]...
> | > As I said, this is CMUCL-specific, but almost any CL implementation
> | > should provide you *some* way to make "ioctl()" calls...
> |
> | I use sbcl and it doesn't seem to work for me.
> +---------------
>
> (*Ahem!*) As I *SAID*, my code is CMUCL-specific, but almost any CL
> implementation should provide you *some* way to make "ioctl()" calls.
>
> SBCL certainly provides such a way, but it's not at all surprising
> that it's *different* from CMUCL -- see the SBCL documentation for
> how to do the corresponding thing.
>
> +---------------
> | Also I am surprised that you need so much code for such seemingly
> | trivial function. I the other language I use it's only 3 lines of
> | code.
> |
> |     system("stty raw -echo") #execute command in subshell raw mode, no
> | echo
> |     char=STDIN.getc          #read character from standard input
> |     system("stty -raw echo") #reset terminal mode
> +---------------
>
> You're "fork()/exec()"-ing *two* external programs!!
> My example code does everything those two external
> program executions do *inside* the original process.
>
> Anyway, who cares how much code it is? You're going to
> use an application-specific wrapper function around it
> anyway, aren't you?
>
> +---------------
> | Is something similar possible with sbcl?
> +---------------
>
> Yes. RTFM.
>
> -Rob
>
> -----
> Rob Warnock                     <r...(a)rpw3.org>
> 627 26th Avenue                 <URL:http://rpw3.org/>
> San Mateo, CA 94403             (650)572-2607

From: Bigos on
On Nov 15, 1:56 am, r...(a)rpw3.org (Rob Warnock) wrote:
> Bigos  <ruby.obj...(a)googlemail.com> wrote:
>
> +---------------
> | r...(a)rpw3.org (Rob Warnock) wrote:
> ...[how to do raw/cbreak input from within CMCUL]...
> | > As I said, this is CMUCL-specific, but almost any CL implementation
> | > should provide you *some* way to make "ioctl()" calls...
> |
> | I use sbcl and it doesn't seem to work for me.
> +---------------
>
> (*Ahem!*) As I *SAID*, my code is CMUCL-specific, but almost any CL
> implementation should provide you *some* way to make "ioctl()" calls.
>
> SBCL certainly provides such a way, but it's not at all surprising
> that it's *different* from CMUCL -- see the SBCL documentation for
> how to do the corresponding thing.
>
> +---------------
> | Also I am surprised that you need so much code for such seemingly
> | trivial function. I the other language I use it's only 3 lines of
> | code.
> |
> |     system("stty raw -echo") #execute command in subshell raw mode, no
> | echo
> |     char=STDIN.getc          #read character from standard input
> |     system("stty -raw echo") #reset terminal mode
> +---------------
>
> You're "fork()/exec()"-ing *two* external programs!!
> My example code does everything those two external
> program executions do *inside* the original process.
>
> Anyway, who cares how much code it is? You're going to
> use an application-specific wrapper function around it
> anyway, aren't you?
>
> +---------------
> | Is something similar possible with sbcl?
> +---------------
>
> Yes. RTFM.
>
> -Rob
>
> -----
> Rob Warnock                     <r...(a)rpw3.org>
> 627 26th Avenue                 <URL:http://rpw3.org/>
> San Mateo, CA 94403             (650)572-2607

yes, I have READ THE FRACTIOUS MANUAL and my problem is that I don't
understand it. When I tried to write my own code based on my
understanding of the documentation and all I got was error messages
having something to do with provided arguments. I found some code
snippet on the web, got rid of error messages, but still didn't
achieve expected behaviour.

(defvar char1)
(sb-ext:run-program "stty raw -echo" nil :input t :output t :wait
nil :pty t )
(setf char1 (read-char ))
(sb-ext:run-program "stty -raw echo" nil :input t :output t :wait
nil :pty t )

(format t "~a~%" char1)

The code fragment above still requires pressing enter to work, so my
expected trick didn't work.

Please be patient with me. Some of you are experts and my daft
questions might annoy you, but I please understand, I will not begin
to understand Lisp documentation overnight. Some time will have to
pass before I get to your level. Lot of it still feels like a foreign
language to me. I spent much time on Friday and Saturday looking for
the answers, and if I have managed to find the answers myself I
wouldn't bother anyone.

Regards,
Jack
From: Rob Warnock on
Bigos <ruby.object(a)googlemail.com> wrote:
+---------------
| rpw3(a)rpw3.org (Rob Warnock) wrote:
| > Bigos �<ruby.obj...(a)googlemail.com> wrote:
| > +---------------
| > | I use sbcl and it doesn't seem to work for me.
| > +---------------
| >
| > (*Ahem!*) As I *SAID*, my code is CMUCL-specific, but almost any CL
| > implementation should provide you *some* way to make "ioctl()" calls.
| >
| > SBCL certainly provides such a way, but it's not at all surprising
| > that it's *different* from CMUCL -- see the SBCL documentation for
| > how to do the corresponding thing.
+---------------

By "corresponding thing" I did *not* mean the RUN-PROGRAM hack, but
rather "the corresponding way in SBCL to do terminal ioctl()s *within*
the SBCL process without running an external program".

What does (APROPOS "TCGETATTR") say in SBCL? Ditto (APROPOS "TCSETATTR")?
Those are the routines you should be looking to use.

Or if those aren't there, what about (APROPOS "IOCTL")? I strongly suspect
at least SB-UNIX:UNIX-IOCTL is there, though you might have to figure out
how to use it to invoke TCGETATTR/TCSETATTR/etc..

+---------------
| yes, I have READ THE FRACTIOUS MANUAL and my problem is that I don't
| understand it. When I tried to write my own code based on my
| understanding of the documentation and all I got was error messages
| having something to do with provided arguments. I found some code
| snippet on the web, got rid of error messages, but still didn't
| achieve expected behaviour.
....
| (sb-ext:run-program "stty raw -echo" nil :input t :output t :wait
| nil :pty t )
+---------------

Well, there's a big part of your problem: You're supplying the wrong
args to SB-EXT:RUN-PROGRAM! The first args *isn't* a shell command,
but an absolute program pathname string [but see "p.s." below], and
the second arg [which you set NIL] is a *list* of string arguments
to the command. And :WAIT NIL means that you're *not* waiting until
the program finishes before doing the next thing [your READ-CHAR],
so no *wonder* it doesn't work!! And you don't want :PTY T, since you
want "stty" to work on the *current* terminal, not some random PTY.
Try this instead:

(sb-ext:run-program "/bin/stty"
'("-icanon" "-echo" "-echoe" "-echok" "-echonl")
:input t :output t :error t :wait t)

and then of course afetr READ-CHAR to turn echoing & line-editing
back on:

(sb-ext:run-program "/bin/stty"
'("icanon" "echo" "echoe" "echok" "echonl")
:input t :output t :error t :wait t)

Note: These work exactly as you seem to want them to on CMUCL, albeit
using EXT:RUN-PROGRAM rather than SB-EXT:RUN-PROGRAM, but there might
be some reason it wouldn't work on SBCL. [I don't know, not being an
SBCL user...]

In any case, do a (DESCRIBE 'SB-EXT:RUN-PROGRAM) and study the output
carefully.

+---------------
| The code fragment above still requires pressing enter to work, so my
| expected trick didn't work.
+---------------

Of course not: (1) The first SB-EXT:RUN-PROGRAM had the wrong args,
and (2) you didn't wait for the command to finish before doing READ-CHAR.

Anyway, I strongly recommend that you *not* continue to try the external
program method for twiddling the terminal state, but instead learn how
to do [or find an example somewhere of doing] "ioctl()"s from *within*
SBCL [approximating more or less my code for CMUCL]. That's more likely
to be reliable.


-Rob

p.s. I see that with SB-EXT:RUN-PROGRAM you can use the keyword arg
:SEARCH T to look in your $PATH instead of having to provide an
absolute path. That is, instead of the first example above you
*should* be able to do this:

(sb-ext:run-program "stty"
'("-icanon" "-echo" "-echoe" "-echok" "-echonl")
:search t :input t :output t :error t :wait t)

p.p.s. One more hint. Try running this:

(sb-ext:run-program "stty" '("-a")
:search t :input t :output t :error t :wait t)

If that works -- that is, prints out the state of your terminal --
but the examples which alter the TTY state *don't* appear to work,
then SBCL may be saving/restoring the terminal state across sub-process
executions [the way some shells do], in which case the external program
method for twiddling the terminal state is *never* going to work!!
In that case you *must* learn & use the internal "ioctl()" method.

-----
Rob Warnock <rpw3(a)rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607

From: Thomas F. Burdick on
On Nov 14, 2:28 pm, r...(a)rpw3.org (Rob Warnock) wrote:

> Here's a little function I hacked up in CMUCL some years ago when I
> needed single character terminal input for an interactive program
> [running on FreeBSD & Linux]:
>
> (use-package :alien)
> (use-package :unix)
>
> #| What "less" does:
> s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
> s.c_oflag |=  (OPOST|ONLCR|TAB3);
> s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
> s.c_cc[VMIN] = 1;
> s.c_cc[VTIME] = 0;
> |#
>
> (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)))))

[snip]

> As I said, this is CMUCL-specific, but almost any CL implementation
> should provide you *some* way to make "ioctl()" calls...

Indeed, and the translation to use the SB-POSIX package (sb-unix being
internals) was dead simple. Where Rob's CMUCL version stack allocates
the alien structures, this one heap allocates both them and their CLOS
wrappers. If that were to ever be a performance issue, I bet you'd
want to refactor this into a with-no-echo-cbreak macro and just call
read-char directly without twiddling the terminal on every read.

(require :sb-posix)
(import '(sb-posix:termios sb-posix:termios-lflag sb-posix:termios-cc
sb-posix:tcgetattr sb-posix:tcsetattr
sb-posix:tcsadrain
sb-posix:icanon sb-posix:echo sb-posix:echoe sb-posix:echok
sb-posix:echonl sb-posix:vmin sb-posix:vtime))

#| What "less" does:
s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
s.c_oflag |= (OPOST|ONLCR|TAB3);
s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
s.c_cc[VMIN] = 1;
s.c_cc[VTIME] = 0;
|#
(defun read-char-no-echo-cbreak (&optional (stream *query-io*))
(let ((old (tcgetattr 0))
(new (tcgetattr 0))
(bits (logior icanon echo echoe echok echonl)))
(unwind-protect
(progn
(setf (termios-lflag new) (logandc2 (termios-lflag old) bits)
(aref (termios-cc new) vmin) 1
(aref (termios-cc new) vtime) 0)
(tcsetattr 0 tcsadrain new)
(read-char stream))
(tcsetattr 0 tcsadrain old))))
From: Bigos on
On Nov 15, 10:56 am, r...(a)rpw3.org (Rob Warnock) wrote:
> Bigos  <ruby.obj...(a)googlemail.com> wrote:
>
> +---------------
> | r...(a)rpw3.org (Rob Warnock) wrote:
> | > Bigos  <ruby.obj...(a)googlemail.com> wrote:
> | > +---------------
> | > | I use sbcl and it doesn't seem to work for me.
> | > +---------------
> | >
> | > (*Ahem!*) As I *SAID*, my code is CMUCL-specific, but almost any CL
> | > implementation should provide you *some* way to make "ioctl()" calls.
> | >
> | > SBCL certainly provides such a way, but it's not at all surprising
> | > that it's *different* from CMUCL -- see the SBCL documentation for
> | > how to do the corresponding thing.
> +---------------
>
> By "corresponding thing" I did *not* mean the RUN-PROGRAM hack, but
> rather "the corresponding way in SBCL to do terminal ioctl()s *within*
> the SBCL process without running an external program".
>
> What does (APROPOS "TCGETATTR") say in SBCL? Ditto (APROPOS "TCSETATTR")?
> Those are the routines you should be looking to use.
>
> Or if those aren't there, what about (APROPOS "IOCTL")? I strongly suspect
> at least SB-UNIX:UNIX-IOCTL is there, though you might have to figure out
> how to use it to invoke TCGETATTR/TCSETATTR/etc..
>
> +---------------
> | yes, I have READ THE FRACTIOUS MANUAL and my problem is that I don't
> | understand it. When I tried to write my own code based on my
> | understanding of the documentation and all I got was error messages
> | having something to do with provided arguments. I found some code
> | snippet on the web, got rid of error messages, but still didn't
> | achieve expected behaviour.
> ...
> | (sb-ext:run-program   "stty raw -echo" nil :input t :output t :wait
> | nil :pty t )
> +---------------
>
> Well, there's a big part of your problem: You're supplying the wrong
> args to SB-EXT:RUN-PROGRAM! The first args *isn't* a shell command,
> but an absolute program pathname string [but see "p.s." below], and
> the second arg [which you set NIL] is a *list* of string arguments
> to the command. And :WAIT NIL means that you're *not* waiting until
> the program finishes before doing the next thing [your READ-CHAR],
> so no *wonder* it doesn't work!! And you don't want :PTY T, since you
> want "stty" to work on the *current* terminal, not some random PTY.
> Try this instead:
>
>     (sb-ext:run-program "/bin/stty"
>                         '("-icanon" "-echo" "-echoe" "-echok" "-echonl")
>                         :input t :output t :error t :wait t)
>
> and then of course afetr READ-CHAR to turn echoing & line-editing
> back on:
>
>     (sb-ext:run-program "/bin/stty"
>                         '("icanon" "echo" "echoe" "echok" "echonl")
>                         :input t :output t :error t :wait t)
>
> Note: These work exactly as you seem to want them to on CMUCL, albeit
> using EXT:RUN-PROGRAM rather than SB-EXT:RUN-PROGRAM, but there might
> be some reason it wouldn't work on SBCL. [I don't know, not being an
> SBCL user...]
>
> In any case, do a (DESCRIBE 'SB-EXT:RUN-PROGRAM) and study the output
> carefully.
>
> +---------------
> | The code fragment above still requires pressing enter to work, so my
> | expected trick didn't work.
> +---------------
>
> Of course not: (1) The first SB-EXT:RUN-PROGRAM had the wrong args,
> and (2) you didn't wait for the command to finish before doing READ-CHAR.
>
> Anyway, I strongly recommend that you *not* continue to try the external
> program method for twiddling the terminal state, but instead learn how
> to do [or find an example somewhere of doing] "ioctl()"s from *within*
> SBCL [approximating more or less my code for CMUCL]. That's more likely
> to be reliable.
>
> -Rob
>
> p.s. I see that with SB-EXT:RUN-PROGRAM you can use the keyword arg
> :SEARCH T to look in your $PATH instead of having to provide an
> absolute path. That is, instead of the first example above you
> *should* be able to do this:
>
>     (sb-ext:run-program "stty"
>                         '("-icanon" "-echo" "-echoe" "-echok" "-echonl")
>                         :search t :input t :output t :error t :wait t)
>
> p.p.s. One more hint. Try running this:
>
>     (sb-ext:run-program "stty" '("-a")
>                         :search t :input t :output t :error t :wait t)
>
> If that works -- that is, prints out the state of your terminal --
> but the examples which alter the TTY state *don't* appear to work,
> then SBCL may be saving/restoring the terminal state across sub-process
> executions [the way some shells do], in which case the external program
> method for twiddling the terminal state is *never* going to work!!
> In that case you *must* learn & use the internal "ioctl()" method.
>
> -----
> Rob Warnock                     <r...(a)rpw3.org>
> 627 26th Avenue                 <URL:http://rpw3.org/>
> San Mateo, CA 94403             (650)572-2607

I have run sbcl in terminal window and got following:

This is SBCL 1.0.29.11.debian, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
* (APROPOS "TCGETATTR")

SB-POSIX:TCGETATTR (fbound)
* (APROPOS "TCSETATTR")

SB-POSIX:TCSETATTR (fbound)
* (APROPOS "IOCTL")

SB-POSIX:IOCTL (fbound)
SB-POSIX::IOCTL-WITH-INT-ARG (fbound)
SB-POSIX::IOCTL-WITH-POINTER-ARG (fbound)
SB-POSIX::IOCTL-WITHOUT-ARG (fbound)
SB-UNIX:UNIX-IOCTL (fbound)

* (sb-ext:run-program "stty" '("-a") :search t :input t :output
t :error t :wait t)
speed 38400 baud; rows 24; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?;
eol2 = M-^?;
swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase =
^W;
lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts
-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon
-ixoff
-iuclc ixany imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0
bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -
echoprt
echoctl echoke


I will search for more information about the subject tomorrow. I need
some rest so I can think properly and analyse the information I got so
far.