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() |