Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ enum {
OPT_NO_VD_SYSTEM_DECORATIONS,
OPT_NO_VD_DESTROY_CONTENT,
OPT_DISPLAY_IME_POLICY,
OPT_OTG_AUTOMATIC_SCREENSHOT,
};

struct sc_option {
Expand Down Expand Up @@ -1063,6 +1064,13 @@ static const struct sc_option options[] = {
.text = "Set the initial window height.\n"
"Default is 0 (automatic).",
},
{
.longopt_id = OPT_OTG_AUTOMATIC_SCREENSHOT,
.longopt = "otg-automatic-screenshot",
.text = "Take a screenshot automatically each time mouse capture is exited in OTG mode.\n"
"This is particularly useful when OTG mode is used to enable USB debugging on a phone with a broken screen.\n"
"The screenshot will display the mouse pointer that will indicate where to move next."
},
};

static const struct sc_shortcut shortcuts[] = {
Expand Down Expand Up @@ -2821,6 +2829,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}
break;
case OPT_OTG_AUTOMATIC_SCREENSHOT:
opts->otg_automatic_screenshot = true;
break;
default:
// getopt prints the error message on stderr
return false;
Expand Down
14 changes: 14 additions & 0 deletions app/src/hid/hid_keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,3 +343,17 @@ void sc_hid_keyboard_generate_open(struct sc_hid_open *hid_open) {
void sc_hid_keyboard_generate_close(struct sc_hid_close *hid_close) {
hid_close->hid_id = SC_HID_ID_KEYBOARD;
}

void
sc_hid_keyboard_generate_screenshot_press_input(struct sc_hid_input *hid_input) {
sc_hid_keyboard_input_init(hid_input);

uint8_t *keys_data = &hid_input->data[SC_HID_KEYBOARD_INDEX_KEYS];
keys_data[0] = 0x66; // Power : Usage ID 0x66
keys_data[1] = 0x81; // Volume Down : Usage ID 0x81
}

void
sc_hid_keyboard_generate_screenshot_release_input(struct sc_hid_input *hid_input) {
sc_hid_keyboard_input_init(hid_input);
}
10 changes: 9 additions & 1 deletion app/src/hid/hid_keyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
// Maybe SDL_Keycode is used by most people, but SDL_Scancode is taken from USB
// HID protocol.
// 0x65 is Application, typically AT-101 Keyboard ends here.
#define SC_HID_KEYBOARD_KEYS 0x66
// Increase the maximum value to 0x82 instead of the previous value 0x66 so that
// we can send Power (0x66) and Volume Down (0x81) to take screenshots.
#define SC_HID_KEYBOARD_KEYS 0x82

#define SC_HID_ID_KEYBOARD 1

Expand Down Expand Up @@ -51,4 +53,10 @@ bool
sc_hid_keyboard_generate_input_from_mods(struct sc_hid_input *hid_input,
uint16_t mods_state);

void
sc_hid_keyboard_generate_screenshot_press_input(struct sc_hid_input *hid_input);

void
sc_hid_keyboard_generate_screenshot_release_input(struct sc_hid_input *hid_input);

#endif
20 changes: 12 additions & 8 deletions app/src/mouse_capture.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ sc_mouse_capture_is_capture_key(struct sc_mouse_capture *mc, SDL_Keycode key) {
return sc_shortcut_mods_is_shortcut_key(mc->sdl_mouse_capture_keys, key);
}

bool
int
sc_mouse_capture_handle_event(struct sc_mouse_capture *mc,
const SDL_Event *event) {
switch (event->type) {
case SDL_WINDOWEVENT:
if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
sc_mouse_capture_set_active(mc, false);
return true;
return SC_MOUSE_CAPTURE_EVENT_CONSUMED;
}
break;
case SDL_KEYDOWN: {
Expand All @@ -37,7 +37,7 @@ sc_mouse_capture_handle_event(struct sc_mouse_capture *mc,
mc->mouse_capture_key_pressed = 0;
}
// Mouse capture keys are never forwarded to the device
return true;
return SC_MOUSE_CAPTURE_EVENT_CONSUMED;
}
break;
}
Expand All @@ -52,7 +52,11 @@ sc_mouse_capture_handle_event(struct sc_mouse_capture *mc,
sc_mouse_capture_toggle(mc);
}
// Mouse capture keys are never forwarded to the device
return true;
if (sc_mouse_capture_is_active(mc)) {
return SC_MOUSE_CAPTURE_EVENT_CONSUMED;
} else {
return SC_MOUSE_CAPTURE_EVENT_CONSUMED_EXIT_CAPTURE_MODE;
}
}
break;
}
Expand All @@ -62,24 +66,24 @@ sc_mouse_capture_handle_event(struct sc_mouse_capture *mc,
if (!sc_mouse_capture_is_active(mc)) {
// The mouse will be captured on SDL_MOUSEBUTTONUP, so consume
// the event
return true;
return SC_MOUSE_CAPTURE_EVENT_CONSUMED;
}
break;
case SDL_MOUSEBUTTONUP:
if (!sc_mouse_capture_is_active(mc)) {
sc_mouse_capture_set_active(mc, true);
return true;
return SC_MOUSE_CAPTURE_EVENT_CONSUMED;
}
break;
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP:
// Touch events are not compatible with relative mode
// (coordinates are not relative), so consume the event
return true;
return SC_MOUSE_CAPTURE_EVENT_CONSUMED;
}

