14

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
Ravexina
  • 55,668
  • 25
  • 164
  • 183
  • Is it for energy reasons, or would a visual solution do? – Jacob Vlijm Mar 28 '19 at 11:38
  • @Ravexina Question, how far does your eDP1 backlight go ? Does it turn off brightness all the way ? If yes, could you please test the two commands I had in https://askubuntu.com/a/814043/295286 answer ? Thank you in advance for the response – Sergiy Kolodyazhnyy Mar 28 '19 at 21:58
  • @JacobVlijm Thank you for your great answer. It's mostly for energy saving so I would prefer something like xrandr --off over --brightness. Do you think that it's possible to change the script in a way that it can detect when was the last time we interacted with a monitor? so based on that it can decide which display should go blank. suppose user is using only eDP1 for [N] minutes (Mouse+Keyboard) and VGA1 goes blank because user does not have any interaction with it. – Ravexina Mar 29 '19 at 00:09
  • @SergiyKolodyazhnyy xbacklight -set 0 seems to turn off brightness all the way however xrandr --brighness 0 does not it's just a running black screen. I tried the commands that you have mentioned in your other answer, and as you might already guess they won't work on my machine: Service 'org.gnome.SettingsDaemon.Power' does not exist.. I should thank you for your answer and time as well :) – Ravexina Mar 29 '19 at 00:09
  • @Ravexina You're very welcome, it was somewhat fun digging through xset source code. Now, as far as your question goes, I'm pretty sure there should be a way, since it is eDP1 monitor name, and that type of device is usually controlled via i2c bus. In theory if we know which bytes to send over i2c, we could disable/enable the device. I'll try to find other solutions available, but there's not much else at this point. As for Jacob's answer, replace subprocess.Popen(["xrandr", "--output", mon, "--brightness", val]) with subprocess.Popen(["xrandr", "--output", mon, "--off"]) – Sergiy Kolodyazhnyy Mar 29 '19 at 03:45
  • @Ravexina everything is possible :) Will post somewhere today or latest tomorrow. – Jacob Vlijm Mar 29 '19 at 06:23
  • 1
    @Ravexina done, seems to work flawlessly, but will keep it running for a day, will post it late tonight. It will keep record on activity per screen, but never dim/switch off screen wher the mouse currently is, right? – Jacob Vlijm Mar 29 '19 at 07:59
  • @JacobVlijm Thanks Jacobe :) I don't really care about the mouse, what I really care about is when was the last interaction with the screen (using keyboard or mouse) Then I want to dim the one which user has no interaction with for a while. Because I might left the mouse on external screen and start typing for hours in VIM on my main screen. However I guess having track of mouse would be be a great option for when we are watching something thus there is no interaction but we don't want the screen where the mouse currently is go blank. – Ravexina Mar 29 '19 at 08:38
  • @JacobVlijm Small hint: tracking mouse isn't a bulletproof way, you probably want to track windows ( and events they received ) on each screen. Because the user could have a window as top-most, currently active on one screen and mouse pointer over the window on another screen. – Sergiy Kolodyazhnyy Mar 29 '19 at 11:38
  • @SergiyKolodyazhnyy I think I'll pick the mouse location as current screen, not to dim. last keyboard or mouse action is recording idle time to where the mouse is. If the window is on screen 1, but I move the mouse on screen b, opening a menu, I'd still consider b as active screen. Most important is I think the behaviour should be clear and consequent. Can't test anything atm, not at home... – Jacob Vlijm Mar 29 '19 at 11:46
  • 1
    OK, well I'll probably try to implement Window tracking on the weekend then. Sadly I don't have that much time to deal with these things right now. – Sergiy Kolodyazhnyy Mar 29 '19 at 12:01
  • 1
    @Ravexina, thanks. I will probably post another (vala) version soon, triggered by either active window or (if there is no valid window) by mouse action. Also will see if we can do something with backlight, which will probably also save some energy. The time during bonus simply is too short, but the question is intersting enough to play a little further. To be continued. Thanks once again. – Jacob Vlijm Mar 31 '19 at 14:51
  • @JacobVlijm I'm looking forward to that :) – Ravexina Mar 31 '19 at 16:02
  • Hi Ravexina, could you join me here: https://chat.stackexchange.com/rooms/91869/discussion-between-jacob-vlijm-and-ravexina – Jacob Vlijm Apr 02 '19 at 16:12

5 Answers5

10

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.

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
  • I like the alternatives... +1 – Fabby Mar 26 '19 at 22:47
  • 1
    @Fabby Thanks :) So far that's the best alternatives available – Sergiy Kolodyazhnyy Mar 26 '19 at 23:04
  • I think the Window Managers are doing some tricks and might provide some hooks. There is a stack exchange site called "Software Design" and it might be a good place to float the idea that when a major window (like a web browser but not Conky or top bar displaying time) has no pixel changes over say 10 minutes the whole monitor (just a single one) starts to dim over 30 seconds until it's completely black. Then navigating the mouse over that monitor (I typically use 3 monitors) restores brightness gradually over a 1 to 5 second period. – WinEunuuchs2Unix Mar 31 '19 at 20:26
  • @WinEunuuchs2Unix I have an idea and there are a few things to watch for, but it's tricky, and yes depends on window manager in question. Problem is the DPMS. It's either dim everything or nothing with DPMS. Best we can do is xrandr --off for now – Sergiy Kolodyazhnyy Mar 31 '19 at 20:39
  • My DPMS knowledge is limited. I think it's a save electricity thing for standy/sleep. There is also HDMI-CEC which might give same results but that is even more of a black hole of magic to me. I don't know if xrandr -off even turns off the monitor. It might be the same as brightness set to zero? Which is another issue I have a program running 24/7 which sets brightness and gamma individually for 3 monitors which even Windows can't do. Not sure if night light, red shift or flux do it. Anyway those programs might get messed up by xrandr off. Sorry for hijacking your answer with speculating:) – WinEunuuchs2Unix Mar 31 '19 at 21:09
  • @WinEunuuchs2Unix No, xrandr --off definitely turns off the monitor. I've two VGA monitors with sounds, and when I use that option it turns them off with an audible pop sound from the internal speakers of the monitor. flux does use xrandr as well in very peculiar way, or at least they might use the XRandr library, not the command itself – Sergiy Kolodyazhnyy Mar 31 '19 at 21:40
  • Well without an IR Blaster or something I don't see how a 15 pin SVGA analogue signal can turn off a TV. But you are the electronic engineer university student genius in this Ubuntu family so I'll take your word for it :P – WinEunuuchs2Unix Mar 31 '19 at 22:10
5

Temporary comment

  1. 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.
  2. 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:

  1. Make sure you have both python3-gi and xprintidle installed

    sudo apt install python3-gi xprintidle
    
  2. Copy the script above into an empty file, save it as dim_inactive, and make it executable

  3. 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.
Jacob Vlijm
  • 83,767
  • Very nice, almost tempted to write something like that as well. But I guess that's the repeated theme here - so far there's nothing better than dimming the other display via software solutions, nothing hardware-ish – Sergiy Kolodyazhnyy Mar 28 '19 at 21:51
  • @SergiyKolodyazhnyy Nah, only backlight would add something, but that's it as far as I can see now. Think something should be possible, but will probably have a cold start, just like xrandr --off I guess. – Jacob Vlijm Mar 28 '19 at 21:54
  • @JacobVlijm Yeah, xrandr --off is what I'd prefer in this case. Especially since VGA backlight cannot be software-controlled. – Sergiy Kolodyazhnyy Mar 28 '19 at 22:00
  • 1
    Thank you for all your efforts, But I'm getting the weirdest behaviors... For example: It dim the monitors but does not reset the brightness back to 1 at any circumstances. When I'm interacting with external display using keyboard and while mouse is in the main display, the external one goes black after idle time. When I don't have any interact with main display but mouse is there it does nothing there and when I move the mouse to other display main one imminently goes black. – Ravexina Mar 30 '19 at 09:13
  • @Ravex (1) are your monitors mirrored? If not, you might have stopped the script with second screen balnked. Fixed that now. (2) Why active screen is defined my mousepos is explained in [2] of the temporary comment (3) Should also be fixed with the changes in (1) (same cause) Huh, no, need to reset idle time, will do that later today. – Jacob Vlijm Mar 30 '19 at 09:50
  • @Ravexina ok, should be fixed now. As mentioned, mouse pos defines active monitor. – Jacob Vlijm Mar 30 '19 at 10:16
  • I stumbled across a similar Python script you posted about 2 years ago except you also had a Bash equivalent which many users might appreciate: https://askubuntu.com/questions/756663/turn-off-external-monitor-when-inactive Congrats on bounty :) – WinEunuuchs2Unix Mar 31 '19 at 18:44
4

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 to Standby when desired.

Hopefully other users have used these options and post a detailed answer for you.

3

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'
Fabby
  • 34,259
  • Actually, this is a nice solution. Hardware, plus this can be scripted. However, OP is using Openbox, which suggests gsettings might not take effect. For good alternative, you may want to include https://unix.stackexchange.com/a/52645/85039 – Sergiy Kolodyazhnyy Mar 27 '19 at 00:32
0

If the monitor has ddc that can work - it's relatively slow, too, but about the only way to reduce power in a ccfl backlit monitor without xrandr. Unfortunately only half of my monitors implement it.

$ ddccontrol dev:/dev/i2c-4
...
        > Power control
                > id=dpms, name=DPMS Control, address=0xd6, delay=-1ms, type=2
                  Possible values:
                        > id=on - name=On, value=1
                        > id=standby - name=Standby, value=4
...

You can easily connect i2c buses to outputs looking at the /sys/class/drm/card* directories Sergiy mentioned above (i. e. their i2c* subdirs).

So, "ddccontrol dev:/dev/i2c-4 -r 0xd6 -w 4" sets it to Standby, "ddccontrol dev:/dev/i2c-4 -r 0xd6 -w 1" sets it to On again.

I have cronjobs set up including getIdle from https://github.com/IonicaBizau/screensaver/blob/master/getIdle.c which gives the X idle time in ms.

You should probably add your user to the i2c group or otherwise enable access to the ddc buses.

One other quirk: while the ddc control is labeled "Power", at least with my monitor I can't use it to determine the actual power status - if I turn the monitor off manually, it still says "On" and I can't turn it on via i2c.

aoe
  • 1