Asbru-cm: [gtk3] VTE right-click

Created on 11 Oct 2019  Â·  26Comments  Â·  Source: asbru-cm/asbru-cm

Currently you cannot right-click on applications inside terminals and let them handle this event, e.g. highlight files by right-clicking on Midnight Commander.

New VTE lets you selectively act on right-click:

  • forward it to the application if it's interested in mouse events;
  • bring up ÁsbrĂș's own context menu otherwise.

Also, you could unconditionally bring up ÁsbrĂș's menu e.g. on Shift + right-click.

See how it's done in gnome-terminal.

gtk3

Most helpful comment

«tada.wav»

asbru-209-vte-right-click.patch.txt

So, what I couldn't figure out how to do is:

  • Directly call VTE's original event handler, see its return value, and continue based on that.

What we can do:

  • "Give up" (return 0) in the handler, that way it's automatically continued in VTE's original event handler.

  • Emit a signal, synthesize a click event on the widget.

So here's what we can do to make this work:

Synthesize a click event from the click handler, check its return value, and later branch on this return value.

Now, this sounds like an infinite recursion. It is indeed one if not done carefully. The recursion has to be broken after 1 step. One way could perhaps be to disconnect our custom handler before emitting the nested event, and reconnect after. Antoher one, which I went for, is to track the nested level in a variable ($deep) and act differently based on this, especially making sure that if we're handling a nested event then we don't nest any further. And then catch its return value in the handler of the original event, to decide whether to pop up the menu.

In other words: Since I couldn't find the equivalent of Python's Vte.Terminal.do_button_press_event(), I replaced it with re-emitting the event on the widget, making sure that the handler of this re-emitted signal immediately returns with 0, that is, bubbles up the event to the default handler.


The check $event -> type eq 'button-press' (as opposed to '2button-press' or '3button-press', meaning double and triple clicks, _not_ the mouse button) is required so that a double click over a mouse-aware terminal app doesn't bring up ÁsbrĂș's menu too.

This should be added to the $button eq 2 case too!!! (I leave it to you guys :)) If the send slow config option is enabled, a double middle-click pastes 3 times (and a triple one pastes 5 times). This is because the quick second and third clicks generate a regular click as well as a double/triple click event.

All 26 comments

I have to agree on this one, I use the right click on my editor too, and have commented the code on Asbru to be able to use it

This was fixed with my pull request

@hanspr If I read your proposal correctly, your solution is to ignore right-click unless we do a shift + right-click ; do I read it right ?

Yes, you got it right.

The right click is used by terminal applications now as mentioned by @egmontkob.

So this way you let the event pass through. And if the user needs access to the popup menu, now it has to shift-right click to access it. That is how gnome-terminal and others are doing it now. Terminal applications are getting more powerful and they need more events to work well.

Egmont's proposal was different...

  • We forward the event to the terminal application
  • If the application does not handle it, we show the context menu
  • If the application shows its own menu, we don't show the menu
  • If user has the "shift" key pressed, we do show (unconditionally) our context menu

I would prefer that way since most of the time, application will not handle the right click...

It sounds more complicated, but if you can implemented it is fine.

It sounds more complicated

Should be fairly trivial. I even linked to the gnome-terminal snippet doing this, which is probably overcomplicated now that I think of it. Here's an even simpler version, it basically goes like:

bool handled = false;
if (no shift)
    handled = send_it_to_vte();
if (not handled)
    pop up the right-click menu;

Hi, @egmontkob

The pseudo code I understand, is implementing it in perl what escapes my grasp (have very little coding experience with gtk, and C bindings to perl). And looks like you are very familiar with gtk and the terminal. Just point me on this exercise so I begin to understand how to relate the C documentation from Gnome VTE, to perl. Or, if you have a link to a place that explains this (all links I had followed are broken or lead to other issues)

This is the reference I'm looking at

https://developer.gnome.org/vte/

more specific the current stable release

https://developer.gnome.org/vte/0.48/VteTerminal.html

The marked property, is the one I assume is needed to test the if (no handled)

imagen

The code in Ásbru is this

