Compare commits
1 Commits
logical_si
...
issue702
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d75422d21 |
2
BUILD.md
2
BUILD.md
@@ -43,7 +43,7 @@ Install the required packages from your package manager.
|
|||||||
sudo apt install ffmpeg libsdl2-2.0-0
|
sudo apt install ffmpeg libsdl2-2.0-0
|
||||||
|
|
||||||
# client build dependencies
|
# client build dependencies
|
||||||
sudo apt install gcc git pkg-config meson ninja-build \
|
sudo apt install make gcc git pkg-config meson ninja-build \
|
||||||
libavcodec-dev libavformat-dev libavutil-dev \
|
libavcodec-dev libavformat-dev libavutil-dev \
|
||||||
libsdl2-dev
|
libsdl2-dev
|
||||||
|
|
||||||
|
|||||||
44
FAQ.md
44
FAQ.md
@@ -1,5 +1,10 @@
|
|||||||
# Frequently Asked Questions
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
## Common issues
|
||||||
|
|
||||||
|
The application is very young, it is not unlikely that you encounter problems
|
||||||
|
with it.
|
||||||
|
|
||||||
Here are the common reported problems and their status.
|
Here are the common reported problems and their status.
|
||||||
|
|
||||||
|
|
||||||
@@ -15,13 +20,9 @@ Windows may need some [drivers] to detect your device.
|
|||||||
[drivers]: https://developer.android.com/studio/run/oem-usb.html
|
[drivers]: https://developer.android.com/studio/run/oem-usb.html
|
||||||
|
|
||||||
|
|
||||||
### I can only mirror, I cannot interact with the device
|
### Mouse clicks do not work
|
||||||
|
|
||||||
On some devices, you may need to enable an option to allow [simulating input].
|
On some devices, you may need to enable an option to allow [simulating input].
|
||||||
In developer options, enable:
|
|
||||||
|
|
||||||
> **USB debugging (Security settings)**
|
|
||||||
> _Allow granting permissions and simulating input via USB debugging_
|
|
||||||
|
|
||||||
[simulating input]: https://github.com/Genymobile/scrcpy/issues/70#issuecomment-373286323
|
[simulating input]: https://github.com/Genymobile/scrcpy/issues/70#issuecomment-373286323
|
||||||
|
|
||||||
@@ -42,16 +43,6 @@ meson x --buildtype release -Dhidpi_support=false
|
|||||||
However, the video will be displayed at lower resolution.
|
However, the video will be displayed at lower resolution.
|
||||||
|
|
||||||
|
|
||||||
### The quality is low on HiDPI display
|
|
||||||
|
|
||||||
On Windows, you may need to configure the [scaling behavior].
|
|
||||||
|
|
||||||
> `scrcpy.exe` > Properties > Compatibility > Change high DPI settings >
|
|
||||||
> Override high DPI scaling behavior > Scaling performed by: _Application_.
|
|
||||||
|
|
||||||
[scaling behavior]: https://github.com/Genymobile/scrcpy/issues/40#issuecomment-424466723
|
|
||||||
|
|
||||||
|
|
||||||
### KWin compositor crashes
|
### KWin compositor crashes
|
||||||
|
|
||||||
On Plasma Desktop, compositor is disabled while _scrcpy_ is running.
|
On Plasma Desktop, compositor is disabled while _scrcpy_ is running.
|
||||||
@@ -59,26 +50,3 @@ On Plasma Desktop, compositor is disabled while _scrcpy_ is running.
|
|||||||
As a workaround, [disable "Block compositing"][kwin].
|
As a workaround, [disable "Block compositing"][kwin].
|
||||||
|
|
||||||
[kwin]: https://github.com/Genymobile/scrcpy/issues/114#issuecomment-378778613
|
[kwin]: https://github.com/Genymobile/scrcpy/issues/114#issuecomment-378778613
|
||||||
|
|
||||||
|
|
||||||
### I get an error "Could not open video stream"
|
|
||||||
|
|
||||||
There may be many reasons. One common cause is that the hardware encoder of your
|
|
||||||
device is not able to encode at the given definition:
|
|
||||||
|
|
||||||
```
|
|
||||||
ERROR: Exception on thread Thread[main,5,main]
|
|
||||||
android.media.MediaCodec$CodecException: Error 0xfffffc0e
|
|
||||||
...
|
|
||||||
Exit due to uncaughtException in main thread:
|
|
||||||
ERROR: Could not open video stream
|
|
||||||
INFO: Initial texture: 1080x2336
|
|
||||||
```
|
|
||||||
|
|
||||||
Just try with a lower definition:
|
|
||||||
|
|
||||||
```
|
|
||||||
scrcpy -m 1920
|
|
||||||
scrcpy -m 1024
|
|
||||||
scrcpy -m 800
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ src = [
|
|||||||
'src/command.c',
|
'src/command.c',
|
||||||
'src/control_msg.c',
|
'src/control_msg.c',
|
||||||
'src/controller.c',
|
'src/controller.c',
|
||||||
|
'src/convert.c',
|
||||||
'src/decoder.c',
|
'src/decoder.c',
|
||||||
'src/device.c',
|
'src/device.c',
|
||||||
'src/device_msg.c',
|
'src/device_msg.c',
|
||||||
'src/event_converter.c',
|
|
||||||
'src/file_handler.c',
|
'src/file_handler.c',
|
||||||
'src/fps_counter.c',
|
'src/fps_counter.c',
|
||||||
'src/input_manager.c',
|
'src/input_manager.c',
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
buffer_write16be(uint8_t *buf, uint16_t value) {
|
buffer_write16be(uint8_t *buf, uint16_t value) {
|
||||||
buf[0] = value >> 8;
|
buf[0] = value >> 8;
|
||||||
@@ -20,12 +18,6 @@ buffer_write32be(uint8_t *buf, uint32_t value) {
|
|||||||
buf[3] = value;
|
buf[3] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
|
||||||
buffer_write64be(uint8_t *buf, uint64_t value) {
|
|
||||||
buffer_write32be(buf, value >> 32);
|
|
||||||
buffer_write32be(&buf[4], (uint32_t) value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint16_t
|
static inline uint16_t
|
||||||
buffer_read16be(const uint8_t *buf) {
|
buffer_read16be(const uint8_t *buf) {
|
||||||
return (buf[0] << 8) | buf[1];
|
return (buf[0] << 8) | buf[1];
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
// To define a circular buffer type of 20 ints:
|
// To define a circular buffer type of 20 ints:
|
||||||
// struct cbuf_int CBUF(int, 20);
|
// struct cbuf_int CBUF(int, 20);
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
|
|||||||
@@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
# define NO_EXIT_CODE -1
|
# define NO_EXIT_CODE -1
|
||||||
|
|
||||||
enum process_result {
|
enum process_result {
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
||||||
#define MIN(X,Y) (X) < (Y) ? (X) : (Y)
|
#define MIN(X,Y) (X) < (Y) ? (X) : (Y)
|
||||||
#define MAX(X,Y) (X) > (Y) ? (X) : (Y)
|
#define MAX(X,Y) (X) > (Y) ? (X) : (Y)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
#include "control_msg.h"
|
#include "control_msg.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "buffer_util.h"
|
#include "buffer_util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
@@ -25,16 +23,6 @@ write_string(const char *utf8, size_t max_len, unsigned char *buf) {
|
|||||||
return 2 + len;
|
return 2 + len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t
|
|
||||||
to_fixed_point_16(float f) {
|
|
||||||
SDL_assert(f >= 0.0f && f <= 1.0f);
|
|
||||||
uint32_t u = f * 0x1p16f; // 2^16
|
|
||||||
if (u >= 0xffff) {
|
|
||||||
u = 0xffff;
|
|
||||||
}
|
|
||||||
return (uint16_t) u;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
||||||
buf[0] = msg->type;
|
buf[0] = msg->type;
|
||||||
@@ -49,15 +37,11 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
|||||||
CONTROL_MSG_TEXT_MAX_LENGTH, &buf[1]);
|
CONTROL_MSG_TEXT_MAX_LENGTH, &buf[1]);
|
||||||
return 1 + len;
|
return 1 + len;
|
||||||
}
|
}
|
||||||
case CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT:
|
case CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT:
|
||||||
buf[1] = msg->inject_touch_event.action;
|
buf[1] = msg->inject_mouse_event.action;
|
||||||
buffer_write64be(&buf[2], msg->inject_touch_event.pointer_id);
|
buffer_write32be(&buf[2], msg->inject_mouse_event.buttons);
|
||||||
write_position(&buf[10], &msg->inject_touch_event.position);
|
write_position(&buf[6], &msg->inject_mouse_event.position);
|
||||||
uint16_t pressure =
|
return 18;
|
||||||
to_fixed_point_16(msg->inject_touch_event.pressure);
|
|
||||||
buffer_write16be(&buf[22], pressure);
|
|
||||||
buffer_write32be(&buf[24], msg->inject_touch_event.buttons);
|
|
||||||
return 28;
|
|
||||||
case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
||||||
write_position(&buf[1], &msg->inject_scroll_event.position);
|
write_position(&buf[1], &msg->inject_scroll_event.position);
|
||||||
buffer_write32be(&buf[13],
|
buffer_write32be(&buf[13],
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "android/input.h"
|
#include "android/input.h"
|
||||||
#include "android/keycodes.h"
|
#include "android/keycodes.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
@@ -15,12 +14,10 @@
|
|||||||
#define CONTROL_MSG_SERIALIZED_MAX_SIZE \
|
#define CONTROL_MSG_SERIALIZED_MAX_SIZE \
|
||||||
(3 + CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH)
|
(3 + CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH)
|
||||||
|
|
||||||
#define POINTER_ID_MOUSE UINT64_C(-1);
|
|
||||||
|
|
||||||
enum control_msg_type {
|
enum control_msg_type {
|
||||||
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||||
CONTROL_MSG_TYPE_INJECT_TEXT,
|
CONTROL_MSG_TYPE_INJECT_TEXT,
|
||||||
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT,
|
||||||
CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||||
CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
||||||
CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
||||||
@@ -50,10 +47,8 @@ struct control_msg {
|
|||||||
struct {
|
struct {
|
||||||
enum android_motionevent_action action;
|
enum android_motionevent_action action;
|
||||||
enum android_motionevent_buttons buttons;
|
enum android_motionevent_buttons buttons;
|
||||||
uint64_t pointer_id;
|
|
||||||
struct position position;
|
struct position position;
|
||||||
float pressure;
|
} inject_mouse_event;
|
||||||
} inject_touch_event;
|
|
||||||
struct {
|
struct {
|
||||||
struct position position;
|
struct position position;
|
||||||
int32_t hscroll;
|
int32_t hscroll;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "cbuf.h"
|
#include "cbuf.h"
|
||||||
#include "control_msg.h"
|
#include "control_msg.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#include "event_converter.h"
|
#include "convert.h"
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#define MAP(FROM, TO) case FROM: *to = TO; return true
|
#define MAP(FROM, TO) case FROM: *to = TO; return true
|
||||||
#define FAIL default: return false
|
#define FAIL default: return false
|
||||||
@@ -33,6 +31,7 @@ autocomplete_metastate(enum android_metastate metastate) {
|
|||||||
return metastate;
|
return metastate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static enum android_metastate
|
static enum android_metastate
|
||||||
convert_meta_state(SDL_Keymod mod) {
|
convert_meta_state(SDL_Keymod mod) {
|
||||||
enum android_metastate metastate = 0;
|
enum android_metastate metastate = 0;
|
||||||
@@ -128,6 +127,15 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to) {
|
||||||
|
switch (from) {
|
||||||
|
MAP(SDL_MOUSEBUTTONDOWN, AMOTION_EVENT_ACTION_DOWN);
|
||||||
|
MAP(SDL_MOUSEBUTTONUP, AMOTION_EVENT_ACTION_UP);
|
||||||
|
FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static enum android_motionevent_buttons
|
static enum android_motionevent_buttons
|
||||||
convert_mouse_buttons(uint32_t state) {
|
convert_mouse_buttons(uint32_t state) {
|
||||||
enum android_motionevent_buttons buttons = 0;
|
enum android_motionevent_buttons buttons = 0;
|
||||||
@@ -150,7 +158,8 @@ convert_mouse_buttons(uint32_t state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to) {
|
input_key_from_sdl_to_android(const SDL_KeyboardEvent *from,
|
||||||
|
struct control_msg *to) {
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
to->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||||
|
|
||||||
if (!convert_keycode_action(from->type, &to->inject_keycode.action)) {
|
if (!convert_keycode_action(from->type, &to->inject_keycode.action)) {
|
||||||
@@ -167,100 +176,45 @@ convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to) {
|
|
||||||
switch (from) {
|
|
||||||
MAP(SDL_MOUSEBUTTONDOWN, AMOTION_EVENT_ACTION_DOWN);
|
|
||||||
MAP(SDL_MOUSEBUTTONUP, AMOTION_EVENT_ACTION_UP);
|
|
||||||
FAIL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from,
|
||||||
struct size frame_size, struct control_msg *to) {
|
struct size screen_size,
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
struct control_msg *to) {
|
||||||
|
to->type = CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT;
|
||||||
|
|
||||||
if (!convert_mouse_action(from->type, &to->inject_touch_event.action)) {
|
if (!convert_mouse_action(from->type, &to->inject_mouse_event.action)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t x = from->x;
|
to->inject_mouse_event.buttons =
|
||||||
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.position.screen_size = frame_size;
|
|
||||||
to->inject_touch_event.position.point.x = x;
|
|
||||||
to->inject_touch_event.position.point.y = y;
|
|
||||||
to->inject_touch_event.pressure = 1.f;
|
|
||||||
to->inject_touch_event.buttons =
|
|
||||||
convert_mouse_buttons(SDL_BUTTON(from->button));
|
convert_mouse_buttons(SDL_BUTTON(from->button));
|
||||||
|
to->inject_mouse_event.position.screen_size = screen_size;
|
||||||
|
to->inject_mouse_event.position.point.x = from->x;
|
||||||
|
to->inject_mouse_event.position.point.y = from->y;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
convert_mouse_motion(const SDL_MouseMotionEvent *from, const SDL_Rect *rect,
|
mouse_motion_from_sdl_to_android(const SDL_MouseMotionEvent *from,
|
||||||
struct size frame_size, struct control_msg *to) {
|
struct size screen_size,
|
||||||
int32_t x = from->x;
|
struct control_msg *to) {
|
||||||
int32_t y = from->y;
|
to->type = CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT;
|
||||||
map_coords(&x, &y, rect, frame_size);
|
to->inject_mouse_event.action = AMOTION_EVENT_ACTION_MOVE;
|
||||||
|
to->inject_mouse_event.buttons = convert_mouse_buttons(from->state);
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
to->inject_mouse_event.position.screen_size = screen_size;
|
||||||
to->inject_touch_event.action = AMOTION_EVENT_ACTION_MOVE;
|
to->inject_mouse_event.position.point.x = from->x;
|
||||||
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
|
to->inject_mouse_event.position.point.y = from->y;
|
||||||
to->inject_touch_event.position.screen_size = frame_size;
|
|
||||||
to->inject_touch_event.position.point.x = x;
|
|
||||||
to->inject_touch_event.position.point.y = y;
|
|
||||||
to->inject_touch_event.pressure = 1.f;
|
|
||||||
to->inject_touch_event.buttons = convert_mouse_buttons(from->state);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
convert_touch_action(SDL_EventType from, enum android_motionevent_action *to) {
|
|
||||||
switch (from) {
|
|
||||||
MAP(SDL_FINGERMOTION, AMOTION_EVENT_ACTION_MOVE);
|
|
||||||
MAP(SDL_FINGERDOWN, AMOTION_EVENT_ACTION_DOWN);
|
|
||||||
MAP(SDL_FINGERUP, AMOTION_EVENT_ACTION_UP);
|
|
||||||
FAIL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
convert_touch(const SDL_TouchFingerEvent *from, struct size screen_size,
|
mouse_wheel_from_sdl_to_android(const SDL_MouseWheelEvent *from,
|
||||||
struct control_msg *to) {
|
struct position position,
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
struct control_msg *to) {
|
||||||
|
|
||||||
if (!convert_touch_action(from->type, &to->inject_touch_event.action)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
to->inject_touch_event.pointer_id = from->fingerId;
|
|
||||||
to->inject_touch_event.position.screen_size = screen_size;
|
|
||||||
// SDL touch event coordinates are normalized in the range [0; 1]
|
|
||||||
to->inject_touch_event.position.point.x = from->x * screen_size.width;
|
|
||||||
to->inject_touch_event.position.point.y = from->y * screen_size.height;
|
|
||||||
to->inject_touch_event.pressure = from->pressure;
|
|
||||||
to->inject_touch_event.buttons = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct position position,
|
|
||||||
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;
|
||||||
41
app/src/convert.h
Normal file
41
app/src/convert.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef CONVERT_H
|
||||||
|
#define CONVERT_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <SDL2/SDL_events.h>
|
||||||
|
|
||||||
|
#include "control_msg.h"
|
||||||
|
|
||||||
|
struct complete_mouse_motion_event {
|
||||||
|
SDL_MouseMotionEvent *mouse_motion_event;
|
||||||
|
struct size screen_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct complete_mouse_wheel_event {
|
||||||
|
SDL_MouseWheelEvent *mouse_wheel_event;
|
||||||
|
struct point position;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool
|
||||||
|
input_key_from_sdl_to_android(const SDL_KeyboardEvent *from,
|
||||||
|
struct control_msg *to);
|
||||||
|
|
||||||
|
bool
|
||||||
|
mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from,
|
||||||
|
struct size screen_size,
|
||||||
|
struct control_msg *to);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
bool
|
||||||
|
mouse_motion_from_sdl_to_android(const SDL_MouseMotionEvent *from,
|
||||||
|
struct size screen_size,
|
||||||
|
struct control_msg *to);
|
||||||
|
|
||||||
|
// on Android, a scroll event requires the current mouse position
|
||||||
|
bool
|
||||||
|
mouse_wheel_from_sdl_to_android(const SDL_MouseWheelEvent *from,
|
||||||
|
struct position position,
|
||||||
|
struct control_msg *to);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -8,8 +8,8 @@
|
|||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
#include "config.h"
|
||||||
#include "buffer_util.h"
|
#include "buffer_util.h"
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
#include "lock_util.h"
|
#include "lock_util.h"
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
struct video_buffer;
|
struct video_buffer;
|
||||||
|
|
||||||
struct decoder {
|
struct decoder {
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#include "device.h"
|
#include "device.h"
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <SDL2/SDL_assert.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "buffer_util.h"
|
#include "buffer_util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#define DEVICE_MSG_TEXT_MAX_LENGTH 4093
|
#define DEVICE_MSG_TEXT_MAX_LENGTH 4093
|
||||||
#define DEVICE_MSG_SERIALIZED_MAX_SIZE (3 + DEVICE_MSG_TEXT_MAX_LENGTH)
|
#define DEVICE_MSG_SERIALIZED_MAX_SIZE (3 + DEVICE_MSG_TEXT_MAX_LENGTH)
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
#ifndef CONVERT_H
|
|
||||||
#define CONVERT_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <SDL2/SDL_events.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "control_msg.h"
|
|
||||||
|
|
||||||
struct complete_mouse_motion_event {
|
|
||||||
SDL_MouseMotionEvent *mouse_motion_event;
|
|
||||||
struct size screen_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct complete_mouse_wheel_event {
|
|
||||||
SDL_MouseWheelEvent *mouse_wheel_event;
|
|
||||||
struct point position;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool
|
|
||||||
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to);
|
|
||||||
|
|
||||||
bool
|
|
||||||
convert_mouse_button(const SDL_MouseButtonEvent *from, const SDL_Rect *rect,
|
|
||||||
struct size frame_size, struct control_msg *to);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
bool
|
|
||||||
convert_mouse_motion(const SDL_MouseMotionEvent *from, const SDL_Rect *rect,
|
|
||||||
struct size frame_size, struct control_msg *to);
|
|
||||||
|
|
||||||
bool
|
|
||||||
convert_touch(const SDL_TouchFingerEvent *from, struct size screen_size,
|
|
||||||
struct control_msg *to);
|
|
||||||
|
|
||||||
// on Android, a scroll event requires the current mouse position
|
|
||||||
bool
|
|
||||||
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct position position,
|
|
||||||
struct control_msg *to);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "cbuf.h"
|
#include "cbuf.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <SDL2/SDL_assert.h>
|
#include <SDL2/SDL_assert.h>
|
||||||
#include <SDL2/SDL_timer.h>
|
#include <SDL2/SDL_timer.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "lock_util.h"
|
#include "lock_util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
struct fps_counter {
|
struct fps_counter {
|
||||||
SDL_Thread *thread;
|
SDL_Thread *thread;
|
||||||
SDL_mutex *mutex;
|
SDL_mutex *mutex;
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
|
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <SDL2/SDL_assert.h>
|
||||||
|
#include "convert.h"
|
||||||
#include "config.h"
|
|
||||||
#include "event_converter.h"
|
|
||||||
#include "lock_util.h"
|
#include "lock_util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
@@ -104,7 +102,7 @@ press_back_or_turn_screen_on(struct controller *controller) {
|
|||||||
msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
|
msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request 'press back or turn screen on'");
|
LOGW("Could not request 'turn screen on'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +373,7 @@ input_manager_process_key(struct input_manager *input_manager,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
if (convert_input_key(event, &msg)) {
|
if (input_key_from_sdl_to_android(event, &msg)) {
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request 'inject keycode'");
|
LOGW("Could not request 'inject keycode'");
|
||||||
}
|
}
|
||||||
@@ -389,29 +387,16 @@ input_manager_process_mouse_motion(struct input_manager *input_manager,
|
|||||||
// do not send motion events when no button is pressed
|
// do not send motion events when no button is pressed
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event->which == SDL_TOUCH_MOUSEID) {
|
|
||||||
// simulated from touch events, so it's a duplicate
|
|
||||||
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 (mouse_motion_from_sdl_to_android(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'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
input_manager_process_touch(struct input_manager *input_manager,
|
|
||||||
const SDL_TouchFingerEvent *event) {
|
|
||||||
struct control_msg msg;
|
|
||||||
if (convert_touch(event, input_manager->screen->frame_size, &msg)) {
|
|
||||||
if (!controller_push_msg(input_manager->controller, &msg)) {
|
|
||||||
LOGW("Could not request 'inject touch event'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_outside_device_screen(struct input_manager *input_manager, int x, int y)
|
is_outside_device_screen(struct input_manager *input_manager, int x, int y)
|
||||||
{
|
{
|
||||||
@@ -423,10 +408,6 @@ void
|
|||||||
input_manager_process_mouse_button(struct input_manager *input_manager,
|
input_manager_process_mouse_button(struct input_manager *input_manager,
|
||||||
const SDL_MouseButtonEvent *event,
|
const SDL_MouseButtonEvent *event,
|
||||||
bool control) {
|
bool control) {
|
||||||
if (event->which == SDL_TOUCH_MOUSEID) {
|
|
||||||
// simulated from touch events, so it's a duplicate
|
|
||||||
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);
|
||||||
@@ -453,7 +434,9 @@ 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 (mouse_button_from_sdl_to_android(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'");
|
||||||
}
|
}
|
||||||
@@ -468,7 +451,7 @@ input_manager_process_mouse_wheel(struct input_manager *input_manager,
|
|||||||
.point = get_mouse_point(input_manager->screen),
|
.point = get_mouse_point(input_manager->screen),
|
||||||
};
|
};
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
if (convert_mouse_wheel(event, position, &msg)) {
|
if (mouse_wheel_from_sdl_to_android(event, position, &msg)) {
|
||||||
if (!controller_push_msg(input_manager->controller, &msg)) {
|
if (!controller_push_msg(input_manager->controller, &msg)) {
|
||||||
LOGW("Could not request 'inject mouse wheel event'");
|
LOGW("Could not request 'inject mouse wheel event'");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "fps_counter.h"
|
#include "fps_counter.h"
|
||||||
@@ -29,10 +28,6 @@ void
|
|||||||
input_manager_process_mouse_motion(struct input_manager *input_manager,
|
input_manager_process_mouse_motion(struct input_manager *input_manager,
|
||||||
const SDL_MouseMotionEvent *event);
|
const SDL_MouseMotionEvent *event);
|
||||||
|
|
||||||
void
|
|
||||||
input_manager_process_touch(struct input_manager *input_manager,
|
|
||||||
const SDL_TouchFingerEvent *event);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
input_manager_process_mouse_button(struct input_manager *input_manager,
|
input_manager_process_mouse_button(struct input_manager *input_manager,
|
||||||
const SDL_MouseButtonEvent *event,
|
const SDL_MouseButtonEvent *event,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
#define SDL_MAIN_HANDLED // avoid link error on Linux Windows Subsystem
|
#define SDL_MAIN_HANDLED // avoid link error on Linux Windows Subsystem
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
#include "config.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "recorder.h"
|
#include "recorder.h"
|
||||||
|
|
||||||
@@ -328,7 +328,7 @@ parse_args(struct args *args, int argc, char *argv[]) {
|
|||||||
{"push-target", required_argument, NULL,
|
{"push-target", required_argument, NULL,
|
||||||
OPT_PUSH_TARGET},
|
OPT_PUSH_TARGET},
|
||||||
{"record", required_argument, NULL, 'r'},
|
{"record", required_argument, NULL, 'r'},
|
||||||
{"record-format", required_argument, NULL, 'F'},
|
{"record-format", required_argument, NULL, 'f'},
|
||||||
{"render-expired-frames", no_argument, NULL,
|
{"render-expired-frames", no_argument, NULL,
|
||||||
OPT_RENDER_EXPIRED_FRAMES},
|
OPT_RENDER_EXPIRED_FRAMES},
|
||||||
{"serial", required_argument, NULL, 's'},
|
{"serial", required_argument, NULL, 's'},
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#ifdef __WINDOWS__
|
#ifdef __WINDOWS__
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
typedef int socket_t;
|
typedef int socket_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
net_init(void);
|
net_init(void);
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <SDL2/SDL_assert.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
// To define a queue type of "struct foo":
|
// To define a queue type of "struct foo":
|
||||||
// struct queue_foo QUEUE(struct foo);
|
// struct queue_foo QUEUE(struct foo);
|
||||||
#define QUEUE(TYPE) { \
|
#define QUEUE(TYPE) { \
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
// receive events from the device
|
// receive events from the device
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
#include <libavutil/time.h>
|
#include <libavutil/time.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <SDL2/SDL_assert.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
#include "config.h"
|
||||||
#include "lock_util.h"
|
#include "lock_util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
@@ -33,11 +33,6 @@ record_packet_new(const AVPacket *packet) {
|
|||||||
if (!rec) {
|
if (!rec) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// av_packet_ref() does not initialize all fields in old FFmpeg versions
|
|
||||||
// See <https://github.com/Genymobile/scrcpy/issues/707>
|
|
||||||
av_init_packet(&rec->packet);
|
|
||||||
|
|
||||||
if (av_packet_ref(&rec->packet, packet)) {
|
if (av_packet_ref(&rec->packet, packet)) {
|
||||||
SDL_free(rec);
|
SDL_free(rec);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -92,7 +87,6 @@ recorder_init(struct recorder *recorder,
|
|||||||
recorder->format = format;
|
recorder->format = format;
|
||||||
recorder->declared_frame_size = declared_frame_size;
|
recorder->declared_frame_size = declared_frame_size;
|
||||||
recorder->header_written = false;
|
recorder->header_written = false;
|
||||||
recorder->previous = NULL;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -225,22 +219,13 @@ recorder_rescale_packet(struct recorder *recorder, AVPacket *packet) {
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
recorder_write(struct recorder *recorder, AVPacket *packet) {
|
recorder_write(struct recorder *recorder, AVPacket *packet) {
|
||||||
|
SDL_assert(packet->pts != AV_NOPTS_VALUE);
|
||||||
if (!recorder->header_written) {
|
if (!recorder->header_written) {
|
||||||
if (packet->pts != AV_NOPTS_VALUE) {
|
|
||||||
LOGE("The first packet is not a config packet");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool ok = recorder_write_header(recorder, packet);
|
bool ok = recorder_write_header(recorder, packet);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
recorder->header_written = true;
|
recorder->header_written = true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet->pts == AV_NOPTS_VALUE) {
|
|
||||||
// ignore config packets
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
recorder_rescale_packet(recorder, packet);
|
recorder_rescale_packet(recorder, packet);
|
||||||
@@ -263,19 +248,6 @@ run_recorder(void *data) {
|
|||||||
|
|
||||||
if (recorder->stopped && queue_is_empty(&recorder->queue)) {
|
if (recorder->stopped && queue_is_empty(&recorder->queue)) {
|
||||||
mutex_unlock(recorder->mutex);
|
mutex_unlock(recorder->mutex);
|
||||||
struct record_packet *last = recorder->previous;
|
|
||||||
if (last) {
|
|
||||||
// assign an arbitrary duration to the last packet
|
|
||||||
last->packet.duration = 100000;
|
|
||||||
bool ok = recorder_write(recorder, &last->packet);
|
|
||||||
if (!ok) {
|
|
||||||
// failing to write the last frame is not very serious, no
|
|
||||||
// future frame may depend on it, so the resulting file
|
|
||||||
// will still be valid
|
|
||||||
LOGW("Could not record last packet");
|
|
||||||
}
|
|
||||||
record_packet_delete(last);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,20 +256,8 @@ run_recorder(void *data) {
|
|||||||
|
|
||||||
mutex_unlock(recorder->mutex);
|
mutex_unlock(recorder->mutex);
|
||||||
|
|
||||||
// recorder->previous is only written from this thread, no need to lock
|
bool ok = recorder_write(recorder, &rec->packet);
|
||||||
struct record_packet *previous = recorder->previous;
|
record_packet_delete(rec);
|
||||||
recorder->previous = rec;
|
|
||||||
|
|
||||||
if (!previous) {
|
|
||||||
// we just received the first packet
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we now know the duration of the previous packet
|
|
||||||
previous->packet.duration = rec->packet.pts - previous->packet.pts;
|
|
||||||
|
|
||||||
bool ok = recorder_write(recorder, &previous->packet);
|
|
||||||
record_packet_delete(previous);
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGE("Could not record packet");
|
LOGE("Could not record packet");
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
|
|
||||||
@@ -35,12 +34,6 @@ struct recorder {
|
|||||||
bool stopped; // set on recorder_stop() by the stream reader
|
bool stopped; // set on recorder_stop() by the stream reader
|
||||||
bool failed; // set on packet write failure
|
bool failed; // set on packet write failure
|
||||||
struct recorder_queue queue;
|
struct recorder_queue queue;
|
||||||
|
|
||||||
// we can write a packet only once we received the next one so that we can
|
|
||||||
// set its duration (next_pts - current_pts)
|
|
||||||
// "previous" is only accessed from the recorder thread, so it does not
|
|
||||||
// need to be protected by the mutex
|
|
||||||
struct record_packet *previous;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
@@ -145,10 +144,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;
|
||||||
@@ -183,11 +180,6 @@ handle_event(SDL_Event *event, bool control) {
|
|||||||
input_manager_process_mouse_button(&input_manager, &event->button,
|
input_manager_process_mouse_button(&input_manager, &event->button,
|
||||||
control);
|
control);
|
||||||
break;
|
break;
|
||||||
case SDL_FINGERMOTION:
|
|
||||||
case SDL_FINGERDOWN:
|
|
||||||
case SDL_FINGERUP:
|
|
||||||
input_manager_process_touch(&input_manager, &event->tfinger);
|
|
||||||
break;
|
|
||||||
case SDL_DROPFILE: {
|
case SDL_DROPFILE: {
|
||||||
if (!control) {
|
if (!control) {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <recorder.h>
|
#include <recorder.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
struct scrcpy_options {
|
struct scrcpy_options {
|
||||||
const char *serial;
|
const char *serial;
|
||||||
const char *crop;
|
const char *crop;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "icon.xpm"
|
#include "icon.xpm"
|
||||||
@@ -122,32 +121,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 +169,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 +219,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 +270,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 +277,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
struct video_buffer;
|
struct video_buffer;
|
||||||
@@ -17,7 +16,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,15 +33,9 @@ struct screen {
|
|||||||
.width = 0, \
|
.width = 0, \
|
||||||
.height = 0, \
|
.height = 0, \
|
||||||
}, \
|
}, \
|
||||||
.rect = { \
|
.has_frame = false, \
|
||||||
.x = 0, \
|
.fullscreen = false, \
|
||||||
.y = 0, \
|
.no_window = false, \
|
||||||
.w = 0, \
|
|
||||||
.h = 0, \
|
|
||||||
}, \
|
|
||||||
.has_frame = false, \
|
|
||||||
.fullscreen = false, \
|
|
||||||
.no_window = false, \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize default values
|
// initialize default values
|
||||||
@@ -67,9 +59,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);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
#include <SDL2/SDL_stdinc.h>
|
#include <SDL2/SDL_stdinc.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
xstrncpy(char *dest, const char *src, size_t n) {
|
xstrncpy(char *dest, const char *src, size_t n) {
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
// like strncpy, except:
|
// like strncpy, except:
|
||||||
// - it copies at most n-1 chars
|
// - it copies at most n-1 chars
|
||||||
// - the dest string is nul-terminated
|
// - the dest string is nul-terminated
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
#include "config.h"
|
||||||
#include "buffer_util.h"
|
#include "buffer_util.h"
|
||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
@@ -69,15 +69,6 @@ notify_stopped(void) {
|
|||||||
SDL_PushEvent(&stop_event);
|
SDL_PushEvent(&stop_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
process_config_packet(struct stream *stream, AVPacket *packet) {
|
|
||||||
if (stream->recorder && !recorder_push(stream->recorder, packet)) {
|
|
||||||
LOGE("Could not send config packet to recorder");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
process_frame(struct stream *stream, AVPacket *packet) {
|
process_frame(struct stream *stream, AVPacket *packet) {
|
||||||
if (stream->decoder && !decoder_push(stream->decoder, packet)) {
|
if (stream->decoder && !decoder_push(stream->decoder, packet)) {
|
||||||
@@ -157,13 +148,7 @@ stream_push_packet(struct stream *stream, AVPacket *packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_config) {
|
if (!is_config) {
|
||||||
// config packet
|
|
||||||
bool ok = process_config_packet(stream, packet);
|
|
||||||
if (!ok) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// data packet
|
// data packet
|
||||||
bool ok = stream_parse(stream, packet);
|
bool ok = stream_parse(stream, packet);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <SDL2/SDL_atomic.h>
|
#include <SDL2/SDL_atomic.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
struct video_buffer;
|
struct video_buffer;
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
net_init(void) {
|
net_init(void) {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
struct index {
|
struct index {
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
SDL_Surface *
|
SDL_Surface *
|
||||||
read_xpm(char *xpm[]);
|
read_xpm(char *xpm[]);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "fps_counter.h"
|
#include "fps_counter.h"
|
||||||
|
|
||||||
// forward declarations
|
// forward declarations
|
||||||
|
|||||||
@@ -67,39 +67,35 @@ static void test_serialize_inject_text_long(void) {
|
|||||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_serialize_inject_touch_event(void) {
|
static void test_serialize_inject_mouse_event(void) {
|
||||||
struct control_msg msg = {
|
struct control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
.type = CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT,
|
||||||
.inject_touch_event = {
|
.inject_mouse_event = {
|
||||||
.action = AMOTION_EVENT_ACTION_DOWN,
|
.action = AMOTION_EVENT_ACTION_DOWN,
|
||||||
.pointer_id = 0x1234567887654321L,
|
.buttons = AMOTION_EVENT_BUTTON_PRIMARY,
|
||||||
.position = {
|
.position = {
|
||||||
.point = {
|
.point = {
|
||||||
.x = 100,
|
.x = 260,
|
||||||
.y = 200,
|
.y = 1026,
|
||||||
},
|
},
|
||||||
.screen_size = {
|
.screen_size = {
|
||||||
.width = 1080,
|
.width = 1080,
|
||||||
.height = 1920,
|
.height = 1920,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.pressure = 1.0f,
|
|
||||||
.buttons = AMOTION_EVENT_BUTTON_PRIMARY,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
|
||||||
int size = control_msg_serialize(&msg, buf);
|
int size = control_msg_serialize(&msg, buf);
|
||||||
assert(size == 28);
|
assert(size == 18);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT,
|
||||||
0x00, // AKEY_EVENT_ACTION_DOWN
|
0x00, // AKEY_EVENT_ACTION_DOWN
|
||||||
0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21, // pointer id
|
0x00, 0x00, 0x00, 0x01, // AMOTION_EVENT_BUTTON_PRIMARY
|
||||||
0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0xc8, // 100 200
|
0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x04, 0x02, // 260 1026
|
||||||
0x04, 0x38, 0x07, 0x80, // 1080 1920
|
0x04, 0x38, 0x07, 0x80, // 1080 1920
|
||||||
0xff, 0xff, // pressure
|
|
||||||
0x00, 0x00, 0x00, 0x01 // AMOTION_EVENT_BUTTON_PRIMARY
|
|
||||||
};
|
};
|
||||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
}
|
}
|
||||||
@@ -240,7 +236,7 @@ int main(void) {
|
|||||||
test_serialize_inject_keycode();
|
test_serialize_inject_keycode();
|
||||||
test_serialize_inject_text();
|
test_serialize_inject_text();
|
||||||
test_serialize_inject_text_long();
|
test_serialize_inject_text_long();
|
||||||
test_serialize_inject_touch_event();
|
test_serialize_inject_mouse_event();
|
||||||
test_serialize_inject_scroll_event();
|
test_serialize_inject_scroll_event();
|
||||||
test_serialize_back_or_screen_on();
|
test_serialize_back_or_screen_on();
|
||||||
test_serialize_expand_notification_panel();
|
test_serialize_expand_notification_panel();
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ if prebuilt_server == ''
|
|||||||
build_always: true, # gradle is responsible for tracking source changes
|
build_always: true, # gradle is responsible for tracking source changes
|
||||||
output: 'scrcpy-server.jar',
|
output: 'scrcpy-server.jar',
|
||||||
command: [find_program('./scripts/build-wrapper.sh'), meson.current_source_dir(), '@OUTPUT@', get_option('buildtype')],
|
command: [find_program('./scripts/build-wrapper.sh'), meson.current_source_dir(), '@OUTPUT@', get_option('buildtype')],
|
||||||
console: true,
|
|
||||||
install: true,
|
install: true,
|
||||||
install_dir: 'share/scrcpy')
|
install_dir: 'share/scrcpy')
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ public final class ControlMessage {
|
|||||||
|
|
||||||
public static final int TYPE_INJECT_KEYCODE = 0;
|
public static final int TYPE_INJECT_KEYCODE = 0;
|
||||||
public static final int TYPE_INJECT_TEXT = 1;
|
public static final int TYPE_INJECT_TEXT = 1;
|
||||||
public static final int TYPE_INJECT_TOUCH_EVENT = 2;
|
public static final int TYPE_INJECT_MOUSE_EVENT = 2;
|
||||||
public static final int TYPE_INJECT_SCROLL_EVENT = 3;
|
public static final int TYPE_INJECT_SCROLL_EVENT = 3;
|
||||||
public static final int TYPE_BACK_OR_SCREEN_ON = 4;
|
public static final int TYPE_BACK_OR_SCREEN_ON = 4;
|
||||||
public static final int TYPE_EXPAND_NOTIFICATION_PANEL = 5;
|
public static final int TYPE_EXPAND_NOTIFICATION_PANEL = 5;
|
||||||
@@ -22,8 +22,6 @@ public final class ControlMessage {
|
|||||||
private int action; // KeyEvent.ACTION_* or MotionEvent.ACTION_* or POWER_MODE_*
|
private int action; // KeyEvent.ACTION_* or MotionEvent.ACTION_* or POWER_MODE_*
|
||||||
private int keycode; // KeyEvent.KEYCODE_*
|
private int keycode; // KeyEvent.KEYCODE_*
|
||||||
private int buttons; // MotionEvent.BUTTON_*
|
private int buttons; // MotionEvent.BUTTON_*
|
||||||
private long pointerId;
|
|
||||||
private float pressure;
|
|
||||||
private Position position;
|
private Position position;
|
||||||
private int hScroll;
|
private int hScroll;
|
||||||
private int vScroll;
|
private int vScroll;
|
||||||
@@ -32,63 +30,60 @@ public final class ControlMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createInjectKeycode(int action, int keycode, int metaState) {
|
public static ControlMessage createInjectKeycode(int action, int keycode, int metaState) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = TYPE_INJECT_KEYCODE;
|
event.type = TYPE_INJECT_KEYCODE;
|
||||||
msg.action = action;
|
event.action = action;
|
||||||
msg.keycode = keycode;
|
event.keycode = keycode;
|
||||||
msg.metaState = metaState;
|
event.metaState = metaState;
|
||||||
return msg;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createInjectText(String text) {
|
public static ControlMessage createInjectText(String text) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = TYPE_INJECT_TEXT;
|
event.type = TYPE_INJECT_TEXT;
|
||||||
msg.text = text;
|
event.text = text;
|
||||||
return msg;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createInjectTouchEvent(int action, long pointerId, Position position, float pressure,
|
public static ControlMessage createInjectMouseEvent(int action, int buttons, Position position) {
|
||||||
int buttons) {
|
ControlMessage event = new ControlMessage();
|
||||||
ControlMessage msg = new ControlMessage();
|
event.type = TYPE_INJECT_MOUSE_EVENT;
|
||||||
msg.type = TYPE_INJECT_TOUCH_EVENT;
|
event.action = action;
|
||||||
msg.action = action;
|
event.buttons = buttons;
|
||||||
msg.pointerId = pointerId;
|
event.position = position;
|
||||||
msg.pressure = pressure;
|
return event;
|
||||||
msg.position = position;
|
|
||||||
msg.buttons = buttons;
|
|
||||||
return msg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll) {
|
public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = TYPE_INJECT_SCROLL_EVENT;
|
event.type = TYPE_INJECT_SCROLL_EVENT;
|
||||||
msg.position = position;
|
event.position = position;
|
||||||
msg.hScroll = hScroll;
|
event.hScroll = hScroll;
|
||||||
msg.vScroll = vScroll;
|
event.vScroll = vScroll;
|
||||||
return msg;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createSetClipboard(String text) {
|
public static ControlMessage createSetClipboard(String text) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = TYPE_SET_CLIPBOARD;
|
event.type = TYPE_SET_CLIPBOARD;
|
||||||
msg.text = text;
|
event.text = text;
|
||||||
return msg;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mode one of the {@code Device.SCREEN_POWER_MODE_*} constants
|
* @param mode one of the {@code Device.SCREEN_POWER_MODE_*} constants
|
||||||
*/
|
*/
|
||||||
public static ControlMessage createSetScreenPowerMode(int mode) {
|
public static ControlMessage createSetScreenPowerMode(int mode) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = TYPE_SET_SCREEN_POWER_MODE;
|
event.type = TYPE_SET_SCREEN_POWER_MODE;
|
||||||
msg.action = mode;
|
event.action = mode;
|
||||||
return msg;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createEmpty(int type) {
|
public static ControlMessage createEmpty(int type) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = type;
|
event.type = type;
|
||||||
return msg;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getType() {
|
public int getType() {
|
||||||
@@ -115,14 +110,6 @@ public final class ControlMessage {
|
|||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getPointerId() {
|
|
||||||
return pointerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getPressure() {
|
|
||||||
return pressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Position getPosition() {
|
public Position getPosition() {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ public class ControlMessageReader {
|
|||||||
|
|
||||||
private static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 9;
|
private static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 9;
|
||||||
private static final int INJECT_MOUSE_EVENT_PAYLOAD_LENGTH = 17;
|
private static final int INJECT_MOUSE_EVENT_PAYLOAD_LENGTH = 17;
|
||||||
private static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 21;
|
|
||||||
private static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
private static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
||||||
private static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
private static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
||||||
|
|
||||||
@@ -60,8 +59,8 @@ public class ControlMessageReader {
|
|||||||
case ControlMessage.TYPE_INJECT_TEXT:
|
case ControlMessage.TYPE_INJECT_TEXT:
|
||||||
msg = parseInjectText();
|
msg = parseInjectText();
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
case ControlMessage.TYPE_INJECT_MOUSE_EVENT:
|
||||||
msg = parseInjectTouchEvent();
|
msg = parseInjectMouseEvent();
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||||
msg = parseInjectScrollEvent();
|
msg = parseInjectScrollEvent();
|
||||||
@@ -121,20 +120,14 @@ public class ControlMessageReader {
|
|||||||
return ControlMessage.createInjectText(text);
|
return ControlMessage.createInjectText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("checkstyle:MagicNumber")
|
private ControlMessage parseInjectMouseEvent() {
|
||||||
private ControlMessage parseInjectTouchEvent() {
|
if (buffer.remaining() < INJECT_MOUSE_EVENT_PAYLOAD_LENGTH) {
|
||||||
if (buffer.remaining() < INJECT_TOUCH_EVENT_PAYLOAD_LENGTH) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int action = toUnsigned(buffer.get());
|
int action = toUnsigned(buffer.get());
|
||||||
long pointerId = buffer.getLong();
|
|
||||||
Position position = readPosition(buffer);
|
|
||||||
// 16 bits fixed-point
|
|
||||||
int pressureInt = toUnsigned(buffer.getShort());
|
|
||||||
// convert it to a float between 0 and 1 (0x1p16f is 2^16 as float)
|
|
||||||
float pressure = pressureInt == 0xffff ? 1f : (pressureInt / 0x1p16f);
|
|
||||||
int buttons = buffer.getInt();
|
int buttons = buffer.getInt();
|
||||||
return ControlMessage.createInjectTouchEvent(action, pointerId, position, pressure, buttons);
|
Position position = readPosition(buffer);
|
||||||
|
return ControlMessage.createInjectMouseEvent(action, buttons, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ControlMessage parseInjectScrollEvent() {
|
private ControlMessage parseInjectScrollEvent() {
|
||||||
|
|||||||
@@ -19,32 +19,38 @@ public class Controller {
|
|||||||
|
|
||||||
private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
|
private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
|
||||||
|
|
||||||
private long lastTouchDown;
|
private long lastMouseDown;
|
||||||
private final PointersState pointersState = new PointersState();
|
private final MotionEvent.PointerProperties[] pointerProperties = {new MotionEvent.PointerProperties()};
|
||||||
private final MotionEvent.PointerProperties[] pointerProperties =
|
private final MotionEvent.PointerCoords[] pointerCoords = {new MotionEvent.PointerCoords()};
|
||||||
new MotionEvent.PointerProperties[PointersState.MAX_POINTERS];
|
|
||||||
private final MotionEvent.PointerCoords[] pointerCoords =
|
|
||||||
new MotionEvent.PointerCoords[PointersState.MAX_POINTERS];
|
|
||||||
|
|
||||||
public Controller(Device device, DesktopConnection connection) {
|
public Controller(Device device, DesktopConnection connection) {
|
||||||
this.device = device;
|
this.device = device;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
initPointers();
|
initPointer();
|
||||||
sender = new DeviceMessageSender(connection);
|
sender = new DeviceMessageSender(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initPointers() {
|
private void initPointer() {
|
||||||
for (int i = 0; i < PointersState.MAX_POINTERS; ++i) {
|
MotionEvent.PointerProperties props = pointerProperties[0];
|
||||||
MotionEvent.PointerProperties props = new MotionEvent.PointerProperties();
|
props.id = 0;
|
||||||
props.toolType = MotionEvent.TOOL_TYPE_FINGER;
|
props.toolType = MotionEvent.TOOL_TYPE_FINGER;
|
||||||
|
|
||||||
MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
|
MotionEvent.PointerCoords coords = pointerCoords[0];
|
||||||
coords.orientation = 0;
|
coords.orientation = 0;
|
||||||
coords.size = 1;
|
coords.pressure = 1;
|
||||||
|
coords.size = 1;
|
||||||
|
}
|
||||||
|
|
||||||
pointerProperties[i] = props;
|
private void setPointerCoords(Point point) {
|
||||||
pointerCoords[i] = coords;
|
MotionEvent.PointerCoords coords = pointerCoords[0];
|
||||||
}
|
coords.x = point.getX();
|
||||||
|
coords.y = point.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setScroll(int hScroll, int vScroll) {
|
||||||
|
MotionEvent.PointerCoords coords = pointerCoords[0];
|
||||||
|
coords.setAxisValue(MotionEvent.AXIS_HSCROLL, hScroll);
|
||||||
|
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("checkstyle:MagicNumber")
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
@@ -81,8 +87,8 @@ public class Controller {
|
|||||||
case ControlMessage.TYPE_INJECT_TEXT:
|
case ControlMessage.TYPE_INJECT_TEXT:
|
||||||
injectText(msg.getText());
|
injectText(msg.getText());
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
case ControlMessage.TYPE_INJECT_MOUSE_EVENT:
|
||||||
injectTouch(msg.getAction(), msg.getPointerId(), msg.getPosition(), msg.getPressure(), 0);
|
injectMouse(msg.getAction(), msg.getButtons(), msg.getPosition());
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||||
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
||||||
@@ -142,42 +148,19 @@ public class Controller {
|
|||||||
return successCount;
|
return successCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean injectTouch(int action, long pointerId, Position position, float pressure, int buttons) {
|
private boolean injectMouse(int action, int buttons, Position position) {
|
||||||
long now = SystemClock.uptimeMillis();
|
long now = SystemClock.uptimeMillis();
|
||||||
|
if (action == MotionEvent.ACTION_DOWN) {
|
||||||
|
lastMouseDown = now;
|
||||||
|
}
|
||||||
Point point = device.getPhysicalPoint(position);
|
Point point = device.getPhysicalPoint(position);
|
||||||
if (point == null) {
|
if (point == null) {
|
||||||
// ignore event
|
// ignore event
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
setPointerCoords(point);
|
||||||
int pointerIndex = pointersState.getPointerIndex(pointerId);
|
MotionEvent event = MotionEvent.obtain(lastMouseDown, now, action, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, 0, 0,
|
||||||
if (pointerIndex == -1) {
|
InputDevice.SOURCE_TOUCHSCREEN, 0);
|
||||||
Ln.w("Too many pointers for touch event");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Pointer pointer = pointersState.get(pointerIndex);
|
|
||||||
pointer.setPoint(point);
|
|
||||||
pointer.setPressure(pressure);
|
|
||||||
pointer.setUp(action == MotionEvent.ACTION_UP);
|
|
||||||
|
|
||||||
int pointerCount = pointersState.update(pointerProperties, pointerCoords);
|
|
||||||
|
|
||||||
if (pointerCount == 1) {
|
|
||||||
if (action == MotionEvent.ACTION_DOWN) {
|
|
||||||
lastTouchDown = now;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// secondary pointers must use ACTION_POINTER_* ORed with the pointerIndex
|
|
||||||
if (action == MotionEvent.ACTION_UP) {
|
|
||||||
action = MotionEvent.ACTION_POINTER_UP | (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
|
|
||||||
} else if (action == MotionEvent.ACTION_DOWN) {
|
|
||||||
action = MotionEvent.ACTION_POINTER_DOWN | (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties,
|
|
||||||
pointerCoords, 0, 0, 1f, 1f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
|
|
||||||
return injectEvent(event);
|
return injectEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,30 +171,23 @@ public class Controller {
|
|||||||
// ignore event
|
// ignore event
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
setPointerCoords(point);
|
||||||
MotionEvent.PointerProperties props = pointerProperties[0];
|
setScroll(hScroll, vScroll);
|
||||||
props.id = 0;
|
MotionEvent event = MotionEvent.obtain(lastMouseDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, 0,
|
||||||
|
0, InputDevice.SOURCE_MOUSE, 0);
|
||||||
MotionEvent.PointerCoords coords = pointerCoords[0];
|
|
||||||
coords.x = point.getX();
|
|
||||||
coords.y = point.getY();
|
|
||||||
coords.setAxisValue(MotionEvent.AXIS_HSCROLL, hScroll);
|
|
||||||
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
|
|
||||||
|
|
||||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties,
|
|
||||||
pointerCoords, 0, 0, 1f, 1f, 0, 0, InputDevice.SOURCE_MOUSE, 0);
|
|
||||||
return injectEvent(event);
|
return injectEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState) {
|
private boolean injectKeyEvent(int action, int keyCode, int repeat, int metaState) {
|
||||||
long now = SystemClock.uptimeMillis();
|
long now = SystemClock.uptimeMillis();
|
||||||
KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD,
|
KeyEvent event = new KeyEvent(now, now, action, keyCode, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
|
||||||
0, 0, InputDevice.SOURCE_KEYBOARD);
|
InputDevice.SOURCE_KEYBOARD);
|
||||||
return injectEvent(event);
|
return injectEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean injectKeycode(int keyCode) {
|
private boolean injectKeycode(int keyCode) {
|
||||||
return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0) && injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0);
|
return injectKeyEvent(KeyEvent.ACTION_DOWN, keyCode, 0, 0)
|
||||||
|
&& injectKeyEvent(KeyEvent.ACTION_UP, keyCode, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean injectEvent(InputEvent event) {
|
private boolean injectEvent(InputEvent event) {
|
||||||
|
|||||||
@@ -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,55 +0,0 @@
|
|||||||
package com.genymobile.scrcpy;
|
|
||||||
|
|
||||||
public class Pointer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pointer id as received from the client.
|
|
||||||
*/
|
|
||||||
private final long id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local pointer id, using the lowest possible values to fill the {@link android.view.MotionEvent.PointerProperties PointerProperties}.
|
|
||||||
*/
|
|
||||||
private final int localId;
|
|
||||||
|
|
||||||
private Point point;
|
|
||||||
private float pressure;
|
|
||||||
private boolean up;
|
|
||||||
|
|
||||||
public Pointer(long id, int localId) {
|
|
||||||
this.id = id;
|
|
||||||
this.localId = localId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLocalId() {
|
|
||||||
return localId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Point getPoint() {
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPoint(Point point) {
|
|
||||||
this.point = point;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getPressure() {
|
|
||||||
return pressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPressure(float pressure) {
|
|
||||||
this.pressure = pressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUp() {
|
|
||||||
return up;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUp(boolean up) {
|
|
||||||
this.up = up;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
package com.genymobile.scrcpy;
|
|
||||||
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PointersState {
|
|
||||||
|
|
||||||
public static final int MAX_POINTERS = 10;
|
|
||||||
|
|
||||||
private final List<Pointer> pointers = new ArrayList<>();
|
|
||||||
|
|
||||||
private int indexOf(long id) {
|
|
||||||
for (int i = 0; i < pointers.size(); ++i) {
|
|
||||||
Pointer pointer = pointers.get(i);
|
|
||||||
if (pointer.getId() == id) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isLocalIdAvailable(int localId) {
|
|
||||||
for (int i = 0; i < pointers.size(); ++i) {
|
|
||||||
Pointer pointer = pointers.get(i);
|
|
||||||
if (pointer.getLocalId() == localId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int nextUnusedLocalId() {
|
|
||||||
for (int localId = 0; localId < MAX_POINTERS; ++localId) {
|
|
||||||
if (isLocalIdAvailable(localId)) {
|
|
||||||
return localId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Pointer get(int index) {
|
|
||||||
return pointers.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPointerIndex(long id) {
|
|
||||||
int index = indexOf(id);
|
|
||||||
if (index != -1) {
|
|
||||||
// already exists, return it
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
if (pointers.size() >= MAX_POINTERS) {
|
|
||||||
// it's full
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
// id 0 is reserved for mouse events
|
|
||||||
int localId = nextUnusedLocalId();
|
|
||||||
if (localId == -1) {
|
|
||||||
throw new AssertionError("pointers.size() < maxFingers implies that a local id is available");
|
|
||||||
}
|
|
||||||
Pointer pointer = new Pointer(id, localId);
|
|
||||||
pointers.add(pointer);
|
|
||||||
// return the index of the pointer
|
|
||||||
return pointers.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the motion event parameters.
|
|
||||||
*
|
|
||||||
* @param props the pointer properties
|
|
||||||
* @param coords the pointer coordinates
|
|
||||||
* @return The number of items initialized (the number of pointers).
|
|
||||||
*/
|
|
||||||
public int update(MotionEvent.PointerProperties[] props, MotionEvent.PointerCoords[] coords) {
|
|
||||||
int count = pointers.size();
|
|
||||||
for (int i = 0; i < count; ++i) {
|
|
||||||
Pointer pointer = pointers.get(i);
|
|
||||||
|
|
||||||
// id 0 is reserved for mouse events
|
|
||||||
props[i].id = pointer.getLocalId();
|
|
||||||
|
|
||||||
Point point = pointer.getPoint();
|
|
||||||
coords[i].x = point.getX();
|
|
||||||
coords[i].y = point.getY();
|
|
||||||
coords[i].pressure = pointer.getPressure();
|
|
||||||
}
|
|
||||||
cleanUp();
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all pointers which are UP.
|
|
||||||
*/
|
|
||||||
private void cleanUp() {
|
|
||||||
for (int i = pointers.size() - 1; i >= 0; --i) {
|
|
||||||
Pointer pointer = pointers.get(i);
|
|
||||||
if (pointer.isUp()) {
|
|
||||||
pointers.remove(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
}
|
try {
|
||||||
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class);
|
||||||
private Method getGetPrimaryClipMethod() {
|
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class);
|
||||||
if (getPrimaryClipMethod == null) {
|
} catch (NoSuchMethodException e) {
|
||||||
try {
|
throw new AssertionError(e);
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class,
|
|
||||||
String.class, int.class);
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", 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;
|
||||||
}
|
try {
|
||||||
|
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class);
|
||||||
private Method getInjectInputEventMethod() {
|
} catch (NoSuchMethodException e) {
|
||||||
if (injectInputEventMethod == null) {
|
throw new AssertionError(e);
|
||||||
try {
|
|
||||||
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", 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;
|
||||||
}
|
try {
|
||||||
|
@SuppressLint("ObsoleteSdkInt") // we may lower minSdkVersion in the future
|
||||||
private Method getIsScreenOnMethod() {
|
String methodName = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH ? "isInteractive" : "isScreenOn";
|
||||||
if (isScreenOnMethod == null) {
|
isScreenOnMethod = manager.getClass().getMethod(methodName);
|
||||||
try {
|
} catch (NoSuchMethodException e) {
|
||||||
@SuppressLint("ObsoleteSdkInt") // we may lower minSdkVersion in the future
|
throw new AssertionError(e);
|
||||||
String methodName = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH ? "isInteractive" : "isScreenOn";
|
|
||||||
isScreenOnMethod = manager.getClass().getMethod(methodName);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
}
|
|
||||||
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 {
|
||||||
return;
|
collapsePanelsMethod = manager.getClass().getMethod("collapsePanels");
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
Ln.e("ServiceBarManager.collapsePanels() is not available on this device");
|
||||||
|
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 {
|
|
||||||
// the method signature has changed in Android Q
|
|
||||||
// <https://github.com/Genymobile/scrcpy/issues/586>
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
|
||||||
getBuiltInDisplayMethod = CLASS.getMethod("getBuiltInDisplay", int.class);
|
|
||||||
} else {
|
|
||||||
getBuiltInDisplayMethod = CLASS.getMethod("getInternalDisplayToken");
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return getBuiltInDisplayMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IBinder getBuiltInDisplay() {
|
|
||||||
Method method = getGetBuiltInDisplayMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
|
// the method signature has changed in Android Q
|
||||||
|
// <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) {
|
||||||
// call getBuiltInDisplay(0)
|
return (IBinder) CLASS.getMethod("getBuiltInDisplay", int.class).invoke(null, builtInDisplayId);
|
||||||
return (IBinder) method.invoke(null, 0);
|
|
||||||
}
|
}
|
||||||
|
return (IBinder) CLASS.getMethod("getPhysicalDisplayToken", long.class).invoke(null, builtInDisplayId);
|
||||||
// call getInternalDisplayToken()
|
} catch (Exception e) {
|
||||||
return (IBinder) method.invoke(null);
|
throw new AssertionError(e);
|
||||||
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public final class WindowManager {
|
|||||||
try {
|
try {
|
||||||
Class<?> cls = manager.getClass();
|
Class<?> cls = manager.getClass();
|
||||||
try {
|
try {
|
||||||
return (Integer) cls.getMethod("getRotation").invoke(manager);
|
return (Integer) manager.getClass().getMethod("getRotation").invoke(manager);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
// method changed since this commit:
|
// method changed since this commit:
|
||||||
// https://android.googlesource.com/platform/frameworks/base/+/8ee7285128c3843401d4c4d0412cd66e86ba49e3%5E%21/#F2
|
// https://android.googlesource.com/platform/frameworks/base/+/8ee7285128c3843401d4c4d0412cd66e86ba49e3%5E%21/#F2
|
||||||
|
|||||||
@@ -77,36 +77,24 @@ public class ControlMessageReaderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("checkstyle:MagicNumber")
|
public void testParseMouseEvent() throws IOException {
|
||||||
public void testParseTouchEvent() throws IOException {
|
|
||||||
ControlMessageReader reader = new ControlMessageReader();
|
ControlMessageReader reader = new ControlMessageReader();
|
||||||
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
DataOutputStream dos = new DataOutputStream(bos);
|
DataOutputStream dos = new DataOutputStream(bos);
|
||||||
dos.writeByte(ControlMessage.TYPE_INJECT_TOUCH_EVENT);
|
dos.writeByte(ControlMessage.TYPE_INJECT_KEYCODE);
|
||||||
dos.writeByte(MotionEvent.ACTION_DOWN);
|
dos.writeByte(MotionEvent.ACTION_DOWN);
|
||||||
dos.writeLong(-42); // pointerId
|
|
||||||
dos.writeInt(100);
|
|
||||||
dos.writeInt(200);
|
|
||||||
dos.writeShort(1080);
|
|
||||||
dos.writeShort(1920);
|
|
||||||
dos.writeShort(0xffff); // pressure
|
|
||||||
dos.writeInt(MotionEvent.BUTTON_PRIMARY);
|
dos.writeInt(MotionEvent.BUTTON_PRIMARY);
|
||||||
|
dos.writeInt(KeyEvent.META_CTRL_ON);
|
||||||
byte[] packet = bos.toByteArray();
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
reader.readFrom(new ByteArrayInputStream(packet));
|
reader.readFrom(new ByteArrayInputStream(packet));
|
||||||
ControlMessage event = reader.next();
|
ControlMessage event = reader.next();
|
||||||
|
|
||||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_TOUCH_EVENT, event.getType());
|
Assert.assertEquals(ControlMessage.TYPE_INJECT_KEYCODE, event.getType());
|
||||||
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
|
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
|
||||||
Assert.assertEquals(-42, event.getPointerId());
|
Assert.assertEquals(MotionEvent.BUTTON_PRIMARY, event.getKeycode());
|
||||||
Assert.assertEquals(100, event.getPosition().getPoint().getX());
|
Assert.assertEquals(KeyEvent.META_CTRL_ON, event.getMetaState());
|
||||||
Assert.assertEquals(200, event.getPosition().getPoint().getY());
|
|
||||||
Assert.assertEquals(1080, event.getPosition().getScreenSize().getWidth());
|
|
||||||
Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight());
|
|
||||||
Assert.assertEquals(1f, event.getPressure(), 0f); // must be exact
|
|
||||||
Assert.assertEquals(MotionEvent.BUTTON_PRIMARY, event.getButtons());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user