hyprwm / hypridle

Hyprland's idle daemon
BSD 3-Clause "New" or "Revised" License
403 stars 25 forks source link

[Feature] set power source type on listener for laptops #67

Open AmirAref opened 5 months ago

AmirAref commented 5 months ago

Hi, thanks for your nice program, I think it could be so helpful to add a parameter on listener configuration for laptops to define different listeners for AC power or Battery power. For example, I want to set more time to go to sleep on AC power :

listener {
    on-power = battery # on battery power
    timeout = 600
    on-power = systemctl suspend
}

listener {
    on-power = AC # on AC power
    timeout = 1800
    on-power = systemctl suspend
}
fufexan commented 5 months ago

I've actually thought about this before, and I plan to implement it (though I don't have as much time as I'd like to do that). I've envisioned a different syntax:

listener {
    timeouts = 600, 1800 # BATT, AC
    command = systemctl suspend
}

or

listener {
    timeout-battery = 600
    timeout-ac = 1800
    command = systemctl suspend
}

in which case timeout would still be used as fallback if you want both BAT/AC timeouts to be the same.

AmirAref commented 5 months ago

I think the second syntax is better, also it should to be considered that for example someone may not want to set any listener for sleep or lock commands on AC power but do it on Battery.

So something like this behavior could be efficient :

Which means a listener can have only one of AC or battery timeouts, or both of them.

fufexan commented 5 months ago

I have a practical usecase for setting both ac and bat, and that is DPMS.

stinobook commented 5 months ago

Hello,

Quick question in the meantime: With hypridle running, is it possible to 'reload' the config file or add a listener ? Or does it need to be killed and restarted? this way we could just have 2 hypridle configs, one for battery power and one for mains :)

FYI atm i do it like this: file /etc/acpi/handler.sh and just use 2 different configs.


    ac_adapter)
        case "$2" in
            AC*|AD*)
                case "$4" in
                    00000000)
                        logger 'AC unplugged'
            sudo -u stino cp /home/stino/.config/hypr/hypridle-battery.conf /home/stino/.config/hypr/hypridle.conf
            sudo -u stino pkill hypridle
            sudo -u stino hypridle
                        ;;
                    00000001)
                        logger 'AC plugged'
            sudo -u stino cp /home/stino/.config/hypr/hypridle-ac.conf /home/stino/.config/hypr/hypridle.conf
            sudo -u stino pkill hypridle
            sudo -u stino hypridle
                        ;;
                esac
                ;;
            *)
                logger "ACPI action undefined: $2"
                ;;
        esac
        ;;
AmirAref commented 5 months ago

@stinobook it's not necessary to overwrite the main hypridle.conf file, you can easily create two files, one for battery and one for AC, and when you want to change your profile, just kill the previous hypeidle process and start a new process with the config file that you want using -c flag which mentioned here as configuration file path. For example, my ~/.config/hypr/ files:

hypridle.conf
hypridle-ac.conf
hypridle-battery.conf

for switching to AC mode:

pkill hypridle
hypridle -c ~/.config/hypr/hypridle-ac.conf

also, this tricky way to start the process in background would be useful (which means you can close your terminal after that):

nohup hypridle -c hypridle-ac.conf &
bzglve commented 5 months ago

I'm not sure if this is a good idea, but it would be convenient to have different timings for one or another percentage of the battery

something like

# base listener that works for battery < 100%
listener {
    timeout-battery = 600
    command = systemctl suspend
}

# listener that works for battery < 60%
listener {
    timeout-battery-60 = 300
    command = systemctl suspend
}

# listener that works for battery < 10%
listener {
    timeout-battery-10 = 120
    command = systemctl suspend
}
yawor commented 5 months ago

also, this tricky way to start the process in background would be useful (which means you can close your terminal after that):

nohup hypridle -c hypridle-ac.conf &

If you're using hyprland, it's better to do

hyprctl dispatch exec hypridle -c hypridle-ac.conf

to start new hypridle instance. That way it's started in the same fashion as it's started on login using exec-once.

Also if you'd like to reload some executable on config file change, I recommend using watchexec program. For example, I run waybar from hyprland like this:

exec-once = watchexec --stdin-quit -r -w ~/.config/waybar waybar

The watchexec starts waybar and monitors the waybar config directory. If it detects any changes to the files in that directory, it restarts the waybar process.

The --stdin-quit is important, because without it watchexec won't detect when hyprland exits and it stays running. With that parameter it properly terminates the target process and itself when hyprland exits.

AmirAref commented 5 months ago

The watchexec starts waybar and monitors the waybar config directory. If it detects any changes to the files in that directory, it restarts the waybar process.

@yawor Oh, thank you. I had to take lots of effort to customize my waybar and reload the new config manually, every time.

Also, i didn't know about the hyprctl dispatch exec command, it's very useful.

Atemu commented 3 months ago

There's some further things to consider here. Consider the following config and scenario:

listener {
    timeout-battery = 600
    timeout-ac = 1800
    command = systemctl suspend
}
  1. Laptop is plugged in and left to idle 700s.
  2. Laptop is plugged out.

What should happen?

A "dumb" mode switch to "battery state" would not do anything because the 600s timeout would have already happened. Such behaviour must be avoided IMHO since it represents an illegal state; a state after the event should have fired but didn't actually fire.

I think the least surprising behaviour would be to fire the event immediately after plugging out eventhough it's overdue.

Another interpretation could however be to expect it to wait another 600s after plugging out and basically consider the plugging in/out itself to be an event that should reset the timeout.

Which of these two interpretations should be used should perhaps be user-configurable on a per-listener level with a global default.

Atemu commented 3 months ago

Regarding battery levels:

Behaving differently based on battery percentage is also a feature I think hypridle should have. I'd like to propose this option and semantics instead:

listener {
    timeout-battery = 600
    timeout-ac = 1800
    battery-percent = 60
    command = systemctl suspend
}

Everything >60% battery would then be considered the same as AC.

This too would be quite limited in some regards and may not necessarily cover all use-cases.

A better solution would perhaps be to have a generic event listening system where the user can declare their own events to listen/poll for in addition to the absence of user input (which hypridle produces itself).

yawor commented 3 months ago

@Atemu doing dynamic timeouts depending on some external state is not really doable in current approach to timeouts in hypridle. It doesn't create its own timers directly. Instead it registers all configured timeouts in the compositor (using idle notification Wayland API) when it starts and just wait for for callbacks. The first scenario you've mentioned, where the laptop is unplugged after the timeout has already passed already happens with media playback if you don't inhibit the timeout directly in the compositor. If it's inhibited only in hypridle, then the timeout still happens but it's just ignored by hypridle and won't fire even after the video has finished. The idle notification Wayland API is not very flexible. I'm don't have much experience with it, but looking at it, it would be hard to register and unregister timeouts dynamically. I'm wondering if it would be possible to take a different approach. Instead of executing actions on compositor's timeouts, only register on timeout with a very short time (for example 1 second) just to get notifications when the compositor enters or exits idle state, and then handle all timers internally. Creation of timers could then be also controlled by other external sources or states.

demonspork commented 1 week ago

Maybe just have hypridle re-set itself and re-register with the new set of timers whenever the power status changes?

Atemu commented 1 week ago

Oh wow I just thought of a cursed setup where you'd have two hypridle systemd services that point at separate configs where the AC/battery differences are taken care of. Both would Conflicts= each other and un/plugging would start the respective hypridle which would automatically stop the other.

That's so stupid it might actually work.