Compare commits

..

10 Commits

Author SHA1 Message Date
Romain Vimont
0933378545 Apply buttons mask if not --forward-all-clicks
If --forward-all-clicks is not set, then only left clicks are forwarded.
For consistency, also mask the buttons state in other events.
2021-12-29 23:52:08 +01:00
Romain Vimont
e7e2b3d90f Reorder mouse processor ops
Group the mouse events callbacks before the touch event callback.
2021-12-29 23:20:35 +01:00
Romain Vimont
fd9b4a5133 Simplify mouse injection implementation
The static functions are now so simple they become unnecessary: the
control message may be initialized directly instead.
2021-12-29 23:19:04 +01:00
Romain Vimont
d3e10e2146 Make some event conversions infaillible
When the implementation handles all possible input values, it may never
fail.
2021-12-29 17:04:56 +01:00
Romain Vimont
8fcc5e9e4e Use scrcpy input events for mouse processors
Pass scrcpy input events instead of SDL input events to mouse
processors.

These events represent exactly what mouse processors need, abstracted
from any visual orientation and scaling applied on the SDL window.

This makes the mouse processors independent of the "screen" instance,
and the implementation source code independent of the SDL API.
2021-12-29 16:56:11 +01:00
Romain Vimont
c0cb944c94 Use scrcpy input events for key processors
Pass scrcpy input events instead of SDL input events to key processors.

This makes the source code of key processors independent of the SDL API.
2021-12-29 16:14:34 +01:00
Romain Vimont
57905a0e6d Use common sc_action in input manager
Now that the scrcpy input events API exposes a sc_action enum, use the
same from the input manager.
2021-12-29 15:56:59 +01:00
Romain Vimont
04b2b1d58f Add intermediate input events layer
This aims to make the key/mouse processors independent of the "screen",
instead of processing SDL events themselves.

In particular, these scrcpy events are not impacted by any UI window
scaling or rotation (contrary to SDL events).
2021-12-29 15:55:43 +01:00
Romain Vimont
ccfb4d3cfd Rename SC_MOD_* to SC_SHORTCUT_MOD_*
This will avoid conflicts with new SC_MOD_* constants.
2021-12-29 01:39:55 +01:00
Romain Vimont
49a788841d Remove actions bitset
The input manager exposed functions taking an "actions" parameter,
containing a bitmask-OR of ACTION_UP and ACTION_DOWN.

But they are never called with both actions simultaneously anymore, so
simplify.

Refs 964b6d2243
Refs d0739911a3
2021-12-29 01:34:54 +01:00
36 changed files with 137 additions and 701 deletions

View File

@@ -188,7 +188,7 @@
identification within third-party archives. identification within third-party archives.
Copyright (C) 2018 Genymobile Copyright (C) 2018 Genymobile
Copyright (C) 2018-2022 Romain Vimont Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -672,7 +672,7 @@ Baca [halaman pengembang].
## Lisensi ## Lisensi
Copyright (C) 2018 Genymobile Copyright (C) 2018 Genymobile
Copyright (C) 2018-2022 Romain Vimont Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -790,7 +790,7 @@ Leggi la [pagina per sviluppatori].
## Licenza (in inglese) ## Licenza (in inglese)
Copyright (C) 2018 Genymobile Copyright (C) 2018 Genymobile
Copyright (C) 2018-2022 Romain Vimont Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -776,7 +776,7 @@ _⁴Android 7以上のみ._
## ライセンス ## ライセンス
Copyright (C) 2018 Genymobile Copyright (C) 2018 Genymobile
Copyright (C) 2018-2022 Romain Vimont Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -475,7 +475,7 @@ _²화면이 꺼진 상태에서 우클릭 시 다시 켜지며, 그 외의 상
## 라이선스 ## 라이선스
Copyright (C) 2018 Genymobile Copyright (C) 2018 Genymobile
Copyright (C) 2018-2022 Romain Vimont Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1017,7 +1017,7 @@ Read the [developers page].
## Licence ## Licence
Copyright (C) 2018 Genymobile Copyright (C) 2018 Genymobile
Copyright (C) 2018-2022 Romain Vimont Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -857,7 +857,7 @@ Leia a [página dos desenvolvedores][developers page].
## Licença ## Licença
Copyright (C) 2018 Genymobile Copyright (C) 2018 Genymobile
Copyright (C) 2018-2022 Romain Vimont Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -720,7 +720,7 @@ Lea la [hoja de desarrolladores (en inglés)](DEVELOP.md).
## Licencia ## Licencia
Copyright (C) 2018 Genymobile Copyright (C) 2018 Genymobile
Copyright (C) 2018-2022 Romain Vimont Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -801,7 +801,7 @@ Bakınız [FAQ](FAQ.md).
## Lisans ## Lisans
Copyright (C) 2018 Genymobile Copyright (C) 2018 Genymobile
Copyright (C) 2018-2022 Romain Vimont Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -842,7 +842,7 @@ ADB=/path/to/adb scrcpy
## 许可协议 ## 许可协议
Copyright (C) 2018 Genymobile Copyright (C) 2018 Genymobile
Copyright (C) 2018-2022 Romain Vimont Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -679,7 +679,7 @@ _³只支援 Android 7+。_
## Licence ## Licence
Copyright (C) 2018 Genymobile Copyright (C) 2018 Genymobile
Copyright (C) 2018-2022 Romain Vimont Copyright (C) 2018-2021 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -77,7 +77,6 @@ if aoa_hid_support
src += [ src += [
'src/aoa_hid.c', 'src/aoa_hid.c',
'src/hid_keyboard.c', 'src/hid_keyboard.c',
'src/hid_mouse.c',
] ]
endif endif

View File

