zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: zcurses mouse handling
@ 2007-11-07 22:30 Peter Stephenson
  2007-11-10 20:26 ` PATCH: zcurses querychar Peter Stephenson
  0 siblings, 1 reply; 2+ messages in thread
From: Peter Stephenson @ 2007-11-07 22:30 UTC (permalink / raw)
  To: Zsh hackers list

This implements mouse handling, but only for ncurses.  The array of
mouse status could be done in a myriad different ways; the one here is a
fairly simple formatting of the information available, not of all which
is necessarily useful.

The manual page doesn't document the way buttons and events are
combined, so I've just assumed that only the combined results like
BUTTON1_PRESSED are available publically, which complicates the code a
bit.

It appears the REPORT_MOUSE_POSITION mask ("zcurses mouse +motion")
isn't implemented; Google produced hints to that effect, but no
definitive ruling; anyone have a clue?

In principle you can specify mouse events to be reported or not in much
more detail, but there didn't seem a lot of point in that.

I think the big thing missing now is to be able to query characters and
attributes within windows.

Index: Doc/Zsh/mod_curses.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/mod_curses.yo,v
retrieving revision 1.19
diff -u -r1.19 mod_curses.yo
--- Doc/Zsh/mod_curses.yo	6 Nov 2007 14:14:17 -0000	1.19
+++ Doc/Zsh/mod_curses.yo	7 Nov 2007 22:20:46 -0000
@@ -23,8 +23,9 @@
 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) ] [...])
 xitem(tt(zcurses) tt(bg) var(targetwin) [ var({+/-}attribute) | var(fg_col)tt(/)var(bg_col) | tt(@)var(char) ] [...])
-xitem(tt(zcurses) tt(scroll) [ tt(on) | tt(off) | {+/-}var(lines) ])
-xitem(tt(zcurses) tt(input) var(targetwin) [ var(param) [ var(kpparm) ] ])
+xitem(tt(zcurses) tt(scroll) var(targetwin) [ tt(on) | tt(off) | {+/-}var(lines) ])
+xitem(tt(zcurses) tt(input) var(targetwin) [ var(param) [ var(kparam) [ var(mparam) ] ] ])
+xitem(tt(zcurses) tt(mouse) [ tt(delay) var(num) | {+/-}tt(motion) ])
 item(tt(zcurses) tt(timeout) var(targetwin) var(intval))(
 Manipulate curses windows.  All uses of this command should be
 bracketed by `tt(zcurses init)' to initialise use of curses, and
@@ -129,14 +130,59 @@
 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.
