buzz / volctl

Per-application volume control and OSD for Linux desktops.
https://buzz.github.io/volctl/
GNU General Public License v2.0
131 stars 19 forks source link

feature request: wayland support #39

Open RiedleroD opened 3 years ago

RiedleroD commented 3 years ago

I just switched from xorg (qtile) to wayland (sway) and volctl doesn't work. That's kinda sad because I loved it in my previous configuration.

Anyway, currently the icon just doesn't show up. When the volume is changed, then a new floating window pops up with 0% volume (in the exact center of the screen) and volctl prints this to console:

(volctl:260504): Gtk-CRITICAL **: 10:07:48.702: gtk_widget_get_scale_factor: assertion 'GTK_IS_WIDGET (widget)' failed
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/volctl/app.py", line 141, in update_values
    self._create_osd()
  File "/usr/lib/python3.9/site-packages/volctl/app.py", line 94, in _create_osd
    self._osd = VolumeOverlay(self)
  File "/usr/lib/python3.9/site-packages/volctl/osd.py", line 63, in __init__
    self._make_window_clicktrough()
  File "/usr/lib/python3.9/site-packages/volctl/osd.py", line 177, in _make_window_clicktrough
    win = X.XID(self.get_window().get_xid())
AttributeError: 'GdkWaylandWindow' object has no attribute 'get_xid'

The windows stack on top of each other, have window borders and don't close automatically.

I hope it's possible to implement basic wayland support.

buzz commented 3 years ago

Unfortunately Wayland is currently not supported. I don't know how much work it would be to support Wayland natively or through XWayland.

PRs welcome!

Narrat commented 3 years ago

As GTK supports Wayland directly it shouldn't be that much of a problem (I had my fingers crossed as of writing this sentence :D) First approach could be to reduce/limit the feature set if wayland is detected. To make it at least run natively and then check for ways of readding said features (which could be tricky)

notfound4 commented 3 years ago

Sorry for the response delay. The main problem here is GTK.StatusIcon. From what I understand GTK is deprecating StatusIcon. It uses Xembed for the tray icon, an old standart that will be dropped with Wayland. SNI will be the replacement To port volctl to wayland, you'll need to find an alternative, and a few are available :

buzz commented 3 years ago

AppIndicator3 is the "modern" replacement for StatusIcon. As you mentioned, it is limited in terms of functionality and flexibility. This has been intentional. It was an attempt to improve the inconsistent user experience of tray icons.

volctl will not use QT (which would basically be a complete rewrite).

The probably best approach would be to fall back to AppIndicator3 if StatusIcon is not available and implement the functionality within the limits of AppIndicator3. I will most certainly not find the time to look into this in the foreseeable future.

PR welcome.

notfound4 commented 3 years ago

I tried to add an option to use AppIndicator3 instead of StatusIcon, but it proved really difficult, with a lot of hacks in order to try to recover some functionalities (hooking into the "about to show" signal of the menu). In the end, I spend a lot of time, it was painfull and I have no results. I have really limited spare time, and I'd rather like to spend it on something interesting and/or fun.

I really like volctl and I found no alternatives working in Wayland. I think I'll fork the project and reimplement the GTK part into QT.

I'll probably go back to it when an alternative to AppIndicator3 is available and propose a PR.

I am sorry I cannot do more. Thanks for all your work !

buzz commented 3 years ago

@notfound4

I did some more research on the topic.

When I started the project years ago I chose Gtk.StatusIcon for the simplicity and flexibility (arbitrary mouse events). AppIndicator turned out to be overly complicated and I had difficulties showing the sliders window beneath the status icon. Gtk.StatusIcon just passed the window coordinates in the callback.

QSystemTrayIcon can do both SNI and XEmbed BTW.

From reading the specs, SNI should be sufficient to cover the use case of volctl: Activate (Left click), SecondaryActivate (Middle click) and ContextMenu (Right click), as well as scroll events. AppIndicator uses SNI, but in a way that you can't listen for left click/right click, both will just activate the status icon context menu. The menu can only hold ordinary Gtk.MenuItems (no arbitrary widgets) as it needs to be serialized over D-Bus. IMO this makes AppIndicator a no-go for volctl as you would need to do 2 clicks to open the sliders window. And until now I didn't find a solution to get the tray icon location on the screen.