@@ -446,7 +446,7 @@ Copyright \(co 2018 Genymobile
Genymobile Genymobile
.UE .UE
Copyright \(co 2018\-2022 Copyright \(co 2018\-2021
.MT rom@rom1v.com .MT rom@rom1v.com
Romain Vimont Romain Vimont
.ME .ME

View File

@@ -214,17 +214,6 @@ static const struct sc_option options[] = {
.text = "Limit the frame rate of screen capture (officially supported " .text = "Limit the frame rate of screen capture (officially supported "
"since Android 10, but may work on earlier versions).", "since Android 10, but may work on earlier versions).",
}, },
{
.shortopt = 'M',
.longopt = "hid-mouse",
.text = "Simulate a physical mouse by using HID over AOAv2.\n"
"In this mode, the computer mouse is captured to control the "
"device directly (relative mouse mode).\n"
"LAlt, LSuper or RSuper toggle the capture mode, to give "
"control of the mouse back to the computer.\n"
"It may only work over USB, and is currently only supported "
"on Linux.\n",
},
{ {
.shortopt = 'm', .shortopt = 'm',
.longopt = "max-size", .longopt = "max-size",
@@ -251,8 +240,11 @@ static const struct sc_option options[] = {
{ {
.shortopt = 'N', .shortopt = 'N',
.longopt = "no-display", .longopt = "no-display",
.text = "Do not display device (only when screen recording or V4L2 " .text = "Do not display device (only when screen recording "
"sink is enabled).", #ifdef HAVE_V4L2
"or V4L2 sink "
#endif
"is enabled).",
}, },
{ {
.longopt_id = OPT_NO_KEY_REPEAT, .longopt_id = OPT_NO_KEY_REPEAT,
@@ -389,14 +381,14 @@ static const struct sc_option options[] = {
"Default is 0 (not forced): the local port used for " "Default is 0 (not forced): the local port used for "
"establishing the tunnel will be used.", "establishing the tunnel will be used.",
}, },
#ifdef HAVE_V4L2
{ {
.longopt_id = OPT_V4L2_SINK, .longopt_id = OPT_V4L2_SINK,
.longopt = "v4l2-sink", .longopt = "v4l2-sink",
.argdesc = "/dev/videoN", .argdesc = "/dev/videoN",
.text = "Output to v4l2loopback device.\n" .text = "Output to v4l2loopback device.\n"
"It requires to lock the video orientation (see " "It requires to lock the video orientation (see "
"--lock-video-orientation).\n" "--lock-video-orientation).",
"This feature is only available on Linux.",
}, },
{ {
.longopt_id = OPT_V4L2_BUFFER, .longopt_id = OPT_V4L2_BUFFER,
@@ -406,9 +398,9 @@ static const struct sc_option options[] = {
"frames. This increases latency to compensate for jitter.\n" "frames. This increases latency to compensate for jitter.\n"
"This option is similar to --display-buffer, but specific to " "This option is similar to --display-buffer, but specific to "
"V4L2 sink.\n" "V4L2 sink.\n"
"This feature is only available on Linux.\n"
"Default is 0 (no buffering).", "Default is 0 (no buffering).",
}, },
#endif
{ {
.shortopt = 'V', .shortopt = 'V',
.longopt = "verbosity", .longopt = "verbosity",
@@ -1308,13 +1300,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
args->help = true; args->help = true;
break; break;
case 'K': case 'K':
#ifdef HAVE_AOA_HID
opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_HID; opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_HID;
#else
LOGE("HID over AOA (-K/--hid-keyboard) is not supported on "
"this platform. It is only available on Linux.");
return false;
#endif
break; break;
case OPT_MAX_FPS: case OPT_MAX_FPS:
if (!parse_max_fps(optarg, &opts->max_fps)) { if (!parse_max_fps(optarg, &opts->max_fps)) {
@@ -1326,15 +1312,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false; return false;
} }
break; break;
case 'M':
#ifdef HAVE_AOA_HID
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_HID;
#else
LOGE("HID over AOA (-M/--hid-mouse) is not supported on this"
"platform. It is only available on Linux.");
return false;
#endif
break;
case OPT_LOCK_VIDEO_ORIENTATION: case OPT_LOCK_VIDEO_ORIENTATION:
if (!parse_lock_video_orientation(optarg, if (!parse_lock_video_orientation(optarg,
&opts->lock_video_orientation)) { &opts->lock_video_orientation)) {
@@ -1487,24 +1464,16 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
opts->tcpip = true; opts->tcpip = true;
opts->tcpip_dst = optarg; opts->tcpip_dst = optarg;
break; break;
case OPT_V4L2_SINK:
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2
case OPT_V4L2_SINK:
opts->v4l2_device = optarg; opts->v4l2_device = optarg;
#else
LOGE("V4L2 (--v4l2-sink) is only available on Linux.");
return false;
#endif
break; break;
case OPT_V4L2_BUFFER: case OPT_V4L2_BUFFER:
#ifdef HAVE_V4L2
if (!parse_buffering_time(optarg, &opts->v4l2_buffer)) { if (!parse_buffering_time(optarg, &opts->v4l2_buffer)) {
return false; return false;
} }
#else
LOGE("V4L2 (--v4l2-buffer) is only available on Linux.");
return false;
#endif
break; break;
#endif
default: default:
// getopt prints the error message on stderr // getopt prints the error message on stderr
return false; return false;

View File

@@ -7,7 +7,6 @@
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
#define MIN(X,Y) (X) < (Y) ? (X) : (Y) #define MIN(X,Y) (X) < (Y) ? (X) : (Y)
#define MAX(X,Y) (X) > (Y) ? (X) : (Y) #define MAX(X,Y) (X) > (Y) ? (X) : (Y)
#define CLAMP(V,X,Y) MIN( MAX((V),(X)), (Y) )
#define container_of(ptr, type, member) \ #define container_of(ptr, type, member) \
((type *) (((char *) (ptr)) - offsetof(type, member))) ((type *) (((char *) (ptr)) - offsetof(type, member)))

View File

@@ -119,8 +119,7 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
(uint32_t) msg->inject_scroll_event.hscroll); (uint32_t) msg->inject_scroll_event.hscroll);
buffer_write32be(&buf[17], buffer_write32be(&buf[17],
(uint32_t) msg->inject_scroll_event.vscroll); (uint32_t) msg->inject_scroll_event.vscroll);
buffer_write32be(&buf[21], msg->inject_scroll_event.buttons); return 21;
return 25;
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON: case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
buf[1] = msg->inject_keycode.action; buf[1] = msg->inject_keycode.action;
return 2; return 2;
@@ -193,12 +192,11 @@ control_msg_log(const struct control_msg *msg) {
} }
case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT: case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
LOG_CMSG("scroll position=%" PRIi32 ",%" PRIi32 " hscroll=%" PRIi32 LOG_CMSG("scroll position=%" PRIi32 ",%" PRIi32 " hscroll=%" PRIi32
" vscroll=%" PRIi32 " buttons=%06lx", " vscroll=%" PRIi32,
msg->inject_scroll_event.position.point.x, msg->inject_scroll_event.position.point.x,
msg->inject_scroll_event.position.point.y, msg->inject_scroll_event.position.point.y,
msg->inject_scroll_event.hscroll, msg->inject_scroll_event.hscroll,
msg->inject_scroll_event.vscroll, msg->inject_scroll_event.vscroll);
(long) msg->inject_scroll_event.buttons);
break; break;
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON: case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
LOG_CMSG("back-or-screen-on %s", LOG_CMSG("back-or-screen-on %s",

View File

@@ -70,7 +70,6 @@ struct control_msg {
struct sc_position position; struct sc_position position;
int32_t hscroll; int32_t hscroll;
int32_t vscroll; int32_t vscroll;
enum android_motionevent_buttons buttons;
} inject_scroll_event; } inject_scroll_event;
struct { struct {
enum android_keyevent_action action; // action for the BACK key enum android_keyevent_action action; // action for the BACK key

View File

@@ -387,6 +387,15 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
} }
} }
static void
sc_key_processor_process_text(struct sc_key_processor *kp,
const struct sc_text_event *event) {
(void) kp;
(void) event;
// Never forward text input via HID (all the keys are injected separately)
}
bool bool
sc_hid_keyboard_init(struct sc_hid_keyboard *kb, struct sc_aoa *aoa) { sc_hid_keyboard_init(struct sc_hid_keyboard *kb, struct sc_aoa *aoa) {
kb->aoa = aoa; kb->aoa = aoa;
@@ -406,9 +415,7 @@ sc_hid_keyboard_init(struct sc_hid_keyboard *kb, struct sc_aoa *aoa) {
static const struct sc_key_processor_ops ops = { static const struct sc_key_processor_ops ops = {
.process_key = sc_key_processor_process_key, .process_key = sc_key_processor_process_key,
// Never forward text input via HID (all the keys are injected .process_text = sc_key_processor_process_text,
// separately)
.process_text = NULL,
}; };
// Clipboard synchronization is requested over the control socket, while HID // Clipboard synchronization is requested over the control socket, while HID

View File

@@ -1,267 +0,0 @@
#include "hid_mouse.h"
#include <assert.h>
#include "input_events.h"
#include "util/log.h"
/** Downcast mouse processor to hid_mouse */
#define DOWNCAST(MP) container_of(MP, struct sc_hid_mouse, mouse_processor)
#define HID_MOUSE_ACCESSORY_ID 2
// 1 byte for buttons + padding, 1 byte for X position, 1 byte for Y position
#define HID_MOUSE_EVENT_SIZE 4
/**
* Mouse descriptor from the specification:
* <https://www.usb.org/sites/default/files/hid1_11.pdf>
*
* Appendix E (p71): §E.10 Report Descriptor (Mouse)
*
* The usage tags (like Wheel) are listed in "HID Usage Tables":
* <https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf>
* §4 Generic Desktop Page (0x01) (p26)
*/
static const unsigned char mouse_report_desc[] = {
// Usage Page (Generic Desktop)
0x05, 0x01,
// Usage (Mouse)
0x09, 0x02,
// Collection (Application)
0xA1, 0x01,
// Usage (Pointer)
0x09, 0x01,
// Collection (Physical)
0xA1, 0x00,
// Usage Page (Buttons)
0x05, 0x09,
// Usage Minimum (1)
0x19, 0x01,
// Usage Maximum (5)
0x29, 0x05,
// Logical Minimum (0)
0x15, 0x00,
// Logical Maximum (1)
0x25, 0x01,
// Report Count (5)
0x95, 0x05,
// Report Size (1)
0x75, 0x01,
// Input (Data, Variable, Absolute): 5 buttons bits
0x81, 0x02,
// Report Count (1)
0x95, 0x01,
// Report Size (3)
0x75, 0x03,
// Input (Constant): 3 bits padding
0x81, 0x01,
// Usage Page (Generic Desktop)
0x05, 0x01,
// Usage (X)
0x09, 0x30,
// Usage (Y)
0x09, 0x31,
// Usage (Wheel)
0x09, 0x38,
// Local Minimum (-127)
0x15, 0x81,
// Local Maximum (127)
0x25, 0x7F,
// Report Size (8)
0x75, 0x08,
// Report Count (3)
0x95, 0x03,
// Input (Data, Variable, Relative): 3 position bytes (X, Y, Wheel)
0x81, 0x06,
// End Collection
0xC0,
// End Collection
0xC0,
};
/**
* A mouse HID event is 3 bytes long:
*
* - byte 0: buttons state
* - byte 1: relative x motion (signed byte from -127 to 127)
* - byte 2: relative y motion (signed byte from -127 to 127)
*
* 7 6 5 4 3 2 1 0
* +---------------+
* byte 0: |0 0 0 . . . . .| buttons state
* +---------------+
* ^ ^ ^ ^ ^
* | | | | `- left button
* | | | `--- right button
* | | `----- middle button
* | `------- button 4
* `--------- button 5
*
* +---------------+
* byte 1: |. . . . . . . .| relative x motion
* +---------------+
* byte 2: |. . . . . . . .| relative y motion
* +---------------+
* byte 3: |. . . . . . . .| wheel motion (-1, 0 or 1)
* +---------------+
*
* As an example, here is the report for a motion of (x=5, y=-4) with left
* button pressed:
*
* +---------------+
* |0 0 0 0 0 0 0 1| left button pressed
* +---------------+
* |0 0 0 0 0 1 0 1| horizontal motion (x = 5)
* +---------------+
* |1 1 1 1 1 1 0 0| relative y motion (y = -4)
* +---------------+
* |0 0 0 0 0 0 0 0| wheel motion
* +---------------+
*/
static bool
sc_hid_mouse_event_init(struct sc_hid_event *hid_event) {
unsigned char *buffer = calloc(1, HID_MOUSE_EVENT_SIZE);
if (!buffer) {
LOG_OOM();
return false;
}
sc_hid_event_init(hid_event, HID_MOUSE_ACCESSORY_ID, buffer,
HID_MOUSE_EVENT_SIZE);
return true;
}
static unsigned char
buttons_state_to_hid_buttons(uint8_t buttons_state) {
unsigned char c = 0;
if (buttons_state & SC_MOUSE_BUTTON_LEFT) {
c |= 1 << 0;
}
if (buttons_state & SC_MOUSE_BUTTON_RIGHT) {
c |= 1 << 1;
}
if (buttons_state & SC_MOUSE_BUTTON_MIDDLE) {
c |= 1 << 2;
}
if (buttons_state & SC_MOUSE_BUTTON_X1) {
c |= 1 << 3;
}
if (buttons_state & SC_MOUSE_BUTTON_X2) {
c |= 1 << 4;
}
return c;
}
static void
sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp,
const struct sc_mouse_motion_event *event) {
struct sc_hid_mouse *mouse = DOWNCAST(mp);
struct sc_hid_event hid_event;
if (!sc_hid_mouse_event_init(&hid_event)) {
return;
}
unsigned char *buffer = hid_event.buffer;
buffer[0] = buttons_state_to_hid_buttons(event->buttons_state);
buffer[1] = CLAMP(event->xrel, -127, 127);
buffer[2] = CLAMP(event->yrel, -127, 127);
buffer[3] = 0; // wheel coordinates only used for scrolling
if (!sc_aoa_push_hid_event(mouse->aoa, &hid_event)) {
sc_hid_event_destroy(&hid_event);
LOGW("Could request HID event");
}
}
static void
sc_mouse_processor_process_mouse_click(struct sc_mouse_processor *mp,
const struct sc_mouse_click_event *event) {
struct sc_hid_mouse *mouse = DOWNCAST(mp);
struct sc_hid_event hid_event;
if (!sc_hid_mouse_event_init(&hid_event)) {
return;
}
unsigned char *buffer = hid_event.buffer;
buffer[0] = buttons_state_to_hid_buttons(event->buttons_state);
buffer[1] = 0; // no x motion
buffer[2] = 0; // no y motion
buffer[3] = 0; // wheel coordinates only used for scrolling
if (!sc_aoa_push_hid_event(mouse->aoa, &hid_event)) {
sc_hid_event_destroy(&hid_event);
LOGW("Could request HID event");
}
}
static void
sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
const struct sc_mouse_scroll_event *event) {
struct sc_hid_mouse *mouse = DOWNCAST(mp);
struct sc_hid_event hid_event;
if (!sc_hid_mouse_event_init(&hid_event)) {
return;
}
unsigned char *buffer = hid_event.buffer;
buffer[0] = 0; // buttons state irrelevant (and unknown)
buffer[1] = 0; // no x motion
buffer[2] = 0; // no y motion
// In practice, vscroll is always -1, 0 or 1, but in theory other values
// are possible
buffer[3] = CLAMP(event->vscroll, -127, 127);
// Horizontal scrolling ignored
if (!sc_aoa_push_hid_event(mouse->aoa, &hid_event)) {
sc_hid_event_destroy(&hid_event);
LOGW("Could request HID event");
}
}
bool
sc_hid_mouse_init(struct sc_hid_mouse *mouse, struct sc_aoa *aoa) {
mouse->aoa = aoa;
bool ok = sc_aoa_setup_hid(aoa, HID_MOUSE_ACCESSORY_ID, mouse_report_desc,
ARRAY_LEN(mouse_report_desc));
if (!ok) {
LOGW("Register HID mouse failed");
return false;
}
static const struct sc_mouse_processor_ops ops = {
.process_mouse_motion = sc_mouse_processor_process_mouse_motion,
.process_mouse_click = sc_mouse_processor_process_mouse_click,
.process_mouse_scroll = sc_mouse_processor_process_mouse_scroll,
// Touch events not supported (coordinates are not relative)
.process_touch = NULL,
};
mouse->mouse_processor.ops = &ops;
mouse->mouse_processor.relative_mode = true;
return true;
}
void
sc_hid_mouse_destroy(struct sc_hid_mouse *mouse) {
bool ok = sc_aoa_unregister_hid(mouse->aoa, HID_MOUSE_ACCESSORY_ID);
if (!ok) {
LOGW("Could not unregister HID");
}
}

View File

@@ -1,23 +0,0 @@
#ifndef HID_MOUSE_H
#define HID_MOUSE_H
#include "common.h"
#include <stdbool.h>
#include "aoa_hid.h"
#include "trait/mouse_processor.h"
struct sc_hid_mouse {
struct sc_mouse_processor mouse_processor; // mouse processor trait
struct sc_aoa *aoa;
};
bool
sc_hid_mouse_init(struct sc_hid_mouse *mouse, struct sc_aoa *aoa);
void
sc_hid_mouse_destroy(struct sc_hid_mouse *mouse);
#endif

View File

@@ -361,13 +361,10 @@ struct sc_mouse_scroll_event {
struct sc_position position; struct sc_position position;
int32_t hscroll; int32_t hscroll;
int32_t vscroll; int32_t vscroll;
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
}; };
struct sc_mouse_motion_event { struct sc_mouse_motion_event {
struct sc_position position; struct sc_position position;
int32_t xrel;
int32_t yrel;
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
}; };

View File

@@ -4,7 +4,6 @@
#include <SDL2/SDL_keycode.h> #include <SDL2/SDL_keycode.h>
#include "input_events.h" #include "input_events.h"
#include "screen.h"
#include "util/log.h" #include "util/log.h"
static inline uint16_t static inline uint16_t
@@ -122,22 +121,24 @@ is_shortcut_mod(struct input_manager *im, uint16_t sdl_mod) {
} }
void void
input_manager_init(struct input_manager *im, input_manager_init(struct input_manager *im, struct controller *controller,
const struct input_manager_params *params) { struct screen *screen, struct sc_key_processor *kp,
assert(!params->control || (params->kp && params->kp->ops)); struct sc_mouse_processor *mp,
assert(!params->control || (params->mp && params->mp->ops)); const struct scrcpy_options *options) {
assert(!options->control || (kp && kp->ops));
assert(!options->control || (mp && mp->ops));
im->controller = params->controller; im->controller = controller;
im->screen = params->screen; im->screen = screen;
im->kp = params->kp; im->kp = kp;
im->mp = params->mp; im->mp = mp;
im->control = params->control; im->control = options->control;
im->forward_all_clicks = params->forward_all_clicks; im->forward_all_clicks = options->forward_all_clicks;
im->legacy_paste = params->legacy_paste; im->legacy_paste = options->legacy_paste;
im->clipboard_autosync = params->clipboard_autosync; im->clipboard_autosync = options->clipboard_autosync;
const struct sc_shortcut_mods *shortcut_mods = params->shortcut_mods; const struct sc_shortcut_mods *shortcut_mods = &options->shortcut_mods;
assert(shortcut_mods->count); assert(shortcut_mods->count);
assert(shortcut_mods->count < SC_MAX_SHORTCUT_MODS); assert(shortcut_mods->count < SC_MAX_SHORTCUT_MODS);
for (unsigned i = 0; i < shortcut_mods->count; ++i) { for (unsigned i = 0; i < shortcut_mods->count; ++i) {
@@ -385,11 +386,6 @@ rotate_client_right(struct screen *screen) {
static void static void
input_manager_process_text_input(struct input_manager *im, input_manager_process_text_input(struct input_manager *im,
const SDL_TextInputEvent *event) { const SDL_TextInputEvent *event) {
if (!im->kp->ops->process_text) {
// The key processor does not support text input
return;
}
if (is_shortcut_mod(im, SDL_GetModState())) { if (is_shortcut_mod(im, SDL_GetModState())) {
// A shortcut must never generate text events // A shortcut must never generate text events
return; return;
@@ -626,7 +622,6 @@ input_manager_process_key(struct input_manager *im,
.mods_state = sc_mods_state_from_sdl(event->keysym.mod), .mods_state = sc_mods_state_from_sdl(event->keysym.mod),
}; };
assert(im->kp->ops->process_key);
im->kp->ops->process_key(im->kp, &evt, ack_to_wait); im->kp->ops->process_key(im->kp, &evt, ack_to_wait);
} }
@@ -638,10 +633,10 @@ input_manager_process_mouse_motion(struct input_manager *im,
if (im->forward_all_clicks) { if (im->forward_all_clicks) {
mask |= SDL_BUTTON_MMASK | SDL_BUTTON_RMASK; mask |= SDL_BUTTON_MMASK | SDL_BUTTON_RMASK;
} }
//if (!(event->state & mask)) { if (!(event->state & mask)) {
// // do not send motion events when no click is pressed // do not send motion events when no click is pressed
// return; 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;
@@ -653,14 +648,11 @@ input_manager_process_mouse_motion(struct input_manager *im,
.point = screen_convert_window_to_frame_coords(im->screen, .point = screen_convert_window_to_frame_coords(im->screen,
event->x, event->y), event->x, event->y),
}, },
.xrel = event->xrel,
.yrel = event->yrel,
.buttons_state = .buttons_state =
sc_mouse_buttons_state_from_sdl(event->state, sc_mouse_buttons_state_from_sdl(event->state,
im->forward_all_clicks), im->forward_all_clicks),
}; };
assert(im->mp->ops->process_mouse_motion);
im->mp->ops->process_mouse_motion(im->mp, &evt); im->mp->ops->process_mouse_motion(im->mp, &evt);
if (im->vfinger_down) { if (im->vfinger_down) {
@@ -675,11 +667,6 @@ input_manager_process_mouse_motion(struct input_manager *im,
static void static void
input_manager_process_touch(struct input_manager *im, input_manager_process_touch(struct input_manager *im,
const SDL_TouchFingerEvent *event) { const SDL_TouchFingerEvent *event) {
if (!im->mp->ops->process_touch) {
// The mouse processor does not support touch events
return;
}
int dw; int dw;
int dh; int dh;
SDL_GL_GetDrawableSize(im->screen->window, &dw, &dh); SDL_GL_GetDrawableSize(im->screen->window, &dw, &dh);
@@ -773,15 +760,8 @@ input_manager_process_mouse_button(struct input_manager *im,
im->forward_all_clicks), im->forward_all_clicks),
}; };
assert(im->mp->ops->process_mouse_click);
im->mp->ops->process_mouse_click(im->mp, &evt); im->mp->ops->process_mouse_click(im->mp, &evt);
if (im->mp->relative_mode) {
assert(!im->vfinger_down); // vfinger must not be used in relative mode
// No pinch-to-zoom simulation
return;
}
// Pinch-to-zoom simulation. // Pinch-to-zoom simulation.
// //
// If Ctrl is hold when the left-click button is pressed, then // If Ctrl is hold when the left-click button is pressed, then
@@ -811,15 +791,10 @@ input_manager_process_mouse_button(struct input_manager *im,
static void static void
input_manager_process_mouse_wheel(struct input_manager *im, input_manager_process_mouse_wheel(struct input_manager *im,
const SDL_MouseWheelEvent *event) { const SDL_MouseWheelEvent *event) {
if (!im->mp->ops->process_mouse_scroll) {
// The mouse processor does not support scroll events
return;
}
// mouse_x and mouse_y are expressed in pixels relative to the window // mouse_x and mouse_y are expressed in pixels relative to the window
int mouse_x; int mouse_x;
int mouse_y; int mouse_y;
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y); SDL_GetMouseState(&mouse_x, &mouse_y);
struct sc_mouse_scroll_event evt = { struct sc_mouse_scroll_event evt = {
.position = { .position = {
@@ -829,8 +804,6 @@ input_manager_process_mouse_wheel(struct input_manager *im,
}, },
.hscroll = event->x, .hscroll = event->x,
.vscroll = event->y, .vscroll = event->y,
.buttons_state =
sc_mouse_buttons_state_from_sdl(buttons, im->forward_all_clicks),
}; };
im->mp->ops->process_mouse_scroll(im->mp, &evt); im->mp->ops->process_mouse_scroll(im->mp, &evt);

View File

@@ -10,11 +10,10 @@
#include "controller.h" #include "controller.h"
#include "fps_counter.h" #include "fps_counter.h"
#include "options.h" #include "options.h"
#include "screen.h"
#include "trait/key_processor.h" #include "trait/key_processor.h"
#include "trait/mouse_processor.h" #include "trait/mouse_processor.h"
struct screen; // forward declaration to avoid cycle
struct input_manager { struct input_manager {
struct controller *controller; struct controller *controller;
struct screen *screen; struct screen *screen;
@@ -44,22 +43,11 @@ struct input_manager {
uint64_t next_sequence; // used for request acknowledgements uint64_t next_sequence; // used for request acknowledgements
}; };
struct input_manager_params {
struct controller *controller;
struct screen *screen;
struct sc_key_processor *kp;
struct sc_mouse_processor *mp;
bool control;
bool forward_all_clicks;
bool legacy_paste;
bool clipboard_autosync;
const struct sc_shortcut_mods *shortcut_mods;
};
void void
input_manager_init(struct input_manager *im, input_manager_init(struct input_manager *im, struct controller *controller,
const struct input_manager_params *params); struct screen *screen, struct sc_key_processor *kp,
struct sc_mouse_processor *mp,
const struct scrcpy_options *options);
bool bool
input_manager_handle_event(struct input_manager *im, SDL_Event *event); input_manager_handle_event(struct input_manager *im, SDL_Event *event);

View File

@@ -108,7 +108,6 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
.position = event->position, .position = event->position,
.hscroll = event->hscroll, .hscroll = event->hscroll,
.vscroll = event->vscroll, .vscroll = event->vscroll,
.buttons = convert_mouse_buttons(event->buttons_state),
}, },
}; };
@@ -151,6 +150,4 @@ sc_mouse_inject_init(struct sc_mouse_inject *mi,
}; };
mi->mouse_processor.ops = &ops; mi->mouse_processor.ops = &ops;
mi->mouse_processor.relative_mode = false;
} }

View File

@@ -38,11 +38,6 @@ enum sc_keyboard_input_mode {
SC_KEYBOARD_INPUT_MODE_HID, SC_KEYBOARD_INPUT_MODE_HID,
}; };
enum sc_mouse_input_mode {
SC_MOUSE_INPUT_MODE_INJECT,
SC_MOUSE_INPUT_MODE_HID,
};
enum sc_key_inject_mode { enum sc_key_inject_mode {
// Inject special keys, letters and space as key events. // Inject special keys, letters and space as key events.
// Inject numbers and punctuation as text events. // Inject numbers and punctuation as text events.
@@ -95,7 +90,6 @@ struct scrcpy_options {
enum sc_log_level log_level; enum sc_log_level log_level;
enum sc_record_format record_format; enum sc_record_format record_format;
enum sc_keyboard_input_mode keyboard_input_mode; enum sc_keyboard_input_mode keyboard_input_mode;
enum sc_mouse_input_mode mouse_input_mode;
struct sc_port_range port_range; struct sc_port_range port_range;
uint32_t tunnel_host; uint32_t tunnel_host;
uint16_t tunnel_port; uint16_t tunnel_port;

View File

@@ -17,9 +17,9 @@
#include "decoder.h" #include "decoder.h"
#include "events.h" #include "events.h"
#include "file_handler.h" #include "file_handler.h"
#include "input_manager.h"
#ifdef HAVE_AOA_HID #ifdef HAVE_AOA_HID
# include "hid_keyboard.h" # include "hid_keyboard.h"
# include "hid_mouse.h"
#endif #endif
#include "keyboard_inject.h" #include "keyboard_inject.h"
#include "mouse_inject.h" #include "mouse_inject.h"
@@ -56,12 +56,8 @@ struct scrcpy {
struct sc_hid_keyboard keyboard_hid; struct sc_hid_keyboard keyboard_hid;
#endif #endif
}; };
union {
struct sc_mouse_inject mouse_inject; struct sc_mouse_inject mouse_inject;
#ifdef HAVE_AOA_HID struct input_manager input_manager;
struct sc_hid_mouse mouse_hid;
#endif
};
}; };
static inline void static inline void
@@ -193,6 +189,11 @@ handle_event(struct scrcpy *s, const struct scrcpy_options *options,
} }
bool consumed = screen_handle_event(&s->screen, event); bool consumed = screen_handle_event(&s->screen, event);
if (consumed) {
goto end;
}
consumed = input_manager_handle_event(&s->input_manager, event);
(void) consumed; (void) consumed;
end: end:
@@ -335,8 +336,6 @@ scrcpy(struct scrcpy_options *options) {
bool stream_started = false; bool stream_started = false;
#ifdef HAVE_AOA_HID #ifdef HAVE_AOA_HID
bool aoa_hid_initialized = false; bool aoa_hid_initialized = false;
bool hid_keyboard_initialized = false;
bool hid_mouse_initialized = false;
#endif #endif
bool controller_initialized = false; bool controller_initialized = false;
bool controller_started = false; bool controller_started = false;
@@ -451,100 +450,17 @@ scrcpy(struct scrcpy_options *options) {
stream_add_sink(&s->stream, &rec->packet_sink); stream_add_sink(&s->stream, &rec->packet_sink);
} }
struct sc_key_processor *kp = NULL;
struct sc_mouse_processor *mp = NULL;
if (options->control) { if (options->control) {
#ifdef HAVE_AOA_HID #ifdef HAVE_AOA_HID
bool use_hid_keyboard = if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID) {
options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID;
bool use_hid_mouse =
options->mouse_input_mode == SC_MOUSE_INPUT_MODE_HID;
if (use_hid_keyboard || use_hid_mouse) {
bool ok = sc_acksync_init(&s->acksync); bool ok = sc_acksync_init(&s->acksync);
if (!ok) { if (!ok) {
goto end; goto end;
} }
ok = sc_aoa_init(&s->aoa, serial, &s->acksync);
if (!ok) {
LOGE("Failed to enable HID over AOA");
sc_acksync_destroy(&s->acksync);
goto aoa_hid_end;
}
if (use_hid_keyboard) {
if (sc_hid_keyboard_init(&s->keyboard_hid, &s->aoa)) {
hid_keyboard_initialized = true;
kp = &s->keyboard_hid.key_processor;
} else {
LOGE("Could not initialize HID keyboard");
}
}
if (use_hid_mouse) {
if (sc_hid_mouse_init(&s->mouse_hid, &s->aoa)) {
hid_mouse_initialized = true;
mp = &s->mouse_hid.mouse_processor;
} else {
LOGE("Could not initialized HID mouse");
}
}
bool need_aoa = hid_keyboard_initialized || hid_mouse_initialized;
if (!need_aoa || !sc_aoa_start(&s->aoa)) {
sc_acksync_destroy(&s->acksync);
sc_aoa_destroy(&s->aoa);
goto aoa_hid_end;
}
acksync = &s->acksync; acksync = &s->acksync;
aoa_hid_initialized = true;
aoa_hid_end:
if (!aoa_hid_initialized) {
if (hid_keyboard_initialized) {
sc_hid_keyboard_destroy(&s->keyboard_hid);
hid_keyboard_initialized = false;
} }
if (hid_mouse_initialized) {
sc_hid_mouse_destroy(&s->mouse_hid);
hid_mouse_initialized = false;
}
}
if (use_hid_keyboard && !hid_keyboard_initialized) {
LOGE("Fallback to default keyboard injection method "
"(-K/--hid-keyboard ignored)");
options->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT;
}
if (use_hid_mouse && !hid_mouse_initialized) {
LOGE("Fallback to default mouse injection method "
"(-M/--hid-mouse ignored)");
options->mouse_input_mode = SC_MOUSE_INPUT_MODE_INJECT;
}
}
#else
assert(options->keyboard_input_mode != SC_KEYBOARD_INPUT_MODE_HID);
assert(options->mouse_input_mode != SC_MOUSE_INPUT_MODE_HID);
#endif #endif
// keyboard_input_mode may have been reset if HID mode failed
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_INJECT) {
sc_keyboard_inject_init(&s->keyboard_inject, &s->controller,
options);
kp = &s->keyboard_inject.key_processor;
}
// mouse_input_mode may have been reset if HID mode failed
if (options->mouse_input_mode == SC_MOUSE_INPUT_MODE_INJECT) {
sc_mouse_inject_init(&s->mouse_inject, &s->controller);
mp = &s->mouse_inject.mouse_processor;
}
if (!controller_init(&s->controller, s->server.control_socket, if (!controller_init(&s->controller, s->server.control_socket,
acksync)) { acksync)) {
goto end; goto end;
@@ -565,7 +481,6 @@ aoa_hid_end:
LOGW("Could not request 'set screen power mode'"); LOGW("Could not request 'set screen power mode'");
} }
} }
} }
if (options->display) { if (options->display) {
@@ -573,14 +488,6 @@ aoa_hid_end:
options->window_title ? options->window_title : info->device_name; options->window_title ? options->window_title : info->device_name;
struct screen_params screen_params = { struct screen_params screen_params = {
.controller = &s->controller,
.kp = kp,
.mp = mp,
.control = options->control,
.forward_all_clicks = options->forward_all_clicks,
.legacy_paste = options->legacy_paste,
.clipboard_autosync = options->clipboard_autosync,
.shortcut_mods = &options->shortcut_mods,
.window_title = window_title, .window_title = window_title,
.frame_size = info->frame_size, .frame_size = info->frame_size,
.always_on_top = options->always_on_top, .always_on_top = options->always_on_top,
@@ -623,6 +530,64 @@ aoa_hid_end:
} }
stream_started = true; stream_started = true;
struct sc_key_processor *kp = NULL;
struct sc_mouse_processor *mp = NULL;
if (options->control) {
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID) {
#ifdef HAVE_AOA_HID
bool aoa_hid_ok = false;
bool ok = sc_aoa_init(&s->aoa, serial, acksync);
if (!ok) {
goto aoa_hid_end;
}
if (!sc_hid_keyboard_init(&s->keyboard_hid, &s->aoa)) {
sc_aoa_destroy(&s->aoa);
goto aoa_hid_end;
}
if (!sc_aoa_start(&s->aoa)) {
sc_hid_keyboard_destroy(&s->keyboard_hid);
sc_aoa_destroy(&s->aoa);
goto aoa_hid_end;
}
aoa_hid_ok = true;
kp = &s->keyboard_hid.key_processor;
aoa_hid_initialized = true;
aoa_hid_end:
if (!aoa_hid_ok) {
LOGE("Failed to enable HID over AOA, "
"fallback to default keyboard injection method "
"(-K/--hid-keyboard ignored)");
options->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT;
}
#else
LOGE("HID over AOA is not supported on this platform, "
"fallback to default keyboard injection method "
"(-K/--hid-keyboard ignored)");
options->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT;
#endif
}
// keyboard_input_mode may have been reset if HID mode failed
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_INJECT) {
sc_keyboard_inject_init(&s->keyboard_inject, &s->controller,
options);
kp = &s->keyboard_inject.key_processor;
}
sc_mouse_inject_init(&s->mouse_inject, &s->controller);
mp = &s->mouse_inject.mouse_processor;
}
input_manager_init(&s->input_manager, &s->controller, &s->screen, kp, mp,
options);
ret = event_loop(s, options); ret = event_loop(s, options);
LOGD("quit..."); LOGD("quit...");
@@ -635,9 +600,7 @@ end:
// end-of-stream // end-of-stream
#ifdef HAVE_AOA_HID #ifdef HAVE_AOA_HID
if (aoa_hid_initialized) { if (aoa_hid_initialized) {
if (hid_keyboard_initialized) {
sc_hid_keyboard_destroy(&s->keyboard_hid); sc_hid_keyboard_destroy(&s->keyboard_hid);
}
sc_aoa_stop(&s->aoa); sc_aoa_stop(&s->aoa);
} }
if (acksync) { if (acksync) {

View File

@@ -156,17 +156,6 @@ get_initial_optimal_size(struct sc_size content_size, uint16_t req_width,
return window_size; return window_size;
} }
static inline void
screen_capture_mouse(struct screen *screen, bool capture) {
if (SDL_SetRelativeMouseMode(capture)) {
LOGE("Could not set relative mouse mode to %s: %s",
capture ? "true" : "false", SDL_GetError());
return;
}
screen->mouse_captured = capture;
}
static void static void
screen_update_content_rect(struct screen *screen) { screen_update_content_rect(struct screen *screen) {
int dw; int dw;
@@ -365,8 +354,6 @@ screen_init(struct screen *screen, const struct screen_params *params) {
screen->fullscreen = false; screen->fullscreen = false;
screen->maximized = false; screen->maximized = false;
screen->event_failed = false; screen->event_failed = false;
screen->mouse_captured = false;
screen->mouse_capture_key_pressed = 0;
static const struct sc_video_buffer_callbacks cbs = { static const struct sc_video_buffer_callbacks cbs = {
.on_new_frame = sc_video_buffer_on_new_frame, .on_new_frame = sc_video_buffer_on_new_frame,
@@ -483,20 +470,6 @@ screen_init(struct screen *screen, const struct screen_params *params) {
goto error_destroy_texture; goto error_destroy_texture;
} }
struct input_manager_params im_params = {
.controller = params->controller,
.screen = screen,
.kp = params->kp,
.mp = params->mp,
.control = params->control,
.forward_all_clicks = params->forward_all_clicks,
.legacy_paste = params->legacy_paste,
.clipboard_autosync = params->clipboard_autosync,
.shortcut_mods = params->shortcut_mods,
};
input_manager_init(&screen->im, &im_params);
// Reset the window size to trigger a SIZE_CHANGED event, to workaround // Reset the window size to trigger a SIZE_CHANGED event, to workaround
// HiDPI issues with some SDL renderers when several displays having // HiDPI issues with some SDL renderers when several displays having
// different HiDPI scaling are connected // different HiDPI scaling are connected
@@ -754,11 +727,6 @@ screen_resize_to_pixel_perfect(struct screen *screen) {
content_size.height); content_size.height);
} }
static inline bool
screen_is_mouse_capture_key(SDL_Keycode key) {
return key == SDLK_LALT || key == SDLK_LGUI || key == SDLK_RGUI;
}
bool bool
screen_handle_event(struct screen *screen, SDL_Event *event) { screen_handle_event(struct screen *screen, SDL_Event *event) {
switch (event->type) { switch (event->type) {
@@ -801,71 +769,11 @@ screen_handle_event(struct screen *screen, SDL_Event *event) {
apply_pending_resize(screen); apply_pending_resize(screen);
screen_render(screen, true); screen_render(screen, true);
break; break;
case SDL_WINDOWEVENT_FOCUS_LOST:
if (screen->im.mp->relative_mode) {
screen_capture_mouse(screen, false);
}
break;
} }
return true; return true;
case SDL_KEYDOWN:
if (screen->im.mp->relative_mode) {
SDL_Keycode key = event->key.keysym.sym;
if (screen_is_mouse_capture_key(key)) {
if (!screen->mouse_capture_key_pressed) {
screen->mouse_capture_key_pressed = key;
return true;
} else {
// Another mouse capture key has been pressed, cancel
// mouse (un)capture
screen->mouse_capture_key_pressed = 0;
// Do not return, the event must be forwarded to the
// input manager
}
}
}
break;
case SDL_KEYUP:
if (screen->im.mp->relative_mode) {
SDL_Keycode key = event->key.keysym.sym;
SDL_Keycode cap = screen->mouse_capture_key_pressed;
screen->mouse_capture_key_pressed = 0;
if (key == cap) {
// A mouse capture key has been pressed then released:
// toggle the capture mouse mode
screen_capture_mouse(screen, !screen->mouse_captured);
return true;
}
// Do not return, the event must be forwarded to the input
// manager
}
break;
case SDL_MOUSEWHEEL:
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
if (screen->im.mp->relative_mode && !screen->mouse_captured) {
// Do not forward to input manager, the mouse will be captured
// on SDL_MOUSEBUTTONUP
return true;
}
break;
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP:
if (screen->im.mp->relative_mode) {
// Touch events are not compatible with relative mode
// (coordinates are not relative)
return true;
}
break;
case SDL_MOUSEBUTTONUP:
if (screen->im.mp->relative_mode && !screen->mouse_captured) {
screen_capture_mouse(screen, true);
return true;
}
} }
return input_manager_handle_event(&screen->im, event); return false;
} }
struct sc_point struct sc_point

View File

@@ -7,14 +7,10 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include "controller.h"
#include "coords.h" #include "coords.h"
#include "fps_counter.h" #include "fps_counter.h"
#include "input_manager.h"
#include "opengl.h" #include "opengl.h"
#include "trait/key_processor.h"
#include "trait/frame_sink.h" #include "trait/frame_sink.h"
#include "trait/mouse_processor.h"
#include "video_buffer.h" #include "video_buffer.h"
struct screen { struct screen {
@@ -24,7 +20,6 @@ struct screen {
bool open; // track the open/close state to assert correct behavior bool open; // track the open/close state to assert correct behavior
#endif #endif
struct input_manager im;
struct sc_video_buffer vb; struct sc_video_buffer vb;
struct fps_counter fps_counter; struct fps_counter fps_counter;
@@ -51,25 +46,10 @@ struct screen {
bool event_failed; // in case SDL_PushEvent() returned an error bool event_failed; // in case SDL_PushEvent() returned an error
bool mouse_captured; // only relevant in relative mouse mode
// To enable/disable mouse capture, a mouse capture key (LALT, LGUI or
// RGUI) must be pressed. This variable tracks the pressed capture key.
SDL_Keycode mouse_capture_key_pressed;
AVFrame *frame; AVFrame *frame;
}; };
struct screen_params { struct screen_params {
struct controller *controller;
struct sc_key_processor *kp;
struct sc_mouse_processor *mp;
bool control;
bool forward_all_clicks;
bool legacy_paste;
bool clipboard_autosync;
const struct sc_shortcut_mods *shortcut_mods;
const char *window_title; const char *window_title;
struct sc_size frame_size; struct sc_size frame_size;
bool always_on_top; bool always_on_top;

View File

@@ -29,24 +29,17 @@ struct sc_key_processor {
struct sc_key_processor_ops { struct sc_key_processor_ops {
/** /**
* Process a keyboard event * Process the keyboard event
* *
* The `sequence` number (if different from `SC_SEQUENCE_INVALID`) indicates * The `sequence` number (if different from `SC_SEQUENCE_INVALID`) indicates
* the acknowledgement number to wait for before injecting this event. * the acknowledgement number to wait for before injecting this event.
* This allows to ensure that the device clipboard is set before injecting * This allows to ensure that the device clipboard is set before injecting
* Ctrl+v on the device. * Ctrl+v on the device.
*
* This function is mandatory.
*/ */
void void
(*process_key)(struct sc_key_processor *kp, (*process_key)(struct sc_key_processor *kp,
const struct sc_key_event *event, uint64_t ack_to_wait); const struct sc_key_event *event, uint64_t ack_to_wait);
/**
* Process an input text
*
* This function is optional.
*/
void void
(*process_text)(struct sc_key_processor *kp, (*process_text)(struct sc_key_processor *kp,
const struct sc_text_event *event); const struct sc_text_event *event);

View File

@@ -16,48 +16,21 @@
*/ */
struct sc_mouse_processor { struct sc_mouse_processor {
const struct sc_mouse_processor_ops *ops; const struct sc_mouse_processor_ops *ops;
/**
* If set, the mouse processor works in relative mode (the absolute
* position is irrelevant). In particular, it indicates that the mouse
* pointer must be "captured" by the UI.
*/
bool relative_mode;
}; };
struct sc_mouse_processor_ops { struct sc_mouse_processor_ops {
/**
* Process a mouse motion event
*
* This function is mandatory.
*/
void void
(*process_mouse_motion)(struct sc_mouse_processor *mp, (*process_mouse_motion)(struct sc_mouse_processor *mp,
const struct sc_mouse_motion_event *event); const struct sc_mouse_motion_event *event);
/**
* Process a mouse click event
*
* This function is mandatory.
*/
void void
(*process_mouse_click)(struct sc_mouse_processor *mp, (*process_mouse_click)(struct sc_mouse_processor *mp,
const struct sc_mouse_click_event *event); const struct sc_mouse_click_event *event);
/**
* Process a mouse scroll event
*
* This function is optional.
*/
void void
(*process_mouse_scroll)(struct sc_mouse_processor *mp, (*process_mouse_scroll)(struct sc_mouse_processor *mp,
const struct sc_mouse_scroll_event *event); const struct sc_mouse_scroll_event *event);
/**
* Process a touch event
*
* This function is optional.
*/
void void
(*process_touch)(struct sc_mouse_processor *mp, (*process_touch)(struct sc_mouse_processor *mp,
const struct sc_touch_event *event); const struct sc_touch_event *event);

View File

@@ -11,7 +11,6 @@ static void test_get_ip_single_line() {
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route)); char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
assert(ip); assert(ip);
assert(!strcmp(ip, "192.168.12.34")); assert(!strcmp(ip, "192.168.12.34"));
free(ip);
} }
static void test_get_ip_single_line_without_eol() { static void test_get_ip_single_line_without_eol() {
@@ -21,7 +20,6 @@ static void test_get_ip_single_line_without_eol() {
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route)); char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
assert(ip); assert(ip);
assert(!strcmp(ip, "192.168.12.34")); assert(!strcmp(ip, "192.168.12.34"));
free(ip);
} }
static void test_get_ip_single_line_with_trailing_space() { static void test_get_ip_single_line_with_trailing_space() {
@@ -31,7 +29,6 @@ static void test_get_ip_single_line_with_trailing_space() {
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route)); char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
assert(ip); assert(ip);
assert(!strcmp(ip, "192.168.12.34")); assert(!strcmp(ip, "192.168.12.34"));
free(ip);
} }
static void test_get_ip_multiline_first_ok() { static void test_get_ip_multiline_first_ok() {
@@ -43,7 +40,6 @@ static void test_get_ip_multiline_first_ok() {
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route)); char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
assert(ip); assert(ip);
assert(!strcmp(ip, "192.168.1.2")); assert(!strcmp(ip, "192.168.1.2"));
free(ip);
} }
static void test_get_ip_multiline_second_ok() { static void test_get_ip_multiline_second_ok() {
@@ -55,7 +51,6 @@ static void test_get_ip_multiline_second_ok() {
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route)); char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
assert(ip); assert(ip);
assert(!strcmp(ip, "192.168.1.3")); assert(!strcmp(ip, "192.168.1.3"));
free(ip);
} }
static void test_get_ip_no_wlan() { static void test_get_ip_no_wlan() {

View File

@@ -126,13 +126,12 @@ static void test_serialize_inject_scroll_event(void) {
}, },
.hscroll = 1, .hscroll = 1,
.vscroll = -1, .vscroll = -1,
.buttons = 1,
}, },
}; };
unsigned char buf[CONTROL_MSG_MAX_SIZE]; unsigned char buf[CONTROL_MSG_MAX_SIZE];
size_t size = control_msg_serialize(&msg, buf); size_t size = control_msg_serialize(&msg, buf);
assert(size == 25); assert(size == 21);
const unsigned char expected[] = { const unsigned char expected[] = {
CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT, CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
@@ -140,7 +139,6 @@ static void test_serialize_inject_scroll_event(void) {
0x04, 0x38, 0x07, 0x80, // 1080 1920 0x04, 0x38, 0x07, 0x80, // 1080 1920
0x00, 0x00, 0x00, 0x01, // 1 0x00, 0x00, 0x00, 0x01, // 1
0xFF, 0xFF, 0xFF, 0xFF, // -1 0xFF, 0xFF, 0xFF, 0xFF, // -1
0x00, 0x00, 0x00, 0x01, // 1
}; };
assert(!memcmp(buf, expected, sizeof(expected))); assert(!memcmp(buf, expected, sizeof(expected)));
} }

View File

@@ -71,13 +71,12 @@ public final class ControlMessage {
return msg; return msg;
} }
public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll, int buttons) { public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll) {
ControlMessage msg = new ControlMessage(); ControlMessage msg = new ControlMessage();
msg.type = TYPE_INJECT_SCROLL_EVENT; msg.type = TYPE_INJECT_SCROLL_EVENT;
msg.position = position; msg.position = position;
msg.hScroll = hScroll; msg.hScroll = hScroll;
msg.vScroll = vScroll; msg.vScroll = vScroll;
msg.buttons = buttons;
return msg; return msg;
} }

