Compare commits
1 Commits
logical_si
...
locked
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d40a7ffde |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,4 +2,3 @@ build/
|
|||||||
/dist/
|
/dist/
|
||||||
.idea/
|
.idea/
|
||||||
.gradle/
|
.gradle/
|
||||||
/x/
|
|
||||||
|
|||||||
@@ -282,15 +282,6 @@ meson x -Dserver_debugger=true
|
|||||||
meson configure x -Dserver_debugger=true
|
meson configure x -Dserver_debugger=true
|
||||||
```
|
```
|
||||||
|
|
||||||
If your device runs Android 8 or below, set the `server_debugger_method` to
|
|
||||||
`old` in addition:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
meson x -Dserver_debugger=true -Dserver_debugger_method=old
|
|
||||||
# or, if x is already configured
|
|
||||||
meson configure x -Dserver_debugger=true -Dserver_debugger_method=old
|
|
||||||
```
|
|
||||||
|
|
||||||
Then recompile.
|
Then recompile.
|
||||||
|
|
||||||
When you start scrcpy, it will start a debugger on port 5005 on the device.
|
When you start scrcpy, it will start a debugger on port 5005 on the device.
|
||||||
|
|||||||
25
README.md
25
README.md
@@ -261,16 +261,6 @@ scrcpy -s 192.168.0.1:5555 # short version
|
|||||||
|
|
||||||
You can start several instances of _scrcpy_ for several devices.
|
You can start several instances of _scrcpy_ for several devices.
|
||||||
|
|
||||||
#### Autostart on device connection
|
|
||||||
|
|
||||||
You could use [AutoAdb]:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
autoadb scrcpy -s '{}'
|
|
||||||
```
|
|
||||||
|
|
||||||
[AutoAdb]: https://github.com/rom1v/usbaudio
|
|
||||||
|
|
||||||
#### SSH tunnel
|
#### SSH tunnel
|
||||||
|
|
||||||
To connect to a remote device, it is possible to connect a local `adb` client to
|
To connect to a remote device, it is possible to connect a local `adb` client to
|
||||||
@@ -353,21 +343,6 @@ scrcpy --no-control
|
|||||||
scrcpy -n
|
scrcpy -n
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Display
|
|
||||||
|
|
||||||
If several displays are available, it is possible to select the display to
|
|
||||||
mirror:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
scrcpy --display 1
|
|
||||||
```
|
|
||||||
|
|
||||||
The list of display ids can be retrieved by:
|
|
||||||
|
|
||||||
```
|
|
||||||
adb shell dumpsys display # search "mDisplayId=" in the output
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Turn screen off
|
#### Turn screen off
|
||||||
|
|
||||||
It is possible to turn the device screen off while mirroring on start with a
|
It is possible to turn the device screen off while mirroring on start with a
|
||||||
|
|||||||
@@ -124,9 +124,6 @@ conf.set('WINDOWS_NOCONSOLE', get_option('windows_noconsole'))
|
|||||||
# run a server debugger and wait for a client to be attached
|
# run a server debugger and wait for a client to be attached
|
||||||
conf.set('SERVER_DEBUGGER', get_option('server_debugger'))
|
conf.set('SERVER_DEBUGGER', get_option('server_debugger'))
|
||||||
|
|
||||||
# select the debugger method ('old' for Android < 9, 'new' for Android >= 9)
|
|
||||||
conf.set('SERVER_DEBUGGER_METHOD_NEW', get_option('server_debugger_method') == 'new')
|
|
||||||
|
|
||||||
configure_file(configuration: conf, output: 'config.h')
|
configure_file(configuration: conf, output: 'config.h')
|
||||||
|
|
||||||
src_dir = include_directories('src')
|
src_dir = include_directories('src')
|
||||||
|
|||||||
13
app/scrcpy.1
13
app/scrcpy.1
@@ -33,15 +33,6 @@ The values are expressed in the device natural orientation (typically, portrait
|
|||||||
.B \-\-max\-size
|
.B \-\-max\-size
|
||||||
value is computed on the cropped size.
|
value is computed on the cropped size.
|
||||||
|
|
||||||
.TP
|
|
||||||
.BI "\-\-display " id
|
|
||||||
Specify the display id to mirror.
|
|
||||||
|
|
||||||
The list of possible display ids can be listed by "adb shell dumpsys display"
|
|
||||||
(search "mDisplayId=" in the output).
|
|
||||||
|
|
||||||
Default is 0.
|
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-f, \-\-fullscreen
|
.B \-f, \-\-fullscreen
|
||||||
Start in fullscreen.
|
Start in fullscreen.
|
||||||
@@ -140,13 +131,13 @@ Set a custom window title.
|
|||||||
.BI "\-\-window\-x " value
|
.BI "\-\-window\-x " value
|
||||||
Set the initial window horizontal position.
|
Set the initial window horizontal position.
|
||||||
|
|
||||||
Default is "auto".\n
|
Default is -1 (automatic).\n
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-window\-y " value
|
.BI "\-\-window\-y " value
|
||||||
Set the initial window vertical position.
|
Set the initial window vertical position.
|
||||||
|
|
||||||
Default is "auto".\n
|
Default is -1 (automatic).\n
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-window\-width " value
|
.BI "\-\-window\-width " value
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -36,15 +35,6 @@ scrcpy_print_usage(const char *arg0) {
|
|||||||
" (typically, portrait for a phone, landscape for a tablet).\n"
|
" (typically, portrait for a phone, landscape for a tablet).\n"
|
||||||
" Any --max-size value is computed on the cropped size.\n"
|
" Any --max-size value is computed on the cropped size.\n"
|
||||||
"\n"
|
"\n"
|
||||||
" --display id\n"
|
|
||||||
" Specify the display id to mirror.\n"
|
|
||||||
"\n"
|
|
||||||
" The list of possible display ids can be listed by:\n"
|
|
||||||
" adb shell dumpsys display\n"
|
|
||||||
" (search \"mDisplayId=\" in the output)\n"
|
|
||||||
"\n"
|
|
||||||
" Default is 0.\n"
|
|
||||||
"\n"
|
|
||||||
" -f, --fullscreen\n"
|
" -f, --fullscreen\n"
|
||||||
" Start in fullscreen.\n"
|
" Start in fullscreen.\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -126,11 +116,11 @@ scrcpy_print_usage(const char *arg0) {
|
|||||||
"\n"
|
"\n"
|
||||||
" --window-x value\n"
|
" --window-x value\n"
|
||||||
" Set the initial window horizontal position.\n"
|
" Set the initial window horizontal position.\n"
|
||||||
" Default is \"auto\".\n"
|
" Default is -1 (automatic).\n"
|
||||||
"\n"
|
"\n"
|
||||||
" --window-y value\n"
|
" --window-y value\n"
|
||||||
" Set the initial window vertical position.\n"
|
" Set the initial window vertical position.\n"
|
||||||
" Default is \"auto\".\n"
|
" Default is -1 (automatic).\n"
|
||||||
"\n"
|
"\n"
|
||||||
" --window-width value\n"
|
" --window-width value\n"
|
||||||
" Set the initial window width.\n"
|
" Set the initial window width.\n"
|
||||||
@@ -312,16 +302,8 @@ parse_lock_video_orientation(const char *s, int8_t *lock_video_orientation) {
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_window_position(const char *s, int16_t *position) {
|
parse_window_position(const char *s, int16_t *position) {
|
||||||
// special value for "auto"
|
|
||||||
static_assert(WINDOW_POSITION_UNDEFINED == -0x8000, "unexpected value");
|
|
||||||
|
|
||||||
if (!strcmp(s, "auto")) {
|
|
||||||
*position = WINDOW_POSITION_UNDEFINED;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
long value;
|
long value;
|
||||||
bool ok = parse_integer_arg(s, &value, false, -0x7FFF, 0x7FFF,
|
bool ok = parse_integer_arg(s, &value, false, -1, 0x7FFF,
|
||||||
"window position");
|
"window position");
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return false;
|
return false;
|
||||||
@@ -372,18 +354,6 @@ parse_port_range(const char *s, struct port_range *port_range) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
parse_display_id(const char *s, uint16_t *display_id) {
|
|
||||||
long value;
|
|
||||||
bool ok = parse_integer_arg(s, &value, false, 0, 0xFFFF, "display id");
|
|
||||||
if (!ok) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*display_id = (uint16_t) value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_record_format(const char *optarg, enum recorder_format *format) {
|
parse_record_format(const char *optarg, enum recorder_format *format) {
|
||||||
if (!strcmp(optarg, "mp4")) {
|
if (!strcmp(optarg, "mp4")) {
|
||||||
@@ -428,7 +398,6 @@ guess_record_format(const char *filename) {
|
|||||||
#define OPT_WINDOW_BORDERLESS 1011
|
#define OPT_WINDOW_BORDERLESS 1011
|
||||||
#define OPT_MAX_FPS 1012
|
#define OPT_MAX_FPS 1012
|
||||||
#define OPT_LOCK_VIDEO_ORIENTATION 1013
|
#define OPT_LOCK_VIDEO_ORIENTATION 1013
|
||||||
#define OPT_DISPLAY_ID 1014
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||||
@@ -436,7 +405,6 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
|||||||
{"always-on-top", no_argument, NULL, OPT_ALWAYS_ON_TOP},
|
{"always-on-top", no_argument, NULL, OPT_ALWAYS_ON_TOP},
|
||||||
{"bit-rate", required_argument, NULL, 'b'},
|
{"bit-rate", required_argument, NULL, 'b'},
|
||||||
{"crop", required_argument, NULL, OPT_CROP},
|
{"crop", required_argument, NULL, OPT_CROP},
|
||||||
{"display", required_argument, NULL, OPT_DISPLAY_ID},
|
|
||||||
{"fullscreen", no_argument, NULL, 'f'},
|
{"fullscreen", no_argument, NULL, 'f'},
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
{"lock-video-orientation", required_argument, NULL,
|
{"lock-video-orientation", required_argument, NULL,
|
||||||
@@ -485,11 +453,6 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
|||||||
case OPT_CROP:
|
case OPT_CROP:
|
||||||
opts->crop = optarg;
|
opts->crop = optarg;
|
||||||
break;
|
break;
|
||||||
case OPT_DISPLAY_ID:
|
|
||||||
if (!parse_display_id(optarg, &opts->display_id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'f':
|
case 'f':
|
||||||
opts->fullscreen = true;
|
opts->fullscreen = true;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -94,23 +94,6 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
|
|||||||
MAP(SDLK_UP, AKEYCODE_DPAD_UP);
|
MAP(SDLK_UP, AKEYCODE_DPAD_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(mod & (KMOD_NUM | KMOD_SHIFT))) {
|
|
||||||
// Handle Numpad events when Num Lock is disabled
|
|
||||||
// If SHIFT is pressed, a text event will be sent instead
|
|
||||||
switch(from) {
|
|
||||||
MAP(SDLK_KP_0, AKEYCODE_INSERT);
|
|
||||||
MAP(SDLK_KP_1, AKEYCODE_MOVE_END);
|
|
||||||
MAP(SDLK_KP_2, AKEYCODE_DPAD_DOWN);
|
|
||||||
MAP(SDLK_KP_3, AKEYCODE_PAGE_DOWN);
|
|
||||||
MAP(SDLK_KP_4, AKEYCODE_DPAD_LEFT);
|
|
||||||
MAP(SDLK_KP_6, AKEYCODE_DPAD_RIGHT);
|
|
||||||
MAP(SDLK_KP_7, AKEYCODE_MOVE_HOME);
|
|
||||||
MAP(SDLK_KP_8, AKEYCODE_DPAD_UP);
|
|
||||||
MAP(SDLK_KP_9, AKEYCODE_PAGE_UP);
|
|
||||||
MAP(SDLK_KP_PERIOD, AKEYCODE_FORWARD_DEL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prefer_text) {
|
if (prefer_text) {
|
||||||
// do not forward alpha and space key events
|
// do not forward alpha and space key events
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ fps_counter_init(struct fps_counter *counter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
counter->thread = NULL;
|
counter->thread = NULL;
|
||||||
atomic_init(&counter->started, 0);
|
SDL_AtomicSet(&counter->started, 0);
|
||||||
// no need to initialize the other fields, they are unused until started
|
// no need to initialize the other fields, they are unused until started
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -35,16 +35,6 @@ fps_counter_destroy(struct fps_counter *counter) {
|
|||||||
SDL_DestroyMutex(counter->mutex);
|
SDL_DestroyMutex(counter->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
|
||||||
is_started(struct fps_counter *counter) {
|
|
||||||
return atomic_load_explicit(&counter->started, memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
set_started(struct fps_counter *counter, bool started) {
|
|
||||||
atomic_store_explicit(&counter->started, started, memory_order_release);
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be called with mutex locked
|
// must be called with mutex locked
|
||||||
static void
|
static void
|
||||||
display_fps(struct fps_counter *counter) {
|
display_fps(struct fps_counter *counter) {
|
||||||
@@ -80,10 +70,10 @@ run_fps_counter(void *data) {
|
|||||||
|
|
||||||
mutex_lock(counter->mutex);
|
mutex_lock(counter->mutex);
|
||||||
while (!counter->interrupted) {
|
while (!counter->interrupted) {
|
||||||
while (!counter->interrupted && !is_started(counter)) {
|
while (!counter->interrupted && !SDL_AtomicGet(&counter->started)) {
|
||||||
cond_wait(counter->state_cond, counter->mutex);
|
cond_wait(counter->state_cond, counter->mutex);
|
||||||
}
|
}
|
||||||
while (!counter->interrupted && is_started(counter)) {
|
while (!counter->interrupted && SDL_AtomicGet(&counter->started)) {
|
||||||
uint32_t now = SDL_GetTicks();
|
uint32_t now = SDL_GetTicks();
|
||||||
check_interval_expired(counter, now);
|
check_interval_expired(counter, now);
|
||||||
|
|
||||||
@@ -106,7 +96,7 @@ fps_counter_start(struct fps_counter *counter) {
|
|||||||
counter->nr_skipped = 0;
|
counter->nr_skipped = 0;
|
||||||
mutex_unlock(counter->mutex);
|
mutex_unlock(counter->mutex);
|
||||||
|
|
||||||
set_started(counter, true);
|
SDL_AtomicSet(&counter->started, 1);
|
||||||
cond_signal(counter->state_cond);
|
cond_signal(counter->state_cond);
|
||||||
|
|
||||||
// counter->thread is always accessed from the same thread, no need to lock
|
// counter->thread is always accessed from the same thread, no need to lock
|
||||||
@@ -124,13 +114,13 @@ fps_counter_start(struct fps_counter *counter) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
fps_counter_stop(struct fps_counter *counter) {
|
fps_counter_stop(struct fps_counter *counter) {
|
||||||
set_started(counter, false);
|
SDL_AtomicSet(&counter->started, 0);
|
||||||
cond_signal(counter->state_cond);
|
cond_signal(counter->state_cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
fps_counter_is_started(struct fps_counter *counter) {
|
fps_counter_is_started(struct fps_counter *counter) {
|
||||||
return is_started(counter);
|
return SDL_AtomicGet(&counter->started);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -155,7 +145,7 @@ fps_counter_join(struct fps_counter *counter) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
fps_counter_add_rendered_frame(struct fps_counter *counter) {
|
fps_counter_add_rendered_frame(struct fps_counter *counter) {
|
||||||
if (!is_started(counter)) {
|
if (!SDL_AtomicGet(&counter->started)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +158,7 @@ fps_counter_add_rendered_frame(struct fps_counter *counter) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
fps_counter_add_skipped_frame(struct fps_counter *counter) {
|
fps_counter_add_skipped_frame(struct fps_counter *counter) {
|
||||||
if (!is_started(counter)) {
|
if (!SDL_AtomicGet(&counter->started)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#ifndef FPSCOUNTER_H
|
#ifndef FPSCOUNTER_H
|
||||||
#define FPSCOUNTER_H
|
#define FPSCOUNTER_H
|
||||||
|
|
||||||
#include <stdatomic.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <SDL2/SDL_atomic.h>
|
||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ struct fps_counter {
|
|||||||
|
|
||||||
// atomic so that we can check without locking the mutex
|
// atomic so that we can check without locking the mutex
|
||||||
// if the FPS counter is disabled, we don't want to lock unnecessarily
|
// if the FPS counter is disabled, we don't want to lock unnecessarily
|
||||||
atomic_bool started;
|
SDL_atomic_t started;
|
||||||
|
|
||||||
// the following fields are protected by the mutex
|
// the following fields are protected by the mutex
|
||||||
bool interrupted;
|
bool interrupted;
|
||||||
|
|||||||
@@ -7,6 +7,33 @@
|
|||||||
#include "util/lock.h"
|
#include "util/lock.h"
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
|
// Convert window coordinates (as provided by SDL_GetMouseState() to renderer
|
||||||
|
// coordinates (as provided in SDL mouse events)
|
||||||
|
//
|
||||||
|
// See my question:
|
||||||
|
// <https://stackoverflow.com/questions/49111054/how-to-get-mouse-position-on-mouse-wheel-event>
|
||||||
|
static void
|
||||||
|
convert_to_renderer_coordinates(SDL_Renderer *renderer, int *x, int *y) {
|
||||||
|
SDL_Rect viewport;
|
||||||
|
float scale_x, scale_y;
|
||||||
|
SDL_RenderGetViewport(renderer, &viewport);
|
||||||
|
SDL_RenderGetScale(renderer, &scale_x, &scale_y);
|
||||||
|
*x = (int) (*x / scale_x) - viewport.x;
|
||||||
|
*y = (int) (*y / scale_y) - viewport.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct point
|
||||||
|
get_mouse_point(struct screen *screen) {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
SDL_GetMouseState(&x, &y);
|
||||||
|
convert_to_renderer_coordinates(screen->renderer, &x, &y);
|
||||||
|
return (struct point) {
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static const int ACTION_DOWN = 1;
|
static const int ACTION_DOWN = 1;
|
||||||
static const int ACTION_UP = 1 << 1;
|
static const int ACTION_UP = 1 << 1;
|
||||||
|
|
||||||
@@ -400,8 +427,8 @@ convert_mouse_motion(const SDL_MouseMotionEvent *from, struct screen *screen,
|
|||||||
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 = screen->frame_size;
|
to->inject_touch_event.position.screen_size = screen->frame_size;
|
||||||
to->inject_touch_event.position.point =
|
to->inject_touch_event.position.point.x = from->x;
|
||||||
screen_convert_to_frame_coords(screen, from->x, from->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);
|
||||||
|
|
||||||
@@ -437,14 +464,12 @@ convert_touch(const SDL_TouchFingerEvent *from, struct screen *screen,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct size frame_size = screen->frame_size;
|
struct size frame_size = screen->frame_size;
|
||||||
// SDL touch event coordinates are normalized in the range [0; 1]
|
|
||||||
float x = from->x * frame_size.width;
|
|
||||||
float y = from->y * frame_size.height;
|
|
||||||
|
|
||||||
to->inject_touch_event.pointer_id = from->fingerId;
|
to->inject_touch_event.pointer_id = from->fingerId;
|
||||||
to->inject_touch_event.position.screen_size = frame_size;
|
to->inject_touch_event.position.screen_size = frame_size;
|
||||||
to->inject_touch_event.position.point =
|
// SDL touch event coordinates are normalized in the range [0; 1]
|
||||||
screen_convert_to_frame_coords(screen, x, y);
|
to->inject_touch_event.position.point.x = from->x * frame_size.width;
|
||||||
|
to->inject_touch_event.position.point.y = from->y * frame_size.height;
|
||||||
to->inject_touch_event.pressure = from->pressure;
|
to->inject_touch_event.pressure = from->pressure;
|
||||||
to->inject_touch_event.buttons = 0;
|
to->inject_touch_event.buttons = 0;
|
||||||
return true;
|
return true;
|
||||||
@@ -479,8 +504,8 @@ convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen,
|
|||||||
|
|
||||||
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 = screen->frame_size;
|
to->inject_touch_event.position.screen_size = screen->frame_size;
|
||||||
to->inject_touch_event.position.point =
|
to->inject_touch_event.position.point.x = from->x;
|
||||||
screen_convert_to_frame_coords(screen, from->x, from->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));
|
||||||
@@ -532,15 +557,9 @@ input_manager_process_mouse_button(struct input_manager *im,
|
|||||||
static bool
|
static bool
|
||||||
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen,
|
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen,
|
||||||
struct control_msg *to) {
|
struct control_msg *to) {
|
||||||
|
|
||||||
// mouse_x and mouse_y are expressed in pixels relatice to the window
|
|
||||||
int mouse_x;
|
|
||||||
int mouse_y;
|
|
||||||
SDL_GetMouseState(&mouse_x, &mouse_y);
|
|
||||||
|
|
||||||
struct position position = {
|
struct position position = {
|
||||||
.screen_size = screen->frame_size,
|
.screen_size = screen->frame_size,
|
||||||
.point = screen_convert_to_frame_coords(screen, mouse_x, mouse_y),
|
.point = get_mouse_point(screen),
|
||||||
};
|
};
|
||||||
|
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
|
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
|
||||||
|
|||||||
@@ -286,7 +286,6 @@ scrcpy(const struct scrcpy_options *options) {
|
|||||||
.max_fps = options->max_fps,
|
.max_fps = options->max_fps,
|
||||||
.lock_video_orientation = options->lock_video_orientation,
|
.lock_video_orientation = options->lock_video_orientation,
|
||||||
.control = options->control,
|
.control = options->control,
|
||||||
.display_id = options->display_id,
|
|
||||||
};
|
};
|
||||||
if (!server_start(&server, options->serial, ¶ms)) {
|
if (!server_start(&server, options->serial, ¶ms)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -21,11 +21,10 @@ struct scrcpy_options {
|
|||||||
uint32_t bit_rate;
|
uint32_t bit_rate;
|
||||||
uint16_t max_fps;
|
uint16_t max_fps;
|
||||||
int8_t lock_video_orientation;
|
int8_t lock_video_orientation;
|
||||||
int16_t window_x; // WINDOW_POSITION_UNDEFINED for "auto"
|
int16_t window_x;
|
||||||
int16_t window_y; // WINDOW_POSITION_UNDEFINED for "auto"
|
int16_t window_y;
|
||||||
uint16_t window_width;
|
uint16_t window_width;
|
||||||
uint16_t window_height;
|
uint16_t window_height;
|
||||||
uint16_t display_id;
|
|
||||||
bool show_touches;
|
bool show_touches;
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
bool always_on_top;
|
bool always_on_top;
|
||||||
@@ -52,11 +51,10 @@ struct scrcpy_options {
|
|||||||
.bit_rate = DEFAULT_BIT_RATE, \
|
.bit_rate = DEFAULT_BIT_RATE, \
|
||||||
.max_fps = 0, \
|
.max_fps = 0, \
|
||||||
.lock_video_orientation = DEFAULT_LOCK_VIDEO_ORIENTATION, \
|
.lock_video_orientation = DEFAULT_LOCK_VIDEO_ORIENTATION, \
|
||||||
.window_x = WINDOW_POSITION_UNDEFINED, \
|
.window_x = -1, \
|
||||||
.window_y = WINDOW_POSITION_UNDEFINED, \
|
.window_y = -1, \
|
||||||
.window_width = 0, \
|
.window_width = 0, \
|
||||||
.window_height = 0, \
|
.window_height = 0, \
|
||||||
.display_id = 0, \
|
|
||||||
.show_touches = false, \
|
.show_touches = false, \
|
||||||
.fullscreen = false, \
|
.fullscreen = false, \
|
||||||
.always_on_top = false, \
|
.always_on_top = false, \
|
||||||
|
|||||||
@@ -149,32 +149,6 @@ get_initial_optimal_size(struct size frame_size, uint16_t req_width,
|
|||||||
return window_size;
|
return window_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
update_frame_rect(struct screen *screen) {
|
|
||||||
int ww;
|
|
||||||
int wh;
|
|
||||||
SDL_GL_GetDrawableSize(screen->window, &ww, &wh);
|
|
||||||
|
|
||||||
// 32 bits because we need to multiply two 16 bits values
|
|
||||||
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;
|
||||||
@@ -212,10 +186,8 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
|||||||
window_flags |= SDL_WINDOW_BORDERLESS;
|
window_flags |= SDL_WINDOW_BORDERLESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int x = window_x != WINDOW_POSITION_UNDEFINED
|
int x = window_x != -1 ? window_x : (int) SDL_WINDOWPOS_UNDEFINED;
|
||||||
? window_x : (int) SDL_WINDOWPOS_UNDEFINED;
|
int y = window_y != -1 ? window_y : (int) SDL_WINDOWPOS_UNDEFINED;
|
||||||
int y = window_y != WINDOW_POSITION_UNDEFINED
|
|
||||||
? window_y : (int) SDL_WINDOWPOS_UNDEFINED;
|
|
||||||
screen->window = SDL_CreateWindow(window_title, x, y,
|
screen->window = SDL_CreateWindow(window_title, x, y,
|
||||||
window_size.width, window_size.height,
|
window_size.width, window_size.height,
|
||||||
window_flags);
|
window_flags);
|
||||||
@@ -232,6 +204,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);
|
||||||
@@ -277,6 +256,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);
|
||||||
|
|
||||||
@@ -322,7 +307,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);
|
||||||
|
|
||||||
@@ -330,16 +314,10 @@ screen_update_frame(struct screen *screen, struct video_buffer *vb) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
screen_window_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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,7 +389,7 @@ screen_handle_window_event(struct screen *screen,
|
|||||||
// window is maximized or fullscreen is enabled.
|
// window is maximized or fullscreen is enabled.
|
||||||
screen->windowed_window_size = get_window_size(screen->window);
|
screen->windowed_window_size = get_window_size(screen->window);
|
||||||
}
|
}
|
||||||
screen_window_resized(screen);
|
screen_render(screen);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
case SDL_WINDOWEVENT_MAXIMIZED:
|
||||||
// The backup size must be non-nul.
|
// The backup size must be non-nul.
|
||||||
@@ -432,11 +410,3 @@ screen_handle_window_event(struct screen *screen,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct point
|
|
||||||
screen_convert_to_frame_coords(struct screen *screen, float x, float y) {
|
|
||||||
struct point out;
|
|
||||||
out.x = (x - screen->rect.x) * screen->frame_size.width / screen->rect.w;
|
|
||||||
out.y = (y - screen->rect.y) * screen->frame_size.height / screen->rect.h;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#define WINDOW_POSITION_UNDEFINED (-0x8000)
|
|
||||||
|
|
||||||
struct video_buffer;
|
struct video_buffer;
|
||||||
|
|
||||||
struct screen {
|
struct screen {
|
||||||
@@ -22,7 +20,6 @@ struct screen {
|
|||||||
// Since we receive the event SIZE_CHANGED before MAXIMIZED, we must be
|
// Since we receive the event SIZE_CHANGED before MAXIMIZED, we must be
|
||||||
// able to revert the size to its non-maximized value.
|
// able to revert the size to its non-maximized value.
|
||||||
struct size windowed_window_size_backup;
|
struct size windowed_window_size_backup;
|
||||||
struct SDL_Rect rect;
|
|
||||||
bool has_frame;
|
bool has_frame;
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
bool maximized;
|
bool maximized;
|
||||||
@@ -45,12 +42,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, \
|
||||||
.maximized = false, \
|
.maximized = false, \
|
||||||
@@ -62,7 +53,6 @@ void
|
|||||||
screen_init(struct screen *screen);
|
screen_init(struct screen *screen);
|
||||||
|
|
||||||
// initialize screen, create window, renderer and texture (window is hidden)
|
// initialize screen, create window, renderer and texture (window is hidden)
|
||||||
// window_x and window_y accept WINDOW_POSITION_UNDEFINED
|
|
||||||
bool
|
bool
|
||||||
screen_init_rendering(struct screen *screen, const char *window_title,
|
screen_init_rendering(struct screen *screen, const char *window_title,
|
||||||
struct size frame_size, bool always_on_top,
|
struct size frame_size, bool always_on_top,
|
||||||
@@ -81,10 +71,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);
|
||||||
|
|
||||||
// update content after window resizing
|
|
||||||
void
|
|
||||||
screen_window_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);
|
||||||
@@ -105,9 +91,4 @@ screen_resize_to_pixel_perfect(struct screen *screen);
|
|||||||
void
|
void
|
||||||
screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event);
|
screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event);
|
||||||
|
|
||||||
// convert point from window coordinates to frame coordinates
|
|
||||||
// x and y are expressed in pixels (float to allow partial pixels)
|
|
||||||
struct point
|
|
||||||
screen_convert_to_frame_coords(struct screen *screen, float x, float y);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
102
app/src/server.c
102
app/src/server.c
@@ -5,7 +5,6 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
|
||||||
#include <SDL2/SDL_timer.h>
|
#include <SDL2/SDL_timer.h>
|
||||||
#include <SDL2/SDL_platform.h>
|
#include <SDL2/SDL_platform.h>
|
||||||
|
|
||||||
@@ -235,25 +234,17 @@ execute_server(struct server *server, const struct server_params *params) {
|
|||||||
char bit_rate_string[11];
|
char bit_rate_string[11];
|
||||||
char max_fps_string[6];
|
char max_fps_string[6];
|
||||||
char lock_video_orientation_string[3];
|
char lock_video_orientation_string[3];
|
||||||
char display_id_string[6];
|
|
||||||
sprintf(max_size_string, "%"PRIu16, params->max_size);
|
sprintf(max_size_string, "%"PRIu16, params->max_size);
|
||||||
sprintf(bit_rate_string, "%"PRIu32, params->bit_rate);
|
sprintf(bit_rate_string, "%"PRIu32, params->bit_rate);
|
||||||
sprintf(max_fps_string, "%"PRIu16, params->max_fps);
|
sprintf(max_fps_string, "%"PRIu16, params->max_fps);
|
||||||
sprintf(lock_video_orientation_string, "%"PRIi8, params->lock_video_orientation);
|
sprintf(lock_video_orientation_string, "%"PRIi8, params->lock_video_orientation);
|
||||||
sprintf(display_id_string, "%"PRIu16, params->display_id);
|
|
||||||
const char *const cmd[] = {
|
const char *const cmd[] = {
|
||||||
"shell",
|
"shell",
|
||||||
"CLASSPATH=" DEVICE_SERVER_PATH,
|
"CLASSPATH=" DEVICE_SERVER_PATH,
|
||||||
"app_process",
|
"app_process",
|
||||||
#ifdef SERVER_DEBUGGER
|
#ifdef SERVER_DEBUGGER
|
||||||
# define SERVER_DEBUGGER_PORT "5005"
|
# define SERVER_DEBUGGER_PORT "5005"
|
||||||
# ifdef SERVER_DEBUGGER_METHOD_NEW
|
|
||||||
/* Android 9 and above */
|
|
||||||
"-XjdwpProvider:internal -XjdwpOptions:transport=dt_socket,suspend=y,server=y,address="
|
|
||||||
# else
|
|
||||||
/* Android 8 and below */
|
|
||||||
"-agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address="
|
"-agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address="
|
||||||
# endif
|
|
||||||
SERVER_DEBUGGER_PORT,
|
SERVER_DEBUGGER_PORT,
|
||||||
#endif
|
#endif
|
||||||
"/", // unused
|
"/", // unused
|
||||||
@@ -267,7 +258,6 @@ execute_server(struct server *server, const struct server_params *params) {
|
|||||||
params->crop ? params->crop : "-",
|
params->crop ? params->crop : "-",
|
||||||
"true", // always send frame meta (packet boundaries + timestamp)
|
"true", // always send frame meta (packet boundaries + timestamp)
|
||||||
params->control ? "true" : "false",
|
params->control ? "true" : "false",
|
||||||
display_id_string,
|
|
||||||
};
|
};
|
||||||
#ifdef SERVER_DEBUGGER
|
#ifdef SERVER_DEBUGGER
|
||||||
LOGI("Server debugger waiting for a client on device port "
|
LOGI("Server debugger waiting for a client on device port "
|
||||||
@@ -318,12 +308,14 @@ connect_to_server(uint16_t port, uint32_t attempts, uint32_t delay) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
close_socket(socket_t socket) {
|
close_socket(socket_t *socket) {
|
||||||
assert(socket != INVALID_SOCKET);
|
assert(*socket != INVALID_SOCKET);
|
||||||
net_shutdown(socket, SHUT_RDWR);
|
net_shutdown(*socket, SHUT_RDWR);
|
||||||
if (!net_close(socket)) {
|
if (!net_close(*socket)) {
|
||||||
LOGW("Could not close socket");
|
LOGW("Could not close socket");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
*socket = INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -331,22 +323,6 @@ server_init(struct server *server) {
|
|||||||
*server = (struct server) SERVER_INITIALIZER;
|
*server = (struct server) SERVER_INITIALIZER;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
run_wait_server(void *data) {
|
|
||||||
struct server *server = data;
|
|
||||||
cmd_simple_wait(server->process, NULL); // ignore exit code
|
|
||||||
// no need for synchronization, server_socket is initialized before this
|
|
||||||
// thread was created
|
|
||||||
if (server->server_socket != INVALID_SOCKET
|
|
||||||
&& !atomic_flag_test_and_set(&server->server_socket_closed)) {
|
|
||||||
// On Linux, accept() is unblocked by shutdown(), but on Windows, it is
|
|
||||||
// unblocked by closesocket(). Therefore, call both (close_socket()).
|
|
||||||
close_socket(server->server_socket);
|
|
||||||
}
|
|
||||||
LOGD("Server terminated");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
server_start(struct server *server, const char *serial,
|
server_start(struct server *server, const char *serial,
|
||||||
const struct server_params *params) {
|
const struct server_params *params) {
|
||||||
@@ -360,50 +336,30 @@ server_start(struct server *server, const char *serial,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!push_server(serial)) {
|
if (!push_server(serial)) {
|
||||||
goto error1;
|
SDL_free(server->serial);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!enable_tunnel_any_port(server, params->port_range)) {
|
if (!enable_tunnel_any_port(server, params->port_range)) {
|
||||||
goto error1;
|
SDL_free(server->serial);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// server will connect to our server socket
|
// server will connect to our server socket
|
||||||
server->process = execute_server(server, params);
|
server->process = execute_server(server, params);
|
||||||
if (server->process == PROCESS_NONE) {
|
|
||||||
goto error2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the server process dies before connecting to the server socket, then
|
if (server->process == PROCESS_NONE) {
|
||||||
// the client will be stuck forever on accept(). To avoid the problem, we
|
if (!server->tunnel_forward) {
|
||||||
// must be able to wake up the accept() call when the server dies. To keep
|
close_socket(&server->server_socket);
|
||||||
// things simple and multiplatform, just spawn a new thread waiting for the
|
}
|
||||||
// server process and calling shutdown()/close() on the server socket if
|
disable_tunnel(server);
|
||||||
// necessary to wake up any accept() blocking call.
|
SDL_free(server->serial);
|
||||||
server->wait_server_thread =
|
return false;
|
||||||
SDL_CreateThread(run_wait_server, "wait-server", server);
|
|
||||||
if (!server->wait_server_thread) {
|
|
||||||
cmd_terminate(server->process);
|
|
||||||
cmd_simple_wait(server->process, NULL); // ignore exit code
|
|
||||||
goto error2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server->tunnel_enabled = true;
|
server->tunnel_enabled = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
error2:
|
|
||||||
if (!server->tunnel_forward) {
|
|
||||||
bool was_closed =
|
|
||||||
atomic_flag_test_and_set(&server->server_socket_closed);
|
|
||||||
// the thread is not started, the flag could not be already set
|
|
||||||
assert(!was_closed);
|
|
||||||
(void) was_closed;
|
|
||||||
close_socket(server->server_socket);
|
|
||||||
}
|
|
||||||
disable_tunnel(server);
|
|
||||||
error1:
|
|
||||||
SDL_free(server->serial);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -421,11 +377,7 @@ server_connect_to(struct server *server) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we don't need the server socket anymore
|
// we don't need the server socket anymore
|
||||||
if (!atomic_flag_test_and_set(&server->server_socket_closed)) {
|
close_socket(&server->server_socket);
|
||||||
// close it from here
|
|
||||||
close_socket(server->server_socket);
|
|
||||||
// otherwise, it is closed by run_wait_server()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
uint32_t attempts = 100;
|
uint32_t attempts = 100;
|
||||||
uint32_t delay = 100; // ms
|
uint32_t delay = 100; // ms
|
||||||
@@ -452,27 +404,29 @@ server_connect_to(struct server *server) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
server_stop(struct server *server) {
|
server_stop(struct server *server) {
|
||||||
if (server->server_socket != INVALID_SOCKET
|
if (server->server_socket != INVALID_SOCKET) {
|
||||||
&& !atomic_flag_test_and_set(&server->server_socket_closed)) {
|
close_socket(&server->server_socket);
|
||||||
close_socket(server->server_socket);
|
|
||||||
}
|
}
|
||||||
if (server->video_socket != INVALID_SOCKET) {
|
if (server->video_socket != INVALID_SOCKET) {
|
||||||
close_socket(server->video_socket);
|
close_socket(&server->video_socket);
|
||||||
}
|
}
|
||||||
if (server->control_socket != INVALID_SOCKET) {
|
if (server->control_socket != INVALID_SOCKET) {
|
||||||
close_socket(server->control_socket);
|
close_socket(&server->control_socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(server->process != PROCESS_NONE);
|
assert(server->process != PROCESS_NONE);
|
||||||
|
|
||||||
cmd_terminate(server->process);
|
if (!cmd_terminate(server->process)) {
|
||||||
|
LOGW("Could not terminate server");
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_simple_wait(server->process, NULL); // ignore exit code
|
||||||
|
LOGD("Server terminated");
|
||||||
|
|
||||||
if (server->tunnel_enabled) {
|
if (server->tunnel_enabled) {
|
||||||
// ignore failure
|
// ignore failure
|
||||||
disable_tunnel(server);
|
disable_tunnel(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_WaitThread(server->wait_server_thread, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
#ifndef SERVER_H
|
#ifndef SERVER_H
|
||||||
#define SERVER_H
|
#define SERVER_H
|
||||||
|
|
||||||
#include <stdatomic.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
@@ -14,8 +12,6 @@
|
|||||||
struct server {
|
struct server {
|
||||||
char *serial;
|
char *serial;
|
||||||
process_t process;
|
process_t process;
|
||||||
SDL_Thread *wait_server_thread;
|
|
||||||
atomic_flag server_socket_closed;
|
|
||||||
socket_t server_socket; // only used if !tunnel_forward
|
socket_t server_socket; // only used if !tunnel_forward
|
||||||
socket_t video_socket;
|
socket_t video_socket;
|
||||||
socket_t control_socket;
|
socket_t control_socket;
|
||||||
@@ -28,8 +24,6 @@ struct server {
|
|||||||
#define SERVER_INITIALIZER { \
|
#define SERVER_INITIALIZER { \
|
||||||
.serial = NULL, \
|
.serial = NULL, \
|
||||||
.process = PROCESS_NONE, \
|
.process = PROCESS_NONE, \
|
||||||
.wait_server_thread = NULL, \
|
|
||||||
.server_socket_closed = ATOMIC_FLAG_INIT, \
|
|
||||||
.server_socket = INVALID_SOCKET, \
|
.server_socket = INVALID_SOCKET, \
|
||||||
.video_socket = INVALID_SOCKET, \
|
.video_socket = INVALID_SOCKET, \
|
||||||
.control_socket = INVALID_SOCKET, \
|
.control_socket = INVALID_SOCKET, \
|
||||||
@@ -50,7 +44,6 @@ struct server_params {
|
|||||||
uint16_t max_fps;
|
uint16_t max_fps;
|
||||||
int8_t lock_video_orientation;
|
int8_t lock_video_orientation;
|
||||||
bool control;
|
bool control;
|
||||||
uint16_t display_id;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// init default values
|
// init default values
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.6.2'
|
classpath 'com.android.tools.build:gradle:3.4.2'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
|||||||
@@ -129,6 +129,11 @@ page at http://checkstyle.sourceforge.net/config.html -->
|
|||||||
</module>
|
</module>
|
||||||
<module name="IllegalInstantiation" />
|
<module name="IllegalInstantiation" />
|
||||||
<module name="InnerAssignment" />
|
<module name="InnerAssignment" />
|
||||||
|
<module name="MagicNumber">
|
||||||
|
<property name="severity" value="info" />
|
||||||
|
<property name="ignoreHashCodeMethod" value="true" />
|
||||||
|
<property name="ignoreAnnotation" value="true" />
|
||||||
|
</module>
|
||||||
<module name="MissingSwitchDefault" />
|
<module name="MissingSwitchDefault" />
|
||||||
<module name="SimplifyBooleanExpression" />
|
<module name="SimplifyBooleanExpression" />
|
||||||
<module name="SimplifyBooleanReturn" />
|
<module name="SimplifyBooleanReturn" />
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
33
gradlew
vendored
33
gradlew
vendored
@@ -125,8 +125,8 @@ if $darwin; then
|
|||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
if $cygwin ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
@@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|||||||
else
|
else
|
||||||
eval `echo args$i`="\"$arg\""
|
eval `echo args$i`="\"$arg\""
|
||||||
fi
|
fi
|
||||||
i=`expr $i + 1`
|
i=$((i+1))
|
||||||
done
|
done
|
||||||
case $i in
|
case $i in
|
||||||
0) set -- ;;
|
(0) set -- ;;
|
||||||
1) set -- "$args0" ;;
|
(1) set -- "$args0" ;;
|
||||||
2) set -- "$args0" "$args1" ;;
|
(2) set -- "$args0" "$args1" ;;
|
||||||
3) set -- "$args0" "$args1" "$args2" ;;
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -175,9 +175,14 @@ save () {
|
|||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
echo " "
|
echo " "
|
||||||
}
|
}
|
||||||
APP_ARGS=`save "$@"`
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
3
gradlew.bat
vendored
3
gradlew.bat
vendored
@@ -29,9 +29,6 @@ if "%DIRNAME%" == "" set DIRNAME=.
|
|||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
|
||||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
|||||||
@@ -6,4 +6,3 @@ option('prebuilt_server', type: 'string', description: 'Path of the prebuilt ser
|
|||||||
option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable')
|
option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable')
|
||||||
option('hidpi_support', type: 'boolean', value: true, description: 'Enable High DPI support')
|
option('hidpi_support', type: 'boolean', value: true, description: 'Enable High DPI support')
|
||||||
option('server_debugger', type: 'boolean', value: false, description: 'Run a server debugger and wait for a client to be attached')
|
option('server_debugger', type: 'boolean', value: false, description: 'Run a server debugger and wait for a client to be attached')
|
||||||
option('server_debugger_method', type: 'combo', choices: ['old', 'new'], value: 'new', description: 'Select the debugger method (Android < 9: "old", Android >= 9: "new")')
|
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import java.nio.charset.StandardCharsets;
|
|||||||
|
|
||||||
public class ControlMessageReader {
|
public class ControlMessageReader {
|
||||||
|
|
||||||
static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 9;
|
private static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 9;
|
||||||
static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 27;
|
private static final int INJECT_MOUSE_EVENT_PAYLOAD_LENGTH = 17;
|
||||||
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
private static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 21;
|
||||||
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
private static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
||||||
|
private static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
||||||
|
|
||||||
public static final int TEXT_MAX_LENGTH = 300;
|
public static final int TEXT_MAX_LENGTH = 300;
|
||||||
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4093;
|
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4093;
|
||||||
@@ -121,6 +122,7 @@ public class ControlMessageReader {
|
|||||||
return ControlMessage.createInjectText(text);
|
return ControlMessage.createInjectText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
private ControlMessage parseInjectTouchEvent() {
|
private ControlMessage parseInjectTouchEvent() {
|
||||||
if (buffer.remaining() < INJECT_TOUCH_EVENT_PAYLOAD_LENGTH) {
|
if (buffer.remaining() < INJECT_TOUCH_EVENT_PAYLOAD_LENGTH) {
|
||||||
return null;
|
return null;
|
||||||
@@ -170,10 +172,12 @@ public class ControlMessageReader {
|
|||||||
return new Position(x, y, screenWidth, screenHeight);
|
return new Position(x, y, screenWidth, screenHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
private static int toUnsigned(short value) {
|
private static int toUnsigned(short value) {
|
||||||
return value & 0xffff;
|
return value & 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
private static int toUnsigned(byte value) {
|
private static int toUnsigned(byte value) {
|
||||||
return value & 0xff;
|
return value & 0xff;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ public class Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
public void control() throws IOException {
|
public void control() throws IOException {
|
||||||
// on start, power on the device
|
// on start, power on the device
|
||||||
if (!device.isScreenOn()) {
|
if (!device.isScreenOn()) {
|
||||||
@@ -75,29 +76,19 @@ public class Controller {
|
|||||||
ControlMessage msg = connection.receiveControlMessage();
|
ControlMessage msg = connection.receiveControlMessage();
|
||||||
switch (msg.getType()) {
|
switch (msg.getType()) {
|
||||||
case ControlMessage.TYPE_INJECT_KEYCODE:
|
case ControlMessage.TYPE_INJECT_KEYCODE:
|
||||||
if (device.supportsInputEvents()) {
|
injectKeycode(msg.getAction(), msg.getKeycode(), msg.getMetaState());
|
||||||
injectKeycode(msg.getAction(), msg.getKeycode(), msg.getMetaState());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_TEXT:
|
case ControlMessage.TYPE_INJECT_TEXT:
|
||||||
if (device.supportsInputEvents()) {
|
injectText(msg.getText());
|
||||||
injectText(msg.getText());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
||||||
if (device.supportsInputEvents()) {
|
injectTouch(msg.getAction(), msg.getPointerId(), msg.getPosition(), msg.getPressure(), msg.getButtons());
|
||||||
injectTouch(msg.getAction(), msg.getPointerId(), msg.getPosition(), msg.getPressure(), msg.getButtons());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||||
if (device.supportsInputEvents()) {
|
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
||||||
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
||||||
if (device.supportsInputEvents()) {
|
pressBackOrTurnScreenOn();
|
||||||
pressBackOrTurnScreenOn();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||||
device.expandNotificationPanel();
|
device.expandNotificationPanel();
|
||||||
@@ -113,9 +104,7 @@ public class Controller {
|
|||||||
device.setClipboardText(msg.getText());
|
device.setClipboardText(msg.getText());
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
||||||
if (device.supportsInputEvents()) {
|
device.setScreenPowerMode(msg.getAction());
|
||||||
device.setScreenPowerMode(msg.getAction());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_ROTATE_DEVICE:
|
case ControlMessage.TYPE_ROTATE_DEVICE:
|
||||||
device.rotateDevice();
|
device.rotateDevice();
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ public final class DesktopConnection implements Closeable {
|
|||||||
controlSocket.close();
|
controlSocket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
private void send(String deviceName, int width, int height) throws IOException {
|
private void send(String deviceName, int width, int height) throws IOException {
|
||||||
byte[] buffer = new byte[DEVICE_NAME_FIELD_LENGTH + 4];
|
byte[] buffer = new byte[DEVICE_NAME_FIELD_LENGTH + 4];
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.genymobile.scrcpy;
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
import com.genymobile.scrcpy.wrappers.InputManager;
|
|
||||||
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||||
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||||
import com.genymobile.scrcpy.wrappers.WindowManager;
|
import com.genymobile.scrcpy.wrappers.WindowManager;
|
||||||
@@ -26,35 +25,9 @@ public final class Device {
|
|||||||
private ScreenInfo screenInfo;
|
private ScreenInfo screenInfo;
|
||||||
private RotationListener rotationListener;
|
private RotationListener rotationListener;
|
||||||
|
|
||||||
/**
|
|
||||||
* Logical display identifier
|
|
||||||
*/
|
|
||||||
private final int displayId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The surface flinger layer stack associated with this logical display
|
|
||||||
*/
|
|
||||||
private final int layerStack;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The FLAG_PRESENTATION from the DisplayInfo
|
|
||||||
*/
|
|
||||||
private final boolean isPresentationDisplay;
|
|
||||||
|
|
||||||
public Device(Options options) {
|
public Device(Options options) {
|
||||||
displayId = options.getDisplayId();
|
DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo();
|
||||||
DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo(displayId);
|
|
||||||
if (displayInfo == null) {
|
|
||||||
int[] displayIds = serviceManager.getDisplayManager().getDisplayIds();
|
|
||||||
throw new InvalidDisplayIdException(displayId, displayIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
int displayInfoFlags = displayInfo.getFlags();
|
|
||||||
|
|
||||||
screenInfo = ScreenInfo.computeScreenInfo(displayInfo, options.getCrop(), options.getMaxSize(), options.getLockedVideoOrientation());
|
screenInfo = ScreenInfo.computeScreenInfo(displayInfo, options.getCrop(), options.getMaxSize(), options.getLockedVideoOrientation());
|
||||||
layerStack = displayInfo.getLayerStack();
|
|
||||||
isPresentationDisplay = (displayInfoFlags & DisplayInfo.FLAG_PRESENTATION) != 0;
|
|
||||||
|
|
||||||
registerRotationWatcher(new IRotationWatcher.Stub() {
|
registerRotationWatcher(new IRotationWatcher.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void onRotationChanged(int rotation) throws RemoteException {
|
public void onRotationChanged(int rotation) throws RemoteException {
|
||||||
@@ -68,24 +41,12 @@ public final class Device {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if ((displayInfoFlags & DisplayInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS) == 0) {
|
|
||||||
Ln.w("Display doesn't have FLAG_SUPPORTS_PROTECTED_BUFFERS flag, mirroring can be restricted");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!supportsInputEvents()) {
|
|
||||||
Ln.w("Input events are not supported for displays with FLAG_PRESENTATION enabled for devices with API lower than 29");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized ScreenInfo getScreenInfo() {
|
public synchronized ScreenInfo getScreenInfo() {
|
||||||
return screenInfo;
|
return screenInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLayerStack() {
|
|
||||||
return layerStack;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Point getPhysicalPoint(Position position) {
|
public Point getPhysicalPoint(Position position) {
|
||||||
// it hides the field on purpose, to read it with a lock
|
// it hides the field on purpose, to read it with a lock
|
||||||
@SuppressWarnings("checkstyle:HiddenField")
|
@SuppressWarnings("checkstyle:HiddenField")
|
||||||
@@ -115,22 +76,7 @@ public final class Device {
|
|||||||
return Build.MODEL;
|
return Build.MODEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean supportsInputEvents() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return !isPresentationDisplay;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
|
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
|
||||||
if (!supportsInputEvents()) {
|
|
||||||
throw new AssertionError("Could not inject input event if !supportsInputEvents()");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (displayId != 0 && !InputManager.setDisplayId(inputEvent, displayId)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return serviceManager.getInputManager().injectInputEvent(inputEvent, mode);
|
return serviceManager.getInputManager().injectInputEvent(inputEvent, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,10 +109,8 @@ public final class Device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setClipboardText(String text) {
|
public void setClipboardText(String text) {
|
||||||
boolean ok = serviceManager.getClipboardManager().setText(text);
|
serviceManager.getClipboardManager().setText(text);
|
||||||
if (ok) {
|
Ln.i("Device clipboard set");
|
||||||
Ln.i("Device clipboard set");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,10 +122,8 @@ public final class Device {
|
|||||||
Ln.e("Could not get built-in display");
|
Ln.e("Could not get built-in display");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean ok = SurfaceControl.setDisplayPowerMode(d, mode);
|
SurfaceControl.setDisplayPowerMode(d, mode);
|
||||||
if (ok) {
|
Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on"));
|
||||||
Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ public class DeviceMessageWriter {
|
|||||||
private final byte[] rawBuffer = new byte[MAX_EVENT_SIZE];
|
private final byte[] rawBuffer = new byte[MAX_EVENT_SIZE];
|
||||||
private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer);
|
private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer);
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
public void writeTo(DeviceMessage msg, OutputStream output) throws IOException {
|
public void writeTo(DeviceMessage msg, OutputStream output) throws IOException {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
buffer.put((byte) DeviceMessage.TYPE_CLIPBOARD);
|
buffer.put((byte) DeviceMessage.TYPE_CLIPBOARD);
|
||||||
|
|||||||
@@ -1,25 +1,12 @@
|
|||||||
package com.genymobile.scrcpy;
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
public final class DisplayInfo {
|
public final class DisplayInfo {
|
||||||
private final int displayId;
|
|
||||||
private final Size size;
|
private final Size size;
|
||||||
private final int rotation;
|
private final int rotation;
|
||||||
private final int layerStack;
|
|
||||||
private final int flags;
|
|
||||||
|
|
||||||
public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 0x00000001;
|
public DisplayInfo(Size size, int rotation) {
|
||||||
public static final int FLAG_PRESENTATION = 0x00000008;
|
|
||||||
|
|
||||||
public DisplayInfo(int displayId, Size size, int rotation, int layerStack, int flags) {
|
|
||||||
this.displayId = displayId;
|
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.rotation = rotation;
|
this.rotation = rotation;
|
||||||
this.layerStack = layerStack;
|
|
||||||
this.flags = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDisplayId() {
|
|
||||||
return displayId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Size getSize() {
|
public Size getSize() {
|
||||||
@@ -29,13 +16,5 @@ public final class DisplayInfo {
|
|||||||
public int getRotation() {
|
public int getRotation() {
|
||||||
return rotation;
|
return rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLayerStack() {
|
|
||||||
return layerStack;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFlags() {
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
package com.genymobile.scrcpy;
|
|
||||||
|
|
||||||
public class InvalidDisplayIdException extends RuntimeException {
|
|
||||||
|
|
||||||
private final int displayId;
|
|
||||||
private final int[] availableDisplayIds;
|
|
||||||
|
|
||||||
public InvalidDisplayIdException(int displayId, int[] availableDisplayIds) {
|
|
||||||
super("There is no display having id " + displayId);
|
|
||||||
this.displayId = displayId;
|
|
||||||
this.availableDisplayIds = availableDisplayIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDisplayId() {
|
|
||||||
return displayId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int[] getAvailableDisplayIds() {
|
|
||||||
return availableDisplayIds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,7 +11,6 @@ public class Options {
|
|||||||
private Rect crop;
|
private Rect crop;
|
||||||
private boolean sendFrameMeta; // send PTS so that the client may record properly
|
private boolean sendFrameMeta; // send PTS so that the client may record properly
|
||||||
private boolean control;
|
private boolean control;
|
||||||
private int displayId;
|
|
||||||
|
|
||||||
public int getMaxSize() {
|
public int getMaxSize() {
|
||||||
return maxSize;
|
return maxSize;
|
||||||
@@ -76,12 +75,4 @@ public class Options {
|
|||||||
public void setControl(boolean control) {
|
public void setControl(boolean control) {
|
||||||
this.control = control;
|
this.control = control;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDisplayId() {
|
|
||||||
return displayId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDisplayId(int displayId) {
|
|
||||||
this.displayId = displayId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,17 +66,15 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
IBinder display = createDisplay();
|
IBinder display = createDisplay();
|
||||||
ScreenInfo screenInfo = device.getScreenInfo();
|
ScreenInfo screenInfo = device.getScreenInfo();
|
||||||
Rect contentRect = screenInfo.getContentRect();
|
Rect contentRect = screenInfo.getContentRect();
|
||||||
// include the locked video orientation
|
/// include the locked video orientation
|
||||||
Rect videoRect = screenInfo.getVideoSize().toRect();
|
Rect videoRect = screenInfo.getVideoSize().toRect();
|
||||||
// does not include the locked video orientation
|
// does not include the locked video orientation
|
||||||
Rect unlockedVideoRect = screenInfo.getUnlockedVideoSize().toRect();
|
Rect unlockedVideoRect = screenInfo.getUnlockedVideoSize().toRect();
|
||||||
int videoRotation = screenInfo.getVideoRotation();
|
int videoRotation = screenInfo.getVideoRotation();
|
||||||
int layerStack = device.getLayerStack();
|
|
||||||
|
|
||||||
setSize(format, videoRect.width(), videoRect.height());
|
setSize(format, videoRect.width(), videoRect.height());
|
||||||
configure(codec, format);
|
configure(codec, format);
|
||||||
Surface surface = codec.createInputSurface();
|
Surface surface = codec.createInputSurface();
|
||||||
setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack);
|
setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect);
|
||||||
codec.start();
|
codec.start();
|
||||||
try {
|
try {
|
||||||
alive = encode(codec, fd);
|
alive = encode(codec, fd);
|
||||||
@@ -147,6 +145,7 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
return MediaCodec.createEncoderByType("video/avc");
|
return MediaCodec.createEncoderByType("video/avc");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
private static MediaFormat createFormat(int bitRate, int maxFps, int iFrameInterval) {
|
private static MediaFormat createFormat(int bitRate, int maxFps, int iFrameInterval) {
|
||||||
MediaFormat format = new MediaFormat();
|
MediaFormat format = new MediaFormat();
|
||||||
format.setString(MediaFormat.KEY_MIME, "video/avc");
|
format.setString(MediaFormat.KEY_MIME, "video/avc");
|
||||||
@@ -179,12 +178,12 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
format.setInteger(MediaFormat.KEY_HEIGHT, height);
|
format.setInteger(MediaFormat.KEY_HEIGHT, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setDisplaySurface(IBinder display, Surface surface, int orientation, Rect deviceRect, Rect displayRect, int layerStack) {
|
private static void setDisplaySurface(IBinder display, Surface surface, int orientation, Rect deviceRect, Rect displayRect) {
|
||||||
SurfaceControl.openTransaction();
|
SurfaceControl.openTransaction();
|
||||||
try {
|
try {
|
||||||
SurfaceControl.setDisplaySurface(display, surface);
|
SurfaceControl.setDisplaySurface(display, surface);
|
||||||
SurfaceControl.setDisplayProjection(display, orientation, deviceRect, displayRect);
|
SurfaceControl.setDisplayProjection(display, orientation, deviceRect, displayRect);
|
||||||
SurfaceControl.setDisplayLayerStack(display, layerStack);
|
SurfaceControl.setDisplayLayerStack(display, 0);
|
||||||
} finally {
|
} finally {
|
||||||
SurfaceControl.closeTransaction();
|
SurfaceControl.closeTransaction();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ public final class ScreenInfo {
|
|||||||
return rect.width() + ":" + rect.height() + ":" + rect.left + ":" + rect.top;
|
return rect.width() + ":" + rect.height() + ":" + rect.left + ":" + rect.top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
private static Size computeVideoSize(int w, int h, int maxSize) {
|
private static Size computeVideoSize(int w, int h, int maxSize) {
|
||||||
// Compute the video size and the padding of the content inside this video.
|
// Compute the video size and the padding of the content inside this video.
|
||||||
// Principle:
|
// Principle:
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ public final class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void scrcpy(Options options) throws IOException {
|
private static void scrcpy(Options options) throws IOException {
|
||||||
Ln.i("Device: " + Build.MANUFACTURER + " " + Build.MODEL + " (Android " + Build.VERSION.RELEASE + ")");
|
|
||||||
final Device device = new Device(options);
|
final Device device = new Device(options);
|
||||||
boolean tunnelForward = options.isTunnelForward();
|
boolean tunnelForward = options.isTunnelForward();
|
||||||
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) {
|
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) {
|
||||||
@@ -69,6 +68,7 @@ public final class Server {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
private static Options createOptions(String... args) {
|
private static Options createOptions(String... args) {
|
||||||
if (args.length < 1) {
|
if (args.length < 1) {
|
||||||
throw new IllegalArgumentException("Missing client version");
|
throw new IllegalArgumentException("Missing client version");
|
||||||
@@ -77,11 +77,11 @@ public final class Server {
|
|||||||
String clientVersion = args[0];
|
String clientVersion = args[0];
|
||||||
if (!clientVersion.equals(BuildConfig.VERSION_NAME)) {
|
if (!clientVersion.equals(BuildConfig.VERSION_NAME)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")");
|
"The server version (" + clientVersion + ") does not match the client " + "(" + BuildConfig.VERSION_NAME + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length != 10) {
|
if (args.length != 9) {
|
||||||
throw new IllegalArgumentException("Expecting 10 parameters");
|
throw new IllegalArgumentException("Expecting 9 parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
Options options = new Options();
|
Options options = new Options();
|
||||||
@@ -111,12 +111,10 @@ public final class Server {
|
|||||||
boolean control = Boolean.parseBoolean(args[8]);
|
boolean control = Boolean.parseBoolean(args[8]);
|
||||||
options.setControl(control);
|
options.setControl(control);
|
||||||
|
|
||||||
int displayId = Integer.parseInt(args[9]);
|
|
||||||
options.setDisplayId(displayId);
|
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
private static Rect parseCrop(String crop) {
|
private static Rect parseCrop(String crop) {
|
||||||
if ("-".equals(crop)) {
|
if ("-".equals(crop)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -141,6 +139,7 @@ public final class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
private static void suggestFix(Throwable e) {
|
private static void suggestFix(Throwable e) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
if (e instanceof MediaCodec.CodecException) {
|
if (e instanceof MediaCodec.CodecException) {
|
||||||
@@ -152,16 +151,6 @@ public final class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (e instanceof InvalidDisplayIdException) {
|
|
||||||
InvalidDisplayIdException idie = (InvalidDisplayIdException) e;
|
|
||||||
int[] displayIds = idie.getAvailableDisplayIds();
|
|
||||||
if (displayIds != null && displayIds.length > 0) {
|
|
||||||
Ln.e("Try to use one of the available display ids:");
|
|
||||||
for (int id : displayIds) {
|
|
||||||
Ln.e(" scrcpy --display " + id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String... args) throws Exception {
|
public static void main(String... args) throws Exception {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ public final class StringUtils {
|
|||||||
// not instantiable
|
// not instantiable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
public static int getUtf8TruncationIndex(byte[] utf8, int maxLength) {
|
public static int getUtf8TruncationIndex(byte[] utf8, int maxLength) {
|
||||||
int len = utf8.length;
|
int len = utf8.length;
|
||||||
if (len <= maxLength) {
|
if (len <= maxLength) {
|
||||||
|
|||||||
@@ -74,15 +74,13 @@ public class ClipboardManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean setText(CharSequence text) {
|
public void setText(CharSequence text) {
|
||||||
try {
|
try {
|
||||||
Method method = getSetPrimaryClipMethod();
|
Method method = getSetPrimaryClipMethod();
|
||||||
ClipData clipData = ClipData.newPlainText(null, text);
|
ClipData clipData = ClipData.newPlainText(null, text);
|
||||||
setPrimaryClip(method, manager, clipData);
|
setPrimaryClip(method, manager, clipData);
|
||||||
return true;
|
|
||||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke method", e);
|
Ln.e("Could not invoke method", e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,28 +12,15 @@ public final class DisplayManager {
|
|||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayInfo getDisplayInfo(int displayId) {
|
public DisplayInfo getDisplayInfo() {
|
||||||
try {
|
try {
|
||||||
Object displayInfo = manager.getClass().getMethod("getDisplayInfo", int.class).invoke(manager, displayId);
|
Object displayInfo = manager.getClass().getMethod("getDisplayInfo", int.class).invoke(manager, 0);
|
||||||
if (displayInfo == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Class<?> cls = displayInfo.getClass();
|
Class<?> cls = displayInfo.getClass();
|
||||||
// width and height already take the rotation into account
|
// width and height already take the rotation into account
|
||||||
int width = cls.getDeclaredField("logicalWidth").getInt(displayInfo);
|
int width = cls.getDeclaredField("logicalWidth").getInt(displayInfo);
|
||||||
int height = cls.getDeclaredField("logicalHeight").getInt(displayInfo);
|
int height = cls.getDeclaredField("logicalHeight").getInt(displayInfo);
|
||||||
int rotation = cls.getDeclaredField("rotation").getInt(displayInfo);
|
int rotation = cls.getDeclaredField("rotation").getInt(displayInfo);
|
||||||
int layerStack = cls.getDeclaredField("layerStack").getInt(displayInfo);
|
return new DisplayInfo(new Size(width, height), rotation);
|
||||||
int flags = cls.getDeclaredField("flags").getInt(displayInfo);
|
|
||||||
return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int[] getDisplayIds() {
|
|
||||||
try {
|
|
||||||
return (int[]) manager.getClass().getMethod("getDisplayIds").invoke(manager);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ public final class InputManager {
|
|||||||
private final IInterface manager;
|
private final IInterface manager;
|
||||||
private Method injectInputEventMethod;
|
private Method injectInputEventMethod;
|
||||||
|
|
||||||
private static Method setDisplayIdMethod;
|
|
||||||
|
|
||||||
public InputManager(IInterface manager) {
|
public InputManager(IInterface manager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
@@ -39,23 +37,4 @@ public final class InputManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Method getSetDisplayIdMethod() throws NoSuchMethodException {
|
|
||||||
if (setDisplayIdMethod == null) {
|
|
||||||
setDisplayIdMethod = InputEvent.class.getMethod("setDisplayId", int.class);
|
|
||||||
}
|
|
||||||
return setDisplayIdMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean setDisplayId(InputEvent inputEvent, int displayId) {
|
|
||||||
try {
|
|
||||||
Method method = getSetDisplayIdMethod();
|
|
||||||
method.invoke(inputEvent, displayId);
|
|
||||||
return true;
|
|
||||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
|
||||||
// just a warning, it might happen on old devices
|
|
||||||
Ln.w("Cannot associate a display id to the input event");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,14 +121,12 @@ public final class SurfaceControl {
|
|||||||
return setDisplayPowerModeMethod;
|
return setDisplayPowerModeMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean setDisplayPowerMode(IBinder displayToken, int mode) {
|
public static void setDisplayPowerMode(IBinder displayToken, int mode) {
|
||||||
try {
|
try {
|
||||||
Method method = getSetDisplayPowerModeMethod();
|
Method method = getSetDisplayPowerModeMethod();
|
||||||
method.invoke(null, displayToken, mode);
|
method.invoke(null, displayToken, mode);
|
||||||
return true;
|
|
||||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke method", e);
|
Ln.e("Could not invoke method", e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,6 @@ public class ControlMessageReaderTest {
|
|||||||
dos.writeInt(KeyEvent.META_CTRL_ON);
|
dos.writeInt(KeyEvent.META_CTRL_ON);
|
||||||
byte[] packet = bos.toByteArray();
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
// The message type (1 byte) does not count
|
|
||||||
Assert.assertEquals(ControlMessageReader.INJECT_KEYCODE_PAYLOAD_LENGTH, packet.length - 1);
|
|
||||||
|
|
||||||
reader.readFrom(new ByteArrayInputStream(packet));
|
reader.readFrom(new ByteArrayInputStream(packet));
|
||||||
ControlMessage event = reader.next();
|
ControlMessage event = reader.next();
|
||||||
|
|
||||||
@@ -80,6 +77,7 @@ public class ControlMessageReaderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
public void testParseTouchEvent() throws IOException {
|
public void testParseTouchEvent() throws IOException {
|
||||||
ControlMessageReader reader = new ControlMessageReader();
|
ControlMessageReader reader = new ControlMessageReader();
|
||||||
|
|
||||||
@@ -97,9 +95,6 @@ public class ControlMessageReaderTest {
|
|||||||
|
|
||||||
byte[] packet = bos.toByteArray();
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
// The message type (1 byte) does not count
|
|
||||||
Assert.assertEquals(ControlMessageReader.INJECT_TOUCH_EVENT_PAYLOAD_LENGTH, packet.length - 1);
|
|
||||||
|
|
||||||
reader.readFrom(new ByteArrayInputStream(packet));
|
reader.readFrom(new ByteArrayInputStream(packet));
|
||||||
ControlMessage event = reader.next();
|
ControlMessage event = reader.next();
|
||||||
|
|
||||||
@@ -115,6 +110,7 @@ public class ControlMessageReaderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
public void testParseScrollEvent() throws IOException {
|
public void testParseScrollEvent() throws IOException {
|
||||||
ControlMessageReader reader = new ControlMessageReader();
|
ControlMessageReader reader = new ControlMessageReader();
|
||||||
|
|
||||||
@@ -130,9 +126,6 @@ public class ControlMessageReaderTest {
|
|||||||
|
|
||||||
byte[] packet = bos.toByteArray();
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
// The message type (1 byte) does not count
|
|
||||||
Assert.assertEquals(ControlMessageReader.INJECT_SCROLL_EVENT_PAYLOAD_LENGTH, packet.length - 1);
|
|
||||||
|
|
||||||
reader.readFrom(new ByteArrayInputStream(packet));
|
reader.readFrom(new ByteArrayInputStream(packet));
|
||||||
ControlMessage event = reader.next();
|
ControlMessage event = reader.next();
|
||||||
|
|
||||||
@@ -240,9 +233,6 @@ public class ControlMessageReaderTest {
|
|||||||
|
|
||||||
byte[] packet = bos.toByteArray();
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
// The message type (1 byte) does not count
|
|
||||||
Assert.assertEquals(ControlMessageReader.SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH, packet.length - 1);
|
|
||||||
|
|
||||||
reader.readFrom(new ByteArrayInputStream(packet));
|
reader.readFrom(new ByteArrayInputStream(packet));
|
||||||
ControlMessage event = reader.next();
|
ControlMessage event = reader.next();
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
public class StringUtilsTest {
|
public class StringUtilsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
public void testUtf8Truncate() {
|
public void testUtf8Truncate() {
|
||||||
String s = "aÉbÔc";
|
String s = "aÉbÔc";
|
||||||
byte[] utf8 = s.getBytes(StandardCharsets.UTF_8);
|
byte[] utf8 = s.getBytes(StandardCharsets.UTF_8);
|
||||||
|
|||||||
Reference in New Issue
Block a user