Enable controls for camera video source

This will allow the implementation of camera-specific shortcuts.

Co-authored-by: Tommie <teh420@gmail.com>
This commit is contained in:
Romain Vimont
2025-11-02 14:47:40 +01:00
parent 0a81dd52ea
commit 13e2e3d36b
7 changed files with 138 additions and 97 deletions

View File

@@ -2928,7 +2928,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
} }
#endif #endif
if (opts->control) { if (opts->control && opts->video_source == SC_VIDEO_SOURCE_DISPLAY) {
if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) { if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) {
opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA
: SC_KEYBOARD_INPUT_MODE_SDK; : SC_KEYBOARD_INPUT_MODE_SDK;
@@ -3106,8 +3106,10 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
} }
if (opts->control) { if (opts->control) {
LOGI("Camera video source: control disabled"); // Disable all inputs for camera
opts->control = false; opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_DISABLED;
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_DISABLED;
opts->gamepad_input_mode = SC_GAMEPAD_INPUT_MODE_DISABLED;
} }
} else if (opts->camera_id } else if (opts->camera_id
|| opts->camera_ar || opts->camera_ar

View File

@@ -29,6 +29,7 @@ sc_input_manager_init(struct sc_input_manager *im,
im->kp = params->kp; im->kp = params->kp;
im->mp = params->mp; im->mp = params->mp;
im->gp = params->gp; im->gp = params->gp;
im->camera = params->camera;
im->mouse_bindings = params->mouse_bindings; im->mouse_bindings = params->mouse_bindings;
im->legacy_paste = params->legacy_paste; im->legacy_paste = params->legacy_paste;
@@ -52,7 +53,7 @@ sc_input_manager_init(struct sc_input_manager *im,
static void static void
send_keycode(struct sc_input_manager *im, enum android_keycode keycode, send_keycode(struct sc_input_manager *im, enum android_keycode keycode,
enum sc_action action, const char *name) { enum sc_action action, const char *name) {
assert(im->controller && im->kp); assert(im->controller && im->kp && !im->camera);
// send DOWN event // send DOWN event
struct sc_control_msg msg; struct sc_control_msg msg;
@@ -109,7 +110,7 @@ action_menu(struct sc_input_manager *im, enum sc_action action) {
static void static void
press_back_or_turn_screen_on(struct sc_input_manager *im, press_back_or_turn_screen_on(struct sc_input_manager *im,
enum sc_action action) { enum sc_action action) {
assert(im->controller && im->kp); assert(im->controller && im->kp && !im->camera);
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON; msg.type = SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
@@ -124,7 +125,7 @@ press_back_or_turn_screen_on(struct sc_input_manager *im,
static void static void
expand_notification_panel(struct sc_input_manager *im) { expand_notification_panel(struct sc_input_manager *im) {
assert(im->controller); assert(im->controller && !im->camera);
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL; msg.type = SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL;
@@ -136,7 +137,7 @@ expand_notification_panel(struct sc_input_manager *im) {
static void static void
expand_settings_panel(struct sc_input_manager *im) { expand_settings_panel(struct sc_input_manager *im) {
assert(im->controller); assert(im->controller && !im->camera);
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL; msg.type = SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL;
@@ -148,7 +149,7 @@ expand_settings_panel(struct sc_input_manager *im) {
static void static void
collapse_panels(struct sc_input_manager *im) { collapse_panels(struct sc_input_manager *im) {
assert(im->controller); assert(im->controller && !im->camera);
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS; msg.type = SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS;
@@ -160,7 +161,7 @@ collapse_panels(struct sc_input_manager *im) {
static bool static bool
get_device_clipboard(struct sc_input_manager *im, enum sc_copy_key copy_key) { get_device_clipboard(struct sc_input_manager *im, enum sc_copy_key copy_key) {
assert(im->controller && im->kp); assert(im->controller && im->kp && !im->camera);
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_GET_CLIPBOARD; msg.type = SC_CONTROL_MSG_TYPE_GET_CLIPBOARD;
@@ -177,7 +178,7 @@ get_device_clipboard(struct sc_input_manager *im, enum sc_copy_key copy_key) {
static bool static bool
set_device_clipboard(struct sc_input_manager *im, bool paste, set_device_clipboard(struct sc_input_manager *im, bool paste,
uint64_t sequence) { uint64_t sequence) {
assert(im->controller && im->kp); assert(im->controller && im->kp && !im->camera);
char *text = SDL_GetClipboardText(); char *text = SDL_GetClipboardText();
if (!text) { if (!text) {
@@ -209,7 +210,7 @@ set_device_clipboard(struct sc_input_manager *im, bool paste,
static void static void
set_display_power(struct sc_input_manager *im, bool on) { set_display_power(struct sc_input_manager *im, bool on) {
assert(im->controller); assert(im->controller && !im->camera);
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_SET_DISPLAY_POWER; msg.type = SC_CONTROL_MSG_TYPE_SET_DISPLAY_POWER;
@@ -236,7 +237,7 @@ switch_fps_counter_state(struct sc_input_manager *im) {
static void static void
clipboard_paste(struct sc_input_manager *im) { clipboard_paste(struct sc_input_manager *im) {
assert(im->controller && im->kp); assert(im->controller && im->kp && !im->camera);
char *text = SDL_GetClipboardText(); char *text = SDL_GetClipboardText();
if (!text) { if (!text) {
@@ -267,7 +268,7 @@ clipboard_paste(struct sc_input_manager *im) {
static void static void
rotate_device(struct sc_input_manager *im) { rotate_device(struct sc_input_manager *im) {
assert(im->controller); assert(im->controller && !im->camera);
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_ROTATE_DEVICE; msg.type = SC_CONTROL_MSG_TYPE_ROTATE_DEVICE;
@@ -279,7 +280,7 @@ rotate_device(struct sc_input_manager *im) {
static void static void
open_hard_keyboard_settings(struct sc_input_manager *im) { open_hard_keyboard_settings(struct sc_input_manager *im) {
assert(im->controller); assert(im->controller && !im->camera);
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS; msg.type = SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS;
@@ -313,7 +314,7 @@ apply_orientation_transform(struct sc_input_manager *im,
static void static void
sc_input_manager_process_text_input(struct sc_input_manager *im, sc_input_manager_process_text_input(struct sc_input_manager *im,
const SDL_TextInputEvent *event) { const SDL_TextInputEvent *event) {
if (!im->kp || im->screen->paused) { if (im->camera || !im->kp || im->screen->paused) {
return; return;
} }
@@ -473,7 +474,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
} }
return; return;
} }
if (control) { if (control && !im->camera) {
switch (sdl_keycode) { switch (sdl_keycode) {
case SDLK_H: case SDLK_H:
if (im->kp && !shift && !repeat && !paused) { if (im->kp && !shift && !repeat && !paused) {
@@ -578,6 +579,8 @@ sc_input_manager_process_key(struct sc_input_manager *im,
return; return;
} }
assert(!im->camera);
uint64_t ack_to_wait = SC_SEQUENCE_INVALID; uint64_t ack_to_wait = SC_SEQUENCE_INVALID;
bool is_ctrl_v = ctrl && !shift && sdl_keycode == SDLK_V && down && !repeat; bool is_ctrl_v = ctrl && !shift && sdl_keycode == SDLK_V && down && !repeat;
if (im->clipboard_autosync && is_ctrl_v) { if (im->clipboard_autosync && is_ctrl_v) {
@@ -649,7 +652,7 @@ sc_input_manager_get_position(struct sc_input_manager *im, int32_t x,
static void static void
sc_input_manager_process_mouse_motion(struct sc_input_manager *im, sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
const SDL_MouseMotionEvent *event) { const SDL_MouseMotionEvent *event) {
if (!im->mp || im->screen->paused) { if (im->camera || !im->mp || im->screen->paused) {
return; return;
} }
@@ -688,7 +691,7 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
static void static void
sc_input_manager_process_touch(struct sc_input_manager *im, sc_input_manager_process_touch(struct sc_input_manager *im,
const SDL_TouchFingerEvent *event) { const SDL_TouchFingerEvent *event) {
if (!im->mp || im->screen->paused) { if (im->camera || !im->mp || im->screen->paused) {
return; return;
} }
@@ -743,6 +746,10 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
// some mouse events do not interact with the device, so process the event // some mouse events do not interact with the device, so process the event
// even if control is disabled // even if control is disabled
if (im->camera) {
return;
}
if (event->which == SDL_TOUCH_MOUSEID) { if (event->which == SDL_TOUCH_MOUSEID) {
// simulated from touch events, so it's a duplicate // simulated from touch events, so it's a duplicate
return; return;
@@ -910,7 +917,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
static void static void
sc_input_manager_process_mouse_wheel(struct sc_input_manager *im, sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
const SDL_MouseWheelEvent *event) { const SDL_MouseWheelEvent *event) {
if (!im->kp || im->screen->paused) { if (im->camera || !im->kp || im->screen->paused) {
return; return;
} }
@@ -940,7 +947,7 @@ sc_input_manager_process_gamepad_device(struct sc_input_manager *im,
const SDL_GamepadDeviceEvent *event) { const SDL_GamepadDeviceEvent *event) {
// Handle device added or removed even if paused // Handle device added or removed even if paused
if (!im->gp) { if (im->camera || !im->gp) {
return; return;
} }
@@ -985,7 +992,7 @@ sc_input_manager_process_gamepad_device(struct sc_input_manager *im,
static void static void
sc_input_manager_process_gamepad_axis(struct sc_input_manager *im, sc_input_manager_process_gamepad_axis(struct sc_input_manager *im,
const SDL_GamepadAxisEvent *event) { const SDL_GamepadAxisEvent *event) {
if (!im->gp || im->screen->paused) { if (im->camera || !im->gp || im->screen->paused) {
return; return;
} }
@@ -1005,7 +1012,7 @@ sc_input_manager_process_gamepad_axis(struct sc_input_manager *im,
static void static void
sc_input_manager_process_gamepad_button(struct sc_input_manager *im, sc_input_manager_process_gamepad_button(struct sc_input_manager *im,
const SDL_GamepadButtonEvent *event) { const SDL_GamepadButtonEvent *event) {
if (!im->gp || im->screen->paused) { if (im->camera || !im->gp || im->screen->paused) {
return; return;
} }
@@ -1031,7 +1038,7 @@ is_apk(const char *file) {
static void static void
sc_input_manager_process_file(struct sc_input_manager *im, sc_input_manager_process_file(struct sc_input_manager *im,
const SDL_DropEvent *event) { const SDL_DropEvent *event) {
if (!im->controller) { if (im->camera || !im->controller) {
return; return;
} }

View File

@@ -24,6 +24,8 @@ struct sc_input_manager {
struct sc_mouse_processor *mp; struct sc_mouse_processor *mp;
struct sc_gamepad_processor *gp; struct sc_gamepad_processor *gp;
bool camera;
struct sc_mouse_bindings mouse_bindings; struct sc_mouse_bindings mouse_bindings;
bool legacy_paste; bool legacy_paste;
bool clipboard_autosync; bool clipboard_autosync;
@@ -53,6 +55,7 @@ struct sc_input_manager_params {
struct sc_key_processor *kp; struct sc_key_processor *kp;
struct sc_mouse_processor *mp; struct sc_mouse_processor *mp;
struct sc_gamepad_processor *gp; struct sc_gamepad_processor *gp;
bool camera;
struct sc_mouse_bindings mouse_bindings; struct sc_mouse_bindings mouse_bindings;
bool legacy_paste; bool legacy_paste;

View File

@@ -802,6 +802,7 @@ aoa_complete:
struct sc_screen_params screen_params = { struct sc_screen_params screen_params = {
.video = options->video_playback, .video = options->video_playback,
.camera = options->video_source == SC_VIDEO_SOURCE_CAMERA,
.controller = controller, .controller = controller,
.fp = fp, .fp = fp,
.kp = kp, .kp = kp,

View File

@@ -302,6 +302,7 @@ sc_screen_init(struct sc_screen *screen,
screen->orientation = SC_ORIENTATION_0; screen->orientation = SC_ORIENTATION_0;
screen->video = params->video; screen->video = params->video;
screen->camera = params->camera;
screen->req.x = params->window_x; screen->req.x = params->window_x;
screen->req.y = params->window_y; screen->req.y = params->window_y;
@@ -412,6 +413,7 @@ sc_screen_init(struct sc_screen *screen,
.kp = params->kp, .kp = params->kp,
.mp = params->mp, .mp = params->mp,
.gp = params->gp, .gp = params->gp,
.camera = params->camera,
.mouse_bindings = params->mouse_bindings, .mouse_bindings = params->mouse_bindings,
.legacy_paste = params->legacy_paste, .legacy_paste = params->legacy_paste,
.clipboard_autosync = params->clipboard_autosync, .clipboard_autosync = params->clipboard_autosync,

View File

@@ -30,6 +30,7 @@ struct sc_screen {
#endif #endif
bool video; bool video;
bool camera;
struct sc_display display; struct sc_display display;
struct sc_input_manager im; struct sc_input_manager im;
@@ -74,6 +75,7 @@ struct sc_screen {
struct sc_screen_params { struct sc_screen_params {
bool video; bool video;
bool camera;
struct sc_controller *controller; struct sc_controller *controller;
struct sc_file_pusher *fp; struct sc_file_pusher *fp;

View File

@@ -13,6 +13,7 @@ import com.genymobile.scrcpy.device.Size;
import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.util.LogUtils; import com.genymobile.scrcpy.util.LogUtils;
import com.genymobile.scrcpy.video.SurfaceCapture; import com.genymobile.scrcpy.video.SurfaceCapture;
import com.genymobile.scrcpy.video.VideoSource;
import com.genymobile.scrcpy.video.VirtualDisplayListener; import com.genymobile.scrcpy.video.VirtualDisplayListener;
import com.genymobile.scrcpy.wrappers.ClipboardManager; import com.genymobile.scrcpy.wrappers.ClipboardManager;
import com.genymobile.scrcpy.wrappers.InputManager; import com.genymobile.scrcpy.wrappers.InputManager;
@@ -75,6 +76,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
private UhidManager uhidManager; private UhidManager uhidManager;
private final boolean camera;
private final int displayId; private final int displayId;
private final boolean supportsInputEvents; private final boolean supportsInputEvents;
private final ControlChannel controlChannel; private final ControlChannel controlChannel;
@@ -101,9 +103,22 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
private SurfaceCapture surfaceCapture; private SurfaceCapture surfaceCapture;
public Controller(ControlChannel controlChannel, CleanUp cleanUp, Options options) { public Controller(ControlChannel controlChannel, CleanUp cleanUp, Options options) {
this.displayId = options.getDisplayId(); this.camera = options.getVideoSource() == VideoSource.CAMERA;
this.controlChannel = controlChannel; this.controlChannel = controlChannel;
this.cleanUp = cleanUp; this.cleanUp = cleanUp;
if (this.camera) {
// Unused for camera
this.displayId = Device.DISPLAY_ID_NONE;
this.supportsInputEvents = false;
this.sender = null;
this.clipboardAutosync = false;
this.powerOn = false;
return;
}
this.displayId = options.getDisplayId();
this.clipboardAutosync = options.getClipboardAutosync(); this.clipboardAutosync = options.getClipboardAutosync();
this.powerOn = options.getPowerOn(); this.powerOn = options.getPowerOn();
initPointers(); initPointers();
@@ -201,7 +216,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
private void control() throws IOException { private void control() throws IOException {
// on start, power on the device // on start, power on the device
if (powerOn && displayId == 0 && !Device.isScreenOn(displayId)) { if (!camera && powerOn && displayId == 0 && !Device.isScreenOn(displayId)) {
Device.pressReleaseKeycode(KeyEvent.KEYCODE_POWER, displayId, Device.INJECT_MODE_ASYNC); Device.pressReleaseKeycode(KeyEvent.KEYCODE_POWER, displayId, Device.INJECT_MODE_ASYNC);
// dirty hack // dirty hack
@@ -236,24 +251,30 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
} }
}, "control-recv"); }, "control-recv");
thread.start(); thread.start();
if (sender != null) {
sender.start(); sender.start();
} }
}
@Override @Override
public void stop() { public void stop() {
if (thread != null) { if (thread != null) {
thread.interrupt(); thread.interrupt();
} }
if (sender != null) {
sender.stop(); sender.stop();
} }
}
@Override @Override
public void join() throws InterruptedException { public void join() throws InterruptedException {
if (thread != null) { if (thread != null) {
thread.join(); thread.join();
} }
if (sender != null) {
sender.join(); sender.join();
} }
}
private boolean handleEvent() throws IOException { private boolean handleEvent() throws IOException {
ControlMessage msg; ControlMessage msg;
@@ -269,6 +290,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
int type = msg.getType(); int type = msg.getType();
if (!camera) {
switch (type) { switch (type) {
case ControlMessage.TYPE_INJECT_KEYCODE: case ControlMessage.TYPE_INJECT_KEYCODE:
if (supportsInputEvents) { if (supportsInputEvents) {
@@ -282,7 +304,8 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
return true; return true;
case ControlMessage.TYPE_INJECT_TOUCH_EVENT: case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
if (supportsInputEvents) { if (supportsInputEvents) {
injectTouch(msg.getAction(), msg.getPointerId(), msg.getPosition(), msg.getPressure(), msg.getActionButton(), msg.getButtons()); injectTouch(
msg.getAction(), msg.getPointerId(), msg.getPosition(), msg.getPressure(), msg.getActionButton(), msg.getButtons());
} }
return true; return true;
case ControlMessage.TYPE_INJECT_SCROLL_EVENT: case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
@@ -339,6 +362,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
default: default:
// fall through // fall through
} }
}
throw new AssertionError("Unexpected message type: " + type); throw new AssertionError("Unexpected message type: " + type);
} }