4

In Windows (since at least XP, probably before), if you use the keyboard to move or resize a Window, you can get very precise locations and sizes – even more than with a mouse – by using the Ctrl key in combination with the arrow keys (after having invoked the Move or Resize functionality from the window menu [Alt+Space]) to move the window borders a pixel at a time.

Is there a way to do the same (or a similar) thing in Ubuntu?

Wilson F
  • 445
  • 5
  • 20

2 Answers2

5

Moving/resizing windows by 1px

Assuming you are using Unity, the script below moves or resizes windows by 1 px. The script can be run with 8 different arguments. Depending on the move/re-size options you want to use, you can add the commands to a shortcut key combination. An overview of options and the corresponding commands below:

enter image description here


Exceptions/limitations

There are a few limitations:

  • gnome-terminal windows can only be re-sized in steps. As a result increasing/decreasing the window size by 1px does not work with gnome-terminal.
  • The window to be moved/re-sized needs to be at least a few px from both the Unity launcher and the top panel.

How to use

  • First install wmctrl, which is needed to get the window geometry and to move the window.

    sudo apt-get install wmctrl
    
  • Create a directory ~/bin (so in your home directory)

  • Copy the script below into an empty file, save it as move_window (no extension)
  • Make it executable (right-click on the file > Properties > Permissions (tab) , tick "allow execute")

    To test, open a terminal window and run subsequently:

    move_window l
    move_window r
    move_window u
    move_window d
    

    Since the terminal window is the front-most, it should move 1px to the left/right/up/down.

    (As mentioned, resizing does not work with gnome-terminal)

  • If it works correctly, add the commands to shortcut keys;
    choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the commands to four different shortcut key combinations. That might be tricky, since the commands you mentioned are probably occupied. What worked on my system:

    • Shift+Ctrl+arrow key left
    • Shift+Ctrl+arrow key right
    • Shift+Ctrl+arrow key up
    • Shift+Ctrl+arrow key down

    for the move actions.
    For the resize actions you'll have to try additional combinations.

The script

#!/usr/bin/env python3
import subprocess
import sys
# calibration
cal = 4
# direction, as argument from user input (l, r, u, d / h+, h-, v+, v-)
direction = sys.argv[1]
# move step size 
mv = -1 if  direction in ["l", "d", "h-", "v-"] else 1

def get(command):
    return subprocess.check_output(["/bin/bash", "-c", command])\
           .decode("utf-8")

def execute(command):
    subprocess.call(["/bin/bash", "-c", command])
# find the top shift (height of the panel = resolution - working area)
res_output = get("xrandr").split(); idf = res_output.index("current")
res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", "")))[-1]
topshift = int(res) - int(get("wmctrl -d").split()[8].split("x")[-1])+cal
# find frontmost window
def get_windowid():
    cmd = "xprop -root"
    frontmost = [l for l in get(cmd).splitlines() if\
                 "ACTIVE_WINDOW(WINDOW)" in l][0].split()[-1]
    return frontmost[:2]+"0"+frontmost[2:]
# get window geometry, create move command
set_w = [w.split()[0:6] for w in get("wmctrl -lG").splitlines()\
          if get_windowid() in w][0]
set_w[0] = "wmctrl -ir "+set_w[0]+" -e 0"
set_w.pop(1)

if direction in ["l", "r"]:
    set_w[1] = str(int(set_w[1])+mv); set_w[2] = str(int(set_w[2])-topshift)  
elif direction in ["u", "d"]:
    set_w[2] = str(int(set_w[2])-topshift-mv) 
elif direction in ["v-", "v+"]:
    set_w[2] = str(int(set_w[2])-topshift); set_w[4] = str(int(set_w[4])+mv)
elif direction in ["h-", "h+"]:
    set_w[2] = str(int(set_w[2])-topshift); set_w[3] = str(int(set_w[3])+mv)

execute((",").join(set_w))

Note

