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?

4
  • I'm running 14.04, if that matters.
    – Wilson F
    Dec 24, 2014 at 16:23
  • I would like all directions for both moving and resizing, but I'll take what I can get. : )
    – Wilson F
    Dec 24, 2014 at 17:59
  • 1
    Been away from my computer for a few days, and I'm just back now. I'll give it a try and let you know.
    – Wilson F
    Dec 29, 2014 at 16:45
  • I just updated my question to include information - about how it works in Windows - that I forgot to mention originally.
    – Wilson F
    Dec 29, 2014 at 23:01

2 Answers 2

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.

7
  • @WilsonF The explanation is optional :) (it should work without). Dec 29, 2014 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, 2014 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, 2014 at 20:13
  • @WilsonF AHA, rotated? that is interesting. I will take a look myself too, curious to how that behaves. Dec 29, 2014 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, 2014 at 20: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...

2
  • 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, 2014 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, 2014 at 14:03

You must log in to answer this question.

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