【问题标题】:Xlib center windowXlib 中心窗口
【发布时间】:2012-02-10 16:02:34
【问题描述】:

我正在编写一个Xlib 应用程序,我希望窗口居中。我已经将XMoveWindow(desktopWidth - width) / 2, (desktopHeight - height) / 2 一起使用,它大致在正确的位置。

但问题是宽度和高度是客户区域,而不是总面积。有什么办法让我得到窗口的总面积?

我需要使用Xlib,因为我使用的是GlxOpenGL。我不想使用SDL,也不想使用庞大的图形库。

【问题讨论】:

  • 我怀疑一般来说这是可能的。一些窗口管理器(Compiz?)完全独立于客户端窗口绘制窗口装饰。客户端不知道也不知道装饰的大小(除非它使用一些 Compiz 特定的技巧)。在大多数其他 WM 中,客户端窗口的父级或祖父级都是 WM 装饰器窗口,您可以检查它以了解装饰器的大小。您仍然需要在客户区致电XMoveWindow,但您可以调整装饰大小。
  • @n.m.您可以在 XSetWindowAttributes 中使用 override_redirect 来防止 WM 装饰。
  • @luserdroog:是的,但这不是问题......

标签: linux x11 xlib


【解决方案1】:

有多种方法可以解决此问题,具体取决于您这样做的原因。前两个被大多数窗口管理器“官方支持”并在规范中进行了描述,然后它变成了脆弱的黑客。

语义

规范鼓励您使用 _NET_WM_WINDOW_TYPE 而不是设置位置,如果这样做有意义的话。见http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html#id2507144

例如,DIALOG 类型(或带有 WM_TRANSIENT_FOR 提示集的窗口)通常会在其父窗口或屏幕上居中,而 _NET_WM_WINDOW_TYPE_SPLASH(启动画面)类型通常会在屏幕上居中。 “通常”这里的意思是“明智的窗口管理器可能会居中,使用奇怪的窗口管理器的人不是你的问题,让他们受苦。”

(沿同一行的另一个提示是_NET_WM_STATE_FULLSCREEN,虽然不是您想要的,但它避免了手动调整大小/定位以全屏显示。)

如果语义提示有效,则处理定位的窗口管理器代码有望比任何可以轻松手动编写的代码更智能,例如它可能处理多头设置。设置正确的语义类型还可以让 WM 在其他方面变得智能,而不是定位。

重力

如果规范中没有对您有帮助的语义提示,那么您可以手动居中。重要的是要注意窗口管理器可以忽略手动位置请求,其中一些会。如果您在 WM_NORMAL_HINTS 中设置了 USPosition 标志,有些可能只接受请求(该标志应该仅在用户明确请求位置时设置,例如使用 -geometry 命令行选项)。其他人可能总是忽略该请求。但是,您可能会忽略执行此操作的 WM;用户选择使用该 WM。

补偿窗口装饰(标题栏等)的方式是使用 WM_NORMAL_HINTS 的 win_gravity 字段,该字段最初在 ICCCM 中(参见 http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.2.3),但在 EWMH 的实现说明中更好地指定:http://standards.freedesktop.org/wm-spec/latest/ar01s09.html#id2570420

WM_NORMAL_HINTS 见http://tronche.com/gui/x/xlib/ICC/client-to-window-manager/wm-normal-hints.html#XSizeHints(注意:属性类型是WM_SIZE_HINTS,属性名称是WM_NORMAL_HINTS,所以涉及到两个不同的原子名称)。

要居中,您可以将 win_gravity 设置为 Center,这允许您定位窗口的中心(包括其装饰)而不是左上角。

win_gravity 不经常使用,并且可能在某些窗口管理器中存在错误,因为没有人费心对其进行编码/测试,但它应该可以在更主流的窗口管理器中使用。

更新,可能的混淆点:X 协议中还有其他“重力”,特别是 CreateWindow 请求让您设置“bit_gravity”和“win_gravity”;这些与 XSizeHints.win_gravity 不同。 CreateWindow 重力描述了在调整窗口大小时如何处理窗口的内容(像素/子窗口)。

基于猜测装饰尺寸的技巧

