Skip to content

feat: add opt-in user presence overlay for multi-user sessions#211

Open
nullstacked wants to merge 1 commit intopikvm:masterfrom
nullstacked:feature/presence-overlay
Open

feat: add opt-in user presence overlay for multi-user sessions#211
nullstacked wants to merge 1 commit intopikvm:masterfrom
nullstacked:feature/presence-overlay

Conversation

@nullstacked
Copy link
Copy Markdown

@nullstacked nullstacked commented Apr 16, 2026

Summary

  • Adds user presence awareness to the KVM web UI (who is watching, who is controlling, who is idle)
  • Fully opt-in: kvmd.presence.enabled: false by default — zero behavior change unless enabled
  • Overlay is toggleable per-browser via Web UI settings
  • All styling via CSS classes + custom properties — fully themeable

Motivation

When multiple operators share access to a PiKVM device, there is no way to know who else is connected or who is actively sending input. This leads to input conflicts (last-write-wins at the HID gadget) and coordination overhead via external chat. This change gives every connected user real-time visibility into session state.

Performance impact

None measurable.

  • FPS: record_input() adds ~200ns per HID event (one dict lookup + one comparison on the rate-limited common path). At 1kHz mouse input, that is 0.2ms/sec of CPU — invisible against the video encode/decode pipeline (5-15ms/frame)
  • Memory: Three module-level dicts bounded by concurrent user count (typically 1-5). Total footprint ~500 bytes. Auto-pruned after 1 hour of inactivity
  • WS bandwidth: Presence events are diff-only — sent only on state change. ~100 bytes per event, at most a few per minute during steady-state. Negligible vs the H.264 stream
  • When disabled (default): Zero overhead. All presence calls gated behind if self.__presence_enabled. record_input() returns after one dict lookup on an empty dict

Design

  • New module kvmd/apps/kvmd/presence.py — module-level registry tracking connected users and last HID input timestamp per user
  • record_input() is on the HID hot path (~1kHz during mouse drag). Rate-limited to one time.monotonic() + dict write per 250ms per user. Common-case cost: one dict lookup + one comparison
  • WS broadcasts are diff-only — no event sent unless state actually changed
  • _last_input entries auto-pruned after 1 hour (bounds dict size)
  • Single-threaded asyncio — no locks needed
  • All presence logic gated behind self.__presence_enabled — zero overhead when disabled
  • Overlay styled via CSS classes + custom properties (--cs-presence-*) for easy theming

Screenshots

image

Files changed

File Change
kvmd/apps/kvmd/presence.py NEW — presence registry module
kvmd/apps/kvmd/server.py WS lifecycle hooks, broadcast loop
kvmd/apps/kvmd/api/hid.py Capture ws param, call record_input
kvmd/apps/_scheme.py Config flag
kvmd/apps/kvmd/__init__.py Pass config to server
web/share/css/kvm/presence.css NEW — overlay styles + CSS custom properties
web/share/js/kvm/session.js Overlay renderer + toggle binding
web/kvm/index.html Settings toggle checkbox + CSS link

User-facing behavior

When kvmd.presence.enabled: true in override.yaml:

  • Small text overlay appears on the video stream showing each connected user
  • Users with recent input (last 10s) show as "is controlling" (green)
  • Users with no input for 5+ minutes show as "is watching (idle)" (dimmed)
  • Others show as "is watching"
  • Toggle in Web UI settings: "Show who is watching/controlling"
  • No background — clean floating text with shadow for readability on any video content
  • Works in both normal and full-screen/full-tab modes
  • Fully customizable via CSS custom properties:
    :root {
        --cs-presence-controlling-fg: #00ff00;
        --cs-presence-font-size: 14px;
        --cs-presence-top: 20px;
    }

Test plan

  • Enable via kvmd.presence.enabled: true in override.yaml, restart kvmd
  • Open KVM view in two browsers with different user accounts
  • Verify both users appear in overlay
  • Type/move mouse — verify "is controlling" state appears and decays after 10s
  • Wait 5+ minutes — verify "(idle)" state appears
  • Toggle overlay off via Web UI settings — verify it hides
  • Disable presence.enabled, restart — verify zero presence events sent
  • Verify no console errors, no performance degradation during active mouse use
  • Verify overlay visible in full-screen/full-tab mode
  • Override CSS variables — verify styling changes apply

When multiple users connect to the same PiKVM, there is currently no
visibility into who else is watching or who is actively controlling the
machine. This adds a lightweight presence system behind a new config
flag (kvmd.presence.enabled, default false).

When enabled, the KVM web UI shows a small overlay listing connected
users and their state: "is controlling" (recent HID input), "is watching",
or "is watching (idle)". The overlay is toggleable per-browser via
Web UI settings.

The HID hot path (record_input) is rate-limited to one time.monotonic()
syscall per 0.25s per user. WS broadcasts are diff-only. All state is
bounded and auto-pruned.
@nullstacked nullstacked force-pushed the feature/presence-overlay branch from 46d3cec to 915e432 Compare April 16, 2026 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant