2

I'm coming from Windows 10, although I've started (and accepted) the way Ubuntu works, their are still a few things that I love about Windows 10, the feature in question here is the way If I snap one windows to the extreme right side of display it takes up the right half portion of the display (which is also similar in ubuntu) but another app in background takes the left half of display and doesn't need to manually do that.

Screenshot

In this picture, if I snap the Browser windows to the right, Nautilus window wouldn't affect by the action, But I want it (nautilus window) to snap to left

Details

  • Ubuntu 17.04
  • Gnome flavour

Update

result of the fillscreen.py script

1st result

in first try fillscreen notification came (I couldn't capture that) and it moved the 2nd window (window in the right) in a box, and 1st window wasn't affected at all.

2nd result

in second try, this offset was present, but it did work (and it mostly works)

1 Answers1

1

Important note!

The script below will do exactly as you describe on the two youngest windows, that is: the two windows that were last created.

The script, behavior

  • The script acts on dragging one of the two "newest" windows to one of the two areas on the screen, as shown in the image.

    enter image description here

    The area is deliberately not tight into the corner, to make sure it does not interfere with "normal"window snapping.

  • If the window is dragged on to either one of the areas, the script waits 0.15 second to see if the mouse is still in the same position, to make sure not to act if the user was "on his way" to the corner of the screen for normal window snapping.

  • Subsequently, the dragged window is snapped into the half of the screen the area is on, the second window is snapped to the opposite side of the screen

    1. drag the window to the area

    enter image description here

    2. the window snaps, the other one snaps to the opposite site

    enter image description here

  • finally, as a confirmation, a notification shows during three seconds:

    enter image description here

    See the script in action

The script & setup

The setup involves two items:

  • the script:

    #!/usr/bin/env python3
    import sys
    import os
    import subprocess
    import time
    from operator import itemgetter
    from itertools import groupby
    import math
    
    #--- set your preferences below: padding between windows, margin(s)
    cols = 2; rows = 1; padding = 20; left_margin = 0; top_margin = 30
    #---
    
    fpath = os.path.dirname(os.path.abspath(__file__))
    n_wins = cols*rows
    
    
    def get_spot(pos):
        # get the resolution
        scrdata = get("xrandr").split(); resindex = scrdata.index("connected")+2
        res = [int(n) for n in scrdata[resindex].split("+")[0].split("x")]
        # list the corners, could be more elegant no doubt
        corners = [[0, res[1]], [res[0], res[1]]]
        diff = [int(math.sqrt(sum([(c[i]-pos[i])**2 for i, n in enumerate(res)])))\
                for c in corners]
        return diff
    
    def get(cmd):
        try:
            return subprocess.check_output(cmd).decode("utf-8")
        except subprocess.CalledProcessError:
            pass
    
    def get_res():
        xr = get("xrandr").split(); pos = xr.index("current")
        return [int(xr[pos+1]), int(xr[pos+3].replace(",", "") )]
    
    def get_pos():
        return [int(s.split(":")[1]) for s in get(["xdotool", "getmouselocation"]).split()[:2]]
    
    
    def check_window(w_id):
        w_type = get(["xprop", "-id", w_id])
        if " _NET_WM_WINDOW_TYPE_NORMAL" in w_type:
            return True
        else:
            return False
    
    def confirm():
        val = False
        mouseloc = get_spot(get_pos())
        match = [mouseloc.index(n) for n in mouseloc if 50 < n < 400]
        if match:
            time.sleep(0.15)
            val = True if get_spot(get_pos()) == mouseloc else False
        return val, match
    
    def arrange_wins(active, side):
        # get resolution
        res = get_res()
        # define (calculate) the area to divide
        area_h = res[0] - left_margin; area_v = res[1] - top_margin
        # create a list of calculated coordinates
        x_coords = [int(left_margin+area_h/cols*n) for n in range(cols)]
        y_coords = [int(top_margin+area_v/rows*n) for n in range(rows)]
        coords = sum([[(cx, cy) for cx in x_coords] for cy in y_coords], [])
        # calculate the corresponding window size, given the padding, margins, columns and rows
        w_size = [str(int(area_h/cols - padding)), str(int(area_v/rows - padding))]
        # find windows of the application, identified by their pid
        active = hex(int(get(["xdotool", "getactivewindow"])))
        active = active[:2]+(10-len(active))*"0"+active[2:]
        wlist = [w.split()[0] for w in get(["wmctrl", "-l"]).splitlines()]
        w_list = [w for w in wlist if check_window(w) == True][-n_wins:]
        try:
            w_list = w_list[::-1] if w_list.index(active) != side else w_list
        except ValueError:
            pass
        else: 
            print(w_list)
            # remove possibly maximization, move the windows
            for n, w in enumerate(w_list):
                data = (",").join([str(item) for item in coords[n]])+","+(",").join(w_size)
                cmd1 = "wmctrl -ir "+w+" -b remove,maximized_horz"
                cmd2 = "wmctrl -ir "+w+" -b remove,maximized_vert"
                cmd3 = "wmctrl -ir "+w+" -e 0,"+data
                for cmd in [cmd1, cmd2, cmd3]:
                    subprocess.Popen(["/bin/bash", "-c", cmd])
    
    wins1 = []
    
    while True:
        time.sleep(0.5)
        windata = get(["wmctrl", "-lG"])
        if windata:
            wins2 = [[l[0], l[2]] for l in [
                ln.split() for ln in windata.splitlines()]
                       ]
            # combined window locations old/new, grouped to see if moved
            winlocs = sorted(wins1 + wins2, key = itemgetter(0))
            test = [[item, [item[1] for item in list(occ)]] \
                    for item, occ in groupby(winlocs, itemgetter(0))]
            for item in test:
                # old loc, new loc of window
                locs = item[1]
                # window moves?
                if locs.count(locs[0]) != len(locs):
                    args = confirm()
                    if args[0]:
                        arrange_wins(item[0], args[1][0])
                        subprocess.Popen([
                            "notify-send", "-i", os.path.join(
                                fpath, "left.png"), "Fill screen"
                            ])
                        time.sleep(3)
                        subprocess.Popen(["pkill", "notify-osd"])
            wins1 = wins2
    
  • an icon to show in the notification

    enter image description here

Setup

  1. Install both xdotooland wmctrl
  2. Copy the script into an empty file, save it as fillscreen.py in a dedicated folder somewhere.
  3. Right- click on the icon above, save it as (exactly) left.png in one and the same folder as the script.
  4. Now open a terminal, run the command:

    python3 /path/to/fillscreen.py
    

    Note that this terminal window is one of the two windows the script will snap. Draf the terminal to either one of the areas on the left or right. The two most recent windows should snap.

  5. If all works fine, add the script to startup applications: Dash > Startup Applications > Add. Add the command:

    /bin/bash -c "sleep 10 && python3 /path/to/fillscreen.py"
    

Note

Due to the fact that the script only acts on window movement, and subsequently all further actions are dependent on the situation, the script is very low on juice. Much lower then I expected it to be when I started working on it.

Jacob Vlijm
  • 85,475