+
+If both var(param) and var(kparam) 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(kparam).  The
+key names are the macros defined in the tt(curses.h) or tt(ncurses.h)
+with the prefix `tt(KEY_)' removed; see also the description of the
+parameter tt(zcurses_keycodes) below.  Other keys cause a value to be
+set in var(param) as before.  On a succesful return only one of
+var(param) or var(kparam) contains a non-empty string; the other is set
+to an empty string.
+
+If var(mparam) is also supplied, tt(input) attempts to handle mouse
+input.  This is only available with the ncurses library; mouse handling
+can be detected by checking for the exit status of `tt(zcurses mouse)' with
+no arguments.  If a mouse
+button is clicked (or double- or triple-clicked, or pressed or released with
+a configurable delay from being clicked) then tt(kparam) is set to the string
+tt(MOUSE), and var(mparam) is set to an array consisting of the
+following elements:
+startitem()
+sitem(-)(An identifier to discriminate different input devices; this
+is only rarely useful.)
+sitem(-)(The x, y and z coordinates of the mouse click relative to
+the full screen, as three elements in that order (i.e. the y coordinate
+is, unusually, after the x coordinate).  The z coordinate is only
+available for a few unusual input devices and is otherwise set to zero.)
+sitem(-)(Any events that occurred as separate items; usually
+there will be just one.  An event consists of tt(PRESSED), tt(RELEASED),
+tt(CLICKED), tt(DOUBLE_CLICKED) or tt(TRIPLE_CLICKED) followed
+immediately (in the same element) by the number of the button.)
+sitem(-)(If the shift key was pressed, the string tt(SHIFT).)
+sitem(-)(If the control key was pressed, the string tt(CTRL).)
+sitem(-)(If the alt key was pressed, the string tt(ALT).)
+endsitem()
+
+Not all mouse events may be passed through to the terminal window;
+most terminal emulators handle some mouse events themselves.  Note
+that the ncurses manual implies that using input both with and
+without mouse handling may cause the mouse cursor to appear and
+disappear.
+
+The subcommand tt(mouse) can be used to configure the use of the mouse.
+There is no window argument; mouse options are global.
+`tt(zcurses mouse)' with no arguments returns status 0 if mouse handling
+is possible, else status 1.  Otherwise, the possible arguments (which
+may be combined on the same command line) are as follows.
+tt(delay) var(num) sets the maximum delay in milliseconds between press and
+release events to be considered as a click; the value 0 disables click
+resolution, and the default is one sixth of a second.  tt(motion) proceeded
+by an optional `tt(PLUS())' (the default) or tt(-) turns on or off
+reporting of mouse motion in addition to clicks, presses and releases,
+which are always reported.  However, it appears reports for mouse
+motion are not currently implemented.
 
 tt(timeout) specifies a timeout value for input from var(targetwin).
 If var(intval) is negative, `tt(zcurses input)' waits indefinitely for
Index: Src/Modules/curses.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Modules/curses.c,v
retrieving revision 1.33
diff -u -r1.33 curses.c
--- Src/Modules/curses.c	6 Nov 2007 14:14:17 -0000	1.33
+++ Src/Modules/curses.c	7 Nov 2007 22:20:47 -0000
@@ -92,6 +92,7 @@
 static struct ttyinfo curses_tty_state;
 static LinkList zcurses_windows;
 static HashTable zcurses_colorpairs = NULL;
+static int zcurses_flags;
 
 #define ZCURSES_EINVALID 1
 #define ZCURSES_EDEFINED 2
@@ -106,6 +107,11 @@
 static int zc_errno, zc_color_phase=0;
 static short next_cp=0;
 
+enum {
+    ZCF_MOUSE_ACTIVE,
+    ZCF_MOUSE_MASK_CHANGED
+};
+
 static const struct zcurses_namenumberpair zcurses_attributes[] = {
     {"blink", A_BLINK},
     {"bold", A_BOLD},
@@ -131,6 +137,70 @@
     {NULL, 0}
 };
 
+#ifdef NCURSES_MOUSE_VERSION
+enum zcurses_mouse_event_types {
+    ZCME_PRESSED,
+    ZCME_RELEASED,
+    ZCME_CLICKED,
+    ZCME_DOUBLE_CLICKED,
+    ZCME_TRIPLE_CLICKED
+};
+
+static const struct zcurses_namenumberpair zcurses_mouse_event_list[] = {
+    {"PRESSED", ZCME_PRESSED},
+    {"RELEASED", ZCME_RELEASED},
+    {"CLICKED", ZCME_CLICKED},
+    {"DOUBLE_CLICKED", ZCME_DOUBLE_CLICKED},
+    {"TRIPLE_CLICKED", ZCME_TRIPLE_CLICKED},
+    {NULL, 0}
+};
+
+struct zcurses_mouse_event {
+    int button;
+    int what;
+    mmask_t event;
+};
+
+static const struct zcurses_mouse_event zcurses_mouse_map[] = {
+    { 1, ZCME_PRESSED, BUTTON1_PRESSED },
+    { 1, ZCME_RELEASED, BUTTON1_RELEASED },
+    { 1, ZCME_CLICKED, BUTTON1_CLICKED },
+    { 1, ZCME_DOUBLE_CLICKED, BUTTON1_DOUBLE_CLICKED },
+    { 1, ZCME_TRIPLE_CLICKED, BUTTON1_TRIPLE_CLICKED },
+
+    { 2, ZCME_PRESSED, BUTTON2_PRESSED },
+    { 2, ZCME_RELEASED, BUTTON2_RELEASED },
+    { 2, ZCME_CLICKED, BUTTON2_CLICKED },
+    { 2, ZCME_DOUBLE_CLICKED, BUTTON2_DOUBLE_CLICKED },
+    { 2, ZCME_TRIPLE_CLICKED, BUTTON2_TRIPLE_CLICKED },
+
+    { 3, ZCME_PRESSED, BUTTON3_PRESSED },
+    { 3, ZCME_RELEASED, BUTTON3_RELEASED },
+    { 3, ZCME_CLICKED, BUTTON3_CLICKED },
+    { 3, ZCME_DOUBLE_CLICKED, BUTTON3_DOUBLE_CLICKED },
+    { 3, ZCME_TRIPLE_CLICKED, BUTTON3_TRIPLE_CLICKED },
+
+    { 4, ZCME_PRESSED, BUTTON4_PRESSED },
+    { 4, ZCME_RELEASED, BUTTON4_RELEASED },
+    { 4, ZCME_CLICKED, BUTTON4_CLICKED },
+    { 4, ZCME_DOUBLE_CLICKED, BUTTON4_DOUBLE_CLICKED },
+    { 4, ZCME_TRIPLE_CLICKED, BUTTON4_TRIPLE_CLICKED },
+
+#ifdef BUTTON5_PRESSED
+    /* Not defined if only 32 bits available */
+    { 5, ZCME_PRESSED, BUTTON5_PRESSED },
+    { 5, ZCME_RELEASED, BUTTON5_RELEASED },
+    { 5, ZCME_CLICKED, BUTTON5_CLICKED },
+    { 5, ZCME_DOUBLE_CLICKED, BUTTON5_DOUBLE_CLICKED },
+    { 5, ZCME_TRIPLE_CLICKED, BUTTON5_TRIPLE_CLICKED },
+#endif
+    { 0, 0, 0 }
+};
+
+mmask_t zcurses_mouse_mask = ALL_MOUSE_EVENTS;
+
+#endif
+
 /* Autogenerated keypad string/number mapping*/
 #include "curses_keys.h"
 
@@ -919,6 +989,7 @@
     ZCWin w;
     char *var;
     int keypadnum = -1;
+    int nargs = arrlen(args);
 #ifdef HAVE_WGET_WCH
     int ret;
     wint_t wi;
@@ -936,12 +1007,37 @@
 
     w = (ZCWin)getdata(node);
 
-    if (args[1] && args[2]) {
+    if (nargs >= 3) {
 	keypad(w->win, TRUE);
     } else {
 	keypad(w->win, FALSE);
     }
 
+    if (nargs >= 4) {
+#ifdef NCURSES_MOUSE_VERSION
+	if (!(zcurses_flags & ZCF_MOUSE_ACTIVE) ||
+	    (zcurses_flags & ZCF_MOUSE_MASK_CHANGED)) {
+	    if (mousemask(zcurses_mouse_mask, NULL) == ERR) {
+		zwarnnam(nam, "current mouse mode is not supported");
+		return 1;
+	    }
+	    zcurses_flags = (zcurses_flags & ~ZCF_MOUSE_MASK_CHANGED) |
+		ZCF_MOUSE_ACTIVE;
+	}
+#else
+	zwarnnam(nam, "mouse events are not supported");
+	return 1;
+#endif
+    }
+#ifdef NCURSES_MOUSE_VERSION
+    else {
+	if (zcurses_flags & ZCF_MOUSE_ACTIVE) {
+	    mousemask((mmask_t)0, NULL);
+	    zcurses_flags &= ~ZCF_MOUSE_ACTIVE;
+	}
+    }
+#endif
+
 #ifdef HAVE_WGET_WCH
     switch (wget_wch(w->win, &wi)) {
     case OK:
@@ -988,32 +1084,97 @@
 	var = "REPLY";
     if (!setsparam(var, ztrdup(instr)))
 	return 1;
-    if (args[1] && args[2]) {
+    if (nargs >= 3) {
 	if (keypadnum > 0) {
-	    const struct zcurses_namenumberpair *nnptr;
-	    char fbuf[DIGBUFSIZE+1];
-
-	    for (nnptr = keypad_names; nnptr->name; nnptr++) {
-		if (keypadnum == nnptr->number) {
-		    if (!setsparam(args[2], ztrdup(nnptr->name)))
-			return 1;
+#ifdef NCURSES_MOUSE_VERSION
+	    if (nargs >= 4 && keypadnum == KEY_MOUSE) {
+		MEVENT mevent;
+		char digits[DIGBUFSIZE];
+		LinkList margs;
+		const struct zcurses_mouse_event *zcmmp = zcurses_mouse_map;
+
+		if (!setsparam(args[2], ztrdup("MOUSE")))
+		    return 1;
+		if (getmouse(&mevent) == ERR) {
+		    /*
+		     * This may happen if the mouse wasn't in
+		     * the window, so set the array to empty
+		     * but return success.
+		     */
+		    setaparam(args[3], mkarray(NULL));
 		    return 0;
 		}
-	    }
-	    if (keypadnum > KEY_F0) {
-		/* assume it's a function key */
-		sprintf(fbuf, "F%d", keypadnum - KEY_F0);
+		margs = newlinklist();
+		sprintf(digits, "%d", (int)mevent.id);
+		addlinknode(margs, dupstring(digits));
+		sprintf(digits, "%d", mevent.x);
+		addlinknode(margs, dupstring(digits));
+		sprintf(digits, "%d", mevent.y);
+		addlinknode(margs, dupstring(digits));
+		sprintf(digits, "%d", mevent.z);
+		addlinknode(margs, dupstring(digits));
+
+		/*
+		 * We only expect one event, but it doesn't hurt
+		 * to keep testing.
+		 */
+		for (; zcmmp->button; zcmmp++) {
+		    if (mevent.bstate & zcmmp->event) {
+			const struct zcurses_namenumberpair *zcmelp =
+			    zcurses_mouse_event_list;
+			for (; zcmelp->name; zcmelp++) {
+			    if (zcmelp->number == zcmmp->what) {
+				char *evstr = zhalloc(strlen(zcmelp->name)+2);
+				sprintf(evstr, "%s%d", zcmelp->name,
+					zcmmp->button);
+				addlinknode(margs, evstr);
+
+				break;
+			    }
+			}
+		    }
+		}
+		if (mevent.bstate & BUTTON_SHIFT)
+		    addlinknode(margs, "SHIFT");
+		if (mevent.bstate & BUTTON_CTRL)
+		    addlinknode(margs, "CTRL");
+		if (mevent.bstate & BUTTON_SHIFT)
+		    addlinknode(margs, "ALT");
+		if (!setaparam(args[3], zlinklist2array(margs)));
+		    return 1;
 	    } else {
-		/* print raw number */
-		sprintf(fbuf, "%d", keypadnum);
+#endif
+		const struct zcurses_namenumberpair *nnptr;
+		char fbuf[DIGBUFSIZE+1];
+
+		for (nnptr = keypad_names; nnptr->name; nnptr++) {
+		    if (keypadnum == nnptr->number) {
+			if (!setsparam(args[2], ztrdup(nnptr->name)))
+			    return 1;
+			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);
+		}
+		if (!setsparam(args[2], ztrdup(fbuf)))
+		    return 1;
+#ifdef NCURSES_MOUSE_VERSION
 	    }
-	    if (!setsparam(args[2], ztrdup(fbuf)))
-		return 1;
+#endif
 	} else {
 	    if (!setsparam(args[2], ztrdup("")))
 		return 1;
 	}
     }
+#ifdef NCURSES_MOUSE_VERSION
+    if (keypadnum != KEY_MOUSE && nargs >= 4)
+	setaparam(args[3], mkarray(NULL));
+#endif
     return 0;
 }
 
@@ -1058,6 +1219,55 @@
 
 
 static int
+zccmd_mouse(const char *nam, char **args)
+{
+#ifdef NCURSES_MOUSE_VERSION
+    int ret = 0;
+
+    for (; *args; args++) {
+	if (!strcmp(*args, "delay")) {
+	    char *eptr;
+	    zlong delay;
+
+	    if (!*++args ||
+		((delay = zstrtol(*args, &eptr, 10)), eptr != NULL)) {
+		zwarnnam(nam, "mouse delay requires an integer argument");
+		return 1;
+	    }
+	    if (mouseinterval((int)delay) != OK)
+		ret = 1;
+	} else {
+	    char *arg = *args;
+	    int onoff = 1;
+	    if (*arg == '+')
+		arg++;
+	    else if (*arg == '-') {
+		arg++;
+		onoff = 0;
+	    }
+	    if (!strcmp(arg, "motion")) {
+		mmask_t old_mask = zcurses_mouse_mask;
+		if (onoff)
+		    zcurses_mouse_mask |= REPORT_MOUSE_POSITION;
+		else
+		    zcurses_mouse_mask &= ~REPORT_MOUSE_POSITION;
+		if (old_mask != zcurses_mouse_mask)
+		    zcurses_flags |= ZCF_MOUSE_MASK_CHANGED;
+	    } else {
+		zwarnnam(nam, "unrecognised mouse command: %s", *arg);
+		return 1;
+	    }
+	}
+    }
+
+    return ret;
+#else
+    return 1;
+#endif
+}
+
+
+static int
 zccmd_position(const char *nam, char **args)
 {
     LinkNode node;
@@ -1141,8 +1351,9 @@
 	{"attr", zccmd_attr, 2, -1},
 	{"bg", zccmd_bg, 2, -1},
 	{"scroll", zccmd_scroll, 2, 2},
-	{"input", zccmd_input, 1, 3},
+	{"input", zccmd_input, 1, 4},
 	{"timeout", zccmd_timeout, 2, 2},
+	{"mouse", zccmd_mouse, 1, -1},
 	{"touch", zccmd_touch, 1, -1},
 	{NULL, (zccmd_t)0, 0, 0}
     };
@@ -1165,7 +1376,7 @@
 	zwarnnam(nam, "too few arguments for subcommand: %s", args[0]);
 	return 1;
     } else if (zcsc->maxargs >= 0 && num_args > zcsc->maxargs) {
-	zwarnnam(nam, "too may arguments for subcommand: %s", args[0]);
+	zwarnnam(nam, "too many arguments for subcommand: %s", args[0]);
 	return 1;
     }
 

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


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

* PATCH: zcurses querychar
  2007-11-07 22:30 PATCH: zcurses mouse handling Peter Stephenson
@ 2007-11-10 20:26 ` Peter Stephenson
  0 siblings, 0 replies; 2+ messages in thread
