This page covers the two files that form the top-level entry point and session orchestrator of the scrcpy client: app/src/main.c and app/src/scrcpy.c. It also describes the scrcpy_options configuration struct and the scrcpy_exit_code return interface.
For the CLI argument parser that populates scrcpy_options, see Command-Line Interface. For the server management subsystem that scrcpy() starts, see ADB and Server Management. For screen rendering, input, media, and recording subsystems that scrcpy() wires together, see pages 2.3–2.7. For OTG mode (scrcpy_otg()), see OTG Mode and USB Input.
The client entry point follows a two-stage design:
main.c — platform normalization, library initialization, CLI parsing, and dispatch to either scrcpy() or scrcpy_otg().scrcpy.c — session lifecycle: SDL setup, server startup, component construction and wiring, the SDL event loop, and ordered teardown.All configuration flows through a single scrcpy_options struct. All exit paths return an scrcpy_exit_code value.
main.cSources: app/src/main.c1-153
On Windows, main() converts the wide-character command line (via CommandLineToArgvW) into a UTF-8 char** before calling main_scrcpy(). On all other platforms, main() calls main_scrcpy() directly.
main_scrcpy()main_scrcpy() runs the following steps in order before handing off to the session function:
| Step | Call | Purpose |
|---|---|---|
| 1 | scrcpy_parse_args() | Parse CLI args into scrcpy_cli_args / scrcpy_options |
| 2 | sc_set_log_level() | Apply verbosity from options |
| 3 | SC_MAIN_THREAD_ID = sc_thread_get_id() | Record main thread identity for later assertions |
| 4 | av_register_all() | Register FFmpeg codecs (legacy builds only, guarded by SCRCPY_LAVF_REQUIRES_REGISTER_ALL) |
| 5 | avdevice_register_all() | Register libavdevice (only when v4l2_device is set and HAVE_V4L2 is defined) |
| 6 | net_init() | Initialize platform sockets (Winsock on Windows, no-op elsewhere) |
| 7 | sc_log_configure() | Finalize logging |
| 8 | scrcpy() or scrcpy_otg() | Run the session |
After the session returns, main_scrcpy() optionally pauses the terminal (controlled by pause_on_exit) before returning the exit code to the OS.
scrcpy_optionsSources: app/src/options.h230-334 app/src/options.c5-116
struct scrcpy_options is the single configuration object passed from main_scrcpy() into scrcpy(). It is populated by scrcpy_parse_args() and never modified after that.
The default values are defined in scrcpy_options_default in options.c. Key defaults include:
| Field | Default | Notes |
|---|---|---|
video_codec | SC_CODEC_H264 | H.264 encoding |
audio_codec | SC_CODEC_OPUS | Opus encoding |
video_source | SC_VIDEO_SOURCE_DISPLAY | Screen capture |
audio_source | SC_AUDIO_SOURCE_AUTO | Auto-resolved per source |
keyboard_input_mode | SC_KEYBOARD_INPUT_MODE_AUTO | Auto-selected |
mouse_input_mode | SC_MOUSE_INPUT_MODE_AUTO | Auto-selected |
gamepad_input_mode | SC_GAMEPAD_INPUT_MODE_DISABLED | Gamepads off |
control | true | Device control enabled |
video / audio | true | Both streams forwarded |
video_playback / audio_playback | true | Both rendered locally |
window | true | SDL window shown |
clipboard_autosync | true | Auto clipboard sync |
cleanup | true | Server removed on exit |
power_on | true | Device powered on at start |
mipmaps | true | OpenGL mipmaps enabled |
shortcut_mods | LALT | LSUPER | Default shortcut modifier keys |
port_range | 27183–27199 | ADB tunnel port range |
audio_output_buffer | 5 ms | SDL audio output buffer |
The struct also contains compile-time-conditional fields for HAVE_V4L2 (v4l2_device, v4l2_buffer) and HAVE_USB (otg).
Diagram: scrcpy_options field groups
scrcpy_exit_codeSources: app/src/scrcpy.h8-17
scrcpy() and scrcpy_otg() both return an enum scrcpy_exit_code, which main_scrcpy() propagates as the process exit status.
| Value | Integer | Meaning |
|---|---|---|
SCRCPY_EXIT_SUCCESS | 0 | Normal termination |
SCRCPY_EXIT_FAILURE | 1 | Error (connection, demuxer, recorder, etc.) |
SCRCPY_EXIT_DISCONNECTED | 2 | Device disconnected during an active session |
scrcpy.cSources: app/src/scrcpy.c1-1068
scrcpy Structstruct scrcpy (defined statically inside scrcpy()) is the container that owns every subsystem for the session lifetime.
Sources: app/src/scrcpy.c48-92
The keyboard, mouse, and gamepad fields are stored in C union blocks — at most one variant is active per session, selected based on options->keyboard_input_mode, options->mouse_input_mode, and options->gamepad_input_mode.
The scrcpy() function initializes components in a specific order. Many steps are guarded by bool flags (e.g. server_started, controller_initialized) that are checked in the cleanup end: block.
Diagram: Startup sequence in scrcpy()
Sources: app/src/scrcpy.c380-968
Before the main event loop runs, scrcpy() waits for the server to connect using await_for_server(). This function runs its own inner SDL_WaitEvent() loop, handling only three events:
| Event | Outcome |
|---|---|
SC_EVENT_SERVER_CONNECTED | Sets connected = true, returns true |
SC_EVENT_SERVER_CONNECTION_FAILED | Returns false |
SDL_QUIT | Sets connected = false, returns true (clean exit) |
Server lifecycle callbacks (sc_server_on_connection_failed, sc_server_on_connected, sc_server_on_disconnected) are registered in sc_server_callbacks and push SDL user events to unblock await_for_server().
Sources: app/src/scrcpy.c236-260 app/src/scrcpy.c322-346
SDL initialization is split into subsystems based on which features are active:
| Subsystem | Condition |
|---|---|
SDL_INIT_EVENTS | Always |
SDL_INIT_VIDEO | options->window or control && clipboard_autosync |
SDL_INIT_AUDIO | options->audio_playback |
SDL_INIT_GAMECONTROLLER | gamepad_input_mode != DISABLED |
sdl_set_hints() applies the following SDL hints before the server thread starts:
SDL_HINT_RENDER_DRIVER — requested render driverSDL_HINT_APP_NAME — application name (used by PulseAudio)SDL_HINT_RENDER_SCALE_QUALITY — linear filtering ("1")SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH — click-to-focus behaviorSDL_HINT_TOUCH_MOUSE_EVENTS — suppress synthetic mouse eventsSDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR — prevent compositor bypassSDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS — prevent minimization on focus lossSDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS — allow joystick events in backgroundOn Windows, sdl_configure() also installs a SetConsoleCtrlHandler so that Ctrl+C pushes an SDL_QUIT event rather than abruptly terminating the process.
Sources: app/src/scrcpy.c104-176
After all components are initialized, scrcpy() connects them using the packet_source / frame_source sink interfaces. The wiring depends on which features are enabled.
Diagram: Data flow wiring in scrcpy()
Sources: app/src/scrcpy.c592-871
Input device selection within scrcpy() evaluates keyboard_input_mode, mouse_input_mode, and gamepad_input_mode from scrcpy_options. The chosen implementations are stored in unions inside struct scrcpy and their key_processor, mouse_processor, and gamepad_processor interface pointers are passed into sc_screen_params.
| Mode | Keyboard type | Mouse type | Gamepad type |
|---|---|---|---|
SDK | sc_keyboard_sdk | sc_mouse_sdk | — |
UHID | sc_keyboard_uhid | sc_mouse_uhid | sc_gamepad_uhid |
AOA | sc_keyboard_aoa | sc_mouse_aoa | sc_gamepad_aoa |
AOA input additionally requires sc_usb, sc_aoa, and sc_acksync initialization, and opens the USB device by serial number before starting sc_aoa_start().
Sources: app/src/scrcpy.c640-796
event_loop() is the heart of the session. It calls SDL_WaitEvent() in a blocking loop until a terminal event arrives.
Diagram: Event routing in event_loop()
Sources: app/src/scrcpy.c178-218
The SC_EVENT_RUN_ON_MAIN_THREAD mechanism allows other threads to schedule work on the main thread without additional synchronization primitives. Callbacks posted via sc_push_event() are drained before each component is stopped.
terminate_event_loop() is called immediately after event_loop() returns. It calls sc_reject_new_runnables() and drains any remaining SC_EVENT_RUN_ON_MAIN_THREAD events with SDL_PollEvent() to prevent memory leaks from unexecuted closures.
Sources: app/src/scrcpy.c220-233
After event_loop() returns and the window is hidden, the end: label governs teardown. Components are stopped and joined in dependency order:
Sources: app/src/scrcpy.c958-1067
Key ordering constraints:
sc_screen_destroy() must run after sc_demuxer_join() to ensure no frames arrive after the screen is freed.sc_server_stop().sc_aoa_join() runs after sc_screen_join() to ensure AOA HID events are no longer dispatched.Each session generates a random 31-bit session ID (scid) via scrcpy_generate_scid(). This value is passed to the server to allow multiple concurrent scrcpy instances to coexist on the same device. Only 31 bits are used to avoid signed-integer issues on the Java side of the server.
Sources: app/src/scrcpy.c357-363
Refresh this wiki
This wiki was recently refreshed. Please wait 3 days to refresh again.