return false;
return SC_MOUSE_CAPTURE_EVENT_UNCONSUMED;
}

void
Expand Down
10 changes: 8 additions & 2 deletions app/src/mouse_capture.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ struct sc_mouse_capture {

};

enum {
SC_MOUSE_CAPTURE_EVENT_UNCONSUMED,
SC_MOUSE_CAPTURE_EVENT_CONSUMED,
SC_MOUSE_CAPTURE_EVENT_CONSUMED_EXIT_CAPTURE_MODE,
};

void
sc_mouse_capture_init(struct sc_mouse_capture *mc, SDL_Window *window,
uint8_t shortcut_mods);
Expand All @@ -30,8 +36,8 @@ sc_mouse_capture_is_active(struct sc_mouse_capture *mc);
void
sc_mouse_capture_toggle(struct sc_mouse_capture *mc);

// Return true if it consumed the event
bool
// Return if it consumed the event
int
sc_mouse_capture_handle_event(struct sc_mouse_capture *mc,
const SDL_Event *event);

Expand Down
1 change: 1 addition & 0 deletions app/src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ const struct scrcpy_options scrcpy_options_default = {
.angle = NULL,
.vd_destroy_content = true,
.vd_system_decorations = true,
.otg_automatic_screenshot = false,
};

enum sc_orientation
Expand Down
1 change: 1 addition & 0 deletions app/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ struct scrcpy_options {
const char *start_app;
bool vd_destroy_content;
bool vd_system_decorations;
bool otg_automatic_screenshot;
};

extern const struct scrcpy_options scrcpy_options_default;
Expand Down
8 changes: 8 additions & 0 deletions app/src/trait/key_processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ struct sc_key_processor_ops {
void
(*process_text)(struct sc_key_processor *kp,
const struct sc_text_event *event);

/**
* Take screenshot
*
* This function is optional.
*/
bool
(*take_screenshot)(struct sc_key_processor *kp);
};

#endif
27 changes: 27 additions & 0 deletions app/src/usb/keyboard_aoa.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "keyboard_aoa.h"

#include <assert.h>
#include <unistd.h>

#include "input_events.h"
#include "util/log.h"
Expand Down Expand Up @@ -62,6 +63,31 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
}
}

