zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: curses input
@ 2007-10-26 20:13 Peter Stephenson
  2007-10-26 21:56 ` Peter Stephenson
  0 siblings, 1 reply; 4+ messages in thread
From: Peter Stephenson @ 2007-10-26 20:13 UTC (permalink / raw)
  To: Zsh hackers list

"zcurses input" reads a single character with cbreak and noecho set.
Handling nocbreak would be clumsy and the manual says you should use
noecho with cbreak.  There are all sorts of possible extensions but this
is already good enough to do most of what you want.

What would be quite nice is to handle keypad mode (see curs_getch(3X)),
which would probably mean parsing KEY_* values and converting them to
strings to return.  We could just return the corresponding integer
values, but it seems a cop out.

In principle it would be good to have proper zsh input in curses
mode, but that's pie in the sky at the moment.

Index: configure.ac
===================================================================
RCS file: /cvsroot/zsh/zsh/configure.ac,v
retrieving revision 1.73
diff -u -r1.73 configure.ac
--- configure.ac	24 Oct 2007 08:47:43 -0000	1.73
+++ configure.ac	26 Oct 2007 20:11:00 -0000
@@ -1134,7 +1134,7 @@
 	       brk sbrk \
 	       pathconf sysconf \
 	       tgetent tigetflag tigetnum tigetstr setupterm initscr \
-	       setcchar waddwstr \
+	       setcchar waddwstr wget_wch \
 	       pcre_compile pcre_study pcre_exec \
 	       nl_langinfo \
 	       erand48 open_memstream \
Index: Doc/Zsh/mod_curses.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/mod_curses.yo,v
retrieving revision 1.10
diff -u -r1.10 mod_curses.yo
--- Doc/Zsh/mod_curses.yo	24 Oct 2007 22:23:10 -0000	1.10
+++ Doc/Zsh/mod_curses.yo	26 Oct 2007 20:11:01 -0000
@@ -19,7 +19,8 @@
 xitem(tt(zcurses) tt(string) var(targetwin) var(string) )
 xitem(tt(zcurses) tt(border) var(targetwin) var(border) )(
 xitem(tt(zcurses) tt(attr) var(targetwin) [ var({+/-}attribute) | var(fg_col)tt(/)var(bg_col) ] [...])
-item(tt(zcurses) tt(scroll) [ tt(on) | tt(off) | {+/-}var(lines) ])(
+xitem(tt(zcurses) tt(scroll) [ tt(on) | tt(off) | {+/-}var(lines) ])
+item(tt(input) var(targetwin) [ var(param) ])(
 Manipulate curses windows.  All uses of this command should be
 bracketed by `tt(zcurses init)' to initialise use of curses, and
 `tt(zcurses end)' to end it; omitting `tt(zcurses end)' can cause
@@ -62,7 +63,11 @@
 of lines without changing the current cursor position (which therefore
 appears to move in the opposite direction relative to the window).
 In the second case, if scrolling is tt(off) it is temporarily turned tt(on)
-to allow the window to be scrolled,
+to allow the window to be scrolled.
+
+tt(input) reads a single character from the window without echoing
+it back.  If var(param) is supplied the character is assigned to the
+parameter var(param), else it is assigned to the parameter var(REPLY).
 )
 enditem()
 
Index: Src/Modules/curses.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Modules/curses.c,v
retrieving revision 1.22
diff -u -r1.22 curses.c
--- Src/Modules/curses.c	24 Oct 2007 22:23:10 -0000	1.22
+++ Src/Modules/curses.c	26 Oct 2007 20:11:01 -0000
@@ -43,6 +43,7 @@
 #ifndef MULTIBYTE_SUPPORT
 # undef HAVE_SETCCHAR
 # undef HAVE_WADDWSTR
+# undef HAVE_WGET_WCH
 #endif
 
 #ifdef HAVE_SETCCHAR
@@ -335,6 +336,16 @@
 	    zcurses_colorpairs->printnode   = NULL;
 
 	}
+	/*
+	 * We use cbreak mode because we don't want line buffering
+	 * on input since we'd just need to loop over characters.
+	 * We use noecho since the manual says that's the right
+	 * thing to do with cbreak.
+	 *
+	 * Turn these on immediately to catch typeahead.
+	 */
+	cbreak();
+	noecho();
 	gettyinfo(&curses_tty_state);
     } else {
 	settyinfo(&curses_tty_state);
@@ -669,6 +680,59 @@
 }
 
 
+static int
+zccmd_input(const char *nam, char **args)
+{
+    LinkNode node;
+    ZCWin w;
+    char *var;
+#ifdef HAVE_WGET_WCH
+    wint_t wi;
+    VARARR(char, instr, 2*MB_CUR_MAX+1);
+#else
+    int ci;
+    instr[3];
+#endif
+
+    node = zcurses_validate_window(args[0], ZCURSES_USED);
+    if (node == NULL) {
+	zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
+	return 1;
+    }
+
+    w = (ZCWin)getdata(node);
+
+#ifdef HAVE_WGET_WCH
+    if (wget_wch(w->win, &wi) == ERR)
+	return 1;
+    wctomb(instr, (wchar_t)wi);
+    if (!*instr) {
+	instr[0] = Meta;
+	instr[1] = '\0' ^ 32;
+	instr[2] = '\0';
+    } else {
+	(void)metafy(instr, strlen(instr), META_NOALLOC);
+    }
+#else
+    ci = wgetch(w->win);
+    if (imeta(ci)) {
+	instr[0] = Meta;
+	instr[1] = (char)ci ^ 32;
+	instr[2] = '\0';
+    } else {
+	instr[0] = (char)ci;
+	instr[1] = '\0';
+    }
+#endif
+    if (args[1])
+	var = args[1];
+    else
+	var = "REPLY";
+    if (!setsparam(var, ztrdup(instr)))
+	return 1;
+    return 0;
+}
+
 /*********************
   Main builtin handler
  *********************/
@@ -693,6 +757,7 @@
 	{"end", zccmd_endwin, 0, 0},
 	{"attr", zccmd_attr, 2, -1},
 	{"scroll", zccmd_scroll, 2, 2},
+	{"input", zccmd_input, 1, 2},
 	{NULL, (zccmd_t)0, 0, 0}
     };
 
 

-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: PATCH: curses input
  2007-10-26 20:13 PATCH: curses input Peter Stephenson
@ 2007-10-26 21:56 ` Peter Stephenson
  2007-10-29 13:29   ` Clint Adams
  0 siblings, 1 reply; 4+ messages in thread
From: Peter Stephenson @ 2007-10-26 21:56 UTC (permalink / raw)
  To: Zsh Hackers' List

On Fri, 26 Oct 2007 21:13:14 +0100
Peter Stephenson <p.w.stephenson@ntlworld.com> wrote:
> What would be quite nice is to handle keypad mode (see curs_getch(3X)),
> which would probably mean parsing KEY_* values and converting them to
> strings to return.  We could just return the corresponding integer
> values, but it seems a cop out.

How about this?  Full patch including the previous one; also fixes
a string termination problem.

Index: configure.ac
===================================================================
RCS file: /cvsroot/zsh/zsh/configure.ac,v
retrieving revision 1.73
diff -u -r1.73 configure.ac
--- configure.ac	24 Oct 2007 08:47:43 -0000	1.73
+++ configure.ac	26 Oct 2007 21:55:37 -0000
@@ -1134,7 +1134,7 @@
 	       brk sbrk \
 	       pathconf sysconf \
 	       tgetent tigetflag tigetnum tigetstr setupterm initscr \
-	       setcchar waddwstr \
+	       setcchar waddwstr wget_wch \
 	       pcre_compile pcre_study pcre_exec \
 	       nl_langinfo \
 	       erand48 open_memstream \
@@ -1354,6 +1354,46 @@
 ERRNO_H="$zsh_cv_path_errno_h"
 AC_SUBST(ERRNO_H)dnl
 
