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

update freedesktop api #264

Merged
merged 11 commits into from
Feb 26, 2023
Merged

Conversation

tpeacock19
Copy link
Contributor

The PowerManagement api seems to be deprecated,
https://www.freedesktop.org/wiki/Specifications/power-management-spec/

This commit updates the current pm module to use the
org.freedesktop.login1.Manager.Inhibit api. This also has the
benefit of working with swayidle.

a bit unrelated, but I'm thinking of working on a wayland
specific pm module that will rely on
https://wayland.app/protocols/idle-inhibit-unstable-v1

Would you be interested in this?

@FedeDP
Copy link
Owner

FedeDP commented Dec 10, 2022

Hi! Thanks for this PR! 🎆
I will review it right now :)

src/modules/pm.c Outdated
static void acquire_lock(void) {
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *sysbus = get_system_bus();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd avoid exposing the system bus; instead, we should rely on the already existent way to do that (async):

SYSBUS_ARG_REPLY(...)

Basically, the only line that should need a change is:

USERBUS_ARG_REPLY(pm_args, parse_bus_reply, &pm_inh_token,
                            "org.freedesktop.PowerManagement.Inhibit",
                            "/org/freedesktop/PowerManagement/Inhibit",
                            "org.freedesktop.PowerManagement.Inhibit",
                            "Inhibit");

That should become

SYSBUS_ARG_REPLY(pm_args, parse_bus_reply, &pm_inh_token, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", Inhibit);

and properly manage the reply message in the parse_bus_reply callback :)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -206,3 +206,7 @@ static int proxy_async_request(struct sd_bus_message *m, void *userdata, sd_bus_
sd_bus *get_user_bus(void) {
return userbus;
}

sd_bus *get_system_bus(void) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following what i wrote below, there is no need to expose sysbus.

@tpeacock19
Copy link
Contributor Author

I've been working on this a bit as a separate custom module so I could try and get a clear idea of what we want it to do. You can see it here.

I will push a few commits to be more inline with this and update with your comments.

@FedeDP
Copy link
Owner

FedeDP commented Dec 10, 2022

Thank you man :) and btw thanks for the effort!

@tpeacock19
Copy link
Contributor Author

Okay, I believe I have updated it so it correctly uses your macros and will report to the login1.manager when Clight is inhibited, and will also inhibit Clight when some other exterenal program reports to login1.manager.

@FedeDP
Copy link
Owner

FedeDP commented Dec 10, 2022

Hi! I will review as soon as i got some spare time, thank you!

a bit unrelated, but I'm thinking of working on a wayland
specific pm module that will rely on
https://wayland.app/protocols/idle-inhibit-unstable-v1

Session idle is already addressed by Clightd: https://github.com/FedeDP/Clightd/blob/master/src/modules/idle.c, and should work everywhere(tty, xorg and wayland). Isn't it working for you?

Moreover, i am a bit wary about adding system-specific code in Clight (like Xorg specific, wl specific, kde specific and so on).

@FedeDP
Copy link
Owner

FedeDP commented Dec 10, 2022

Just a side note: can you actually keep the bare minimum diff from master? There are multiple changes that are just linting/coding style related.
It would make reviewing job much simpler! :)
Thank you!
(your IDE is doing that automatically, i think...)

@tpeacock19
Copy link
Contributor Author

tpeacock19 commented Dec 11, 2022

Just a side note: can you actually keep the bare minimum diff from
master? There are multiple changes that are just linting/coding style

Sorry about that. I was trying to use a .clang-format to match what you
have already in the repo. But I believe I did that after my initial
commit. I just force pushed some changes, let me know if that is better.

Session idle is already addressed by Clightd:
https://github.com/FedeDP/Clightd/blob/master/src/modules/idle.c, and
should work everywhere(tty, xorg and wayland). Isn't it working for you?

Well what I observe as not "working" is this. Applications like mpv, and
others, only make use of the wayland specific idle interface when it is
available. It doesn't seem like this is picked up by idle.c. So when I
watch a video with mpv and I have Clight controlling the dimmer, it
never notices that it is running. This PR can provide a workaround if
someone is using swayidle, so perhaps it isn't really necessary.

edit: Swayidle does not report wayland specific idling events to the
freedesktop.login1 api. I was mistaken on this point.

@FedeDP
Copy link
Owner

FedeDP commented Dec 11, 2022

Applications like mpv, and others, only make use of the wayland specific idle interface when it is available.

Oh, so you are talking about org.freedesktop.ScreenSaver API then?
I mean, Clightd Idle signals when the user is afk (ie: she/he did not touch any input device) for given timeout. This is used by Clight for screen dimming and DPMS management.

But i think you are referring to the Clight Inhibited message, that is run when an external application asks the DE to inhibit its power management features, to avoid eg: dimming while a movie is being watched.
So, in the end, you are saying that on wayland systemd, eg: mpv uses the wayland specific interface/API instead of the common org.freedesktop.ScreenSaver dbus API (https://people.freedesktop.org/~hadess/idle-inhibition-spec/re01.html)?

I found this: https://www.reddit.com/r/linuxquestions/comments/t03c4q/how_do_i_lock_the_screen_with_sway/.
I think it is actually an issue with sway if it does not implement the freedesktop common API, am i wrong?

Copy link
Owner

@FedeDP FedeDP left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a first attempt at a review.
I am not getting all the ext_inhibit related code; i don't think it should belong here as it has nothing to do with power management, right?
It should perhaps being part of the inhibition logic in interface.c (or inhibit.c)?
If i am correct, i think we can work on that in another PR.

src/modules/pm.c Outdated
}

if (locks != NULL) {
DECLARE_HEAP_MSG(suspend_req, SUSPEND_REQ);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are leaking the suspend_req here, if we are not going to publish it!
Moreover, i'd just call publish_susp_req(); there.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a second thought, do we really need to suspend if any logind inhibition is ON? I mean, we should just eventually suspend:

  • when session is not active (checked below)
  • when laptop goes to sleep

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the logic behind this was to publish the message so any other subscribed modules would know. I think this was the only way I could get autocalibration to disable without calling it specifically. Is there a better way to handle this?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an inhibiton option (https://github.com/FedeDP/Clight/blob/master/Extra/clight.conf#L45) to automatically inhibit backlight calibratin on inhibition!

src/modules/pm.c Outdated
@@ -122,8 +148,14 @@ static int on_session_change(UNUSED sd_bus_message *m, UNUSED void *userdata, UN

static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata) {
int r = -EINVAL;
if (!strcmp(member, "Inhibit")) {
if (strcmp(member, "Inhibit") != 0) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmmh it was == 0 before; is this right?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moreover, note that since parse_bus_reply callback is only called when acquiring the lock, i think we can drop the old branch of the if else statement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I dont think it was ==0 although, it should have been that or != since strcmp returns an int. But your larger point stands, I'll remove this.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (!strcmp(member, "Inhibit")) {

means

if (strcmp(member, "Inhibit") == 0) {

:P
Thanks btw!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh I thought zero and null were different. Good to know. I should probably get a book on the language...

src/modules/pm.c Outdated
@@ -108,6 +110,30 @@ static int on_new_suspend(sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bu

/* Listener on logind session.Active for current session */
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update the comment here explaining what are we watching now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I'll update this once I fully understand how you want to approach this.

src/modules/pm.c Outdated Show resolved Hide resolved
src/modules/pm.c Outdated Show resolved Hide resolved
src/modules/pm.c Outdated Show resolved Hide resolved
src/modules/pm.c Outdated Show resolved Hide resolved
src/modules/pm.c Outdated Show resolved Hide resolved
@tpeacock19
Copy link
Contributor Author

Oh, so you are talking about org.freedesktop.ScreenSaver API then? I
mean, Clightd Idle signals when the user is afk (ie: she/he did not
touch any input device) for given timeout. This is used by Clight for
screen dimming and DPMS management.

Yes, you are correct. Sorry I wasn't clear about this this before.

I found this:
https://www.reddit.com/r/linuxquestions/comments/t03c4q/how_do_i_lock_the_screen_with_sway/.
I think it is actually an issue with sway if it does not implement the
freedesktop common API, am i wrong?

I don't believe that API is specifc to wayland, but to specific desktop
environments. I use River and whenever I use mpv or Firefox to watch or
play movies Clight continues to dim. I think this entire area of
discussion has been fairly contentious.

I'm not certain the dbus api should be An excerpt from mpv's Man page
will speak to this a bit more.

Disabling Screensaver
By default, mpv tries to disable the OS screensaver during playback
(only if a VO using the OS GUI API is active). --stop-screensaver=no
disables this.

  A common problem is that Linux desktop environments ignore the standard
  screensaver APIs on which mpv  relies.  In  particular,  mpv  uses  the
  Screen  Saver  extension (XSS) on X11, and the idle-inhibit protocol on
  Wayland.

  GNOME in particular still ignores the idle-inhibit  protocol,  and  has
  its  own  D-Bus interfaces for display power management, which mpv does
  not support.

Gnome and Kde are the only wayland compositors to my knowledge that
implement a the screensaver api. I think this is because they are
Desktop Environments that also have X capabilities, so they can/should
just reuse their implementations. There was an issue with this and
Firefox a few years ago and they ended up implementing the wayland specific
idle-inhibit protocol.
https://bugzilla.mozilla.org/show_bug.cgi?id=1587360. Chromium followed
suit last year with this commit https://chromium.googlesource.com/chromium/src/+/88dd539f24324451b75f142b604855eee54ac7bb.

I don't know which direction you want to go, but it would be great to be
able to use this with a wlroots based compositor (Sway, River, dwl,
Wayfire, etc.).

To your comment about the ext-inhibit in the PR. This was an attempt
to workaround this very issue. I was assuming that when using swayidle
(which is a generic wlroots idle manager, not specific to sway anymore),
that it would report any idling to the freedesktop.login1 API. So, I
thought by monitoring this dbus property we could identify any external
inhibitors that are not making use of that API. Unfortunately that does
not seem to be the case, so I'm fine with removing it.

Sadly, ScreenSaver spec does not have an Inhibited signal, thus Clight
cannot know whether an inhibition has been requested to eg: your DE
power manager. Consequently Clight has to provide the API
implementation itself. It works like this:

However, this is from the ScreenSaver wiki page. I do think the
ListInhibitors property would provide that information as it does list
anything inhibiting idle, Suspend, or shutdown. However, it still would
not provide full insight into the wayland idle-inhibit protocol as
mentioned above.

@FedeDP
Copy link
Owner

FedeDP commented Dec 11, 2022

I think we should:

  • implement the logind part only in this PR
  • open an issue to further discuss the wlroots idle protocol impl

I am still not convinced that much about adding specific code in Clight; i think that the freedesktop standard should be followed as much as we can from sway and other wayland compositors (and other apps too!).
IMHO i am using firefox on X and it correctly inhibits my screensaver (and thus Clight). Perhaps on walyand it enforces the usage of the idle protocol, thus failing.
But i am open to discuss it further :)

EDIT:
on a side note, i don't get why wayland compositors are not providing an org.freedesktop.ScreenSaver manager; perhaps i am missing some key pieces there.

@FedeDP
Copy link
Owner

FedeDP commented Dec 11, 2022

https://bugzilla.mozilla.org/show_bug.cgi?id=1587360#c3 :/ it's incredible how hard it is sometimes to have standards on linux...

@tpeacock19
Copy link
Contributor Author

Yeah it is a very convoluted, the linux power management space. I think
the ScreenSaver api good, but its also largely used by Desktop
Environments. Wayland compositors are not necessarily the same thing, so
it would make sense them to not implement the API. Wayland's
idle-inhibit is analagous to X11 Screen Saver Extension. Xorg does not
provide the org.freedesktop.ScreenSaver manager, that is provided by
Gnome, Kde, etc.

I imagine if you ran i3 on X, or any other window manager without a
desktop environment, you'd run into this same issue unless you ran
Chrome/Firefox with the workaround you highlighted out in the wiki:
XDG_CURRENT_DESKTOP=XFCE chromium.

But overall I'm happy just distilling this PR to simply the logind
idle-inhibit lock and either opening an issue or trying to create a
separate module for the wayland stuff and maybe we can have it in the
wiki. I do have one question for you.

Sending a inhibition lock to logind must have a 'Why' and it can be any
of "shutdown", "sleep", "idle", "handle-power-key", "handle-suspend-key", "handle-hibernate-key", "handle-lid-switch". It
has a 'Mode' as well that "block" or "delay". Does it make sense for the
pm module to be sending the idle inhibit lock? I started this PR there
simply because that was where the PowerManagement API used, but now
looking at the inhibit mdule I'm not so sure.

@tpeacock19 tpeacock19 force-pushed the feat/freedesktop-inhibit branch 2 times, most recently from 976d9a0 to 523ba9b Compare December 11, 2022 20:26
@tpeacock19
Copy link
Contributor Author

I've got this to just replacing org.freedesktop.PowerManagement.Inhibit
with the org.freedesktop.login1.Manager.Inhibit APIs. I can squash the
commits if you prefer a cleaner commit message.

src/modules/pm.c Outdated
@@ -59,14 +60,14 @@ static void receive(const msg_t *const msg, UNUSED const void* userdata) {
case PM_REQ: {
pm_upd *up = (pm_upd *)MSG_DATA();
if (VALIDATE_REQ(up)) {
on_pm_req(up->new);
on_pm_req(up->new, up->old);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: VALIDATE_REQ already takes care of rejecting requests if up->new == current state; we can avoid passing the additional param here, reducing the diff.
See: https://github.com/FedeDP/Clight/blob/master/src/pubsub/validations.c#L184

src/modules/pm.c Outdated

static void on_pm_req(const bool new, const bool old) {
if (new && !old) {
if (acquire_lock()) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the split acquire_lock and release_lock.
I think we could support both /org/freedesktop/PowerManagement/Inhibit, therefore just adding new:

  • acquire_logind_lock
  • acquire_freedesktop_lock

and their release_foo_lock.
WDYT?

Supporting both should work on more systems (ie: non systemd ones!).

@tpeacock19
Copy link
Contributor Author

I'm happy to include the PowerManagement as a fallback for non-systemd systems. However, I don't have any way to test it, so you may need to make some changes to the PR. Currently, the only spec for the PowerManagement API I can find is this for xfce. It doesn't seem like the call to Uninhibit returns any value to represent success or failure.

This makes things particularly difficult for the release_lock function because to release a systemd-inhibit inhibitor you simply need to close([file descriptor]), Which I believe will succeed whether it is a systemd or non-systemd system, assuming the PowerManagement API also makes use of file descriptors.

Systemd fallback to PowerManagement

static bool release_lock(void) {
    if (close(pm_inh_token) == 0) {
        DEBUG("Released systemd-inhibit inhibition.\n");
        pm_inh_token = -1;
        return true;
    } else {
        USERBUS_ARG(pm_args, "org.freedesktop.PowerManagement.Inhibit",
                    "/org/freedesktop/PowerManagement/Inhibit",
                    "org.freedesktop.PowerManagement.Inhibit", "UnInhibit");
        if (call(&pm_args, "u", pm_inh_token) == 0) {
            DEBUG("Released PowerManagement inhibition.\n");
            pm_inh_token = -1;
            return true;
        }
    }
    return false;
}

Systemd

  • pm_inh_token reprents systemd-inhibit fd
  • fd is closed with close(pm_inh_token) call above and release_lock() returns true

Non-systemd

  • pm_inh_token represents PowerManagement fd
  • fd is closed with close(pm_inh_token) call above and release_lock() returns true
  • Uninhibit method never called

PowerManagement fallback to Systemd

static bool release_lockv2(void) {
    USERBUS_ARG(pm_args, "org.freedesktop.PowerManagement.Inhibit",
                "/org/freedesktop/PowerManagement/Inhibit",
                "org.freedesktop.PowerManagement.Inhibit", "UnInhibit");
    if (call(&pm_args, "u", pm_inh_token) == 0) {
        DEBUG("Released PowerManagement inhibition.\n");
        pm_inh_token = -1;
        return true;
    } else if (close(pm_inh_token) == 0) {
        DEBUG("Released systemd-inhibit inhibition.\n");
        pm_inh_token = -1;
        return true;
    }
    return false;
}

Systemd

  • pm_inh_token reprents systemd-inhibit fd
  • Uninhibit call returns 0 (despite the method not being available) and release_lock() returns true
  • fd is never closed and the systemd inhibitor is not released

Non-systemd

  • pm_inh_token represents PowerManagement fd
  • Uninhibit method called with PowerManagement inhibitor being released and release_lock() returns true

@FedeDP
Copy link
Owner

FedeDP commented Jan 8, 2023

Thanks for updating this!
I think we could simply store in a static variable whether we inhibited through systemd or powermanagement freedesktop API; and then the release method will just proxy to release_systemd or release_powermanagement checking that value.
It is simple and effective, as much as i can tell. WDYT?

Aside from this, let me state my gratefulness for this discussion and for your work! :)

@tpeacock19
Copy link
Contributor Author

No problem at all. I've learned a lot about C and programming in general through this exercise and am happy to contribute how I can.

A question about the variable. Would it be enough to use a boolean such as systemd_inh_api (or pm_inh_api` depending on which we use first) or would it need t be a char? I'm still not certain the most appropriate/efficient way to manipulate strings in C. I wonder if I am making it more complicated than need be.

@FedeDP
Copy link
Owner

FedeDP commented Jan 8, 2023

A bool is ok! Or you can use enum values like { NONE, SYSTEMD, FREEDESKTOP }.
The latter is probably the most readable choice, but i am ok with just using a bool too!

Moreover, updated todo.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
@tpeacock19
Copy link
Contributor Author

Oh I forgot to let you know I’ve made the changes you requested, if you want to have another look

@FedeDP
Copy link
Owner

FedeDP commented Jan 13, 2023

Hi! I made some really small changes, and opened a PR against this branch in your fork!
Can you take a look?
Thank you!

…ack.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
@tpeacock19
Copy link
Contributor Author

okay, I've accepted your PR and tested on my machine, everything looks
in order.

@FedeDP
Copy link
Owner

FedeDP commented Feb 26, 2023

Thank you for the great patience and work! :)

@FedeDP FedeDP merged commit 0701495 into FedeDP:master Feb 26, 2023
@tpeacock19 tpeacock19 deleted the feat/freedesktop-inhibit branch February 26, 2023 19:52
SYSBUS_ARG_REPLY(pm_args, parse_bus_reply, &pm_inh_token,
"org.freedesktop.login1", "/org/freedesktop/login1",
"org.freedesktop.login1.Manager", "Inhibit");
int ret = call(&pm_args, "ssss", "idle", "Clight", "Idle inhibitor.", "block");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tpeacock19 i just noticed we are inhibiting idle here; i think we should be inhibiting shutdown, sleep instead.

At the same time, idle should be inhibited by the inhibit module IMHO, like here: https://github.com/FedeDP/Clight/tree/master/src/modules#L60
What do you think?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can do a PR in that direction myself btw ;) i want to just gather your feedback since you implemented it!

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@tpeacock19 tpeacock19 Mar 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seven distinct inhibitor lock types may be taken, or a combination of them:

  1. sleep inhibits system suspend and hibernation requested by (unprivileged) users
  2. shutdown inhibits high-level system power-off and reboot requested by (unprivileged) users
  3. idle inhibits that the system goes into idle mode, possibly resulting in automatic system suspend or shutdown depending on configuration.

From the Freedesktop Docs. I had assumed that idle would cover the cases for automatic suspend/sleep/shutdown and if it was explicitly called, then it should allow for it. That is how I understand the documentation anyway. Do you think differently?

However, since it does default to idle;sleep;shutdown, perhaps it wouldn't be a bad idea?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are right indeed!
Thank you!

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

Successfully merging this pull request may close these issues.

None yet

2 participants