scrcpy 中的屏幕和输入管理子系统负责在主机上显示 Android 设备屏幕并捕获用户输入以控制设备。本文档详细介绍了客户端应用程序如何管理屏幕渲染、处理窗口操作、处理输入事件,以及将它们转换为适当的消息发送给 Android 设备。有关设备如何处理这些消息的信息,请参阅 设备控制。
屏幕和输入管理子系统由两个主要组件组成,它们紧密协作
来源: app/src/screen.h25-73 app/src/input_manager.h18-47
来源: app/src/screen.c802-872 app/src/input_manager.c1013-1085
屏幕管理组件负责处理与在主机上显示 Android 设备屏幕相关的所有事宜。它负责创建和管理窗口、渲染视频帧、处理窗口事件(大小调整、全屏切换等),并维护正确的方向和缩放。
屏幕管理系统的核心是 sc_screen 结构,该结构维护显示窗口及相关组件的状态。它实现了 sc_frame_sink 接口以接收解码后的视频帧。
来源: app/src/screen.c325-485 app/src/screen.c488-511
屏幕以默认参数初始化,但保持隐藏状态,直到收到第一个视频帧。届时,窗口将调整为合适的尺寸并显示。初始化过程设置所需的数据结构,创建 SDL 窗口,并为帧渲染做好准备。
当新的视频帧到达时,会发生以下过程:
渲染过程负责将内容正确缩放到适合窗口,同时保持纵横比。
来源: app/src/screen.c299-323 app/src/screen.c208-226 app/src/screen.c684-706
屏幕组件提供了几个函数来管理窗口状态:
| 功能 | 描述 |
|---|---|
sc_screen_toggle_fullscreen() | 在窗口模式和全屏模式之间切换 |
sc_screen_resize_to_fit() | 将窗口调整到最佳尺寸(去除黑色边框) |
sc_screen_resize_to_pixel_perfect() | 将窗口调整为精确的 1:1 像素映射 |
sc_screen_set_orientation() | 更改显示方向 |
sc_screen_set_paused() | 暂停/恢复显示更新 |
屏幕组件处理不同的显示方向(0°、90°、180°、270°)和翻转方向。当方向更改时,内容尺寸会相应更新,并在需要时调整窗口大小。
缩放的处理是为了保持正确的纵横比,如果需要,窗口会显示黑边。内容矩形(screen->rect)表示窗口内可见的内容部分。
来源: app/src/screen.c16-27 app/src/screen.c586-604 app/src/screen.c165-202
输入管理组件处理来自各种来源(键盘、鼠标、触摸屏、游戏手柄)的用户输入,并将它们转换为适当的控制消息发送到 Android 设备。
sc_input_manager 结构维护所有输入相关功能的状态。它引用用于不同输入类型(按键、鼠标、游戏手柄)的处理器,这些处理器实际处理事件。
来源: app/src/input_manager.h18-47 app/src/input_manager.c15-49
当接收到 SDL 事件时,它会通过 sc_input_manager_handle_event() 函数转发给输入管理器。该函数确定事件类型并将其委托给相应的处理函数:
| 事件类型 | 处理函数 |
|---|---|
| 键盘 | sc_input_manager_process_key() |
| 文本输入 | sc_input_manager_process_text_input() |
| 鼠标移动 | sc_input_manager_process_mouse_motion() |
| 鼠标按钮 | sc_input_manager_process_mouse_button() |
| 鼠标滚轮 | sc_input_manager_process_mouse_wheel() |
| 触摸 | sc_input_manager_process_touch() |
| 游戏手柄 | 各种游戏手柄处理函数 |
来源: app/src/input_manager.c1013-1085
输入管理器实现了各种键盘快捷键,用于控制客户端应用程序和 Android 设备
| 快捷键 | 操作 |
|---|---|
| MOD+f | 切换全屏 |
| MOD+g | 调整到像素完美(1:1) |
| MOD+i | 切换 FPS 计数器 |
| MOD+h | 发送 HOME 键 |
| MOD+b | 发送 BACK 键 |
| MOD+s | 发送 APP_SWITCH 键 |
| MOD+m | 发送 MENU 键 |
| MOD+下/上 | 控制音量 |
| MOD+p | 发送 POWER 键 |
| MOD+o | 打开/关闭屏幕 |
| MOD+左/右 | 更改方向 |
| MOD+c/x/v | 复制/剪切/粘贴 |
| MOD+n | 展开通知面板 |
| MOD+Shift+n | 折叠面板 |
| MOD+Ctrl+r | 旋转设备屏幕 |
MOD 键是可配置的,但在大多数系统上默认为 Alt。
来源: app/src/input_manager.c370-558
输入管理的一个关键方面是处理鼠标和触摸事件时,将窗口坐标转换为帧坐标。此转换考虑了:
来源: app/src/screen.c874-946 app/src/input_manager.c615-630
输入管理系统实现了一些特殊功能,以增强设备控制
虚拟手指:模拟第二个触摸点,用于捏合缩放、旋转和倾斜手势。按住 Ctrl 键并单击时,会在相对于屏幕中心反转的位置创建第二个虚拟触摸点。
鼠标绑定:可配置的鼠标按钮操作,允许不同的鼠标按钮触发 BACK、HOME、APP_SWITCH 等操作。
剪贴板同步:处理计算机和 Android 设备之间的文本复制和粘贴。
游戏手柄支持:处理游戏手柄输入并将其转发给设备。
来源: app/src/input_manager.c334-367 app/src/input_manager.c697-881
屏幕和输入组件紧密集成,屏幕包含输入管理器的实例,输入管理器引用屏幕。这种双向关系允许:
来源: app/src/screen.h25-73 app/src/input_manager.h18-47
两个组件都与 SDL 事件循环集成
sc_screen_handle_event)屏幕管理的核心结构是 sc_screen,它包含:
| 成员 | 描述 |
|---|---|
frame_sink | 用于接收帧的接口实现 |
display | 显示状态(渲染器、纹理) |
im | 输入管理器实例 |
mc | 鼠标捕获实例(用于相对模式) |
fb | 用于传入帧的帧缓冲区 |
fps_counter | FPS 计数器 |
window | SDL 窗口 |
frame_size | 原始视频帧的大小 |
content_size | 旋转后的尺寸 |
orientation | 当前方向 |
rect | 窗口内内容的矩形 |
frame | 当前正在显示的 AVFrame |
fullscreen | 是否处于全屏模式 |
maximized/minimized | 窗口状态 |
paused | 显示更新是否已暂停 |
输入管理的核心结构是 sc_input_manager,它包含
| 成员 | 描述 |
|---|---|
controller | 将消息发送到设备的控制器 |
screen | 屏幕的引用 |
kp/mp/gp | 键盘/鼠标/游戏手柄处理器 |
mouse_bindings | 鼠标按钮操作的配置 |
sdl_shortcut_mods | SDL 快捷键修饰符的表示 |
vfinger_down | 虚拟手指是否激活 |
mouse_buttons_state | 鼠标按钮的当前状态 |
key_repeat | 跟踪重复的按键操作 |
next_sequence | 用于剪贴板同步 |
来源: app/src/input_manager.h18-47
屏幕和输入管理子系统是 scrcpy 的关键部分,负责显示设备屏幕和处理用户输入。它使用 SDL 进行窗口管理和事件处理,实现了多种功能来增强设备控制,并在不同的坐标系统之间进行转换,以确保无论屏幕方向或缩放如何,都能准确地处理输入。
主要方面包括