phillipberndt / autorandr

Auto-detect the connected display hardware and load the appropriate X11 setup using xrandr
2.5k stars 123 forks source link

Be aware of lid state #104

Open Vladimir-csp opened 6 years ago

Vladimir-csp commented 6 years ago

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

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

phillipberndt commented 6 years ago

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.

Vladimir-csp commented 6 years ago

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.

gabri94 commented 5 years ago

I think it would be very useful too

chmduquesne commented 5 years ago

@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

3. And the executable file `.config/autorandr/config-open/block`
```bash
#!/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.

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

  3. 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 commented 5 years ago

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?)

phillipberndt commented 5 years ago

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:

Vladimir-csp commented 5 years ago
  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.
phillipberndt commented 5 years ago

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?

Vladimir-csp commented 5 years ago

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.

phillipberndt commented 5 years ago

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.

Vladimir-csp commented 5 years ago

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?

phillipberndt commented 5 years ago

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.

Vladimir-csp commented 5 years ago

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.

chmduquesne commented 5 years ago

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.

monokrome commented 5 years ago

@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...

chmduquesne commented 5 years ago

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?

Vladimir-csp commented 4 years ago
  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
chmduquesne commented 4 years ago
  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:

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.

phillipberndt commented 4 years ago

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.

Vladimir-csp commented 4 years ago

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

chmduquesne commented 4 years ago

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!

phillipberndt commented 4 years ago

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.)

chmduquesne commented 4 years ago

Cool, thanks!

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

Vladimir-csp commented 4 years ago

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

Diaoul commented 3 years ago

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

ivankovnatsky commented 2 years ago

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
ghost commented 1 year ago

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

Nikratio commented 1 year ago

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.

chmduquesne commented 1 year ago

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.

Nikratio commented 1 year ago

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