From: jurgen_defurne
Subject: read-char vs. read-char-no-hang
Date: 
Message-ID: <f3b300e6-636f-42d4-83b8-182fa4372267@e24g2000vbe.googlegroups.com>
The specification says that read-char returns the next character on
the input stream, and read-char-no-hang the next character on the
input stream or nil if there is none available. This implies that read-
char does not return when no character is available.

However, most (all?) terminal implementations work by sending line-by-
line. This means that practically, there is no difference between both
functions. They will both wait until the operator has hit the return
key.

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, e.g.

Any other solutions, remarks or hints ?

Regards,

Jurgen

From: David Lichteblau
Subject: Re: read-char vs. read-char-no-hang
Date: 
Message-ID: <slrngtp844.sqf.usenet-2008@radon.home.lichteblau.com>
On 2009-04-08, 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, e.g.

http://common-lisp.net/project/linedit/
From: Pascal J. Bourguignon
Subject: Re: read-char vs. read-char-no-hang
Date: 
Message-ID: <87tz4zcq74.fsf@informatimago.com>
jurgen_defurne <··············@pandora.be> writes:

> The specification says that read-char returns the next character on
> the input stream, and read-char-no-hang the next character on the
> input stream or nil if there is none available. This implies that read-
> char does not return when no character is available.
>
> However, most (all?) terminal implementations work by sending line-by-
> line. This means that practically, there is no difference between both
> functions. They will both wait until the operator has hit the return
> key.

The  difference is that at the end of the line, READ-CHAR would hang and
wait for the next line, while READ-CHAR-NO-HANG would return NIL.

> 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, e.g.
>
> Any other solutions, remarks or hints ?

You can wrap your program in a shell script that calls stty before to
set raw mode, and after to restore cooked mode.  But indeed, it means
that your program must handle all terminal I/O, you can't use the
implementation REPL.

-- 
__Pascal Bourguignon__
http://www.informatimago.com
From: Rob Warnock
Subject: Re: read-char vs. read-char-no-hang
Date: 
Message-ID: <tOadnQykJJOt00DUnZ2dnUVZ_t6dnZ2d@speakeasy.net>
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.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: chthon
Subject: Re: read-char vs. read-char-no-hang
Date: 
Message-ID: <044bd9d7-3753-4347-b21a-22b07de3f174@e12g2000vbe.googlegroups.com>
On Apr 9, 3:24 am, ····@rpw3.org (Rob Warnock) wrote:
> 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.org>
> 627 26th Avenue                 <URL:http://rpw3.org/>
> San Mateo, CA 94403             (650)572-2607

After looking somewhat more around yesterday evening, I supposed that
it would have to end up somehow like your code. The biggest problem
was finding out why. Looking through some examples, I then found out
that cbreak is an ioctl setting, not a setting that you can print to
the terminal to change its characteristics (like an VT100 or ANSI
escape code).

But CLISP itself sets one on the wrong track. If you run CLISP from
the command-line and try (read-char) and (read-char-no-hang) they will
return immediately, because CLISP uses the GNU readline library. Then,
if you try it in a program, it does not work anymore. So I should be
able to get the functionality I want through the linked in GNU
readline library.

Thanks for the info on SBCL.

Regards,

Jurgen
From: Rob Warnock
Subject: Re: read-char vs. read-char-no-hang
Date: 
Message-ID: <pZ6dnfA-hdz-OkDUnZ2dnUVZ_g-dnZ2d@speakeasy.net>
chthon  <··············@pandora.be> wrote:
+---------------
| ····@rpw3.org (Rob Warnock) wrote:
| > buts easily enough, though. I don't know what facilities CLISP
| > provides, but here's what I use in CMUCL when running on FreeBSD:
| > ...[code]...
| 
| Thanks for the info on SBCL.
+---------------

Note: The code I gave was for CMUCL, not SBCL. Despite the fact that
SBCL originally forked from CMUCL, the code I gave is unlikely to work
out of the box on SBCL [if nothing else, package names may have changed].

+---------------
| After looking somewhat more around yesterday evening, I supposed that
| it would have to end up somehow like your code. The biggest problem
| was finding out why. Looking through some examples, I then found out
| that cbreak is an ioctl setting, not a setting that you can print to
| the terminal to change its characteristics (like an VT100 or ANSI
| escape code).
+---------------

That's because it's not the terminal that decides when to "push"
data up to the user process, but the operating system.

+---------------
| But CLISP itself sets one on the wrong track. If you run CLISP from
| the command-line and try (read-char) and (read-char-no-hang) they will
| return immediately, because CLISP uses the GNU readline library.
+---------------

The version of CLISP I have does *not* return immediately from READ-CHAR,
but waits for the next #\Newline to be typed, then returns the first
character on that line. And in any case, the GNU readline library
doesn't necessarily fix the operating system issue [it might in fact
make it *worse*!].


-Rob

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