2

In the current workspace, I don't want to see the edge of a window I was working with on another workspace.

Also I like being able to shove a window mostly off screen so forcing bounds wouldn't be a good solution.

Is there a way to just not show a window if it isn't part of the current workspace?

6
  • Refer the best answer of this question askubuntu.com/questions/34400/…
    – Tung Tran
    Dec 4, 2015 at 4:28
  • The excerpt linked to : " but I don't think there's any way to hide applications running on another workspace at the moment. Maybe this is a feature you could request by filing a wishlist bug?"
    – Seph Reed
    Dec 4, 2015 at 4:30
  • Yep, there is, but it would be a background script. Would that interest you? Interesting question. Dec 4, 2015 at 14:09
  • @Jacob Yeah of course! As long as it has some way to hook into window or workspace events so it isn't running all the time.
    – Seph Reed
    Dec 4, 2015 at 16:26
  • That would be impossible I am afraid, there is no event to hook on (there is of course, but not externally). In this case, a script does not need to add noticeable background noise however I expect. If it would, I wouldn't post it. Still interested? Dec 4, 2015 at 16:38

1 Answer 1

2

How to prevent workspace- overlapping windows

I believe the solution below is doing what you describe. This is what it does:

The usual effect: overlapping windows will appear on the adjacent workspaces

enter image description here

Effectively, when pressing a key combination, this will be the result

enter image description here

In practice:

An example

  • Working on (e.g.) workspace 1, with some windows overlapping on other workspaces
  • Then move to workspace 2, press the shortcut key combination: all windows, except the ones on the current workspace, will be minimized, and thus not appear on the current workspace anyway (except for the launcher).
  • Then returning to workspace 1, pressing the key combination again, the desktop will be exactly like you left it. Even de window order (z-wise) and possibly minimized windows will be exactly like it was. At the same time, windows on other than the current workspace will be hidden.

How it works

The solution includes two scripts; one background script, that keeps track on the z-order of the windows (since we do not have tools to get it otherwise), and a script to minimize the windows and keep track on which windows already were minimized by the user.

Why two scripts?

Initially, I had the script combined in one, and it seemed to work fine. However, on my system, it increased the (idle) processor occupation from 3-4% to appr. 9-11%, which is too much for a background script in my opinion, especially when you run multiple scripts at the same time.
The script is now split into a background section that keeps track on the focus- history (to be able to un- minimize the windows in the same z- order as you left the workspace), and a script to call with a keyboard shortcut. The background script adds practically no background noise.

