jeaye/ncurses-rs

Invalid constant values with NCURSES_MOUSE_VERSION = 1

Closed this issue · 5 comments

When NCURSES_MOUSE_VERSION = 1 (happens on Ubuntu 18.04), some constants have different values than when NCURSES_MOUSE_VERSION = 2 (happens on Archlinux, and seems to be what ncurses-rs expects).

Here is the code from /usr/include/ncurses.h on ubuntu (but this part is the same on Archlinux):

/*
 * In 32 bits the version-1 scheme does not provide enough space for a 5th
 * button, unless we choose to change the ABI by omitting the reserved-events.
 */
#if NCURSES_MOUSE_VERSION > 1

#define BUTTON5_RELEASED        NCURSES_MOUSE_MASK(5, NCURSES_BUTTON_RELEASED)
#define BUTTON5_PRESSED         NCURSES_MOUSE_MASK(5, NCURSES_BUTTON_PRESSED)
#define BUTTON5_CLICKED         NCURSES_MOUSE_MASK(5, NCURSES_BUTTON_CLICKED)
#define BUTTON5_DOUBLE_CLICKED  NCURSES_MOUSE_MASK(5, NCURSES_DOUBLE_CLICKED)
#define BUTTON5_TRIPLE_CLICKED  NCURSES_MOUSE_MASK(5, NCURSES_TRIPLE_CLICKED)

#define BUTTON_CTRL             NCURSES_MOUSE_MASK(6, 0001L)
#define BUTTON_SHIFT            NCURSES_MOUSE_MASK(6, 0002L)
#define BUTTON_ALT              NCURSES_MOUSE_MASK(6, 0004L)
#define REPORT_MOUSE_POSITION   NCURSES_MOUSE_MASK(6, 0010L)

#else

#define BUTTON1_RESERVED_EVENT  NCURSES_MOUSE_MASK(1, NCURSES_RESERVED_EVENT)
#define BUTTON2_RESERVED_EVENT  NCURSES_MOUSE_MASK(2, NCURSES_RESERVED_EVENT)
#define BUTTON3_RESERVED_EVENT  NCURSES_MOUSE_MASK(3, NCURSES_RESERVED_EVENT)
#define BUTTON4_RESERVED_EVENT  NCURSES_MOUSE_MASK(4, NCURSES_RESERVED_EVENT)

#define BUTTON_CTRL             NCURSES_MOUSE_MASK(5, 0001L)
#define BUTTON_SHIFT            NCURSES_MOUSE_MASK(5, 0002L)
#define BUTTON_ALT              NCURSES_MOUSE_MASK(5, 0004L)
#define REPORT_MOUSE_POSITION   NCURSES_MOUSE_MASK(5, 0010L)

#endif

#define ALL_MOUSE_EVENTS        (REPORT_MOUSE_POSITION - 1)

In ncurses-rs, these values are defined as constants (hard-coded to the case NCURSES_MOUSE_VERSION = 2), and do not reflect this. As a result, many mouse events are broken when using ncurses-rs on Ubuntu.

A solution could be to detect this in build.rs and trigger a ncurses-mouse-v1 feature, which would be used to set the value of these constants.

Any news on this? I'm using ncurses-rs as backend for a project (tokterm) and i'm experiencing issues with
the mouse events. Only click events are received, but mouse move never reports.
Tested the same basic code on c, and it seems to work just fine so I suppose it has
to do with this issue.

Note that there's still a problem, where the old V1 protocol (the one Ubuntu uses) doesn't properly support the mouse wheel; the "wheel down" event uses the same code as "mouse drag", and therefore cannot easily be detected (this is the main reason a V2 protocol was made).

I suppose ubuntu prefers to keep compatibility with existing programs, which is why it doesn't use the new protocol. It's a shame though :(

Yes, that's true but even then, there are workarounds. For example, as you said, wheel up produces 0x00080000 which is unique, but wheel down uses the same code as mouse move: 0x08000000.
But there is a catch. Mouse move only reports when the mouse cursor change cell, but won't report if the mouse moves inside a char (cell) bound. So, you could keep track of the cell position and check if changed or not. I Know, is not perfect, is not the best, but at least is something.

After some headaches and playing with c in parallel, i made a working example with mouse move events:

extern crate ncurses;

use ncurses::{
    cbreak, clrtoeol, endwin, getmouse, initscr, insertln, keypad, mousemask, noecho, stdscr,
    wgetch, wmove, MEVENT,
};
use std::mem::zeroed;
use ncurses::constants::{ALL_MOUSE_EVENTS, KEY_MOUSE, REPORT_MOUSE_POSITION};

fn main() {
    initscr();
    cbreak();
    noecho();

    keypad(stdscr(), true);

    // Don't mask any mouse events
    let flags = ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION;

    mousemask(flags as u64, Option::None);


    println!("\x1b[?1003h"); // Makes the terminal report mouse movement events

    loop {
        let c = wgetch(stdscr());

        if c == KEY_MOUSE {
            let mut event: MEVENT = unsafe { zeroed() };

            getmouse(&mut event);

            print!("0x{:08X}: row: {} col: {}\n\r", event.bstate, event.x, event.y);
        } else {

            if c == 113 {
                break;
            }

            print!("0x{:08X}\n\r", c);
        }

        wmove(stdscr(), 0, 0);
        insertln();
        clrtoeol();
        wmove(stdscr(), 0, 0);
    }

    println!("\x1b[?1003l"); // Disable mouse movement events, as l = low
    endwin();
}

So, summarizing, sending the csi 1003 action=h and l will activate and deactivate moving events.
Hope it helps. More on this here

Just one more thing, this behavior is not restrictive to ncurses-rs. You have to manually send the escape in c as well. So it seems to come from ncurses lib. Here is a c gist

One more thing on this, just to keep track at least. While reporting moving events, the system won't report button pressed bit flags whilst moving. So, you have to manually keep a mouse state. Also, click, double click and triple click is weird. Mouse down and up events requires slow press action from the user. I need to test this on arch or other distro because all these issues are really frustrating.