153 lines
4.1 KiB
Python
153 lines
4.1 KiB
Python
from abc import ABC, abstractmethod
|
|
from ctypes import WINFUNCTYPE, byref, windll
|
|
from ctypes.wintypes import BOOL, HWND, INT, LPARAM, LPPOINT, LPRECT, POINT, RECT
|
|
import time
|
|
import math
|
|
|
|
user32 = windll.user32
|
|
gdi32 = windll.gdi32
|
|
|
|
user32.GetSystemMetrics.argtypes = [INT]
|
|
|
|
user32.GetCursorPos.argtypes = [LPPOINT]
|
|
user32.SetCursorPos.argtypes = [INT, INT]
|
|
|
|
CB_ENUM_WINDOWS = WINFUNCTYPE(BOOL, HWND, LPARAM)
|
|
|
|
user32.EnumWindows.argtypes = [CB_ENUM_WINDOWS, LPARAM]
|
|
|
|
user32.GetWindowRect.argtypes = [HWND, LPRECT]
|
|
user32.SetWindowPos.argtypes = [HWND, INT, INT, INT, INT, INT, INT]
|
|
user32.IsWindowVisible.argtypes = [HWND]
|
|
|
|
class BouncyObject(ABC):
|
|
@abstractmethod
|
|
def get_position(self) -> tuple[int, int]:
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def set_position(self, xy: tuple[int, int]):
|
|
raise NotImplementedError
|
|
|
|
|
|
class Mouse(BouncyObject):
|
|
def get_position(self) -> tuple[int, int]:
|
|
point = POINT()
|
|
user32.GetCursorPos(byref(point))
|
|
return point.x, point.y
|
|
|
|
def set_position(self, xy: tuple[int, int]):
|
|
x, y = xy
|
|
user32.SetCursorPos(x, y)
|
|
|
|
|
|
class Window(BouncyObject):
|
|
def __init__(self, handle: int):
|
|
self._handle = handle
|
|
|
|
def get_position(self) -> tuple[int, int]:
|
|
rect = RECT()
|
|
user32.GetWindowRect(self._handle, byref(rect))
|
|
return (rect.left, rect.bottom)
|
|
|
|
def set_position(self, xy: tuple[int, int]):
|
|
rect = RECT()
|
|
user32.GetWindowRect(self._handle, byref(rect))
|
|
width = rect.right - rect.left
|
|
height = rect.bottom - rect.top
|
|
x, y = xy
|
|
user32.SetWindowPos(
|
|
self._handle, 0, x, y - height, -1, -1,
|
|
0x0010 | # SWP_NOACTIVATE
|
|
0x0001 | # SWP_NOSIZE
|
|
0x0004 | # SWP_NOZORDER
|
|
0x4000 # SWP_ASYNCWINDOWPOS
|
|
)
|
|
|
|
|
|
def enumerate_windows() -> list[int]:
|
|
windows: list[int] = []
|
|
|
|
@CB_ENUM_WINDOWS
|
|
def _handle_window(hwnd, lparam):
|
|
if user32.IsWindowVisible(hwnd):
|
|
windows.append(hwnd)
|
|
return True
|
|
|
|
user32.EnumWindows(_handle_window, 0)
|
|
return windows
|
|
|
|
|
|
def get_resolution_y() -> int:
|
|
SM_CYSCREEN = 1
|
|
return user32.GetSystemMetrics(SM_CYSCREEN)
|
|
|
|
|
|
class Body(object):
|
|
def __init__(self, controlled_object: BouncyObject):
|
|
self._last_xy: tuple[int, int] | None =None
|
|
self._last_offset: tuple[float, float] = (0.5, 0.5)
|
|
self._velocity = 0.0
|
|
self._controlled_object = controlled_object
|
|
|
|
def update(self, res_y):
|
|
x, y = self._controlled_object.get_position()
|
|
ox, oy = self._last_offset
|
|
x += ox
|
|
y += oy
|
|
|
|
if self._last_xy:
|
|
last_x, last_y = self._last_xy
|
|
dx = x - last_x
|
|
dy = y - last_y
|
|
dist = math.sqrt(dx * dx + dy * dy)
|
|
|
|
# if they move the mouse they can fight it
|
|
# (but they have to move it a lot)
|
|
self._velocity *= 1.0 - min(dist / 128, 1.0)
|
|
|
|
for _ in range(100):
|
|
self._velocity += 0.0002
|
|
|
|
y += self._velocity
|
|
if y > res_y and self._velocity > 0:
|
|
self._velocity = -self._velocity * 0.75
|
|
if abs(self._velocity) < 0.05:
|
|
self._velocity = 0
|
|
|
|
self._controlled_object.set_position((int(x), int(y)))
|
|
self._last_xy = (int(x), int(y))
|
|
self._last_offset = (x % 1, y % 1)
|
|
|
|
|
|
class Bodies(object):
|
|
def __init__(self):
|
|
self._mouse = Body(Mouse())
|
|
self._windows = {}
|
|
|
|
def update_list(self):
|
|
windows2 = {}
|
|
for i in enumerate_windows():
|
|
if existing := self._windows.get(i):
|
|
windows2[i] = existing
|
|
else:
|
|
windows2[i] = Body(Window(i))
|
|
self._windows = windows2
|
|
|
|
def get(self) -> list[Body]:
|
|
return [self._mouse, *self._windows.values()]
|
|
|
|
|
|
def main():
|
|
bodies = Bodies()
|
|
|
|
while True:
|
|
res_y = get_resolution_y()
|
|
bodies.update_list()
|
|
print(len(bodies.get()));
|
|
for b in bodies.get():
|
|
b.update(res_y)
|
|
time.sleep(1/120.0)
|
|
|
|
if __name__ == "__main__":
|
|
main() |