There is a little difference in the way wmctrl reports the window geometry, and the way wmctrl sets the window geometry. In the first case it calculates from the full screen (resolution), in the second case only from the working area (??). Even then, The script had to "calibrate" 4 px vertically, for which I found no satisfying explanation. The good news is that on different computers, I saw no difference in the deviation.

If in your case the window makes unexpected jumps, leave a comment.


Explanation

How it works

  1. The front-most window is looked up with the help of xprop:

    xprop -root
    

    Somewhere in the (extensive) output, there is a line like:

    _NET_ACTIVE_WINDOW(WINDOW): window id # 0x4600a8d
    

    from which our window-id can be parsed: 0x4600a8d. Since the format is a bit different from wmctrl, we need to add a zero on the third position: 0x04200085

  2. The window id is used to look up the window and its current geometry data, in the output of wmctrl -lG. Once we have the correct line, the data we have on the window looks like:

    0x04200085  0 322  52   823  998  <computer_name> <window_name> 
    

    where column 2, 3, 4, 5 are subsequently:

    • x coordinate of the top left corner of the window
    • y coordinate
    • width of the window
    • height of the window
  3. by manipulating these figures, we can move/resize the window with the command:
    (example to move the window to the right by 1 px, changing "322" into "323")

    wmctrl -ir 0x04200085 -e 0,323,52,823,998
    

There are a few complications to deal with, but that is basically how it works.

Jacob Vlijm
  • 83,767
  • @WilsonF The explanation is optional :) (it should work without). – Jacob Vlijm Dec 29 '14 at 20:11
  • I've certainly learned a ton from following your explanation and stepping through the code to see what's going on. It turns out wmctrl behaves even more weirdly when it has to deal with multiple and/or rotated monitors, both of which I have. (E.g. for a monitor rotated 90°, wmctrl's set command uses a different corner/direction than it displays!)

    So I'm going to continue to play with the commands you've demo'd, and I'll see if I can modify your script to work with the peculiarities of my own setup. In the meantime, I'll definitely accept your answer, as it works in the general case.

    – Wilson F Dec 29 '14 at 20:12
  • (Sorry, was editing my comment for too long and had to delete and re-post it. I'm much too long-winded.) – Wilson F Dec 29 '14 at 20:13
  • @WilsonF AHA, rotated? that is interesting. I will take a look myself too, curious to how that behaves. – Jacob Vlijm Dec 29 '14 at 20:16
  • Oh, I realize the explanation isn't necessary to use the script (unless you have a weird system like mine), but I'm trying to learn about the internals of Linux as I use it, so I wanted to understand what was going on. I really appreciate the glimpse into commands I hadn't seen before! – Wilson F Dec 29 '14 at 20:16
  • @WilsonF if you find the time, could you post what the behaviour is on your rotated screen? here it works as usual (relative to the rotation, single screen) – Jacob Vlijm Dec 29 '14 at 22:16
1

Apparently that doesn't work in XFCE either. I have an indirect idea that might work though...

I'd suggest trying Accessibility options for controlling the mouse with the keyboard, setting it to move very slowly, and use that to get the window to move a little bit at a time.

Each desktop seems to put the Accessibility options somewhere different, in XFCE it's under the general Settings Manager, but it shouldn't be hard to find in Unity & others...

Xen2050
  • 8,705
  • Thanks for the suggestion!

    As workarounds go, it's not too bad. It means turning on the feature (Unity Users: it's in System Settings -> Universal Access -> Mouse Keys ), moving and/or resizing the windows as necessary (I didn't have to adjust the amount of movement; in fact I don't see any way to do so), then turning off the feature so I can use my number pad again.

    I'll upvote your answer (well, when I get enough rep - I'm only 2 away!), but I won't tick it as the accepted answer for now, in case someone comes up with something a little more direct.

    – Wilson F Dec 24 '14 at 18:13
  • I like the built-in tools (most of) the DE's / WM's have, already installed & tested & should work... I'll bet there's one that has the windows-like "button to move slower" feature too, or there should be at least – Xen2050 Dec 25 '14 at 14:03