+dnl Where are curses key definitions located?  Need for keypad() mode.
+AC_CACHE_CHECK(where curses key definitions are located, zsh_cv_path_curses_keys_h,
+[dnl This is an identical trick to errno.h, except we use ncurses.h
+dnl if we can.
+if test x$ac_cv_header_ncurses_h = xyes; then
+  echo "#include <ncurses.h>" >nametmp.c
+else
+  if test x$ac_cv_header_curses_h = xyes; then
+    echo "#include <curses.h>" >nametmp.c
+  else
+    echo >nametmp.c
+  fi
+fi
+curses_list="`$CPP nametmp.c |
+sed -n -e 's/^#line[ 	].*\"\(.*\)\"/\1/p' \
+       -e 's/^#[ 	0-9].*\"\(.*\)\"/\1/p' |
+sed 's/\\\\\\\\/\//g' |
+$AWK '{ if ($1 ~ /\.h/) files[[$1]] = $1 }
+  END { for (var in files) print var }'`"
+rm -f nametmp.c
+if x"$curses_list" = x; then
+  echo Failed
+  exit 1
+fi
+for CURSES_TRY_H in $curses_list /dev/null
+do
+  nkeys=`test -f $CURSES_TRY_H && \
+  $EGREP '#[ 	]*define[ 	][ 	]*KEY_' $CURSES_TRY_H | \
+  wc -l | sed 's/[ 	]//g'`
+  if test "x$nkeys" != x && test "$nkeys" -ge 10
+  then
+    CURSES_KEYS_H=$CURSES_TRY_H
+    break
+  fi
+done
+zsh_cv_path_curses_keys_h="$CURSES_KEYS_H"
+])
+CURSES_KEYS_H="$zsh_cv_path_curses_keys_h"
+AC_SUBST(CURSES_KEYS_H)dnl
+
 dnl -----------------------------------------------------
 dnl Look for the file containing the RLIMIT_* definitions
 dnl -----------------------------------------------------
Index: Doc/Zsh/mod_curses.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/mod_curses.yo,v
retrieving revision 1.10
diff -u -r1.10 mod_curses.yo
--- Doc/Zsh/mod_curses.yo	24 Oct 2007 22:23:10 -0000	1.10
+++ Doc/Zsh/mod_curses.yo	26 Oct 2007 21:55:38 -0000
@@ -19,7 +19,8 @@
 xitem(tt(zcurses) tt(string) var(targetwin) var(string) )
 xitem(tt(zcurses) tt(border) var(targetwin) var(border) )(
 xitem(tt(zcurses) tt(attr) var(targetwin) [ var({+/-}attribute) | var(fg_col)tt(/)var(bg_col) ] [...])
-item(tt(zcurses) tt(scroll) [ tt(on) | tt(off) | {+/-}var(lines) ])(
+xitem(tt(zcurses) tt(scroll) [ tt(on) | tt(off) | {+/-}var(lines) ])
+item(tt(input) var(targetwin) [ var(param) [ var(kpparm) ] ])(
 Manipulate curses windows.  All uses of this command should be
 bracketed by `tt(zcurses init)' to initialise use of curses, and
 `tt(zcurses end)' to end it; omitting `tt(zcurses end)' can cause
@@ -44,7 +45,10 @@
 Outputting characters and strings are achieved by tt(char) and tt(string)
 respectively.
 
-To draw a border around window var(targetwin), use tt(border).
+To draw a border around window var(targetwin), use tt(border).  Note
+that the border is not subsequently handled specially:  in other words,
+the border is simply a set of characters output at the edge of the
+window.  Hence it can be overwritten, can scroll off the window, etc.
 
 tt(attr) will set var(targetwin)'s attributes or foreground/background
 color pair for any successive character output.  Each var(attribute)
@@ -62,7 +66,19 @@
 of lines without changing the current cursor position (which therefore
 appears to move in the opposite direction relative to the window).
 In the second case, if scrolling is tt(off) it is temporarily turned tt(on)
-to allow the window to be scrolled,
+to allow the window to be scrolled.
+
+tt(input) reads a single character from the window without echoing
+it back.  If var(param) is supplied the character is assigned to the
+parameter var(param), else it is assigned to the parameter var(REPLY).
+If both var(param) and var(kpparam) are supplied, the key is read
+in `keypad' mode.  In this mode special keys such as function keys
+and arrow keys return the name of the key in the parameter var(kpparam).
+The key names are the macros defined in the tt(curses.h) or tt(ncurses.h)
+with the prefix `tt(KEY_)' removed.  Other keys cause a value to be set in
+var(param) as before.  On a succesful return only one of var(param) or
+var(kpparm) contains a non-empty string; the other is set to an empty
+string.
 )
 enditem()
 
Index: Src/Modules/curses.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Modules/curses.c,v
retrieving revision 1.22
diff -u -r1.22 curses.c
--- Src/Modules/curses.c	24 Oct 2007 22:23:10 -0000	1.22
+++ Src/Modules/curses.c	26 Oct 2007 21:55:38 -0000
@@ -43,6 +43,7 @@
 #ifndef MULTIBYTE_SUPPORT
 # undef HAVE_SETCCHAR
 # undef HAVE_WADDWSTR
+# undef HAVE_WGET_WCH
 #endif
 
 #ifdef HAVE_SETCCHAR
@@ -122,6 +123,9 @@
     {NULL, 0}
 };
 
+/* Autogenerated keypad string/number mapping*/
+#include "curses_keys.h"
+
 static char **
 zcurses_pairs_to_array(const struct zcurses_namenumberpair *nnps)
 {
@@ -335,6 +339,16 @@
 	    zcurses_colorpairs->printnode   = NULL;
 
 	}
+	/*
+	 * We use cbreak mode because we don't want line buffering
+	 * on input since we'd just need to loop over characters.
+	 * We use noecho since the manual says that's the right
+	 * thing to do with cbreak.
+	 *
+	 * Turn these on immediately to catch typeahead.
+	 */
+	cbreak();
+	noecho();
 	gettyinfo(&curses_tty_state);
     } else {
 	settyinfo(&curses_tty_state);
@@ -669,6 +683,105 @@
 }
 
 
