diff options
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | Doc/Zsh/mod_curses.yo | 22 | ||||
-rw-r--r-- | Src/Modules/curses.c | 114 | ||||
-rw-r--r-- | Src/Modules/curses.mdd | 6 | ||||
-rw-r--r-- | Src/Modules/curses_keys.awk | 19 | ||||
-rw-r--r-- | configure.ac | 42 |
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 ----------------------------------------------------- |