Compare commits

...

8 Commits

Author SHA1 Message Date
75ce0b5c3f Forward DOWN and UP separately for right-click
The shortcut "back on screen on" is a bit special: the control is
requested by the client, but the actual event injection (POWER or BACK)
is determined on the device.

To properly inject DOWN and UP events for BACK, transmit the action as
a control parameter.

If the screen is off:
 - on DOWN, inject POWER (DOWN and UP) (wake up the device immediately)
 - on UP, do nothing
If the screen is on:
 - on DOWN, inject BACK DOWN
 - on UP, inject BACK UP

A corner case is when the screen turns off between the DOWN and UP
event. In that case, a BACK UP event will be injected, so it's harmless.

With this change, the actual BACK is handled by Android on UP on the
device (instead of DOWN). This is consistent with the keyboard shortcut
(Mod+b) behavior.
2021-04-16 18:51:57 +02:00
5c65f3c170 Forward DOWN and UP separately for middle-click
With this change, the actual HOME is handled by Android on UP on the
device. This is consistent with the keyboard shortcut (MOD+h) behavior.
2021-04-16 18:51:50 +02:00
f0f96fbc3d Prevent forwarding only mouse UP events
Some mouse clicks DOWN are captured for shortcuts, but the matching UP
event was still forwarded to the device.

Instead, capture both DOWN and UP for shortcuts, and do nothing on UP.
2021-04-16 17:53:37 +02:00
edee69d637 Fix options alphabetical order
"verbosity" < "version"
2021-04-16 17:40:39 +02:00
8ef4c044fa Do not forward SDL_DROPFILE event
The event is handled by scrcpy.c, it is not necessary to send it to
screen or input_manager.
2021-04-13 22:37:08 +02:00
c23c38f99d Move resizing workaround to screen.c 2021-04-13 22:36:59 +02:00
65c4f487b3 Set initial fullscreen from screen.c 2021-04-13 22:15:05 +02:00
c6d7f5ee96 Make screen_show_window() static
It is only used from screen.c now.
2021-04-13 22:04:38 +02:00
13 changed files with 114 additions and 61 deletions

View File

@ -187,16 +187,16 @@ Enable "show touches" on start, restore the initial value on exit.
It only shows physical touches (not clicks from scrcpy).
.TP
.B \-v, \-\-version
Print the version of scrcpy.
.TP
.BI "\-V, \-\-verbosity " value
Set the log level ("debug", "info", "warn" or "error").
Default is "info" for release builds, "debug" for debug builds.
.TP
.B \-v, \-\-version
Print the version of scrcpy.
.TP
.B \-w, \-\-stay-awake
Keep the device on while scrcpy is running, when the device is plugged in.

View File

