From: Bigos on 15 Nov 2009 18:25 On Nov 15, 1:41 pm, "Thomas F. Burdick" <tburd...(a)gmail.com> wrote: > 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)))) It works !!! thank you! I must I need time to analyse this because at the moment I have only vague idea why.
From: Bigos on 15 Nov 2009 19:08 On Nov 15, 1:41 pm, "Thomas F. Burdick" <tburd...(a)gmail.com> wrote: > 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)))) All works expect one little problem, Why in the code below computer prints both "press any key " and "~%you have pressed: >~s<~%" at the same time? I would expect the computer would ask me to press a key, wait for a key press and then display what I have pressed. I will check it tomorrow. It's time to get some rest now. (format t "press any key 5 times~%") (dotimes (i 5) (format t "press any key ") (format t "~%you have pressed: >~s<~%" (read-char-no-echo- cbreak))) (format t "~%")
From: Rob Warnock on 15 Nov 2009 21:14 Bigos <ruby.object(a)googlemail.com> wrote: +--------------- | All works expect one little problem, Why in the code below computer | prints both "press any key " and "~%you have pressed: >~s<~%" at the | same time? +--------------- Hint: FORCE-OUTPUT -Rob ----- Rob Warnock <rpw3(a)rpw3.org> 627 26th Avenue <URL:http://rpw3.org/> San Mateo, CA 94403 (650)572-2607
From: Bigos on 16 Nov 2009 05:43 On Nov 16, 2:14 am, r...(a)rpw3.org (Rob Warnock) wrote: > Bigos <ruby.obj...(a)googlemail.com> wrote: > > +--------------- > | All works expect one little problem, Why in the code below computer > | prints both "press any key " and "~%you have pressed: >~s<~%" at the > | same time? > +--------------- > > Hint: FORCE-OUTPUT > > -Rob > > ----- > Rob Warnock <r...(a)rpw3.org> > 627 26th Avenue <URL:http://rpw3.org/> > San Mateo, CA 94403 (650)572-2607 Yipieeee! it work. Than you very much everyone. It works the way I wanted. Now I will try to analyse the code in spare time and if I have any more questions I will ask them in another thread. (dotimes (i 5) (format t "press any key ") (force-output) (format t "~%you have pressed: >~s<~%" (read-char-no-echo-cbreak)))
From: John Thingstad on 17 Nov 2009 10:12
The Thu, 12 Nov 2009 15:48:08 -0800, Bigos wrote: > Hi, > > I didn't give up yet. I was a bit busy with my day job so didn't have > chance to post anything for a while. > > At the moment I am trying to write a Linux console application. I > already have worked out how to do few things in Lisp, but I couldn't > find answer to the following question: > > How do I grab key presses from a console application in Linux without > having to press Enter? > > I mean something like read-char, without having to press Enter. > > Also I am willing to consider some alternatives, for example some simple > GUI tool-kit. I use nCurses. There is a Lisp mode though I never got it to work. I mostly write this sort of thin in Python. -- John Thingstad |