How to set up

  1. The script(s) need both wmctrl and xdotool:

    sudo apt-get install wmctrl xdotool
    
  2. Copy script1 below into an empty file, safe it as focus_history.py:

    #!/usr/bin/env python3
    import subprocess
    import time
    import os
    
    rootdata = os.environ["HOME"]+"/.focus_history"
    
    def current_windows():
        try:
            return subprocess.check_output(["wmctrl", "-l"]).decode("utf-8")
        except subprocess.CalledProcessError:
            pass
    
    def convert_format(w_id):
        return w_id[:2]+(10-len(w_id))*"0"+w_id[2:]
    
    def read_data():
        return open(rootdata).read().splitlines()
    
    def get_top(wlist):
        try:
            top = convert_format(
                [l.split("#")[-1].strip() for l in subprocess.check_output(
                    ["xprop", "-root"]
                    ).decode("utf-8").splitlines() \
                   if "_NET_ACTIVE_WINDOW(WINDOW)" in l][0])       
            return [l for l in wlist if top in l][0]
        except IndexError:
            pass
    
    open(rootdata, "wt").write("This is an empty line")
    
    while True:
        time.sleep(0.5)
        wdata = current_windows()
        if wdata != None:
            wlist = wdata.splitlines()
            # get frontmost window (as in wmctrl -lG)
            top = get_top(wlist)
            oldlist = read_data()
            if not any([top == oldlist[0], top == None]):
                # clean up closed windows
                [oldlist.remove(l) for l in oldlist if not l.split()[0] in wdata]
                # remove possible other mentions of the active window
                [oldlist.remove(l) for l in oldlist if l.startswith(top.split()[0])]
                open(rootdata, "wt").write(("\n").join([top]+oldlist))
    
  3. Copy script2 below into an empty file, safe it as stop_overlap.py:

    #!/usr/bin/env python3
    import subprocess
    import time
    import os
    
    wfile = os.environ["HOME"]+"/.m_list"
    rootdata = os.environ["HOME"]+"/.focus_history"
    
    def get_res():
        # get the resolution (workspace- size)
        data = subprocess.check_output(["xrandr"]).decode("utf-8").split()
        mark = data.index("current")
        return [int(n) for n in [data[mark+1], data[mark+3].replace(",", "")]]
    
    res =  get_res()
    
    def get_wlist(res):
        try:
            # get the window data
            wlist = [l.split() for l in subprocess.check_output(
                ["wmctrl", "-lG"]).decode("utf-8").splitlines()]
            # check if windows are "normal" windows and see if they are minimized
            show = []; hide = []
            for w in wlist:
                w_data = subprocess.check_output(
                    ["xprop", "-id", w[0]]
                    ).decode("utf-8")
                quality = [
                    "_NET_WM_WINDOW_TYPE_NORMAL" in w_data,
                    "_NET_WM_STATE_HIDDEN" in w_data,
                    ]
                # check if windows are on current workspace or elsewhere
                onthis = all([0 < int(w[2]) < res[0],
                        0 < int(w[3]) < res[1]])
                # summarize what should be done with the windows
                if all([quality == [True ,True], onthis == True]):
                    show.append(w[0])
                elif all([quality == [True, False], onthis == False]):
                    hide.append(w[0])
            return [show, hide, [l[0] for l in wlist]]
        except subprocess.CalledProcessError:
            pass
    
    oncurrent = []; onother = []; d_wlist = []
    wins = get_wlist(res)
    
    for w in wins[1]:
        # hide (minimize) windows on other workspacec -only if- they are not hidden already!
        subprocess.Popen(["xdotool", "windowminimize", w])
        # write hidden windows to a file, so the script will only un- minimize windows
        # that were not hidden in the first place
        open(wfile, "a+").write("\n"+w)
    if wins[0]:
        # if there are windows on the current workspace that need to be un- minimized,
        # show them in the correct z- order, as recorded by the other script
        priority = reversed([l.split()[0] for l in open(rootdata).read().splitlines()])
        try:
            d_wlist = [l for l in open(wfile).read().splitlines() if not l == "\n"]
        except FileNotFoundError:
            d_wlist = []
        for w in priority:
            if all([w in wins[0], w in d_wlist]):
                subprocess.Popen(["wmctrl", "-ia", w])
                time.sleep(0.1)
                d_wlist.remove(w)
        # clean up window list, remove non- existant windows
        d_wlist = set([item for item in d_wlist if item in wins[2]])
        open(wfile, "wt").write(("\n").join(d_wlist))
    
  4. Test- run the setup: before opening any other windows:

    • Run script1 from a terminal window by the command:

      python3 /path/to/focus_history.py
      
    • Now open some random windows, some overlapping your workspaces

    • Now move to the adjacent workspace, run script 2 with the command:

      python3 /path/to/stop_overlap.py
      

    The overlapping windows should disappear

    • Move back to the first workspace, run the last command again, your workspace should be restored exactly like it was

If all works fine, add script1 to Startup Applications: Dash > Startup Applications > Add. Add the command:

/bin/bash -c "sleep 15 && python3 /path/to/focus_history.py

Add script2 to a shortcut key: choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the command:

python3 /path/to/stop_overlap.py

to a shortcut of your choice...

2
  • this is really amazing. Especially as I'm new to Linux and while apt-get is something I've been doing on mac, being able to use python to do something like this is not. Thank you. And if all goes well, this code may become the basis for other future mods I do. It's the best starting point I've seen yet.
    – Seph Reed
    Dec 5, 2015 at 19:27
  • This answers my unasked question with: xprop -id 0x036c7766 | grep "_NET_WM_WINDOW_TYPE_NORMAL" to get REAL windows returned by wmctrl -lG which also answers a different unasked question on how to get window geometry returned. Nov 30, 2019 at 19:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .