autorandr icon indicating copy to clipboard operation
autorandr copied to clipboard

Be aware of lid state

Open Vladimir-csp opened this issue 6 years ago • 30 comments

To handle situations like laptop lid being closed or open with regards to external monitor:

  • save lid state into profile's setup along with output matches
  • trigger profile reload on lid state change.

Lid states can be acquired from /proc/acpi/button/lid/*/state

Vladimir-csp avatar Apr 11 '18 17:04 Vladimir-csp

This change would increase the complexity from a user's perspective a lot. Currently, it's fairly easy: A set of specific monitors connected to specific physical outputs is what autorandr calls a setup. Reproduce a setup, and autorandr will reload the configuration you stored earlier for you. I'd rather not add complexity to this. (For example, valid follow up questions would be: Shouldn't we then distinguish between monitors in stand-by and powered-on monitors as well?)

That being said, you can use the block hook to add this to your personal configurations.

phillipberndt avatar Apr 15 '18 16:04 phillipberndt

For example, valid follow up questions would be: Shouldn't we then distinguish between monitors in stand-by and powered-on monitors as well?

No, there isn't any ambiguous continuation for this. The laptop case is pretty straightforward: if lid is closed, internal monitor becomes useless and is expected to be unused.

There may be a different approach: add a config option to mark output(s) as internal. Treat those outputs as disconnected if lid is closed.

Vladimir-csp avatar Apr 19 '18 07:04 Vladimir-csp

I think it would be very useful too

gabri94 avatar Feb 21 '19 11:02 gabri94

@phillipberndt As a user, I would have expected autorandr to treat lid open/close events as a screen connect/disconnect. At least this is essentially how I think about it when I open/close the lid, since there is no way to actually disconnect it.

That being said, even though I would enjoy if autorandr's behavior would match my way of thinking, it is nice to see that it also provides ways to work around such edge cases and I thank you for that. I just spent an hour figuring this out, so I figured I would share for others who bump into the same issue.


Automatically switch display configuration based on lid state

  1. Create 2 separate display configurations, one with the lid closed, one with the lid open (I will assume you save them as config-open and config-close).
  2. Create the executable file .config/autorandr/config-open/block, which indicates when to block the config
#!/bin/bash

exec grep -q close /proc/acpi/button/lid/LID0/state
  1. And the executable file .config/autorandr/config-open/block
#!/bin/bash

exec grep -q open /proc/acpi/button/lid/LID0/state
  1. It's time to test: open/close the lid and then run
autorandr --change

Your preferred config should apply accordingly. If it does not, make sure you made both block files executables.

  1. Repeat steps 1-4 for as many configs where you want to a different behavior when the lid is open/closed

  2. Automate the whole thing. I used acpid for this, but there could be better ways. Create the executable file /etc/acpi/autorandr.sh

#!/bin/bash

/usr/bin/autorandr --batch --change --default default

Create the file /etc/acpi/events/lid-switch

event=button/lid LID (open|close)
action=/etc/acpi/autorandr.sh

And then restart acpid. The configuration switch should now happen automatically. If not, double check that /etc/acpi/autorandr.sh is executable.

chmduquesne avatar Feb 26 '19 09:02 chmduquesne

This change would increase the complexity from a user's perspective a lot.

Honestly, after going through this, I would find it easier if autorandr treated a closed lid as disconnected (what am I going to display on it when it is closed anyway?)

chmduquesne avatar Feb 26 '19 09:02 chmduquesne

Honestly, after going through this, I would find it easier if autorandr treated a closed lid as disconnected (what am I going to display on it when it is closed anyway?)

Open questions:

  • Does every notebook expose lid state through /proc/acpi/button/lid/LID0/state? (I guess the answer to that is yes)
  • How can autorandr find out which display is the notebook's LCD? Hardcoding a couple of common names doesn't sound good enough. (Forcing users to configure this manually doesn't sound considerably better than what's possible already.)
  • How should it store the additional information about lid state? I made the promise not to break with the original autorandr's format, so this'd have to be an additional file I guess. A generic additional data file, json-encoded or .ini, probably?

phillipberndt avatar Mar 10 '19 18:03 phillipberndt

  1. it was exposed at this path on at least three of my laptops, otherwise no idea.
  2. config option in settings.ini, something like laptop_output. If it is set, then also enable the mechanism.
  3. there is no need to store additional information (at least in this case). If laptop_output is considered disconnected, profile configuration does not need any change.

Vladimir-csp avatar Mar 10 '19 18:03 Vladimir-csp

config option in settings.ini, something like laptop_output. If it is set, then also enable the mechanism.

As I wrote, I don't think this should be something that needs manual configuration. If it does then there's no real advantage over the block script - users still need to know about the feature and configure something to make this work.

there is no need to store additional information (at least in this case). If laptop_output is considered disconnected, profile configuration does not need any change.

The profile must store whether the lid is supposed to be open or closed, for the profile to apply, doesn't it?

phillipberndt avatar Mar 10 '19 18:03 phillipberndt

The profile must store whether the lid is supposed to be open or closed, for the profile to apply, doesn't it?

Why? The profile would just lack an internal output, no EDID, no config (or off). Just like with any other disconnected outputs.

Vladimir-csp avatar Mar 10 '19 18:03 Vladimir-csp

Why? The profile would just lack an internal output, no EDID, no config (or off). Just like with any other disconnected outputs.

The config file contains the parameters that need to be passed to xrandr. And xrandr needs to be passed the information that the internal output is to be disabled. The setup file contains the displays attached to outputs as reported by the system. If the internal display is missing autorandr won't be able to detect that the profile applies.

phillipberndt avatar Mar 10 '19 19:03 phillipberndt

Send --off to every output that isn't listed in config. No EDID on "disconnected" output, no EDID in profile's setup. What is the problem?

Vladimir-csp avatar Mar 10 '19 19:03 Vladimir-csp

No EDID on "disconnected" output

If the system reports that an output has a monitor attached and the setup says that there should be none then the profile doesn't match and autorandr won't attempt to load it. If that'd change, then a profile where a notebook isn't attached to any output but its internal LCD would match even if there's something else attached.

phillipberndt avatar Mar 10 '19 21:03 phillipberndt

By treating internal output as disconnected I meant faking it at info gathering stage. Inject xrandr data characteristic of a disconnected output if lid is closed. Everything else does not need to change in this case. If autorandr receives output as disconnected, then:

if not match["connected"]:
    edid = None

This bit is already in the code. Just fake disconnected before this if happens.

Vladimir-csp avatar Mar 11 '19 04:03 Vladimir-csp

Does every notebook expose lid state through /proc/acpi/button/lid/LID0/state? (I guess the answer to that is yes)

For some laptops, the lid state appears to be reported in /proc/acpi/button/lid/LID/state (see this thread for example). A good catch-all seems to be /proc/acpi/button/lid/*/state

How can autorandr find out which display is the notebook's LCD? Hardcoding a couple of common names doesn't sound good enough. (Forcing users to configure this manually doesn't sound considerably better than what's possible already.)

Unfortunately, I don't know about a bulletproof approach. I would suggest using a list of common names, but make it override-able. This way we would catch 80% of the users by default, and the last 20% can fine-tune their configuration if their use-case is not covered.

How should it store the additional information about lid state? I made the promise not to break with the original autorandr's format, so this'd have to be an additional file I guess. A generic additional data file, json-encoded or .ini, probably?

What would make sense to me would be that autoxrandr would just pretend the screen is disconnected if the lid is closed. Looking at the setup files I have in my profiles, that would correspond to removing the line of the lid from the setup file when the lid is closed.

chmduquesne avatar Mar 12 '19 15:03 chmduquesne

@chmduquesne Looks like Lenovo X1 Carbon is a practical example of one of the ones that use LID as opposed to LID0. One thing that seems confusing here is whether or not it is safe to assume that a lid always maps to a device called eDP*. /proc/ doesn't seem to tell us which display maps to the lid...

monokrome avatar Jul 16 '19 00:07 monokrome

Hi,

I developed a branch where a closed lid is treated as if it was a disconnected output. This means, essentially, that running autorandr --save while the lid is closed will generate a setup file without a line for the lid. This line will still be present when the lid is open.

This is of course not backward compatible with current configurations, but I think it matches better what any user would expect. I know @phillipberndt wants to stay backward compatible, but I find it silly to make this behavior optional, because I can't think of anyone who would like to configure an output they can't see. If there is a situation justifying doing this, I am willing to modify my PR, but I am curious to read about it.

I also wrote code to trigger autorandr automatically when the lid is open/closed. On this part, I would particularly like to have feedback: Generally I tried to avoid depending on acpid, since nowadays I don't see a reason for installing it. I ended up writing a desktop autostart script monitoring the output of libinput debug-events, which I find pretty clean since it runs in userspace. However, there is a catch: for this to work, you must be in the group input. I am not sure whether the benefit of running this process in userspace is a good reason enough to justify forcing the user to be in a given group. The alternative is to make this a systemd service and to run the snippet as root.

What is your general opinion? Does it make sense to make this the default? Is it desirable to keep supporting old configs? Should the lid monitor be a systemd script, or an autostart desktop entry?

chmduquesne avatar Nov 04 '19 20:11 chmduquesne

  1. If internal output is the only one connected, it should not be ignored. So mabye don't do edid = None during xrandr parsing. Count connected outputs after xrandr parsing and then decide.
  2. Internal output name and lid button device needs to be overrideable by config in case autodetection is wrong.
  3. Listener looks rather crude, and input group requirement is not good. A couple of ideas for listening: https://github.com/airtonix/laptop-lid-event-listener/blob/master/dbus-laptop-lid-listener.py https://www.freedesktop.org/software/systemd/python-systemd/login.html

Vladimir-csp avatar Nov 05 '19 05:11 Vladimir-csp

  1. Good idea, I will count the number of screens.
  2. Ok, I will add command line options to override lid output name and lid state file.
  3. I disagree with your comment that it looks crude: It solves the problem in a clean, unix way. The output has a known format, the event has a known name. What I will do, however, is to limit the grep to the second field.

Some comments about the two links you suggested:

  • https://github.com/airtonix/laptop-lid-event-listener/blob/master/dbus-laptop-lid-listener.py → I already looked at this. It is outdated: hal was deprecated in 2011.
  • https://www.freedesktop.org/software/systemd/python-systemd/login.html → I already spent quite some time on the systemd irc channel and while systemd-logind gets the lid open/close events, it seems that it does not make them available to client code. I actually got the advice to look at libinput from the people in this channel.

The systemd service that I provided solves the problem and does not require the user to be in input. I will make the parsing of this output a bit more rigorous, but mostly I think that libinput is the cleanest way of getting the lid events: libinput is also how window managers get keystrokes, and how wayland handles the lid.

chmduquesne avatar Nov 05 '19 10:11 chmduquesne

Thanks for implementing this, this makes the decision whether to do this way easier ;-)

re 3: I'm fine with the systemd stuff as it is in the PR. Those files are in the contrib folder, and others are free to improve it later.

re 2: IMHO it'd suffice to add this once someone complains. Your version should cover almost all cases.

re 1: Sounds good, and having this logic outside the xrandr parser is nicer from a code perspective, too.

phillipberndt avatar Nov 05 '19 15:11 phillipberndt

re 3: If overhead of dumping and grepping through all input events is negligible, then ok.

Vladimir-csp avatar Nov 05 '19 17:11 Vladimir-csp

1 is now implemented.

I elected not to implement 2, because I believe that my version covers all cases (as I mentioned in the PR, I went through the code of the graphic drivers to determine how the output names are generated for xrandr). I would prefer to add code only if we are sure that somebody needs it.

I understand why 3 is controversial. I agree that there is an small overhead: libinput does indeed receive all input events. I tried to mitigate this by improving the regexp performance and only look at the relevant part of the events. All I can say is that I am already using this code on my personal laptop as well as my work laptop without noticeable impact, but that is a totally subjective remark.

Alternatively, I can include code for doing this based on acpid hooks in the contrib directory. I was doing this before and it was working quite well. IMHO it's a heavier dependency, but the users may choose as they wish.

@Vladimir-csp if you can come up with a better implementation, you are very welcome to do so!

chmduquesne avatar Nov 10 '19 17:11 chmduquesne

Merged as is for now, thanks for your work! (Happy to accept further PRs if you guys have good ideas for improving lid event integration.)

phillipberndt avatar Nov 10 '19 21:11 phillipberndt

Cool, thanks!

As far as I am concerned, this issue can be closed, then 🙂

chmduquesne avatar Nov 11 '19 21:11 chmduquesne

Thanks! I've tested it. There was an issue, fix in PR.

Vladimir-csp avatar Nov 13 '19 19:11 Vladimir-csp

In case anyone is interested, I used acpid to trigger autorandr on lid change.

sudo pacman -S acpid
sudo systemctl enable --now acpid

Then in /etc/acpi/handler.sh I added this call on the lid close and open to trigger autorandr the same way udev does.

systemctl start --no-block autorandr.service

acpid also supports custom event configuration in /etc/acpi/events/ so I guess this may be another way to do it

Diaoul avatar Jun 18 '21 17:06 Diaoul

