# Contributor handoff — Genymobile/scrcpy

> Canonical page: https://codevetter.com/unpack/scrcpy/agent-handoff

# Contributor handoff — Genymobile/scrcpy

Scanned commit 19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac (2026-09-05). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Build with meson+ninja (tests only exist in debug buildtype); the server needs the Android SDK via Gradle or can be skipped with -Dprebuilt_server=path. The code is dense C11 with an sc_ prefix convention, callback-struct vtables, vecdeque containers, and a strict init/start/stop/join/destroy lifecycle; every thread posts SDL events rather than touching shared UI state. The single most important invariant is that the C wire protocol and the Java wire protocol are manually kept in sync — always change both sides. Read doc/develop.md, app/src/scrcpy.c, app/src/server.c and Server.java before editing anything cross-cutting.
- Read first: doc/develop.md (architecture overview), app/src/scrcpy.c (wiring of all components), app/src/server.c (adb/tunnel/exec logic), server/.../Server.java + Options.java (server lifecycle and params), app/src/control_msg.h + ControlMessageReader.java (protocol contract). — [doc/develop.md](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/doc/develop.md) [app/src/scrcpy.c](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/scrcpy.c) [app/src/server.c](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/server.c) [server/src/main/java/com/genymobile/scrcpy/Server.java](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/src/main/java/com/genymobile/scrcpy/Server.java) [app/src/control_msg.h](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/control_msg.h)
- Build/test commands: 'meson setup builddir && ninja -C builddir'; tests need -Dbuildtype=debug then 'meson test -C builddir'; run helper './run builddir <args>'; server alone via server/build_without_gradle.sh or gradle; CI equivalents are release/test_client.sh and release/test_server.sh. — [app/meson.build#L224-297](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/meson.build#L224-L297) [run](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/run) [server/build_without_gradle.sh](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/build_without_gradle.sh) [release/test_client.sh](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/release/test_client.sh) [release/test_server.sh](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/release/test_server.sh)
- Convention: every subsystem exposes sc_<name>_init/start/stop/join/destroy and a cbs struct of function pointers (on_ended, on_connected, ...); follow this exact lifecycle when adding components — teardown order in scrcpy() is load-bearing. — [app/src/scrcpy.c#L337-360](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/scrcpy.c#L337-L360) [app/src/scrcpy.c#L914-1035](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/scrcpy.c#L914-L1035)
- Any protocol change must be applied twice: enum/serialize in app/src/control_msg.[ch] AND ControlMessage/ControlMessageReader server-side; same for stream headers (demuxer.c vs Streamer.java) and device messages (device_msg.c vs DeviceMessageWriter). — [app/src/control_msg.c](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/control_msg.c) [server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java) [app/src/demuxer.c#L72-138](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/demuxer.c#L72-L138) [server/src/main/java/com/genymobile/scrcpy/device/Streamer.java#L17-124](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/src/main/java/com/genymobile/scrcpy/device/Streamer.java#L17-L124)
- When adding a server parameter you MUST also update VALIDATE_STRING usage if the value is user-controlled — values are interpolated into an adb shell command and are deliberately restricted from containing shell metacharacters. — [app/src/server.c#L193-206](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/server.c#L193-L206) [app/src/server.c#L305-337](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/server.c#L305-L337)
- SCRCPY_SERVER_PATH (env) and devenv SCRCPY_SERVER_PATH let devs point the client at a locally-built server jar; PORTABLE builds instead look for scrcpy-server next to the binary. — [app/src/server.c#L26-54](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/server.c#L26-L54) [server/meson.build#L27-31](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/meson.build#L27-L31)
- assert() is used as a real correctness mechanism client-side and is compiled out in release (b_ndebug=if-release) — never put side effects in asserts; debug builds memset the scrcpy struct to 42 to catch missed initializations. — [meson.build#L7](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/meson.build#L7) [app/src/scrcpy.c#L322-327](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/scrcpy.c#L322-L327)
- Threads never call SDL functions off the main thread; they sc_push_event(SC_EVENT_*) and the event_loop dispatches — keep this rule or races in SDL will follow (comment at sc_sdl_set_hints). — [app/src/scrcpy.c#L440-442](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/scrcpy.c#L440-L442) [app/src/events.h](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/events.h) [app/src/receiver.c#L40-60](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/receiver.c#L40-L60)
- Windows-specific traps: argv arrives UTF-16 and is converted to UTF-8 in main(); process spawning lives in sys/win/process.c and does not shell-escape args; stdout/stderr are unbuffered for logging. — [app/src/main.c#L29-34](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/main.c#L29-L34) [app/src/main.c#L128-175](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/main.c#L128-L175) [app/src/server.c#L195-200](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/server.c#L195-L200)
- cli.c is ~3600 lines but highly regular — never hand-edit getopt strings; the optstring and longopts are generated from the options[] table by sc_getopt_adapter_create_optstring(). — [app/src/cli.c#L1268-1364](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/cli.c#L1268-L1364) [app/src/cli.c#L3561-3568](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/cli.c#L3561-L3568)
- Safe edit boundaries: util/ helpers and trait/ interfaces are self-contained; adding a feature typically means options.c default + cli.c row + server.c ADD_PARAM + Options.java case + a Server.java wiring line — touch all five or the flag silently does nothing. (inferred) — [app/src/options.c](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/options.c) [app/src/cli.c#L149-220](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/cli.c#L149-L220) [app/src/server.c#L269-458](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/server.c#L269-L458) [server/src/main/java/com/genymobile/scrcpy/Options.java#L336-574](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/src/main/java/com/genymobile/scrcpy/Options.java#L336-L574) [server/src/main/java/com/genymobile/scrcpy/Server.java#L71-195](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/src/main/java/com/genymobile/scrcpy/Server.java#L71-L195)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
