6

I'm using i3-wm on my ubuntu laptop. My screen size is 3200x1800. Which is great for something but horrible for other things. (Netflix vs gvim).

I'd like to change the resolution of one of my work-spaces, or multiple ones. But I have no idea if that is even possible... can I use compton to do this or maybe another program?

Jacob Vlijm
  • 83,767
Fetts Vett
  • 157
  • 1
  • 10
  • What you can do is change the resolution, depending on the current viewport or workspace. The change would however be the current resolution, since you have only one screen. would that help you? – Jacob Vlijm Feb 18 '15 at 20:26
  • Ideally I would like to have, say, workspace 1 on the 3200x1800, but workspace 2 on 1920x1080. And be able to switch between the two workspace's without changing the resolution through any commands or files. If that's what you mean. – Fetts Vett Feb 18 '15 at 20:29
  • 2
    That would be "kind of" possible, meaning the change would be automatically. However, in Unity, all workspaces (viewports) together are one big desktop, with one resolution setting. What can be done is to let a background script understand which is the current workspace and alter the reolution accordingly. That would possibly mess up a window arrangement on the different viewports. Not sure if that is helpful to you. – Jacob Vlijm Feb 18 '15 at 20:33
  • Actually, I think that would be exactly what I want. Since i3 is tiled, everything should retain the same arrangement etc. What would have to be done in this script? – Fetts Vett Feb 18 '15 at 20:37
  • 1
    The mechanism would be like this one http://askubuntu.com/a/560734/72216, only instead of changing the wallpaper, the resolution has to be changed by xrandr. I can look into it, but could you tell me your screen name (look for "connected" from xrandr), and the resolutions to be set on which viewport, and the number of viewports? (not sure I need it) – Jacob Vlijm Feb 18 '15 at 20:43
  • Awesome, thank you. Yeah, my screens name is "eDP1" and the workspaces are 1-10, with workspace 8,9,10 being set to 1920x1080 and 1-7 at 3200x1800 is what I'd like. – Fetts Vett Feb 18 '15 at 20:49

2 Answers2

3

1. Assuming you are on Unity

The script below should change your resolution, depending on the current viewport in Unity. I tested it on several computers for a while, and it ran without an error.

I'd suggest however to also test it for a while and see if it runs without a single break; repeated wmctrl commands can sometimes exit "non-zero" incidentally. If so, we need to build in a try / except.

Notes

  • I tested it on a single-monitor setup. multiple monitors would probably need another way of parsing the output of xrandr.
  • In the head of the script, you need to set the desired resolution for each of your viewports, I set it like mentioned in your comment, but you can change it anytime. Use the format:

    resolutions = [[<horizontal>, <vertical], [<horizontal>, <vertical], ......]
    

    like it shows in the script.

  • No need to say that you should use supported resolutions, as in the output of xrandr for your monitor.

How to use

  1. The script needs wmctrl:

    sudo apt-get install wmctrl
    
  2. copy the script below into an empty file, save it as screen_res.py

  3. Test run it for a while in a terminal window by the command:

    python3 screen_res.py
    
  4. If all works fine, add it to your startup applications: Dash > Startup Applications > Add...

The script

#!/usr/bin/env python3
import subprocess
import time

# list of resolution per viewport, for each viewport a separate [hor, vert]
# I pre- entered your viewports. quite some! listing takes more space than the script :)
resolutions = [
    [3200, 1800],
    [3200, 1800],
    [3200, 1800],
    [3200, 1800],
    [3200, 1800],
    [3200, 1800],
    [3200, 1800],
    [1920, 1080],
    [1920, 1080],
    [1920, 1080],
    ]

def get_xr():
    return subprocess.check_output(["xrandr"]).decode("utf-8").split()

check = get_xr()
plus = 2 if check[check.index("connected")+1] == "primary" else 1

while True:
    # resolution:
    xr = get_xr()    
    res = [int(n) for n in xr[xr.index("connected")+plus].split("+")[0].split("x")]
    # get current viewport
    vp_data = subprocess.check_output(["wmctrl", "-d"]).decode("utf-8").split()
    dt = [int(n) for n in vp_data[3].split("x")]
    cols = int(dt[0]/res[0])
    curr_vpdata = [int(n) for n in vp_data[5].split(",")]
    curr_col = int(curr_vpdata[0]/res[0])+1; curr_row = int(curr_vpdata[1]/res[1])
    curr_vp = curr_col+curr_row*cols
    # check and change resolution if needed
    if res != resolutions[curr_vp-1]:
        new_res = ("x").join([str(n) for n in resolutions[curr_vp-1]])
        subprocess.call(["xrandr", "-s", new_res])
    time.sleep(1)