From: Peter Stephenson @ 2007-11-10 20:26 UTC (permalink / raw)
  To: zsh

On Wed, 07 Nov 2007 22:30:06 +0000
Peter Stephenson <p.w.stephenson@ntlworld.com> wrote:
> I think the big thing missing now is to be able to query characters and
> attributes within windows.

Like so, including wide character support.  I've obsessively rewritten the
manual to refer to subcommands as such; the new bit is at the end of the
changes to the file.

There is a reason after all to have both string and char: string moves
the cursor, char doesn't.

We could probably do with some global querying of state, but otherwise I
think we're in good shape.

I tested it with this function (curses_querychar), which tests quite a
lot of the system (but only in one window).


# Move around the window.  The first line outputs information about
# the character under the cursor.  ASCII keys insert, cursor and backspace
# keys have the obvious effect.  F1 is followed by a key for attribute:
# lower case to turn on, upper case to turn off: bold, dim, reverse, standout,
# underline.  F2 is followed by two colors, for foreground and background:
# two out of (b)lack, (B)lue, (c)yan, (d)efault, (g)reen, (m)agenta,
# (r)ed, (w)hite, (y)ellow.  Any other non-character input aborts.

zmodload zsh/curses

{
  zcurses init

  integer x=0 y=1 i
  local REPLY key attr
  local -a reply color
  while true; do
    zcurses move stdscr $y $x
    zcurses querychar stdscr reply
    zcurses move stdscr 0 0
    reply=(${(qq)reply})
    zcurses string stdscr "($reply)"
    zcurses clear stdscr eol
    zcurses move stdscr $y $x
    zcurses refresh
    zcurses input stdscr REPLY key
    case $key in
      (UP)
      (( y > 1 && y-- ))
      ;;

      (DOWN)
      (( y < LINES-1 && y++ ))
      ;;

      (LEFT)
      (( x > 0 && x-- ))
      ;;

      (RIGHT)
      (( x < COLUMNS-1 && x++ ))
      ;;
      
      (BACKSPACE)
      if (( x > 0 )); then
	(( x-- ))
      elif (( y > 1 )); then
	(( y--, x = LINES-1 ))
      fi
      zcurses move stdscr $y $x
      zcurses char stdscr " "
      ;;

      (F1)
      zcurses input stdscr REPLY
      case $REPLY in
	(b)
	attr=+bold
	;;

	(B)
	attr=-bold
	;;

	(d)
	attr=+dim
	;;

	(D)
	attr=-dim
	;;

	(r)
	attr=+reverse
	;;

	(R)
	attr=-reverse
	;;

	(s)
	attr=+standout
	;;

	(S)
	attr=-standout
	;;

	(u)
	attr=+underline
	;;

	(U)
	attr=-underline
	;;

	(*)
	continue
	;;
      esac
      zcurses attr stdscr $attr
      ;;

      (F2)
      # read fg and bg color
      for (( i = 1; i <= 2; i++ )); do
	zcurses input stdscr
	case $REPLY in
	  (b) color[i]=black;;
	  (B) color[i]=blue;;
	  (c) color[i]=cyan;;
	  (d) color[i]=default;;
	  (g) color[i]=green;;
	  (m) color[i]=magenta;;
	  (r) color[i]=red;;
	  (w) color[i]=white;;
	  (y) color[i]=yellow;;
	  (*) continue 2
	esac
      done
      zcurses attr stdscr $color[1]/$color[2]
      ;;

      ("")
      zcurses char stdscr $REPLY
      if (( x == COLUMNS-1 )); then
	if (( y < LINES-1 )); then
	  (( y++, x = 0 ))
	fi
      else
	(( x++ ))
      fi
      ;;

      (*)
      break
      ;;
    esac
  done

} always {
  zcurses end
}


Index: configure.ac
===================================================================
RCS file: /cvsroot/zsh/zsh/configure.ac,v
retrieving revision 1.77
diff -u -r1.77 configure.ac
--- configure.ac	1 Nov 2007 17:57:57 -0000	1.77
+++ configure.ac	10 Nov 2007 20:19:53 -0000
@@ -1145,7 +1145,7 @@
 	       brk sbrk \
 	       pathconf sysconf \
 	       tgetent tigetflag tigetnum tigetstr setupterm initscr \
-	       setcchar waddwstr wget_wch use_default_colors \
+	       getcchar setcchar waddwstr wget_wch win_wch use_default_colors \
 	       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.20
diff -u -r1.20 mod_curses.yo
--- Doc/Zsh/mod_curses.yo	7 Nov 2007 22:35:15 -0000	1.20
+++ Doc/Zsh/mod_curses.yo	10 Nov 2007 20:19:54 -0000
@@ -26,14 +26,16 @@
 xitem(tt(zcurses) tt(scroll) var(targetwin) [ tt(on) | tt(off) | {+/-}var(lines) ])
 xitem(tt(zcurses) tt(input) var(targetwin) [ var(param) [ var(kparam) [ var(mparam) ] ] ])
 xitem(tt(zcurses) tt(mouse) [ tt(delay) var(num) | {+/-}tt(motion) ])
-item(tt(zcurses) tt(timeout) var(targetwin) var(intval))(
+xitem(tt(zcurses) tt(timeout) var(targetwin) var(intval))
+item(tt(zcurses) tt(querychar) 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
 the terminal to be in an unwanted state.
 
-With tt(addwin), create a window with var(nlines) lines and var(ncols) columns.
-Its upper left corner will be placed at row var(begin_y) and column
+The subcommand tt(addwin) creates a window with var(nlines) lines and
+var(ncols) columns.  Its upper left corner will be placed at row
+var(begin_y) and column
 var(begin_x) of the screen.  var(targetwin) is a string and refers
 to the name of a window that is not currently assigned.  Note
 in particular the curses convention that vertical values appear
@@ -46,37 +48,40 @@
 Note that the coordinates of subwindows are relative to the screen, not
 the parent, as with other windows.
 
-Use tt(delwin) to delete a window created with tt(addwin).  Note
-that tt(end) does em(not) implicitly delete windows, and that
-tt(delwin) does not erase the screen image of the window.
+Use the subcommand tt(delwin) to delete a window created with
+tt(addwin).  Note that tt(end) does em(not) implicitly delete windows,
+and that tt(delwin) does not erase the screen image of the window.
 
 The window corresponding to the full visible screen is called
 tt(stdscr); it always exists after `tt(zcurses init)' and cannot
 be delete with tt(delwin).
 
-The tt(refresh) command will refresh window var(targetwin); this is
+The subcommand tt(refresh) will refresh window var(targetwin); this is
 necessary to make any pending changes (such as characters you have
 prepared for output with tt(char)) visible on the screen.  tt(refresh)
 without an argument causes the screen to be cleared and redrawn.
 If multiple windows are given, the screen is updated once at the end.
 
-The tt(touch) command marks the var(targetwin)s listed as changed.
+The subcommand tt(touch) marks the var(targetwin)s listed as changed.
 This is necessary before tt(refresh)ing windows if a window that
 was in front of another window (which may be tt(stdscr)) is deleted.
 
-tt(move) moves the cursor position in var(targetwin) to new coordinates
-var(new_y) and var(new_x).
-
-tt(clear) erases the contents of var(targetwin).  One (and no more than one)
-of three options may be specified.  With the option tt(redraw),
-in addition the next tt(refresh) of var(targetwin) will cause the screen to be
-cleared and repainted.  With the option tt(eol), var(targetwin) is only
-cleared to the end of the current cursor line.  With the option
+The subcommand tt(move) moves the cursor position in var(targetwin) to
+new coordinates var(new_y) and var(new_x).  Note that the 
+subcommand tt(string) (but not the subcommand tt(char)) advances the
+cursor position over the characters added.
+
+The subcommand tt(clear) erases the contents of var(targetwin).  One
+(and no more than one) of three options may be specified.  With the
+option tt(redraw), in addition the next tt(refresh) of var(targetwin)
+will cause the screen to be cleared and repainted.  With the option
+tt(eol), var(targetwin) is only cleared to the end of the current cursor
+line.  With the option
 tt(bot), var(targetwin) is cleared to the end of the window, i.e
 everything to the right and below the cursor is cleared.
 
-tt(location) writes various positions associated with var(targetwin)
-into the array named var(array).
+The subcommand tt(location) writes various positions associated with
+var(targetwin) into the array named var(array).
 These are, in order:
 startsitem()
 sitem()(The y and x coordinates of the cursor relative to the top left
@@ -94,12 +99,12 @@
 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)
-given on the line may be prepended by a tt(+) to set or a tt(-) to
-unset that attribute; tt(+) is assumed if absent.  The attributes
-supported are tt(blink), tt(bold), tt(dim), tt(reverse), tt(standout),
-and tt(underline).
+The subcommand tt(attr) will set var(targetwin)'s attributes or
+foreground/background color pair for any successive character output.
+Each var(attribute) given on the line may be prepended by a tt(+) to set
+or a tt(-) to unset that attribute; tt(+) is assumed if absent.  The
+attributes supported are tt(blink), tt(bold), tt(dim), tt(reverse),
+tt(standout), and tt(underline).
 
 Each var(fg_col)tt(/)var(bg_col) attribute (to be read as
 `var(fg_col) on var(bg_col)') sets the foreground and background color
@@ -118,18 +123,19 @@
 of attributes override the existing background, turning attributes
 off in the arguments is not useful, though this does not cause an error.
 
-tt(scroll) can be used with tt(on) or tt(off) to enabled or disable
-scrolling of a window when the cursor would otherwise move below the
-window due to typing or output.  It can also be used with a positive
-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.
-
-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).
+The subcommand tt(scroll) can be used with tt(on) or tt(off) to enabled
+or disable scrolling of a window when the cursor would otherwise move
+below the window due to typing or output.  It can also be used with a
+positive 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.
+
+The subcommand 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(kparam) are supplied, the key is read in
 `keypad' mode.  In this mode special keys such as function keys and
@@ -184,13 +190,22 @@
 which are always reported.  However, it appears reports for mouse
 motion are not currently implemented.
 
-tt(timeout) specifies a timeout value for input from var(targetwin).
-If var(intval) is negative, `tt(zcurses input)' waits indefinitely for
-a character to be typed; this is the default.  If var(intval) is zero,
-`tt(zcurses input)' returns immediately; if there is typeahead it is
-returned, else no input is done and status 1 is returned.  If var(intval)
-is positive, `tt(zcurses input)' waits var(intval) milliseconds for
-input and if there is none at the end of that period returns status 1.
+The subcommand tt(timeout) specifies a timeout value for input from
+var(targetwin).  If var(intval) is negative, `tt(zcurses input)' waits
+indefinitely for a character to be typed; this is the default.  If
+var(intval) is zero, `tt(zcurses input)' returns immediately; if there
+is typeahead it is returned, else no input is done and status 1 is
+returned.  If var(intval) is positive, `tt(zcurses input)' waits
+var(intval) milliseconds for input and if there is none at the end of
+that period returns status 1.
+
+The subcommand tt(querychar) queries the character at the current cursor
+position.  The return values are stored in the array named var(param) if
+supplied, else in the array tt(reply).  The first value is the character
+(which may be a multibyte character if the system supports them); the
+second is the color pair in the usual var(fg_col)tt(/)var(bg_col)
+notation.  Any attributes other than color that apply to the character,
+as set with the subcommand tt(attr), appear as additional elements.
 )
 enditem()
 
Index: Src/Modules/curses.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Modules/curses.c,v
retrieving revision 1.37
diff -u -r1.37 curses.c
--- Src/Modules/curses.c	8 Nov 2007 12:13:20 -0000	1.37
+++ Src/Modules/curses.c	10 Nov 2007 20:19:54 -0000
@@ -41,9 +41,11 @@
 #endif
 
 #ifndef MULTIBYTE_SUPPORT
+# undef HAVE_GETCCHAR
 # undef HAVE_SETCCHAR
 # undef HAVE_WADDWSTR
 # undef HAVE_WGET_WCH
+# undef HAVE_WIN_WCH
 #endif
 
 #ifdef HAVE_SETCCHAR
@@ -379,6 +381,25 @@
     return cpn;
 }
 
+static Colorpairnode cpn_match;
+
+static void
+zcurses_colornode(HashNode hn, int cp)
+{
+    Colorpairnode cpn = (Colorpairnode)hn;
+    if (cpn->colorpair == (short)cp)
+	cpn_match = cpn;
+}
+
+static Colorpairnode
+zcurses_colorget_reverse(short cp)
+{
+    cpn_match = NULL;
+    scanhashtable(zcurses_colorpairs, 0, 0, 0,
+		  zcurses_colornode, cp);
+    return cpn_match;
+}
+
 static void
 freecolorpairnode(HashNode hn)
 {
@@ -1302,6 +1323,100 @@
 
 
 static int
+zccmd_querychar(const char *nam, char **args)
+{
+    LinkNode node;
+    ZCWin w;
+    short cp;
+    Colorpairnode cpn;
+    const struct zcurses_namenumberpair *zattrp;
+    LinkList clist;
+#if defined(HAVE_WIN_WCH) && defined(HAVE_GETCCHAR)
+    wchar_t c;
+    cchar_t cc;
+    attr_t attrs;
+    int count;
+    VARARR(char, instr, 2*MB_CUR_MAX+1);
+#else
+    chtype inc;
+    char 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 defined(HAVE_WIN_WCH) && defined(HAVE_GETCCHAR)
+    if (win_wch(w->win, &cc) == ERR)
+	return 1;
+
+    if (getcchar(&cc, &c, &attrs, &cp, NULL) == ERR)
+	return 1;
+    /* Hmmm... I always get 0 for cp, whereas the following works... */
+    cp = PAIR_NUMBER(winch(w->win));
+
+    count = wctomb(instr, c);
+    if (count == -1)
+	return 1;
+    (void)metafy(instr, count, META_NOALLOC);
+#else
+    inc = winch(w->win);
+    /* I think the following is correct, the manual is a little terse */
+    cp = PAIR_NUMBER(inc);
+    inc &= A_CHARTEXT;
+    if (imeta(inc)) {
+	instr[0] = Meta;
+	instr[1] = STOUC(inc ^ 32);
+	instr[2] = '\0';
+    } else {
+	instr[0] = STOUC(inc);
+	instr[1] = '\0';
+    }
+    /*
+     * I'm guessing this is OK... header says attr_t must be at
+     * least as wide as chtype.
+     */
+    attrs = (attr_t)inc;
+#endif
+
+    /*
+     * Attribute numbers vary, so make a linked list.
+     * This also saves us from doing the permanent allocation till
+     * the end.
+     */
+    clist = newlinklist();
+    /* First the (possibly multibyte) character itself. */
+    addlinknode(clist, instr);
+    /*
+     * Next the colo[u]r.
+     * We should be able to match it in the colorpair list, but
+     * if some reason we can't, fail safe and output the number.
+     */
+    cpn = zcurses_colorget_reverse(cp);
+    if (cpn) {
+	addlinknode(clist, cpn->node.nam);
+    } else {
+	/* report color pair number */
+	char digits[DIGBUFSIZE];
+	sprintf(digits, "%d", (int)cp);
+	addlinknode(clist, digits);
+    }
+    /* Now see what attributes are present. */
+    for (zattrp = zcurses_attributes; zattrp->name; zattrp++) {
+	if (attrs & zattrp->number)
+	    addlinknode(clist, zattrp->name);
+    }
+
+    /* Turn this into an array and store it. */
+    return !setaparam(args[1] ? args[1] : "reply", zlinklist2array(clist));
+}
+
+
+static int
 zccmd_touch(const char *nam, char **args)
 {
     LinkNode node;
@@ -1354,6 +1469,7 @@
 	{"input", zccmd_input, 1, 4},
 	{"timeout", zccmd_timeout, 2, 2},
 	{"mouse", zccmd_mouse, 0, -1},
+	{"querychar", zccmd_querychar, 1, 2},
 	{"touch", zccmd_touch, 1, -1},
 	{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] 2+ messages in thread

end of thread, other threads:[~2007-11-10 20:27 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-11-07 22:30 PATCH: zcurses mouse handling Peter Stephenson
2007-11-10 20:26 ` PATCH: zcurses querychar 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).