+static int
+zccmd_input(const char *nam, char **args)
+{
+    LinkNode node;
+    ZCWin w;
+    char *var;
+    int keypadnum = -1;
+#ifdef HAVE_WGET_WCH
+    int ret;
+    wint_t wi;
+    VARARR(char, instr, 2*MB_CUR_MAX+1);
+#else
+    int ci;
+    instr[3];
+#endif
+
+    node = zcurses_validate_window(args[0], ZCURSES_USED);
+    if (node == NULL) {
+	zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
+	return 1;
+    }
+
+    w = (ZCWin)getdata(node);
+
+    if (args[1] && args[2]) {
+	keypad(w->win, TRUE);
+    } else {
+	keypad(w->win, FALSE);
+    }
+
+#ifdef HAVE_WGET_WCH
+    switch (wget_wch(w->win, &wi)) {
+    case OK:
+	ret = wctomb(instr, (wchar_t)wi);
+	if (ret == 0) {
+	    instr[0] = Meta;
+	    instr[1] = '\0' ^ 32;
+	    instr[2] = '\0';
+	} else {
+	    (void)metafy(instr, ret, META_NOALLOC);
+	}
+	break;
+
+    case KEY_CODE_YES:
+	keypadnum = (int)wi;
+	break;
+
+    case ERR:
+    default:
+	return 1;
+    }
+#else
+    ci = wgetch(w->win);
+    if (ci >= 256) {
+	keypadnum = ci;
+    } else {
+	if (imeta(ci)) {
+	    instr[0] = Meta;
+	    instr[1] = (char)ci ^ 32;
+	    instr[2] = '\0';
+	} else {
+	    instr[0] = (char)ci;
+	    instr[1] = '\0';
+	}
+    }
+#endif
+    if (args[1])
+	var = args[1];
+    else
+	var = "REPLY";
+    if (!setsparam(var, ztrdup(keypadnum > 0 ? "" : instr)))
+	return 1;
+    if (args[2]) {
+	if (keypadnum > 0) {
+	    const struct zcurses_namenumberpair *nnptr;
+	    char fbuf[DIGBUFSIZE+1];
+
+	    for (nnptr = keypad_names; nnptr->name; nnptr++) {
+		if (keypadnum == nnptr->number) {
+		    setsparam(args[2], ztrdup(nnptr->name));
+		    return 0;
+		}
+	    }
+	    if (keypadnum > KEY_F0) {
+		/* assume it's a function key */
+		sprintf(fbuf, "F%d", keypadnum - KEY_F0);
+	    } else {
+		/* print raw number */
+		sprintf(fbuf, "%d", keypadnum);
+	    }
+	    setsparam(args[2], ztrdup(fbuf));
+	} else {
+	    setsparam(args[2], ztrdup(""));
+	}
+    }
+    return 0;
+}
+
+
 /*********************
   Main builtin handler
  *********************/
@@ -693,6 +806,7 @@
 	{"end", zccmd_endwin, 0, 0},
 	{"attr", zccmd_attr, 2, -1},
 	{"scroll", zccmd_scroll, 2, 2},
