10

Problem:
On an Ubuntu based laptop, when I sleep or disconnect power to my external monitor then power up the system or reconnect the monitor again, the display doesn't come on.

Poor man's solution:
The only way I've found to get the external display to work (aside from reboot) is to switch from Joined Display to Mirror Display. As soon as the monitor comes alive I can simply cancel the change to Mirror Displays, monitor settings revert, and the external monitor works as usual.

Grievance:
Unfortunately this causes windows that were on my external monitor to be misarranged across desktop workspaces (PopOS here, Ubuntu 20.10). It's a fair bit of effort to re-arrange workspaces after the mirror/cancel process.

Hopeful question:
Are there any command line utilities that might force the external monitor to reset without the need to change from Joined Display to Mirror?

David Parks
  • 2,516

2 Answers2

8

I was having a similar issue with PopOS 22.04 (jammy) on my laptop, and an external HDMI display. Here is a solution that works automatically once implemented.

0. Figure out some useful constants:

  1. Your username: this should be self-explanatory. You can run whoami in a non-privileged shell terminal to find out. In the commands below, when I specify USERNAME, replace it with your username.
  2. The display identifiers of your screens: when your displays are working correctly, you can run xrandr | grep ' connected' (keep the space inside quotes) and look at the output. The first column contains the display identifiers. For example, my internal display is eDP-1, and my external display is HDMI-1-0. Below I will use these values in my scripts, please replace them with your values if they are different.
  3. The DISPLAY environment variable: run echo $DISPLAY in a non-privileged shell terminal to find out. Mine says :1, so I will use it below. If your value is different, please replace :1 with your own value.

1. Create the following script at /usr/lib/pm-utils/sleep.d/99_external_monitor_wake:

#!/bin/bash

DISPLAY=:1; export DISPLAY

case "$1" in suspend | hibernate | pre) # no operation : ;; resume | thaw | post) # wakes the external monitor connected to the HDMI port xrandr --output HDMI-1-0 --auto --left-of eDP-1 --primary sleep 3 xrandr --output HDMI-1-0 --auto --left-of eDP-1 --primary ;; esac

exit 0

You will need to tweak this script according to your setup.

The first thing to change is DISPLAY=:1. This sets the DISPLAY environment variable we found in section 0.2. above. Replace it with your own value if different.

The second thing to change are the two xrandr --output HDMI-1-0 --auto --left-of eDP-1 --primary lines. First you need to replace the display identifiers we found above in section 0.1. with your own values. I am also setting my HDMI display to the left of my internal display, and making my external display primary. You may want to replace --left-of with --right-of, and you may want to omit the --primary flag. Read the man page for xrandr by typing man xrandr for more details. Remember to make the same changes to both lines of command.

You can also experiment with the sleep time if the 3 seconds I specified above doesn't work for you. It's kind of like using AED (idk I'm not an EMT): you give it a jolt first, and sneak in a second command when it's in a state to be able to receive it.

2. Change the script's owner to your user, and make it executable:

sudo chown USERNAME:USERNAME /usr/lib/pm-utils/sleep.d/99_external_monitor_wake
sudo chmod +x /usr/lib/pm-utils/sleep.d/99_external_monitor_wake

You want to replace the user USERNAME with your own login user name found in section 0.0. above.

3. Create a systemd service unit file at /etc/systemd/system/wake-monitor.service to trigger this script upon resuming from sleep:

[Unit]
Description=Wake external monitor connected to HDMI after resuming from sleep, and when entering a display manager
After=sleep.target display-manager.service
StopWhenUnneeded=yes

[Service] User=USERNAME Type=oneshot RemainAfterExit=no ExecStart=/usr/lib/pm-utils/sleep.d/99_external_monitor_wake resume

[Install] WantedBy=sleep.target display-manager.service

You want to replace the user USERNAME with your own login user name found in section 0.0. above.

I included display-manager.service in my After and WantedBy, because I have the same issue when the screen locks after no activity. You can remove it if you only need to fix resuming from sleep.

4. Enable the newly created systemd service:

sudo systemctl daemon-reload
sudo systemctl enable wake-monitor.service

That's it! You should be able to suspend your computer, and wake up with a functioning external display!

If something is not working right, you can debug the service by running sudo systemctl status wake-monitor.service and looking at the output.

References:

Jeremy
  • 2,846
Samm
  • 83
  • When a systemd service, the script can be something like /usr/local/bin/external_monitor_wake. Using it in /usr/lib/pm-utils/sleep.d/ seems confusing as that is a special directory for pm-utils, which we aren't using. – AdamS Feb 19 '23 at 17:20
5

Two things to try:

Sometimes running xrandr with no options will cause the display to be probed and start working.

I've had problems with power saving mode to wake up one screen and not the other, especially when one was disconnected for a while. When this happens, make sure the external monitor is plugged in and powered on and then run

xset dpms force off

and wait a few seconds and then hit a key to unblank the screen. This may wake up the external monitor.

It may be necessary to cycle things completely, and a delay is needed to let things settle:

xset dpms force off; sleep 2; xset dpms force on

An automatic wait may not work, watching it manually may be more effective. If the monitor alternates between states such as blue/blank screen you may need to run the command with the monitor in one of these states (such as when it's a blank screen and not blue).

user10489
  • 4,051
  • 1
    Brilliant!! I had to run xset dpms force on followed by xset dpms force off to get it to come alive, but it came back on! You're officially my hero. – David Parks Jun 11 '21 at 18:53
  • 1
    I suspect something in the power staving state between the display server and the hardware is getting out of sync. I'd file a bug, but I've never been able to reliably reproduce this problem. – user10489 Jun 11 '21 at 21:53
  • I'll add that I had to add a sleep between those statements, so my script currently is: xset dpms force on; sleep 10; xset dpms force off; – David Parks Jun 14 '21 at 15:43
  • 1
    Yes, a delay is needed for things to settle. I'll go ahead and add all that to the answer. I am thking that 5 seconds should be enough; would it help to force them on again at the end? – user10489 Jun 14 '21 at 20:31
  • I've had somewhat inconsistent results for how long to sleep. Today I tried 5s, 7s, 10s (many times) and all failed to bring the external monitor online. Finally I tried 20s and it worked. I'll keep playing with the script to try to get it working consistently and will edit the post with my final config at some point in the future, assuming I get it working consistently. – David Parks Jun 16 '21 at 14:51
  • When I have to do it, I don't time it, I usually wait until both of my monitors completely power off, which could easily be 20-30 seconds. – user10489 Jun 16 '21 at 16:46
  • I discovered the delay wasn't really necessary. Rather the external monitor has to be in the right state to come on. When it fails to reconnect it alternates between a blue screen and a blank screen. If I run the command when it's a blue screen, it won't take, but when it's a dark screen the command works with only a short (or possibly no) delay. – David Parks Jun 23 '21 at 16:57
  • I only delay to wait for it to be in the right state. – user10489 Jun 23 '21 at 21:46
  • Any delay I used landed in either the right or wrong state depending on when I happen to start the script. By using a short delay I simply wait for the monitor to go blank (be in the right state) and then run it, and so far that's worked every time. – David Parks Jun 23 '21 at 22:14