static bool
sc_key_processor_take_screenshot(struct sc_key_processor *kp) {

struct sc_keyboard_aoa *kb = DOWNCAST(kp);

struct sc_hid_input hid_input;

sc_hid_keyboard_generate_screenshot_press_input(&hid_input);
if (!sc_aoa_push_input(kb->aoa, &hid_input)) {
LOGW("Could not push AOA HID input (screenshot press)");
return false;
}

// sleep for 100ms
usleep(100000);

sc_hid_keyboard_generate_screenshot_release_input(&hid_input);
if (!sc_aoa_push_input(kb->aoa, &hid_input)) {
LOGW("Could not push AOA HID input (screenshot release)");
return false;
}

return true;
}

bool
sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa) {
kb->aoa = aoa;
Expand All @@ -84,6 +110,7 @@ sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa) {
// Never forward text input via HID (all the keys are injected
// separately)
.process_text = NULL,
.take_screenshot = sc_key_processor_take_screenshot,
};

// Clipboard synchronization is requested over the control socket, while HID
Expand Down
1 change: 1 addition & 0 deletions app/src/usb/scrcpy_otg.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ scrcpy_otg(struct scrcpy_options *options) {
.window_height = options->window_height,
.window_borderless = options->window_borderless,
.shortcut_mods = options->shortcut_mods,
.otg_automatic_screenshot = options->otg_automatic_screenshot,
};

ok = sc_screen_otg_init(&s->screen_otg, &params);
Expand Down
23 changes: 22 additions & 1 deletion app/src/usb/screen_otg.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ sc_screen_otg_init(struct sc_screen_otg *screen,
sc_mouse_capture_set_active(&screen->mc, true);
}

screen->otg_automatic_screenshot = params->otg_automatic_screenshot;

return true;

error_destroy_window:
Expand Down Expand Up @@ -118,6 +120,15 @@ sc_screen_otg_process_key(struct sc_screen_otg *screen,
kp->ops->process_key(kp, &evt, SC_SEQUENCE_INVALID);
}

static void
sc_screen_otg_take_screenshot(struct sc_screen_otg *screen) {
assert(screen->keyboard);
struct sc_key_processor *kp = &screen->keyboard->key_processor;

assert(kp->ops->take_screenshot);
kp->ops->take_screenshot(kp);
}

static void
sc_screen_otg_process_mouse_motion(struct sc_screen_otg *screen,
const SDL_MouseMotionEvent *event) {
Expand Down Expand Up @@ -261,11 +272,21 @@ sc_screen_otg_process_gamepad_button(struct sc_screen_otg *screen,

void
sc_screen_otg_handle_event(struct sc_screen_otg *screen, SDL_Event *event) {
if (sc_mouse_capture_handle_event(&screen->mc, event)) {
int event_state = sc_mouse_capture_handle_event(&screen->mc, event);
if (event_state == SC_MOUSE_CAPTURE_EVENT_CONSUMED) {
// The mouse capture handler consumed the event
return;
} else if (event_state == SC_MOUSE_CAPTURE_EVENT_CONSUMED_EXIT_CAPTURE_MODE) {
if (screen->otg_automatic_screenshot) {
// send Volume Down + Power event to take a screenshot
LOGI("Taking screenshot");
sc_screen_otg_take_screenshot(screen);
}
// The mouse capture handler consumed the event
return;
}

// assume event_state is SC_MOUSE_CAPTURE_EVENT_UNCONSUMED
switch (event->type) {
case SDL_WINDOWEVENT:
switch (event->window.event) {
Expand Down
2 changes: 2 additions & 0 deletions app/src/usb/screen_otg.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct sc_screen_otg {
SDL_Texture *texture;

struct sc_mouse_capture mc;
bool otg_automatic_screenshot;
};

struct sc_screen_otg_params {
Expand All @@ -37,6 +38,7 @@ struct sc_screen_otg_params {
uint16_t window_height;
bool window_borderless;
uint8_t shortcut_mods; // OR of enum sc_shortcut_mod values
bool otg_automatic_screenshot;
};

bool
Expand Down