$$self{_GUI}{_VTE} -> signal_connect( 'button_press_event' => sub {
    my ( $widget, $event ) = @_;

    if ( $event -> button eq 2 ) {
        return 0 unless $$self{_CFG}{'environments'}{ $$self{_UUID} }{'send slow'};
        my $txt = $$self{_GUI}{_VTE} -> get_clipboard( Gtk3::Gdk::Atom::intern_static_string( 'PRIMARY' ) ) -> wait_for_text;
        $self -> _pasteToVte( $txt, $$self{_CFG}{'environments'}{ $$self{_UUID} }{'send slow'} );
        $$self{FOCUS} -> child_focus( 'GTK_DIR_TAB_FORWARD' );
return 1;
    } elsif ( $event -> button eq 3 ) {
        $self -> _vteMenu( $event );
        return 1;
    }

So, how do I reach this property (input-enabled)

  • using the $widget object
  • or $$self object, that is the terminal
if (!$$self{_GUI}{_VTE}{'input-enabled'})

I need to understand this otherwise or I'll just be tweaking the code with the little examples that are inside the same code.

input-enabled is irrelevant. I think you just need to send $event (in the button eq 3 branch) to the VTE widget in a non-VTE-specific way, i.e. how you'd forward an event to any GTK widget, and check its return value.

I can send actual code later.

Huhh, I thought it was going to be trivial. It is not.

gnome-terminal subclasses VTE into a TerminalScreen and works with that most of the time. The button_press_event handler of TerminalScreen digs up the parent's (VTE's) button_press_event handler and calls that, which returns whether VTE processed the event or not, and TerminalScreen's handler continues based on this.

roxterm uses GTK_WIDGET_CLASS(VTE_TERMINAL_GET_CLASS(widget)) to get the class's button_press_handler rather than the object's, overrides the object's one to call the class's and continues based on its return value. This is probably the simplest if we can figure out how to translate it to Perl. It's not as simple as a Vte::Terminal::button_press_event ($widget, $event) that I hoped for.

xfce4-terminal relies on the hack that if the terminal takes action on the click then it sends data to the child, and as a consequence, VTE emits the "commit" signal. It watches for this signal (whether it's emitted or not) to decide how to continue. Oh, and it invokes the class method as (*GTK_WIDGET_CLASS (terminal_widget_parent_class)->button_press_event) (widget, event), just like roxterm, so the "commit" signal seems mostly an unnecessary complication to me (it works differently on read-only mode, though).

Well, is up to @gfrenoy to decide if we invest time finding out a way to do it, or we do the simple proposal I made.

I personally didn't realize that the right click had that behaviour until you mentioned, I got very costumed very quit to use the shift key.

And at the end I always beleave that a consistent patter is always better when interacting with users.

And double patterns end up not being used or confuses when and how should be used.

Your call @gfrenoy

I give up.

Terminator and Guake (both written in Python) call do_button_press_event, apparently there are auto-generad do_* bindings in Python. I couldn't find them in Perl.

The closest I got is Vte::TerminalClass::button_press_event (or, using its base class: Gtk3::WidgetClass::button_press_event). This symbol exists at least, but when trying to call it, GObject Introspection gives the error

Unable to handle access to field 'button_press_event' for type 'void'

Maybe if someone properly subclasses Vte::Terminal and tries to access it from there then it works, I don't know.

Note that this is the button_press_event class method I'm looking for (that is, the handler as implemented in the widget), and not the signal with the same name (that is, how one would trigger such an action – as the signal can't return a value).

At this point I think we'd need help from a Perl + GTK binding expert.

Or just go with @hanspr's current and preferred solution. On one hand consistency is a great argument (and so is saving our time), on the other hand the existence of the right-click menu becomes much harder to discover.

I would suggest to first apply the simple shift right, and when we finish a first stable solution we begin growing functionality from there.

I would suggest to first apply the simple shift right

I believe it's what in the code right now. My preference would be different though : a "shift" right-click will be sent to the terminal application but without the "shift" key pressed, it will unconditionally display the ÁsbrĂș menu.

Said differently : it exists but it is very unlikely that a terminal application will handle a right click. If really it does, one should use the "shift" key to pass the event to the application. Otherwise, it would behave like today...

Would that be something doable ?

If you want a "shift + right-click" to be sent to the app as a "shift + right-click", I think it's easy: you just have to invert the brand new condition that checks for "shift". I don't know how apps that care about right-click (e.g. Midnight Commander) would handle the "shift" modifier, though, maybe it would confuse some of these apps.

If you want to strip the "shift", that is, on an actual "shift + right-click" send a simple (shiftless) "right-click" to VTE, then I believe it's just as easy or hard as my original suggestion. That is, it's damn simple in C and Python, I just cannot find the Perl equivalent (and I do hope it exists, but I'm not entirely sure).

(Don't you want to rewrite the project in Python? :wink:)

I could be doable, but this would differ completely with any other terminal implementation, it depends of what you consider a terminal is:

1.- An application of its own, (the user interacts with your terminal, and uses its services), or
2.- An application meant to access the real application (the users uses the terminal as conduit to interact with his real application)

In the first case, you design all the time giving privilege to your application

In the second case, you try to be as transparent as possible, and let the user interact with his real application.

I'm see a terminal application as the second. Actually in all terminals I disable all key binding to be able to interact freely with the real application.

@egmontkob

I have noticed that in all terminals, when the application running (example bash) does not use the mouse the terminal has the traditional caret cursor. And when you enter into an application (editor, mc, etc) the cursor changes to a pointer.

Is it possible to know this status on the vte? if the application requested mouse support? directly from the vte or indirectly from the gtk widget.

If so we could implement a solution where, when the application requests mouse control we use shift, and if the application is not requesting mouse control we allow right click directly, it could be a middle ground for the time being.

Interesting idea.

I couldn't make it work, though. Seems that VTE registers (gtk_widget_register_window) an event window (they're on the same level, with the same parent widget), and the cursor is set on this one. I found no way to externally locate this registered window.

Even if we could find it, it would be slightly problematic in certain ways. VTE makes no guarantee whatsoever about its internal organization, so it can break any time. Autodetected regexps or explicit hyperlinks (currently neither of them used by ÁsbrĂș as far as I know, but it might change over time) change the pointer shape to a hand, no matter if the app is interested in mouse or not.

«tada.wav»

asbru-209-vte-right-click.patch.txt

So, what I couldn't figure out how to do is:

  • Directly call VTE's original event handler, see its return value, and continue based on that.

What we can do:

  • "Give up" (return 0) in the handler, that way it's automatically continued in VTE's original event handler.

  • Emit a signal, synthesize a click event on the widget.

So here's what we can do to make this work:

Synthesize a click event from the click handler, check its return value, and later branch on this return value.

Now, this sounds like an infinite recursion. It is indeed one if not done carefully. The recursion has to be broken after 1 step. One way could perhaps be to disconnect our custom handler before emitting the nested event, and reconnect after. Antoher one, which I went for, is to track the nested level in a variable ($deep) and act differently based on this, especially making sure that if we're handling a nested event then we don't nest any further. And then catch its return value in the handler of the original event, to decide whether to pop up the menu.

In other words: Since I couldn't find the equivalent of Python's Vte.Terminal.do_button_press_event(), I replaced it with re-emitting the event on the widget, making sure that the handler of this re-emitted signal immediately returns with 0, that is, bubbles up the event to the default handler.


The check $event -> type eq 'button-press' (as opposed to '2button-press' or '3button-press', meaning double and triple clicks, _not_ the mouse button) is required so that a double click over a mouse-aware terminal app doesn't bring up ÁsbrĂș's menu too.

This should be added to the $button eq 2 case too!!! (I leave it to you guys :)) If the send slow config option is enabled, a double middle-click pastes 3 times (and a triple one pastes 5 times). This is because the quick second and third clicks generate a regular click as well as a double/triple click event.

It works very well.

@gfrenoy

I have already cleaned up the code for PACTerminal.pm (tabs and indentations), could you finish the pull request from PACMain.pm, so I can send the cleaned code in a different pull request.

And wait until I apply all coding standard rules to PACTerminal.pm to apply the patch? I would send it by the end of the day (well my end of the day)

I just noticed that the variable name $deep is already used elsewhere as a local one in that file. You might want to rename it to make it clearer that they are different. Or make it a member of $$self{_GUI}{_VTE}.

I'm wondering: Should the middle button also receive the same trick (send to VTE first to see if it handles it, and if not then slowly paste by ÁsbrĂș)?

Would it even work though? Maybe VTE always handles the middle button (either sends to the application, or pastes itself), and maybe then it reports the same return value for both cases. I don't know, needs further investigation. (Perhaps can be deferred until someone actually complains.)

could you finish the pull request from PACMain.pm,

Sorry, I only got a few minutes today to have a look into this ! What I saw looks really great but I'll need to spend a bit more time to make sure I don't do a big mistake.

asbru-209-vte-right-click-2.patch.txt

Updated against current master, plus minor cosmetics.

asbru-209-vte-right-click-2.patch.txt

Brilliant :+1: Just tried with Midnight Commander and it works like a charm !

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mirlipili picture mirlipili  Â·  12Comments

dakorpar picture dakorpar  Â·  9Comments

Croydon picture Croydon  Â·  3Comments

popxunga picture popxunga  Â·  12Comments

popxunga picture popxunga  Â·  5Comments