如何移动或调整 X11 窗口大小(即使它们被最大化)?

7 投票
1 回答
4825 浏览
提问于 2025-04-18 07:30

我想要改变一个窗口的位置。我的问题是,这个窗口可以被最大化,这样就无法改变它的大小和位置(这个窗口可能属于任何应用程序)。我使用的是KDE4。


我尝试使用ewmh这个Python模块。在我的情况下,当窗口最大化时,我只是想把它从一个显示器移动到另一个显示器,同时保持它最大化。我需要先取消最大化才能设置它的位置,所以我尝试使用ewmh.setWmState()_NET_WM_STATE_MAXIMIZED_VERT_NET_WM_STATE_MAXIMIZED_HORZ设置为0,然后调用ewmh.display.flush()

有时候我可以设置一个之前最大化的窗口的位置和大小,有时候又不行。出于某种原因,这个方法并不总是有效,尤其是在我刚从单个显示器切换到三个显示器时,最大化的窗口更容易出现问题,使用xrandr时尤其如此。

请注意,取消最大化总是能成功,但之后改变窗口的位置(或大小)往往不行。


我也尝试在终端中进行这个操作。这是重现我问题的最简单方法。首先,获取某个最大化窗口的窗口ID($WID)。然后:

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

但是第二个命令在我手动移动或调整窗口大小之前不会有任何效果。与ewmh不同,wmctrl在窗口最大化时从来没有按预期工作。wmctrl可以成功取消最大化窗口,但之后无法改变它的位置或大小。

这个问题在单个和三个显示器的X屏幕状态下都是可以重现的。

这个问题似乎并不特定于某个工具。例如,xdotool也无法在窗口刚取消最大化但没有手动移动或调整大小的情况下改变窗口的大小或位置。


到目前为止,我找到的唯一可靠的解决方法是手动取消窗口的最大化,或者使用ewmhwmctrl,然后手动调整窗口的大小或稍微移动一下。只有这样,我才能始终使用ewmhwmctrl移动或调整它的大小。但显然,这并不是一个可接受的解决方案。

有没有可靠的方法可以设置窗口的大小和位置,即使窗口当前是最大化的?我更倾向于使用Python的方法,但用命令行的解决方案也可以。

1 个回答

8

感谢n.m.的评论,我找到了一个解决方案。以下是我Python 脚本中的相关部分(这个脚本可以保存和恢复所有窗口的状态和大小,所以这个例子会把所有窗口还原到未最大化的状态):

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

执行完这段代码后,你就可以为任何窗口设置大小和位置了。all_win是一个包含所有窗口的列表,这些窗口用自定义类对象表示,并且这些对象里填充了来自ewmh.getClientList()的数据。每个窗口的对象通过client.xwin = disp.create_resource_object("window", client.id)来创建。等待窗口的显示和隐藏操作完成是很重要的,否则结果可能不可靠。此外,还需要限制尝试次数,以防某个窗口意外地被显示或隐藏,导致无限循环。


如果你不想一次性重新配置很多窗口,使用Python的xlib模块来隐藏和显示窗口并没有明显的性能提升,这时候使用xdotool会更简单:

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

如果你想在shell脚本中设置窗口的大小和位置,下面的例子应该可以工作,即使窗口是最大化的状态:

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

撰写回答