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);
}

Reply via email to