@ -179,9 +179,6 @@ scrcpy_print_usage(const char *arg0) {
" on exit.\n"
" It only shows physical touches (not clicks from scrcpy).\n"
"\n"
" -v, --version\n"
" Print the version of scrcpy.\n"
"\n"
" -V, --verbosity value\n"
" Set the log level (debug, info, warn or error).\n"
#ifndef NDEBUG
@ -189,6 +186,9 @@ scrcpy_print_usage(const char *arg0) {
#else
" Default is info.\n"
#endif
"\n"
" -v, --version\n"
" Print the version of scrcpy.\n"
"\n"
" -w, --stay-awake\n"
" Keep the device on while scrcpy is running, when the device\n"

View File

@ -67,6 +67,9 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
buffer_write32be(&buf[17],
(uint32_t) msg->inject_scroll_event.vscroll);
return 21;
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
buf[1] = msg->inject_keycode.action;
return 2;
case CONTROL_MSG_TYPE_SET_CLIPBOARD: {
buf[1] = !!msg->set_clipboard.paste;
size_t len = write_string(msg->set_clipboard.text,
@ -77,7 +80,6 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
case CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
buf[1] = msg->set_screen_power_mode.mode;
return 2;
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
case CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL:
case CONTROL_MSG_TYPE_GET_CLIPBOARD:

View File

@ -64,6 +64,10 @@ struct control_msg {
int32_t hscroll;
int32_t vscroll;
} inject_scroll_event;
struct {
enum android_keyevent_action action; // action for the BACK key
// screen may only be turned on on ACTION_DOWN
} back_or_screen_on;
struct {
char *text; // owned, to be freed by free()
bool paste;

View File

@ -146,13 +146,25 @@ action_cut(struct controller *controller, int actions) {
}
// turn the screen on if it was off, press BACK otherwise
// If the screen is off, it is turned on only on ACTION_DOWN
static void
press_back_or_turn_screen_on(struct controller *controller) {
press_back_or_turn_screen_on(struct controller *controller, int actions) {
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'press back or turn screen on'");
if (actions & ACTION_DOWN) {
msg.back_or_screen_on.action = AKEY_EVENT_ACTION_DOWN;
if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'press back or turn screen on'");
return;
}
}
if (actions & ACTION_UP) {
msg.back_or_screen_on.action = AKEY_EVENT_ACTION_UP;
if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'press back or turn screen on'");
}
}
}
@ -646,13 +658,15 @@ input_manager_process_mouse_button(struct input_manager *im,
}
bool down = event->type == SDL_MOUSEBUTTONDOWN;
if (!im->forward_all_clicks && down) {
if (!im->forward_all_clicks) {
int action = down ? ACTION_DOWN : ACTION_UP;
if (control && event->button == SDL_BUTTON_RIGHT) {
press_back_or_turn_screen_on(im->controller);
press_back_or_turn_screen_on(im->controller, action);
return;
}
if (control && event->button == SDL_BUTTON_MIDDLE) {
action_home(im->controller, ACTION_DOWN | ACTION_UP);
action_home(im->controller, action);
return;
}
@ -665,7 +679,9 @@ input_manager_process_mouse_button(struct input_manager *im,
bool outside = x < r->x || x >= r->x + r->w
|| y < r->y || y >= r->y + r->h;
if (outside) {
screen_resize_to_fit(im->screen);
if (down) {
screen_resize_to_fit(im->screen);
}
return;
}
}

View File

@ -128,30 +128,6 @@ sdl_init_and_configure(bool display, const char *render_driver,
return true;
}
#if defined(__APPLE__) || defined(__WINDOWS__)
# define CONTINUOUS_RESIZING_WORKAROUND
#endif
#ifdef CONTINUOUS_RESIZING_WORKAROUND
// On Windows and MacOS, resizing blocks the event loop, so resizing events are
// not triggered. As a workaround, handle them in an event handler.
//
// <https://bugzilla.libsdl.org/show_bug.cgi?id=2077>
// <https://stackoverflow.com/a/40693139/1987178>
static int
event_watcher(void *data, SDL_Event *event) {
(void) data;
if (event->type == SDL_WINDOWEVENT
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
// In practice, it seems to always be called from the same thread in
// that specific case. Anyway, it's just a workaround.
screen_render(&screen, true);
}
return 0;
}
#endif
static bool
is_apk(const char *file) {
const char *ext = strrchr(file, '.');
@ -191,7 +167,7 @@ handle_event(SDL_Event *event, const struct scrcpy_options *options) {
action = ACTION_PUSH_FILE;
}
file_handler_request(&file_handler, action, file);
break;
goto end;
}
}
@ -209,11 +185,6 @@ end:
static bool
event_loop(const struct scrcpy_options *options) {
#ifdef CONTINUOUS_RESIZING_WORKAROUND
if (options->display) {
SDL_AddEventWatch(event_watcher, NULL);
}
#endif
SDL_Event event;
while (SDL_WaitEvent(&event)) {
enum event_result result = handle_event(&event, options);
@ -394,6 +365,7 @@ scrcpy(const struct scrcpy_options *options) {
.window_borderless = options->window_borderless,
.rotation = options->rotation,
.mipmaps = options->mipmaps,
.fullscreen = options->fullscreen,
};
if (!screen_init(&screen, &video_buffer, &fps_counter,
@ -411,10 +383,6 @@ scrcpy(const struct scrcpy_options *options) {
LOGW("Could not request 'set screen power mode'");
}
}
if (options->fullscreen) {
screen_switch_fullscreen(&screen);
}
}
// now we consumed the header values, the socket receives the video stream

View File

@ -239,6 +239,29 @@ create_texture(struct screen *screen) {
return texture;
}
#if defined(__APPLE__) || defined(__WINDOWS__)
# define CONTINUOUS_RESIZING_WORKAROUND
#endif
#ifdef CONTINUOUS_RESIZING_WORKAROUND
// On Windows and MacOS, resizing blocks the event loop, so resizing events are
// not triggered. As a workaround, handle them in an event handler.
//
// <https://bugzilla.libsdl.org/show_bug.cgi?id=2077>
// <https://stackoverflow.com/a/40693139/1987178>
static int
event_watcher(void *data, SDL_Event *event) {
struct screen *screen = data;
if (event->type == SDL_WINDOWEVENT
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
// In practice, it seems to always be called from the same thread in
// that specific case. Anyway, it's just a workaround.
screen_render(screen, true);
}
return 0;
}
#endif
bool
screen_init(struct screen *screen, struct video_buffer *vb,
struct fps_counter *fps_counter,
@ -362,10 +385,18 @@ screen_init(struct screen *screen, struct video_buffer *vb,
screen_update_content_rect(screen);
if (params->fullscreen) {
screen_switch_fullscreen(screen);
}
#ifdef CONTINUOUS_RESIZING_WORKAROUND
SDL_AddEventWatch(event_watcher, screen);
#endif
return true;
}
void
static void
screen_show_window(struct screen *screen) {
SDL_ShowWindow(screen->window);
}

View File

@ -52,6 +52,8 @@ struct screen_params {
uint8_t rotation;
bool mipmaps;
bool fullscreen;
};
// initialize screen, create window, renderer and texture (window is hidden)
@ -60,10 +62,6 @@ screen_init(struct screen *screen, struct video_buffer *vb,
struct fps_counter *fps_counter,
const struct screen_params *params);
// show the window
void
screen_show_window(struct screen *screen);
// destroy window, renderer and texture (if any)
void
screen_destroy(struct screen *screen);

View File

@ -146,14 +146,18 @@ static void test_serialize_inject_scroll_event(void) {
static void test_serialize_back_or_screen_on(void) {
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
.back_or_screen_on = {
.action = AKEY_EVENT_ACTION_UP,
},
};
unsigned char buf[CONTROL_MSG_MAX_SIZE];
size_t size = control_msg_serialize(&msg, buf);
assert(size == 1);
assert(size == 2);
const unsigned char expected[] = {
CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
0x01, // AKEY_EVENT_ACTION_UP
};
assert(!memcmp(buf, expected, sizeof(expected)));
}

View File

@ -71,6 +71,13 @@ public final class ControlMessage {
return msg;
}
public static ControlMessage createBackOrScreenOn(int action) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_BACK_OR_SCREEN_ON;
msg.action = action;
return msg;
}
public static ControlMessage createSetClipboard(String text, boolean paste) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_SET_CLIPBOARD;

View File

@ -11,6 +11,7 @@ public class ControlMessageReader {
static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 13;
static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 27;
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
static final int BACK_OR_SCREEN_ON_LENGTH = 1;
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 1;
@ -66,13 +67,15 @@ public class ControlMessageReader {
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
msg = parseInjectScrollEvent();
break;
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
msg = parseBackOrScreenOnEvent();
break;
case ControlMessage.TYPE_SET_CLIPBOARD:
msg = parseSetClipboard();
break;
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
msg = parseSetScreenPowerMode();
break;
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
case ControlMessage.TYPE_GET_CLIPBOARD:
@ -150,6 +153,14 @@ public class ControlMessageReader {
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll);
}
private ControlMessage parseBackOrScreenOnEvent() {
if (buffer.remaining() < BACK_OR_SCREEN_ON_LENGTH) {
return null;
}
int action = toUnsigned(buffer.get());
return ControlMessage.createBackOrScreenOn(action);
}
private ControlMessage parseSetClipboard() {
if (buffer.remaining() < SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH) {
return null;

View File

@ -101,7 +101,7 @@ public class Controller {
break;
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
if (device.supportsInputEvents()) {
pressBackOrTurnScreenOn();
pressBackOrTurnScreenOn(msg.getAction());
}
break;
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
@ -255,12 +255,22 @@ public class Controller {
}, 200, TimeUnit.MILLISECONDS);
}
private boolean pressBackOrTurnScreenOn() {
int keycode = Device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER;
if (keepPowerModeOff && keycode == KeyEvent.KEYCODE_POWER) {
private boolean pressBackOrTurnScreenOn(int action) {
if (Device.isScreenOn()) {
return device.injectKeyEvent(action, KeyEvent.KEYCODE_BACK, 0, 0);
}
// Screen is off
// Only press POWER on ACTION_DOWN
if (action != KeyEvent.ACTION_DOWN) {
// do nothing,
return true;
}
if (keepPowerModeOff) {
schedulePowerModeOff();
}
return device.injectKeycode(keycode);
return device.injectKeycode(KeyEvent.KEYCODE_POWER);
}
private boolean setClipboard(String text, boolean paste) {

View File

@ -154,6 +154,7 @@ public class ControlMessageReaderTest {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_BACK_OR_SCREEN_ON);
dos.writeByte(KeyEvent.ACTION_UP);
byte[] packet = bos.toByteArray();
@ -161,6 +162,7 @@ public class ControlMessageReaderTest {
ControlMessage event = reader.next();
Assert.assertEquals(ControlMessage.TYPE_BACK_OR_SCREEN_ON, event.getType());
Assert.assertEquals(KeyEvent.ACTION_DOWN, event.getAction());
}
@Test