about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2007-10-26 21:59:56 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2007-10-26 21:59:56 +0000
commit7f8e2298181fcbf3399eb9bde1564c1fe2fe9df3 (patch)
tree5519c693b2684c85b642f3b600eb8a83a862bd9b
parent26461dcc1b03fa0ad47c7abbec4b8999f1fe5a28 (diff)
downloadzsh-7f8e2298181fcbf3399eb9bde1564c1fe2fe9df3.tar.gz
zsh-7f8e2298181fcbf3399eb9bde1564c1fe2fe9df3.tar.xz
zsh-7f8e2298181fcbf3399eb9bde1564c1fe2fe9df3.zip
24024: add zcurses input with keypad handling
-rw-r--r--ChangeLog8
-rw-r--r--Doc/Zsh/mod_curses.yo22
-rw-r--r--Src/Modules/curses.c114
-rw-r--r--Src/Modules/curses.mdd6
-rw-r--r--Src/Modules/curses_keys.awk19
-rw-r--r--configure.ac42
6 files changed, 207 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index ea33f8399..dedcf4193 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2007-10-26  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 24024: configure.ac, Doc/Zsh/mod_curses.yo,
+	Src/Modules/curses.c:  add "zcurses input" for single character
+	raw input without echoing.  Test for wget_wch for wide
+	character input.  Add handling for keypad() mode by
+	scanning header.
+
 2007-10-26  Clint Adams  <clint@zsh.org>
 
 	* 24022: Completion/Debian/Command/_dpkg-repack: completion for
diff --git a/Doc/Zsh/mod_curses.yo b/Doc/Zsh/mod_curses.yo
index b902fce90..266cdea29 100644
--- a/Doc/Zsh/mod_curses.yo
+++ b/Doc/Zsh/mod_curses.yo
@@ -19,7 +19,8 @@ xitem(tt(zcurses) tt(char) var(targetwin) var(character) )
 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 @@ var(new_y) and var(new_x).
 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 @@ or negative integer to scroll the window up or down the given number
 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()
 
diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c
index 06ad463e1..273bb9e19 100644
--- a/Src/Modules/curses.c
+++ b/Src/Modules/curses.c
@@ -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 @@ static const struct zcurses_namenumberpair zcurses_colors[] = {
     {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 @@ zccmd_init(const char *nam, char **args)
 	    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 @@ zccmd_scroll(const char *nam, char **args)
 }
 
 
+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 @@ bin_zcurses(char *nam, char **args, Options ops, UNUSED(int func))
 	{"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}
     };
 
diff --git a/Src/Modules/curses.mdd b/Src/Modules/curses.mdd
index c9c31f267..e6f87d748 100644
--- a/Src/Modules/curses.mdd
+++ b/Src/Modules/curses.mdd
@@ -5,3 +5,9 @@ load=no
 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
diff --git a/Src/Modules/curses_keys.awk b/Src/Modules/curses_keys.awk
new file mode 100644
index 000000000..55a786521
--- /dev/null
+++ b/Src/Modules/curses_keys.awk
@@ -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")
+}
diff --git a/configure.ac b/configure.ac
index d7483e1fb..ebd339630 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1134,7 +1134,7 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \
 	       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 @@ zsh_cv_path_errno_h="$ERRNO_H"
 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 -----------------------------------------------------