Skip to content

Commit

Permalink
ALL: Included PyWinBox module which hopefully better handles multi-mo…
Browse files Browse the repository at this point in the history
…nitor setups (macOS tests pending)

LINUX: Fixed getClientFrame() (thanks to roym899), removed ewmh and pynput dependencies
WINDOWS: Fixed getAllWindows() not returning all relevant windows (thanks to Xenolphthalein)
MACOS: Fixed 'missing value' in window titles and app names (thanks to yjmd2222), fixed getClientFrame() to be invoked from non-main threads (thanks to super-iby)
  • Loading branch information
Kalmat committed Aug 21, 2023
1 parent 5c28207 commit 4670ffd
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 21 deletions.
Binary file modified dist/PyWinCtl-0.0.44-py3-none-any.whl
Binary file not shown.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
package_data={"pywinctl": ["src/pywinctl/py.typed"]},
test_suite='tests',
install_requires=[
"pywinbox>=0.2",
"pywinbox>=0.3",
"pywin32>=302; sys_platform == 'win32'",
"python-xlib>=0.21; sys_platform == 'linux'",
"pyobjc>=8.1; sys_platform == 'darwin'"
Expand Down
20 changes: 10 additions & 10 deletions src/ewmhlib/_ewmhlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def getDisplayFromRoot(rootId: int) -> Tuple[Xlib.display.Display, Struct, XWind
return defaultDisplay, defaultScreen, defaultRoot


def getProperty(window: XWindow, prop: Union[str, int, Root, Window],
def getProperty(window: XWindow, prop: Union[str, int],
prop_type: int = Xlib.X.AnyPropertyType,
display: Xlib.display.Display = defaultDisplay) -> Optional[Xlib.protocol.request.GetProperty]:
"""
Expand All @@ -175,7 +175,7 @@ def getProperty(window: XWindow, prop: Union[str, int, Root, Window],
return None


def changeProperty(window: XWindow, prop: Union[str, int, Root, Window], data: Union[List[int], str],
def changeProperty(window: XWindow, prop: Union[str, int], data: Union[List[int], str],
prop_type: int = Xlib.Xatom.ATOM, propMode: int = Xlib.X.PropModeReplace,
display: Xlib.display.Display = defaultDisplay):
"""
Expand Down Expand Up @@ -204,7 +204,7 @@ def changeProperty(window: XWindow, prop: Union[str, int, Root, Window], data: U
display.flush()


def sendMessage(winId: int, prop: Union[str, int, Root, Window], data: Union[List[int], str],
def sendMessage(winId: int, prop: Union[str, int], data: Union[List[int], str],
display: Xlib.display.Display = defaultDisplay, root: XWindow = defaultRoot):
"""
Send Client Message to given window/root
Expand Down Expand Up @@ -302,7 +302,7 @@ def __init__(self, root: Optional[XWindow] = None):
self.id: int = self.root.id
self.wmProtocols = self._WmProtocols(self.display, self.root)

def getProperty(self, prop: Union[str, int, Root, Window],
def getProperty(self, prop: Union[str, int],
prop_type: int = Xlib.X.AnyPropertyType) \
-> Optional[Xlib.protocol.request.GetProperty]:
"""
Expand All @@ -316,7 +316,7 @@ def getProperty(self, prop: Union[str, int, Root, Window],
prop = self.display.get_atom(prop)
return getProperty(self.root, prop, prop_type, self.display)

def setProperty(self, prop: Union[str, int, Root, Window], data: Union[List[int], str]):
def setProperty(self, prop: Union[str, int], data: Union[List[int], str]):
"""
Sets the given property for root
Expand All @@ -327,7 +327,7 @@ def setProperty(self, prop: Union[str, int, Root, Window], data: Union[List[int]
"""
sendMessage(self.root.id, prop, data, self.display, self.root)

def sendMessage(self, winId: int, prop: Union[str, int, Root, Window], data: Union[List[int], str]):
def sendMessage(self, winId: int, prop: Union[str, int], data: Union[List[int], str]):
"""
Sends a ClientMessage event to given window
Expand Down Expand Up @@ -1017,7 +1017,7 @@ def __init__(self, winId: int, root: XWindow = defaultRoot):

self._currDesktop = os.environ['XDG_CURRENT_DESKTOP'].lower()

def getProperty(self, prop: Union[str, int, Root, Window], prop_type: int = Xlib.X.AnyPropertyType) \
def getProperty(self, prop: Union[str, int], prop_type: int = Xlib.X.AnyPropertyType) \
-> Optional[Xlib.protocol.request.GetProperty]:
"""
Retrieves given property data from given window
Expand All @@ -1030,7 +1030,7 @@ def getProperty(self, prop: Union[str, int, Root, Window], prop_type: int = Xlib
prop = self.display.get_atom(prop)
return getProperty(self.xWindow, prop, prop_type, self.display)

def sendMessage(self, prop: Union[str, int, Root, Window], data: Union[List[int], str]):
def sendMessage(self, prop: Union[str, int], data: Union[List[int], str]):
"""
Sends a ClientMessage event to current window
Expand All @@ -1039,7 +1039,7 @@ def sendMessage(self, prop: Union[str, int, Root, Window], data: Union[List[int]
"""
return sendMessage(self.id, prop, data)

def changeProperty(self, prop: Union[str, int, Root, Window], data: Union[List[int], str],
def changeProperty(self, prop: Union[str, int], data: Union[List[int], str],
prop_type: int = Xlib.Xatom.ATOM, propMode: Mode = Mode.REPLACE):
"""
Sets given property for the current window. The property might be ignored by the Window Manager, but returned
Expand Down Expand Up @@ -1838,7 +1838,7 @@ def setMoveResize(self, gravity: int = 0, x: Optional[int] = None, y: Optional[i
gravity_flags = gravity_flags | (1 << 12)
else:
gravity_flags = gravity_flags | (1 << 13)
self.sendMessage(Root.MOVERESIZE, [gravity_flags, x, y, width, height])
self.sendMessage(Window.MOVERESIZE, [gravity_flags, x, y, width, height])

def setWmMoveResize(self, x_root: int, y_root: int, orientation: Union[int, MoveResize], button: int, userAction: bool = True):
"""
Expand Down
32 changes: 22 additions & 10 deletions src/pywinctl/_pywinctl_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,22 @@ def getClientFrame(self) -> Rect:
:return: Rect struct
"""
titleHeight, borderWidth = self._getBorderSizes()
rect = self.rect
ret = Rect(int(rect.left + borderWidth), int(rect.top - titleHeight),
int(rect.right - borderWidth), int(rect.bottom - borderWidth))
box = self.box
x = box.left
y = box.top
w = box.width
h = box.height
# Thanks to roym899 (https://github.com/roym899) for his HELP!!!!
_net_extents = self._win._getNetFrameExtents()
if _net_extents and len(_net_extents) >= 4:
x = x + _net_extents[0]
y = y + _net_extents[2]
w = w - (_net_extents[0] + _net_extents[1])
h = h - (_net_extents[2] + _net_extents[3])
ret = Rect(x, y, x + w, y + h)
else:
titleHeight, borderWidth = self._getBorderSizes()
ret = Rect(x + borderWidth, y + titleHeight, x + w - borderWidth, y + h - borderWidth)
return ret

def __repr__(self):
Expand Down Expand Up @@ -612,7 +624,6 @@ def sendBehind(self, sb: bool = True) -> bool:
def acceptInput(self, setTo: bool):
"""
Toggles the window to accept input and focus
WARNING: In Linux systems, this effect is not permanent (will work while program is running)
:param setTo: True/False to toggle window ignoring input and focus
:return: None
Expand All @@ -626,9 +637,10 @@ def acceptInput(self, setTo: bool):

onebyte = int(0xFF)
fourbytes = onebyte | (onebyte << 8) | (onebyte << 16) | (onebyte << 24)
self._xWin.change_property(self._display.get_atom('_NET_WM_WINDOW_OPACITY'), Xlib.Xatom.CARDINAL, 32, [fourbytes])
self._win.changeProperty("_NET_WM_WINDOW_OPACITY", [fourbytes], Xlib.Xatom.CARDINAL)

self._win.changeProperty(self._display.get_atom("_MOTIF_WM_HINTS"), self._motifHints)
if self._motifHints:
self._win.changeProperty("_MOTIF_WM_HINTS", self._motifHints)

self._win.setWmWindowType(Props.WindowType.NORMAL)

Expand All @@ -640,12 +652,12 @@ def acceptInput(self, setTo: bool):

onebyte = int(0xFA) # Calculate as 0xff * target_opacity
fourbytes = onebyte | (onebyte << 8) | (onebyte << 16) | (onebyte << 24)
self._xWin.change_property(self._display.get_atom('_NET_WM_WINDOW_OPACITY'), Xlib.Xatom.CARDINAL, 32, [fourbytes])
self._win.changeProperty("_NET_WM_WINDOW_OPACITY", [fourbytes], Xlib.Xatom.CARDINAL)

ret = self._win.getProperty(self._display.get_atom("_MOTIF_WM_HINTS"))
ret = self._win.getProperty("_MOTIF_WM_HINTS")
# Cinnamon uses this as default: [2, 1, 1, 0, 0]
self._motifHints = [a for a in ret.value] if ret and hasattr(ret, "value") else [2, 0, 0, 0, 0]
self._win.changeProperty(self._display.get_atom("_MOTIF_WM_HINTS"), [0, 0, 0, 0, 0])
self._win.changeProperty("_MOTIF_WM_HINTS", [0, 0, 0, 0, 0])

self._win.setWmWindowType(Props.WindowType.DESKTOP)

Expand Down

0 comments on commit 4670ffd

Please sign in to comment.