Explanation

  • In a loop, the script first checks the screen's current resolution from the command:

    xrandr
    
  • Subsequently, the script checks the total desktop size (all viewports) from the command:

    wmctrl -d
    
  • Then, from the resolution i.c.w. the total desktop size, it calculates the number of columns, which is equal to the total desktop size, divided by the horizontal resolution.

  • Also in the output ofwmctrl -d is the position of the current viewport on the spanning desktop: e.g.: VP: 1680,0.
  • With this information we can conclude on which column & row we are, and check if the currently set resolution matches the resolution as we defined it in the list in the head section of the script.
    If not, the script gives the command to change the resolution with the command:

    xrandr -s <xres>x<yres>
    

2. XFCE version

  • set up like the version above (also needs wmctrl)
#!/usr/bin/env python3
import subprocess
import time

# list of resolution per viewport, for each viewport a separate [hor, vert]
# below just an example! set resolutions for your own screen
resolutions = [
    [1280, 1024],
    [1280, 960],
    [1280, 1024],
    [1280, 1024],
    ]

def get_xr():
    return subprocess.check_output(["xrandr"]).decode("utf-8").split()

check = get_xr()
plus = 2 if check[check.index("connected")+1] == "primary" else 1

while True:
    # resolution:
    xr = get_xr()    
    res = [int(n) for n in xr[xr.index("connected")+plus].split("+")[0].split("x")]
    # get current workspace
    vp_data = subprocess.check_output(["wmctrl", "-d"]).decode("utf-8").splitlines()
    curr_ws = int([l.split()[0] for l in vp_data if "*" in l][0])
    # check and change resolution if needed
    if res != resolutions[curr_ws]:
        new_res = ("x").join([str(n) for n in resolutions[curr_ws]])
        subprocess.call(["xrandr", "-s", new_res])
    time.sleep(1)
Jacob Vlijm
  • 83,767
  • I see you run your code in while-true loop. Isn't there some event you can listen to or some hook that can be triggered when desktop is changed? – umpirsky Feb 25 '15 at 20:18
  • @umpirsky have been looking for it, but never found one. At the same time both xrandr and wmctrl -d should be relatively light-weight when run once per second. Even on my 12 year old museum piece, I don't notice any effect (XFCE version). Maybe a small break between the two, of e.g. 0.5 sec., would make it even lighter. – Jacob Vlijm Feb 25 '15 at 20:29
1

I implemented @Jacob Vlijm's python script for XFCE. It worked well except for an old Windows program running through wine. Every time the script cycled, it would freeze the mouse cursor for less than a second. Checking 1x/sec meant a lot of stuttering. I ported it to a bash script, modifying it to only call wmctrl once per check and only call xrandr if the resolution needs to be changed. Note that it does require wmctrl to be installed.

#!/usr/bin/env bash
#Tested in XFCE 4.12.3 on Ubuntu 18.04
#Requires wmctrl. Install with:
#$ sudo apt-get install wmctrl

#Note: This checks current workspace res 1x/sec. To change rate, change "sleep" parameter below

#Enter appropriate resolution for each workspace, in order, in the same format as below.
RESOLUTIONS=('1920x1080' '1368x768')

check_and_change_res() {
  #echo "Parsing wmctrl -d"
  while read line; do
    #example line from wmctrl:
    #$ wmctrl -d
    #0  - DG: 1368x768  VP: N/A  WA: 0,25 1368x743  1
    #1  * DG: 1368x768  VP: 0,0  WA: 0,25 1368x743  2
    #If your line does not have a "DG:" (desktop geometry) preceding the current resolution, you
    #  will need to change the regex below.
    if [[ ${line} =~ ([[:digit:]]+)[[:space:]]+\*[[:space:]]+DG:[[:space:]]+([[:alnum:]]+) ]]; then
      current_ws="${BASH_REMATCH[1]}"
      current_res="${BASH_REMATCH[2]}"
      target_res="${RESOLUTIONS[current_ws]}"
      #echo "Current workspace: ${current_ws}; Current resolution: ${current_res}; Target resolution: ${target_res}"
      if [[ ! ${current_res} =~ ${target_res} ]]; then
        #echo "Switching resolution to ${target_res}."
        xrandr -s ${target_res}
      fi
    fi
  done
}

while true
do
  check_and_change_res < <(wmctrl -d)
  #This waits for 1 second between checks. To change to a 5-second delay (e.g. performance), use:
  #sleep 5
  sleep 1
done