Automatically reduce resolution to that of the smallest monitor upon connecting new monitor?

I have a monitor and a TV I use sometimes. The monitor is 4k, used at 2560x1440 resolution - except when the TV is connected I need to manually reduce the resolution to 1080, and then manually set it back to 1440 after disconnecting the TV.

xrandr output looks like:

DVI-I-1 connected (normal left inverted right x axis y axis)
   1920x1080     60.00 +  59.94    23.98    60.05    60.00  
   1440x480      60.05  
   1280x720      60.00    59.94  
   720x480       59.94  
   640x480       59.93  
HDMI-0 connected primary 2560x1440+0+0 (normal left inverted right x axis y axis) 597mm x 336mm
   3840x2160     60.00 +  59.94    29.97    25.00    23.98  
   2560x1440     59.95* 
   2048x1280     60.00  
   1920x1080     60.00    59.94    50.00    25.00    23.98    60.00    50.04  
   1600x1200     60.00  
   1600x900      60.00  
   1280x1024     75.02    60.02  
   1280x720      59.94  
   1152x864      75.00  
   1024x768      75.03    60.00  
   800x600       75.00    60.32  
   720x576       50.00  
   720x480       59.94  
   640x480       75.00    59.94    59.93  

Solution 1:

Change resolution on signal

With the help of Gdk, we can act on several signals, a.o. on connecting/disconnecting monitors. If we connect both the monitor-added and monitor-removed -signal (Gdk.Display) to update the monitor resolution automatically, depending on the TV being attached, we're done.

In a script

#!/usr/bin/env python3
import gi
gi.require_version("Gdk", "3.0")
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk
import subprocess

triggermon = "DVI-I-1" # if this one is connected, reduce resolution
tochange = "HDMI-0" # the monitor to lower resolution on
lowres = "1920x1080" # the low resolution
highres =  "3840x2160" # the default resolution


class WatchOut:
    def __init__(self):
        self.gdkdsp = Gdk.Display.get_default()
        # use connect/disconnect signales
        self.gdkdsp.connect("monitor_added", self.act_onchange)
        self.gdkdsp.connect("monitor_removed", self.act_onchange)
        # make sure initial configuration is correct
        self.act_onchange()
        # we need a gtk thread to use the signals
        Gtk.main()

    def act_onchange(self, *args):
        # let's default to highres
        curr_res = highres
        # get attached n-monitors, lower res if triggermon is connected
        n_mons = self.gdkdsp.get_n_monitors()
        for n in [n for n in range(self.gdkdsp.get_n_monitors())]:
            mon_name = self.gdkdsp.get_monitor(n).get_model()
            if mon_name == triggermon:
                curr_res = lowres
        # set resolution
        subprocess.Popen([
            "xrandr", "--output", tochange, "--mode", curr_res
        ])               
        
WatchOut()

Set up

  • Copy the script into an empty file, save it as actonmonitor.py and make it executable (if you run it without calling the interpreter explicitely).

  • The monitor names are in the head of the script, and so the resolutions you mentioned, change them if needed.

  • Test- run it in a terminal window.

      /path/to/actonmonitor.py
    
  • See if all works as it should.

  • Add it to your startup applications if you like.

N.B.

The exact position of the secundary monitor in the monitor layout is not in the (your) output of xrandr. If the position matters, you probably need to add an additional xrandr command to the script.