Package: xterm Version: 353-1 Severity: normal Dear Maintainer,
While developing a console based application using ncurses running under an exterm, I noticed I was getting double events for the side-scrolling buttons on my mouse (Elecom Huge: 12 buttons (including side-scrolling on the wheel)). Upon investigation, I found that while X itself is sending a press event followed immediately by a release event for buttons 6 and 7, I found that my program was recieving two press events for each press of either button 6 or 7 \x27[<0;14;24M <- button 1 pressed \x27[<0;14;24m <- button 1 preleased \x27[<66;14;24M <- button 6 presssed ONCE \x27[<66;14;24M \x27[<67;14;24M <- button 7 presssed ONCE \x27[<67;14;24M \x27[<65;14;24M <- one click turning scroll one way \x27[<64;14;24M <- one click turning scroll the other way way It does not matter if 1000 or 1003 mode is used, or if 1006 is on or off (the above is 1003 + 1006). I expect either a press followed by a release (M then m in sgr (1006) mode, or just a single press as for buttons 4 and 5 (though I think I would prefer press and release). I have attached the test program used to produce the above. -- System Information: Debian Release: bullseye/sid APT prefers unstable APT policy: (500, 'unstable') Architecture: amd64 (x86_64) Foreign Architectures: i386 Kernel: Linux 5.4.0-4-amd64 (SMP w/8 CPU cores) Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_OOT_MODULE, TAINT_UNSIGNED_MODULE Locale: LANG=en_AU.UTF-8, LC_CTYPE=en_AU.UTF-8 (charmap=UTF-8) (ignored: LC_ALL set to en_US.utf8), LANGUAGE=en_AU:en (charmap=UTF-8) (ignored: LC_ALL set to en_US.utf8) Shell: /bin/sh linked to /bin/dash Init: systemd (via /run/systemd/system) LSM: AppArmor: enabled Versions of packages xterm depends on: ii libc6 2.29-10 ii libfontconfig1 2.13.1-2+b1 ii libfreetype6 2.10.1-2 ii libice6 2:1.0.9-2 ii libtinfo6 6.1+20191019-1 ii libutempter0 1.1.6-4 ii libx11-6 2:1.6.8-1 ii libxaw7 2:1.0.13-1+b2 ii libxext6 2:1.3.3-1+b2 ii libxft2 2.3.2-2 ii libxinerama1 2:1.1.4-2 ii libxmu6 2:1.1.2-2+b3 ii libxpm4 1:3.5.12-1 ii libxt6 1:1.1.5-1+b3 ii xbitmaps 1.1.1-2 Versions of packages xterm recommends: ii x11-utils 7.7+4 Versions of packages xterm suggests: pn xfonts-cyrillic <none> -- no debconf information
#include <termios.h> #include <unistd.h> #include <stdio.h> #include <string.h> #define MOUSE_MOVES_ON "\033[?1003h" #define MOUSE_MOVES_OFF "\033[?1003l" #define SGR_ON "\033[?1006h" #define SGR_OFF "\033[?1006l" #define UTF8_ON "\033[?1005h" #define UTF8_OFF "\033[?1005h" struct termios save_termios; void tty_raw(int fd) { struct termios termios; tcgetattr (fd, &save_termios); termios = save_termios; termios.c_lflag &= ~(ECHO | ICANON | ISIG); termios.c_cc[VMIN] = 1; termios.c_cc[VTIME] = 0; tcsetattr (fd, TCSAFLUSH, &termios); } void tty_reset(int fd) { tcsetattr (fd, TCSAFLUSH, &save_termios); } typedef enum { st_idle, st_escape, st_csi, st_mouse1, st_mouse2, st_mouse3, } state_t; state_t state; unsigned char mouse_chars[3]; int mouse_buttons; void parse_mouse (void) { int x = mouse_chars[1] - '!'; // want 0-based coords int y = mouse_chars[2] - '!'; // want 0-based coords int c = mouse_chars[0] - ' '; int b = c & 3; int m = c & 0x20; // motion + lowest button int e = c & 0xc0; // extended buttons int shift = (c >> 2) & 7; // shift state if (m) { b = -1; } else { // xterm doesn't send release events for buttons 4-7 mouse_buttons &= ~(0x7c); if (!e && b == 3) { mouse_buttons = 0; // we don't know which one :P b = -1; } else { if (e) { b += 4 * (e >> 6) - 1; } mouse_buttons |= 1 << b; } } printf ("%3d %3d %02x %03x %02x %2d\r", x, y, c, mouse_buttons, e, b + 1); fflush (stdout); } void process_char (int ch) { if (ch == 0x1b) { // always reset if escape is seen, may be a desync state = st_escape; } else { switch (state) { case st_idle: break; case st_escape: if (ch == '[') { state = st_csi; } break; case st_csi: if (ch == 'M') { state = st_mouse1; memset (mouse_chars, 0, sizeof (mouse_chars)); } break; case st_mouse1: mouse_chars[0] = ch; state = st_mouse2; break; case st_mouse2: mouse_chars[1] = ch; state = st_mouse3; break; case st_mouse3: mouse_chars[2] = ch; parse_mouse(); state = st_idle; break; } //printf("state %d\n", state); } } void print_bytes (const char *str, int len) { char c; if (!str) return; while (len-- > 0 && (c = *str++)) { switch (c) { case '\a': fputs ("\\a", stdout); break; case '\b': fputs ("\\b", stdout); break; case '\f': fputs ("\\f", stdout); break; case '\n': fputs ("\\n", stdout); break; case '\r': fputs ("\\r", stdout); break; case '\t': fputs ("\\t", stdout); break; case '\\': fputs ("\\\\", stdout); break; case '\'': fputs ("\\'", stdout); break; case '\"': fputs ("\\\"", stdout); break; default: if (c >= 127 || c < 32) printf ("\\x%02d", (unsigned char) c); else putc (c, stdout); break; } } puts(""); fflush (stdout); } int main (void) { char buf[256]; int len; tty_raw (0); write(1, MOUSE_MOVES_ON, sizeof (MOUSE_MOVES_ON) - 1); write(1, SGR_ON, sizeof (SGR_ON) - 1); while (1) { len = read(0, buf, sizeof (buf)); //for (int i = 0; i < len; i++) { // process_char (buf[i]); //} print_bytes (buf, len); if (buf[0] == 'x') { break; } } write(1, SGR_OFF, sizeof (SGR_OFF) - 1); write(1, MOUSE_MOVES_OFF, sizeof (MOUSE_MOVES_OFF) - 1); tty_reset (0); }