Skip to content
Advertisement

How to move or resize X11 windows (even if they are maximized)?

I want to change position of a window. My problem is that the window can be maximized, this does not allow to change its size and location (window can belong to any application). I use KDE4.


I tried to use ewmh Python module. In my case when the window is maximized I just want to move it from one monitor to the other, keeping it maximized. I need to unmaximize it to set its geometry, so I tried to unmaximize it using ewmh.setWmState() to set _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MAXIMIZED_HORZ to 0, then ewmh.display.flush().

Sometimes I can configure the position and size of a previously maximized window, sometimes not. For some reason it does not work reliably, and is especially likely to fail for maximized windows if I just switched from a single-monitor to triple monitors with xrandr.

Please note that unmaximizing always works, what (often) does not work is changing window position (or size) afterwards.


I also tried doing this in the terminal. This is the easiest way to reproduce my problem. First, get the Window ID ($WID) for some maximized window. Then:

wmctrl -i -r $WID -b remove,maximized_vert,maximized_horz
wmctrl -i -r $WID -e 0,1280,50,1250,1250

But the second command does nothing unless I move or resize the window manually before running it. Unlike ewmh, wmctrl never works as expected if the window is maximized. wmctrl unmaximizes the window successfully but cannot change its position or size afterwards.

This is reproducible in both the single and triple monitor X screen states.

This problem seems to be not specific to any particular tool. For example, xdotool also fails to change window size/location if the window was just unmaximized but was not moved/resized manually.


The only reliable workaround I have found so far is to unmaximize the window, either manually, with ewmh or with wmctrl, and then manually change the window size or manually move it a bit. Only then I always can move or resize it with ewmh or wmctrl. But obviously this is not acceptable solution.

Is there reliable way to set window geometry even if the window is currently maximized? A method through Python is preferred, but a solution with shell commands will be fine too.

Advertisement

Answer

Thanks to n.m. comment I found a solution. Here are relevant parts from my python script (it saves and restores state and geometry of all windows, so this example unmaximizes, unmaps and maps all windows):

from time import sleep
from ewmh import EWMH
from Xlib import display, protocol, X
from Xlib.protocol.request import *
...
ewmh = EWMH()
disp = display.Display()
poll_interval = 0.025 # s
poll_attempts_limit = 10
...
def unmaximize(window):
  ewmh.setWmState(window, 0, "_NET_WM_STATE_MAXIMIZED_VERT")
  ewmh.setWmState(window, 0, "_NET_WM_STATE_MAXIMIZED_HORZ")
...
  for client in all_win:
    unmaximize(client.window)
  ewmh.display.flush()
  for client in all_win:
    client.xwin.unmap() 
  poll_attempts = 0
  for client in all_win:
    while client.xwin.get_attributes().map_state == X.IsViewable 
      and poll_attempts < poll_attempts_limit:
      sleep(poll_interval)
      poll_attempts += 1
  for client in all_win:
    client.xwin.map()   
  poll_attempts = 0
  for client in all_win:
    while client.xwin.get_attributes().map_state != X.IsViewable 
      and poll_attempts < poll_attempts_limit:
      sleep(poll_interval)
      poll_attempts += 1

After executing this code it is possible to set window geometry for any window. all_win is a list of all windows represented as list of custom class objects populated with data from ewmh.getClientList(). Each client.xwin = disp.create_resource_object("window", client.id). Waiting for mapping/unmapping to finish is important, otherwise it will be unreliable. Also, it is necessary to limit poll attempts to prevent infinite loop in case some window gets mapped or unmapped unexpectedly.


If you do not want to reconfigure a lot of windows at once, there is no noticeable performance improvement from using python xlib module for unmapping and mapping, it is easier to use xdotool instead:

from os import system
...
system("xdotool windowunmap --sync " + str(client.window.id))
system("xdotool windowmap   --sync " + str(client.window.id))

If you want to set window geometry in shell script the following example should work even if the window is maximized:

wmctrl -i -r $WID -b remove,maximized_vert,maximized_horz
xdotool windowunmap --sync $WID
xdotool windowmap   --sync $WID
wmctrl -i -r $WID -e 0,$x,$y,$width,$height
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement