From 16b3b6fa1e355a4a4264c8d53dc7b2e8fd3045ec Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Thu, 17 Apr 2025 21:51:04 -0700 Subject: [PATCH] Bouncy mouse, bouncy windows, initial code --- bounce_c/.gitignore | 2 + bounce_c/bounce.c | 161 ++++++++++++++++++++++++++++++++++++++++++++ bounce_c/run.bat | 3 + bounce_v1.py.old | 72 ++++++++++++++++++++ main.py | 153 +++++++++++++++++++++++++++++++++++++++++ main.py.old | 88 ++++++++++++++++++++++++ 6 files changed, 479 insertions(+) create mode 100644 bounce_c/.gitignore create mode 100644 bounce_c/bounce.c create mode 100644 bounce_c/run.bat create mode 100644 bounce_v1.py.old create mode 100644 main.py create mode 100644 main.py.old diff --git a/bounce_c/.gitignore b/bounce_c/.gitignore new file mode 100644 index 0000000..60e5a2c --- /dev/null +++ b/bounce_c/.gitignore @@ -0,0 +1,2 @@ +*.exe +*.pdb \ No newline at end of file diff --git a/bounce_c/bounce.c b/bounce_c/bounce.c new file mode 100644 index 0000000..a4ff37b --- /dev/null +++ b/bounce_c/bounce.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include + +#define MAX_WINDOWS 1024 + +// resolution +uint32_t res_y; +void globals_init() { + res_y = GetSystemMetrics(SM_CYSCREEN); +} + +// bodies +typedef struct body_t { + int32_t last_x; + int32_t last_y; + float offset_x; + float offset_y; + float velocity; +} body_t; + +void body_init(body_t* body) { + body->last_x = -1; + body->last_y = -1; + body->offset_x = 0.5; + body->offset_y = 0.5; + body->velocity = 0.0; +} + +void body_update(body_t* body, int32_t x, int32_t y, int32_t* x_new, int32_t* y_new) { + if (body->last_x != -1 && body->last_y != -1) { + int32_t dx = x - body->last_x; + int32_t dy = y - body->last_y; + double dist = sqrt((double) dx * dx + dy * dy); + body->velocity *= 1.0 - min(dist / 128, 1.0); + } + + float float_x = x + body->offset_x; + float float_y = y + body->offset_y; + for (uint8_t step = 0; step < 100; step += 1) { + body->velocity += 0.0002; + + float_y += body->velocity; + if (float_y > res_y && body->velocity > 0) { + body->velocity = -body->velocity * 0.75; + if (-body->velocity < 0.05) { + body->velocity = 0; + } + } + } + + *x_new = (int) float_x; + *y_new = (int) float_y; + body->last_x = *x_new; + body->last_y = *y_new; + body->offset_x = float_x - *x_new; + body->offset_y = float_y - *y_new; +} + +// == mouse == +typedef struct mouse_body_t { + body_t body; +} mouse_body_t; + +mouse_body_t mouse_body; + +void mouse_init() { + body_init(&mouse_body.body); +} + +void mouse_update() { + POINT xy; + GetCursorPos(&xy); + int32_t new_x, new_y; + body_update(&mouse_body.body, xy.x, xy.y, &new_x, &new_y); + SetCursorPos(new_x, new_y); +} + +// == window == +typedef struct window_body_t { + HWND window; + body_t body; +} window_body_t; + +size_t n_windows; +window_body_t* windows; +size_t _n_new_windows; +window_body_t* _new_windows; + +void window_init() { + n_windows = 0; + windows = calloc(sizeof(window_body_t), MAX_WINDOWS); + _n_new_windows = 0; + _new_windows = calloc(sizeof(window_body_t), MAX_WINDOWS); +} + +BOOL CALLBACK window_handle_enum(HWND window, LPARAM _) { + if (_n_new_windows >= MAX_WINDOWS) { + return 0; + } + if (!IsWindowVisible(window)) { + return 1; + } + window_body_t new; + new.window = window; + body_init(&new.body); + for (size_t i = 0; i < n_windows; i++) { + if (windows[i].window == new.window) { + new.body = windows[i].body; + break; + } + } + _new_windows[_n_new_windows++] = new; + return 1; +} + +void window_enumerate() { + EnumWindows(window_handle_enum, 0); + + // do the swap + window_body_t* tmp_windows = windows; + + n_windows = _n_new_windows; + windows = _new_windows; + + _n_new_windows = 0; + _new_windows = tmp_windows; +} + +void window_update() { + window_enumerate(); + + for (size_t i = 0; i < n_windows; i++) { + RECT rect; + GetWindowRect(windows[i].window, &rect); + int32_t x = rect.left; + int32_t y = rect.bottom; + + body_update(&windows[i].body, x, y, &x, &y); + + int32_t height = rect.bottom - rect.top; + uint32_t flags = SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_ASYNCWINDOWPOS; + SetWindowPos(windows[i].window, 0, x, y - height, -1, -1, flags); + } +} + +int main() { + globals_init(); + + mouse_init(); + window_init(); + + while (1) { + // mouse_update(); + window_update(); + + Sleep(1000/120); + } +} \ No newline at end of file diff --git a/bounce_c/run.bat b/bounce_c/run.bat new file mode 100644 index 0000000..db4d193 --- /dev/null +++ b/bounce_c/run.bat @@ -0,0 +1,3 @@ +del bounce.exe +zig cc bounce.c -o bounce.exe -Wall -Ofast +bounce.exe \ No newline at end of file diff --git a/bounce_v1.py.old b/bounce_v1.py.old new file mode 100644 index 0000000..f32d295 --- /dev/null +++ b/bounce_v1.py.old @@ -0,0 +1,72 @@ +from ctypes import POINTER, WINFUNCTYPE, byref, windll +from ctypes.wintypes import BOOL, HWND, INT, LPARAM, LPPOINT, POINT +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) + +def get_mouse() -> tuple[int, int]: + point = POINT() + user32.GetCursorPos(byref(point)) + x, y = point.x, point.y + return x, y + +def get_resolution() -> tuple[int, int]: + SM_CXSCREEN = 0 + SM_CYSCREEN = 1 + + return ( + user32.GetSystemMetrics(SM_CXSCREEN), + user32.GetSystemMetrics(SM_CYSCREEN) + ) + +def set_mouse(x: int, y: int): + user32.SetCursorPos(x, y) + + +class MouseBody(object): + def __init__(self): + self._last_mouse = None + self.velocity = 0.0 + + def update(self): + mouse_x, mouse_y = get_mouse() + if self._last_mouse: + last_x, last_y = self._last_mouse + dx = mouse_x - last_x + dy = mouse_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 / 64, 1.0) + + _, res_y = get_resolution() + self.velocity += 1 + + new_y = int(mouse_y + self.velocity) + + if new_y > res_y: + # simulate an inelastic collision + self.velocity = -self.velocity * 0.75 + new_y = int(new_y + self.velocity) + + set_mouse(mouse_x, new_y) + self._last_mouse = (mouse_x, new_y) + +def main(): + body = MouseBody() + while True: + time.sleep(1/120.0) + body.update() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..f4cbada --- /dev/null +++ b/main.py @@ -0,0 +1,153 @@ +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() \ No newline at end of file diff --git a/main.py.old b/main.py.old new file mode 100644 index 0000000..515ec15 --- /dev/null +++ b/main.py.old @@ -0,0 +1,88 @@ +from ctypes import POINTER, WINFUNCTYPE, WinError, create_unicode_buffer, get_last_error, windll +from ctypes.wintypes import BOOL, DWORD, HBRUSH, HDC, HINSTANCE, HMENU, HWND, LPARAM, LPCWSTR, LPVOID, LPWSTR, INT, RECT +import time + +user32 = windll.user32 +gdi32 = windll.gdi32 + +CB_ENUM_WINDOWS = WINFUNCTYPE(BOOL, HWND, LPARAM) + +# thanks: https://stackoverflow.com/questions/37501191/how-to-get-windows-window-names-with-ctypes-in-python +def check_zero(result, func, args): + if not result: + err = get_last_error() + if err: + raise WinError(err) + return args + +user32.CreateWindowExW.argtypes = [ + DWORD, LPCWSTR, LPCWSTR, DWORD, + INT, INT, INT, INT, + HWND, HMENU, HINSTANCE, LPVOID +] +user32.CreateWindowExW.errcheck = check_zero + +user32.FillRect.argtypes = [HDC, POINTER(RECT), HBRUSH] + +user32.GetWindowDC.argtypes = [HWND] +user32.GetWindowDC.errcheck = check_zero + +user32.GetWindowTextLengthW.argtypes = [HWND] +user32.GetWindowTextLengthW.errcheck = check_zero + +user32.GetWindowTextW.argtypes = [HWND, LPWSTR, INT] +user32.GetWindowTextW.errcheck = check_zero + +def find_all_windows(): + windows = [] + + @CB_ENUM_WINDOWS + def _handle_window(hwnd, lparam): + # +1: make room for the null + length = user32.GetWindowTextLengthW(hwnd) + 1 + buffer = create_unicode_buffer(length) + user32.GetWindowTextW(hwnd, buffer, length) + windows.append((hwnd, buffer.value)) + return True + + user32.EnumWindows(_handle_window, 0) + return windows + +def fix_window(window): + """ + hdc = user32.GetWindowDC(window) + rect = RECT(0, 0, 10000, 10000) + brush = gdi32.CreateSolidBrush(0x00FF0000) + user32.FillRect(hdc, rect, brush) + gdi32.DeleteObject(brush) + print(window) + """ + classname = create_unicode_buffer("overlapper\x00") + windowname = create_unicode_buffer("overlapper\x00") + window = user32.CreateWindowExW( + 0x08000000 | # WS_EX_NOACTIVATE + 0x8 | # WS_EX_TOPMOST + 0x0, # 0x20, # WS_EX_TRANSPARENT + classname, + windowname, + 0x10000000, # WS_VISIBLE + 0, 0, 128, 128, # x y w h + None, None, + None, None + ) + user32.ShowWindow(window, 5) # SW_SHOW + +def fix_step(): + windows = find_all_windows() + for window_id, window_name in windows: + if window_name.endswith("Visual Studio Code"): + fix_window(window_id) + +def main(): + fix_step() + + while True: + time.sleep(1) + +if __name__ == "__main__": + main() \ No newline at end of file