fohrloop/wakepy

Add support for Xfce

Opened this issue · 3 comments

Different methods on xfce:

org.Xfce.Session.Manager.Inhibit

  • Introduced in xfce-session/#22 (in particular, the commit 895a580d). Merged into xfce-session in commit aa586e05 (April 13th, 2023). It is not part of the xfce-4.18 branch, but it is part of the xfce-session-4.19.0 tag.
  • Since Xfce releases every odd number (..., 4.14, 4.16, 4.18, 4.20, ...) as binary releases (changelog here), the org.Xfce.Session.Manager.Inhibit will only be part of the 4.19 "development" (source) release, and then finally in the Xfce 4.20, which is probably out in late 2024 or early 2025 (ref). See also: Schedule for and Status of the Xfce 4.20 Development Cycle.
  • D-Bus path: /org/xfce/SessionManager
  • D-Bus interface: org.xfce.Session.Manager
  • Method: Inhibit, with parameters app_id (str), toplevel_xid (int), reason (str), flags (int) and return value inhibit_cookie (int)

org.freedesktop.PowerManagement.Inhibit

  • Obsolete spec, but still used: https://wakepy.readthedocs.io/stable/methods-reference.html#org-freedesktop-powermanagement
  • There's a bug in XFCE. org.freedesktop.PowerManagement.Inhibit inhibits also screensaver: https://gitlab.xfce.org/xfce/xfce4-power-manager/-/issues/65
  • This is present on Xfce 4.18 session bus. Tested and this prevents idle; it implements keep.presenting mode.
  • D-Bus name org.freedesktop.PowerManagement (on Session bus)
  • D-Bus path: /org/freedesktop/PowerManagement/Inhibit
  • D-Bus interface: org.freedesktop.PowerManagement.Inhibit
  • Method: Inhibit, with parameters application_name (str) and reason (str) and return value inhibit_cookie (int). Note that since this does not have input parameter "flags", you may not control what this inhibits, and there are two kinds of behaviors found in the wild (keep.running (=the correct one?) and keep.presenting).

org.freedesktop.login1.Manager.Inhibit

  • This is present on Xfce 4.18 system bus
  • D-Bus name: org.freedesktop.login1
  • D-Bus path:/org/freedesktop/login1
  • D-Bus interface: org.freedesktop.login1.Manager
  • Method: Inhibit, with parameters what (str) who (str) why (str) mode (str) and return value file_descriptor (File Descriptor).

org.xfce.PowerManager

  • Mentioned on xfce4-power-manager roadmap for Xfce 4.10
  • Likely to just be a proxy or alias for org.freedesktop.PowerManagement.Inhibit
  • This is present on Xfce 4.18 session bus. Tested and this prevents idle; it implements keep.presenting mode.
  • D-Bus name: org.xfce.PowerManager
  • D-Bus path: /org/freedesktop/PowerManagement/Inhibit
  • D-Bus interface: org.freedesktop.PowerManagement.Inhibit
  • Method: Inhibit, with parameters application_name (str) and reason (str) and return value inhibit_cookie (int).

Notes

I've done some testing and here are results.

org.Xfce.Session.Manager.Inhibit

  • Does not exists yet in Xfce 4.18. Should probably be the chosen method on Xfce 4.20 and above (in the future).

org.freedesktop.PowerManagement.Inhibit

  • Has only one possible "mode": keep.presenting, which prevents the idle action and keeps also the screen awake. This cannot be modified in any way, so implementing keep.presenting mode is not possible. In my understanding, the spec says that this should only prevent suspending (allow automatic screenlock+screen saver), so using this method would mean that wakepy would rely on a bug existing on Xfce. See also xfce4-power-manager/#65 and org.freedesktop.PowerManagement docs. This bug has been fixed in KDE 5.12.90.

org.xfce.PowerManager

  • Seems to be just a proxy / alias for org.freedesktop.PowerManagement.Inhibit

org.freedesktop.login1.Manager.Inhibit

  • Should be able to prevent suspend/sleep or the idle action. Checking if this is possible without root.
Test 1
  • Trying to inhibit "sleep" with org.freedesktop.login1.Manager.Inhibit. (no sudo used)
  • Result: This does not work.
Test 1 details
from jeepney import DBusAddress, new_method_call
from jeepney.io.blocking import open_dbus_connection
from jeepney.wrappers import unwrap_msg