I used NixOS own module for acpid:

  services.acpid = {
    enable = true;

    lidEventCommands = ''
      #!${pkgs.bash}/bin/bash

      export DISPLAY=:0

      if grep -q open /proc/acpi/button/lid/LID/state; then
        ${pkgs.sudo}/bin/sudo -u ivan ${pkgs.autorandr}/bin/autorandr all
      else
        ${pkgs.sudo}/bin/sudo -u ivan ${pkgs.autorandr}/bin/autorandr monitor
      fi
    '';
  };

In plain it looks similar to this:

cat /nix/store/grkf04p1asj2q07ac472njl8wi8lz4np-acpi-events/lidEvent
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       │ File: /nix/store/grkf04p1asj2q07ac472njl8wi8lz4np-acpi-events/lidEvent
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ event=button/lid.*
   2   │ action=/nix/store/l333rww8bci83pnwbr06i1la0kcfcs1p-lidEvent.sh/bin/lidEvent.sh '%e'
───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cat /nix/store/l333rww8bci83pnwbr06i1la0kcfcs1p-lidEvent.sh/bin/lidEvent.sh
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       │ File: /nix/store/l333rww8bci83pnwbr06i1la0kcfcs1p-lidEvent.sh/bin/lidEvent.sh
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ #!/nix/store/a54wrar1jym1d8yvlijq0l2gghmy8szz-bash-5.1-p12/bin/bash
   2   │ export DISPLAY=:0
   3   │
   4   │ if grep -q open /proc/acpi/button/lid/LID/state; then
   5   │   /nix/store/rh30apmhk2vsnzd9dp60zyfni87v500i-sudo-1.9.7p2/bin/sudo -u ivan /nix/store/igzf3bhn1brjvx07mi2yw270f7c1npab-autorandr-1.11/bin/autorandr all
   6   │ else
   7   │   /nix/store/rh30apmhk2vsnzd9dp60zyfni87v500i-sudo-1.9.7p2/bin/sudo -u ivan /nix/store/igzf3bhn1brjvx07mi2yw270f7c1npab-autorandr-1.11/bin/autorandr monitor
   8   │ fi

ivankovnatsky avatar Dec 14 '21 15:12 ivankovnatsky

Here is a small script I created & it worked for me without issues:

#!/bin/bash
# Allows to automatically disable/enable the laptop screen when the lid is opend/closed

sudo pacman -S acpid --needed --noconfirm

sudo mkdir /etc/acpi/actions

sudo tee /etc/acpi/actions/lid.sh > /dev/null <<EOT
#!/bin/bash
# Automatically enable/disable output to Laptop (LVDS1) when lid close/open event happens

export XAUTHORITY=/home/$USER/.Xauthority
export DISPLAY=":0.0"

case "$3" in
    close)
        logger 'LID closed'
        xrandr --output LVDS1 --off
        ;;
    open)
        logger 'LID opened'
        xrandr --output LVDS1 --auto
        ;;
    *)
        logger "ACPI action undefined: $3"
        ;;
esac
EOT

sudo tee /etc/acpi/events/lid > /dev/null <<EOT
event=button/lid LID (open|close)
action=/etc/acpi/actions/lid.sh
EOT

sudo chmod +x /etc/acpi/actions/lid.sh

# remove 'anything' event handler
sudo rm /etc/acpi/events/anything

# restart acpid
sudo systemctl stop acpid
sudo systemctl enable acpid
sudo systemctl start acpid

Checked in at https://github.com/Xcalizorz/endeavouros-i3wm-setup/blob/main/custom-scripts/install-acpid-events.sh

ghost avatar Dec 26 '22 08:12 ghost

For what it's worth, with current autorandr the lid is handled exactly as I expect, i.e. "lid closed" means internal monitor is disconnected. However, I need to manually run autorandr --change to detect this. It would be great if autorandr were to run automatically when the lid is closed/opened.

Nikratio avatar Jul 10 '23 16:07 Nikratio

For what it's worth, with current autorandr the lid is handled exactly as I expect, i.e. "lid closed" means internal monitor is disconnected.

Yes, that was implemented in #169.

However, I need to manually run autorandr --change to detect this. It would be great if autorandr were to run automatically when the lid is closed/opened.

For this to happen, make sure contrib/etc/xdg/autostart/autorandr-lid-listener.desktop is installed properly.

Alternatively, you can run the script I suggested in #269, available in https://github.com/chmduquesne/autorandr/blob/dbus_monitor/contrib/autorandr_dbus_monitor.sh, which can replace every script that triggers autorandr.

chmduquesne avatar Jul 11 '23 08:07 chmduquesne

Thanks! Why not close this issue as fixed then? :-).

Nikratio avatar Jul 12 '23 10:07 Nikratio