How to bind Modifier+MouseButton in GJS?

I have asked this question on the Gnome-Shell extensions mailing list, but haven’t received any answer. So I thought here might be a good place to ask this question once more. If you know a better place, I would be grateful if you could point me to it!

Question: When developing a Gnome-Shell extension, is it somehow possible to globally bind a callback to mouse events? I am interested in mouse click events while other modifiers are pressed. Like key-bindings, but with mouse buttons instead.

Background: I am currently developing a successor to Gnome-Pie (https://github.com/Schneegans/Gnome-Pie) in form of a Gnome-Shell Extension. This allows me to do things which are normally not possible with standard Wayland clients (such as grabbing the complete input, listing running applications, …). This works very well and I am quite happy with GJS and Clutter. However, one incredibly useful feature of Gnome-Pie was to bind pie menus to mouse buttons. You could either:

  1. Open pie menus when a specific modifier is hold down and you press a button or
  2. Open pie menus when pressing a mouse button only (this is useful if you have one of these mice featuring many buttons) or
  3. Open a pie menu when a mouse button was pressed for some time (e.g. 500 milliseconds). If you clicked faster, it would just act as a normal click event.

This was all implemented with X11 specific code, obviously. But from the things I tried, I still have the hope that at least 1. and 2. could be possible for Gnome-Shell extensions under Wayland. Here is what I tried:

  • Bind to events of global.stage. This event is only emitted when some ui elements of Gnome-Shell are clicked on, not client windows (why?).

    global.stage.connect('button-release-event', (actor, event) => {
      // do something
      return Clutter.EVENT_STOP;
    });
    
  • Bind to events of global.display. Someone on Stackoverflow mentioned a key-press-event on the global MetaDisplay. But it seems that this does not exist (anymore?). Is there an alternative?

  • Polling. It is possible to get pointer information at any time with global.get_pointer(); So this works somehow, but the original click event is not captured. While this could open a pie menu, there would still be a click on whatever is currently under the pointer.

    GLib.timeout_add(GLib.PRIORITY_DEFAULT, 10, () => {
      const [x, y, mods] = global.get_pointer();
      if (mods & Clutter.ModifierType.BUTTON1_MASK &&
          mods & Clutter.ModifierType.CONTROL_MASK) {
    
        // do something
      }
    
      return true;
    });
    
  • Weird combination of Main.pushModal() and Seat.create_virtual_device(). It might be possible to capture the input globally and forward the events I am not interested in with virtual devices. While this could work - I did not try this yet - it sounds veeery hacky.

Has somebody an idea whether this could be achieved?

2 Likes

First, events start at the reactive actor “closest” to the event, and only bubble up to the parent if the event wasn’t handled. That is, clicks on any actor that return Clutter.EVENT_STOP from their own handler will stop the event and it won’t reach the stage.

You could get around that by using captured-event::button, as that is emitted first on the stage and then bubbles down to the actor under the pointer, however that still won’t work for windows:

Mutter filters all events beore they are processed normally by Clutter, and events that are processed either by mutter itself (shortcuts, gestures, window drag operations etc.) or by clients (clicks inside windows, keyboard events while a window has key focus etc.) aren’t passed on to Clutter. And even if they were, the client would already have processed the event, which isn’t what you want anyway.

There never was such a signal on MetaDisplay. There is API for (un)registering keyboard shortcuts, but no equivalent for global “mouse shortcuts”.

Indeed, and also likely to break stuff left-and-right.

I’m afraid the best option isn’t quick and easy: Instead of trying to find a way to work around mutter’s event machinery, implement new API for “mouse shortcuts” and propose its inclusion in mutter. That would give you (and others) a clean way for building functionality on top.

1 Like

Thank you for the quick reply! I am afraid that you might be right … :slight_smile:

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.