+	{"input", zccmd_input, 1, 3},
 	{NULL, (zccmd_t)0, 0, 0}
     };
 
Index: Src/Modules/curses.mdd
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Modules/curses.mdd,v
retrieving revision 1.3
diff -u -r1.3 curses.mdd
--- Src/Modules/curses.mdd	14 Oct 2007 04:24:47 -0000	1.3
+++ Src/Modules/curses.mdd	26 Oct 2007 21:55:38 -0000
@@ -5,3 +5,9 @@
 autobins="zcurses"
 
 objects="curses.o"
+
+:<<\Make
+curses.o curses..o: curses_keys.h
+
+curses_keys.h: curses_keys.awk @CURSES_KEYS_H@
+	$(AWK) -f $(sdir)/curses_keys.awk @CURSES_KEYS_H@ /dev/null >curses_keys.h
Index: Src/Modules/curses_keys.awk
===================================================================
RCS file: Src/Modules/curses_keys.awk
diff -N Src/Modules/curses_keys.awk
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Src/Modules/curses_keys.awk	26 Oct 2007 21:55:38 -0000
@@ -0,0 +1,19 @@
+BEGIN {nkeydefs = 0}
+
+/^[\t ]*#[\t ]*define[\t _]*KEY_[A-Z0-9_]*[\t ]/ {
+    keyindex = index($0, "KEY_")
+    keytail = substr($0, keyindex, 80)
+    split(keytail, tmp)
+    keynam = substr(tmp[1], 5, 30)
+    if (keynam != "MIN" && keynam != "MAX") {
+	name[nkeydefs++] = keynam
+    }
+}
+
+END {
+    printf("static const struct zcurses_namenumberpair keypad_names[] = {\n")
+    for (i = 0; i < 0 + nkeydefs; i++)
+        printf("    {\"%s\", KEY_%s},\n", name[i], name[i])
+    printf("    {NULL, 0}\n")
+    printf("};\n")
+}


-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: PATCH: curses input
  2007-10-26 21:56 ` Peter Stephenson
@ 2007-10-29 13:29   ` Clint Adams
  2007-10-29 14:04     ` Peter Stephenson
  0 siblings, 1 reply; 4+ messages in thread
From: Clint Adams @ 2007-10-29 13:29 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: Zsh Hackers' List

On Fri, Oct 26, 2007 at 10:56:33PM +0100, Peter Stephenson wrote:
> +zsh_cv_path_curses_keys_h="$CURSES_KEYS_H"

I haven't looked into this at all, but a previously-dirtied working
directory seems to compile just fine, but a fresh checkout &
Util/preconfig results in complaints that there's no rule to build
curses_keys.h or something.


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: PATCH: curses input
  2007-10-29 13:29   ` Clint Adams
@ 2007-10-29 14:04     ` Peter Stephenson
  0 siblings, 0 replies; 4+ messages in thread
From: Peter Stephenson @ 2007-10-29 14:04 UTC (permalink / raw)
  To: Zsh Hackers' List

Clint Adams wrote:
> On Fri, Oct 26, 2007 at 10:56:33PM +0100, Peter Stephenson wrote:
> > +zsh_cv_path_curses_keys_h="$CURSES_KEYS_H"
> 
> I haven't looked into this at all, but a previously-dirtied working
> directory seems to compile just fine, but a fresh checkout &
> Util/preconfig results in complaints that there's no rule to build
> curses_keys.h or something.

I didn't get this with a quick test; maybe there's some race in a make
dependency.  It should be handled by this in curses.mdd:


:<<\Make
curses.o curses..o: curses_keys.h

curses_keys.h: curses_keys.awk @CURSES_KEYS_H@
	$(AWK) -f $(sdir)/curses_keys.awk @CURSES_KEYS_H@ /dev/null \
	  >curses_keys.h


So even if there's no suitable header we should just get an empty set
of definitions.  CURSES_KEYS_H is supposed to get substituted
unconditionally even if it's empty.

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2007-10-29 14:04 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-10-26 20:13 PATCH: curses input Peter Stephenson
2007-10-26 21:56 ` Peter Stephenson
2007-10-29 13:29   ` Clint Adams
2007-10-29 14:04     ` Peter Stephenson

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).