addr = DBusAddress(
    object_path='/org/freedesktop/login1',
    bus_name='org.freedesktop.login1',
    interface='org.freedesktop.login1.Manager',
)

msg = new_method_call(
    addr,
    method='Inhibit',
    signature='ssss',
    body=('sleep', 'wakepy', 'wakelock active', 'block'),
)

connection = open_dbus_connection(bus="SYSTEM", enable_fds=True)
reply = connection.send_and_get_reply(msg, timeout=2)
resp = unwrap_msg(reply)

fd = resp[0]

Running this with -i flag to keep the file descriptor object open.

(venv) fohrloop@fedora:~/code/wakepy$ python -i foo.py 
>>> fd
<FileDescriptor (5)>

But this did not prevent automatic suspend

Test 2
  • Trying to inhibit "sleep" with org.freedesktop.login1.Manager.Inhibit with sudo.
  • This time the suspend was prevented. After logging back in (from screensaver/screenlock), I was greeted with this:

image

Code

Some pieces of code for possible use in the future

org.freedesktop.login1

class FreedesktopLogin1Inhibit(Method):
    """Method using org.freedesktop.login1.Manager.Inhibit D-Bus API

    References:
    [1]: https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.login1.html
    [2]: https://systemd.io/INHIBITOR_LOCKS/

    """
    name = "org.freedesktop.login1"
    mode_name = ModeName.KEEP_RUNNING

    login1_manager = DBusAddress(
        bus=BusType.SYSTEM,
        service="org.freedesktop.login1",
        path="/org/freedesktop/login1",
        interface="org.freedesktop.login1.Manager",
    )

    # Note: There is no "UnInhibit" method.  The method returns a file
    # descriptor. The lock is released the moment this file descriptor and all
    # its duplicates are closed.
    method_inhibit = DBusMethod(
        name="Inhibit",
        signature="ssss",
        params=("what", "who", "why", "mode"),
        output_signature="h",
        output_params=("fd",),
    ).of(login1_manager)


    supported_platforms = (PlatformType.UNIX_LIKE_FOSS,)

    def enter_mode(self) -> None:

        # The method arguments are:
        # what (str):
        #   Colon-separated list of lock types: shutdown, sleep, idle,
        #   handle-power-key, handle-suspend-key, handle-hibernate-key
        #   and handle-lid-switch. The ones interesting for wakepy are "sleep"
        #   and "idle".
        # who (str)
        #   The name of the application requesting the lock.
        # why (str):
        #   The reason for requesting the lock.
        # mode (str):
        #   The lock mode. Either "block" or "delay".
        call = DBusMethodCall(
            method=self.method_inhibit,
            args=dict(
                what="sleep",
                who="wakepy",
                why="wakelock active",
                mode="block",
            ),
        )

        retval = self.process_dbus_call(call)
        if retval is None:
            raise RuntimeError(
                "Could not get file handle from org.freedesktop.login1"
            )
        self.inhibit_cookie = retval[0]

lower-level alternative:

from jeepney import DBusAddress, new_method_call
from jeepney.io.blocking import open_dbus_connection
from jeepney.wrappers import unwrap_msg


addr = DBusAddress(
    object_path='/org/freedesktop/login1',
    bus_name='org.freedesktop.login1',
    interface='org.freedesktop.login1.Manager',
)

msg = new_method_call(
    addr,
    method='Inhibit',
    signature='ssss',
    body=('sleep', 'wakepy', 'wakelock active', 'block'),
)

connection = open_dbus_connection(bus="SYSTEM", enable_fds=True)
reply = connection.send_and_get_reply(msg, timeout=2)
resp = unwrap_msg(reply)

fd = resp[0]

org.xfce.PowerManager

class XfcePowerManagerInhibit(FreedesktopInhibitorWithCookieMethod):

    name = "org.xfce.PowerManager"
    mode_name = ModeName.KEEP_RUNNING

    service_dbus_address = DBusAddress(
        bus=BusType.SESSION,
        service="org.xfce.PowerManager",
        path="/org/freedesktop/PowerManagement/Inhibit",
        interface="org.freedesktop.PowerManagement.Inhibit",
    )

As none of the above methods are suitable for just the keep.running method (without sudo), I'm not sure what to do. I asked on the xfce forums some advice. Perhaps there's still some other way to prevent automatic suspending on Xcfe < 4.19.

One possible alternative could be to use the gtk_application_inhibit() (#404)