|
1 | 1 | #!/usr/bin/python |
2 | 2 | # -*- coding: utf-8 -*- |
3 | | - |
| 3 | +import atexit |
4 | 4 | import ctypes |
5 | 5 | import sys |
6 | 6 | import time |
@@ -314,19 +314,18 @@ def alwaysOnBottom(self, aob=True): |
314 | 314 | """ |
315 | 315 |
|
316 | 316 | if aob: |
317 | | - result = win32gui.SetWindowPos(self._hWnd, HWND_BOTTOM, 0, 0, 0, 0, |
318 | | - win32con.SWP_NOSIZE | win32con.SWP_NOMOVE | win32con.SWP_NOACTIVATE) |
319 | | - # there is no HWND_TOPBOTTOM (similar to TOPMOST), so it won't keep window below all others as desired |
320 | | - if self._t is None: |
321 | | - self._t = _sendBottom(self._hWnd) |
322 | | - self._t.daemon = True |
323 | | - if not self._t.is_alive(): |
324 | | - self._t.start() |
325 | | - # TODO: Catch win32con.WM_WINDOWPOSCHANGING and resend window to bottom (is it possible with pywin32?) |
326 | | - # https://stackoverflow.com/questions/527950/how-to-make-always-on-bottom-window |
| 317 | + result = win32gui.SetWindowPos(self._hWnd, win32con.HWND_BOTTOM, 0, 0, 0, 0, |
| 318 | + win32con.SWP_NOSENDCHANGING | win32con.SWP_NOOWNERZORDER | win32con.SWP_ASYNCWINDOWPOS | win32con.SWP_NOSIZE | win32con.SWP_NOMOVE | win32con.SWP_NOACTIVATE | win32con.SWP_NOREDRAW | win32con.SWP_NOCOPYBITS) |
| 319 | + if result != 0: |
| 320 | + # There is no HWND_TOPBOTTOM (similar to TOPMOST), so it won't keep window below all others as desired |
| 321 | + # Is there an easy way to catch WM_WINDOWPOSCHANGING event, unlike this: https://stackoverflow.com/questions/64529896/attach-keyboard-hook-to-specific-window? |
| 322 | + if self._t is None: |
| 323 | + self._t = _sendBottom(self._hWnd) |
| 324 | + if not self._t.is_alive(): |
| 325 | + self._t.start() |
327 | 326 | else: |
328 | 327 | if self._t.is_alive(): |
329 | | - self._t.stop() |
| 328 | + self._t.kill() |
330 | 329 | result = self.sendBehind(sb=False) |
331 | 330 | if result == 0: |
332 | 331 | _raiseWithLastError() |
@@ -428,19 +427,29 @@ def __init__(self, hWnd, interval=0.5): |
428 | 427 | threading.Thread.__init__(self) |
429 | 428 | self._hWnd = hWnd |
430 | 429 | self._interval = interval |
431 | | - self._stop = threading.Event() |
| 430 | + self._kill = threading.Event() |
| 431 | + |
| 432 | + def _isLast(self): |
| 433 | + # This avoids flickering and CPU consumption. Not very smart, but no other option found... by the moment |
| 434 | + h = win32gui.GetWindow(self._hWnd, win32con.GW_HWNDLAST) |
| 435 | + last = True |
| 436 | + while h != 0 and h != self._hWnd: |
| 437 | + h = win32gui.GetWindow(h, win32con.GW_HWNDPREV) |
| 438 | + # not sure if this always guarantees these other windows are "system" windows (not user windows) |
| 439 | + if h != self._hWnd and win32gui.IsWindowVisible(h) and win32gui.GetClassName(h) not in ("WorkerW", "Progman"): |
| 440 | + last = False |
| 441 | + break |
| 442 | + return last |
432 | 443 |
|
433 | 444 | def run(self): |
434 | | - |
435 | | - while not self._stop.is_set() and win32gui.IsWindow(self._hWnd): |
436 | | - # TODO: Find a smart way (not a for) to get if this is necessary (window is not already at the bottom) |
437 | | - # Window flickers a bit. All these parameters are intended to minimize it... with limited success |
438 | | - win32gui.SetWindowPos(self._hWnd, win32con.HWND_BOTTOM, 0, 0, 0, 0, |
439 | | - win32con.SWP_NOSENDCHANGING | win32con.SWP_NOOWNERZORDER | win32con.SWP_ASYNCWINDOWPOS | win32con.SWP_NOSIZE | win32con.SWP_NOMOVE | win32con.SWP_NOACTIVATE | win32con.SWP_NOREDRAW | win32con.SWP_NOCOPYBITS) |
440 | | - self._stop.wait(self._interval) |
441 | | - |
442 | | - def stop(self): |
443 | | - self._stop.set() |
| 445 | + while not self._kill.is_set() and win32gui.IsWindow(self._hWnd): |
| 446 | + if not self._isLast(): |
| 447 | + win32gui.SetWindowPos(self._hWnd, win32con.HWND_BOTTOM, 0, 0, 0, 0, |
| 448 | + win32con.SWP_NOSENDCHANGING | win32con.SWP_NOOWNERZORDER | win32con.SWP_ASYNCWINDOWPOS | win32con.SWP_NOSIZE | win32con.SWP_NOMOVE | win32con.SWP_NOACTIVATE | win32con.SWP_NOREDRAW | win32con.SWP_NOCOPYBITS) |
| 449 | + self._kill.wait(self._interval) |
| 450 | + |
| 451 | + def kill(self): |
| 452 | + self._kill.set() |
444 | 453 |
|
445 | 454 |
|
446 | 455 | def cursor(): |
|
0 commit comments