macOS: cached CGMainDisplayID goes stale after display re-enumeration; all mouse input collapses to (0,0)

Abierto
#150 1 comentario 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
3/5
Tiempo estimado
1-2 días
Aptitud para principiantes
68/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
cpp, macos

Línea de trabajo

Start in macos_backend.cpp around line 387 and trace MacosInputState, submit_absolute_motion(), and post_mouse(), focusing on how the cached display ID is used after display re-enumeration. Reproduce the monitor reconnect scenario and verify that both absolute and relative mouse input continue to map to the current display rather than a zero-sized bounds rectangle.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Bug

On macOS, MacosInputState caches CGMainDisplayID() once at construction (macos_backend.cpp:387). When the display later re-enumerates (monitor power cycle, DisplayPort link renegotiation), macOS assigns a new display ID and the cached ID becomes invalid. From that point on:

  • submit_absolute_motion() calls CGDisplayBounds(state_->display) on the stale ID, which returns a zero rect. scale_absolute_axis() then returns 0 for display_size <= 0, so every absolute mouse event is mapped to (0,0) — the cursor is pinned to the top-left corner.
  • post_mouse() clamps the target location into that same zero rect (std::clamp(raw, origin, origin + size - 1)), so relative motion is also trapped — observed cursor positions oscillate only between (0,0) and (-1,-1).

Restarting the consumer process (re-creating the state, hence re-caching the current display ID) restores input until the next display re-enumeration.

Evidence (observed via Sunshine v2026.914.233613 on Mac mini M4, macOS arm64)

  • Main display ID changes across monitor reconnects: 10 → 12 → 1 over consecutive days.
  • While the bug is active:
    • CGDisplayBounds(staleId 12)(0,0,0,0)
    • CGDisplayBounds(CGMainDisplayID() = 1)(0,0,1920,1080)
    • Client input packets arrive (UDP control channel has traffic), video capture is unaffected (capture re-enumerates per session), but the cursor never leaves the origin.
  • After restarting Sunshine, input works again — until the display ID changes once more.

Reproduction

  1. Start a libvirtualhid consumer (e.g. Sunshine) and note the main display ID.
  2. Force the display to re-enumerate: power-cycle the monitor, or unplug/replug it (many DP/USB-C monitors do this on their own when entering deep sleep).
  3. Send absolute or relative mouse input — cursor stays pinned at (0,0).

Suggested fix

Either:

  • Resolve the display at event time (CGMainDisplayID() in submit_absolute_motion / post_mouse instead of the cached value), or
  • Register CGDisplayRegisterReconfigurationCallback and update the cached display / display_scaling on reconfiguration events.

Additionally, guard against zero-sized CGDisplayBounds results (invalid/offline display) instead of clamping into a degenerate rect — e.g. fall back to CGMainDisplayID() bounds.

Happy to provide more diagnostics if needed.

Lenguaje dominante
C++
Estrellas
57
Forks
15
Merge medio
11 h 15 min
PR fusionados (30 d)
33

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de LizardByte/libvirtualhid

Todos los issues de LizardByte/libvirtualhid

Issues similares

Más issues de C++

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.