From b87c56799ab6f6d651e1dc6c712a625545a4ad5f Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Sun, 2 Feb 2025 13:17:51 +0300 Subject: implemented mouse support via special ^KMOUSE and with negative keys * You need to set 0,64ED to enable mouse processing in Curses. It is always enabled in Gtk as it should never make the experience worse. sample.teco_ini enables mouse support, since this should be the new default. `sciteco --no-profile` won't have it enabled, though. * On curses, it requires the ncurses mouse protocol version 2, which will also be supported by PDCurses. * Similar to the Curses API, a special key macro ^KMOUSE is inserted if any of the supported mouse events has been detected. * You can then use -EJ to get the type of mouse event, which can be used with a computed goto in the command-line editing macro. Alternatively, this could have been solved with separate ^KMOUSE:PRESSED, ^KMOUSE:RELEASED etc. pseudo-key macros. * The default ^KMOUSE implementation in fnkeys.tes supports the following: * Left click: Edit command line to jump to position. * Ctrl+left click: Jump to beginning of line. * Right click: Insert position or position range (when dragging). * Double right click: insert range for word under cursor * Ctrl+right click: Insert beginning of line * Scroll wheel: scrolls (faster with shift) * Ctrl+scroll wheel: zoom (GTK-only) * Currently, there is no visual feedback when "selecting" ranges via right-click+drag. This would be tricky to do and most terminal emulators do not appear to support continuous mouse updates. --- src/interface-curses/interface.c | 102 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'src/interface-curses/interface.c') diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c index c8daab9..fe2b1bb 100644 --- a/src/interface-curses/interface.c +++ b/src/interface-curses/interface.c @@ -53,6 +53,7 @@ * Some macros in term.h interfere with our code. */ #undef lines +#undef buttons #endif #include @@ -687,6 +688,15 @@ teco_interface_init_interactive(GError **error) set_escdelay(25); #endif + /* + * Disables click-detection. + * If we'd want to discern PRESSED and CLICKED events, + * we'd have to emulate the same feature on GTK. + */ +#if NCURSES_MOUSE_VERSION >= 2 + mouseinterval(0); +#endif + /* * We always have a CTRL handler on Windows, but doing it * here again, ensures that we have a higher precedence @@ -1584,9 +1594,92 @@ teco_interface_refresh(void) doupdate(); } +#if NCURSES_MOUSE_VERSION >= 2 + +#define BUTTON_NUM(X) \ + (BUTTON##X##_PRESSED | BUTTON##X##_RELEASED | \ + BUTTON##X##_CLICKED | BUTTON##X##_DOUBLE_CLICKED | BUTTON##X##_TRIPLE_CLICKED) +#define BUTTON_EVENT(X) \ + (BUTTON1_##X | BUTTON2_##X | BUTTON3_##X | BUTTON4_##X | BUTTON5_##X) + +static gboolean +teco_interface_getmouse(void) +{ + MEVENT event; + WINDOW *current = teco_view_get_window(teco_interface_current_view); + + /* + * Return mouse coordinates relative to the view. + * They will be in characters, but that's what SCI_POSITIONFROMPOINT + * expects on Scinterm anyway. + */ + if (getmouse(&event) != OK || + !wmouse_trafo(current, &event.y, &event.x, FALSE)) + /* no event inside of current view */ + return FALSE; + + /* + * NOTE: There will only be one of the button bits + * set in bstate, so we don't loose information translating + * them to enums. + * + * At least on ncurses, we don't always get a RELEASED event. + * It instead sends only REPORT_MOUSE_POSITION, + * so make sure not to overwrite teco_mouse.button in this case. + */ + if (event.bstate & BUTTON_NUM(4)) + /* scroll up - there will be no RELEASED event */ + teco_mouse.type = TECO_MOUSE_SCROLLUP; + else if (event.bstate & BUTTON_NUM(5)) + /* scroll down - there will be no RELEASED event */ + teco_mouse.type = TECO_MOUSE_SCROLLDOWN; + else if (event.bstate & BUTTON_EVENT(RELEASED)) + teco_mouse.type = TECO_MOUSE_RELEASED; + else if (event.bstate & BUTTON_EVENT(PRESSED)) + teco_mouse.type = TECO_MOUSE_PRESSED; + else + /* can also be REPORT_MOUSE_POSITION */ + teco_mouse.type = TECO_MOUSE_RELEASED; + + teco_mouse.x = event.x; + teco_mouse.y = event.y; + + if (event.bstate & BUTTON_NUM(1)) + teco_mouse.button = 1; + else if (event.bstate & BUTTON_NUM(2)) + teco_mouse.button = 2; + else if (event.bstate & BUTTON_NUM(3)) + teco_mouse.button = 3; + else if (!(event.bstate & REPORT_MOUSE_POSITION)) + teco_mouse.button = -1; + + teco_mouse.mods = 0; + if (event.bstate & BUTTON_SHIFT) + teco_mouse.mods |= TECO_MOUSE_SHIFT; + if (event.bstate & BUTTON_CTRL) + teco_mouse.mods |= TECO_MOUSE_CTRL; + if (event.bstate & BUTTON_ALT) + teco_mouse.mods |= TECO_MOUSE_ALT; + + return TRUE; +} + +#endif /* NCURSES_MOUSE_VERSION >= 2 */ + static gint teco_interface_blocking_getch(void) { +#if NCURSES_MOUSE_VERSION >= 2 + /* + * FIXME: REPORT_MOUSE_POSITION is necessary at least on + * ncurses, so that BUTTONX_RELEASED events are reported. + * It does NOT report every cursor movement, though. + * What does PDCurses do? + */ + mousemask(teco_ed & TECO_ED_MOUSEKEY + ? ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION : 0, NULL); +#endif + /* no special handling */ raw(); nodelay(teco_interface.input_pad, FALSE); @@ -1697,6 +1790,15 @@ teco_interface_event_loop_iter(void) #undef FNS #undef FN +#if NCURSES_MOUSE_VERSION >= 2 + case KEY_MOUSE: + /* ANY of the mouse events */ + if (teco_interface_getmouse() && + !teco_cmdline_keymacro("MOUSE", -1, error)) + return; + break; +#endif + /* * Control keys and keys with printable representation */ -- cgit v1.2.3