Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use xdg-desktop-portal for Image.grab() on Linux #6392

Open
JakobDev opened this issue Jun 22, 2022 · 11 comments
Open

Use xdg-desktop-portal for Image.grab() on Linux #6392

JakobDev opened this issue Jun 22, 2022 · 11 comments

Comments

@JakobDev
Copy link

JakobDev commented Jun 22, 2022

Making a Screenshot on Linux is little bit hacky. Especially on Wayland. @radarhere tried to solve the problem in #6312 by using gnome-screenshot, but this is not a good way. It works only on Systems with Gnome. Of course, you can also add other Screenshot tools but it is far more hacky and it will not work in Sandboxed Environments. Luckily there is a Better Way: There is a official D-Bus API for taking Screenshots. It works in every Desktop Environment in X11 and Wayland. It works also in Sandboxed and Unsandboxed Environments. Here is a Example code that uses it:

import dbus
import dbus.mainloop.glib
from dbus.mainloop.glib import DBusGMainLoop

from gi.repository import GLib


def testresp(response, results, object_path):
    print(results["uri"])


def main():
    loop = GLib.MainLoop()
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    bus = dbus.SessionBus()
    obj = bus.get_object("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop")
    inter = dbus.Interface(obj, "org.freedesktop.portal.Screenshot")
    bus.add_signal_receiver(
        testresp,
        signal_name="Response",
        dbus_interface="org.freedesktop.portal.Request",
        bus_name="org.freedesktop.portal.Desktop",
        path_keyword="object_path")
    inter.Screenshot("", {})
    loop.run()


main()

It needs the dbus-python. This Lib might not be the best choice, since it might be tricky to install and the mainloop can cause problems.

You can also use this API with the Shell:

gdbus call --session --dest org.freedesktop.portal.Desktop --object-path /org/freedesktop/portal/desktop --method org.freedesktop.portal.Screenshot.Screenshot "" {}

This makes a Screenshot, but I haven't found a Way to get the response, so the Program don't know where the Screenshot has been saved.

This Issue is a note that this API exists. If someone reading this with a better knowledge of D-Bus, he or she can write a better code in a more sustainable lib and make a PR. I might also take a closer look at this when I have some free time.

@radarhere
Copy link
Member

When I try running your Python code, the following dialog appears every time. That doesn't seem like the automatic behaviour that we otherwise have in ImageGrab.

Screen Shot 2022-06-25 at 10 25 22 pm

@JakobDev
Copy link
Author

JakobDev commented Jun 25, 2022

This Dialog should only appear when running inside a Sandbox. Not sure, if that is a KDE issue on my side. But on the other Hand, it is the only solution for making screenshots on Wayland that is not bound to a specific Desktop, so we may need to take the Pill of the Permission Screen at least on Wayland. You may open a Issue about it here.

@radarhere
Copy link
Member

I don't think that opening an issue about it would lead to success.

flatpak/xdg-desktop-portal#649 (comment)

Bypassing the screenshot portal is unacceptable, as it defeats the point of having the portal in the first place.

If someone has a scenario where this is helpful, let us know, but I don't personally see the point of programatically taking a screenshot if I have to take a user action in the middle.

@JakobDev
Copy link
Author

JakobDev commented Jul 4, 2022

If someone has a scenario where this is helpful, let us know, but I don't personally see the point of programatically taking a screenshot if I have to take a user action in the middle.

The problem here is, that Wayland developers do not want apps to take Screenshots silently. The only way to do this, is using this Portal. It is already written in the post you quoted:

Applications should not be able to screenshot your desktop without your permission. Removing the backdoor should not be controversial.

Even the screenshot tool of Gnome is using it, but it is whitelisted, so it don't need to ask for permission. The problem with using this Gnome Screenshot is, that it only works on distros which are using Gnome. Another Issue is, that when the Gnome developers see that a lot of Programs using this tool to bypass the Portal, they will mostly like remove the cli argument.

I know this is a very dumb decision by the developers (which Wayland is full of), but for now we have to deal with it. Maybe Pipewire could provide a solution, but I couldn't find anything about it.

@Avasam
Copy link

Avasam commented Jul 27, 2022

Applications should not be able to screenshot your desktop without your permission. Removing the backdoor should not be controversial.

What about screen recording? Where I'll take multiple images per second. I agree with the idea, but this ain't it. Windows Graphics API solves it elegantly with the colored border (which the user can choose to disable). Maybe they could consider?

@JakobDev
Copy link
Author

JakobDev commented Jul 27, 2022

Screen recording works on Wayland with Pipewire. I have found this. But I don't know if this is really made for screenshots or how to do this in Python. Moreover it would only work on Systems which use Pipewire.

@radarhere
Copy link
Member

xdg-desktop-portal plans to change this behaviour somewhat.

flatpak/xdg-desktop-portal@c827417

let applications request permissions once, and be forever able to screenshot without much hassle

That could be interesting once the next version is released.

@radarhere
Copy link
Member

@JakobDev
Copy link
Author

JakobDev commented Dec 15, 2022

I would suggest using jeepney, because it is written in pure Python and will cause fewer problems. A example implementation can be found here.

@radarhere
Copy link
Member

From the look of that example, it would seem more complicated.

Could you elaborate on what you mean by "will cause fewer problems"?

@nulano
Copy link
Contributor

nulano commented Dec 18, 2023

I've just tested this code with an Ubuntu 23.04 VM (which comes with xdg-desktop-portal 1.16) and it seems to work fine - I get a prompt the first time and after that it no longer asks again even after a reboot. That does seem reasonable. I do find it odd that according to flatpak/xdg-desktop-portal@c827417, this permission seems to be granted for all "unsandboxed" applications at the same time, but that is up to the xdg-desktop-portal devs to figure out.

It needs the dbus-python. This Lib might not be the best choice, since it might be tricky to install and the mainloop can cause problems.

This library was preinstalled for the system Python. However, it does seem to have some multithreading limitations (e.g. the use of a main loop), so perhaps it is not the best way to implement this. I think this might be what @JakobDev dev was referring to.

From https://pypi.org/project/dbus-python/:

dbus-python might not be the best D-Bus binding for you to use. dbus-python does not follow the principle of “In the face of ambiguity, refuse the temptation to guess”, and can’t be changed to not do so without seriously breaking compatibility.

In addition, it uses libdbus (which has known problems with multi-threaded use) and attempts to be main-loop-agnostic (which means you have to select a suitable main loop for your application).

From https://jeepney.readthedocs.io/en/latest/:

The core of Jeepney is I/O free, and the jeepney.io package contains bindings for different event loops to handle I/O. Jeepney tries to be non-magical, so you may have to write a bit more code than with other interfaces such as dbus-python or pydbus.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants