feat(controller): add refresh_display_bounds and dynamic resolution and monitor change detection

This commit is contained in:
2026-09-08 23:47:22 +02:00
parent 3afd197027
commit 8aeb5360f6
+65 -18
View File
@@ -84,24 +84,10 @@ class CatController:
self.config = config self.config = config
self.assets = assets self.assets = assets
# Primary monitor work area (excludes taskbar) # Dynamic display topology signature & periodic checker
work_area = WindowManager.get_work_area() self._current_display_signature = None
self.screen_left = work_area[0] self.next_display_check_time = time.time() + 1.5
self.screen_top = work_area[1] self.refresh_display_bounds(force=True)
self.screen_right = work_area[2]
self.screen_bottom = work_area[3]
# Full virtual desktop spanning ALL monitors used for roaming, platforming,
# and dragging so the cat can freely explore secondary displays.
vd = WindowManager.get_virtual_desktop_bounds()
self.vd_left = vd[0]
self.vd_top = vd[1]
self.vd_right = vd[2]
self.vd_bottom = vd[3]
# Horizontal roaming boundaries across all monitors
self.min_x = float(self.vd_left + 15)
self.max_x = float(self.vd_right - self.config.cat_width - 15)
# Position & motion # Position & motion
self.x = float(self.screen_left + 100) self.x = float(self.screen_left + 100)
@@ -155,6 +141,62 @@ class CatController:
self.walk_speed_px = (WALK_SPEED / config.cat_width) * (config.cat_width * 5.0) self.walk_speed_px = (WALK_SPEED / config.cat_width) * (config.cat_width * 5.0)
self.run_speed_px = (RUN_SPEED / config.cat_width) * (config.cat_width * 4.0) self.run_speed_px = (RUN_SPEED / config.cat_width) * (config.cat_width * 4.0)
def refresh_display_bounds(self, force: bool = False) -> bool:
"""
Dynamically refreshes monitor geometries, virtual desktop boundaries,
and clamps cat position if display topology or resolution changed.
Returns True if a display change occurred, False otherwise.
"""
new_signature = WindowManager.get_display_topology_signature()
if not force and new_signature == self._current_display_signature:
return False
self._current_display_signature = new_signature
# Update primary monitor work area (excludes taskbar)
work_area = WindowManager.get_work_area()
self.screen_left = work_area[0]
self.screen_top = work_area[1]
self.screen_right = work_area[2]
self.screen_bottom = work_area[3]
# Update full virtual desktop spanning all monitors
vd = WindowManager.get_virtual_desktop_bounds()
self.vd_left = vd[0]
self.vd_top = vd[1]
self.vd_right = vd[2]
self.vd_bottom = vd[3]
# Update horizontal roaming boundaries
self.min_x = float(self.vd_left + 15)
self.max_x = float(self.vd_right - self.config.cat_width - 15)
monitors = WindowManager.get_monitors()
logger.info(
f"Dynamic display configuration updated: {len(monitors)} monitor(s) detected, "
f"virtual desktop=({self.vd_left}, {self.vd_top}, {self.vd_right}, {self.vd_bottom}), "
f"primary work area={work_area}"
)
# Ensure the cat is not stranded outside newly active bounds
if hasattr(self, "x") and hasattr(self, "y"):
prev_x = self.x
self.x = max(self.min_x, min(self.max_x, self.x))
# Re-evaluate floor at current position
new_floor = WindowManager.get_floor_y_at(self.x + self.config.cat_width * 0.5, self.config.cat_height)
self.floor_y = new_floor
# If walking or resting on floor, adjust Y to new floor
if hasattr(self, "current_ledge") and not self.current_ledge:
if hasattr(self, "state") and self.state in (CatState.WALK, CatState.SIT, CatState.SLEEP, CatState.RELEASE):
self.y = new_floor
if prev_x != self.x:
logger.info(f"Clamped cat X from {prev_x} to {self.x} due to display boundary change")
return True
# ========================================================================== # ==========================================================================
# State Transitions & Targeting # State Transitions & Targeting
# ========================================================================== # ==========================================================================
@@ -348,6 +390,11 @@ class CatController:
face_type = "happy" if self.state == CatState.HAPPY else ("blink" if self.is_blinking else "open") face_type = "happy" if self.state == CatState.HAPPY else ("blink" if self.is_blinking else "open")
# Dynamic check for monitor addition/removal, resolution or work-area change
if now >= self.next_display_check_time:
self.next_display_check_time = now + 1.5
self.refresh_display_bounds()
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# State: WALK (roaming desktop floors and window ledges) # State: WALK (roaming desktop floors and window ledges)
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------