2

As of the latest Pillow version (9.1.0), the grab() function to get a screenshot only works on X11 and not Wayland. When I disable Wayland, it works fine.

How can I get screenshot on Wayland with pure Python? Ideally the solution doesn't involve relying on other libraries.

Is this even possible for an app to get a full screenshot (including the windows of other apps) given Wayland's security model? Using scrot to get a screenshot results in a completely black image (similar to the issue here). However, gnome-screenshot works just fine so I know this is possible.

bfris
  • 5,272
  • 1
  • 20
  • 37
Al Sweigart
  • 11,566
  • 10
  • 64
  • 92
  • "Pure python" probably not worth it (you could *maybe* call SO libs via `ctypes`). [This](https://github.com/ponty/pyscreenshot) library takes the (imao) most reasonable approach by supporting a wide array of command-line utilities that may already come with the OS, and just calling them via `subprocess`. I can't speak to security / permissions details. Run as Root? – Aaron May 12 '22 at 18:37

2 Answers2

2

Although there is a Wayland protocol extension wlr-screencopy-unstable-v1 to take screenshots (by interacting directly with the Wayland compositor), its not a practical solution because many Compositors (for example GNOME/Mutter) don't implement this extension.

A better solution is to access the Screenshot functionality via dbus using the XDG portal org.freedesktop.portal.Desktop service.

This also ensures that the application runs in flatpak sandboxes.

Minimal example:

import dbus
from gi.repository import GLib
import dbus.mainloop.glib


def response_handler(response, result):
    if response == 0:
        print(f'Screenshot file: {result.get("uri")}')
    else:
        print("Failed to get screenshot")


def main():
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    bus = dbus.SessionBus()
    my_name = bus.get_connection().get_unique_name()[1:].replace(".", "_")
    response_path = f"/org/freedesktop/portal/desktop/request/{my_name}/my_token"
    bus.add_signal_receiver(
        response_handler,
        dbus_interface="org.freedesktop.portal.Request",
        path=response_path,
    )

    desktop = bus.get_object("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop")
    desktop.Screenshot("Screenshot", {"handle_token": "my_token"}, dbus_interface="org.freedesktop.portal.Screenshot")
    loop = GLib.MainLoop()
    loop.run()


if __name__ == "__main__":
    main()
Jürgen Hötzel
  • 18,997
  • 3
  • 42
  • 58
  • This causes a white flash to appear on the screen, then opens a window with "Share this screenshot with the requesting application?" that requires a human to click on the menu. Is there a way around this? – Al Sweigart Jun 04 '22 at 19:58
  • There is currently no way to disable/persist the dialog/permission. Many screenshot tool authors commented on this issue: https://github.com/flatpak/xdg-desktop-portal/issues/649#issuecomment-952953545 Interestingly, the ScreenCast portal has a corresponding `persist_mode` – Jürgen Hötzel Jun 06 '22 at 10:10
1

From what it looks like, there's no clear way of achieving this, so you might have to try a few possible avenues. This is basically a summary of all information I've gathered on this task:

  1. Try using pyscreenshot, since they claim Wayland support. t
  2. If you look at this thread, especially @surajRathi's comment, you'll see he has implemented a solution similar to the SO answer you have linked in the question, but in Python. This seems to be the closest solution to the problem statement.
  3. This comment on a Pillow Issue on GitHub also outlines the fact that there's no standard approach, so you'll have to try whatever works.
Abhinav Mathur
  • 7,791
  • 3
  • 10
  • 24