From aac7612e74bffd1c76da78bba47cf4841933704f Mon Sep 17 00:00:00 2001 From: Leonard Bruns Date: Fri, 6 Oct 2023 16:14:35 +0200 Subject: [PATCH 1/6] Adjust window box for GTK HeaderBar --- src/pywinbox/_pywinbox_linux.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/pywinbox/_pywinbox_linux.py b/src/pywinbox/_pywinbox_linux.py index 2ebbf22..62f4196 100644 --- a/src/pywinbox/_pywinbox_linux.py +++ b/src/pywinbox/_pywinbox_linux.py @@ -26,7 +26,29 @@ def _getWindowBox(handle: EwmhWindow) -> Box: # https://stackoverflow.com/questions/12775136/get-window-position-and-size-in-python-with-xlib geom = handle.xWindow.get_geometry() pos = handle.root.translate_coords(handle.id, 0, 0) - return Box(pos.x, pos.y, geom.width, geom.height) + + # check if application uses special title bar (a.k.a. GTK HeaderBar) + # see: https://docs.gtk.org/gtk4/class.HeaderBar.html + # if it has HeaderBar (e.g., gedit): + # top left will be top left including extra space GTK + # in that case, we subtract gtk extents to the right box + # if it doesn't (e.g., gvim): + # top left will be top left of client window (i.e., excluding the title bar) + # in that case, we add the title bar to get the right box + + _net_extents = handle._getNetFrameExtents() + if _net_extents and len(_net_extents) >= 4: # this means it has no GTK HeaderBar + x = pos.x - int(_net_extents[0]) + y = pos.y - int(_net_extents[2]) + w = geom.width + int(_net_extents[0]) + int(_net_extents[1]) + h = geom.height + int(_net_extents[2]) + int(_net_extents[3]) + else: + _gtk_extents = handle._getGtkFrameExtents() + x = pos.x + int(_gtk_extents[0]) + y = pos.y + int(_gtk_extents[2]) + w = geom.width - int(_gtk_extents[0]) - int(_gtk_extents[1]) + h = geom.height - int(_gtk_extents[2]) - int(_gtk_extents[3]) + return Box(x, y, w, h) def _moveResizeWindow(handle: EwmhWindow, newBox: Box): From ea2e1ad3570f0de7143c52cc39c34b9cd03ea56d Mon Sep 17 00:00:00 2001 From: Leonard Bruns Date: Fri, 6 Oct 2023 18:01:00 +0200 Subject: [PATCH 2/6] Fix comment --- src/pywinbox/_pywinbox_linux.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pywinbox/_pywinbox_linux.py b/src/pywinbox/_pywinbox_linux.py index 62f4196..cbb597a 100644 --- a/src/pywinbox/_pywinbox_linux.py +++ b/src/pywinbox/_pywinbox_linux.py @@ -30,7 +30,7 @@ def _getWindowBox(handle: EwmhWindow) -> Box: # check if application uses special title bar (a.k.a. GTK HeaderBar) # see: https://docs.gtk.org/gtk4/class.HeaderBar.html # if it has HeaderBar (e.g., gedit): - # top left will be top left including extra space GTK + # top left will be top left including extra space (shadows?) # in that case, we subtract gtk extents to the right box # if it doesn't (e.g., gvim): # top left will be top left of client window (i.e., excluding the title bar) From 5229606b012e65013d638956de042621a68bee64 Mon Sep 17 00:00:00 2001 From: Leonard Bruns Date: Sat, 7 Oct 2023 09:42:39 +0200 Subject: [PATCH 3/6] Check for gtk_extents and trust pos and geom otherwise --- src/pywinbox/_pywinbox_linux.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/pywinbox/_pywinbox_linux.py b/src/pywinbox/_pywinbox_linux.py index cbb597a..c614329 100644 --- a/src/pywinbox/_pywinbox_linux.py +++ b/src/pywinbox/_pywinbox_linux.py @@ -37,17 +37,28 @@ def _getWindowBox(handle: EwmhWindow) -> Box: # in that case, we add the title bar to get the right box _net_extents = handle._getNetFrameExtents() - if _net_extents and len(_net_extents) >= 4: # this means it has no GTK HeaderBar + _gtk_extents = handle._getGtkFrameExtents() + if _net_extents and len(_net_extents) >= 4: + # this means it has no GTK HeaderBar x = pos.x - int(_net_extents[0]) y = pos.y - int(_net_extents[2]) w = geom.width + int(_net_extents[0]) + int(_net_extents[1]) h = geom.height + int(_net_extents[2]) + int(_net_extents[3]) - else: + elif _gtk_extents and len(_gtk_extents) >= 4: + # this means there is a GTK HeaderBar _gtk_extents = handle._getGtkFrameExtents() x = pos.x + int(_gtk_extents[0]) y = pos.y + int(_gtk_extents[2]) w = geom.width - int(_gtk_extents[0]) - int(_gtk_extents[1]) h = geom.height - int(_gtk_extents[2]) - int(_gtk_extents[3]) + else: + # something else: best guess is to trust pos and geom from above + # NOTE: if you have this case and are not getting the expected result, + # please open an issue: https://github.com/Kalmat/PyWinBox/issues/new + x = pos.x + y = pos.y + w = geom.width + h = geom.height return Box(x, y, w, h) From 9df9c4dce0235fa4dc0d40c41236c668d914b0b4 Mon Sep 17 00:00:00 2001 From: Leonard Bruns Date: Sun, 8 Oct 2023 21:40:31 +0200 Subject: [PATCH 4/6] Do opposite changes extent adjustments in _moveResizeWindow --- src/pywinbox/_pywinbox_linux.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/pywinbox/_pywinbox_linux.py b/src/pywinbox/_pywinbox_linux.py index c614329..4ee6574 100644 --- a/src/pywinbox/_pywinbox_linux.py +++ b/src/pywinbox/_pywinbox_linux.py @@ -9,6 +9,7 @@ from Xlib.xobject.drawable import Window as XWindow from ._main import Box + from ewmhlib import EwmhWindow @@ -63,8 +64,26 @@ def _getWindowBox(handle: EwmhWindow) -> Box: def _moveResizeWindow(handle: EwmhWindow, newBox: Box): - newLeft = max(0, newBox.left) # Xlib won't accept negative positions - newTop = max(0, newBox.top) - handle.setMoveResize(x=newLeft, y=newTop, width=newBox.width, height=newBox.height, userAction=True) - # handle.configure(x=newLeft, y=newTop, width=newBox.width, height=newBox.height) + _net_extents = handle._getNetFrameExtents() + _gtk_extents = handle._getGtkFrameExtents() + if _net_extents and len(_net_extents) >= 4: + # this means it has no GTK HeaderBar + newLeft = newBox.left + newTop = newBox.top + newWidth = newBox.width + newHeight = newBox.height - int(_net_extents[2]) - int(_net_extents[3]) + elif _gtk_extents and len(_gtk_extents) >= 4: + newLeft = newBox.left - _gtk_extents[0] + newTop = newBox.top - _gtk_extents[2] + newWidth = newBox.width + int(_gtk_extents[0]) + int(_gtk_extents[1]) + newHeight = newBox.height + int(_gtk_extents[2]) + int(_gtk_extents[3]) + else: + newLeft = newBox.left + newTop = newBox.top + newWidth = newBox.width + newHeight = newBox.height + newLeft = max(0, newLeft) # Xlib won't accept negative positions + newTop = max(0, newTop) + handle.setMoveResize(x=newLeft, y=newTop, width=newWidth, height=newHeight, userAction=True) + # handle.configure(x=newLeft, y=newTop, width=newBox.width, height=newBox.height) From 2dbd98abcf39909c3f78c248d565a58ec219954c Mon Sep 17 00:00:00 2001 From: Leonard Bruns Date: Sun, 8 Oct 2023 21:43:19 +0200 Subject: [PATCH 5/6] Clarify _net_extents correction in _moveResizeWindow --- src/pywinbox/_pywinbox_linux.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pywinbox/_pywinbox_linux.py b/src/pywinbox/_pywinbox_linux.py index 4ee6574..87e2909 100644 --- a/src/pywinbox/_pywinbox_linux.py +++ b/src/pywinbox/_pywinbox_linux.py @@ -67,10 +67,10 @@ def _moveResizeWindow(handle: EwmhWindow, newBox: Box): _net_extents = handle._getNetFrameExtents() _gtk_extents = handle._getGtkFrameExtents() if _net_extents and len(_net_extents) >= 4: - # this means it has no GTK HeaderBar + # seems that top left includes window borders, but width and height does not newLeft = newBox.left newTop = newBox.top - newWidth = newBox.width + newWidth = newBox.width - int(_net_extents[0]) - int(_net_extents[1]) newHeight = newBox.height - int(_net_extents[2]) - int(_net_extents[3]) elif _gtk_extents and len(_gtk_extents) >= 4: newLeft = newBox.left - _gtk_extents[0] From 0219a6ec3146dd789ebd6304c0ffb1837d665f2d Mon Sep 17 00:00:00 2001 From: Leonard Bruns Date: Sun, 8 Oct 2023 21:52:30 +0200 Subject: [PATCH 6/6] Fix mypy errors --- src/pywinbox/_pywinbox_linux.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pywinbox/_pywinbox_linux.py b/src/pywinbox/_pywinbox_linux.py index 87e2909..b88d67a 100644 --- a/src/pywinbox/_pywinbox_linux.py +++ b/src/pywinbox/_pywinbox_linux.py @@ -47,7 +47,6 @@ def _getWindowBox(handle: EwmhWindow) -> Box: h = geom.height + int(_net_extents[2]) + int(_net_extents[3]) elif _gtk_extents and len(_gtk_extents) >= 4: # this means there is a GTK HeaderBar - _gtk_extents = handle._getGtkFrameExtents() x = pos.x + int(_gtk_extents[0]) y = pos.y + int(_gtk_extents[2]) w = geom.width - int(_gtk_extents[0]) - int(_gtk_extents[1]) @@ -73,8 +72,8 @@ def _moveResizeWindow(handle: EwmhWindow, newBox: Box): newWidth = newBox.width - int(_net_extents[0]) - int(_net_extents[1]) newHeight = newBox.height - int(_net_extents[2]) - int(_net_extents[3]) elif _gtk_extents and len(_gtk_extents) >= 4: - newLeft = newBox.left - _gtk_extents[0] - newTop = newBox.top - _gtk_extents[2] + newLeft = newBox.left - int(_gtk_extents[0]) + newTop = newBox.top - int(_gtk_extents[2]) newWidth = newBox.width + int(_gtk_extents[0]) + int(_gtk_extents[1]) newHeight = newBox.height + int(_gtk_extents[2]) + int(_gtk_extents[3]) else: