From d57d1a582aed66b61737cf5c496dd74e86cb6cab Mon Sep 17 00:00:00 2001 From: Alexander Tsepkov Date: Tue, 12 Feb 2013 23:33:26 -0500 Subject: [PATCH] Added support for all mouse modes In addition to normal mode (1000), added support for XTERM (1005), URXVT (1015), and SGR (1006) modes. Also added safety to avoid sending garbage characters when the other application uses a mouse mode incorrectly. --- Recipe | 9 ++-- putty.h | 2 +- terminal.c | 113 ++++++++++++++++++++++++++++++++++++++++++-------- terminal.h | 3 +- unix/gtkwin.c | 24 +++++++---- 5 files changed, 120 insertions(+), 31 deletions(-) diff --git a/Recipe b/Recipe index 20ab841e..4a9cb013 100644 --- a/Recipe +++ b/Recipe @@ -278,6 +278,9 @@ WINMISC = MISC winstore winnet winhandl cmdline windefs winmisc winproxy UXMISC = MISC uxstore uxsel uxnet cmdline uxmisc uxproxy time OSXMISC = MISC uxstore uxsel osxsel uxnet uxmisc uxproxy time +# Extended mouse support (> 223 characters) +UTF8MOUSE = fromucs slookup sbcsdat sbcs utf8 + # Character set library, for use in pterm. CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc @@ -304,8 +307,8 @@ U_BE_NOSSH = be_nos_s uxser nocproxy # keywords [G] for Windows GUI app, [C] for Console app, [X] for # X/GTK Unix app, [U] for command-line Unix app. -putty : [G] GUITERM NONSSH WINSSH W_BE_ALL WINMISC winx11 putty.res LIBS -puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res nogss LIBS +putty : [G] GUITERM NONSSH WINSSH W_BE_ALL WINMISC winx11 putty.res UTF8MOUSE LIBS +puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res nogss UTF8MOUSE LIBS plink : [C] winplink wincons NONSSH WINSSH W_BE_ALL logging WINMISC + winx11 plink.res winnojmp LIBS pscp : [C] pscp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC @@ -320,7 +323,7 @@ pageant : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234 puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version + sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc + sshpubk sshaes sshsh256 sshsh512 import winutils puttygen.res - + tree234 notiming winhelp winnojmp LIBS wintime + + tree234 notiming winhelp winnojmp UTF8MOUSE LIBS wintime pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg diff --git a/putty.h b/putty.h index 78bc4294..ebc35caf 100644 --- a/putty.h +++ b/putty.h @@ -196,7 +196,7 @@ typedef enum { } Mouse_Button; typedef enum { - MA_NOTHING, MA_CLICK, MA_2CLK, MA_3CLK, MA_DRAG, MA_RELEASE + MA_NOTHING, MA_CLICK, MA_2CLK, MA_3CLK, MA_DRAG, MA_MOVE, MA_RELEASE } Mouse_Action; /* Keyboard modifiers -- keys the user is actually holding down */ diff --git a/terminal.c b/terminal.c index 915414c3..2b6f0f80 100644 --- a/terminal.c +++ b/terminal.c @@ -10,6 +10,7 @@ #include #include "putty.h" #include "terminal.h" +#include "charset.h" #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) ) #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) ) @@ -45,6 +46,17 @@ #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320) #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI) +#define MM_NONE 0x00 /* No tracking */ +#define MM_NORMAL 0x01 /* Normal tracking mode */ +#define MM_BTN_EVENT 0x02 /* Button event mode */ +#define MM_ANY_EVENT 0x03 /* Any event mode */ + +/** Mouse tracking protocols */ +#define MP_NORMAL 0x00 /* CSI M Cb Cx Cy */ +#define MP_URXVT 0x01 /* CSI Db ; Dx ; Dy M */ +#define MP_SGR 0x02 /* CSI Db ; Dx ; Dy M/m */ +#define MP_XTERM 0x03 /* CSI M Cb WCx WCy */ + #define TM_PUTTY (0xFFFF) #define UPDATE_DELAY ((TICKSPERSEC+49)/50)/* ticks to defer window update */ @@ -1223,7 +1235,8 @@ static void power_on(Terminal *term, int clear) term->erase_char = term->basic_erase_char; term->alt_which = 0; term_print_finish(term); - term->xterm_mouse = 0; + term->xterm_mouse_mode = MM_NONE; + term->xterm_mouse_protocol = MP_NORMAL; set_raw_mouse_mode(term->frontend, FALSE); { int i; @@ -1390,7 +1403,7 @@ void term_reconfig(Terminal *term, Config *cfg) if (term->cfg.no_alt_screen) swap_screen(term, 0, FALSE, FALSE); if (term->cfg.no_mouse_rep) { - term->xterm_mouse = 0; + term->xterm_mouse_mode = MM_NONE; set_raw_mouse_mode(term->frontend, 0); } if (term->cfg.no_remote_charset) { @@ -2391,13 +2404,26 @@ static void toggle_mode(Terminal *term, int mode, int query, int state) term->disptop = 0; break; case 1000: /* xterm mouse 1 (normal) */ - term->xterm_mouse = state ? 1 : 0; + term->xterm_mouse_mode = state ? MM_NORMAL : MM_NONE; set_raw_mouse_mode(term->frontend, state); break; case 1002: /* xterm mouse 2 (inc. button drags) */ - term->xterm_mouse = state ? 2 : 0; + term->xterm_mouse_mode = state ? MM_BTN_EVENT : MM_NONE; set_raw_mouse_mode(term->frontend, state); break; + case 1003: /* xterm any event tracking */ + term->xterm_mouse_mode = state ? MM_ANY_EVENT : MM_NONE; + set_raw_mouse_mode(term->frontend, state); + break; + case 1005: /* use XTERM 1005 mouse protocol */ + term->xterm_mouse_protocol = state ? MP_XTERM : MP_NORMAL; + break; + case 1006: /* use SGR 1006 mouse protocol */ + term->xterm_mouse_protocol = state ? MP_SGR : MP_NORMAL; + break; + case 1015: /* use URXVT 1015 mouse protocol */ + term->xterm_mouse_protocol = state ? MP_URXVT : MP_NORMAL; + break; case 1047: /* alternate screen */ compatibility(OTHER); deselect(term); @@ -5648,7 +5674,7 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, { pos selpoint; termline *ldata; - int raw_mouse = (term->xterm_mouse && + int raw_mouse = (term->xterm_mouse_mode && !term->cfg.no_mouse_rep && !(term->cfg.mouse_override && shift)); int default_seltype; @@ -5701,36 +5727,47 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, if (raw_mouse && (term->selstate != ABOUT_TO) && (term->selstate != DRAGGING)) { int encstate = 0, r, c; - char abuf[16]; + char abuf[64]; + char m; /* SGR 1006's postfix character ('M' or 'm') */ + size_t l; if (term->ldisc) { switch (braw) { case MBT_LEFT: - encstate = 0x20; /* left button down */ + encstate = 0x00; /* left button down */ break; case MBT_MIDDLE: - encstate = 0x21; + encstate = 0x01; break; case MBT_RIGHT: - encstate = 0x22; + encstate = 0x02; break; case MBT_WHEEL_UP: - encstate = 0x60; + encstate = 0x40; break; case MBT_WHEEL_DOWN: - encstate = 0x61; + encstate = 0x41; + break; + case MBT_NOTHING: /* for any event tracking */ + encstate = 0x03; break; default: break; /* placate gcc warning about enum use */ } switch (a) { case MA_DRAG: - if (term->xterm_mouse == 1) + if (term->xterm_mouse_mode == MM_NORMAL) return; encstate += 0x20; break; + case MA_MOVE: + if (term->xterm_mouse_mode != MM_ANY_EVENT) + return; + encstate += 0x20; /* add motion indicator */ + break; case MA_RELEASE: - encstate = 0x23; + if (term->xterm_mouse_protocol != MP_SGR) + encstate = 0x03; term->mouse_is_down = 0; break; case MA_CLICK: @@ -5744,11 +5781,53 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, encstate += 0x04; if (ctrl) encstate += 0x10; - r = y + 33; - c = x + 33; - sprintf(abuf, "\033[M%c%c%c", encstate, c, r); - ldisc_send(term->ldisc, abuf, 6, 0); + switch (term->xterm_mouse_protocol) { + case MP_NORMAL: + /* add safety to avoid sending garbage sequences */ + if (x < 223 && y < 223) { + encstate += 0x20; + r = y + 33; + c = x + 33; + sprintf(abuf, "\033[M%c%c%c", encstate, c, r); + ldisc_send(term->ldisc, abuf, 6, 0); + } + break; + case MP_URXVT: + encstate += 0x20; + r = y + 1; + c = x + 1; + sprintf(abuf, "\033[%d;%d;%dM", encstate, c, r); + l = strlen(abuf); + ldisc_send(term->ldisc, abuf, l, 0); + break; + case MP_SGR: + r = y + 1; + c = x + 1; + m = a == MA_RELEASE ? 'm': 'M'; + sprintf(abuf, "\033[<%d;%d;%d%c", encstate, c, r, m); + l = strlen(abuf); + ldisc_send(term->ldisc, abuf, l, 0); + break; + case MP_XTERM: + /* add safety to avoid sending garbage sequences */ + if (x < 2015 && y < 2015) { + encstate += 0x20; + wchar_t input[2]; + wchar_t *inputp = input; + int inputlen = 2; + input[0] = x + 33; + input[1] = y + 33; + + l = sprintf(abuf, "\033[M%c", encstate); + l += charset_from_unicode(&inputp, &inputlen, + abuf + l, 4, + CS_UTF8, NULL, NULL, 0); + ldisc_send(term->ldisc, abuf, l, 0); + } + break; + default: break; /* placate gcc warning about enum use */ + } } return; } diff --git a/terminal.h b/terminal.h index aef47a5b..0e633dd7 100644 --- a/terminal.h +++ b/terminal.h @@ -151,7 +151,8 @@ struct terminal_tag { int seen_disp_event; int big_cursor; - int xterm_mouse; /* send mouse messages to host */ + int xterm_mouse_mode; /* mouse event mode */ + int xterm_mouse_protocol; /* mouse protocol */ int mouse_is_down; /* used while tracking mouse buttons */ int cset_attr[2]; diff --git a/unix/gtkwin.c b/unix/gtkwin.c index be8b2c3b..d0827688 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -1212,7 +1212,7 @@ gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data) gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) { struct gui_data *inst = (struct gui_data *)data; - int shift, ctrl, alt, x, y, button; + int shift, ctrl, alt, x, y, button, act; /* Remember the timestamp. */ inst->input_event_time = event->time; @@ -1222,19 +1222,25 @@ gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) shift = event->state & GDK_SHIFT_MASK; ctrl = event->state & GDK_CONTROL_MASK; alt = event->state & GDK_MOD1_MASK; - if (event->state & GDK_BUTTON1_MASK) - button = MBT_LEFT; - else if (event->state & GDK_BUTTON2_MASK) - button = MBT_MIDDLE; - else if (event->state & GDK_BUTTON3_MASK) + + if (event->state & GDK_BUTTON1_MASK) { + button = MBT_LEFT; + act = MA_DRAG; + } else if (event->state & GDK_BUTTON2_MASK) { + button = MBT_MIDDLE; + act = MA_DRAG; + } else if (event->state & GDK_BUTTON3_MASK) { button = MBT_RIGHT; - else - return FALSE; /* don't even know what button! */ + act = MA_DRAG; + } else { + button = MBT_NOTHING; /* no button is pressed */ + act = MA_MOVE; + } x = (event->x - inst->cfg.window_border) / inst->font_width; y = (event->y - inst->cfg.window_border) / inst->font_height; - term_mouse(inst->term, button, translate_button(button), MA_DRAG, + term_mouse(inst->term, button, translate_button(button), act, x, y, shift, ctrl, alt); return TRUE;