-
Notifications
You must be signed in to change notification settings - Fork 2
Fix multi-monitor detection: periodic monitor list refresh and monito… #35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…r-aware screenshot collection
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Improves robustness of multi-monitor event/screenshot association when displays are connected/disconnected by periodically refreshing the monitor list and preferring monitor-matching screenshots for events.
Changes:
- Refreshes the
screeninfomonitor list periodically during coordinate-to-monitor resolution. - Passes
monitor_indexthrough event aggregation so screenshot selection prefers the event’s monitor. - Updates screenshot selection helpers to filter candidates by
monitor_indexwhen available.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
src/record/models/event_queue.py |
Prefer screenshots from the same monitor as the input event when selecting “before” and “after” screenshots. |
src/record/handlers/input_event.py |
Periodically refresh the monitor list to handle monitor hot-plug/unplug scenarios. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| self._monitors = list(get_monitors()) | ||
| self._monitors_last_refresh = time.time() | ||
| self._monitors_refresh_interval = 5.0 |
Copilot
AI
Feb 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get_monitors() is called unguarded during initialization, but refreshes are wrapped in a try/except. If screeninfo fails or returns an empty list at startup, the handler can crash before it ever reaches the safer refresh logic. Consider wrapping the initial get_monitors() call similarly and ensuring _monitors is non-empty (fallback to a single synthetic monitor or a safe default).
| except Exception: | ||
| pass | ||
| self._monitors_last_refresh = now | ||
|
|
Copilot
AI
Feb 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The monitor refresh can leave self._monitors empty (e.g., get_monitors() returns []). The rest of _get_monitor assumes at least one monitor exists (uses self._monitors[0] later), which can raise IndexError. Add a guard after refresh (and before the loop) to handle the empty-list case safely.
| # If no monitors are available (e.g., get_monitors() returned an empty list), | |
| # return a safe default monitor description to avoid IndexError. | |
| if not self._monitors: | |
| return 0, {"left": 0, "top": 0, "width": 0, "height": 0} |
| Returns: | ||
| Monitor index (0-based) | ||
| """ | ||
| now = time.time() | ||
| if now - self._monitors_last_refresh > self._monitors_refresh_interval: |
Copilot
AI
Feb 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _get_monitor docstring/type annotation says it returns a single int monitor index, but the implementation returns a tuple (idx, monitor_dict). Update the return type/docstring to match the actual return value to avoid misleading callers and type checkers.
| def _collect_screenshots(self, timestamp: float, monitor_index: int = None) -> Any: | ||
| """Get screenshot before timestamp, preferring the given monitor.""" | ||
| constants = constants_manager.get() |
Copilot
AI
Feb 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type hint mismatch: monitor_index defaults to None but is annotated as int. Use Optional[int] (and propagate the same change to _collect_end_screenshot) to reflect actual usage and avoid confusing static analysis.
| if monitor_index is not None: | ||
| matching = [s for s in start_candidates if s.monitor_index == monitor_index] | ||
| if matching: | ||
| return matching[-1] | ||
| return start_candidates[-1] |
Copilot
AI
Feb 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This list comprehension allocates a new list of all matching screenshots just to pick the last one. You can avoid the extra allocation by iterating start_candidates in reverse and returning the first match, which is both faster and uses less memory.
| if monitor_index is not None: | ||
| matching = [s for s in exact_candidates if s.monitor_index == monitor_index] | ||
| if matching: | ||
| return matching[-1] | ||
| return exact_candidates[-1] |
Copilot
AI
Feb 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above: building matching allocates a list when you only need a single element. Iterate exact_candidates in reverse and return the first matching monitor entry to reduce allocations.
| except Exception: | ||
| pass |
Copilot
AI
Feb 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'except' clause does nothing but pass and there is no explanatory comment.
| except Exception: | |
| pass | |
| except Exception as exc: | |
| # Keep using previously cached monitor information if refresh fails. | |
| print(f"Warning: Failed to refresh monitor information: {exc}") |
fixes the case where displays are being connected and disconnected. Ties events to displays.