mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-03-17 09:34:36 +01:00
Compare commits
6 Commits
virtualfin
...
finger
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
907e55a66b | ||
|
|
6d98ab5d29 | ||
|
|
91ba44b653 | ||
|
|
7b143b8fbd | ||
|
|
8d1f58278e | ||
|
|
b2d815bfc7 |
2
BUILD.md
2
BUILD.md
@@ -43,7 +43,7 @@ Install the required packages from your package manager.
|
||||
sudo apt install ffmpeg libsdl2-2.0-0
|
||||
|
||||
# client build dependencies
|
||||
sudo apt install gcc git pkg-config meson ninja-build \
|
||||
sudo apt install make gcc git pkg-config meson ninja-build \
|
||||
libavcodec-dev libavformat-dev libavutil-dev \
|
||||
libsdl2-dev
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
static inline void
|
||||
buffer_write16be(uint8_t *buf, uint16_t value) {
|
||||
buf[0] = value >> 8;
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
// To define a circular buffer type of 20 ints:
|
||||
// struct cbuf_int CBUF(int, 20);
|
||||
//
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "log.h"
|
||||
#include "str_util.h"
|
||||
|
||||
@@ -33,8 +33,6 @@
|
||||
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
# define NO_EXIT_CODE -1
|
||||
|
||||
enum process_result {
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
||||
#define MIN(X,Y) (X) < (Y) ? (X) : (Y)
|
||||
#define MAX(X,Y) (X) > (Y) ? (X) : (Y)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#include "control_msg.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <SDL2/SDL_assert.h>
|
||||
#include <SDL_assert.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "buffer_util.h"
|
||||
#include "log.h"
|
||||
#include "str_util.h"
|
||||
@@ -49,15 +48,19 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
||||
CONTROL_MSG_TEXT_MAX_LENGTH, &buf[1]);
|
||||
return 1 + len;
|
||||
}
|
||||
case CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT:
|
||||
buf[1] = msg->inject_mouse_event.action;
|
||||
buffer_write32be(&buf[2], msg->inject_mouse_event.buttons);
|
||||
write_position(&buf[6], &msg->inject_mouse_event.position);
|
||||
return 18;
|
||||
case CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT:
|
||||
buf[1] = msg->inject_touch_event.action;
|
||||
buffer_write64be(&buf[2], msg->inject_touch_event.pointer_id);
|
||||
buffer_write64be(&buf[2], msg->inject_touch_event.finger_id);
|
||||
write_position(&buf[10], &msg->inject_touch_event.position);
|
||||
uint16_t pressure =
|
||||
to_fixed_point_16(msg->inject_touch_event.pressure);
|
||||
buffer_write16be(&buf[22], pressure);
|
||||
buffer_write32be(&buf[24], msg->inject_touch_event.buttons);
|
||||
return 28;
|
||||
return 24;
|
||||
case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
||||
write_position(&buf[1], &msg->inject_scroll_event.position);
|
||||
buffer_write32be(&buf[13],
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "android/input.h"
|
||||
#include "android/keycodes.h"
|
||||
#include "common.h"
|
||||
@@ -15,12 +14,10 @@
|
||||
#define CONTROL_MSG_SERIALIZED_MAX_SIZE \
|
||||
(3 + CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH)
|
||||
|
||||
#define POINTER_ID_MOUSE UINT64_C(-1);
|
||||
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-2);
|
||||
|
||||
enum control_msg_type {
|
||||
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||
CONTROL_MSG_TYPE_INJECT_TEXT,
|
||||
CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT,
|
||||
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||
CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||
CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
||||
@@ -51,7 +48,11 @@ struct control_msg {
|
||||
struct {
|
||||
enum android_motionevent_action action;
|
||||
enum android_motionevent_buttons buttons;
|
||||
uint64_t pointer_id;
|
||||
struct position position;
|
||||
} inject_mouse_event;
|
||||
struct {
|
||||
enum android_motionevent_action action;
|
||||
uint64_t finger_id;
|
||||
struct position position;
|
||||
float pressure;
|
||||
} inject_touch_event;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <SDL2/SDL_mutex.h>
|
||||
#include <SDL2/SDL_thread.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "cbuf.h"
|
||||
#include "control_msg.h"
|
||||
#include "net.h"
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#include <SDL2/SDL_thread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "compat.h"
|
||||
#include "config.h"
|
||||
#include "buffer_util.h"
|
||||
#include "events.h"
|
||||
#include "lock_util.h"
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
struct video_buffer;
|
||||
|
||||
struct decoder {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "device.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
|
||||
bool
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "net.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <string.h>
|
||||
#include <SDL2/SDL_assert.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "buffer_util.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define DEVICE_MSG_TEXT_MAX_LENGTH 4093
|
||||
#define DEVICE_MSG_SERIALIZED_MAX_SIZE (3 + DEVICE_MSG_TEXT_MAX_LENGTH)
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "event_converter.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define MAP(FROM, TO) case FROM: *to = TO; return true
|
||||
#define FAIL default: return false
|
||||
|
||||
@@ -128,6 +126,15 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to) {
|
||||
switch (from) {
|
||||
MAP(SDL_MOUSEBUTTONDOWN, AMOTION_EVENT_ACTION_DOWN);
|
||||
MAP(SDL_MOUSEBUTTONUP, AMOTION_EVENT_ACTION_UP);
|
||||
FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
static enum android_motionevent_buttons
|
||||
convert_mouse_buttons(uint32_t state) {
|
||||
enum android_motionevent_buttons buttons = 0;
|
||||
@@ -167,31 +174,20 @@ convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to) {
|
||||
switch (from) {
|
||||
MAP(SDL_MOUSEBUTTONDOWN, AMOTION_EVENT_ACTION_DOWN);
|
||||
MAP(SDL_MOUSEBUTTONUP, AMOTION_EVENT_ACTION_UP);
|
||||
FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
convert_mouse_button(const SDL_MouseButtonEvent *from, struct size screen_size,
|
||||
struct control_msg *to) {
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT;
|
||||
|
||||
if (!convert_mouse_action(from->type, &to->inject_touch_event.action)) {
|
||||
if (!convert_mouse_action(from->type, &to->inject_mouse_event.action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
|
||||
to->inject_touch_event.position.screen_size = screen_size;
|
||||
to->inject_touch_event.position.point.x = from->x;
|
||||
to->inject_touch_event.position.point.y = from->y;
|
||||
to->inject_touch_event.pressure = 1.f;
|
||||
to->inject_touch_event.buttons =
|
||||
to->inject_mouse_event.buttons =
|
||||
convert_mouse_buttons(SDL_BUTTON(from->button));
|
||||
to->inject_mouse_event.position.screen_size = screen_size;
|
||||
to->inject_mouse_event.position.point.x = from->x;
|
||||
to->inject_mouse_event.position.point.y = from->y;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -199,14 +195,12 @@ convert_mouse_button(const SDL_MouseButtonEvent *from, struct size screen_size,
|
||||
bool
|
||||
convert_mouse_motion(const SDL_MouseMotionEvent *from, struct size screen_size,
|
||||
struct control_msg *to) {
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
||||
to->inject_touch_event.action = AMOTION_EVENT_ACTION_MOVE;
|
||||
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
|
||||
to->inject_touch_event.position.screen_size = screen_size;
|
||||
to->inject_touch_event.position.point.x = from->x;
|
||||
to->inject_touch_event.position.point.y = from->y;
|
||||
to->inject_touch_event.pressure = 1.f;
|
||||
to->inject_touch_event.buttons = convert_mouse_buttons(from->state);
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT;
|
||||
to->inject_mouse_event.action = AMOTION_EVENT_ACTION_MOVE;
|
||||
to->inject_mouse_event.buttons = convert_mouse_buttons(from->state);
|
||||
to->inject_mouse_event.position.screen_size = screen_size;
|
||||
to->inject_mouse_event.position.point.x = from->x;
|
||||
to->inject_mouse_event.position.point.y = from->y;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -230,13 +224,12 @@ convert_touch(const SDL_TouchFingerEvent *from, struct size screen_size,
|
||||
return false;
|
||||
}
|
||||
|
||||
to->inject_touch_event.pointer_id = from->fingerId;
|
||||
to->inject_touch_event.finger_id = from->fingerId;
|
||||
to->inject_touch_event.position.screen_size = screen_size;
|
||||
// SDL touch event coordinates are normalized in the range [0; 1]
|
||||
to->inject_touch_event.position.point.x = from->x * screen_size.width;
|
||||
to->inject_touch_event.position.point.y = from->y * screen_size.height;
|
||||
to->inject_touch_event.position.point.y = from->x * screen_size.height;
|
||||
to->inject_touch_event.pressure = from->pressure;
|
||||
to->inject_touch_event.buttons = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <SDL2/SDL_events.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "control_msg.h"
|
||||
|
||||
struct complete_mouse_motion_event {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <SDL2/SDL_mutex.h>
|
||||
#include <SDL2/SDL_thread.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "cbuf.h"
|
||||
#include "command.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <SDL2/SDL_assert.h>
|
||||
#include <SDL2/SDL_timer.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "lock_util.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include <SDL2/SDL_mutex.h>
|
||||
#include <SDL2/SDL_thread.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
struct fps_counter {
|
||||
SDL_Thread *thread;
|
||||
SDL_mutex *mutex;
|
||||
|
||||
@@ -1,25 +1,10 @@
|
||||
#include "input_manager.h"
|
||||
|
||||
#include <SDL2/SDL_assert.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "event_converter.h"
|
||||
#include "lock_util.h"
|
||||
#include "log.h"
|
||||
|
||||
void
|
||||
input_manager_init(struct input_manager *input_manager,
|
||||
struct controller *controller,
|
||||
struct video_buffer *video_buffer,
|
||||
struct screen *screen) {
|
||||
input_manager->controller = controller;
|
||||
input_manager->video_buffer = video_buffer;
|
||||
input_manager->screen = screen;
|
||||
|
||||
input_manager->ctrl_down = false;
|
||||
input_manager->vfinger.down = false;
|
||||
}
|
||||
|
||||
// Convert window coordinates (as provided by SDL_GetMouseState() to renderer
|
||||
// coordinates (as provided in SDL mouse events)
|
||||
//
|
||||
@@ -117,7 +102,7 @@ press_back_or_turn_screen_on(struct controller *controller) {
|
||||
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'");
|
||||
LOGW("Could not request 'turn screen on'");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,26 +209,6 @@ clipboard_paste(struct controller *controller) {
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
simulate_virtual_finger(struct input_manager *input_manager, bool down,
|
||||
struct position *position) {
|
||||
SDL_assert(input_manager->vfinger.down != down);
|
||||
input_manager->vfinger.down = down;
|
||||
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
||||
msg.inject_touch_event.action = down ? AMOTION_EVENT_ACTION_DOWN
|
||||
: AMOTION_EVENT_ACTION_UP;
|
||||
msg.inject_touch_event.pointer_id = POINTER_ID_VIRTUAL_FINGER;
|
||||
msg.inject_touch_event.position = *position;
|
||||
msg.inject_touch_event.pressure = 1.f;
|
||||
msg.inject_touch_event.buttons = 0;
|
||||
|
||||
if (!controller_push_msg(input_manager->controller, &msg)) {
|
||||
LOGW("Could not request 'inject virtual finger event'");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
input_manager_process_text_input(struct input_manager *input_manager,
|
||||
const SDL_TextInputEvent *event) {
|
||||
@@ -277,14 +242,6 @@ input_manager_process_key(struct input_manager *input_manager,
|
||||
bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
|
||||
bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI);
|
||||
|
||||
// store the Ctrl state to modify mouse events
|
||||
input_manager->ctrl_down = ctrl;
|
||||
|
||||
if (input_manager->vfinger.down && !ctrl) {
|
||||
simulate_virtual_finger(input_manager, false,
|
||||
&input_manager->vfinger.position);
|
||||
}
|
||||
|
||||
// use Cmd on macOS, Ctrl on other platforms
|
||||
#ifdef __APPLE__
|
||||
bool cmd = !ctrl && meta;
|
||||
@@ -464,11 +421,6 @@ void
|
||||
input_manager_process_mouse_button(struct input_manager *input_manager,
|
||||
const SDL_MouseButtonEvent *event,
|
||||
bool control) {
|
||||
if (event->which == SDL_TOUCH_MOUSEID) {
|
||||
// simulated from touch events, so it's a duplicate
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->type == SDL_MOUSEBUTTONDOWN) {
|
||||
if (control && event->button == SDL_BUTTON_RIGHT) {
|
||||
press_back_or_turn_screen_on(input_manager->controller);
|
||||
@@ -479,24 +431,12 @@ input_manager_process_mouse_button(struct input_manager *input_manager,
|
||||
return;
|
||||
}
|
||||
// double-click on black borders resize to fit the device screen
|
||||
if (event->button == SDL_BUTTON_LEFT) {
|
||||
if (event->clicks >= 2) {
|
||||
bool outside =
|
||||
is_outside_device_screen(input_manager, event->x, event->y);
|
||||
if (outside) {
|
||||
screen_resize_to_fit(input_manager->screen);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct virtual_finger *vfinger = &input_manager->vfinger;
|
||||
if (input_manager->ctrl_down && !vfinger->down) {
|
||||
vfinger->position.point.x = event->x;
|
||||
vfinger->position.point.y = event->y;
|
||||
vfinger->position.screen_size =
|
||||
input_manager->screen->frame_size,
|
||||
simulate_virtual_finger(input_manager, true,
|
||||
&vfinger->position);
|
||||
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
|
||||
bool outside =
|
||||
is_outside_device_screen(input_manager, event->x, event->y);
|
||||
if (outside) {
|
||||
screen_resize_to_fit(input_manager->screen);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// otherwise, send the click event to the device
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "controller.h"
|
||||
#include "fps_counter.h"
|
||||
@@ -14,21 +13,8 @@ struct input_manager {
|
||||
struct controller *controller;
|
||||
struct video_buffer *video_buffer;
|
||||
struct screen *screen;
|
||||
|
||||
bool ctrl_down;
|
||||
|
||||
struct virtual_finger {
|
||||
bool down;
|
||||
struct position position;
|
||||
} vfinger;
|
||||
};
|
||||
|
||||
void
|
||||
input_manager_init(struct input_manager *input_manager,
|
||||
struct controller *controller,
|
||||
struct video_buffer *video_buffer,
|
||||
struct screen *screen);
|
||||
|
||||
void
|
||||
input_manager_process_text_input(struct input_manager *input_manager,
|
||||
const SDL_TextInputEvent *event);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <stdint.h>
|
||||
#include <SDL2/SDL_mutex.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
|
||||
static inline void
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#define SDL_MAIN_HANDLED // avoid link error on Linux Windows Subsystem
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "compat.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "recorder.h"
|
||||
|
||||
@@ -328,7 +328,7 @@ parse_args(struct args *args, int argc, char *argv[]) {
|
||||
{"push-target", required_argument, NULL,
|
||||
OPT_PUSH_TARGET},
|
||||
{"record", required_argument, NULL, 'r'},
|
||||
{"record-format", required_argument, NULL, 'F'},
|
||||
{"record-format", required_argument, NULL, 'f'},
|
||||
{"render-expired-frames", no_argument, NULL,
|
||||
OPT_RENDER_EXPIRED_FRAMES},
|
||||
{"serial", required_argument, NULL, 's'},
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
typedef int socket_t;
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
bool
|
||||
net_init(void);
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include <stddef.h>
|
||||
#include <SDL2/SDL_assert.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
// To define a queue type of "struct foo":
|
||||
// struct queue_foo QUEUE(struct foo);
|
||||
#define QUEUE(TYPE) { \
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <SDL2/SDL_mutex.h>
|
||||
#include <SDL2/SDL_thread.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "net.h"
|
||||
|
||||
// receive events from the device
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#include <libavutil/time.h>
|
||||
#include <SDL2/SDL_assert.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "compat.h"
|
||||
#include "config.h"
|
||||
#include "lock_util.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <SDL2/SDL_mutex.h>
|
||||
#include <SDL2/SDL_thread.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "queue.h"
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <sys/time.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "command.h"
|
||||
#include "common.h"
|
||||
#include "compat.h"
|
||||
@@ -37,7 +36,12 @@ static struct decoder decoder;
|
||||
static struct recorder recorder;
|
||||
static struct controller controller;
|
||||
static struct file_handler file_handler;
|
||||
static struct input_manager input_manager;
|
||||
|
||||
static struct input_manager input_manager = {
|
||||
.controller = &controller,
|
||||
.video_buffer = &video_buffer,
|
||||
.screen = &screen,
|
||||
};
|
||||
|
||||
// init SDL and set appropriate hints
|
||||
static bool
|
||||
@@ -306,8 +310,6 @@ scrcpy(const struct scrcpy_options *options) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
input_manager_init(&input_manager, &controller, &video_buffer, &screen);
|
||||
|
||||
if (!server_connect_to(&server)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <stdint.h>
|
||||
#include <recorder.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
struct scrcpy_options {
|
||||
const char *serial;
|
||||
const char *crop;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <string.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "compat.h"
|
||||
#include "icon.xpm"
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <SDL2/SDL.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
|
||||
struct video_buffer;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "command.h"
|
||||
#include "net.h"
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
#include <SDL2/SDL_stdinc.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
size_t
|
||||
xstrncpy(char *dest, const char *src, size_t n) {
|
||||
size_t i;
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
// like strncpy, except:
|
||||
// - it copies at most n-1 chars
|
||||
// - the dest string is nul-terminated
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#include <SDL2/SDL_thread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "compat.h"
|
||||
#include "config.h"
|
||||
#include "buffer_util.h"
|
||||
#include "decoder.h"
|
||||
#include "events.h"
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <SDL2/SDL_atomic.h>
|
||||
#include <SDL2/SDL_thread.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "net.h"
|
||||
|
||||
struct video_buffer;
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
#include "command.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
bool
|
||||
net_init(void) {
|
||||
// do nothing
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "net.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
|
||||
bool
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
|
||||
struct index {
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
SDL_Surface *
|
||||
read_xpm(char *xpm[]);
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <SDL2/SDL_mutex.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "fps_counter.h"
|
||||
|
||||
// forward declarations
|
||||
|
||||
@@ -67,12 +67,45 @@ static void test_serialize_inject_text_long(void) {
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_inject_mouse_event(void) {
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT,
|
||||
.inject_mouse_event = {
|
||||
.action = AMOTION_EVENT_ACTION_DOWN,
|
||||
.buttons = AMOTION_EVENT_BUTTON_PRIMARY,
|
||||
.position = {
|
||||
.point = {
|
||||
.x = 260,
|
||||
.y = 1026,
|
||||
},
|
||||
.screen_size = {
|
||||
.width = 1080,
|
||||
.height = 1920,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
|
||||
int size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 18);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT,
|
||||
0x00, // AKEY_EVENT_ACTION_DOWN
|
||||
0x00, 0x00, 0x00, 0x01, // AMOTION_EVENT_BUTTON_PRIMARY
|
||||
0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x04, 0x02, // 260 1026
|
||||
0x04, 0x38, 0x07, 0x80, // 1080 1920
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
#include <stdio.h>
|
||||
static void test_serialize_inject_touch_event(void) {
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||
.inject_touch_event = {
|
||||
.action = AMOTION_EVENT_ACTION_DOWN,
|
||||
.pointer_id = 0x1234567887654321L,
|
||||
.finger_id = 0x1234567887654321L,
|
||||
.position = {
|
||||
.point = {
|
||||
.x = 100,
|
||||
@@ -84,22 +117,20 @@ static void test_serialize_inject_touch_event(void) {
|
||||
},
|
||||
},
|
||||
.pressure = 1.0f,
|
||||
.buttons = AMOTION_EVENT_BUTTON_PRIMARY,
|
||||
},
|
||||
};
|
||||
|
||||
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
|
||||
int size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 28);
|
||||
assert(size == 24);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||
0x00, // AKEY_EVENT_ACTION_DOWN
|
||||
0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21, // pointer id
|
||||
0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21, // finger id
|
||||
0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0xc8, // 100 200
|
||||
0x04, 0x38, 0x07, 0x80, // 1080 1920
|
||||
0xff, 0xff, // pressure
|
||||
0x00, 0x00, 0x00, 0x01 // AMOTION_EVENT_BUTTON_PRIMARY
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
@@ -240,6 +271,7 @@ int main(void) {
|
||||
test_serialize_inject_keycode();
|
||||
test_serialize_inject_text();
|
||||
test_serialize_inject_text_long();
|
||||
test_serialize_inject_mouse_event();
|
||||
test_serialize_inject_touch_event();
|
||||
test_serialize_inject_scroll_event();
|
||||
test_serialize_back_or_screen_on();
|
||||
|
||||
@@ -7,14 +7,15 @@ public final class ControlMessage {
|
||||
|
||||
public static final int TYPE_INJECT_KEYCODE = 0;
|
||||
public static final int TYPE_INJECT_TEXT = 1;
|
||||
public static final int TYPE_INJECT_TOUCH_EVENT = 2;
|
||||
public static final int TYPE_INJECT_SCROLL_EVENT = 3;
|
||||
public static final int TYPE_BACK_OR_SCREEN_ON = 4;
|
||||
public static final int TYPE_EXPAND_NOTIFICATION_PANEL = 5;
|
||||
public static final int TYPE_COLLAPSE_NOTIFICATION_PANEL = 6;
|
||||
public static final int TYPE_GET_CLIPBOARD = 7;
|
||||
public static final int TYPE_SET_CLIPBOARD = 8;
|
||||
public static final int TYPE_SET_SCREEN_POWER_MODE = 9;
|
||||
public static final int TYPE_INJECT_MOUSE_EVENT = 2;
|
||||
public static final int TYPE_INJECT_TOUCH_EVENT = 3;
|
||||
public static final int TYPE_INJECT_SCROLL_EVENT = 4;
|
||||
public static final int TYPE_BACK_OR_SCREEN_ON = 5;
|
||||
public static final int TYPE_EXPAND_NOTIFICATION_PANEL = 6;
|
||||
public static final int TYPE_COLLAPSE_NOTIFICATION_PANEL = 7;
|
||||
public static final int TYPE_GET_CLIPBOARD = 8;
|
||||
public static final int TYPE_SET_CLIPBOARD = 9;
|
||||
public static final int TYPE_SET_SCREEN_POWER_MODE = 10;
|
||||
|
||||
private int type;
|
||||
private String text;
|
||||
@@ -22,7 +23,7 @@ public final class ControlMessage {
|
||||
private int action; // KeyEvent.ACTION_* or MotionEvent.ACTION_* or POWER_MODE_*
|
||||
private int keycode; // KeyEvent.KEYCODE_*
|
||||
private int buttons; // MotionEvent.BUTTON_*
|
||||
private long pointerId;
|
||||
private long fingerId;
|
||||
private float pressure;
|
||||
private Position position;
|
||||
private int hScroll;
|
||||
@@ -47,15 +48,22 @@ public final class ControlMessage {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static ControlMessage createInjectTouchEvent(int action, long pointerId, Position position, float pressure,
|
||||
int buttons) {
|
||||
public static ControlMessage createInjectMouseEvent(int action, int buttons, Position position) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = TYPE_INJECT_MOUSE_EVENT;
|
||||
msg.action = action;
|
||||
msg.buttons = buttons;
|
||||
msg.position = position;
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static ControlMessage createInjectTouchEvent(int action, long fingerId, Position position, float pressure) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = TYPE_INJECT_TOUCH_EVENT;
|
||||
msg.action = action;
|
||||
msg.pointerId = pointerId;
|
||||
msg.fingerId = fingerId;
|
||||
msg.pressure = pressure;
|
||||
msg.position = position;
|
||||
msg.buttons = buttons;
|
||||
return msg;
|
||||
}
|
||||
|
||||
@@ -115,8 +123,8 @@ public final class ControlMessage {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
public long getPointerId() {
|
||||
return pointerId;
|
||||
public long getFingerId() {
|
||||
return fingerId;
|
||||
}
|
||||
|
||||
public float getPressure() {
|
||||
|
||||
@@ -60,6 +60,9 @@ public class ControlMessageReader {
|
||||
case ControlMessage.TYPE_INJECT_TEXT:
|
||||
msg = parseInjectText();
|
||||
break;
|
||||
case ControlMessage.TYPE_INJECT_MOUSE_EVENT:
|
||||
msg = parseInjectMouseEvent();
|
||||
break;
|
||||
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
||||
msg = parseInjectTouchEvent();
|
||||
break;
|
||||
@@ -121,20 +124,28 @@ public class ControlMessageReader {
|
||||
return ControlMessage.createInjectText(text);
|
||||
}
|
||||
|
||||
@SuppressWarnings("checkstyle:MagicNumber")
|
||||
private ControlMessage parseInjectMouseEvent() {
|
||||
if (buffer.remaining() < INJECT_MOUSE_EVENT_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
int action = toUnsigned(buffer.get());
|
||||
int buttons = buffer.getInt();
|
||||
Position position = readPosition(buffer);
|
||||
return ControlMessage.createInjectMouseEvent(action, buttons, position);
|
||||
}
|
||||
|
||||
private ControlMessage parseInjectTouchEvent() {
|
||||
if (buffer.remaining() < INJECT_TOUCH_EVENT_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
int action = toUnsigned(buffer.get());
|
||||
long pointerId = buffer.getLong();
|
||||
long fingerId = buffer.getLong();
|
||||
Position position = readPosition(buffer);
|
||||
// 16 bits fixed-point
|
||||
int pressureInt = toUnsigned(buffer.getShort());
|
||||
// convert it to a float between 0 and 1 (0x1p16f is 2^16 as float)
|
||||
float pressure = pressureInt == 0xffff ? 1f : (pressureInt / 0x1p16f);
|
||||
int buttons = buffer.getInt();
|
||||
return ControlMessage.createInjectTouchEvent(action, pointerId, position, pressure, buttons);
|
||||
return ControlMessage.createInjectTouchEvent(action, fingerId, position, pressure);
|
||||
}
|
||||
|
||||
private ControlMessage parseInjectScrollEvent() {
|
||||
|
||||
@@ -13,40 +13,65 @@ import java.io.IOException;
|
||||
|
||||
public class Controller {
|
||||
|
||||
private static final int MAX_FINGERS = 10;
|
||||
|
||||
private final Device device;
|
||||
private final DesktopConnection connection;
|
||||
private final DeviceMessageSender sender;
|
||||
|
||||
private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
|
||||
|
||||
private long lastMouseDown;
|
||||
private final MotionEvent.PointerProperties[] mousePointerProperties = {new MotionEvent.PointerProperties()};
|
||||
private final MotionEvent.PointerCoords[] mousePointerCoords = {new MotionEvent.PointerCoords()};
|
||||
|
||||
private long lastTouchDown;
|
||||
private final PointersState pointersState = new PointersState();
|
||||
private final MotionEvent.PointerProperties[] pointerProperties =
|
||||
new MotionEvent.PointerProperties[PointersState.MAX_POINTERS];
|
||||
private final MotionEvent.PointerCoords[] pointerCoords =
|
||||
new MotionEvent.PointerCoords[PointersState.MAX_POINTERS];
|
||||
private final FingersState fingersState = new FingersState(MAX_FINGERS);
|
||||
private final MotionEvent.PointerProperties[] touchPointerProperties = new MotionEvent.PointerProperties[MAX_FINGERS];
|
||||
private final MotionEvent.PointerCoords[] touchPointerCoords = new MotionEvent.PointerCoords[MAX_FINGERS];
|
||||
|
||||
public Controller(Device device, DesktopConnection connection) {
|
||||
this.device = device;
|
||||
this.connection = connection;
|
||||
initPointers();
|
||||
initMousePointer();
|
||||
initTouchPointers();
|
||||
sender = new DeviceMessageSender(connection);
|
||||
}
|
||||
|
||||
private void initPointers() {
|
||||
for (int i = 0; i < PointersState.MAX_POINTERS; ++i) {
|
||||
private void initMousePointer() {
|
||||
MotionEvent.PointerProperties props = mousePointerProperties[0];
|
||||
props.id = 0;
|
||||
props.toolType = MotionEvent.TOOL_TYPE_FINGER;
|
||||
|
||||
MotionEvent.PointerCoords coords = mousePointerCoords[0];
|
||||
coords.orientation = 0;
|
||||
coords.pressure = 1;
|
||||
coords.size = 1;
|
||||
}
|
||||
|
||||
private void initTouchPointers() {
|
||||
for (int i = 0; i < MAX_FINGERS; ++i) {
|
||||
MotionEvent.PointerProperties props = new MotionEvent.PointerProperties();
|
||||
props.toolType = MotionEvent.TOOL_TYPE_FINGER;
|
||||
|
||||
MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
|
||||
coords.orientation = 0;
|
||||
coords.size = 1;
|
||||
|
||||
pointerProperties[i] = props;
|
||||
pointerCoords[i] = coords;
|
||||
}
|
||||
}
|
||||
|
||||
private void setMousePointerCoords(Point point) {
|
||||
MotionEvent.PointerCoords coords = mousePointerCoords[0];
|
||||
coords.x = point.getX();
|
||||
coords.y = point.getY();
|
||||
}
|
||||
|
||||
private void setScroll(int hScroll, int vScroll) {
|
||||
MotionEvent.PointerCoords coords = mousePointerCoords[0];
|
||||
coords.setAxisValue(MotionEvent.AXIS_HSCROLL, hScroll);
|
||||
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
|
||||
}
|
||||
|
||||
@SuppressWarnings("checkstyle:MagicNumber")
|
||||
public void control() throws IOException {
|
||||
// on start, power on the device
|
||||
@@ -81,8 +106,8 @@ public class Controller {
|
||||
case ControlMessage.TYPE_INJECT_TEXT:
|
||||
injectText(msg.getText());
|
||||
break;
|
||||
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
||||
injectTouch(msg.getAction(), msg.getPointerId(), msg.getPosition(), msg.getPressure(), 0);
|
||||
case ControlMessage.TYPE_INJECT_MOUSE_EVENT:
|
||||
injectMouse(msg.getAction(), msg.getButtons(), msg.getPosition());
|
||||
break;
|
||||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
||||
@@ -142,42 +167,47 @@ public class Controller {
|
||||
return successCount;
|
||||
}
|
||||
|
||||
private boolean injectTouch(int action, long pointerId, Position position, float pressure, int buttons) {
|
||||
private boolean injectMouse(int action, int buttons, Position position) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
lastMouseDown = now;
|
||||
}
|
||||
Point point = device.getPhysicalPoint(position);
|
||||
if (point == null) {
|
||||
// ignore event
|
||||
return false;
|
||||
}
|
||||
setMousePointerCoords(point);
|
||||
MotionEvent event = MotionEvent.obtain(lastMouseDown, now, action, 1, mousePointerProperties, mousePointerCoords, 0, buttons, 1f, 1f, 0, 0,
|
||||
InputDevice.SOURCE_TOUCHSCREEN, 0);
|
||||
return injectEvent(event);
|
||||
}
|
||||
|
||||
int pointerIndex = pointersState.getPointerIndex(pointerId);
|
||||
if (pointerIndex == -1) {
|
||||
Ln.w("Too many pointers for touch event");
|
||||
private boolean injectTouch(int action, int fingerId, Position position, float pressure) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
lastTouchDown = now;
|
||||
}
|
||||
Point point = device.getPhysicalPoint(position);
|
||||
if (point == null) {
|
||||
// ignore event
|
||||
return false;
|
||||
}
|
||||
Pointer pointer = pointersState.get(pointerIndex);
|
||||
pointer.setPoint(point);
|
||||
pointer.setPressure(pressure);
|
||||
pointer.setUp(action == MotionEvent.ACTION_UP);
|
||||
|
||||
int pointerCount = pointersState.update(pointerProperties, pointerCoords);
|
||||
|
||||
if (pointerCount == 1) {
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
lastTouchDown = now;
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
if (!fingersState.unset(fingerId)) {
|
||||
Ln.w("Unexpected ACTION_UP on unknown finger");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// secondary pointers must use ACTION_POINTER_* ORed with the pointerIndex
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
action = MotionEvent.ACTION_POINTER_UP | (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
|
||||
} else if (action == MotionEvent.ACTION_DOWN) {
|
||||
action = MotionEvent.ACTION_POINTER_DOWN | (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
|
||||
// ACTION_DOWN or ACTION_MOVE
|
||||
if (!fingersState.set(fingerId, point, pressure)) {
|
||||
Ln.w("Too many fingers for touch event");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties,
|
||||
pointerCoords, 0, 0, 1f, 1f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
|
||||
int pointerCount = fingersState.update(touchPointerProperties, touchPointerCoords);
|
||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, touchPointerProperties, touchPointerCoords, 0, 0, 1f, 1f, 0, 0,
|
||||
InputDevice.SOURCE_TOUCHSCREEN, 0);
|
||||
return injectEvent(event);
|
||||
}
|
||||
|
||||
@@ -188,30 +218,23 @@ public class Controller {
|
||||
// ignore event
|
||||
return false;
|
||||
}
|
||||
|
||||
MotionEvent.PointerProperties props = pointerProperties[0];
|
||||
props.id = 0;
|
||||
|
||||
MotionEvent.PointerCoords coords = pointerCoords[0];
|
||||
coords.x = point.getX();
|
||||
coords.y = point.getY();
|
||||
coords.setAxisValue(MotionEvent.AXIS_HSCROLL, hScroll);
|
||||
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
|
||||
|
||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties,
|
||||
pointerCoords, 0, 0, 1f, 1f, 0, 0, InputDevice.SOURCE_MOUSE, 0);
|
||||
setMousePointerCoords(point);
|
||||
setScroll(hScroll, vScroll);
|
||||
MotionEvent event = MotionEvent.obtain(lastMouseDown, now, MotionEvent.ACTION_SCROLL, 1, mousePointerProperties, mousePointerCoords, 0, 0, 1f, 1f, 0,
|
||||
0, InputDevice.SOURCE_MOUSE, 0);
|
||||
return injectEvent(event);
|
||||
}
|
||||
|
||||
private boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD,
|
||||
0, 0, InputDevice.SOURCE_KEYBOARD);
|
||||
KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
|
||||
InputDevice.SOURCE_KEYBOARD);
|
||||
return injectEvent(event);
|
||||
}
|
||||
|
||||
private boolean injectKeycode(int keyCode) {
|
||||
return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0);
|
||||
return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0)
|
||||
&& injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0);
|
||||
}
|
||||
|
||||
private boolean injectEvent(InputEvent event) {
|
||||
|
||||
32
server/src/main/java/com/genymobile/scrcpy/Finger.java
Normal file
32
server/src/main/java/com/genymobile/scrcpy/Finger.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.genymobile.scrcpy;
|
||||
|
||||
public class Finger {
|
||||
|
||||
private final long id;
|
||||
private Point point;
|
||||
private float pressure;
|
||||
|
||||
public Finger(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Point getPoint() {
|
||||
return point;
|
||||
}
|
||||
|
||||
public void setPoint(Point point) {
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
public float getPressure() {
|
||||
return pressure;
|
||||
}
|
||||
|
||||
public void setPressure(float pressure) {
|
||||
this.pressure = pressure;
|
||||
}
|
||||
}
|
||||
99
server/src/main/java/com/genymobile/scrcpy/FingersState.java
Normal file
99
server/src/main/java/com/genymobile/scrcpy/FingersState.java
Normal file
@@ -0,0 +1,99 @@
|
||||
package com.genymobile.scrcpy;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class FingersState {
|
||||
|
||||
/**
|
||||
* Array of enabled fingers (can contain "null" holes).
|
||||
* <p>
|
||||
* Once a Finger (identified by its id received from the client) is enabled, it is never moved.
|
||||
* <p>
|
||||
* Its index is its local identifier injected into MotionEvents.
|
||||
*/
|
||||
private final Finger[] fingers;
|
||||
|
||||
public FingersState(int maxFingers) {
|
||||
fingers = new Finger[maxFingers];
|
||||
}
|
||||
|
||||
private int indexOf(long id) {
|
||||
for (int i = 0; i < fingers.length; ++i) {
|
||||
Finger finger = fingers[i];
|
||||
if (finger != null && finger.getId() == id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int indexOfFirstEmpty() {
|
||||
for (int i = 0; i < fingers.length; ++i) {
|
||||
if (fingers[i] == null) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private Finger create(long id) {
|
||||
int index = indexOf(id);
|
||||
if (index != -1) {
|
||||
// already exists, return it
|
||||
return fingers[index];
|
||||
}
|
||||
int firstEmpty = indexOfFirstEmpty();
|
||||
if (firstEmpty == -1) {
|
||||
// it's full
|
||||
return null;
|
||||
}
|
||||
Finger finger = new Finger(id);
|
||||
fingers[firstEmpty] = finger;
|
||||
return finger;
|
||||
}
|
||||
|
||||
public boolean unset(int id) {
|
||||
int index = indexOf(id);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
fingers[index] = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean set(int id, Point point, float pressure) {
|
||||
Finger finger = create(id);
|
||||
if (finger == null) {
|
||||
return false;
|
||||
}
|
||||
finger.setPoint(point);
|
||||
finger.setPressure(pressure);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the motion event parameters.
|
||||
*
|
||||
* @param props the pointer properties
|
||||
* @param coords the pointer coordinates
|
||||
* @return The number of items initialized (the number of fingers).
|
||||
*/
|
||||
public int update(MotionEvent.PointerProperties[] props, MotionEvent.PointerCoords[] coords) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < fingers.length; ++i) {
|
||||
Finger finger = fingers[i];
|
||||
if (finger != null) {
|
||||
// id 0 is reserved for mouse events
|
||||
props[count].id = i + 1;
|
||||
|
||||
Point point = finger.getPoint();
|
||||
coords[i].x = point.getX();
|
||||
coords[i].y = point.getY();
|
||||
coords[i].pressure = finger.getPressure();
|
||||
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.genymobile.scrcpy;
|
||||
|
||||
public class Pointer {
|
||||
|
||||
/**
|
||||
* Pointer id as received from the client.
|
||||
*/
|
||||
private final long id;
|
||||
|
||||
/**
|
||||
* Local pointer id, using the lowest possible values to fill the {@link android.view.MotionEvent.PointerProperties PointerProperties}.
|
||||
*/
|
||||
private final int localId;
|
||||
|
||||
private Point point;
|
||||
private float pressure;
|
||||
private boolean up;
|
||||
|
||||
public Pointer(long id, int localId) {
|
||||
this.id = id;
|
||||
this.localId = localId;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getLocalId() {
|
||||
return localId;
|
||||
}
|
||||
|
||||
public Point getPoint() {
|
||||
return point;
|
||||
}
|
||||
|
||||
public void setPoint(Point point) {
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
public float getPressure() {
|
||||
return pressure;
|
||||
}
|
||||
|
||||
public void setPressure(float pressure) {
|
||||
this.pressure = pressure;
|
||||
}
|
||||
|
||||
public boolean isUp() {
|
||||
return up;
|
||||
}
|
||||
|
||||
public void setUp(boolean up) {
|
||||
this.up = up;
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package com.genymobile.scrcpy;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PointersState {
|
||||
|
||||
public static final int MAX_POINTERS = 10;
|
||||
|
||||
private final List<Pointer> pointers = new ArrayList<>();
|
||||
|
||||
private int indexOf(long id) {
|
||||
for (int i = 0; i < pointers.size(); ++i) {
|
||||
Pointer pointer = pointers.get(i);
|
||||
if (pointer.getId() == id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private boolean isLocalIdAvailable(int localId) {
|
||||
for (int i = 0; i < pointers.size(); ++i) {
|
||||
Pointer pointer = pointers.get(i);
|
||||
if (pointer.getLocalId() == localId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private int nextUnusedLocalId() {
|
||||
for (int localId = 0; localId < MAX_POINTERS; ++localId) {
|
||||
if (isLocalIdAvailable(localId)) {
|
||||
return localId;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Pointer get(int index) {
|
||||
return pointers.get(index);
|
||||
}
|
||||
|
||||
public int getPointerIndex(long id) {
|
||||
int index = indexOf(id);
|
||||
if (index != -1) {
|
||||
// already exists, return it
|
||||
return index;
|
||||
}
|
||||
if (pointers.size() >= MAX_POINTERS) {
|
||||
// it's full
|
||||
return -1;
|
||||
}
|
||||
// id 0 is reserved for mouse events
|
||||
int localId = nextUnusedLocalId();
|
||||
if (localId == -1) {
|
||||
throw new AssertionError("pointers.size() < maxFingers implies that a local id is available");
|
||||
}
|
||||
Pointer pointer = new Pointer(id, localId);
|
||||
pointers.add(pointer);
|
||||
// return the index of the pointer
|
||||
return pointers.size() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the motion event parameters.
|
||||
*
|
||||
* @param props the pointer properties
|
||||
* @param coords the pointer coordinates
|
||||
* @return The number of items initialized (the number of pointers).
|
||||
*/
|
||||
public int update(MotionEvent.PointerProperties[] props, MotionEvent.PointerCoords[] coords) {
|
||||
int count = pointers.size();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
Pointer pointer = pointers.get(i);
|
||||
|
||||
// id 0 is reserved for mouse events
|
||||
props[i].id = pointer.getLocalId();
|
||||
|
||||
Point point = pointer.getPoint();
|
||||
coords[i].x = point.getX();
|
||||
coords[i].y = point.getY();
|
||||
coords[i].pressure = pointer.getPressure();
|
||||
}
|
||||
cleanUp();
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all pointers which are UP.
|
||||
*/
|
||||
private void cleanUp() {
|
||||
for (int i = pointers.size() - 1; i >= 0; --i) {
|
||||
Pointer pointer = pointers.get(i);
|
||||
if (pointer.isUp()) {
|
||||
pointers.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,34 @@ public class ControlMessageReaderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("checkstyle:MagicNumber")
|
||||
public void testParseMouseEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_INJECT_MOUSE_EVENT);
|
||||
dos.writeByte(MotionEvent.ACTION_DOWN);
|
||||
dos.writeInt(MotionEvent.BUTTON_PRIMARY);
|
||||
dos.writeInt(100);
|
||||
dos.writeInt(200);
|
||||
dos.writeShort(1080);
|
||||
dos.writeShort(1920);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
|
||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_MOUSE_EVENT, event.getType());
|
||||
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
|
||||
Assert.assertEquals(MotionEvent.BUTTON_PRIMARY, event.getButtons());
|
||||
Assert.assertEquals(100, event.getPosition().getPoint().getX());
|
||||
Assert.assertEquals(200, event.getPosition().getPoint().getY());
|
||||
Assert.assertEquals(1080, event.getPosition().getScreenSize().getWidth());
|
||||
Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseTouchEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
@@ -85,13 +112,12 @@ public class ControlMessageReaderTest {
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_INJECT_TOUCH_EVENT);
|
||||
dos.writeByte(MotionEvent.ACTION_DOWN);
|
||||
dos.writeLong(-42); // pointerId
|
||||
dos.writeLong(-42); // fingerId
|
||||
dos.writeInt(100);
|
||||
dos.writeInt(200);
|
||||
dos.writeShort(1080);
|
||||
dos.writeShort(1920);
|
||||
dos.writeShort(0xffff); // pressure
|
||||
dos.writeInt(MotionEvent.BUTTON_PRIMARY);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
@@ -100,13 +126,12 @@ public class ControlMessageReaderTest {
|
||||
|
||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_TOUCH_EVENT, event.getType());
|
||||
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
|
||||
Assert.assertEquals(-42, event.getPointerId());
|
||||
Assert.assertEquals(-42, event.getFingerId());
|
||||
Assert.assertEquals(100, event.getPosition().getPoint().getX());
|
||||
Assert.assertEquals(200, event.getPosition().getPoint().getY());
|
||||
Assert.assertEquals(1080, event.getPosition().getScreenSize().getWidth());
|
||||
Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight());
|
||||
Assert.assertEquals(1f, event.getPressure(), 0f); // must be exact
|
||||
Assert.assertEquals(MotionEvent.BUTTON_PRIMARY, event.getButtons());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user