# vrising-cam Hold **Q / E** to rotate the camera in V Rising on Linux, by emulating the game's "Rotate camera" gesture: holding the right mouse button while streaming relative mouse motion. The emulation happens at the kernel level (uinput), so it works the same on X11 and Wayland — verified on KDE Plasma Wayland with the game running through plain Steam/Proton. Single-file, no services: the first run creates a `.venv` next to the script, installs `evdev`, and re-executes itself inside it. The script only acts while its process is running; `Ctrl+C` to stop, and the emulated right mouse button is always released on exit (including on SIGTERM and crashes, via `atexit`). ## Usage ``` ./vrising-cam.py # hold Q = rotate left, hold E = rotate right ``` While running, the process watches your keyboard's `/dev/input/event*` nodes for Q / E, presses the emulated right mouse button for as long as either key is held, and emits relative X motion at a continuous rate. ### Options | Flag | Meaning | | --- | --- | | `--speed N` | rotation speed in relative X units per second (default `2000`) | | `--tick-ms N` | milliseconds between motion ticks (default `1.0`; lower = smoother, higher = lighter) | | `--ease SECONDS` | ramp speed up over `SECONDS` on press and decay on release (`0` = off; try `0.15`) | | `--invert` | swap the rotation direction of Q and E | | `-v`, `--verbose` | log key presses/releases, RMB injection, measured motion rate | | `--test-rmb [SECONDS]` | self-test: hold the injected RMB for `SECONDS` (default 3) and read events back from the kernel; no keyboard needed | | `--check` | show detected keyboards, uinput and grab status, then exit | ### Tuning the feel - **Smoothness** comes from the fractional accumulator: ticks deliver 0–1 unit each at a fine cadence, so motion is progress-continuous instead of pulsing. The default 1 ms tick keeps bursts at 0–1 unit practically throughout; bulge the interval (e.g. `--tick-ms 50`) for a lighter pipeline if you ever need to. - **Speed**: `--speed 1000` is a slow orbit; `3000+` is a fast spin. Because motion is rate-continuous, `--speed` directly maps to how fast the camera rotates. - **Ease**: `--ease 0.15` gives short smoothstep accel after pressing and a linear decay on key release (the emulated RMB stays held during the decay, then releases). `--ease 0` (default) is instant — some people prefer that. The previous caveat applies while tap-spinning: easing shapes hold-and-drag feel, not button clicks. - Q held together with E cancels out (no motion) but keeps the RMB hold. ### CPU cost Idle the loop wakes 4×/s via `select`. While rotating, it wakes once per tick (1000/s at the default 1 ms) plus one kernel write per emitted event — well under 1% of one core on any modern machine. ## Setup on Arch (KDE Wayland / X11 alike) 1. Make `/dev/uinput` writable: ``` echo 'KERNEL=="uinput", MODE="0660", GROUP="input", TAG+="uaccess"' | sudo tee /etc/udev/rules.d/70-vrising-cam.rules sudo udevadm control --reload-rules && sudo udevadm trigger sudo usermod -aG input $USER # log out and back in ``` 2. Ensure the `uinput` module loads at boot: ``` sudo modprobe uinput echo uinput | sudo tee /etc/modules-load.d/uinput.conf ``` 3. Run `./vrising-cam.py --check` — it should print `READY`: `/dev/uinput` openable, your keyboards listed exposing Q+E, and no "exclusively grabbed" warnings. ## Troubleshooting - **No camera rotation at all** → run with `-v`: hold Q and watch for `key: Q press` lines. No lines while rotating means some other process exclusively grabbed your keyboard (`EVIOCGRAB`; remappers like keyd/kanata/kmonad do this) — the script warns about that with `--check` and `-v`. - **Keys detected but desktop/game shows nothing** → run the self-test `./vrising-cam.py --test-rmb`: if `kernel read-back: OK` still shows no reaction on the desktop, get `sudo libinput debug-events` (`sudo pacman -S libinput-tools` on current Arch) and watch for `BTN_RIGHT` lines while the test runs. - **Stop quickly** → `Ctrl+C` (or `kill`, also emulated) — the emulated RMB is released in the `finally`/`atexit` path. ## Files - `vrising-cam.py` — the whole program (single file by design) - `.venv/` — auto-created next to the script on first run (git-ignored)