How to standby a monitor using xset in multi monitor setup?
While having two or more monitor working together, Is there any way to put a single one of them on standby/suspend by issuing a command like: xset dpms force suspend
? or having a time set for that purpose like: xset dpms 100 0 0
which works on these monitors separately?
I've got two monitor working along each other, eDP1 (My Laptop) and VGA1 (An External monitor).
I want each of them to go into suspend/standby mode separately if I'm not directly interacting with them, suppose I'm watching a movie on VGA1, and for an hour and half eDP1 is on doing nothing.
I'm not interested in using xrandor --off --output eDP1
because it's not fast enough to work with.
I want my monitor to be ready to work, with a simple mouse movement so I'm able to switch between them fast.
- Running: Ubuntu 18.04
- Window Manager: OpenBox
Solution 1:
Controlling individual monitors is not possible with xset ( and X11 actually)
As the title suggests, it is not possible for reasons of how xset
is built and due to the X11 functions it uses. If we look at the source code, xset
calls DPMSForceLevel(dpy,DPMSModeSuspend)
(line 557), and the display variable dpy
comes from XOpenDisplay()
function ( line 203 ), and that is by definition:
A server, together with its screens and input devices, is called a display.
In other words, xset
applies settings globally to the whole display, not individual Screens. It'd be necessary to change xset
source code in order for that to work. DPMS extensions themselves mostly seem to only call whole display, not individual screens, so it is not possible to even write custom code with X11 library.
Manually controlling that setting via /sys
subsystem also doesn't appear to be working
$ sudo bash -c 'echo Off > /sys/class/drm/card0-VGA-1/dpms'
[sudo] password for admin:
bash: /sys/class/drm/card0-VGA-1/dpms: Permission denied
Screens also are taken out of DPMS mode when key or mouse events occur, so considering that you may want to move your mouse or use keyboard, either of those actions would cause the monitor to leave DPMS mode.
Alternative workarounds
Best alternative (and actually physically working solution) is xrandr
- it could be used to control the individual "outputs". In particular,
xrandr --output VGA-1 -off
will set that output off. Yes, you've mentioned that you don't want to use this solution since it is not fast enough, however so far it is the best one available. It has couple of advantages:
- immune to key and mouse events
- independently controls outpus unlike
xset
The xrandr --output VGA-1 --brightness 0.1
will colorize the screen in such way that it appears off, even though --brightness
is a software solution, so the display is not actually dimmed at hardware level, nor it is off on hardware level. However, it does the job of blanking a screen and is also resistant to key/mouse events.
I've looked source code of Mate and Budgie screensavers, which are both forks of GNOME screensaver, however in either case they seem to be a software solution, since there's no mention of DPMS in the source code.
Solution 2:
Temporary comment
- On request of OP, I made the script below switch the screen off by means of
xrandr
. On a longer test, this worked out pretty badly. Not so much the switching off failed, but at reactivating the screen, screen layout was totally messed up. I'd be happy to post it to see if it works in your case however, but my advice is not to use it.
In the script, I went back to setting brightness to zero instead. - There was some discussion on wether we should define the active monitor by mouse location, or by location of the active window. The latter will not work if no window exists. We might not have a window at all (apart from the desktop itself) in which case the choice of window to black out would be random (or break if we won't include the exception). IMO the only option that makes sense -and would work in a predictable way in all cases- is to define the active screen by mouse position. Furthermore, that is also how the window manager decides where new windows should appear.
Then what did I change in this version?
The idle time is now defined by both keyboard- and mouse activity by default. Waking up is also done by either one.
Automatically dim inactive screen
As said by my fellow answerers, switching screens off from cli separately is a challenge at best, and I didn't find an option either.
What I did find is a way to automatically dim all screens, except the one where the mouse is, after x time.
Here we go
#!/usr/bin/env python3
import subprocess
import gi
gi.require_version("Gdk", "3.0")
from gi.repository import Gdk
import time
import sys
def get_idle():
try:
return int(subprocess.check_output("xprintidle")) / 1000
except subprocess.CalledProcessError:
return 0
def get_monitors():
screen = Gdk.Screen.get_default()
n_mons = display.get_n_monitors()
mons = [screen.get_monitor_plug_name(i) for i in range(n_mons)]
return mons
def set_mon_dimmed(mon, dim):
print(mon, dim)
val = "0.0" if dim else "1"
try:
subprocess.Popen(["xrandr", "--output", mon, "--brightness", val])
except subprocess.CalledProcessError:
print("oops")
def mousepos():
# find out mouse location
return Gdk.get_default_root_window().get_pointer()[1:3]
def get_currmonitor_atpos(x, y, display=None):
"""
fetch the current monitor (obj) at position. display is optional to save
fuel if it is already fetched elsewhere
"""
if not display:
display = Gdk.Display.get_default()
return display.get_monitor_at_point(x, y)
display = Gdk.Display.get_default()
wait = int(sys.argv[1])
elapsed = 0
# set intervals to check
res = 2
monitors = [m for m in get_monitors()]
for m in monitors:
set_mon_dimmed(m, False)
monrecord = {}
for m in monitors:
monrecord[m] = {"idle": 0, "dimmed": False}
display = Gdk.Display.get_default()
idle1 = 0
while True:
time.sleep(res)
curr_mousepos = mousepos()
activemon = get_currmonitor_atpos(
curr_mousepos[0], curr_mousepos[1]
).get_model()
idle2 = get_idle()
if idle2 < idle1:
monrecord[activemon]["idle"] = 0
if monrecord[activemon]["dimmed"]:
set_mon_dimmed(activemon, False)
monrecord[activemon]["dimmed"] = False
for m in monrecord.keys():
curr_idle = monrecord[m]["idle"]
print(m, curr_idle)
if all([
curr_idle > wait,
monrecord[m]["dimmed"] is not True,
m != activemon
]):
set_mon_dimmed(m, True)
monrecord[m]["dimmed"] = True
else:
if m != activemon:
monrecord[m]["idle"] = curr_idle + res
idle1 = idle2
How to set up
Set up is straightforward:
-
Make sure you have both
python3-gi
andxprintidle
installedsudo apt install python3-gi xprintidle
Copy the script above into an empty file, save it as
dim_inactive
, and make it executable-
Run it by the command:
/path/to/dim_inactive <idle_time_in_seconds>
an example:
/path/to/dim_inactive 120
will dim all screens where the mouse is not after two minutes
Additional information / explanation
- The script lists all screens on startup
- It keeps record if idle time per monitor (possibly more than 2). If a monitor isn't accessed for x seconds, it is blacked out, apart from the monitor where the mouse is.
- According to a good (but bad) tradition, Gnome keeps breaking stuff and keeps changing API's. As a result, running this script on 19.04 and above, You'll get a few deprecated warnings. At the same time, it doesn't break on PEP8. Will nevertheless update to the latest API's.
Solution 3:
For years I had my laptop setup such that when lid is closed laptop would suspend and external monitors would go blank.
For your reason of wanting to watch a video for 90 minutes on external monitor and have laptop screen go blank I changed lid close option to "Do Nothing":
- Advantage: When I close Laptop lid all Laptop windows go below full screen video.
- Advantage: When I open Laptop lid windows are restored and are no longer below full screen video.
- Disadvantage: I have to make video non-full screen to access top bar menu to select suspend from gear menu.
- Advantage: When system is suspended by menu on external monitor, opening laptop lid still resumes system.
I'm not using DPMS for external monitors but you could check your settings with xset q
command:
$ xset q
Keyboard Control:
auto repeat: on key click percent: 0 LED mask: 00000002
XKB indicators:
00: Caps Lock: off 01: Num Lock: on 02: Scroll Lock: off
03: Compose: off 04: Kana: off 05: Sleep: off
06: Suspend: off 07: Mute: off 08: Misc: off
09: Mail: off 10: Charging: off 11: Shift Lock: off
12: Group 2: off 13: Mouse Keys: off
auto repeat delay: 500 repeat rate: 33
auto repeating keys: 00ffffffdffffbbf
fadfffefffedffff
9fffffffffffffff
fff7ffffffffffff
bell percent: 50 bell pitch: 400 bell duration: 100
Pointer Control:
acceleration: 5/1 threshold: 5
Screen Saver:
prefer blanking: yes allow exposures: yes
timeout: 0 cycle: 0
Colors:
default colormap: 0xb3 BlackPixel: 0x0 WhitePixel: 0xffffff
Font Path:
/usr/share/fonts/X11/misc,/usr/share/fonts/X11/Type1,built-ins
DPMS (Energy Star):
Standby: 0 Suspend: 0 Off: 0
DPMS is Disabled
Notice these lines:
Screen Saver:
prefer blanking: yes
- You would likely want
prefer blanking: no
Also notice these lines:
DPMS (Energy Star):
Standby: 0 Suspend: 0 Off: 0
DPMS is Disabled
- You would likely want
DPMS is enabled
to set monitor toStandby
when desired.
Hopefully other users have used these options and post a detailed answer for you.
Solution 4:
How about simply closing the laptop?
Why?
These two monitors are one display area so turning one off will create a number of issues like screen redrawing, applications moving to the main monitor, ...
(I went down that road a few years ago and the only reliable way I found of doing what you want to do is to push the button on the external monitor or close the laptop)
Just ensure you set these power settings with gsettings set
:
org.gnome.settings-daemon.plugins.power lid-close-suspend-with-external-monitor false
org.gnome.settings-daemon.plugins.power lid-close-ac-action 'nothing'
org.gnome.settings-daemon.plugins.power lid-close-battery-action 'nothing'