View File

@@ -10,7 +10,7 @@ public class ControlMessageReader {
static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 13; static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 13;
static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 27; static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 27;
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 24; static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
static final int BACK_OR_SCREEN_ON_LENGTH = 1; static final int BACK_OR_SCREEN_ON_LENGTH = 1;
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1; static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
static final int GET_CLIPBOARD_LENGTH = 1; static final int GET_CLIPBOARD_LENGTH = 1;
@@ -154,8 +154,7 @@ public class ControlMessageReader {
Position position = readPosition(buffer); Position position = readPosition(buffer);
int hScroll = buffer.getInt(); int hScroll = buffer.getInt();
int vScroll = buffer.getInt(); int vScroll = buffer.getInt();
int buttons = buffer.getInt(); return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll);
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll, buttons);
} }
private ControlMessage parseBackOrScreenOnEvent() { private ControlMessage parseBackOrScreenOnEvent() {

View File

@@ -98,7 +98,7 @@ public class Controller {
break; break;
case ControlMessage.TYPE_INJECT_SCROLL_EVENT: case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
if (device.supportsInputEvents()) { if (device.supportsInputEvents()) {
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll(), msg.getButtons()); injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
} }
break; break;
case ControlMessage.TYPE_BACK_OR_SCREEN_ON: case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
@@ -221,7 +221,7 @@ public class Controller {
return device.injectEvent(event, Device.INJECT_MODE_ASYNC); return device.injectEvent(event, Device.INJECT_MODE_ASYNC);
} }
private boolean injectScroll(Position position, int hScroll, int vScroll, int buttons) { private boolean injectScroll(Position position, int hScroll, int vScroll) {
long now = SystemClock.uptimeMillis(); long now = SystemClock.uptimeMillis();
Point point = device.getPhysicalPoint(position); Point point = device.getPhysicalPoint(position);
if (point == null) { if (point == null) {
@@ -239,7 +239,7 @@ public class Controller {
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll); coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
MotionEvent event = MotionEvent MotionEvent event = MotionEvent
.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, .obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, DEFAULT_DEVICE_ID, 0,
InputDevice.SOURCE_MOUSE, 0); InputDevice.SOURCE_MOUSE, 0);
return device.injectEvent(event, Device.INJECT_MODE_ASYNC); return device.injectEvent(event, Device.INJECT_MODE_ASYNC);
} }

View File

@@ -128,7 +128,6 @@ public class ControlMessageReaderTest {
dos.writeShort(1920); dos.writeShort(1920);
dos.writeInt(1); dos.writeInt(1);
dos.writeInt(-1); dos.writeInt(-1);
dos.writeInt(1);
byte[] packet = bos.toByteArray(); byte[] packet = bos.toByteArray();
@@ -145,7 +144,6 @@ public class ControlMessageReaderTest {
Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight()); Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight());
Assert.assertEquals(1, event.getHScroll()); Assert.assertEquals(1, event.getHScroll());
Assert.assertEquals(-1, event.getVScroll()); Assert.assertEquals(-1, event.getVScroll());
Assert.assertEquals(1, event.getButtons());
} }
@Test @Test