Compare commits
27 Commits
v1.11
...
rotatedevi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb0f339271 | ||
|
|
bdd05b4a16 | ||
|
|
525d6d4a75 | ||
|
|
4041043d1c | ||
|
|
fbc86a616c | ||
|
|
b2bf25c52c | ||
|
|
5eeaed09ae | ||
|
|
3100533e56 | ||
|
|
8a694a9785 | ||
|
|
26529d377f | ||
|
|
15a206b7fc | ||
|
|
d0f5a7fd9f | ||
|
|
510caff0cd | ||
|
|
b5ebb234dd | ||
|
|
73e8ec1b35 | ||
|
|
8dc11a0286 | ||
|
|
06104a701b | ||
|
|
7637a113e3 | ||
|
|
31d9d56117 | ||
|
|
6abb8fd0cd | ||
|
|
2b845680b0 | ||
|
|
ebdc2ee8b5 | ||
|
|
dfd0707a29 | ||
|
|
83ace84280 | ||
|
|
c9d886f38b | ||
|
|
7d7f3daff2 | ||
|
|
40c3c57613 |
6
BUILD.md
6
BUILD.md
@@ -233,10 +233,10 @@ You can then [run](README.md#run) _scrcpy_.
|
|||||||
|
|
||||||
## Prebuilt server
|
## Prebuilt server
|
||||||
|
|
||||||
- [`scrcpy-server-v1.10.jar`][direct-scrcpy-server]
|
- [`scrcpy-server-v1.11`][direct-scrcpy-server]
|
||||||
_(SHA-256: cbeb1a4e046f1392c1dc73c3ccffd7f86dec4636b505556ea20929687a119390)_
|
_(SHA-256: ff3a454012e91d9185cfe8ca7691cea16c43a7dcc08e92fa47ab9f0ea675abd1)_
|
||||||
|
|
||||||
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.10/scrcpy-server-v1.10.jar
|
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.11/scrcpy-server-v1.11
|
||||||
|
|
||||||
Download the prebuilt server somewhere, and specify its path during the Meson
|
Download the prebuilt server somewhere, and specify its path during the Meson
|
||||||
configuration:
|
configuration:
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -1,4 +1,4 @@
|
|||||||
# scrcpy (v1.10)
|
# scrcpy (v1.11)
|
||||||
|
|
||||||
This application provides display and control of Android devices connected on
|
This application provides display and control of Android devices connected on
|
||||||
USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access.
|
USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access.
|
||||||
@@ -62,13 +62,13 @@ For Gentoo, an [Ebuild] is available: [`scrcpy/`][ebuild-link].
|
|||||||
For Windows, for simplicity, prebuilt archives with all the dependencies
|
For Windows, for simplicity, prebuilt archives with all the dependencies
|
||||||
(including `adb`) are available:
|
(including `adb`) are available:
|
||||||
|
|
||||||
- [`scrcpy-win32-v1.10.zip`][direct-win32]
|
- [`scrcpy-win32-v1.11.zip`][direct-win32]
|
||||||
_(SHA-256: f98b400b3764404b33b212e9762dd6f1593ddb766c1480fc2609c94768e4a8e1)_
|
_(SHA-256: f25ed46e6f3e81e0ff9b9b4df7fe1a4bbd13f8396b7391be0a488b64c675b41e)_
|
||||||
- [`scrcpy-win64-v1.10.zip`][direct-win64]
|
- [`scrcpy-win64-v1.11.zip`][direct-win64]
|
||||||
_(SHA-256: 95de34575d873c7e95dfcfb5e74d0f6af4f70b2a5bc6fde0f48d1a05480e3a44)_
|
_(SHA-256: 3802c9ea0307d437947ff150ec65e53990b0beaacd0c8d0bed19c7650ce141bd)_
|
||||||
|
|
||||||
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.10/scrcpy-win32-v1.10.zip
|
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.11/scrcpy-win32-v1.11.zip
|
||||||
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.10/scrcpy-win64-v1.10.zip
|
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.11/scrcpy-win64-v1.11.zip
|
||||||
|
|
||||||
You can also [build the app manually][BUILD].
|
You can also [build the app manually][BUILD].
|
||||||
|
|
||||||
@@ -425,6 +425,7 @@ Also see [issue #14].
|
|||||||
| Click on `POWER` | `Ctrl`+`p` | `Cmd`+`p`
|
| Click on `POWER` | `Ctrl`+`p` | `Cmd`+`p`
|
||||||
| Power on | _Right-click²_ | _Right-click²_
|
| Power on | _Right-click²_ | _Right-click²_
|
||||||
| Turn device screen off (keep mirroring)| `Ctrl`+`o` | `Cmd`+`o`
|
| Turn device screen off (keep mirroring)| `Ctrl`+`o` | `Cmd`+`o`
|
||||||
|
| Rotate device screen | `Ctrl`+`r` | `Cmd`+`r`
|
||||||
| Expand notification panel | `Ctrl`+`n` | `Cmd`+`n`
|
| Expand notification panel | `Ctrl`+`n` | `Cmd`+`n`
|
||||||
| Collapse notification panel | `Ctrl`+`Shift`+`n` | `Cmd`+`Shift`+`n`
|
| Collapse notification panel | `Ctrl`+`Shift`+`n` | `Cmd`+`Shift`+`n`
|
||||||
| Copy device clipboard to computer | `Ctrl`+`c` | `Cmd`+`c`
|
| Copy device clipboard to computer | `Ctrl`+`c` | `Cmd`+`c`
|
||||||
|
|||||||
@@ -10,16 +10,16 @@ src = [
|
|||||||
'src/file_handler.c',
|
'src/file_handler.c',
|
||||||
'src/fps_counter.c',
|
'src/fps_counter.c',
|
||||||
'src/input_manager.c',
|
'src/input_manager.c',
|
||||||
'src/net.c',
|
|
||||||
'src/receiver.c',
|
'src/receiver.c',
|
||||||
'src/recorder.c',
|
'src/recorder.c',
|
||||||
'src/scrcpy.c',
|
'src/scrcpy.c',
|
||||||
'src/screen.c',
|
'src/screen.c',
|
||||||
'src/server.c',
|
'src/server.c',
|
||||||
'src/str_util.c',
|
|
||||||
'src/tiny_xpm.c',
|
|
||||||
'src/stream.c',
|
'src/stream.c',
|
||||||
|
'src/tiny_xpm.c',
|
||||||
'src/video_buffer.c',
|
'src/video_buffer.c',
|
||||||
|
'src/util/net.c',
|
||||||
|
'src/util/str_util.c'
|
||||||
]
|
]
|
||||||
|
|
||||||
if not get_option('crossbuild_windows')
|
if not get_option('crossbuild_windows')
|
||||||
@@ -85,7 +85,7 @@ endif
|
|||||||
conf = configuration_data()
|
conf = configuration_data()
|
||||||
|
|
||||||
# expose the build type
|
# expose the build type
|
||||||
conf.set('BUILD_DEBUG', get_option('buildtype') == 'debug')
|
conf.set('NDEBUG', get_option('buildtype') != 'debug')
|
||||||
|
|
||||||
# the version, updated on release
|
# the version, updated on release
|
||||||
conf.set_quoted('SCRCPY_VERSION', meson.project_version())
|
conf.set_quoted('SCRCPY_VERSION', meson.project_version())
|
||||||
@@ -141,13 +141,16 @@ install_man('scrcpy.1')
|
|||||||
### TESTS
|
### TESTS
|
||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
|
['test_buffer_util', [
|
||||||
|
'tests/test_buffer_util.c'
|
||||||
|
]],
|
||||||
['test_cbuf', [
|
['test_cbuf', [
|
||||||
'tests/test_cbuf.c',
|
'tests/test_cbuf.c',
|
||||||
]],
|
]],
|
||||||
['test_control_event_serialize', [
|
['test_control_event_serialize', [
|
||||||
'tests/test_control_msg_serialize.c',
|
'tests/test_control_msg_serialize.c',
|
||||||
'src/control_msg.c',
|
'src/control_msg.c',
|
||||||
'src/str_util.c'
|
'src/util/str_util.c'
|
||||||
]],
|
]],
|
||||||
['test_device_event_deserialize', [
|
['test_device_event_deserialize', [
|
||||||
'tests/test_device_msg_deserialize.c',
|
'tests/test_device_msg_deserialize.c',
|
||||||
@@ -158,7 +161,7 @@ tests = [
|
|||||||
]],
|
]],
|
||||||
['test_strutil', [
|
['test_strutil', [
|
||||||
'tests/test_strutil.c',
|
'tests/test_strutil.c',
|
||||||
'src/str_util.c'
|
'src/util/str_util.c'
|
||||||
]],
|
]],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -195,6 +195,10 @@ turn screen on
|
|||||||
.B Ctrl+o
|
.B Ctrl+o
|
||||||
turn device screen off (keep mirroring)
|
turn device screen off (keep mirroring)
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B Ctrl+r
|
||||||
|
rotate device screen
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+n
|
.B Ctrl+n
|
||||||
expand notification panel
|
expand notification panel
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
#include "str_util.h"
|
#include "util/str_util.h"
|
||||||
|
|
||||||
static const char *adb_command;
|
static const char *adb_command;
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ adb_execute(const char *serial, const char *const adb_cmd[], size_t len) {
|
|||||||
|
|
||||||
memcpy(&cmd[i], adb_cmd, len * sizeof(const char *));
|
memcpy(&cmd[i], adb_cmd, len * sizeof(const char *));
|
||||||
cmd[len + i] = NULL;
|
cmd[len + i] = NULL;
|
||||||
enum process_result r = cmd_execute(cmd[0], cmd, &process);
|
enum process_result r = cmd_execute(cmd, &process);
|
||||||
if (r != PROCESS_SUCCESS) {
|
if (r != PROCESS_SUCCESS) {
|
||||||
show_adb_err_msg(r, cmd);
|
show_adb_err_msg(r, cmd);
|
||||||
return PROCESS_NONE;
|
return PROCESS_NONE;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
# define PRIsizet PRIu32
|
# define PRIsizet PRIu32
|
||||||
# endif
|
# endif
|
||||||
# define PROCESS_NONE NULL
|
# define PROCESS_NONE NULL
|
||||||
|
# define NO_EXIT_CODE -1u // max value as unsigned
|
||||||
typedef HANDLE process_t;
|
typedef HANDLE process_t;
|
||||||
typedef DWORD exit_code_t;
|
typedef DWORD exit_code_t;
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
# define PRIsizet "zu"
|
# define PRIsizet "zu"
|
||||||
# define PRIexitcode "d"
|
# define PRIexitcode "d"
|
||||||
# define PROCESS_NONE -1
|
# define PROCESS_NONE -1
|
||||||
|
# define NO_EXIT_CODE -1
|
||||||
typedef pid_t process_t;
|
typedef pid_t process_t;
|
||||||
typedef int exit_code_t;
|
typedef int exit_code_t;
|
||||||
|
|
||||||
@@ -35,8 +37,6 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
# define NO_EXIT_CODE -1
|
|
||||||
|
|
||||||
enum process_result {
|
enum process_result {
|
||||||
PROCESS_SUCCESS,
|
PROCESS_SUCCESS,
|
||||||
PROCESS_ERROR_GENERIC,
|
PROCESS_ERROR_GENERIC,
|
||||||
@@ -44,7 +44,7 @@ enum process_result {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum process_result
|
enum process_result
|
||||||
cmd_execute(const char *path, const char *const argv[], process_t *process);
|
cmd_execute(const char *const argv[], process_t *process);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
cmd_terminate(process_t pid);
|
cmd_terminate(process_t pid);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#include "control_msg.h"
|
#include "control_msg.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "buffer_util.h"
|
#include "util/buffer_util.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
#include "str_util.h"
|
#include "util/str_util.h"
|
||||||
|
|
||||||
static void
|
static void
|
||||||
write_position(uint8_t *buf, const struct position *position) {
|
write_position(uint8_t *buf, const struct position *position) {
|
||||||
@@ -27,7 +27,7 @@ write_string(const char *utf8, size_t max_len, unsigned char *buf) {
|
|||||||
|
|
||||||
static uint16_t
|
static uint16_t
|
||||||
to_fixed_point_16(float f) {
|
to_fixed_point_16(float f) {
|
||||||
SDL_assert(f >= 0.0f && f <= 1.0f);
|
assert(f >= 0.0f && f <= 1.0f);
|
||||||
uint32_t u = f * 0x1p16f; // 2^16
|
uint32_t u = f * 0x1p16f; // 2^16
|
||||||
if (u >= 0xffff) {
|
if (u >= 0xffff) {
|
||||||
u = 0xffff;
|
u = 0xffff;
|
||||||
@@ -78,6 +78,7 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
|||||||
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||||
case CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
case CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
||||||
case CONTROL_MSG_TYPE_GET_CLIPBOARD:
|
case CONTROL_MSG_TYPE_GET_CLIPBOARD:
|
||||||
|
case CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
||||||
// no additional data
|
// no additional data
|
||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ enum control_msg_type {
|
|||||||
CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
||||||
CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||||
CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
||||||
|
CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum screen_power_mode {
|
enum screen_power_mode {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "lock_util.h"
|
#include "util/lock.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
controller_init(struct controller *controller, socket_t control_socket) {
|
controller_init(struct controller *controller, socket_t control_socket) {
|
||||||
@@ -85,7 +85,8 @@ run_controller(void *data) {
|
|||||||
}
|
}
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
bool non_empty = cbuf_take(&controller->queue, &msg);
|
bool non_empty = cbuf_take(&controller->queue, &msg);
|
||||||
SDL_assert(non_empty);
|
assert(non_empty);
|
||||||
|
(void) non_empty;
|
||||||
mutex_unlock(controller->mutex);
|
mutex_unlock(controller->mutex);
|
||||||
|
|
||||||
bool ok = process_msg(controller, &msg);
|
bool ok = process_msg(controller, &msg);
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "cbuf.h"
|
|
||||||
#include "control_msg.h"
|
#include "control_msg.h"
|
||||||
#include "net.h"
|
|
||||||
#include "receiver.h"
|
#include "receiver.h"
|
||||||
|
#include "util/cbuf.h"
|
||||||
|
#include "util/net.h"
|
||||||
|
|
||||||
struct control_msg_queue CBUF(struct control_msg, 64);
|
struct control_msg_queue CBUF(struct control_msg, 64);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
#include <libavutil/time.h>
|
#include <libavutil/time.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
|
||||||
#include <SDL2/SDL_events.h>
|
#include <SDL2/SDL_events.h>
|
||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
@@ -10,12 +9,11 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "buffer_util.h"
|
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
#include "lock_util.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "recorder.h"
|
#include "recorder.h"
|
||||||
#include "video_buffer.h"
|
#include "video_buffer.h"
|
||||||
|
#include "util/buffer_util.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
// set the decoded frame as ready for rendering, and notify
|
// set the decoded frame as ready for rendering, and notify
|
||||||
static void
|
static void
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "device.h"
|
#include "device.h"
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
device_read_info(socket_t device_socket, char *device_name, struct size *size) {
|
device_read_info(socket_t device_socket, char *device_name, struct size *size) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "net.h"
|
#include "util/net.h"
|
||||||
|
|
||||||
#define DEVICE_NAME_FIELD_LENGTH 64
|
#define DEVICE_NAME_FIELD_LENGTH 64
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
#include "device_msg.h"
|
#include "device_msg.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "buffer_util.h"
|
#include "util/buffer_util.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
device_msg_deserialize(const unsigned char *buf, size_t len,
|
device_msg_deserialize(const unsigned char *buf, size_t len,
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#include "file_handler.h"
|
#include "file_handler.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "lock_util.h"
|
#include "util/lock.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
#define DEFAULT_PUSH_TARGET "/sdcard/"
|
#define DEFAULT_PUSH_TARGET "/sdcard/"
|
||||||
|
|
||||||
@@ -120,7 +120,8 @@ run_file_handler(void *data) {
|
|||||||
}
|
}
|
||||||
struct file_handler_request req;
|
struct file_handler_request req;
|
||||||
bool non_empty = cbuf_take(&file_handler->queue, &req);
|
bool non_empty = cbuf_take(&file_handler->queue, &req);
|
||||||
SDL_assert(non_empty);
|
assert(non_empty);
|
||||||
|
(void) non_empty;
|
||||||
|
|
||||||
process_t process;
|
process_t process;
|
||||||
if (req.action == ACTION_INSTALL_APK) {
|
if (req.action == ACTION_INSTALL_APK) {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "cbuf.h"
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
#include "util/cbuf.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ACTION_INSTALL_APK,
|
ACTION_INSTALL_APK,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#include "fps_counter.h"
|
#include "fps_counter.h"
|
||||||
|
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <assert.h>
|
||||||
#include <SDL2/SDL_timer.h>
|
#include <SDL2/SDL_timer.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "lock_util.h"
|
#include "util/lock.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
#define FPS_COUNTER_INTERVAL_MS 1000
|
#define FPS_COUNTER_INTERVAL_MS 1000
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ run_fps_counter(void *data) {
|
|||||||
uint32_t now = SDL_GetTicks();
|
uint32_t now = SDL_GetTicks();
|
||||||
check_interval_expired(counter, now);
|
check_interval_expired(counter, now);
|
||||||
|
|
||||||
SDL_assert(counter->next_timestamp > now);
|
assert(counter->next_timestamp > now);
|
||||||
uint32_t remaining = counter->next_timestamp - now;
|
uint32_t remaining = counter->next_timestamp - now;
|
||||||
|
|
||||||
// ignore the reason (timeout or signaled), we just loop anyway
|
// ignore the reason (timeout or signaled), we just loop anyway
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
|
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "event_converter.h"
|
#include "event_converter.h"
|
||||||
#include "lock_util.h"
|
#include "util/lock.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
// Convert window coordinates (as provided by SDL_GetMouseState() to renderer
|
// Convert window coordinates (as provided by SDL_GetMouseState() to renderer
|
||||||
// coordinates (as provided in SDL mouse events)
|
// coordinates (as provided in SDL mouse events)
|
||||||
@@ -211,13 +211,23 @@ clipboard_paste(struct controller *controller) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rotate_device(struct controller *controller) {
|
||||||
|
struct control_msg msg;
|
||||||
|
msg.type = CONTROL_MSG_TYPE_ROTATE_DEVICE;
|
||||||
|
|
||||||
|
if (!controller_push_msg(controller, &msg)) {
|
||||||
|
LOGW("Could not request device rotation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
input_manager_process_text_input(struct input_manager *im,
|
input_manager_process_text_input(struct input_manager *im,
|
||||||
const SDL_TextInputEvent *event) {
|
const SDL_TextInputEvent *event) {
|
||||||
if (!im->prefer_text) {
|
if (!im->prefer_text) {
|
||||||
char c = event->text[0];
|
char c = event->text[0];
|
||||||
if (isalpha(c) || c == ' ') {
|
if (isalpha(c) || c == ' ') {
|
||||||
SDL_assert(event->text[1] == '\0');
|
assert(event->text[1] == '\0');
|
||||||
// letters and space are handled as raw key event
|
// letters and space are handled as raw key event
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -388,6 +398,11 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
case SDLK_r:
|
||||||
|
if (control && cmd && !shift && !repeat && down) {
|
||||||
|
rotate_device(controller);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -550,13 +565,8 @@ convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen,
|
|||||||
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
|
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
|
||||||
|
|
||||||
to->inject_scroll_event.position = position;
|
to->inject_scroll_event.position = position;
|
||||||
|
to->inject_scroll_event.hscroll = from->x;
|
||||||
int mul = from->direction == SDL_MOUSEWHEEL_NORMAL ? 1 : -1;
|
to->inject_scroll_event.vscroll = from->y;
|
||||||
// SDL behavior seems inconsistent between horizontal and vertical scrolling
|
|
||||||
// so reverse the horizontal
|
|
||||||
// <https://wiki.libsdl.org/SDL_MouseWheelEvent#Remarks>
|
|
||||||
to->inject_scroll_event.hscroll = -mul * from->x;
|
|
||||||
to->inject_scroll_event.vscroll = mul * from->y;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
#ifndef LOCKUTIL_H
|
|
||||||
#define LOCKUTIL_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <SDL2/SDL_mutex.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
mutex_lock(SDL_mutex *mutex) {
|
|
||||||
if (SDL_LockMutex(mutex)) {
|
|
||||||
LOGC("Could not lock mutex");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
mutex_unlock(SDL_mutex *mutex) {
|
|
||||||
if (SDL_UnlockMutex(mutex)) {
|
|
||||||
LOGC("Could not unlock mutex");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
cond_wait(SDL_cond *cond, SDL_mutex *mutex) {
|
|
||||||
if (SDL_CondWait(cond, mutex)) {
|
|
||||||
LOGC("Could not wait on condition");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
|
||||||
cond_wait_timeout(SDL_cond *cond, SDL_mutex *mutex, uint32_t ms) {
|
|
||||||
int r = SDL_CondWaitTimeout(cond, mutex, ms);
|
|
||||||
if (r < 0) {
|
|
||||||
LOGC("Could not wait on condition with timeout");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
cond_signal(SDL_cond *cond) {
|
|
||||||
if (SDL_CondSignal(cond)) {
|
|
||||||
LOGC("Could not signal a condition");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "log.h"
|
|
||||||
#include "recorder.h"
|
#include "recorder.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
struct args {
|
struct args {
|
||||||
struct scrcpy_options opts;
|
struct scrcpy_options opts;
|
||||||
@@ -175,6 +175,9 @@ static void usage(const char *arg0) {
|
|||||||
" " CTRL_OR_CMD "+o\n"
|
" " CTRL_OR_CMD "+o\n"
|
||||||
" turn device screen off (keep mirroring)\n"
|
" turn device screen off (keep mirroring)\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" " CTRL_OR_CMD "+r\n"
|
||||||
|
" rotate device screen\n"
|
||||||
|
"\n"
|
||||||
" " CTRL_OR_CMD "+n\n"
|
" " CTRL_OR_CMD "+n\n"
|
||||||
" expand notification panel\n"
|
" expand notification panel\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -243,7 +246,7 @@ parse_bit_rate(char *optarg, uint32_t *bit_rate) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (value < 0 || ((uint32_t) -1) / mul < value) {
|
if (value < 0 || ((uint32_t) -1) / mul < (unsigned long) value) {
|
||||||
LOGE("Bitrate must be positive and less than 2^32: %s", optarg);
|
LOGE("Bitrate must be positive and less than 2^32: %s", optarg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -625,7 +628,7 @@ main(int argc, char *argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILD_DEBUG
|
#ifndef NDEBUG
|
||||||
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
|
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#include "receiver.h"
|
#include "receiver.h"
|
||||||
|
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <assert.h>
|
||||||
#include <SDL2/SDL_clipboard.h>
|
#include <SDL2/SDL_clipboard.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "device_msg.h"
|
#include "device_msg.h"
|
||||||
#include "lock_util.h"
|
#include "util/lock.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
receiver_init(struct receiver *receiver, socket_t control_socket) {
|
receiver_init(struct receiver *receiver, socket_t control_socket) {
|
||||||
@@ -23,7 +23,7 @@ receiver_destroy(struct receiver *receiver) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
process_msg(struct receiver *receiver, struct device_msg *msg) {
|
process_msg(struct device_msg *msg) {
|
||||||
switch (msg->type) {
|
switch (msg->type) {
|
||||||
case DEVICE_MSG_TYPE_CLIPBOARD:
|
case DEVICE_MSG_TYPE_CLIPBOARD:
|
||||||
LOGI("Device clipboard copied");
|
LOGI("Device clipboard copied");
|
||||||
@@ -33,7 +33,7 @@ process_msg(struct receiver *receiver, struct device_msg *msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
process_msgs(struct receiver *receiver, const unsigned char *buf, size_t len) {
|
process_msgs(const unsigned char *buf, size_t len) {
|
||||||
size_t head = 0;
|
size_t head = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
struct device_msg msg;
|
struct device_msg msg;
|
||||||
@@ -45,11 +45,11 @@ process_msgs(struct receiver *receiver, const unsigned char *buf, size_t len) {
|
|||||||
return head;
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
process_msg(receiver, &msg);
|
process_msg(&msg);
|
||||||
device_msg_destroy(&msg);
|
device_msg_destroy(&msg);
|
||||||
|
|
||||||
head += r;
|
head += r;
|
||||||
SDL_assert(head <= len);
|
assert(head <= len);
|
||||||
if (head == len) {
|
if (head == len) {
|
||||||
return head;
|
return head;
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ run_receiver(void *data) {
|
|||||||
size_t head = 0;
|
size_t head = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
SDL_assert(head < DEVICE_MSG_SERIALIZED_MAX_SIZE);
|
assert(head < DEVICE_MSG_SERIALIZED_MAX_SIZE);
|
||||||
ssize_t r = net_recv(receiver->control_socket, buf,
|
ssize_t r = net_recv(receiver->control_socket, buf,
|
||||||
DEVICE_MSG_SERIALIZED_MAX_SIZE - head);
|
DEVICE_MSG_SERIALIZED_MAX_SIZE - head);
|
||||||
if (r <= 0) {
|
if (r <= 0) {
|
||||||
@@ -72,7 +72,7 @@ run_receiver(void *data) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t consumed = process_msgs(receiver, buf, r);
|
ssize_t consumed = process_msgs(buf, r);
|
||||||
if (consumed == -1) {
|
if (consumed == -1) {
|
||||||
// an error occurred
|
// an error occurred
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "net.h"
|
#include "util/net.h"
|
||||||
|
|
||||||
// receive events from the device
|
// receive events from the device
|
||||||
// managed by the controller
|
// managed by the controller
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#include "recorder.h"
|
#include "recorder.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <libavutil/time.h>
|
#include <libavutil/time.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "lock_util.h"
|
#include "util/lock.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
static const AVRational SCRCPY_TIME_BASE = {1, 1000000}; // timestamps in us
|
static const AVRational SCRCPY_TIME_BASE = {1, 1000000}; // timestamps in us
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ recorder_get_format_name(enum recorder_format format) {
|
|||||||
bool
|
bool
|
||||||
recorder_open(struct recorder *recorder, const AVCodec *input_codec) {
|
recorder_open(struct recorder *recorder, const AVCodec *input_codec) {
|
||||||
const char *format_name = recorder_get_format_name(recorder->format);
|
const char *format_name = recorder_get_format_name(recorder->format);
|
||||||
SDL_assert(format_name);
|
assert(format_name);
|
||||||
const AVOutputFormat *format = find_muxer(format_name);
|
const AVOutputFormat *format = find_muxer(format_name);
|
||||||
if (!format) {
|
if (!format) {
|
||||||
LOGE("Could not find muxer");
|
LOGE("Could not find muxer");
|
||||||
@@ -357,7 +357,7 @@ recorder_join(struct recorder *recorder) {
|
|||||||
bool
|
bool
|
||||||
recorder_push(struct recorder *recorder, const AVPacket *packet) {
|
recorder_push(struct recorder *recorder, const AVPacket *packet) {
|
||||||
mutex_lock(recorder->mutex);
|
mutex_lock(recorder->mutex);
|
||||||
SDL_assert(!recorder->stopped);
|
assert(!recorder->stopped);
|
||||||
|
|
||||||
if (recorder->failed) {
|
if (recorder->failed) {
|
||||||
// reject any new packet (this will stop the stream)
|
// reject any new packet (this will stop the stream)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "queue.h"
|
#include "util/queue.h"
|
||||||
|
|
||||||
enum recorder_format {
|
enum recorder_format {
|
||||||
RECORDER_FORMAT_AUTO,
|
RECORDER_FORMAT_AUTO,
|
||||||
|
|||||||
@@ -18,15 +18,15 @@
|
|||||||
#include "file_handler.h"
|
#include "file_handler.h"
|
||||||
#include "fps_counter.h"
|
#include "fps_counter.h"
|
||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
#include "log.h"
|
|
||||||
#include "lock_util.h"
|
|
||||||
#include "net.h"
|
|
||||||
#include "recorder.h"
|
#include "recorder.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
#include "tiny_xpm.h"
|
#include "tiny_xpm.h"
|
||||||
#include "video_buffer.h"
|
#include "video_buffer.h"
|
||||||
|
#include "util/lock.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
#include "util/net.h"
|
||||||
|
|
||||||
static struct server server = SERVER_INITIALIZER;
|
static struct server server = SERVER_INITIALIZER;
|
||||||
static struct screen screen = SCREEN_INITIALIZER;
|
static struct screen screen = SCREEN_INITIALIZER;
|
||||||
@@ -103,6 +103,7 @@ sdl_init_and_configure(bool display) {
|
|||||||
// <https://stackoverflow.com/a/40693139/1987178>
|
// <https://stackoverflow.com/a/40693139/1987178>
|
||||||
static int
|
static int
|
||||||
event_watcher(void *data, SDL_Event *event) {
|
event_watcher(void *data, SDL_Event *event) {
|
||||||
|
(void) data;
|
||||||
if (event->type == SDL_WINDOWEVENT
|
if (event->type == SDL_WINDOWEVENT
|
||||||
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
|
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||||
// called from another thread, not very safe, but it's a workaround!
|
// called from another thread, not very safe, but it's a workaround!
|
||||||
@@ -201,6 +202,7 @@ handle_event(SDL_Event *event, bool control) {
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
event_loop(bool display, bool control) {
|
event_loop(bool display, bool control) {
|
||||||
|
(void) display;
|
||||||
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
||||||
if (display) {
|
if (display) {
|
||||||
SDL_AddEventWatch(event_watcher, NULL);
|
SDL_AddEventWatch(event_watcher, NULL);
|
||||||
@@ -256,6 +258,7 @@ sdl_priority_from_av_level(int level) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
av_log_callback(void *avcl, int level, const char *fmt, va_list vl) {
|
av_log_callback(void *avcl, int level, const char *fmt, va_list vl) {
|
||||||
|
(void) avcl;
|
||||||
SDL_LogPriority priority = sdl_priority_from_av_level(level);
|
SDL_LogPriority priority = sdl_priority_from_av_level(level);
|
||||||
if (priority == 0) {
|
if (priority == 0) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
@@ -7,10 +8,10 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "icon.xpm"
|
#include "icon.xpm"
|
||||||
#include "lock_util.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "tiny_xpm.h"
|
#include "tiny_xpm.h"
|
||||||
#include "video_buffer.h"
|
#include "video_buffer.h"
|
||||||
|
#include "util/lock.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
#define DISPLAY_MARGINS 96
|
#define DISPLAY_MARGINS 96
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ get_optimal_size(struct size current_size, struct size frame_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// w and h must fit into 16 bits
|
// w and h must fit into 16 bits
|
||||||
SDL_assert_release(w < 0x10000 && h < 0x10000);
|
assert(w < 0x10000 && h < 0x10000);
|
||||||
return (struct size) {w, h};
|
return (struct size) {w, h};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,8 +186,8 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
|||||||
window_flags |= SDL_WINDOW_BORDERLESS;
|
window_flags |= SDL_WINDOW_BORDERLESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int x = window_x != -1 ? window_x : SDL_WINDOWPOS_UNDEFINED;
|
int x = window_x != -1 ? window_x : (int) SDL_WINDOWPOS_UNDEFINED;
|
||||||
int y = window_y != -1 ? window_y : SDL_WINDOWPOS_UNDEFINED;
|
int y = window_y != -1 ? window_y : (int) SDL_WINDOWPOS_UNDEFINED;
|
||||||
screen->window = SDL_CreateWindow(window_title, x, y,
|
screen->window = SDL_CreateWindow(window_title, x, y,
|
||||||
window_size.width, window_size.height,
|
window_size.width, window_size.height,
|
||||||
window_flags);
|
window_flags);
|
||||||
@@ -392,8 +393,8 @@ screen_handle_window_event(struct screen *screen,
|
|||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
case SDL_WINDOWEVENT_MAXIMIZED:
|
||||||
// The backup size must be non-nul.
|
// The backup size must be non-nul.
|
||||||
SDL_assert(screen->windowed_window_size_backup.width);
|
assert(screen->windowed_window_size_backup.width);
|
||||||
SDL_assert(screen->windowed_window_size_backup.height);
|
assert(screen->windowed_window_size_backup.height);
|
||||||
// Revert the last size, it was updated while screen was maximized.
|
// Revert the last size, it was updated while screen was maximized.
|
||||||
screen->windowed_window_size = screen->windowed_window_size_backup;
|
screen->windowed_window_size = screen->windowed_window_size_backup;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
|
||||||
#include <SDL2/SDL_timer.h>
|
#include <SDL2/SDL_timer.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
#include "net.h"
|
#include "util/net.h"
|
||||||
|
|
||||||
#define SOCKET_NAME "scrcpy"
|
#define SOCKET_NAME "scrcpy"
|
||||||
#define SERVER_FILENAME "scrcpy-server"
|
#define SERVER_FILENAME "scrcpy-server"
|
||||||
|
|
||||||
#define DEFAULT_SERVER_PATH PREFIX "/share/scrcpy/" SERVER_FILENAME
|
#define DEFAULT_SERVER_PATH PREFIX "/share/scrcpy/" SERVER_FILENAME
|
||||||
#define DEVICE_SERVER_PATH "/data/local/tmp/" SERVER_FILENAME
|
#define DEVICE_SERVER_PATH "/data/local/tmp/scrcpy-server.jar"
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
get_server_path(void) {
|
get_server_path(void) {
|
||||||
@@ -124,7 +124,7 @@ execute_server(struct server *server, const struct server_params *params) {
|
|||||||
sprintf(max_fps_string, "%"PRIu16, params->max_fps);
|
sprintf(max_fps_string, "%"PRIu16, params->max_fps);
|
||||||
const char *const cmd[] = {
|
const char *const cmd[] = {
|
||||||
"shell",
|
"shell",
|
||||||
"CLASSPATH=/data/local/tmp/" SERVER_FILENAME,
|
"CLASSPATH=" DEVICE_SERVER_PATH,
|
||||||
"app_process",
|
"app_process",
|
||||||
#ifdef SERVER_DEBUGGER
|
#ifdef SERVER_DEBUGGER
|
||||||
# define SERVER_DEBUGGER_PORT "5005"
|
# define SERVER_DEBUGGER_PORT "5005"
|
||||||
@@ -199,7 +199,7 @@ connect_to_server(uint16_t port, uint32_t attempts, uint32_t delay) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
close_socket(socket_t *socket) {
|
close_socket(socket_t *socket) {
|
||||||
SDL_assert(*socket != INVALID_SOCKET);
|
assert(*socket != INVALID_SOCKET);
|
||||||
net_shutdown(*socket, SHUT_RDWR);
|
net_shutdown(*socket, SHUT_RDWR);
|
||||||
if (!net_close(*socket)) {
|
if (!net_close(*socket)) {
|
||||||
LOGW("Could not close socket");
|
LOGW("Could not close socket");
|
||||||
@@ -323,7 +323,7 @@ server_stop(struct server *server) {
|
|||||||
close_socket(&server->control_socket);
|
close_socket(&server->control_socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_assert(server->process != PROCESS_NONE);
|
assert(server->process != PROCESS_NONE);
|
||||||
|
|
||||||
if (!cmd_terminate(server->process)) {
|
if (!cmd_terminate(server->process)) {
|
||||||
LOGW("Could not terminate server");
|
LOGW("Could not terminate server");
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "net.h"
|
#include "util/net.h"
|
||||||
|
|
||||||
struct server {
|
struct server {
|
||||||
char *serial;
|
char *serial;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
#include <libavutil/time.h>
|
#include <libavutil/time.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
|
||||||
#include <SDL2/SDL_events.h>
|
#include <SDL2/SDL_events.h>
|
||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
@@ -10,12 +10,11 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "buffer_util.h"
|
|
||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
#include "lock_util.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "recorder.h"
|
#include "recorder.h"
|
||||||
|
#include "util/buffer_util.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
#define BUFSIZE 0x10000
|
#define BUFSIZE 0x10000
|
||||||
|
|
||||||
@@ -44,7 +43,8 @@ stream_recv_packet(struct stream *stream, AVPacket *packet) {
|
|||||||
|
|
||||||
uint64_t pts = buffer_read64be(header);
|
uint64_t pts = buffer_read64be(header);
|
||||||
uint32_t len = buffer_read32be(&header[8]);
|
uint32_t len = buffer_read32be(&header[8]);
|
||||||
SDL_assert(len);
|
assert(pts == NO_PTS || (pts & 0x8000000000000000) == 0);
|
||||||
|
assert(len);
|
||||||
|
|
||||||
if (av_new_packet(packet, len)) {
|
if (av_new_packet(packet, len)) {
|
||||||
LOGE("Could not allocate packet");
|
LOGE("Could not allocate packet");
|
||||||
@@ -52,12 +52,12 @@ stream_recv_packet(struct stream *stream, AVPacket *packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r = net_recv_all(stream->socket, packet->data, len);
|
r = net_recv_all(stream->socket, packet->data, len);
|
||||||
if (r < len) {
|
if (r < 0 || ((uint32_t) r) < len) {
|
||||||
av_packet_unref(packet);
|
av_packet_unref(packet);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
packet->pts = pts != NO_PTS ? pts : AV_NOPTS_VALUE;
|
packet->pts = pts != NO_PTS ? (int64_t) pts : AV_NOPTS_VALUE;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -107,8 +107,9 @@ stream_parse(struct stream *stream, AVPacket *packet) {
|
|||||||
AV_NOPTS_VALUE, AV_NOPTS_VALUE, -1);
|
AV_NOPTS_VALUE, AV_NOPTS_VALUE, -1);
|
||||||
|
|
||||||
// PARSER_FLAG_COMPLETE_FRAMES is set
|
// PARSER_FLAG_COMPLETE_FRAMES is set
|
||||||
SDL_assert(r == in_len);
|
assert(r == in_len);
|
||||||
SDL_assert(out_len == in_len);
|
(void) r;
|
||||||
|
assert(out_len == in_len);
|
||||||
|
|
||||||
if (stream->parser->key_frame == 1) {
|
if (stream->parser->key_frame == 1) {
|
||||||
packet->flags |= AV_PKT_FLAG_KEY;
|
packet->flags |= AV_PKT_FLAG_KEY;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "net.h"
|
#include "util/net.h"
|
||||||
|
|
||||||
struct video_buffer;
|
struct video_buffer;
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,11 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "log.h"
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
enum process_result
|
enum process_result
|
||||||
cmd_execute(const char *path, const char *const argv[], pid_t *pid) {
|
cmd_execute(const char *const argv[], pid_t *pid) {
|
||||||
int fd[2];
|
int fd[2];
|
||||||
|
|
||||||
if (pipe(fd) == -1) {
|
if (pipe(fd) == -1) {
|
||||||
@@ -51,7 +52,7 @@ cmd_execute(const char *path, const char *const argv[], pid_t *pid) {
|
|||||||
// child close read side
|
// child close read side
|
||||||
close(fd[0]);
|
close(fd[0]);
|
||||||
if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) == 0) {
|
if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) == 0) {
|
||||||
execvp(path, (char *const *)argv);
|
execvp(argv[0], (char *const *)argv);
|
||||||
if (errno == ENOENT) {
|
if (errno == ENOENT) {
|
||||||
ret = PROCESS_ERROR_MISSING_BINARY;
|
ret = PROCESS_ERROR_MISSING_BINARY;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "net.h"
|
#include "util/net.h"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
#include "str_util.h"
|
#include "util/str_util.h"
|
||||||
|
|
||||||
static int
|
static int
|
||||||
build_cmd(char *cmd, size_t len, const char *const argv[]) {
|
build_cmd(char *cmd, size_t len, const char *const argv[]) {
|
||||||
@@ -19,7 +19,7 @@ build_cmd(char *cmd, size_t len, const char *const argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum process_result
|
enum process_result
|
||||||
cmd_execute(const char *path, const char *const argv[], HANDLE *handle) {
|
cmd_execute(const char *const argv[], HANDLE *handle) {
|
||||||
STARTUPINFOW si;
|
STARTUPINFOW si;
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
memset(&si, 0, sizeof(si));
|
memset(&si, 0, sizeof(si));
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "net.h"
|
#include "util/net.h"
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
net_init(void) {
|
net_init(void) {
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
#include "tiny_xpm.h"
|
#include "tiny_xpm.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
struct index {
|
struct index {
|
||||||
char c;
|
char c;
|
||||||
@@ -36,7 +37,7 @@ find_color(struct index *index, int len, char c, uint32_t *color) {
|
|||||||
// (non-const) "char *"
|
// (non-const) "char *"
|
||||||
SDL_Surface *
|
SDL_Surface *
|
||||||
read_xpm(char *xpm[]) {
|
read_xpm(char *xpm[]) {
|
||||||
#if SDL_ASSERT_LEVEL >= 2
|
#ifndef NDEBUG
|
||||||
// patch the XPM to change the icon color in debug mode
|
// patch the XPM to change the icon color in debug mode
|
||||||
xpm[2] = ". c #CC00CC";
|
xpm[2] = ". c #CC00CC";
|
||||||
#endif
|
#endif
|
||||||
@@ -51,24 +52,26 @@ read_xpm(char *xpm[]) {
|
|||||||
int chars = strtol(endptr + 1, &endptr, 10);
|
int chars = strtol(endptr + 1, &endptr, 10);
|
||||||
|
|
||||||
// sanity checks
|
// sanity checks
|
||||||
SDL_assert(0 <= width && width < 256);
|
assert(0 <= width && width < 256);
|
||||||
SDL_assert(0 <= height && height < 256);
|
assert(0 <= height && height < 256);
|
||||||
SDL_assert(0 <= colors && colors < 256);
|
assert(0 <= colors && colors < 256);
|
||||||
SDL_assert(chars == 1); // this implementation does not support more
|
assert(chars == 1); // this implementation does not support more
|
||||||
|
|
||||||
|
(void) chars;
|
||||||
|
|
||||||
// init index
|
// init index
|
||||||
struct index index[colors];
|
struct index index[colors];
|
||||||
for (int i = 0; i < colors; ++i) {
|
for (int i = 0; i < colors; ++i) {
|
||||||
const char *line = xpm[1+i];
|
const char *line = xpm[1+i];
|
||||||
index[i].c = line[0];
|
index[i].c = line[0];
|
||||||
SDL_assert(line[1] == '\t');
|
assert(line[1] == '\t');
|
||||||
SDL_assert(line[2] == 'c');
|
assert(line[2] == 'c');
|
||||||
SDL_assert(line[3] == ' ');
|
assert(line[3] == ' ');
|
||||||
if (line[4] == '#') {
|
if (line[4] == '#') {
|
||||||
index[i].color = 0xff000000 | strtol(&line[5], &endptr, 0x10);
|
index[i].color = 0xff000000 | strtol(&line[5], &endptr, 0x10);
|
||||||
SDL_assert(*endptr == '\0');
|
assert(*endptr == '\0');
|
||||||
} else {
|
} else {
|
||||||
SDL_assert(!strcmp("None", &line[4]));
|
assert(!strcmp("None", &line[4]));
|
||||||
index[i].color = 0;
|
index[i].color = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,7 +88,8 @@ read_xpm(char *xpm[]) {
|
|||||||
char c = line[x];
|
char c = line[x];
|
||||||
uint32_t color;
|
uint32_t color;
|
||||||
bool color_found = find_color(index, colors, c, &color);
|
bool color_found = find_color(index, colors, c, &color);
|
||||||
SDL_assert(color_found);
|
assert(color_found);
|
||||||
|
(void) color_found;
|
||||||
pixels[y * width + x] = color;
|
pixels[y * width + x] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ buffer_read32be(const uint8_t *buf) {
|
|||||||
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline
|
static inline uint64_t
|
||||||
uint64_t buffer_read64be(const uint8_t *buf) {
|
buffer_read64be(const uint8_t *buf) {
|
||||||
uint32_t msb = buffer_read32be(buf);
|
uint32_t msb = buffer_read32be(buf);
|
||||||
uint32_t lsb = buffer_read32be(&buf[4]);
|
uint32_t lsb = buffer_read32be(&buf[4]);
|
||||||
return ((uint64_t) msb << 32) | lsb;
|
return ((uint64_t) msb << 32) | lsb;
|
||||||
74
app/src/util/lock.h
Normal file
74
app/src/util/lock.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#ifndef LOCK_H
|
||||||
|
#define LOCK_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <SDL2/SDL_mutex.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
mutex_lock(SDL_mutex *mutex) {
|
||||||
|
int r = SDL_LockMutex(mutex);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (r) {
|
||||||
|
LOGC("Could not lock mutex: %s", SDL_GetError());
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void) r;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
mutex_unlock(SDL_mutex *mutex) {
|
||||||
|
int r = SDL_UnlockMutex(mutex);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (r) {
|
||||||
|
LOGC("Could not unlock mutex: %s", SDL_GetError());
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void) r;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
cond_wait(SDL_cond *cond, SDL_mutex *mutex) {
|
||||||
|
int r = SDL_CondWait(cond, mutex);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (r) {
|
||||||
|
LOGC("Could not wait on condition: %s", SDL_GetError());
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void) r;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
cond_wait_timeout(SDL_cond *cond, SDL_mutex *mutex, uint32_t ms) {
|
||||||
|
int r = SDL_CondWaitTimeout(cond, mutex, ms);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (r < 0) {
|
||||||
|
LOGC("Could not wait on condition with timeout: %s", SDL_GetError());
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
cond_signal(SDL_cond *cond) {
|
||||||
|
int r = SDL_CondSignal(cond);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (r) {
|
||||||
|
LOGC("Could not signal a condition: %s", SDL_GetError());
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void) r;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
#ifndef QUEUE_H
|
#ifndef QUEUE_H
|
||||||
#define QUEUE_H
|
#define QUEUE_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
// type so that we can "return" it)
|
// type so that we can "return" it)
|
||||||
#define queue_take(PQ, NEXTFIELD, PITEM) \
|
#define queue_take(PQ, NEXTFIELD, PITEM) \
|
||||||
(void) ({ \
|
(void) ({ \
|
||||||
SDL_assert(!queue_is_empty(PQ)); \
|
assert(!queue_is_empty(PQ)); \
|
||||||
*(PITEM) = (PQ)->first; \
|
*(PITEM) = (PQ)->first; \
|
||||||
(PQ)->first = (PQ)->first->NEXTFIELD; \
|
(PQ)->first = (PQ)->first->NEXTFIELD; \
|
||||||
})
|
})
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
#include "video_buffer.h"
|
#include "video_buffer.h"
|
||||||
|
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <assert.h>
|
||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <libavutil/avutil.h>
|
#include <libavutil/avutil.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "lock_util.h"
|
#include "util/lock.h"
|
||||||
#include "log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
video_buffer_init(struct video_buffer *vb, struct fps_counter *fps_counter,
|
video_buffer_init(struct video_buffer *vb, struct fps_counter *fps_counter,
|
||||||
@@ -91,7 +91,7 @@ video_buffer_offer_decoded_frame(struct video_buffer *vb,
|
|||||||
|
|
||||||
const AVFrame *
|
const AVFrame *
|
||||||
video_buffer_consume_rendered_frame(struct video_buffer *vb) {
|
video_buffer_consume_rendered_frame(struct video_buffer *vb) {
|
||||||
SDL_assert(!vb->rendering_frame_consumed);
|
assert(!vb->rendering_frame_consumed);
|
||||||
vb->rendering_frame_consumed = true;
|
vb->rendering_frame_consumed = true;
|
||||||
fps_counter_add_rendered_frame(vb->fps_counter);
|
fps_counter_add_rendered_frame(vb->fps_counter);
|
||||||
if (vb->render_expired_frames) {
|
if (vb->render_expired_frames) {
|
||||||
|
|||||||
76
app/tests/test_buffer_util.c
Normal file
76
app/tests/test_buffer_util.c
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "util/buffer_util.h"
|
||||||
|
|
||||||
|
static void test_buffer_write16be(void) {
|
||||||
|
uint16_t val = 0xABCD;
|
||||||
|
uint8_t buf[2];
|
||||||
|
|
||||||
|
buffer_write16be(buf, val);
|
||||||
|
|
||||||
|
assert(buf[0] == 0xAB);
|
||||||
|
assert(buf[1] == 0xCD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_buffer_write32be(void) {
|
||||||
|
uint32_t val = 0xABCD1234;
|
||||||
|
uint8_t buf[4];
|
||||||
|
|
||||||
|
buffer_write32be(buf, val);
|
||||||
|
|
||||||
|
assert(buf[0] == 0xAB);
|
||||||
|
assert(buf[1] == 0xCD);
|
||||||
|
assert(buf[2] == 0x12);
|
||||||
|
assert(buf[3] == 0x34);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_buffer_write64be(void) {
|
||||||
|
uint64_t val = 0xABCD1234567890EF;
|
||||||
|
uint8_t buf[8];
|
||||||
|
|
||||||
|
buffer_write64be(buf, val);
|
||||||
|
|
||||||
|
assert(buf[0] == 0xAB);
|
||||||
|
assert(buf[1] == 0xCD);
|
||||||
|
assert(buf[2] == 0x12);
|
||||||
|
assert(buf[3] == 0x34);
|
||||||
|
assert(buf[4] == 0x56);
|
||||||
|
assert(buf[5] == 0x78);
|
||||||
|
assert(buf[6] == 0x90);
|
||||||
|
assert(buf[7] == 0xEF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_buffer_read16be(void) {
|
||||||
|
uint8_t buf[2] = {0xAB, 0xCD};
|
||||||
|
|
||||||
|
uint16_t val = buffer_read16be(buf);
|
||||||
|
|
||||||
|
assert(val == 0xABCD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_buffer_read32be(void) {
|
||||||
|
uint8_t buf[4] = {0xAB, 0xCD, 0x12, 0x34};
|
||||||
|
|
||||||
|
uint32_t val = buffer_read32be(buf);
|
||||||
|
|
||||||
|
assert(val == 0xABCD1234);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_buffer_read64be(void) {
|
||||||
|
uint8_t buf[8] = {0xAB, 0xCD, 0x12, 0x34,
|
||||||
|
0x56, 0x78, 0x90, 0xEF};
|
||||||
|
|
||||||
|
uint64_t val = buffer_read64be(buf);
|
||||||
|
|
||||||
|
assert(val == 0xABCD1234567890EF);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
test_buffer_write16be();
|
||||||
|
test_buffer_write32be();
|
||||||
|
test_buffer_write64be();
|
||||||
|
test_buffer_read16be();
|
||||||
|
test_buffer_read32be();
|
||||||
|
test_buffer_read64be();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "cbuf.h"
|
#include "util/cbuf.h"
|
||||||
|
|
||||||
struct int_queue CBUF(int, 32);
|
struct int_queue CBUF(int, 32);
|
||||||
|
|
||||||
|
|||||||
@@ -236,6 +236,21 @@ static void test_serialize_set_screen_power_mode(void) {
|
|||||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_serialize_rotate_device(void) {
|
||||||
|
struct control_msg msg = {
|
||||||
|
.type = CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
|
||||||
|
int size = control_msg_serialize(&msg, buf);
|
||||||
|
assert(size == 1);
|
||||||
|
|
||||||
|
const unsigned char expected[] = {
|
||||||
|
CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
||||||
|
};
|
||||||
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
test_serialize_inject_keycode();
|
test_serialize_inject_keycode();
|
||||||
test_serialize_inject_text();
|
test_serialize_inject_text();
|
||||||
@@ -248,5 +263,6 @@ int main(void) {
|
|||||||
test_serialize_get_clipboard();
|
test_serialize_get_clipboard();
|
||||||
test_serialize_set_clipboard();
|
test_serialize_set_clipboard();
|
||||||
test_serialize_set_screen_power_mode();
|
test_serialize_set_screen_power_mode();
|
||||||
|
test_serialize_rotate_device();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include <queue.h>
|
#include "util/queue.h"
|
||||||
|
|
||||||
struct foo {
|
struct foo {
|
||||||
int value;
|
int value;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "str_util.h"
|
#include "util/str_util.h"
|
||||||
|
|
||||||
static void test_xstrncpy_simple(void) {
|
static void test_xstrncpy_simple(void) {
|
||||||
char s[] = "xxxxxxxxxx";
|
char s[] = "xxxxxxxxxx";
|
||||||
@@ -126,6 +127,16 @@ static void test_xstrjoin_truncated_after_sep(void) {
|
|||||||
assert(!strcmp("abc de ", s));
|
assert(!strcmp("abc de ", s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_strquote(void) {
|
||||||
|
const char *s = "abcde";
|
||||||
|
char *out = strquote(s);
|
||||||
|
|
||||||
|
// add '"' at the beginning and the end
|
||||||
|
assert(!strcmp("\"abcde\"", out));
|
||||||
|
|
||||||
|
SDL_free(out);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_utf8_truncate(void) {
|
static void test_utf8_truncate(void) {
|
||||||
const char *s = "aÉbÔc";
|
const char *s = "aÉbÔc";
|
||||||
assert(strlen(s) == 7); // É and Ô are 2 bytes-wide
|
assert(strlen(s) == 7); // É and Ô are 2 bytes-wide
|
||||||
@@ -166,6 +177,7 @@ int main(void) {
|
|||||||
test_xstrjoin_truncated_in_token();
|
test_xstrjoin_truncated_in_token();
|
||||||
test_xstrjoin_truncated_before_sep();
|
test_xstrjoin_truncated_before_sep();
|
||||||
test_xstrjoin_truncated_after_sep();
|
test_xstrjoin_truncated_after_sep();
|
||||||
|
test_strquote();
|
||||||
test_utf8_truncate();
|
test_utf8_truncate();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
project('scrcpy', 'c',
|
project('scrcpy', 'c',
|
||||||
version: '1.11',
|
version: '1.11',
|
||||||
meson_version: '>= 0.37',
|
meson_version: '>= 0.37',
|
||||||
default_options: 'c_std=c11')
|
default_options: [
|
||||||
|
'c_std=c11',
|
||||||
|
'warning_level=2',
|
||||||
|
])
|
||||||
|
|
||||||
if get_option('compile_app')
|
if get_option('compile_app')
|
||||||
subdir('app')
|
subdir('app')
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ EOF
|
|||||||
|
|
||||||
echo "Generating java from aidl..."
|
echo "Generating java from aidl..."
|
||||||
cd "$SERVER_DIR/src/main/aidl"
|
cd "$SERVER_DIR/src/main/aidl"
|
||||||
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/aidl" -o "$CLASSES_DIR" \
|
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/aidl" -o"$CLASSES_DIR" \
|
||||||
android/view/IRotationWatcher.aidl
|
android/view/IRotationWatcher.aidl
|
||||||
|
|
||||||
echo "Compiling java sources..."
|
echo "Compiling java sources..."
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public final class ControlMessage {
|
|||||||
public static final int TYPE_GET_CLIPBOARD = 7;
|
public static final int TYPE_GET_CLIPBOARD = 7;
|
||||||
public static final int TYPE_SET_CLIPBOARD = 8;
|
public static final int TYPE_SET_CLIPBOARD = 8;
|
||||||
public static final int TYPE_SET_SCREEN_POWER_MODE = 9;
|
public static final int TYPE_SET_SCREEN_POWER_MODE = 9;
|
||||||
|
public static final int TYPE_ROTATE_DEVICE = 10;
|
||||||
|
|
||||||
private int type;
|
private int type;
|
||||||
private String text;
|
private String text;
|
||||||
@@ -47,8 +48,7 @@ public final class ControlMessage {
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createInjectTouchEvent(int action, long pointerId, Position position, float pressure,
|
public static ControlMessage createInjectTouchEvent(int action, long pointerId, Position position, float pressure, int buttons) {
|
||||||
int buttons) {
|
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage msg = new ControlMessage();
|
||||||
msg.type = TYPE_INJECT_TOUCH_EVENT;
|
msg.type = TYPE_INJECT_TOUCH_EVENT;
|
||||||
msg.action = action;
|
msg.action = action;
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ public class ControlMessageReader {
|
|||||||
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||||
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
||||||
case ControlMessage.TYPE_GET_CLIPBOARD:
|
case ControlMessage.TYPE_GET_CLIPBOARD:
|
||||||
|
case ControlMessage.TYPE_ROTATE_DEVICE:
|
||||||
msg = ControlMessage.createEmpty(type);
|
msg = ControlMessage.createEmpty(type);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import java.io.IOException;
|
|||||||
|
|
||||||
public class Controller {
|
public class Controller {
|
||||||
|
|
||||||
|
private static final int DEVICE_ID_VIRTUAL = -1;
|
||||||
|
|
||||||
private final Device device;
|
private final Device device;
|
||||||
private final DesktopConnection connection;
|
private final DesktopConnection connection;
|
||||||
private final DeviceMessageSender sender;
|
private final DeviceMessageSender sender;
|
||||||
@@ -21,10 +23,8 @@ public class Controller {
|
|||||||
|
|
||||||
private long lastTouchDown;
|
private long lastTouchDown;
|
||||||
private final PointersState pointersState = new PointersState();
|
private final PointersState pointersState = new PointersState();
|
||||||
private final MotionEvent.PointerProperties[] pointerProperties =
|
private final MotionEvent.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[PointersState.MAX_POINTERS];
|
||||||
new MotionEvent.PointerProperties[PointersState.MAX_POINTERS];
|
private final MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[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;
|
||||||
@@ -106,6 +106,9 @@ public class Controller {
|
|||||||
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
||||||
device.setScreenPowerMode(msg.getAction());
|
device.setScreenPowerMode(msg.getAction());
|
||||||
break;
|
break;
|
||||||
|
case ControlMessage.TYPE_ROTATE_DEVICE:
|
||||||
|
device.rotateDevice();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
@@ -176,8 +179,9 @@ public class Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties,
|
MotionEvent event = MotionEvent
|
||||||
pointerCoords, 0, buttons, 1f, 1f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
|
.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEVICE_ID_VIRTUAL, 0,
|
||||||
|
InputDevice.SOURCE_TOUCHSCREEN, 0);
|
||||||
return injectEvent(event);
|
return injectEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,15 +202,16 @@ public class Controller {
|
|||||||
coords.setAxisValue(MotionEvent.AXIS_HSCROLL, hScroll);
|
coords.setAxisValue(MotionEvent.AXIS_HSCROLL, hScroll);
|
||||||
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
|
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
|
||||||
|
|
||||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties,
|
MotionEvent event = MotionEvent
|
||||||
pointerCoords, 0, 0, 1f, 1f, 0, 0, InputDevice.SOURCE_MOUSE, 0);
|
.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, DEVICE_ID_VIRTUAL, 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.genymobile.scrcpy;
|
|||||||
|
|
||||||
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||||
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||||
|
import com.genymobile.scrcpy.wrappers.WindowManager;
|
||||||
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -170,6 +171,27 @@ public final class Device {
|
|||||||
Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on"));
|
Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable auto-rotation (if enabled), set the screen rotation and re-enable auto-rotation (if it was enabled).
|
||||||
|
*/
|
||||||
|
public void rotateDevice() {
|
||||||
|
WindowManager wm = serviceManager.getWindowManager();
|
||||||
|
|
||||||
|
boolean accelerometerRotation = !wm.isRotationFrozen();
|
||||||
|
|
||||||
|
int currentRotation = wm.getRotation();
|
||||||
|
int newRotation = (currentRotation & 1) ^ 1; // 0->1, 1->0, 2->1, 3->0
|
||||||
|
String newRotationString = newRotation == 0 ? "portrait" : "landscape";
|
||||||
|
|
||||||
|
Ln.i("Device rotation requested: " + newRotationString);
|
||||||
|
wm.freezeRotation(newRotation);
|
||||||
|
|
||||||
|
// restore auto-rotate if necessary
|
||||||
|
if (accelerometerRotation) {
|
||||||
|
wm.thawRotation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Rect flipRect(Rect crop) {
|
static Rect flipRect(Rect crop) {
|
||||||
return new Rect(crop.top, crop.left, crop.bottom, crop.right);
|
return new Rect(crop.top, crop.left, crop.bottom, crop.right);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,7 @@ public final class Ln {
|
|||||||
private static final String PREFIX = "[server] ";
|
private static final String PREFIX = "[server] ";
|
||||||
|
|
||||||
enum Level {
|
enum Level {
|
||||||
DEBUG,
|
DEBUG, INFO, WARN, ERROR
|
||||||
INFO,
|
|
||||||
WARN,
|
|
||||||
ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Level THRESHOLD = BuildConfig.DEBUG ? Level.DEBUG : Level.INFO;
|
private static final Level THRESHOLD = BuildConfig.DEBUG ? Level.DEBUG : Level.INFO;
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ public class Point {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Point point = (Point) o;
|
Point point = (Point) o;
|
||||||
return x == point.x
|
return x == point.x && y == point.y;
|
||||||
&& y == point.y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -39,9 +38,6 @@ public class Point {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Point{"
|
return "Point{" + "x=" + x + ", y=" + y + '}';
|
||||||
+ "x=" + x
|
|
||||||
+ ", y=" + y
|
|
||||||
+ '}';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ public class Position {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Position position = (Position) o;
|
Position position = (Position) o;
|
||||||
return Objects.equals(point, position.point)
|
return Objects.equals(point, position.point) && Objects.equals(screenSize, position.screenSize);
|
||||||
&& Objects.equals(screenSize, position.screenSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -43,10 +42,7 @@ public class Position {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Position{"
|
return "Position{" + "point=" + point + ", screenSize=" + screenSize + '}';
|
||||||
+ "point=" + point
|
|
||||||
+ ", screenSize=" + screenSize
|
|
||||||
+ '}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
package com.genymobile.scrcpy;
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.media.MediaCodec;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public final class Server {
|
public final class Server {
|
||||||
|
|
||||||
private static final String SERVER_PATH = "/data/local/tmp/scrcpy-server";
|
private static final String SERVER_PATH = "/data/local/tmp/scrcpy-server.jar";
|
||||||
|
|
||||||
private Server() {
|
private Server() {
|
||||||
// not instantiable
|
// not instantiable
|
||||||
@@ -73,8 +75,8 @@ public final class Server {
|
|||||||
|
|
||||||
String clientVersion = args[0];
|
String clientVersion = args[0];
|
||||||
if (!clientVersion.equals(BuildConfig.VERSION_NAME)) {
|
if (!clientVersion.equals(BuildConfig.VERSION_NAME)) {
|
||||||
throw new IllegalArgumentException("The server version (" + clientVersion + ") does not match the client "
|
throw new IllegalArgumentException(
|
||||||
+ "(" + BuildConfig.VERSION_NAME + ")");
|
"The server version (" + clientVersion + ") does not match the client " + "(" + BuildConfig.VERSION_NAME + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length != 8) {
|
if (args.length != 8) {
|
||||||
@@ -133,11 +135,25 @@ public final class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void suggestFix(Throwable e) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
if (e instanceof MediaCodec.CodecException) {
|
||||||
|
MediaCodec.CodecException mce = (MediaCodec.CodecException) e;
|
||||||
|
if (mce.getErrorCode() == 0xfffffc0e) {
|
||||||
|
Ln.e("The hardware encoder is not able to encode at the given definition.");
|
||||||
|
Ln.e("Try with a lower definition:");
|
||||||
|
Ln.e(" scrcpy -m 1024");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String... args) throws Exception {
|
public static void main(String... args) throws Exception {
|
||||||
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void uncaughtException(Thread t, Throwable e) {
|
public void uncaughtException(Thread t, Throwable e) {
|
||||||
Ln.e("Exception on thread " + t, e);
|
Ln.e("Exception on thread " + t, e);
|
||||||
|
suggestFix(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ public final class Size {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Size size = (Size) o;
|
Size size = (Size) o;
|
||||||
return width == size.width
|
return width == size.width && height == size.height;
|
||||||
&& height == size.height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -49,9 +48,6 @@ public final class Size {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Size{"
|
return "Size{" + "width=" + width + ", height=" + height + '}';
|
||||||
+ "width=" + width
|
|
||||||
+ ", height=" + height
|
|
||||||
+ '}';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
package com.genymobile.scrcpy;
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.app.Instrumentation;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public final class Workarounds {
|
public final class Workarounds {
|
||||||
private Workarounds() {
|
private Workarounds() {
|
||||||
@@ -48,14 +52,25 @@ public final class Workarounds {
|
|||||||
applicationInfo.packageName = "com.genymobile.scrcpy";
|
applicationInfo.packageName = "com.genymobile.scrcpy";
|
||||||
|
|
||||||
// appBindData.appInfo = applicationInfo;
|
// appBindData.appInfo = applicationInfo;
|
||||||
Field appInfo = appBindDataClass.getDeclaredField("appInfo");
|
Field appInfoField = appBindDataClass.getDeclaredField("appInfo");
|
||||||
appInfo.setAccessible(true);
|
appInfoField.setAccessible(true);
|
||||||
appInfo.set(appBindData, applicationInfo);
|
appInfoField.set(appBindData, applicationInfo);
|
||||||
|
|
||||||
// activityThread.mBoundApplication = appBindData;
|
// activityThread.mBoundApplication = appBindData;
|
||||||
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
||||||
mBoundApplicationField.setAccessible(true);
|
mBoundApplicationField.setAccessible(true);
|
||||||
mBoundApplicationField.set(activityThread, appBindData);
|
mBoundApplicationField.set(activityThread, appBindData);
|
||||||
|
|
||||||
|
// Context ctx = activityThread.getSystemContext();
|
||||||
|
Method getSystemContextMethod = activityThreadClass.getDeclaredMethod("getSystemContext");
|
||||||
|
Context ctx = (Context) getSystemContextMethod.invoke(activityThread);
|
||||||
|
|
||||||
|
Application app = Instrumentation.newApplication(Application.class, ctx);
|
||||||
|
|
||||||
|
// activityThread.mInitialApplication = app;
|
||||||
|
Field mInitialApplicationField = activityThreadClass.getDeclaredField("mInitialApplication");
|
||||||
|
mInitialApplicationField.setAccessible(true);
|
||||||
|
mInitialApplicationField.set(activityThread, app);
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
// this is a workaround, so failing is not an error
|
// this is a workaround, so failing is not an error
|
||||||
Ln.w("Could not fill app info: " + throwable.getMessage());
|
Ln.w("Could not fill app info: " + throwable.getMessage());
|
||||||
|
|||||||
@@ -22,47 +22,37 @@ public class ClipboardManager {
|
|||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method getGetPrimaryClipMethod() {
|
private Method getGetPrimaryClipMethod() throws NoSuchMethodException {
|
||||||
if (getPrimaryClipMethod == null) {
|
if (getPrimaryClipMethod == null) {
|
||||||
try {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class);
|
||||||
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class);
|
} else {
|
||||||
} else {
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class);
|
||||||
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class);
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getPrimaryClipMethod;
|
return getPrimaryClipMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method getSetPrimaryClipMethod() {
|
private Method getSetPrimaryClipMethod() throws NoSuchMethodException {
|
||||||
if (setPrimaryClipMethod == null) {
|
if (setPrimaryClipMethod == null) {
|
||||||
try {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class);
|
||||||
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class);
|
} else {
|
||||||
} else {
|
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, int.class);
|
||||||
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class,
|
|
||||||
String.class, int.class);
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return setPrimaryClipMethod;
|
return setPrimaryClipMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClipData getPrimaryClip(Method method, IInterface manager) throws InvocationTargetException,
|
private static ClipData getPrimaryClip(Method method, IInterface manager) throws InvocationTargetException, IllegalAccessException {
|
||||||
IllegalAccessException {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
return (ClipData) method.invoke(manager, PACKAGE_NAME);
|
return (ClipData) method.invoke(manager, PACKAGE_NAME);
|
||||||
}
|
}
|
||||||
return (ClipData) method.invoke(manager, PACKAGE_NAME, USER_ID);
|
return (ClipData) method.invoke(manager, PACKAGE_NAME, USER_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setPrimaryClip(Method method, IInterface manager, ClipData clipData) throws InvocationTargetException,
|
private static void setPrimaryClip(Method method, IInterface manager, ClipData clipData)
|
||||||
IllegalAccessException {
|
throws InvocationTargetException, IllegalAccessException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
method.invoke(manager, clipData, PACKAGE_NAME);
|
method.invoke(manager, clipData, PACKAGE_NAME);
|
||||||
} else {
|
} else {
|
||||||
@@ -71,32 +61,26 @@ public class ClipboardManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CharSequence getText() {
|
public CharSequence getText() {
|
||||||
Method method = getGetPrimaryClipMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
|
Method method = getGetPrimaryClipMethod();
|
||||||
ClipData clipData = getPrimaryClip(method, manager);
|
ClipData clipData = getPrimaryClip(method, manager);
|
||||||
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 | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
Ln.e("Could not invoke method", e);
|
||||||
return null;
|
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);
|
|
||||||
try {
|
try {
|
||||||
|
Method method = getSetPrimaryClipMethod();
|
||||||
|
ClipData clipData = ClipData.newPlainText(null, text);
|
||||||
setPrimaryClip(method, manager, clipData);
|
setPrimaryClip(method, manager, clipData);
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
Ln.e("Could not invoke method", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,26 +21,19 @@ public final class InputManager {
|
|||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method getInjectInputEventMethod() {
|
private Method getInjectInputEventMethod() throws NoSuchMethodException {
|
||||||
if (injectInputEventMethod == null) {
|
if (injectInputEventMethod == null) {
|
||||||
try {
|
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class);
|
||||||
injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return injectInputEventMethod;
|
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);
|
Method method = getInjectInputEventMethod();
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
return (boolean) method.invoke(manager, inputEvent, mode);
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
|
Ln.e("Could not invoke method", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,28 +17,21 @@ public final class PowerManager {
|
|||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method getIsScreenOnMethod() {
|
private Method getIsScreenOnMethod() throws NoSuchMethodException {
|
||||||
if (isScreenOnMethod == null) {
|
if (isScreenOnMethod == null) {
|
||||||
try {
|
@SuppressLint("ObsoleteSdkInt") // we may lower minSdkVersion in the future
|
||||||
@SuppressLint("ObsoleteSdkInt") // we may lower minSdkVersion in the future
|
String methodName = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH ? "isInteractive" : "isScreenOn";
|
||||||
String methodName = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH ? "isInteractive" : "isScreenOn";
|
isScreenOnMethod = manager.getClass().getMethod(methodName);
|
||||||
isScreenOnMethod = manager.getClass().getMethod(methodName);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return isScreenOnMethod;
|
return isScreenOnMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isScreenOn() {
|
public boolean isScreenOn() {
|
||||||
Method method = getIsScreenOnMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
return (Boolean) method.invoke(manager);
|
Method method = getIsScreenOnMethod();
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
return (boolean) method.invoke(manager);
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
|
Ln.e("Could not invoke method", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,49 +17,35 @@ public class StatusBarManager {
|
|||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method getExpandNotificationsPanelMethod() {
|
private Method getExpandNotificationsPanelMethod() throws NoSuchMethodException {
|
||||||
if (expandNotificationsPanelMethod == null) {
|
if (expandNotificationsPanelMethod == null) {
|
||||||
try {
|
expandNotificationsPanelMethod = manager.getClass().getMethod("expandNotificationsPanel");
|
||||||
expandNotificationsPanelMethod = manager.getClass().getMethod("expandNotificationsPanel");
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return expandNotificationsPanelMethod;
|
return expandNotificationsPanelMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method getCollapsePanelsMethod() {
|
private Method getCollapsePanelsMethod() throws NoSuchMethodException {
|
||||||
if (collapsePanelsMethod == null) {
|
if (collapsePanelsMethod == null) {
|
||||||
try {
|
collapsePanelsMethod = manager.getClass().getMethod("collapsePanels");
|
||||||
collapsePanelsMethod = manager.getClass().getMethod("collapsePanels");
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return collapsePanelsMethod;
|
return collapsePanelsMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expandNotificationsPanel() {
|
public void expandNotificationsPanel() {
|
||||||
Method method = getExpandNotificationsPanelMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
|
Method method = getExpandNotificationsPanelMethod();
|
||||||
method.invoke(manager);
|
method.invoke(manager);
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
Ln.e("Could not invoke method", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void collapsePanels() {
|
public void collapsePanels() {
|
||||||
Method method = getCollapsePanelsMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
|
Method method = getCollapsePanelsMethod();
|
||||||
method.invoke(manager);
|
method.invoke(manager);
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
Ln.e("Could not invoke method", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,29 +84,23 @@ public final class SurfaceControl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Method getGetBuiltInDisplayMethod() {
|
private static Method getGetBuiltInDisplayMethod() throws NoSuchMethodException {
|
||||||
if (getBuiltInDisplayMethod == null) {
|
if (getBuiltInDisplayMethod == null) {
|
||||||
try {
|
// the method signature has changed in Android Q
|
||||||
// the method signature has changed in Android Q
|
// <https://github.com/Genymobile/scrcpy/issues/586>
|
||||||
// <https://github.com/Genymobile/scrcpy/issues/586>
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
getBuiltInDisplayMethod = CLASS.getMethod("getBuiltInDisplay", int.class);
|
||||||
getBuiltInDisplayMethod = CLASS.getMethod("getBuiltInDisplay", int.class);
|
} else {
|
||||||
} else {
|
getBuiltInDisplayMethod = CLASS.getMethod("getInternalDisplayToken");
|
||||||
getBuiltInDisplayMethod = CLASS.getMethod("getInternalDisplayToken");
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getBuiltInDisplayMethod;
|
return getBuiltInDisplayMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IBinder getBuiltInDisplay() {
|
public static IBinder getBuiltInDisplay() {
|
||||||
Method method = getGetBuiltInDisplayMethod();
|
|
||||||
if (method == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
|
Method method = getGetBuiltInDisplayMethod();
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
// call getBuiltInDisplay(0)
|
// call getBuiltInDisplay(0)
|
||||||
return (IBinder) method.invoke(null, 0);
|
return (IBinder) method.invoke(null, 0);
|
||||||
@@ -114,32 +108,25 @@ public final class SurfaceControl {
|
|||||||
|
|
||||||
// call getInternalDisplayToken()
|
// call getInternalDisplayToken()
|
||||||
return (IBinder) method.invoke(null);
|
return (IBinder) method.invoke(null);
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
Ln.e("Could not invoke method", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Method getSetDisplayPowerModeMethod() {
|
private static Method getSetDisplayPowerModeMethod() throws NoSuchMethodException {
|
||||||
if (setDisplayPowerModeMethod == null) {
|
if (setDisplayPowerModeMethod == null) {
|
||||||
try {
|
setDisplayPowerModeMethod = CLASS.getMethod("setDisplayPowerMode", IBinder.class, int.class);
|
||||||
setDisplayPowerModeMethod = CLASS.getMethod("setDisplayPowerMode", IBinder.class, int.class);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Ln.e("Could not find method", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return setDisplayPowerModeMethod;
|
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 method = getSetDisplayPowerModeMethod();
|
||||||
method.invoke(null, displayToken, mode);
|
method.invoke(null, displayToken, mode);
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke " + method.getName(), e);
|
Ln.e("Could not invoke method", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,95 @@
|
|||||||
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.IRotationWatcher;
|
import android.view.IRotationWatcher;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public final class WindowManager {
|
public final class WindowManager {
|
||||||
private final IInterface manager;
|
private final IInterface manager;
|
||||||
|
private Method getRotationMethod;
|
||||||
|
private Method freezeRotationMethod;
|
||||||
|
private Method isRotationFrozenMethod;
|
||||||
|
private Method thawRotationMethod;
|
||||||
|
|
||||||
public WindowManager(IInterface manager) {
|
public WindowManager(IInterface manager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getRotation() {
|
private Method getGetRotationMethod() throws NoSuchMethodException {
|
||||||
try {
|
if (getRotationMethod == null) {
|
||||||
Class<?> cls = manager.getClass();
|
Class<?> cls = manager.getClass();
|
||||||
try {
|
try {
|
||||||
return (Integer) cls.getMethod("getRotation").invoke(manager);
|
|
||||||
} 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
|
||||||
return (Integer) cls.getMethod("getDefaultDisplayRotation").invoke(manager);
|
getRotationMethod = cls.getMethod("getDefaultDisplayRotation");
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
// old version
|
||||||
|
getRotationMethod = cls.getMethod("getRotation");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
}
|
||||||
throw new AssertionError(e);
|
return getRotationMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Method getFreezeRotationMethod() throws NoSuchMethodException {
|
||||||
|
if (freezeRotationMethod == null) {
|
||||||
|
freezeRotationMethod = manager.getClass().getMethod("freezeRotation", int.class);
|
||||||
|
}
|
||||||
|
return freezeRotationMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Method getIsRotationFrozenMethod() throws NoSuchMethodException {
|
||||||
|
if (isRotationFrozenMethod == null) {
|
||||||
|
isRotationFrozenMethod = manager.getClass().getMethod("isRotationFrozen");
|
||||||
|
}
|
||||||
|
return isRotationFrozenMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Method getThawRotationMethod() throws NoSuchMethodException {
|
||||||
|
if (thawRotationMethod == null) {
|
||||||
|
thawRotationMethod = manager.getClass().getMethod("thawRotation");
|
||||||
|
}
|
||||||
|
return thawRotationMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRotation() {
|
||||||
|
try {
|
||||||
|
Method method = getGetRotationMethod();
|
||||||
|
return (int) method.invoke(manager);
|
||||||
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
|
Ln.e("Could not invoke method", e);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void freezeRotation(int rotation) {
|
||||||
|
try {
|
||||||
|
Method method = getFreezeRotationMethod();
|
||||||
|
method.invoke(manager, rotation);
|
||||||
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
|
Ln.e("Could not invoke method", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRotationFrozen() {
|
||||||
|
try {
|
||||||
|
Method method = getIsRotationFrozenMethod();
|
||||||
|
return (boolean) method.invoke(manager);
|
||||||
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
|
Ln.e("Could not invoke method", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void thawRotation() {
|
||||||
|
try {
|
||||||
|
Method method = getThawRotationMethod();
|
||||||
|
method.invoke(manager);
|
||||||
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
|
Ln.e("Could not invoke method", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,11 +97,12 @@ public final class WindowManager {
|
|||||||
try {
|
try {
|
||||||
Class<?> cls = manager.getClass();
|
Class<?> cls = manager.getClass();
|
||||||
try {
|
try {
|
||||||
cls.getMethod("watchRotation", IRotationWatcher.class).invoke(manager, rotationWatcher);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
// display parameter added since this commit:
|
// display parameter added since this commit:
|
||||||
// https://android.googlesource.com/platform/frameworks/base/+/35fa3c26adcb5f6577849fd0df5228b1f67cf2c6%5E%21/#F1
|
// https://android.googlesource.com/platform/frameworks/base/+/35fa3c26adcb5f6577849fd0df5228b1f67cf2c6%5E%21/#F1
|
||||||
cls.getMethod("watchRotation", IRotationWatcher.class, int.class).invoke(manager, rotationWatcher, 0);
|
cls.getMethod("watchRotation", IRotationWatcher.class, int.class).invoke(manager, rotationWatcher, 0);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
// old version
|
||||||
|
cls.getMethod("watchRotation", IRotationWatcher.class).invoke(manager, rotationWatcher);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
|
|||||||
@@ -240,6 +240,22 @@ public class ControlMessageReaderTest {
|
|||||||
Assert.assertEquals(Device.POWER_MODE_NORMAL, event.getAction());
|
Assert.assertEquals(Device.POWER_MODE_NORMAL, event.getAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseRotateDevice() throws IOException {
|
||||||
|
ControlMessageReader reader = new ControlMessageReader();
|
||||||
|
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream dos = new DataOutputStream(bos);
|
||||||
|
dos.writeByte(ControlMessage.TYPE_ROTATE_DEVICE);
|
||||||
|
|
||||||
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
|
reader.readFrom(new ByteArrayInputStream(packet));
|
||||||
|
ControlMessage event = reader.next();
|
||||||
|
|
||||||
|
Assert.assertEquals(ControlMessage.TYPE_ROTATE_DEVICE, event.getType());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultiEvents() throws IOException {
|
public void testMultiEvents() throws IOException {
|
||||||
ControlMessageReader reader = new ControlMessageReader();
|
ControlMessageReader reader = new ControlMessageReader();
|
||||||
|
|||||||
Reference in New Issue
Block a user