1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-11-01 18:30:55 +01:00

24073 (plus tweak): zcurses mouse handling

This commit is contained in:
Peter Stephenson 2007-11-07 22:35:13 +00:00
parent 1e836045b3
commit a563cd8958
3 changed files with 291 additions and 28 deletions

View file

@ -1,3 +1,9 @@
2007-11-07 Peter Stephenson <p.w.stephenson@ntlworld.com>
* 24073 (plus tweak to allow "zcurses mouse" with no additional
arguments): Doc/Zsh/mod_curses.yo, Src/Modules/curses.c:
add zcurses mouse handling.
2007-11-06 Peter Stephenson <p.w.stephenson@ntlworld.com>
* 24070: Src/utils.c, Test/A03quoting.ztst,

View file

@ -23,8 +23,9 @@ 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) ] [...])
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 @@ 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.
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

View file

@ -92,6 +92,7 @@ static struct ttyinfo saved_tty_state;
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 HashTable zcurses_colorpairs = NULL;
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 @@ static const struct zcurses_namenumberpair zcurses_colors[] = {
{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 @@ zccmd_input(const char *nam, char **args)
ZCWin w;
char *var;
int keypadnum = -1;
int nargs = arrlen(args);
#ifdef HAVE_WGET_WCH
int ret;
wint_t wi;
@ -936,12 +1007,37 @@ zccmd_input(const char *nam, char **args)
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 @@ zccmd_input(const char *nam, char **args)
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];
#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;
for (nnptr = keypad_names; nnptr->name; nnptr++) {
if (keypadnum == nnptr->number) {
if (!setsparam(args[2], ztrdup(nnptr->name)))
return 1;
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;
}
@ -1057,6 +1218,55 @@ zccmd_timeout(const char *nam, char **args)
}
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)
{
@ -1141,8 +1351,9 @@ bin_zcurses(char *nam, char **args, Options ops, UNUSED(int func))
{"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, 0, -1},
{"touch", zccmd_touch, 1, -1},
{NULL, (zccmd_t)0, 0, 0}
};
@ -1165,7 +1376,7 @@ bin_zcurses(char *nam, char **args, Options ops, UNUSED(int func))
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;
}