But there are two other options I found:

So, currently I tend to statusnotifier. Or a better solution is turning up.

buzz commented 3 years ago

I implemented support for StatusNotifier(Homepage). The code will use this implementation if available and falls back to Gtk.StatusIcon.

This should fix the status icon in Wayland.

@RiedleroD @notfound4 Could give the current master a try and see if it works for you?

You need to compile statusnotifier yourself. Don't forget to add the --enable-introspection flag to enable GObject introspection, so the lib can be used from Python.

buzz commented 3 years ago

Wayland support is still blocked.

It's not possible by design to position a client window on the screen on Wayland. But that is currently needed for the slider window to appear beneath the status icon.

I don't know what would be a good approach on Wayland to handle this case. It might be it's just not possible to create a custom pop-up (other than a regular menu) beneath a status icon. Further research is needed.

RiedleroD commented 3 years ago

is it possible to create a hidden maximized parent window & position the popup relative to it? Not sure, but I think the java UI creates menu popups as child windows, so that should be possible.

Also, I tried to build & install StatusNotifier, but failed. volctl just doesn't do anything on startup, so I assume that I failed to install it properly.

buzz commented 3 years ago

is it possible to create a hidden maximized parent window & position the popup relative to it? Not sure, but I think the java UI creates menu popups as child windows, so that should be possible.

Yes, in theory. It would be a terrible hack. But then, what would be achieved? You could still not position the sliders window in relation to the status icon as a Wayland surface has no means to access any other surfaces.

So, one should go over to the Wayland people and ask what to do in this particular case. Probably they would tell you that this case is already covered by the SNI context menu. As we know, SNI context menus can't show arbitrary widgets as it needs to serialize the menu over D-BUS. So, then you are back to a spec which does not account for this application behavior. You would end up having a simple context menu, with a topmost option "Show mixer" or similar. This would then bring up the sliders window (which would be positioned somewhere on your screen, depending on the decision of your Wayland compositor). Seems to defy the whole purpose of volctl IMO as the main benefit is to conveniently access the volume slider with just one click.

It should be possible to implement this by extending the Wayland protocol. This has been done in kwayland. But I suppose such an extension would need to be supported by the Wayland compositor.

There might be other projects with the same problem. Maybe one of them has found a good solution to this problem?

RiedleroD commented 3 years ago

I just found this by accident. I believe tooltips in gtk use this, which would mean that we can use this to position the popup just above the cursor. I don't have the time rn to find a better documentation, but it's at least another lead.

buzz commented 3 years ago

Unfortunately not. That's what I was speaking of. You can only position in relation to a parent surface, which we don't have as the status icon is rendered by the StatusNotifierHost. That's the dilemma.

A positioner object is used to position surfaces relative to some parent surface.
[source]

bb010g commented 2 years ago

I feel like wlr-layer-shell-unstable-v1 is close to what you want, allowing you to position relative to an anchor, but you can only anchor to an edge of a layer surface, so unless you're told about a tray layer surface, that's not gonna do it. (This is different from xdg_surface v3 of xdg-shell, which I believe is what you were talking about before.) (A layer might also be overkill.)

bb010g commented 2 years ago

In the realm of slightly evil bodges, if you can detect where the pointer is as soon as possible (the mouse shouldn't have moved far after clicking), the window could be spawned at that position. This works as long as the tray item wasn't opened via a non-mouse method, but this would still be more functionality under Wayland than before.

Consolatis commented 5 months ago

I think the best solution would be if the SNI spec (or a follow up protocol) would allow for at least some basic things like label + slider (and label + progress bar) in the context menu.

My hope was that the Ayatana project would push for that but that one seems pretty dead sadly.

buzz commented 5 months ago

I think the best solution would be if the SNI spec (or a follow up protocol) would allow for at least some basic things like label + slider (and label + progress bar) in the context menu.

Yep, I'm aware of the thread at xdg-specs and was pretty excited about it initially. Unfortunately it seems to have evolved into a stalled discussion, adding yet another layer to the never-ending story of broken tray specs in Linux land. :cry:

My hope was that the Ayatana project would push for that but that one seems pretty dead sadly.

Yeah, no more development there, even that it's still widely used...