Compare commits
2 Commits
logical_si
...
virtualfin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2d0dcd002 | ||
|
|
9448eae8a4 |
@@ -16,6 +16,7 @@
|
|||||||
(3 + CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH)
|
(3 + CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH)
|
||||||
|
|
||||||
#define POINTER_ID_MOUSE UINT64_C(-1);
|
#define POINTER_ID_MOUSE UINT64_C(-1);
|
||||||
|
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-2);
|
||||||
|
|
||||||
enum control_msg_type {
|
enum control_msg_type {
|
||||||
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||||
|
|||||||
@@ -176,30 +176,19 @@ convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
|
||||||
map_coords(int32_t *x, int32_t *y, const SDL_Rect *rect,
|
|
||||||
struct size frame_size) {
|
|
||||||
*x = (*x - rect->x) * frame_size.width / rect->w;
|
|
||||||
*y = (*y - rect->y) * frame_size.height / rect->h;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
convert_mouse_button(const SDL_MouseButtonEvent *from, const SDL_Rect *rect,
|
convert_mouse_button(const SDL_MouseButtonEvent *from, struct size screen_size,
|
||||||
struct size frame_size, struct control_msg *to) {
|
struct control_msg *to) {
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
||||||
|
|
||||||
if (!convert_mouse_action(from->type, &to->inject_touch_event.action)) {
|
if (!convert_mouse_action(from->type, &to->inject_touch_event.action)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t x = from->x;
|
|
||||||
int32_t y = from->y;
|
|
||||||
map_coords(&x, &y, rect, frame_size);
|
|
||||||
|
|
||||||
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
|
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
|
||||||
to->inject_touch_event.position.screen_size = frame_size;
|
to->inject_touch_event.position.screen_size = screen_size;
|
||||||
to->inject_touch_event.position.point.x = x;
|
to->inject_touch_event.position.point.x = from->x;
|
||||||
to->inject_touch_event.position.point.y = y;
|
to->inject_touch_event.position.point.y = from->y;
|
||||||
to->inject_touch_event.pressure = 1.f;
|
to->inject_touch_event.pressure = 1.f;
|
||||||
to->inject_touch_event.buttons =
|
to->inject_touch_event.buttons =
|
||||||
convert_mouse_buttons(SDL_BUTTON(from->button));
|
convert_mouse_buttons(SDL_BUTTON(from->button));
|
||||||
@@ -208,18 +197,14 @@ convert_mouse_button(const SDL_MouseButtonEvent *from, const SDL_Rect *rect,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
convert_mouse_motion(const SDL_MouseMotionEvent *from, const SDL_Rect *rect,
|
convert_mouse_motion(const SDL_MouseMotionEvent *from, struct size screen_size,
|
||||||
struct size frame_size, struct control_msg *to) {
|
struct control_msg *to) {
|
||||||
int32_t x = from->x;
|
|
||||||
int32_t y = from->y;
|
|
||||||
map_coords(&x, &y, rect, frame_size);
|
|
||||||
|
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
||||||
to->inject_touch_event.action = AMOTION_EVENT_ACTION_MOVE;
|
to->inject_touch_event.action = AMOTION_EVENT_ACTION_MOVE;
|
||||||
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
|
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
|
||||||
to->inject_touch_event.position.screen_size = frame_size;
|
to->inject_touch_event.position.screen_size = screen_size;
|
||||||
to->inject_touch_event.position.point.x = x;
|
to->inject_touch_event.position.point.x = from->x;
|
||||||
to->inject_touch_event.position.point.y = y;
|
to->inject_touch_event.position.point.y = from->y;
|
||||||
to->inject_touch_event.pressure = 1.f;
|
to->inject_touch_event.pressure = 1.f;
|
||||||
to->inject_touch_event.buttons = convert_mouse_buttons(from->state);
|
to->inject_touch_event.buttons = convert_mouse_buttons(from->state);
|
||||||
|
|
||||||
@@ -260,7 +245,6 @@ convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct position position,
|
|||||||
struct control_msg *to) {
|
struct control_msg *to) {
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
|
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
|
||||||
|
|
||||||
// TODO map coords
|
|
||||||
to->inject_scroll_event.position = position;
|
to->inject_scroll_event.position = position;
|
||||||
|
|
||||||
int mul = from->direction == SDL_MOUSEWHEEL_NORMAL ? 1 : -1;
|
int mul = from->direction == SDL_MOUSEWHEEL_NORMAL ? 1 : -1;
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ bool
|
|||||||
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to);
|
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
convert_mouse_button(const SDL_MouseButtonEvent *from, const SDL_Rect *rect,
|
convert_mouse_button(const SDL_MouseButtonEvent *from, struct size screen_size,
|
||||||
struct size frame_size, struct control_msg *to);
|
struct control_msg *to);
|
||||||
|
|
||||||
// the video size may be different from the real device size, so we need the
|
// the video size may be different from the real device size, so we need the
|
||||||
// size to which the absolute position apply, to scale it accordingly
|
// size to which the absolute position apply, to scale it accordingly
|
||||||
bool
|
bool
|
||||||
convert_mouse_motion(const SDL_MouseMotionEvent *from, const SDL_Rect *rect,
|
convert_mouse_motion(const SDL_MouseMotionEvent *from, struct size screen_size,
|
||||||
struct size frame_size, struct control_msg *to);
|
struct control_msg *to);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
convert_touch(const SDL_TouchFingerEvent *from, struct size screen_size,
|
convert_touch(const SDL_TouchFingerEvent *from, struct size screen_size,
|
||||||
|
|||||||
@@ -7,6 +7,19 @@
|
|||||||
#include "lock_util.h"
|
#include "lock_util.h"
|
||||||
#include "log.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
|
// Convert window coordinates (as provided by SDL_GetMouseState() to renderer
|
||||||
// coordinates (as provided in SDL mouse events)
|
// coordinates (as provided in SDL mouse events)
|
||||||
//
|
//
|
||||||
@@ -211,6 +224,26 @@ 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
|
void
|
||||||
input_manager_process_text_input(struct input_manager *input_manager,
|
input_manager_process_text_input(struct input_manager *input_manager,
|
||||||
const SDL_TextInputEvent *event) {
|
const SDL_TextInputEvent *event) {
|
||||||
@@ -244,6 +277,14 @@ input_manager_process_key(struct input_manager *input_manager,
|
|||||||
bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
|
bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
|
||||||
bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI);
|
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
|
// use Cmd on macOS, Ctrl on other platforms
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
bool cmd = !ctrl && meta;
|
bool cmd = !ctrl && meta;
|
||||||
@@ -394,7 +435,7 @@ input_manager_process_mouse_motion(struct input_manager *input_manager,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
if (convert_mouse_motion(event, &input_manager->screen->rect, input_manager->screen->frame_size, &msg)) {
|
if (convert_mouse_motion(event, input_manager->screen->frame_size, &msg)) {
|
||||||
if (!controller_push_msg(input_manager->controller, &msg)) {
|
if (!controller_push_msg(input_manager->controller, &msg)) {
|
||||||
LOGW("Could not request 'inject mouse motion event'");
|
LOGW("Could not request 'inject mouse motion event'");
|
||||||
}
|
}
|
||||||
@@ -427,6 +468,7 @@ input_manager_process_mouse_button(struct input_manager *input_manager,
|
|||||||
// simulated from touch events, so it's a duplicate
|
// simulated from touch events, so it's a duplicate
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->type == SDL_MOUSEBUTTONDOWN) {
|
if (event->type == SDL_MOUSEBUTTONDOWN) {
|
||||||
if (control && event->button == SDL_BUTTON_RIGHT) {
|
if (control && event->button == SDL_BUTTON_RIGHT) {
|
||||||
press_back_or_turn_screen_on(input_manager->controller);
|
press_back_or_turn_screen_on(input_manager->controller);
|
||||||
@@ -437,7 +479,8 @@ input_manager_process_mouse_button(struct input_manager *input_manager,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// double-click on black borders resize to fit the device screen
|
// double-click on black borders resize to fit the device screen
|
||||||
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
|
if (event->button == SDL_BUTTON_LEFT) {
|
||||||
|
if (event->clicks >= 2) {
|
||||||
bool outside =
|
bool outside =
|
||||||
is_outside_device_screen(input_manager, event->x, event->y);
|
is_outside_device_screen(input_manager, event->x, event->y);
|
||||||
if (outside) {
|
if (outside) {
|
||||||
@@ -445,6 +488,17 @@ input_manager_process_mouse_button(struct input_manager *input_manager,
|
|||||||
return;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
// otherwise, send the click event to the device
|
// otherwise, send the click event to the device
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,7 +507,7 @@ input_manager_process_mouse_button(struct input_manager *input_manager,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
if (convert_mouse_button(event, &input_manager->screen->rect, input_manager->screen->frame_size, &msg)) {
|
if (convert_mouse_button(event, input_manager->screen->frame_size, &msg)) {
|
||||||
if (!controller_push_msg(input_manager->controller, &msg)) {
|
if (!controller_push_msg(input_manager->controller, &msg)) {
|
||||||
LOGW("Could not request 'inject mouse button event'");
|
LOGW("Could not request 'inject mouse button event'");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,21 @@ struct input_manager {
|
|||||||
struct controller *controller;
|
struct controller *controller;
|
||||||
struct video_buffer *video_buffer;
|
struct video_buffer *video_buffer;
|
||||||
struct screen *screen;
|
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
|
void
|
||||||
input_manager_process_text_input(struct input_manager *input_manager,
|
input_manager_process_text_input(struct input_manager *input_manager,
|
||||||
const SDL_TextInputEvent *event);
|
const SDL_TextInputEvent *event);
|
||||||
|
|||||||
@@ -37,12 +37,7 @@ static struct decoder decoder;
|
|||||||
static struct recorder recorder;
|
static struct recorder recorder;
|
||||||
static struct controller controller;
|
static struct controller controller;
|
||||||
static struct file_handler file_handler;
|
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
|
// init SDL and set appropriate hints
|
||||||
static bool
|
static bool
|
||||||
@@ -145,10 +140,8 @@ handle_event(SDL_Event *event, bool control) {
|
|||||||
case SDL_WINDOWEVENT:
|
case SDL_WINDOWEVENT:
|
||||||
switch (event->window.event) {
|
switch (event->window.event) {
|
||||||
case SDL_WINDOWEVENT_EXPOSED:
|
case SDL_WINDOWEVENT_EXPOSED:
|
||||||
screen_render(&screen);
|
|
||||||
break;
|
|
||||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||||
screen_resized(&screen);
|
screen_render(&screen);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -313,6 +306,8 @@ scrcpy(const struct scrcpy_options *options) {
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input_manager_init(&input_manager, &controller, &video_buffer, &screen);
|
||||||
|
|
||||||
if (!server_connect_to(&server)) {
|
if (!server_connect_to(&server)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,32 +122,6 @@ get_initial_optimal_size(struct size frame_size) {
|
|||||||
return get_optimal_size(frame_size, frame_size);
|
return get_optimal_size(frame_size, frame_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
update_frame_rect(struct screen *screen) {
|
|
||||||
struct size window_size = get_window_size(screen);
|
|
||||||
|
|
||||||
// 32 bits because we need to multiply two 16 bits values
|
|
||||||
uint32_t ww = window_size.width;
|
|
||||||
uint32_t wh = window_size.height;
|
|
||||||
uint32_t fw = screen->frame_size.width;
|
|
||||||
uint32_t fh = screen->frame_size.height;
|
|
||||||
|
|
||||||
SDL_Rect *rect = &screen->rect;
|
|
||||||
|
|
||||||
bool keep_width = fw * wh > fh * ww;
|
|
||||||
if (keep_width) {
|
|
||||||
rect->x = 0;
|
|
||||||
rect->w = ww;
|
|
||||||
rect->h = ww * fh / fw;
|
|
||||||
rect->y = (wh - rect->h) / 2;
|
|
||||||
} else {
|
|
||||||
rect->y = 0;
|
|
||||||
rect->h = wh;
|
|
||||||
rect->w = wh * fw / fh;
|
|
||||||
rect->x = (ww - rect->w) / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_init(struct screen *screen) {
|
screen_init(struct screen *screen) {
|
||||||
*screen = (struct screen) SCREEN_INITIALIZER;
|
*screen = (struct screen) SCREEN_INITIALIZER;
|
||||||
@@ -196,6 +170,13 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (SDL_RenderSetLogicalSize(screen->renderer, frame_size.width,
|
||||||
|
frame_size.height)) {
|
||||||
|
LOGE("Could not set renderer logical size: %s", SDL_GetError());
|
||||||
|
screen_destroy(screen);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
SDL_Surface *icon = read_xpm(icon_xpm);
|
SDL_Surface *icon = read_xpm(icon_xpm);
|
||||||
if (icon) {
|
if (icon) {
|
||||||
SDL_SetWindowIcon(screen->window, icon);
|
SDL_SetWindowIcon(screen->window, icon);
|
||||||
@@ -239,6 +220,12 @@ static bool
|
|||||||
prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
||||||
if (screen->frame_size.width != new_frame_size.width
|
if (screen->frame_size.width != new_frame_size.width
|
||||||
|| screen->frame_size.height != new_frame_size.height) {
|
|| screen->frame_size.height != new_frame_size.height) {
|
||||||
|
if (SDL_RenderSetLogicalSize(screen->renderer, new_frame_size.width,
|
||||||
|
new_frame_size.height)) {
|
||||||
|
LOGE("Could not set renderer logical size: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// frame dimension changed, destroy texture
|
// frame dimension changed, destroy texture
|
||||||
SDL_DestroyTexture(screen->texture);
|
SDL_DestroyTexture(screen->texture);
|
||||||
|
|
||||||
@@ -284,7 +271,6 @@ screen_update_frame(struct screen *screen, struct video_buffer *vb) {
|
|||||||
mutex_unlock(vb->mutex);
|
mutex_unlock(vb->mutex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
update_frame_rect(screen);
|
|
||||||
update_texture(screen, frame);
|
update_texture(screen, frame);
|
||||||
mutex_unlock(vb->mutex);
|
mutex_unlock(vb->mutex);
|
||||||
|
|
||||||
@@ -292,16 +278,10 @@ screen_update_frame(struct screen *screen, struct video_buffer *vb) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
screen_resized(struct screen *screen) {
|
|
||||||
update_frame_rect(screen);
|
|
||||||
screen_render(screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_render(struct screen *screen) {
|
screen_render(struct screen *screen) {
|
||||||
SDL_RenderClear(screen->renderer);
|
SDL_RenderClear(screen->renderer);
|
||||||
SDL_RenderCopy(screen->renderer, screen->texture, NULL, &screen->rect);
|
SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL);
|
||||||
SDL_RenderPresent(screen->renderer);
|
SDL_RenderPresent(screen->renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ struct screen {
|
|||||||
struct size frame_size;
|
struct size frame_size;
|
||||||
//used only in fullscreen mode to know the windowed window size
|
//used only in fullscreen mode to know the windowed window size
|
||||||
struct size windowed_window_size;
|
struct size windowed_window_size;
|
||||||
struct SDL_Rect rect; // frame location and size inside the window
|
|
||||||
bool has_frame;
|
bool has_frame;
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
bool no_window;
|
bool no_window;
|
||||||
@@ -35,12 +34,6 @@ struct screen {
|
|||||||
.width = 0, \
|
.width = 0, \
|
||||||
.height = 0, \
|
.height = 0, \
|
||||||
}, \
|
}, \
|
||||||
.rect = { \
|
|
||||||
.x = 0, \
|
|
||||||
.y = 0, \
|
|
||||||
.w = 0, \
|
|
||||||
.h = 0, \
|
|
||||||
}, \
|
|
||||||
.has_frame = false, \
|
.has_frame = false, \
|
||||||
.fullscreen = false, \
|
.fullscreen = false, \
|
||||||
.no_window = false, \
|
.no_window = false, \
|
||||||
@@ -67,9 +60,6 @@ screen_destroy(struct screen *screen);
|
|||||||
bool
|
bool
|
||||||
screen_update_frame(struct screen *screen, struct video_buffer *vb);
|
screen_update_frame(struct screen *screen, struct video_buffer *vb);
|
||||||
|
|
||||||
void
|
|
||||||
screen_resized(struct screen *screen);
|
|
||||||
|
|
||||||
// render the texture to the renderer
|
// render the texture to the renderer
|
||||||
void
|
void
|
||||||
screen_render(struct screen *screen);
|
screen_render(struct screen *screen);
|
||||||
|
|||||||
@@ -161,11 +161,7 @@ public final class Device {
|
|||||||
* @param mode one of the {@code SCREEN_POWER_MODE_*} constants
|
* @param mode one of the {@code SCREEN_POWER_MODE_*} constants
|
||||||
*/
|
*/
|
||||||
public void setScreenPowerMode(int mode) {
|
public void setScreenPowerMode(int mode) {
|
||||||
IBinder d = SurfaceControl.getBuiltInDisplay();
|
IBinder d = SurfaceControl.getBuiltInDisplay(0);
|
||||||
if (d == null) {
|
|
||||||
Ln.e("Could not get built-in display");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SurfaceControl.setDisplayPowerMode(d, mode);
|
SurfaceControl.setDisplayPowerMode(d, mode);
|
||||||
Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on"));
|
Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +1,44 @@
|
|||||||
package com.genymobile.scrcpy.wrappers;
|
package com.genymobile.scrcpy.wrappers;
|
||||||
|
|
||||||
import com.genymobile.scrcpy.Ln;
|
|
||||||
|
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.IInterface;
|
import android.os.IInterface;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public class ClipboardManager {
|
public class ClipboardManager {
|
||||||
|
|
||||||
private static final String PACKAGE_NAME = "com.android.shell";
|
|
||||||
private static final int USER_ID = 0;
|
|
||||||
|
|
||||||
private final IInterface manager;
|
private final IInterface manager;
|
||||||
private Method getPrimaryClipMethod;
|
private final Method getPrimaryClipMethod;
|
||||||
private Method setPrimaryClipMethod;
|
private final Method setPrimaryClipMethod;
|
||||||
|
|
||||||
public ClipboardManager(IInterface manager) {
|
public ClipboardManager(IInterface manager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
|
||||||
|
|
||||||
private Method getGetPrimaryClipMethod() {
|
|
||||||
if (getPrimaryClipMethod == null) {
|
|
||||||
try {
|
try {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
|
||||||
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class);
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class);
|
||||||
} else {
|
|
||||||
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class);
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return getPrimaryClipMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Method getSetPrimaryClipMethod() {
|
|
||||||
if (setPrimaryClipMethod == null) {
|
|
||||||
try {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
|
||||||
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class);
|
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class);
|
||||||
} else {
|
|
||||||
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class,
|
|
||||||
String.class, int.class);
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
Ln.e("Could not find method", e);
|
throw new AssertionError(e);
|
||||||
}
|
|
||||||
}
|
|
||||||
return setPrimaryClipMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ClipData getPrimaryClip(Method method, IInterface manager) throws InvocationTargetException,
|
|
||||||
IllegalAccessException {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
|
||||||
return (ClipData) method.invoke(manager, PACKAGE_NAME);
|
|
||||||
}
|
|
||||||
return (ClipData) method.invoke(manager, PACKAGE_NAME, USER_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void setPrimaryClip(Method method, IInterface manager, ClipData clipData) throws InvocationTargetException,
|
|
||||||
IllegalAccessException {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
|
||||||
method.invoke(manager, clipData, PACKAGE_NAME);
|
|
||||||
} else {
|
|
||||||
method.invoke(manager, clipData, PACKAGE_NAME, USER_ID);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CharSequence getText() {
|
public CharSequence getText() {
|
||||||
Method method = getGetPrimaryClipMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
ClipData clipData = getPrimaryClip(method, manager);
|
ClipData clipData = (ClipData) getPrimaryClipMethod.invoke(manager, "com.android.shell");
|
||||||
if (clipData == null || clipData.getItemCount() == 0) {
|
if (clipData == null || clipData.getItemCount() == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return clipData.getItemAt(0).getText();
|
return clipData.getItemAt(0).getText();
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
throw new AssertionError(e);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setText(CharSequence text) {
|
public void setText(CharSequence text) {
|
||||||
Method method = getSetPrimaryClipMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ClipData clipData = ClipData.newPlainText(null, text);
|
ClipData clipData = ClipData.newPlainText(null, text);
|
||||||
try {
|
try {
|
||||||
setPrimaryClip(method, manager, clipData);
|
setPrimaryClipMethod.invoke(manager, clipData, "com.android.shell");
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package com.genymobile.scrcpy.wrappers;
|
package com.genymobile.scrcpy.wrappers;
|
||||||
|
|
||||||
import com.genymobile.scrcpy.Ln;
|
|
||||||
|
|
||||||
import android.os.IInterface;
|
import android.os.IInterface;
|
||||||
import android.view.InputEvent;
|
import android.view.InputEvent;
|
||||||
|
|
||||||
@@ -15,33 +13,22 @@ public final class InputManager {
|
|||||||
public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2;
|
public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2;
|
||||||
|
|
||||||
private final IInterface manager;
|
private final IInterface manager;
|
||||||
private Method injectInputEventMethod;
|
private final Method injectInputEventMethod;
|
||||||
|
|
||||||
public InputManager(IInterface manager) {
|
public InputManager(IInterface manager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
|
||||||
|
|
||||||
private Method getInjectInputEventMethod() {
|
|
||||||
if (injectInputEventMethod == null) {
|
|
||||||
try {
|
try {
|
||||||
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class);
|
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
Ln.e("Could not find method", e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return injectInputEventMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
|
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
|
||||||
Method method = getInjectInputEventMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
return (Boolean) method.invoke(manager, inputEvent, mode);
|
return (Boolean) injectInputEventMethod.invoke(manager, inputEvent, mode);
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
throw new AssertionError(e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package com.genymobile.scrcpy.wrappers;
|
package com.genymobile.scrcpy.wrappers;
|
||||||
|
|
||||||
import com.genymobile.scrcpy.Ln;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IInterface;
|
import android.os.IInterface;
|
||||||
@@ -11,35 +9,24 @@ import java.lang.reflect.Method;
|
|||||||
|
|
||||||
public final class PowerManager {
|
public final class PowerManager {
|
||||||
private final IInterface manager;
|
private final IInterface manager;
|
||||||
private Method isScreenOnMethod;
|
private final Method isScreenOnMethod;
|
||||||
|
|
||||||
public PowerManager(IInterface manager) {
|
public PowerManager(IInterface manager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
|
||||||
|
|
||||||
private Method getIsScreenOnMethod() {
|
|
||||||
if (isScreenOnMethod == null) {
|
|
||||||
try {
|
try {
|
||||||
@SuppressLint("ObsoleteSdkInt") // we may lower minSdkVersion in the future
|
@SuppressLint("ObsoleteSdkInt") // we may lower minSdkVersion in the future
|
||||||
String methodName = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH ? "isInteractive" : "isScreenOn";
|
String methodName = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH ? "isInteractive" : "isScreenOn";
|
||||||
isScreenOnMethod = manager.getClass().getMethod(methodName);
|
isScreenOnMethod = manager.getClass().getMethod(methodName);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
Ln.e("Could not find method", e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return isScreenOnMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isScreenOn() {
|
public boolean isScreenOn() {
|
||||||
Method method = getIsScreenOnMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
return (Boolean) method.invoke(manager);
|
return (Boolean) isScreenOnMethod.invoke(manager);
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
throw new AssertionError(e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,49 +17,35 @@ public class StatusBarManager {
|
|||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method getExpandNotificationsPanelMethod() {
|
public void expandNotificationsPanel() {
|
||||||
if (expandNotificationsPanelMethod == null) {
|
if (expandNotificationsPanelMethod == null) {
|
||||||
try {
|
try {
|
||||||
expandNotificationsPanelMethod = manager.getClass().getMethod("expandNotificationsPanel");
|
expandNotificationsPanelMethod = manager.getClass().getMethod("expandNotificationsPanel");
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
Ln.e("Could not find method", e);
|
Ln.e("ServiceBarManager.expandNotificationsPanel() is not available on this device");
|
||||||
}
|
|
||||||
}
|
|
||||||
return expandNotificationsPanelMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Method getCollapsePanelsMethod() {
|
|
||||||
if (collapsePanelsMethod == null) {
|
|
||||||
try {
|
|
||||||
collapsePanelsMethod = manager.getClass().getMethod("collapsePanels");
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return collapsePanelsMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void expandNotificationsPanel() {
|
|
||||||
Method method = getExpandNotificationsPanelMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
method.invoke(manager);
|
expandNotificationsPanelMethod.invoke(manager);
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
Ln.e("Could not invoke ServiceBarManager.expandNotificationsPanel()", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void collapsePanels() {
|
public void collapsePanels() {
|
||||||
Method method = getCollapsePanelsMethod();
|
if (collapsePanelsMethod == null) {
|
||||||
if (method == null) {
|
try {
|
||||||
|
collapsePanelsMethod = manager.getClass().getMethod("collapsePanels");
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
Ln.e("ServiceBarManager.collapsePanels() is not available on this device");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
method.invoke(manager);
|
collapsePanelsMethod.invoke(manager);
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
Ln.e("Could not invoke ServiceBarManager.collapsePanels()", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
package com.genymobile.scrcpy.wrappers;
|
package com.genymobile.scrcpy.wrappers;
|
||||||
|
|
||||||
import com.genymobile.scrcpy.Ln;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
@SuppressLint("PrivateApi")
|
@SuppressLint("PrivateApi")
|
||||||
public final class SurfaceControl {
|
public final class SurfaceControl {
|
||||||
|
|
||||||
@@ -28,9 +23,6 @@ public final class SurfaceControl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Method getBuiltInDisplayMethod;
|
|
||||||
private static Method setDisplayPowerModeMethod;
|
|
||||||
|
|
||||||
private SurfaceControl() {
|
private SurfaceControl() {
|
||||||
// only static methods
|
// only static methods
|
||||||
}
|
}
|
||||||
@@ -84,62 +76,24 @@ public final class SurfaceControl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Method getGetBuiltInDisplayMethod() {
|
public static IBinder getBuiltInDisplay(int builtInDisplayId) {
|
||||||
if (getBuiltInDisplayMethod == null) {
|
|
||||||
try {
|
try {
|
||||||
// the method signature has changed in Android Q
|
// the method signature has changed in Android Q
|
||||||
// <https://github.com/Genymobile/scrcpy/issues/586>
|
// <https://github.com/Genymobile/scrcpy/issues/586>
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
getBuiltInDisplayMethod = CLASS.getMethod("getBuiltInDisplay", int.class);
|
return (IBinder) CLASS.getMethod("getBuiltInDisplay", int.class).invoke(null, builtInDisplayId);
|
||||||
} else {
|
|
||||||
getBuiltInDisplayMethod = CLASS.getMethod("getInternalDisplayToken");
|
|
||||||
}
|
}
|
||||||
} catch (NoSuchMethodException e) {
|
return (IBinder) CLASS.getMethod("getPhysicalDisplayToken", long.class).invoke(null, builtInDisplayId);
|
||||||
Ln.e("Could not find method", e);
|
} catch (Exception e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getBuiltInDisplayMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IBinder getBuiltInDisplay() {
|
|
||||||
Method method = getGetBuiltInDisplayMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
|
||||||
// call getBuiltInDisplay(0)
|
|
||||||
return (IBinder) method.invoke(null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// call getInternalDisplayToken()
|
|
||||||
return (IBinder) method.invoke(null);
|
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Method getSetDisplayPowerModeMethod() {
|
|
||||||
if (setDisplayPowerModeMethod == null) {
|
|
||||||
try {
|
|
||||||
setDisplayPowerModeMethod = CLASS.getMethod("setDisplayPowerMode", IBinder.class, int.class);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return setDisplayPowerModeMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setDisplayPowerMode(IBinder displayToken, int mode) {
|
public static void setDisplayPowerMode(IBinder displayToken, int mode) {
|
||||||
Method method = getSetDisplayPowerModeMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
method.invoke(null, displayToken, mode);
|
CLASS.getMethod("setDisplayPowerMode", IBinder.class, int.class).invoke(null, displayToken, mode);
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (Exception e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user