diff options
-rw-r--r-- | README | 1 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | doc/sciteco.7.template | 9 | ||||
-rw-r--r-- | lib/fnkeys.tes | 68 | ||||
-rw-r--r-- | sample.teco_ini | 3 | ||||
-rw-r--r-- | src/core-commands.c | 146 | ||||
-rw-r--r-- | src/interface-curses/interface.c | 102 | ||||
-rw-r--r-- | src/interface-gtk/interface.c | 140 | ||||
-rw-r--r-- | src/interface.c | 3 | ||||
-rw-r--r-- | src/interface.h | 22 | ||||
-rw-r--r-- | src/sciteco.h | 2 |
11 files changed, 439 insertions, 61 deletions
@@ -57,6 +57,7 @@ Features by the standard library `fnkeys.tes`. In fact, all keys with printable representation and control keys can be remapped using key macros - and they can be context-sensitive as well! +* Scriptable mouse support via the key macro mechanism (see also `fnkeys.tes`) * Many TECO-11 features, like that most commands have a colon-modified form, string-building characters, exotic match characters... * Interactivity: Immediate searching (similar to search-as-you-type) and diff --git a/configure.ac b/configure.ac index 798b756..e355c24 100644 --- a/configure.ac +++ b/configure.ac @@ -335,6 +335,10 @@ case $INTERFACE in AC_DEFINE(PDC_FORCE_UTF8, 1, [PDCursesMod forces use of UTF8]) ]) + AC_CHECK_FUNC([has_mouse], [ + AC_DEFINE(PDC_NCMOUSE, 1, [PDCurses built with ncurses mouse API]) + ]) + # This is detectable at runtime on PDCursesMod using PDC_get_version(). # "Classic" PDCurses however does not allow runtime or compile-time checks for # discerning e.g. WinCon from SDL. diff --git a/doc/sciteco.7.template b/doc/sciteco.7.template index 6ca81ab..97e1fd7 100644 --- a/doc/sciteco.7.template +++ b/doc/sciteco.7.template @@ -300,6 +300,15 @@ The default action is \fBnot\fP performed when \(lq^KCLOSE\(rq has merely been masked out in the current parser state (see below). .TP +.SCITECO_TOPIC ^KMOUSE +.B ^KMOUSE +Mouse event occurred. +This may not be delivered unless bit 7 (64) is set in the +\fBED\fP flags. +You can use \fBEJ\fP with negative keys to retrieve +the event type, mouse coordinates and other information +about the last mouse event. +.TP .BI ^K x Any other key with printable representation and all control codes are looked up with a \(lq^K\(rq prefix. diff --git a/lib/fnkeys.tes b/lib/fnkeys.tes index ab78025..857c249 100644 --- a/lib/fnkeys.tes +++ b/lib/fnkeys.tes @@ -124,3 +124,71 @@ 1U[F9] @[F10]{(ESZOOMIN{-12D}} 1U[F10] + +!* + * Mouse integration. + * Might be disabled unless bit 7 (64) is enabled in ED. + * + * 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: Insertion beginning of line + * Scroll wheel: scrolls (faster with shift) + * Ctrl+scroll wheel: zoom (GTK-only) + *! +@[MOUSE]{ + -2EJESCHARPOSITIONFROMPOINTU.p + -4EJ&2"N Q.pESLINEFROMPOSITIONESPOSITIONFROMLINEU.p ' + 1,Q.pESWORDSTARTPOSITION:U.#ws + 1,Q.pESWORDENDPOSITION:U.#we + Q.p:U.p + + -EJOpressed,released,scrollup,scrolldown + !pressed! + !* left click *! + -3EJ-1"= Q.p-.M#c !* not reached *! ' + { -9D + !* right click *! + -3EJ-3"= Q.p"U I(\.p ' ' + !* middle click *! + -3EJ-2"= :Q~"U I(g~) ' ' + } + !released! + { -9D + -3EJ-3"= Q.p"U + !* right click *! + <-A-("=1;'R> \U.o + Q.o-Q.p"= + .-1"> -2A-)"= + R <-A-("=1;'R> + \-Q.p"= + !* double right-click *! + .,ZD I\.#ws,\.#we + ' + ' ' + ZJ I) + | + Q.o-Q.p"> Q.o,Q.p U.oU.p ' + !* right drag *! + .,ZD I\.o,\.p) + ' + ' ' + } + !scrollup! + -4EJ&2"= + -4EJ&1"=-1|-2',0ESLINESCROLL + | + ESZOOMIN + ' + {-9D} + !scrolldown! + -4EJ&2"= + -4EJ&1"=1|2',0ESLINESCROLL + | + ESZOOMOUT + ' + {-9D} +} +@[MOUSE]{(M[MOUSE]} +1U[MOUSE] diff --git a/sample.teco_ini b/sample.teco_ini index a9caf26..0debdcc 100644 --- a/sample.teco_ini +++ b/sample.teco_ini @@ -59,6 +59,9 @@ EMQ[$SCITECOPATH]/opener.tes !* Enable default function key macros *! EMQ[$SCITECOPATH]/fnkeys.tes +!* Comment out to disable mouse interaction on Curses *! +0,64ED + !* Uncomment if terminal supports OSC-52 clipboards *! !!0,256ED diff --git a/src/core-commands.c b/src/core-commands.c index f74b5f2..0d23adb 100644 --- a/src/core-commands.c +++ b/src/core-commands.c @@ -2221,37 +2221,50 @@ TECO_DEFINE_STATE_CASEINSENSITIVE(teco_state_escape, * Without any argument ED returns the current flags. * * Currently, the following flags are used by \*(ST: - * - 4: If enabled, prefer raw single-byte ANSI encoding - * for all new buffers and registers. - * This does not change the encoding of any existing - * buffers and any initialized default register when set via - * \fBED\fP, so you might want to launch \*(ST with \fB--8bit\fP. - * - 8: Enable/disable automatic folding of case-insensitive - * command characters during interactive key translation. - * The case of letter keys is inverted, so one or two - * character commands will typically be inserted upper-case, - * but you can still press Shift to insert lower-case letters. - * Case-insensitive Q-Register specifications are not - * case folded. - * This is thought to improve the readability of the command - * line macro. - * - 16: Enable/disable automatic translation of end of - * line sequences to and from line feed. - * Disabling this flag allows 8-bit clean loading and saving - * of files. - * - 32: Enable/Disable buffer editing hooks - * (via execution of macro in global Q-Register \(lqED\(rq) - * - 128: Enable/Disable enforcement of UNIX98 - * \(lq/bin/sh\(rq emulation for operating system command - * executions - * - 256: Enable/Disable OSC-52 clipboard support. - * Must only be enabled if the terminal emulator is configured - * properly. - * - 512: Enable/Disable Unicode icons in the Curses UI. - * This requires a capable font, like the ones provided - * by the \(lqNerd Fonts\(rq project. - * Changes to this flag in interactive mode may not become - * effective immediately. + * .IP 4: 5 + * If enabled, prefer raw single-byte ANSI encoding + * for all new buffers and registers. + * This does not change the encoding of any existing + * buffers and any initialized default register when set via + * \fBED\fP, so you might want to launch \*(ST with \fB--8bit\fP. + * .IP 8: + * Enable/disable automatic folding of case-insensitive + * command characters during interactive key translation. + * The case of letter keys is inverted, so one or two + * character commands will typically be inserted upper-case, + * but you can still press Shift to insert lower-case letters. + * Case-insensitive Q-Register specifications are not + * case folded. + * This is thought to improve the readability of the command + * line macro. + * .IP 16: + * Enable/disable automatic translation of end of + * line sequences to and from line feed. + * Disabling this flag allows 8-bit clean loading and saving + * of files. + * .IP 32: + * Enable/Disable buffer editing hooks + * (via execution of macro in global Q-Register \(lqED\(rq) + * .IP 64: + * .SCITECO_TOPIC mouse + * Enable/Disable processing and delivery of mouse events in + * the Curses UI. + * If enabled, the terminal emulator's default mouse behavior + * may be inhibited. + * .IP 128: + * Enable/Disable enforcement of UNIX98 + * \(lq/bin/sh\(rq emulation for operating system command + * executions + * .IP 256: + * Enable/Disable OSC-52 clipboard support. + * Must only be enabled if the terminal emulator is configured + * properly. + * .IP 512: + * Enable/Disable Unicode icons in the Curses UI. + * This requires a capable font, like the ones provided + * by the \(lqNerd Fonts\(rq project. + * Changes to this flag in interactive mode may not become + * effective immediately. * * The features controlled thus are discribed in other sections * of this manual. @@ -2277,9 +2290,10 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error) /*$ EJ properties * [key]EJ -> value -- Get and set system properties - * -EJ -> value * value,keyEJ * rgb,color,3EJ + * -EJ -> event + * -2EJ -> y, x * * This command may be used to get and set system * properties. @@ -2293,16 +2307,16 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error) * write-only or read-only. * * The following property keys are defined: - * .IP 0 4 + * .IP 0: 4 * The current user interface: 1 for Curses, 2 for GTK * (\fBread-only\fP) - * .IP 1 + * .IP 1: * The current numbfer of buffers: Also the numeric id * of the last buffer in the ring. This is implied if * no argument is given, so \(lqEJ\(rq returns the number * of buffers in the ring. * (\fBread-only\fP) - * .IP 2 + * .IP 2: * The current memory limit in bytes. * This limit helps to prevent dangerous out-of-memory * conditions (e.g. resulting from infinite loops) by @@ -2328,7 +2342,7 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error) * this happens you may have to clear your command-line * first. * Memory limiting is enabled by default. - * .IP 3 + * .IP 3: * This \fBwrite-only\fP property allows redefining the * first 16 entries of the terminal color palette \(em a * feature required by some @@ -2369,17 +2383,60 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error) * on exit the author is aware of is \fBxterm\fP(1) and * the Linux console driver. * You have been warned. Good luck. - * .IP 4 + * .IP 4: * The column after the last horizontal movement. * This is only used by \fBfnkeys.tes\fP and is similar to the Scintilla-internal * setting \fBSCI_CHOOSECARETX\fP. * Unless most other settings, this is on purpose not restored on rubout, * so it "survives" command line replacements. + * . + * .IP -1: + * Type of the last mouse event (\fBread-only\fP). + * One of the following values will be returned: + * .RS + * . IP 1: 4 + * Some button has been pressed + * . IP 2: + * Some button has been released + * . IP 3: + * Scroll up + * . IP 4: + * Scroll down + * .RE + * .IP -2: + * Coordinates of the mouse pointer relative to the Scintilla view + * at the time of the last mouse event. + * This is in pixels or cells depending on the UI. + * First the Y coordinate is pushed, followed by the X coordinate, + * allowing you to pass them on directly to the \fBSCI_POSITIONFROMPOINT\fP + * and similar Scintilla messages using the \fBES\fP command. + * (\fBread-only\fP) + * .IP -3: + * Number of the mouse button involved in the last mouse event, beginning with 1. + * Can be -1 if the button cannot be determined or is irrelevant. + * (\fBread-only\fP) + * .IP -4: + * Bit mask describing the key modifiers at the time of the last + * mouse event (\fBread-only\fP). + * Currently, the following flags are used: + * .RS + * . IP 1: 4 + * Shift key + * . IP 2: + * Control key (CTRL) + * . IP 4: + * Alt key + * .RE */ static void teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error) { enum { + EJ_MOUSE_MODS = -4, + EJ_MOUSE_BUTTON, + EJ_MOUSE_COORD, + EJ_MOUSE_TYPE, + EJ_USER_INTERFACE = 0, EJ_BUFFERS, EJ_MEMORY_LIMIT, @@ -2442,6 +2499,21 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error) * Get property */ switch (property) { + case EJ_MOUSE_TYPE: + teco_expressions_push(teco_mouse.type); + break; + case EJ_MOUSE_COORD: + /* can be passed down to @ES/POSITIONFROMPOINT// */ + teco_expressions_push(teco_mouse.y); + teco_expressions_push(teco_mouse.x); + break; + case EJ_MOUSE_BUTTON: + teco_expressions_push(teco_mouse.button); + break; + case EJ_MOUSE_MODS: + teco_expressions_push(teco_mouse.mods); + break; + case EJ_USER_INTERFACE: /* * FIXME: Replace INTERFACE_* macros with 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 <Scintilla.h> @@ -688,6 +689,15 @@ teco_interface_init_interactive(GError **error) #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 * than the one installed by PDCurses. @@ -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 <CTRL/C> 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 */ diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index 1a990e8..4619b75 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -68,8 +68,8 @@ static void teco_interface_cmdline_commit_cb(GtkIMContext *context, gchar *str, static void teco_interface_size_allocate_cb(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data); -static gboolean teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, - gpointer user_data); +static gboolean teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, + gpointer user_data); static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer user_data); static gboolean teco_interface_sigterm_handler(gpointer user_data) G_GNUC_UNUSED; @@ -133,16 +133,12 @@ teco_view_new(void) * This disables mouse and key events on this view. * For some strange reason, masking events on * the event box does NOT work. - * - * NOTE: Scroll events are still allowed - scrolling - * is currently not under direct control of SciTECO - * (i.e. it is OK the side effects of scrolling are not - * tracked). */ gtk_widget_set_can_focus(GTK_WIDGET(sci), FALSE); gint events = gtk_widget_get_events(GTK_WIDGET(sci)); - events &= ~(GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); - events &= ~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); + events &= ~(GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_SCROLL_MASK | + GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); gtk_widget_set_events(GTK_WIDGET(sci), events); g_signal_connect(sci, SCINTILLA_NOTIFY, @@ -240,7 +236,7 @@ teco_interface_init(void) G_CALLBACK(teco_interface_window_delete_cb), NULL); g_signal_connect(teco_interface.window, "key-press-event", - G_CALLBACK(teco_interface_key_pressed_cb), NULL); + G_CALLBACK(teco_interface_input_cb), NULL); GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); @@ -316,6 +312,18 @@ teco_interface_init(void) g_signal_connect(teco_interface.event_box_widget, "size-allocate", G_CALLBACK(teco_interface_size_allocate_cb), NULL); + gint events = gtk_widget_get_events(teco_interface.event_box_widget); + events |= GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_SCROLL_MASK; + gtk_widget_set_events(teco_interface.event_box_widget, events); + + g_signal_connect(teco_interface.event_box_widget, "button-press-event", + G_CALLBACK(teco_interface_input_cb), NULL); + g_signal_connect(teco_interface.event_box_widget, "button-release-event", + G_CALLBACK(teco_interface_input_cb), NULL); + g_signal_connect(teco_interface.event_box_widget, "scroll-event", + G_CALLBACK(teco_interface_input_cb), NULL); + teco_interface.message_bar_widget = gtk_info_bar_new(); gtk_widget_set_name(teco_interface.message_bar_widget, "sciteco-message-bar"); GtkWidget *message_bar_content = @@ -977,7 +985,7 @@ teco_interface_get_ansi_key(GdkEventKey *event) static gboolean teco_interface_handle_key_press(GdkEventKey *event, GError **error) { - const teco_view_t *last_view = teco_interface_current_view; + g_assert(event->type == GDK_KEY_PRESS); switch (event->keyval) { case GDK_KEY_Escape: @@ -1105,10 +1113,77 @@ teco_interface_handle_key_press(GdkEventKey *event, GError **error) gtk_im_context_filter_keypress(teco_interface.input_method, event); } - teco_interface_refresh(teco_interface_current_view != last_view); return TRUE; } +static gboolean +teco_interface_handle_mouse_button(GdkEventButton *event, GError **error) +{ + switch (event->type) { + case GDK_BUTTON_PRESS: + teco_mouse.type = TECO_MOUSE_PRESSED; + break; + case GDK_BUTTON_RELEASE: + default: + teco_mouse.type = TECO_MOUSE_RELEASED; + break; + } + + teco_mouse.x = event->x; + teco_mouse.y = event->y; + teco_mouse.button = event->button; + + teco_mouse.mods = 0; + if (event->state & GDK_SHIFT_MASK) + teco_mouse.mods |= TECO_MOUSE_SHIFT; + if (event->state & GDK_CONTROL_MASK) + teco_mouse.mods |= TECO_MOUSE_CTRL; + /* + * NOTE: GTK returns MOD1 *without* SHIFT for ALT. + */ + if ((event->state & (GDK_MOD1_MASK | GDK_SHIFT_MASK)) == GDK_MOD1_MASK) + teco_mouse.mods |= TECO_MOUSE_ALT; + + return teco_cmdline_keymacro("MOUSE", -1, error); +} + +static gboolean +teco_interface_handle_scroll(GdkEventScroll *event, GError **error) +{ + g_assert(event->type == GDK_SCROLL); + + /* + * FIXME: Do we have to support GDK_SCROLL_SMOOTH? + */ + switch (event->direction) { + case GDK_SCROLL_UP: + teco_mouse.type = TECO_MOUSE_SCROLLUP; + break; + case GDK_SCROLL_DOWN: + teco_mouse.type = TECO_MOUSE_SCROLLDOWN; + break; + default: + return TRUE; + } + + teco_mouse.x = event->x; + teco_mouse.y = event->y; + teco_mouse.button = -1; + + teco_mouse.mods = 0; + if (event->state & GDK_SHIFT_MASK) + teco_mouse.mods |= TECO_MOUSE_SHIFT; + if (event->state & GDK_CONTROL_MASK) + teco_mouse.mods |= TECO_MOUSE_CTRL; + /* + * NOTE: GTK returns MOD1 *without* SHIFT for ALT. + */ + if ((event->state & (GDK_MOD1_MASK | GDK_SHIFT_MASK)) == GDK_MOD1_MASK) + teco_mouse.mods |= TECO_MOUSE_ALT; + + return teco_cmdline_keymacro("MOUSE", -1, error); +} + gboolean teco_interface_event_loop(GError **error) { @@ -1274,15 +1349,16 @@ teco_interface_size_allocate_cb(GtkWidget *widget, } static gboolean -teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data) +teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) { static gboolean recursed = FALSE; g_autoptr(GError) error = NULL; #ifdef DEBUG - g_printf("KEY \"%s\" (%d) SHIFT=%d CNTRL=%d\n", - event->string, *event->string, - event->state & GDK_SHIFT_MASK, event->state & GDK_CONTROL_MASK); + if (event->type == GDK_KEY_PRESS) + g_printf("KEY \"%s\" (%d) SHIFT=%d CNTRL=%d\n", + event->key.string, *event->key.string, + event->key.state & GDK_SHIFT_MASK, event->key.state & GDK_CONTROL_MASK); #endif if (recursed) { @@ -1295,8 +1371,9 @@ teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer us * during execution, but the current implementation is * probably easier. */ - if (event->state & GDK_CONTROL_MASK && - gdk_keyval_to_upper(event->keyval) == GDK_KEY_C) + if (event->type == GDK_KEY_PRESS && + event->key.state & GDK_CONTROL_MASK && + gdk_keyval_to_upper(event->key.keyval) == GDK_KEY_C) /* * Handle asynchronous interruptions if CTRL+C is pressed. * If the execution thread is currently blocking, @@ -1305,7 +1382,7 @@ teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer us teco_interrupted = TRUE; else g_queue_push_tail(teco_interface.event_queue, - gdk_event_copy((GdkEvent *)event)); + gdk_event_copy(event)); return TRUE; } @@ -1314,7 +1391,7 @@ teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer us teco_memory_start_limiting(); - g_queue_push_tail(teco_interface.event_queue, gdk_event_copy((GdkEvent *)event)); + g_queue_push_tail(teco_interface.event_queue, gdk_event_copy(event)); GdkWindow *top_window = gdk_window_get_toplevel(gtk_widget_get_window(teco_interface.window)); @@ -1333,11 +1410,28 @@ teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer us * the Curses UI. */ gdk_window_freeze_updates(top_window); + const teco_view_t *last_view = teco_interface_current_view; teco_interrupted = FALSE; - teco_interface_handle_key_press(&event->key, &error); + switch (event->type) { + case GDK_KEY_PRESS: + teco_interface_handle_key_press(&event->key, &error); + break; + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + teco_interface_handle_mouse_button(&event->button, &error); + break; + case GDK_SCROLL: + teco_interface_handle_scroll(&event->scroll, &error); + break; + default: + g_assert_not_reached(); + } teco_interrupted = FALSE; + teco_interface_refresh(teco_interface_current_view != last_view); gdk_window_thaw_updates(top_window); if (g_error_matches(error, TECO_ERROR, TECO_ERROR_QUIT)) { @@ -1374,7 +1468,7 @@ teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer close_event->key.window = gtk_widget_get_parent_window(widget); close_event->key.keyval = GDK_KEY_Close; - return teco_interface_key_pressed_cb(widget, &close_event->key, NULL); + return teco_interface_input_cb(widget, close_event, NULL); } static gboolean @@ -1386,5 +1480,5 @@ teco_interface_sigterm_handler(gpointer user_data) g_autoptr(GdkEvent) close_event = gdk_event_new(GDK_KEY_PRESS); close_event->key.keyval = GDK_KEY_Close; - return teco_interface_key_pressed_cb(teco_interface.window, &close_event->key, NULL); + return teco_interface_input_cb(teco_interface.window, close_event, NULL); } diff --git a/src/interface.c b/src/interface.c index a3c9935..9ec1bed 100644 --- a/src/interface.c +++ b/src/interface.c @@ -43,6 +43,9 @@ TECO_DEFINE_UNDO_CALL(teco_interface_ssm, unsigned int, uptr_t, sptr_t); TECO_DEFINE_UNDO_CALL(teco_interface_info_update_qreg, const teco_qreg_t *); TECO_DEFINE_UNDO_CALL(teco_interface_info_update_buffer, const teco_buffer_t *); +/** Last mouse event */ +teco_mouse_t teco_mouse = {0}; + typedef struct { teco_string_t str; gchar name[]; diff --git a/src/interface.h b/src/interface.h index 12e76fb..967f14d 100644 --- a/src/interface.h +++ b/src/interface.h @@ -141,6 +141,28 @@ void teco_interface_popup_clear(void); /** @pure */ gboolean teco_interface_is_interrupted(void); +typedef struct { + enum { + TECO_MOUSE_PRESSED = 1, + TECO_MOUSE_RELEASED, + TECO_MOUSE_SCROLLUP, + TECO_MOUSE_SCROLLDOWN + } type; + + guint x; /*< X-coordinate relative to view */ + guint y; /*< Y-coordinate relative to view */ + + gint button; /*< number of pressed mouse button or -1 */ + + enum { + TECO_MOUSE_SHIFT = (1 << 0), + TECO_MOUSE_CTRL = (1 << 1), + TECO_MOUSE_ALT = (1 << 2) + } mods; +} teco_mouse_t; + +extern teco_mouse_t teco_mouse; + /** @pure main entry point */ gboolean teco_interface_event_loop(GError **error); diff --git a/src/sciteco.h b/src/sciteco.h index 909821b..4868303 100644 --- a/src/sciteco.h +++ b/src/sciteco.h @@ -89,7 +89,7 @@ enum { TECO_ED_AUTOCASEFOLD = (1 << 3), TECO_ED_AUTOEOL = (1 << 4), TECO_ED_HOOKS = (1 << 5), - //TECO_ED_MOUSEKEY = (1 << 6), + TECO_ED_MOUSEKEY = (1 << 6), TECO_ED_SHELLEMU = (1 << 7), TECO_ED_OSC52 = (1 << 8), TECO_ED_ICONS = (1 << 9) |