Imported from mozayed007/scrctrl (
AGENTS.md). Install upstream withnpx skills add mozayed007/scrctrl. Copyright stays with the author.
AGENTS.md
Project: ScrCtrl — scrcpy Device Manager for Windows
Quick Reference
- Language: Python 3.9+
- Entry points:
scrcpy_cli.py(CLI),scrcpy_tui.py(Textual TUI),scrcpy_legacy_menu.py(fallback menu) - Core library:
scrcpy_manager.py(pure Python, no UI deps) - Binaries:
bin\scrcpy.exe,bin\adb.exe(downloaded from official Genymobile releases) - Config:
config\devices.ini,config\quality.ini,config\lastused.ini,config\userprefs.ini - Key dependency:
textual(optional, for TUI)
Architecture
scrcpy_cli.py → argparse routing, update workflow, CLI entry
scrcpy_tui.py → Textual App / MainScreen (imports screens + manager)
scrcpy_tui_screens.py → Modal screens (ProfileEdit, Help, LaunchOptions, etc.)
scrcpy_legacy_menu.py → input()-based menus (extends ScrcpyManager)
scrcpy_manager.py → ScrcpyManager, Device, ADB wrappers, INI I/O, ProfileField schema
The manager is the single source of truth for all scrcpy argument building.
Profile Schema (config/devices.ini)
Connection & Identity
nickname— Display nameip— Wireless IP addressserial— USB serial numberquality— Preset name fromquality.inimode—mirror|otg|camera
Streaming & Quality
video_codec—h264|h265|av1| empty (uses quality preset)audio_codec—opus|aac|flac|raw| emptyaudio_source—output|playback|mic|mic-unprocessed| ... | emptyrender_fit—auto|stretch|crop|letterbox| emptyorientation—0|90|180|270|flip0|flip90|flip180|flip270| emptywindow_aspect_ratio_lock—yes(default) |nokeyboard—disabled|sdk|uhid|aoa| emptymouse—disabled|sdk|uhid|aoa| emptygamepad—disabled|uhid|aoa| emptyshortcut_mod— Shortcut modifier list, e.g.rctrlorlctrl,lsuper
Display & Behavior
keep_active—__YES__/yes/true/1/on→--keep-activebackground_color— Hex color, e.g.#234567display_id— Display id →--display-id=<id>crop— Crop asWIDTH:HEIGHT:X:Y→--crop=<spec>fullscreen—yes→--fullscreenalways_on_top—yes→--always-on-topflex_display—yes→--flex-display(only meaningful withnew_displayormode=mirror)new_display— Virtual display spec, e.g.1920x1080/160→--new-display=1920x1080/160no_control—yes→--no-controlpower_off_on_close—yes→--power-off-on-closeturn_screen_off—yes→--turn-screen-offshow_touches—yes→--show-touchesno_audio—yes→--no-audiono_window—yes→--no-window
Recording
record— File path →--record=<path>record_format—mp4|mkv|m4a|mka|opus|aac|flac|wav|raw→--record-format=<fmt>
Camera
camera_id— Camera id →--camera-id=<id>camera_facing—front|back|external→--camera-facing=<facing>camera_size—WIDTHxHEIGHT→--camera-size=<size>camera_fps— Frame rate →--camera-fps=<fps>camera_torch—yes→--camera-torchcamera_zoom— Zoom value →--camera-zoom=<zoom>
Boolean Normalization
Profile booleans use is_profile_bool_yes(value) which accepts:
__YES__,yes,y,true,1,on→ True- Everything else (including empty string) → False
Quality Preset Schema (config/quality.ini)
Each section is a preset name used by profiles.
video_bitrate— e.g.12M→--video-bit-rate=12Mmax_fps— e.g.60→--max-fps=60audio_buffer— e.g.10→--audio-output-buffer=10(SDL buffer, default 10ms)audio_delay— e.g.30→--audio-buffer=30(target delay, default 50ms)video_buffer— e.g.20→--video-buffer=20(jitter compensation, default 0ms)resolution— e.g.1920x1080→--max-size=1920video_codec— e.g.h265→--video-codec=h265audio_codec— e.g.opus→--audio-codec=opusaudio_source— e.g.output→--audio-source=output
Precedence rule: Profile-level video_codec/audio_codec/audio_source override the quality preset values.
Default Presets (Optimized for Modern Hardware)
| Preset | Bitrate | FPS | Audio Delay | Video Buffer | Resolution | Video Codec | Audio |
|---|---|---|---|---|---|---|---|
| low | 2M | 30 | 60ms | 50ms | native | h264 | opus |
| balanced | 8M | 60 | 40ms | 30ms | native | h264 | opus |
| high | 12M | 60 | 30ms | 20ms | 1920x1080 | h265 | opus |
| ultra | 32M | 120 | 20ms | 0ms | 2560x1440 | h265 | opus |
- Use
highorultrafor modern devices and laptops (H265 decoding is efficient on modern hardware). audio_delayis--audio-buffer. Lower = more responsive; higher = smoother.video_bufferis--video-buffer.0onultraminimizes latency.audio_buffer(SDL output) is kept at10ms(default) for all presets.
scrcpy Argument Building (build_scrcpy_args)
The canonical place where CLI flags are generated from profile + quality settings.
Key logic:
-s <connection>always first--window-titleis auto-generated- Quality settings are applied (bitrate, fps, buffers, resolution, codecs, source)
- Mode flags:
--otg,--video-source=camera - New display:
--new-display=<spec>(ifmode != otg) - Flex display:
--flex-display(ifmode != otg) - Window/rendering:
--keep-active,--background-color,--render-fit,--no-window-aspect-ratio-lock,--orientation - Behavior:
--no-control,--power-off-on-close - Recording:
--record,--record-format - Profile-level codec overrides (take final precedence over quality presets)
extraargs from CLI are appended last
Validation Helpers
is_valid_video_codec— checks againstVIDEO_CODECSlistis_valid_audio_codec— checks againstAUDIO_CODECSlistis_valid_audio_source— checks againstAUDIO_SOURCESlistis_valid_render_fit— checks againstRENDER_FITSlistis_valid_orientation— checks againstORIENTATIONSlistis_valid_record_format— checks againstRECORD_FORMATSlistis_valid_new_display— validatesWIDTHxHEIGHTorWIDTHxHEIGHT/DPIis_profile_bool_yes— normalizes boolean strings
Coding Conventions
- Use
str | Nonefor optional strings, notOptional[str] - Use
list[str]instead ofList[str] - Use
configparser.ConfigParser(interpolation=None)andparser.optionxform = strto preserve case - Always create
.bakbackup before overwriting INI files - All UI code lives in
scrcpy_tui.pyorscrcpy_legacy_menu.py;scrcpy_manager.pyis pure library - TUI widgets are created inside
try: ... except ImportError:so the module remains importable without Textual - Use
logger = logging.getLogger(__name__)for debug output; enable withSCRCPY_DEBUG=1
Adding New scrcpy Flags
- Add the constant list (e.g.,
NEW_OPTIONS = [...]) toscrcpy_manager.py - Add a validation helper if needed
- Add the field to
get_profile,list_profiles,save_profile - Add the flag to
build_scrcpy_args - Add the field to
ProfileEditScreen(TUI) and_prompt_profile_fields(legacy menu) - Add CLI argument to
build_parserinscrcpy_cli.py - Add to
_build_extra_from_argsinscrcpy_cli.py - Update
config/quality.iniif the preset should include it - Update
scrcpy_agent.pyJSON-safe service methods if the flag should be agent-addressable - Update
scrcpy_mcp.pytool/resource schemas if the flag should be exposed to MCP clients - Update
README.md,AGENTS.md, and tests
Agent Experience / Android CUA
scrcpy_agent.pyis the prompt-free agent service layer; keep it JSON-safe and non-interactivescrcpy_capabilities.pyis the scrcpy-native knowledge catalog; prefer localscrcpy --helpcompatibility over stale docsscrcpy_mcp.pyis the local MCP stdio server; keep tool schemas explicit and avoid arbitraryadb shell- Android CUA sessions require
session_idfor control actions and should use direct ADB screenshots (exec-out screencap -p) - MCP sessions are in-memory; CLI sessions persist under
%TEMP%\scrctrl-agent\sessions - Risky actions should return
approval_requiredorblocked, not silently execute - Scrcpy remains the watch/recording surface; ADB remains the reliable state/action surface