这是一个脆弱的技巧,但是......您可以尝试找出装饰尺寸,然后将其纳入您的定位中。

要获取窗口装饰的大小,一种方法是 _NET_FRAME_EXTENTS 提示,请参阅http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2569862

对于老式的窗口管理器(但不是那些花哨的新合成器,尽管那些希望支持 _NET_FRAME_EXTENTS),窗口装饰是一个 X 窗口,因此您可以获取父窗口并查看它的大小。

这两种方法的问题是你必须在添加装饰之前映射窗口,所以你必须映射;等待获取 MapNotify 事件;然后得到装饰尺寸;然后移动窗口。不幸的是,这将导致用户可见的闪烁(窗口最初会出现然后移动)。我认为没有先映射就可以获取窗口装饰大小的方法。

进一步深入到可怕的黑客领域,您可以假设对于您映射的第一个窗口之后的窗口,装饰将匹配先前映射的窗口。 (并不是说这是一个合理的假设:不同种类的窗户可能有不同的装饰。)

实现说明:请记住,装饰窗口可以随时被销毁,这将在您提到该窗口的任何未完成的 Xlib 请求中导致 X 错误,并默认退出您的程序。为避免这种情况,请在触摸不属于您的客户端的窗口时设置 X 错误处理程序。

覆盖重定向

使用覆盖重定向是一种具有不良副作用的火箭筒,如果您的目标只是使窗口居中,则根本不是一个好主意。

如果您在创建窗口时设置了覆盖重定向标志,则窗口管理器将不会管理其大小、位置、堆叠顺序、装饰或地图状态(窗口管理器对 ConfigureRequest 和 MapRequest 的重定向被覆盖)。

对于用户认为是窗口的任何东西来说,这都是一个非常糟糕的主意。它通常用于工具提示和弹出菜单。如果你在一个窗口上设置了覆盖重定向,那么所有正常的窗口管理 UI 都会被破坏,堆叠顺序将基本上是随机的(窗口往往会卡在顶部或底部,或者更糟的是陷入无限循环与另一个客户重新战斗)。

但是,覆盖重定向的窗口不会有装饰,也不会被 WM 触摸,因此您可以确保将其居中而不会受到干扰。

(如果你不想要任何装饰,请使用 SPLASH 之类的语义类型或使用“MWM”提示,不要使用覆盖重定向。)

总结

简短的回答是设置语义提示(如果有),否则使用 XSizeHints.win_gravity=Center。

您可以看到为什么人们使用工具包和 SDL ;-) 在客户端到窗口管理器的交互中通常有很多奇怪的历史包袱和极端案例,设置窗口位置只是兴奋的开始!

【讨论】:

  • 我不认为重力与装饰的补偿有关。
  • 之所以这样做是因为它会影响包括框架在内的整个窗口。请参阅standards.freedesktop.org/wm-spec/latest/ar01s09.html#id2570420CenterGravity 将参考点设置为“frame 窗口的中心”。您可能会想到传递给 XCreateWindow() 的位重力和窗口重力,这与调整窗口大小时如何影响窗口内容有关;这些与 WM_NORMAL_HINTS 中的重力场无关。令人困惑的是 XSetWindowAttributes.win_gravity 和 XSizeHints.win_gravity 使用相同的 win_gravity 名称,但它们是不同的东西。
  • 啊,谢谢。我不知道。出于某种原因,我确信 WM 是根据客户端窗口而不是框架窗口来做这些事情的。
【解决方案2】:

win_gravity 不经常使用,并且可能在某些窗口管理器中存在错误,因为没有人费心对其进行编码/测试,但它应该可以在更主流的窗口管理器中使用。

显然 Unity 还没有实现这一点。测试表明 XCB_GRAVITY_STATIC 不受尊重,通过快速查看 Unity 源代码,我找不到实现规范这一部分的代码。

【讨论】:

  • 我看到有人给我的答案打了-1。我的答案不是一个疯狂的猜测,我对此进行了调查并在此处创建了一个错误报告bugreports.qt.io/browse/QTBUG-67757
猜你喜欢
  • 2012-09-10
  • 2011-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多