[PATCH, RFC] mouse tracking support
Hi,
I've updated Anatoly's patch from 2005 that adds mouse tracking using
slang/ncurses to mutt. With "set mouse" (unset by default) mutt will
react to button1/2/3/wheel with configurable bindings. I like it :)
I've tested it with Debian's ncurses which unfortunately doesn't
support the mouse wheel (yet), but I think it should work. slang is
untested. Testers welcome.
It would be nice if the patch could be considered for mutt 2.0.
(More comments below.)
# HG changeset patch
# User Christoph Berg <cb@xxxxxxxx>
# Date 1179420372 -7200
# Node ID c3665ea5ba2ac090527ce98fd672252e65c012b7
# Parent 33af2883d52b99ece0a935818a91293b502603aa
Add mouse ncurses/slang mouse tracking capability, set mouse=yes to enable.
Based on a patch by Anatoly Vorobey.
diff -r 33af2883d52b -r c3665ea5ba2a OPS
--- a/OPS Tue May 15 21:05:53 2007 +0200
+++ b/OPS Thu May 17 18:46:12 2007 +0200
@@ -129,6 +129,7 @@ OP_MAIN_UNDELETE_PATTERN "undelete messa
OP_MAIN_UNDELETE_PATTERN "undelete messages matching a pattern"
OP_MAIN_UNTAG_PATTERN "untag messages matching a pattern"
OP_MIDDLE_PAGE "move to the middle of the page"
+OP_MOUSE_SELECT "select entry with a mouse button"
OP_NEXT_ENTRY "move to the next entry"
OP_NEXT_LINE "scroll down one line"
OP_NEXT_PAGE "move to the next page"
diff -r 33af2883d52b -r c3665ea5ba2a curs_lib.c
--- a/curs_lib.c Tue May 15 21:05:53 2007 +0200
+++ b/curs_lib.c Thu May 17 18:46:12 2007 +0200
@@ -80,6 +80,9 @@ event_t mutt_getch (void)
{
int ch;
event_t err = {-1, OP_NULL }, ret;
+#ifndef USE_SLANG_CURSES
+ MEVENT m_event;
+#endif
if (!option(OPTUNBUFFEREDINPUT) && UngetCount)
return (KeyEvent[--UngetCount]);
@@ -100,7 +103,74 @@ event_t mutt_getch (void)
if(ch == ERR)
return err;
-
+
+#ifdef USE_SLANG_CURSES /* mouse tracking */
+ if (ch == KEY_SLANG_MOUSE) {
+ /* get button number and coordinates */
+ /* coordinates are further -1'd synchronize them with the values
+ ncurses returns: starting from 0,0 */
+ ch = getch();
+ if (ch == ERR)
+ return err;
+ LastButton = ch - 32;
+ ch = getch();
+ if (ch == ERR)
+ return err;
+ LastX = ch - 32 - 1;
+ ch = getch();
+ if (ch == ERR)
+ return err;
+ LastY = ch - 32 - 1;
+
+ dprint (5, (debugfile, "SLang mouse message: Button: %d X: %d Y: %d\n",
LastButton, LastX, LastY));
+
+ /* which event is it? */
+ ch = -1;
+
+ /* ignore shift-alt-control modifiers for now */
+
+ if (LastButton & 64) { /* wheel mouse? */
+ if ((LastButton & 3) == 0)
+ ch = KEY_MUTT_BUTTON4;
+ else if ((LastButton & 3) == 1)
+ ch = KEY_MUTT_BUTTON5;
+ } else {
+ if ((LastButton & 3) == 0)
+ ch = KEY_MUTT_BUTTON1;
+ else if ((LastButton & 3) == 1)
+ ch = KEY_MUTT_BUTTON2;
+ else if ((LastButton & 3) == 2)
+ ch = KEY_MUTT_BUTTON3;
+ }
+
+ dprint(5, (debugfile, "SLang mouse: emitting key 0x%x\n", ch));
+
+ }
+#else
+ if (ch == KEY_MOUSE) {
+ if (getmouse(&m_event) == OK) {
+ LastX = m_event.x;
+ LastY = m_event.y;
+ LastButton = m_event.bstate;
+ if (m_event.bstate & BUTTON1_CLICKED)
+ ch = KEY_MUTT_BUTTON1;
+ else if (m_event.bstate & BUTTON2_CLICKED)
+ ch = KEY_MUTT_BUTTON2;
+ else if (m_event.bstate & BUTTON3_CLICKED)
+ ch = KEY_MUTT_BUTTON3;
+ else if (m_event.bstate & BUTTON4_CLICKED)
+ ch = KEY_MUTT_BUTTON4;
+# if NCURSES_MOUSE_VERSION > 1
+ else if (m_event.bstate & BUTTON5_CLICKED)
+ ch = KEY_MUTT_BUTTON5;
+# endif
+ else ch = -1;
+ dprint (5, (debugfile, "NCurses mouse message: bstate: 0x%x X: %d Y:
%d\n", LastButton, LastX, LastY));
+ dprint(5, (debugfile, "NCurses mouse: emitting key 0x%x\n", ch));
+ }
+ }
+#endif /* mouse tracking */
+
if ((ch & 0x80) && option (OPTMETAKEY))
{
/* send ALT-x as ESC-x */
@@ -417,6 +487,10 @@ void mutt_endwin (const char *msg)
attrset (A_NORMAL);
mutt_refresh ();
+#ifdef USE_SLANG_CURSES
+ SLtt_set_mouse_mode(0, 0);
+ /* ncurses: endwin() resets xterm mouse tracking for us */
+#endif
endwin ();
}
diff -r 33af2883d52b -r c3665ea5ba2a curs_main.c
--- a/curs_main.c Tue May 15 21:05:53 2007 +0200
+++ b/curs_main.c Thu May 17 18:46:12 2007 +0200
@@ -744,6 +744,12 @@ int mutt_index_menu (void)
menu_current_bottom (menu);
break;
+ case OP_MOUSE_SELECT:
+ /* jump to a line if we're not on it already; otherwise
+ display that message */
+ menu_mouse_click (menu, OP_DISPLAY_MESSAGE);
+ break;
+
case OP_JUMP:
CHECK_MSGCOUNT;
diff -r 33af2883d52b -r c3665ea5ba2a functions.h
--- a/functions.h Tue May 15 21:05:53 2007 +0200
+++ b/functions.h Thu May 17 18:46:12 2007 +0200
@@ -79,6 +79,7 @@ struct binding_t OpGeneric[] = { /* map:
{ "current-middle", OP_CURRENT_MIDDLE, NULL },
{ "current-bottom", OP_CURRENT_BOTTOM, NULL },
{ "what-key", OP_WHAT_KEY, NULL },
+ { "mouse-select", OP_MOUSE_SELECT, "<Button1>" },
{ NULL, 0, NULL }
};
@@ -158,7 +159,7 @@ struct binding_t OpMain[] = { /* map: in
{ "next-unread", OP_MAIN_NEXT_UNREAD, NULL },
{ "previous-unread", OP_MAIN_PREV_UNREAD, NULL },
{ "parent-message", OP_MAIN_PARENT_MESSAGE, "P" },
-
+ { "mouse-select", OP_MOUSE_SELECT, "<Button1>" },
{ "extract-keys", OP_EXTRACT_KEYS, "\013" },
{ "forget-passphrase", OP_FORGET_PASSPHRASE, "\006" },
diff -r 33af2883d52b -r c3665ea5ba2a init.h
--- a/init.h Tue May 15 21:05:53 2007 +0200
+++ b/init.h Thu May 17 18:46:12 2007 +0200
@@ -1279,7 +1279,14 @@ struct option_t MuttVars[] = {
*/
#endif
-
+ { "mouse", DT_BOOL, R_NONE, OPTMOUSE, 0 },
+ /*
+ ** .pp
+ ** Controls whether or not Mutt will react to mouse clicks in your terminal.
+ ** .pp
+ ** \fBNote:\fP you need to set this option in .muttrc; it won't have any
+ ** effect when used interactively.
+ */
{ "move", DT_QUAD, R_NONE, OPT_MOVE, M_ASKNO },
/*
** .pp
diff -r 33af2883d52b -r c3665ea5ba2a keymap.c
--- a/keymap.c Tue May 15 21:05:53 2007 +0200
+++ b/keymap.c Thu May 17 18:46:12 2007 +0200
@@ -86,11 +86,24 @@ static struct mapping_t KeyNames[] = {
#ifdef KEY_NEXT
{ "<Next>", KEY_NEXT },
#endif
+
+ { "<Button1>", KEY_MUTT_BUTTON1 },
+ { "<Button2>", KEY_MUTT_BUTTON2 },
+ { "<Button3>", KEY_MUTT_BUTTON3 },
+ { "<WheelUp>", KEY_MUTT_BUTTON4 },
+ { "<WheelDown>", KEY_MUTT_BUTTON5 },
+ { "<Button4>", KEY_MUTT_BUTTON4 },
+ { "<Button5>", KEY_MUTT_BUTTON5 },
+
{ NULL, 0 }
};
/* contains the last key the user pressed */
int LastKey;
+
+/* coordinates of last mouse event, and button number */
+int LastX, LastY;
+int LastButton;
struct keymap_t *Keymaps[MENU_MAX];
@@ -661,6 +674,17 @@ void km_init (void)
/* edit-to (default "t") hides generic tag-entry in Compose menu
This will bind tag-entry to "T" in the Compose menu */
km_bindkey ("T", MENU_COMPOSE, OP_TAG);
+
+ /* mouse tracking bindings */
+ km_bindkey ("<Button3>", MENU_GENERIC, OP_EXIT);
+ km_bindkey ("<Button3>", MENU_PAGER, OP_EXIT);
+ km_bindkey ("<Button3>", MENU_MAIN, OP_QUIT);
+#if defined(USE_SLANG_CURSES) || NCURSES_MOUSE_VERSION > 1
+ km_bindkey ("<WheelUp>", MENU_PAGER, OP_PREV_LINE);
+ km_bindkey ("<WheelDown>", MENU_PAGER, OP_NEXT_LINE);
+ km_bindkey ("<WheelUp>", MENU_GENERIC, OP_PREV_ENTRY);
+ km_bindkey ("<WheelDown>", MENU_GENERIC, OP_NEXT_ENTRY);
+#endif
}
void km_error_key (int menu)
diff -r 33af2883d52b -r c3665ea5ba2a keymap.h
--- a/keymap.h Tue May 15 21:05:53 2007 +0200
+++ b/keymap.h Thu May 17 18:46:12 2007 +0200
@@ -87,6 +87,10 @@ extern struct keymap_t *Keymaps[];
/* dokey() records the last real key pressed */
extern int LastKey;
+/* coordinates of last mouse event, and button number */
+extern int LastX, LastY;
+extern int LastButton;
+
extern struct mapping_t Menus[];
struct binding_t
diff -r 33af2883d52b -r c3665ea5ba2a main.c
--- a/main.c Tue May 15 21:05:53 2007 +0200
+++ b/main.c Thu May 17 18:46:12 2007 +0200
@@ -714,6 +714,35 @@ int main (int argc, char **argv)
mutt_init (flags & M_NOSYSRC, commands);
mutt_free_list (&commands);
+ if (!option (OPTNOCURSES) && option (OPTMOUSE))
+ {
+#ifdef USE_SLANG_CURSES
+ /* TODO: also add a runtime option for this. maybe also for the
+ second argument, to force */
+ SLtt_set_mouse_mode (1, 0);
+
+ /* HACK: add ESC[M to SLang's keypad keymap, so that it "finds"
+ it and doesn't clear the input buffer. If we don't do this,
+ SLkeypad.c tries to match ESC[M and when it cannot, returns it
+ but before that flushes the remaining input, so we lose
+ parameters that follow ESC[M.
+
+ Sigh. SLang should support its own mouse tracking together with
+ its own keypad better. */
+
+ SLkp_define_keysym ("\033[M", KEY_SLANG_MOUSE);
+#else
+ mousemask (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED |
BUTTON4_CLICKED
+# if NCURSES_MOUSE_VERSION > 1
+ | BUTTON5_CLICKED
+# endif
+ , 0);
+ /* make ncurses give us a click for every press and ignore doubleclicks
+ and such. -1 here seems like it should be working but didn't. */
+ mouseinterval (0);
+#endif
+ }
+
/* Initialize crypto backends. */
crypt_init ();
diff -r 33af2883d52b -r c3665ea5ba2a menu.c
--- a/menu.c Tue May 15 21:05:53 2007 +0200
+++ b/menu.c Thu May 17 18:46:12 2007 +0200
@@ -662,6 +662,21 @@ void menu_prev_entry (MUTTMENU *menu)
}
else
mutt_error _("You are on the first entry.");
+}
+
+void menu_mouse_click (MUTTMENU *menu, int op)
+{
+ int click = LastY + menu->top - menu->offset;
+ if (click < 0)
+ click = 0;
+ if (click >= menu->max)
+ click = menu->max -1;
+ if (click == menu->current) {
+ mutt_ungetch(0, op);
+ } else {
+ menu->current = click;
+ menu->redraw = REDRAW_MOTION;
+ }
}
static int default_color (int i)
@@ -1005,6 +1020,12 @@ int mutt_menuLoop (MUTTMENU *menu)
mutt_error _("Jumping is not implemented for dialogs.");
else
menu_jump (menu);
+ break;
+
+ case OP_MOUSE_SELECT:
+ /* jump to a line if we're not on it already; otherwise
+ select entry */
+ menu_mouse_click (menu, OP_GENERIC_SELECT_ENTRY);
break;
case OP_ENTER_COMMAND:
diff -r 33af2883d52b -r c3665ea5ba2a mutt.h
--- a/mutt.h Tue May 15 21:05:53 2007 +0200
+++ b/mutt.h Thu May 17 18:46:12 2007 +0200
@@ -407,6 +407,7 @@ enum
OPTMETOO,
OPTMHPURGE,
OPTMIMEFORWDECODE,
+ OPTMOUSE, /* curses/slang mouse tracking */
OPTNARROWTREE,
OPTPAGERSTOP,
OPTPIPEDECODE,
diff -r 33af2883d52b -r c3665ea5ba2a mutt_curses.h
--- a/mutt_curses.h Tue May 15 21:05:53 2007 +0200
+++ b/mutt_curses.h Thu May 17 18:46:12 2007 +0200
@@ -185,6 +185,21 @@ void ci_start_color (void);
#define MAYBE_REDRAW(x) if (option (OPTNEEDREDRAW)) { unset_option
(OPTNEEDREDRAW); x = REDRAW_FULL; }
/* ----------------------------------------------------------------------------
+ * Support for mouse tracking
+ */
+
+/* Neither SLang nor ncurses use keysyms above 0x1000, use these
+ for our private keys */
+#ifdef USE_SLANG_CURSES
+# define KEY_SLANG_MOUSE 0x1001 /* a pseudo-key for SLang */
+#endif
+#define KEY_MUTT_BUTTON1 0x1011
+#define KEY_MUTT_BUTTON2 0x1012
+#define KEY_MUTT_BUTTON3 0x1013
+#define KEY_MUTT_BUTTON4 0x1014
+#define KEY_MUTT_BUTTON5 0x1015
+
+/* ----------------------------------------------------------------------------
* These are here to avoid compiler warnings with -Wall under SunOS 4.1.x
*/
diff -r 33af2883d52b -r c3665ea5ba2a mutt_menu.h
--- a/mutt_menu.h Tue May 15 21:05:53 2007 +0200
+++ b/mutt_menu.h Thu May 17 18:46:12 2007 +0200
@@ -102,6 +102,7 @@ void menu_current_middle (MUTTMENU *);
void menu_current_middle (MUTTMENU *);
void menu_current_bottom (MUTTMENU *);
void menu_check_recenter (MUTTMENU *);
+void menu_mouse_click (MUTTMENU *, int);
void menu_status_line (char *, size_t, MUTTMENU *, const char *);
MUTTMENU *mutt_new_menu (void);
Comments on the original message:
Re: Anatoly Vorobey 2005-04-03 <20050402224923.GA21137@xxxxxxxxx>
> Attached is a patch to enable [some degree of] mouse tracking support,
> for mutt compiled against either ncurses or SLang.
>
> The patch is against CVS HEAD; criticism, praise, bug reports, requests for
> patches against other versions, feature requests, questions, and so on are
> welcome.
>
> I'd especially like to hear if this has any chance of making it into mutt.
> (I'm aware of past discussions of this issue, and the fact that many
> users/developers see no reason to add such a feature. Still, I had an itch
> to scratch, so I wrote it).
>
> Notes:
>
> - To use, apply the patch, configure mutt as usual, with either ncurses or
> SLang, and then add either "#define HAVE_NCURSES_MOUSE 1" or
> "#define HAVE_SLANG_MOUSE 1" to your config.h, and then make, or re-make,
> mutt.
>
> Or, if you don't want to change config.h, use a hack such as
> $ export AM_CFLAGS="-DHAVE_SLANG_MOUSE=1"
> $ make
> That'll make mutt's Makefile pick up the needed flag.
>
> I haven't yet added the recognition of the right option to configure,
> because, in order to do that, I have to learn autoconf first. Been avoiding
> that for some years.
Mouse support is now a config option. I didn't add a configure flag as
I don't think having it built-in but disabled by default hurts.
> - With SLang, you must have a version recent enough to include
> SLtt_set_mouse_mode() . Based on slang's changes.txt, this appears to be
> 0.99.21, but I've only tested with 1.4.9 so far. I haven't tested this stuff
> on platforms other than Linux (so far).
Untested.
> - Currently the events that are recognised are: button clicks and wheel
> movements for wheel mice. Wheel movements are only recognized with SLang
> because ncurses doesn't support wheel movement recognition.
Wheel mouse should work with a ncurses compiled with --ext-mouse
(requires ABI/SONAME bump for libncurses).
> - The following bindings are hardcoded into keymap.c (but can of course be
> changed in .muttrc): In message index menu, wheel movement scrolls the menu;
> clicking a line moves the selection to that message, if it's not selected, or
> displays the message if it's alrady selected (as if you pressed Enter on it).
> This means also that double-clicking a line selects and displays the
> message. In pager mode, wheel movement scrolls the view. Ideas for other
> bindinds/new operations to add which would rely on them will be appreciated.
Left button will jump to/select a line in all menus. Right button is
bound to "exit" in menus/pager and "quit" in the index.
> - Your terminal program may or may not support the needed functionality for
> mouse tracking support to work. Currently, the SLang version only works with
> terminals that support "xterm mouse tracking" in the "old X10 compatibility
> mode", because this is what SLang's SLtt_set_mouse_mode() does. The ncurses
> version works with whatever ncurses was compiled to recognize, which will
> at least be "xterm mouse tracking" in the "normal tracking mode" (better than
> the one SLang uses), and also possibly GPM (Linux) and sysmouse (BSD). I only
> tested ncurses with xterm-style tracking so far. The terminal programs I
> tested this on are:
> * gnome-terminal, a recent version - ncurses and SLang work, wheel movement
> works with SLang.
> * xterm, some recent version - ncurses and SLang work, wheel movement
> doesn't work.
> * PuTTY - only works with ncurses, because PuTTY doesn't support the
> "old X10 compatibility mode".
Tested here with xterm.
> Plans:
>
> - introduce a runtime option to use/not use mouse tracking, therefore
> enabling mutt not to use it even if it's compiled in (suggestions for
> option name/behavior?)
> - integrate recognition of HAVE_SLANG_MOUSE/HAVE_NCURSES_MOUSE into
> configure;
set mouse.
> - configurable speed of wheel scrolling in pager/menu?
Should be done by macros (bind index WheelUp next-page or macro ...
<down><down><down> etc.)
> - look into bypassing SLang's SLtt_set_mouse_mode() and using normal
> mouse tracking with SLang; then it'd be possible to track and bind
> button releases and buttons+modifiers (shift, control, meta -- whenever the
> terminal program doesn't use them for its own actions).
>
> This might be a good thing to do, especially because it's possible that
> the only combination I have right now in which wheel movement works (SLang
> +
> gnome-terminal) does so because gnome-terminal sends too much information
> in violation, strictly speaking, of the X10 compatibility mode; the spec is
> a bit unclear on this. I'd like to see reports of other terminal programs.
xterm uses Shift+Mouse for selection handling as with disabled mouse
tracking; probably not possible/desirable to work around.
> - look into bypassing ncurses' handling of mouse tracking and parsing xterm
> mouse tracking ourselves; this would be bad because ncurses won't use
> GPM/sysmouse if they're available, but this would be good because mutt will
> be able to recognize wheel movement with ncurses.
While gpm support could be a nice feature, I don't think it is needed.
> - suggestions?
>
> I'm going to do some/all of the above based on whether I need these
> features, whether there's enough interest in the patch and
> requests/suggestions from other users, and whether it has any chance of
> making it into mutt.
>
> Best regards,
> Anatoly
Christoph