Compare commits
2 Commits
strbuf.6
...
server_thr
Author | SHA1 | Date | |
---|---|---|---|
781bb7397b | |||
b876cf9ddc |
12
README.md
12
README.md
@ -582,14 +582,6 @@ scrcpy --turn-screen-off --stay-awake
|
|||||||
scrcpy -Sw
|
scrcpy -Sw
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Power off on close
|
|
||||||
|
|
||||||
To turn the device screen off when closing scrcpy:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
scrcpy --power-off-on-close
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Show touches
|
#### Show touches
|
||||||
|
|
||||||
@ -838,7 +830,7 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
|
|||||||
| Turn device screen on | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>o</kbd>
|
| Turn device screen on | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>o</kbd>
|
||||||
| Rotate device screen | <kbd>MOD</kbd>+<kbd>r</kbd>
|
| Rotate device screen | <kbd>MOD</kbd>+<kbd>r</kbd>
|
||||||
| Expand notification panel | <kbd>MOD</kbd>+<kbd>n</kbd> \| _5th-click³_
|
| Expand notification panel | <kbd>MOD</kbd>+<kbd>n</kbd> \| _5th-click³_
|
||||||
| Expand settings panel | <kbd>MOD</kbd>+<kbd>n</kbd>+<kbd>n</kbd> \| _Double-5th-click³_
|
| Expand settings panel | <kbd>MOD</kbd>+<kbd>n</kbd>+<kbd>n</kbd> \| _Double-5th-click³_
|
||||||
| Collapse panels | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>n</kbd>
|
| Collapse panels | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>n</kbd>
|
||||||
| Copy to clipboard⁴ | <kbd>MOD</kbd>+<kbd>c</kbd>
|
| Copy to clipboard⁴ | <kbd>MOD</kbd>+<kbd>c</kbd>
|
||||||
| Cut to clipboard⁴ | <kbd>MOD</kbd>+<kbd>x</kbd>
|
| Cut to clipboard⁴ | <kbd>MOD</kbd>+<kbd>x</kbd>
|
||||||
@ -846,8 +838,6 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
|
|||||||
| Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
|
| Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
|
||||||
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
|
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
|
||||||
| Pinch-to-zoom | <kbd>Ctrl</kbd>+_click-and-move_
|
| Pinch-to-zoom | <kbd>Ctrl</kbd>+_click-and-move_
|
||||||
| Drag & drop APK file | Install APK from computer
|
|
||||||
| Drag & drop non-APK file | [Push file to device](#push-file-to-device)
|
|
||||||
|
|
||||||
_¹Double-click on black borders to remove them._
|
_¹Double-click on black borders to remove them._
|
||||||
_²Right-click turns the screen on if it was off, presses BACK otherwise._
|
_²Right-click turns the screen on if it was off, presses BACK otherwise._
|
||||||
|
@ -27,9 +27,7 @@ src = [
|
|||||||
'src/util/log.c',
|
'src/util/log.c',
|
||||||
'src/util/net.c',
|
'src/util/net.c',
|
||||||
'src/util/process.c',
|
'src/util/process.c',
|
||||||
'src/util/strbuf.c',
|
|
||||||
'src/util/str_util.c',
|
'src/util/str_util.c',
|
||||||
'src/util/term.c',
|
|
||||||
'src/util/thread.c',
|
'src/util/thread.c',
|
||||||
'src/util/tick.c',
|
'src/util/tick.c',
|
||||||
]
|
]
|
||||||
@ -188,9 +186,7 @@ if get_option('buildtype') == 'debug'
|
|||||||
'tests/test_cli.c',
|
'tests/test_cli.c',
|
||||||
'src/cli.c',
|
'src/cli.c',
|
||||||
'src/options.c',
|
'src/options.c',
|
||||||
'src/util/strbuf.c',
|
|
||||||
'src/util/str_util.c',
|
'src/util/str_util.c',
|
||||||
'src/util/term.c',
|
|
||||||
]],
|
]],
|
||||||
['test_clock', [
|
['test_clock', [
|
||||||
'tests/test_clock.c',
|
'tests/test_clock.c',
|
||||||
@ -199,7 +195,6 @@ if get_option('buildtype') == 'debug'
|
|||||||
['test_control_msg_serialize', [
|
['test_control_msg_serialize', [
|
||||||
'tests/test_control_msg_serialize.c',
|
'tests/test_control_msg_serialize.c',
|
||||||
'src/control_msg.c',
|
'src/control_msg.c',
|
||||||
'src/util/strbuf.c',
|
|
||||||
'src/util/str_util.c',
|
'src/util/str_util.c',
|
||||||
]],
|
]],
|
||||||
['test_device_msg_deserialize', [
|
['test_device_msg_deserialize', [
|
||||||
@ -209,13 +204,8 @@ if get_option('buildtype') == 'debug'
|
|||||||
['test_queue', [
|
['test_queue', [
|
||||||
'tests/test_queue.c',
|
'tests/test_queue.c',
|
||||||
]],
|
]],
|
||||||
['test_strbuf', [
|
|
||||||
'tests/test_strbuf.c',
|
|
||||||
'src/util/strbuf.c',
|
|
||||||
]],
|
|
||||||
['test_strutil', [
|
['test_strutil', [
|
||||||
'tests/test_strutil.c',
|
'tests/test_strutil.c',
|
||||||
'src/util/strbuf.c',
|
|
||||||
'src/util/str_util.c',
|
'src/util/str_util.c',
|
||||||
]],
|
]],
|
||||||
]
|
]
|
||||||
|
@ -136,10 +136,6 @@ Set the TCP port (range) used by the client to listen.
|
|||||||
|
|
||||||
Default is 27183:27199.
|
Default is 27183:27199.
|
||||||
|
|
||||||
.TP
|
|
||||||
.B \-\-power\-off\-on\-close
|
|
||||||
Turn the device screen off when closing scrcpy.
|
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-\-prefer\-text
|
.B \-\-prefer\-text
|
||||||
Inject alpha characters and space as text events instead of key events.
|
Inject alpha characters and space as text events instead of key events.
|
||||||
@ -368,10 +364,6 @@ Pinch-to-zoom from the center of the screen
|
|||||||
.B Drag & drop APK file
|
.B Drag & drop APK file
|
||||||
Install APK from computer
|
Install APK from computer
|
||||||
|
|
||||||
.TP
|
|
||||||
.B Drag & drop non-APK file
|
|
||||||
Push file to device (see \fB\-\-push\-target\fR)
|
|
||||||
|
|
||||||
|
|
||||||
.SH Environment variables
|
.SH Environment variables
|
||||||
|
|
||||||
|
1185
app/src/cli.c
1185
app/src/cli.c
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,7 @@ struct sc_clock_point {
|
|||||||
* array.
|
* array.
|
||||||
*
|
*
|
||||||
* To estimate the slope, it splits the last SC_CLOCK_RANGE points into two
|
* To estimate the slope, it splits the last SC_CLOCK_RANGE points into two
|
||||||
* sets of SC_CLOCK_RANGE/2 points, and computes their centroid ("average
|
* sets of SC_CLOCK_RANGE/2 points, and compute their centroid ("average
|
||||||
* point"). The slope of the estimated affine function is that of the line
|
* point"). The slope of the estimated affine function is that of the line
|
||||||
* passing through these two points.
|
* passing through these two points.
|
||||||
*
|
*
|
||||||
|
@ -43,11 +43,6 @@
|
|||||||
# define SCRCPY_SDL_HAS_WINDOW_ALWAYS_ON_TOP
|
# define SCRCPY_SDL_HAS_WINDOW_ALWAYS_ON_TOP
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 6)
|
|
||||||
// <https://github.com/libsdl-org/SDL/commit/d7a318de563125e5bb465b1000d6bc9576fbc6fc>
|
|
||||||
# define SCRCPY_SDL_HAS_HINT_TOUCH_MOUSE_EVENTS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 8)
|
#if SDL_VERSION_ATLEAST(2, 0, 8)
|
||||||
// <https://hg.libsdl.org/SDL/rev/dfde5d3f9781>
|
// <https://hg.libsdl.org/SDL/rev/dfde5d3f9781>
|
||||||
# define SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
|
# define SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
|
||||||
|
@ -56,7 +56,7 @@ static const char *const screen_power_mode_labels[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
write_position(uint8_t *buf, const struct sc_position *position) {
|
write_position(uint8_t *buf, const struct position *position) {
|
||||||
buffer_write32be(&buf[0], position->point.x);
|
buffer_write32be(&buf[0], position->point.x);
|
||||||
buffer_write32be(&buf[4], position->point.y);
|
buffer_write32be(&buf[4], position->point.y);
|
||||||
buffer_write16be(&buf[8], position->screen_size.width);
|
buffer_write16be(&buf[8], position->screen_size.width);
|
||||||
|
@ -57,11 +57,11 @@ struct control_msg {
|
|||||||
enum android_motionevent_action action;
|
enum android_motionevent_action action;
|
||||||
enum android_motionevent_buttons buttons;
|
enum android_motionevent_buttons buttons;
|
||||||
uint64_t pointer_id;
|
uint64_t pointer_id;
|
||||||
struct sc_position position;
|
struct position position;
|
||||||
float pressure;
|
float pressure;
|
||||||
} inject_touch_event;
|
} inject_touch_event;
|
||||||
struct {
|
struct {
|
||||||
struct sc_position position;
|
struct position position;
|
||||||
int32_t hscroll;
|
int32_t hscroll;
|
||||||
int32_t vscroll;
|
int32_t vscroll;
|
||||||
} inject_scroll_event;
|
} inject_scroll_event;
|
||||||
|
@ -3,22 +3,22 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct sc_size {
|
struct size {
|
||||||
uint16_t width;
|
uint16_t width;
|
||||||
uint16_t height;
|
uint16_t height;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_point {
|
struct point {
|
||||||
int32_t x;
|
int32_t x;
|
||||||
int32_t y;
|
int32_t y;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_position {
|
struct position {
|
||||||
// The video screen size may be different from the real device screen size,
|
// The video screen size may be different from the real device screen size,
|
||||||
// so store to which size the absolute position apply, to scale it
|
// so store to which size the absolute position apply, to scale it
|
||||||
// accordingly.
|
// accordingly.
|
||||||
struct sc_size screen_size;
|
struct size screen_size;
|
||||||
struct sc_point point;
|
struct point point;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -332,7 +332,7 @@ input_manager_process_text_input(struct input_manager *im,
|
|||||||
static bool
|
static bool
|
||||||
simulate_virtual_finger(struct input_manager *im,
|
simulate_virtual_finger(struct input_manager *im,
|
||||||
enum android_motionevent_action action,
|
enum android_motionevent_action action,
|
||||||
struct sc_point point) {
|
struct point point) {
|
||||||
bool up = action == AMOTION_EVENT_ACTION_UP;
|
bool up = action == AMOTION_EVENT_ACTION_UP;
|
||||||
|
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
@ -352,8 +352,8 @@ simulate_virtual_finger(struct input_manager *im,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sc_point
|
static struct point
|
||||||
inverse_point(struct sc_point point, struct sc_size size) {
|
inverse_point(struct point point, struct size size) {
|
||||||
point.x = size.width - point.x;
|
point.x = size.width - point.x;
|
||||||
point.y = size.height - point.y;
|
point.y = size.height - point.y;
|
||||||
return point;
|
return point;
|
||||||
@ -545,10 +545,10 @@ input_manager_process_mouse_motion(struct input_manager *im,
|
|||||||
im->mp->ops->process_mouse_motion(im->mp, event);
|
im->mp->ops->process_mouse_motion(im->mp, event);
|
||||||
|
|
||||||
if (im->vfinger_down) {
|
if (im->vfinger_down) {
|
||||||
struct sc_point mouse =
|
struct point mouse =
|
||||||
screen_convert_window_to_frame_coords(im->screen, event->x,
|
screen_convert_window_to_frame_coords(im->screen, event->x,
|
||||||
event->y);
|
event->y);
|
||||||
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size);
|
struct point vfinger = inverse_point(mouse, im->screen->frame_size);
|
||||||
simulate_virtual_finger(im, AMOTION_EVENT_ACTION_MOVE, vfinger);
|
simulate_virtual_finger(im, AMOTION_EVENT_ACTION_MOVE, vfinger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,10 +630,10 @@ input_manager_process_mouse_button(struct input_manager *im,
|
|||||||
#define CTRL_PRESSED (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL))
|
#define CTRL_PRESSED (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL))
|
||||||
if ((down && !im->vfinger_down && CTRL_PRESSED)
|
if ((down && !im->vfinger_down && CTRL_PRESSED)
|
||||||
|| (!down && im->vfinger_down)) {
|
|| (!down && im->vfinger_down)) {
|
||||||
struct sc_point mouse =
|
struct point mouse =
|
||||||
screen_convert_window_to_frame_coords(im->screen, event->x,
|
screen_convert_window_to_frame_coords(im->screen, event->x,
|
||||||
event->y);
|
event->y);
|
||||||
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size);
|
struct point vfinger = inverse_point(mouse, im->screen->frame_size);
|
||||||
enum android_motionevent_action action = down
|
enum android_motionevent_action action = down
|
||||||
? AMOTION_EVENT_ACTION_DOWN
|
? AMOTION_EVENT_ACTION_DOWN
|
||||||
: AMOTION_EVENT_ACTION_UP;
|
: AMOTION_EVENT_ACTION_UP;
|
||||||
|
@ -125,7 +125,7 @@ convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen,
|
|||||||
int mouse_y;
|
int mouse_y;
|
||||||
SDL_GetMouseState(&mouse_x, &mouse_y);
|
SDL_GetMouseState(&mouse_x, &mouse_y);
|
||||||
|
|
||||||
struct sc_position position = {
|
struct position position = {
|
||||||
.screen_size = screen->frame_size,
|
.screen_size = screen->frame_size,
|
||||||
.point = screen_convert_window_to_frame_coords(screen,
|
.point = screen_convert_window_to_frame_coords(screen,
|
||||||
mouse_x, mouse_y),
|
mouse_x, mouse_y),
|
||||||
|
@ -372,7 +372,7 @@ bool
|
|||||||
recorder_init(struct recorder *recorder,
|
recorder_init(struct recorder *recorder,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
enum sc_record_format format,
|
enum sc_record_format format,
|
||||||
struct sc_size declared_frame_size) {
|
struct size declared_frame_size) {
|
||||||
recorder->filename = strdup(filename);
|
recorder->filename = strdup(filename);
|
||||||
if (!recorder->filename) {
|
if (!recorder->filename) {
|
||||||
LOGE("Could not strdup filename");
|
LOGE("Could not strdup filename");
|
||||||
|
@ -25,7 +25,7 @@ struct recorder {
|
|||||||
char *filename;
|
char *filename;
|
||||||
enum sc_record_format format;
|
enum sc_record_format format;
|
||||||
AVFormatContext *ctx;
|
AVFormatContext *ctx;
|
||||||
struct sc_size declared_frame_size;
|
struct size declared_frame_size;
|
||||||
bool header_written;
|
bool header_written;
|
||||||
|
|
||||||
sc_thread thread;
|
sc_thread thread;
|
||||||
@ -44,7 +44,7 @@ struct recorder {
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
recorder_init(struct recorder *recorder, const char *filename,
|
recorder_init(struct recorder *recorder, const char *filename,
|
||||||
enum sc_record_format format, struct sc_size declared_frame_size);
|
enum sc_record_format format, struct size declared_frame_size);
|
||||||
|
|
||||||
void
|
void
|
||||||
recorder_destroy(struct recorder *recorder);
|
recorder_destroy(struct recorder *recorder);
|
||||||
|
121
app/src/scrcpy.c
121
app/src/scrcpy.c
@ -57,30 +57,41 @@ struct scrcpy {
|
|||||||
struct input_manager input_manager;
|
struct input_manager input_manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void
|
|
||||||
push_event(uint32_t type, const char *name) {
|
|
||||||
SDL_Event event;
|
|
||||||
event.type = type;
|
|
||||||
int ret = SDL_PushEvent(&event);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOGE("Could not post %s event: %s", name, SDL_GetError());
|
|
||||||
// What could we do?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#define PUSH_EVENT(TYPE) push_event(TYPE, # TYPE)
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
|
BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
|
||||||
if (ctrl_type == CTRL_C_EVENT) {
|
if (ctrl_type == CTRL_C_EVENT) {
|
||||||
PUSH_EVENT(SDL_QUIT);
|
SDL_Event event;
|
||||||
|
event.type = SDL_QUIT;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
static void
|
// init SDL and set appropriate hints
|
||||||
sdl_set_hints(const char *render_driver) {
|
static bool
|
||||||
|
sdl_init_and_configure(bool display, const char *render_driver,
|
||||||
|
bool disable_screensaver) {
|
||||||
|
uint32_t flags = display ? SDL_INIT_VIDEO : SDL_INIT_EVENTS;
|
||||||
|
if (SDL_Init(flags)) {
|
||||||
|
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
atexit(SDL_Quit);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Clean up properly on Ctrl+C on Windows
|
||||||
|
bool ok = SetConsoleCtrlHandler(windows_ctrl_handler, TRUE);
|
||||||
|
if (!ok) {
|
||||||
|
LOGW("Could not set Ctrl+C handler");
|
||||||
|
}
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
if (!display) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (render_driver && !SDL_SetHint(SDL_HINT_RENDER_DRIVER, render_driver)) {
|
if (render_driver && !SDL_SetHint(SDL_HINT_RENDER_DRIVER, render_driver)) {
|
||||||
LOGW("Could not set render driver");
|
LOGW("Could not set render driver");
|
||||||
@ -98,15 +109,6 @@ sdl_set_hints(const char *render_driver) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SCRCPY_SDL_HAS_HINT_TOUCH_MOUSE_EVENTS
|
|
||||||
// Disable synthetic mouse events from touch events
|
|
||||||
// Touch events with id SDL_TOUCH_MOUSEID are ignored anyway, but it is
|
|
||||||
// better not to generate them in the first place.
|
|
||||||
if (!SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0")) {
|
|
||||||
LOGW("Could not disable synthetic mouse events");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
|
#ifdef SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
|
||||||
// Disable compositor bypassing on X11
|
// Disable compositor bypassing on X11
|
||||||
if (!SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0")) {
|
if (!SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0")) {
|
||||||
@ -118,21 +120,6 @@ sdl_set_hints(const char *render_driver) {
|
|||||||
if (!SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0")) {
|
if (!SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0")) {
|
||||||
LOGW("Could not disable minimize on focus loss");
|
LOGW("Could not disable minimize on focus loss");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sdl_configure(bool display, bool disable_screensaver) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
// Clean up properly on Ctrl+C on Windows
|
|
||||||
bool ok = SetConsoleCtrlHandler(windows_ctrl_handler, TRUE);
|
|
||||||
if (!ok) {
|
|
||||||
LOGW("Could not set Ctrl+C handler");
|
|
||||||
}
|
|
||||||
#endif // _WIN32
|
|
||||||
|
|
||||||
if (!display) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disable_screensaver) {
|
if (disable_screensaver) {
|
||||||
LOGD("Screensaver disabled");
|
LOGD("Screensaver disabled");
|
||||||
@ -141,6 +128,8 @@ sdl_configure(bool display, bool disable_screensaver) {
|
|||||||
LOGD("Screensaver enabled");
|
LOGD("Screensaver enabled");
|
||||||
SDL_EnableScreenSaver();
|
SDL_EnableScreenSaver();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -259,7 +248,9 @@ stream_on_eos(struct stream *stream, void *userdata) {
|
|||||||
(void) stream;
|
(void) stream;
|
||||||
(void) userdata;
|
(void) userdata;
|
||||||
|
|
||||||
PUSH_EVENT(EVENT_STREAM_STOPPED);
|
SDL_Event stop_event;
|
||||||
|
stop_event.type = EVENT_STREAM_STOPPED;
|
||||||
|
SDL_PushEvent(&stop_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -267,18 +258,6 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
static struct scrcpy scrcpy;
|
static struct scrcpy scrcpy;
|
||||||
struct scrcpy *s = &scrcpy;
|
struct scrcpy *s = &scrcpy;
|
||||||
|
|
||||||
// Minimal SDL initialization
|
|
||||||
if (SDL_Init(SDL_INIT_EVENTS)) {
|
|
||||||
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
atexit(SDL_Quit);
|
|
||||||
|
|
||||||
if (!server_init(&s->server)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
bool server_started = false;
|
bool server_started = false;
|
||||||
@ -295,6 +274,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
bool controller_started = false;
|
bool controller_started = false;
|
||||||
bool screen_initialized = false;
|
bool screen_initialized = false;
|
||||||
|
|
||||||
|
bool record = !!options->record_filename;
|
||||||
struct server_params params = {
|
struct server_params params = {
|
||||||
.serial = options->serial,
|
.serial = options->serial,
|
||||||
.log_level = options->log_level,
|
.log_level = options->log_level,
|
||||||
@ -313,32 +293,31 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
.force_adb_forward = options->force_adb_forward,
|
.force_adb_forward = options->force_adb_forward,
|
||||||
.power_off_on_close = options->power_off_on_close,
|
.power_off_on_close = options->power_off_on_close,
|
||||||
};
|
};
|
||||||
if (!server_start(&s->server, ¶ms)) {
|
|
||||||
|
if (!server_init(&s->server, ¶ms)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server_start(&s->server)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
server_started = true;
|
server_started = true;
|
||||||
|
|
||||||
if (options->display) {
|
if (!sdl_init_and_configure(options->display, options->render_driver,
|
||||||
sdl_set_hints(options->render_driver);
|
options->disable_screensaver)) {
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize SDL video in addition if display is enabled
|
|
||||||
if (options->display && SDL_Init(SDL_INIT_VIDEO)) {
|
|
||||||
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
sdl_configure(options->display, options->disable_screensaver);
|
char device_name[DEVICE_NAME_FIELD_LENGTH];
|
||||||
|
struct size frame_size;
|
||||||
|
|
||||||
struct server_info info;
|
if (!server_connect_to(&s->server, device_name, &frame_size)) {
|
||||||
|
|
||||||
if (!server_connect_to(&s->server, &info)) {
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options->display && options->control) {
|
if (options->display && options->control) {
|
||||||
if (!file_handler_init(&s->file_handler, s->server.serial,
|
if (!file_handler_init(&s->file_handler, options->serial,
|
||||||
options->push_target)) {
|
options->push_target)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
@ -356,11 +335,11 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct recorder *rec = NULL;
|
struct recorder *rec = NULL;
|
||||||
if (options->record_filename) {
|
if (record) {
|
||||||
if (!recorder_init(&s->recorder,
|
if (!recorder_init(&s->recorder,
|
||||||
options->record_filename,
|
options->record_filename,
|
||||||
options->record_format,
|
options->record_format,
|
||||||
info.frame_size)) {
|
frame_size)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
rec = &s->recorder;
|
rec = &s->recorder;
|
||||||
@ -406,11 +385,11 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
|
|
||||||
if (options->display) {
|
if (options->display) {
|
||||||
const char *window_title =
|
const char *window_title =
|
||||||
options->window_title ? options->window_title : info.device_name;
|
options->window_title ? options->window_title : device_name;
|
||||||
|
|
||||||
struct screen_params screen_params = {
|
struct screen_params screen_params = {
|
||||||
.window_title = window_title,
|
.window_title = window_title,
|
||||||
.frame_size = info.frame_size,
|
.frame_size = frame_size,
|
||||||
.always_on_top = options->always_on_top,
|
.always_on_top = options->always_on_top,
|
||||||
.window_x = options->window_x,
|
.window_x = options->window_x,
|
||||||
.window_y = options->window_y,
|
.window_y = options->window_y,
|
||||||
@ -433,8 +412,8 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
|
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
if (options->v4l2_device) {
|
if (options->v4l2_device) {
|
||||||
if (!sc_v4l2_sink_init(&s->v4l2_sink, options->v4l2_device,
|
if (!sc_v4l2_sink_init(&s->v4l2_sink, options->v4l2_device, frame_size,
|
||||||
info.frame_size, options->v4l2_buffer)) {
|
options->v4l2_buffer)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
103
app/src/screen.c
103
app/src/screen.c
@ -14,9 +14,9 @@
|
|||||||
|
|
||||||
#define DOWNCAST(SINK) container_of(SINK, struct screen, frame_sink)
|
#define DOWNCAST(SINK) container_of(SINK, struct screen, frame_sink)
|
||||||
|
|
||||||
static inline struct sc_size
|
static inline struct size
|
||||||
get_rotated_size(struct sc_size size, int rotation) {
|
get_rotated_size(struct size size, int rotation) {
|
||||||
struct sc_size rotated_size;
|
struct size rotated_size;
|
||||||
if (rotation & 1) {
|
if (rotation & 1) {
|
||||||
rotated_size.width = size.height;
|
rotated_size.width = size.height;
|
||||||
rotated_size.height = size.width;
|
rotated_size.height = size.width;
|
||||||
@ -27,26 +27,26 @@ get_rotated_size(struct sc_size size, int rotation) {
|
|||||||
return rotated_size;
|
return rotated_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the window size in a struct sc_size
|
// get the window size in a struct size
|
||||||
static struct sc_size
|
static struct size
|
||||||
get_window_size(const struct screen *screen) {
|
get_window_size(const struct screen *screen) {
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
SDL_GetWindowSize(screen->window, &width, &height);
|
SDL_GetWindowSize(screen->window, &width, &height);
|
||||||
|
|
||||||
struct sc_size size;
|
struct size size;
|
||||||
size.width = width;
|
size.width = width;
|
||||||
size.height = height;
|
size.height = height;
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sc_point
|
static struct point
|
||||||
get_window_position(const struct screen *screen) {
|
get_window_position(const struct screen *screen) {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
SDL_GetWindowPosition(screen->window, &x, &y);
|
SDL_GetWindowPosition(screen->window, &x, &y);
|
||||||
|
|
||||||
struct sc_point point;
|
struct point point;
|
||||||
point.x = x;
|
point.x = x;
|
||||||
point.y = y;
|
point.y = y;
|
||||||
return point;
|
return point;
|
||||||
@ -54,7 +54,7 @@ get_window_position(const struct screen *screen) {
|
|||||||
|
|
||||||
// set the window size to be applied when fullscreen is disabled
|
// set the window size to be applied when fullscreen is disabled
|
||||||
static void
|
static void
|
||||||
set_window_size(struct screen *screen, struct sc_size new_size) {
|
set_window_size(struct screen *screen, struct size new_size) {
|
||||||
assert(!screen->fullscreen);
|
assert(!screen->fullscreen);
|
||||||
assert(!screen->maximized);
|
assert(!screen->maximized);
|
||||||
SDL_SetWindowSize(screen->window, new_size.width, new_size.height);
|
SDL_SetWindowSize(screen->window, new_size.width, new_size.height);
|
||||||
@ -62,7 +62,7 @@ set_window_size(struct screen *screen, struct sc_size new_size) {
|
|||||||
|
|
||||||
// get the preferred display bounds (i.e. the screen bounds with some margins)
|
// get the preferred display bounds (i.e. the screen bounds with some margins)
|
||||||
static bool
|
static bool
|
||||||
get_preferred_display_bounds(struct sc_size *bounds) {
|
get_preferred_display_bounds(struct size *bounds) {
|
||||||
SDL_Rect rect;
|
SDL_Rect rect;
|
||||||
#ifdef SCRCPY_SDL_HAS_GET_DISPLAY_USABLE_BOUNDS
|
#ifdef SCRCPY_SDL_HAS_GET_DISPLAY_USABLE_BOUNDS
|
||||||
# define GET_DISPLAY_BOUNDS(i, r) SDL_GetDisplayUsableBounds((i), (r))
|
# define GET_DISPLAY_BOUNDS(i, r) SDL_GetDisplayUsableBounds((i), (r))
|
||||||
@ -80,7 +80,7 @@ get_preferred_display_bounds(struct sc_size *bounds) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_optimal_size(struct sc_size current_size, struct sc_size content_size) {
|
is_optimal_size(struct size current_size, struct size content_size) {
|
||||||
// The size is optimal if we can recompute one dimension of the current
|
// The size is optimal if we can recompute one dimension of the current
|
||||||
// size from the other
|
// size from the other
|
||||||
return current_size.height == current_size.width * content_size.height
|
return current_size.height == current_size.width * content_size.height
|
||||||
@ -94,16 +94,16 @@ is_optimal_size(struct sc_size current_size, struct sc_size content_size) {
|
|||||||
// crops the black borders)
|
// crops the black borders)
|
||||||
// - it keeps the aspect ratio
|
// - it keeps the aspect ratio
|
||||||
// - it scales down to make it fit in the display_size
|
// - it scales down to make it fit in the display_size
|
||||||
static struct sc_size
|
static struct size
|
||||||
get_optimal_size(struct sc_size current_size, struct sc_size content_size) {
|
get_optimal_size(struct size current_size, struct size content_size) {
|
||||||
if (content_size.width == 0 || content_size.height == 0) {
|
if (content_size.width == 0 || content_size.height == 0) {
|
||||||
// avoid division by 0
|
// avoid division by 0
|
||||||
return current_size;
|
return current_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_size window_size;
|
struct size window_size;
|
||||||
|
|
||||||
struct sc_size display_size;
|
struct size display_size;
|
||||||
if (!get_preferred_display_bounds(&display_size)) {
|
if (!get_preferred_display_bounds(&display_size)) {
|
||||||
// could not get display bounds, do not constraint the size
|
// could not get display bounds, do not constraint the size
|
||||||
window_size.width = current_size.width;
|
window_size.width = current_size.width;
|
||||||
@ -135,10 +135,10 @@ get_optimal_size(struct sc_size current_size, struct sc_size content_size) {
|
|||||||
|
|
||||||
// initially, there is no current size, so use the frame size as current size
|
// initially, there is no current size, so use the frame size as current size
|
||||||
// req_width and req_height, if not 0, are the sizes requested by the user
|
// req_width and req_height, if not 0, are the sizes requested by the user
|
||||||
static inline struct sc_size
|
static inline struct size
|
||||||
get_initial_optimal_size(struct sc_size content_size, uint16_t req_width,
|
get_initial_optimal_size(struct size content_size, uint16_t req_width,
|
||||||
uint16_t req_height) {
|
uint16_t req_height) {
|
||||||
struct sc_size window_size;
|
struct size window_size;
|
||||||
if (!req_width && !req_height) {
|
if (!req_width && !req_height) {
|
||||||
window_size = get_optimal_size(content_size, content_size);
|
window_size = get_optimal_size(content_size, content_size);
|
||||||
} else {
|
} else {
|
||||||
@ -166,9 +166,9 @@ screen_update_content_rect(struct screen *screen) {
|
|||||||
int dh;
|
int dh;
|
||||||
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
||||||
|
|
||||||
struct sc_size content_size = screen->content_size;
|
struct size content_size = screen->content_size;
|
||||||
// The drawable size is the window size * the HiDPI scale
|
// The drawable size is the window size * the HiDPI scale
|
||||||
struct sc_size drawable_size = {dw, dh};
|
struct size drawable_size = {dw, dh};
|
||||||
|
|
||||||
SDL_Rect *rect = &screen->rect;
|
SDL_Rect *rect = &screen->rect;
|
||||||
|
|
||||||
@ -200,7 +200,7 @@ screen_update_content_rect(struct screen *screen) {
|
|||||||
static inline SDL_Texture *
|
static inline SDL_Texture *
|
||||||
create_texture(struct screen *screen) {
|
create_texture(struct screen *screen) {
|
||||||
SDL_Renderer *renderer = screen->renderer;
|
SDL_Renderer *renderer = screen->renderer;
|
||||||
struct sc_size size = screen->frame_size;
|
struct size size = screen->frame_size;
|
||||||
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
|
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
|
||||||
SDL_TEXTUREACCESS_STREAMING,
|
SDL_TEXTUREACCESS_STREAMING,
|
||||||
size.width, size.height);
|
size.width, size.height);
|
||||||
@ -282,33 +282,17 @@ sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped,
|
|||||||
(void) vb;
|
(void) vb;
|
||||||
struct screen *screen = userdata;
|
struct screen *screen = userdata;
|
||||||
|
|
||||||
// event_failed implies previous_skipped (the previous frame may not have
|
|
||||||
// been consumed if the event was not sent)
|
|
||||||
assert(!screen->event_failed || previous_skipped);
|
|
||||||
|
|
||||||
bool need_new_event;
|
|
||||||
if (previous_skipped) {
|
if (previous_skipped) {
|
||||||
fps_counter_add_skipped_frame(&screen->fps_counter);
|
fps_counter_add_skipped_frame(&screen->fps_counter);
|
||||||
// The EVENT_NEW_FRAME triggered for the previous frame will consume
|
// The EVENT_NEW_FRAME triggered for the previous frame will consume
|
||||||
// this new frame instead, unless the previous event failed
|
// this new frame instead
|
||||||
need_new_event = screen->event_failed;
|
|
||||||
} else {
|
} else {
|
||||||
need_new_event = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (need_new_event) {
|
|
||||||
static SDL_Event new_frame_event = {
|
static SDL_Event new_frame_event = {
|
||||||
.type = EVENT_NEW_FRAME,
|
.type = EVENT_NEW_FRAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Post the event on the UI thread
|
// Post the event on the UI thread
|
||||||
int ret = SDL_PushEvent(&new_frame_event);
|
SDL_PushEvent(&new_frame_event);
|
||||||
if (ret < 0) {
|
|
||||||
LOGW("Could not post new frame event: %s", SDL_GetError());
|
|
||||||
screen->event_failed = true;
|
|
||||||
} else {
|
|
||||||
screen->event_failed = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +302,6 @@ screen_init(struct screen *screen, const struct screen_params *params) {
|
|||||||
screen->has_frame = false;
|
screen->has_frame = false;
|
||||||
screen->fullscreen = false;
|
screen->fullscreen = false;
|
||||||
screen->maximized = false;
|
screen->maximized = false;
|
||||||
screen->event_failed = false;
|
|
||||||
|
|
||||||
static const struct sc_video_buffer_callbacks cbs = {
|
static const struct sc_video_buffer_callbacks cbs = {
|
||||||
.on_new_frame = sc_video_buffer_on_new_frame,
|
.on_new_frame = sc_video_buffer_on_new_frame,
|
||||||
@ -347,13 +330,13 @@ screen_init(struct screen *screen, const struct screen_params *params) {
|
|||||||
if (screen->rotation) {
|
if (screen->rotation) {
|
||||||
LOGI("Initial display rotation set to %u", screen->rotation);
|
LOGI("Initial display rotation set to %u", screen->rotation);
|
||||||
}
|
}
|
||||||
struct sc_size content_size =
|
struct size content_size =
|
||||||
get_rotated_size(screen->frame_size, screen->rotation);
|
get_rotated_size(screen->frame_size, screen->rotation);
|
||||||
screen->content_size = content_size;
|
screen->content_size = content_size;
|
||||||
|
|
||||||
struct sc_size window_size =
|
struct size window_size = get_initial_optimal_size(content_size,
|
||||||
get_initial_optimal_size(content_size,params->window_width,
|
params->window_width,
|
||||||
params->window_height);
|
params->window_height);
|
||||||
uint32_t window_flags = SDL_WINDOW_HIDDEN
|
uint32_t window_flags = SDL_WINDOW_HIDDEN
|
||||||
| SDL_WINDOW_RESIZABLE
|
| SDL_WINDOW_RESIZABLE
|
||||||
| SDL_WINDOW_ALLOW_HIGHDPI;
|
| SDL_WINDOW_ALLOW_HIGHDPI;
|
||||||
@ -525,10 +508,10 @@ screen_destroy(struct screen *screen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
resize_for_content(struct screen *screen, struct sc_size old_content_size,
|
resize_for_content(struct screen *screen, struct size old_content_size,
|
||||||
struct sc_size new_content_size) {
|
struct size new_content_size) {
|
||||||
struct sc_size window_size = get_window_size(screen);
|
struct size window_size = get_window_size(screen);
|
||||||
struct sc_size target_size = {
|
struct size target_size = {
|
||||||
.width = (uint32_t) window_size.width * new_content_size.width
|
.width = (uint32_t) window_size.width * new_content_size.width
|
||||||
/ old_content_size.width,
|
/ old_content_size.width,
|
||||||
.height = (uint32_t) window_size.height * new_content_size.height
|
.height = (uint32_t) window_size.height * new_content_size.height
|
||||||
@ -539,7 +522,7 @@ resize_for_content(struct screen *screen, struct sc_size old_content_size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_content_size(struct screen *screen, struct sc_size new_content_size) {
|
set_content_size(struct screen *screen, struct size new_content_size) {
|
||||||
if (!screen->fullscreen && !screen->maximized) {
|
if (!screen->fullscreen && !screen->maximized) {
|
||||||
resize_for_content(screen, screen->content_size, new_content_size);
|
resize_for_content(screen, screen->content_size, new_content_size);
|
||||||
} else if (!screen->resize_pending) {
|
} else if (!screen->resize_pending) {
|
||||||
@ -570,7 +553,7 @@ screen_set_rotation(struct screen *screen, unsigned rotation) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_size new_content_size =
|
struct size new_content_size =
|
||||||
get_rotated_size(screen->frame_size, rotation);
|
get_rotated_size(screen->frame_size, rotation);
|
||||||
|
|
||||||
set_content_size(screen, new_content_size);
|
set_content_size(screen, new_content_size);
|
||||||
@ -583,7 +566,7 @@ screen_set_rotation(struct screen *screen, unsigned rotation) {
|
|||||||
|
|
||||||
// recreate the texture and resize the window if the frame size has changed
|
// recreate the texture and resize the window if the frame size has changed
|
||||||
static bool
|
static bool
|
||||||
prepare_for_frame(struct screen *screen, struct sc_size new_frame_size) {
|
prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
||||||
if (screen->frame_size.width != new_frame_size.width
|
if (screen->frame_size.width != new_frame_size.width
|
||||||
|| screen->frame_size.height != new_frame_size.height) {
|
|| screen->frame_size.height != new_frame_size.height) {
|
||||||
// frame dimension changed, destroy texture
|
// frame dimension changed, destroy texture
|
||||||
@ -591,7 +574,7 @@ prepare_for_frame(struct screen *screen, struct sc_size new_frame_size) {
|
|||||||
|
|
||||||
screen->frame_size = new_frame_size;
|
screen->frame_size = new_frame_size;
|
||||||
|
|
||||||
struct sc_size new_content_size =
|
struct size new_content_size =
|
||||||
get_rotated_size(new_frame_size, screen->rotation);
|
get_rotated_size(new_frame_size, screen->rotation);
|
||||||
set_content_size(screen, new_content_size);
|
set_content_size(screen, new_content_size);
|
||||||
|
|
||||||
@ -632,7 +615,7 @@ screen_update_frame(struct screen *screen) {
|
|||||||
|
|
||||||
fps_counter_add_rendered_frame(&screen->fps_counter);
|
fps_counter_add_rendered_frame(&screen->fps_counter);
|
||||||
|
|
||||||
struct sc_size new_frame_size = {frame->width, frame->height};
|
struct size new_frame_size = {frame->width, frame->height};
|
||||||
if (!prepare_for_frame(screen, new_frame_size)) {
|
if (!prepare_for_frame(screen, new_frame_size)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -699,10 +682,10 @@ screen_resize_to_fit(struct screen *screen) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_point point = get_window_position(screen);
|
struct point point = get_window_position(screen);
|
||||||
struct sc_size window_size = get_window_size(screen);
|
struct size window_size = get_window_size(screen);
|
||||||
|
|
||||||
struct sc_size optimal_size =
|
struct size optimal_size =
|
||||||
get_optimal_size(window_size, screen->content_size);
|
get_optimal_size(window_size, screen->content_size);
|
||||||
|
|
||||||
// Center the window related to the device screen
|
// Center the window related to the device screen
|
||||||
@ -728,7 +711,7 @@ screen_resize_to_pixel_perfect(struct screen *screen) {
|
|||||||
screen->maximized = false;
|
screen->maximized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_size content_size = screen->content_size;
|
struct size content_size = screen->content_size;
|
||||||
SDL_SetWindowSize(screen->window, content_size.width, content_size.height);
|
SDL_SetWindowSize(screen->window, content_size.width, content_size.height);
|
||||||
LOGD("Resized to pixel-perfect: %ux%u", content_size.width,
|
LOGD("Resized to pixel-perfect: %ux%u", content_size.width,
|
||||||
content_size.height);
|
content_size.height);
|
||||||
@ -783,7 +766,7 @@ screen_handle_event(struct screen *screen, SDL_Event *event) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_point
|
struct point
|
||||||
screen_convert_drawable_to_frame_coords(struct screen *screen,
|
screen_convert_drawable_to_frame_coords(struct screen *screen,
|
||||||
int32_t x, int32_t y) {
|
int32_t x, int32_t y) {
|
||||||
unsigned rotation = screen->rotation;
|
unsigned rotation = screen->rotation;
|
||||||
@ -797,7 +780,7 @@ screen_convert_drawable_to_frame_coords(struct screen *screen,
|
|||||||
y = (int64_t) (y - screen->rect.y) * h / screen->rect.h;
|
y = (int64_t) (y - screen->rect.y) * h / screen->rect.h;
|
||||||
|
|
||||||
// rotate
|
// rotate
|
||||||
struct sc_point result;
|
struct point result;
|
||||||
switch (rotation) {
|
switch (rotation) {
|
||||||
case 0:
|
case 0:
|
||||||
result.x = x;
|
result.x = x;
|
||||||
@ -820,7 +803,7 @@ screen_convert_drawable_to_frame_coords(struct screen *screen,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_point
|
struct point
|
||||||
screen_convert_window_to_frame_coords(struct screen *screen,
|
screen_convert_window_to_frame_coords(struct screen *screen,
|
||||||
int32_t x, int32_t y) {
|
int32_t x, int32_t y) {
|
||||||
screen_hidpi_scale_coords(screen, &x, &y);
|
screen_hidpi_scale_coords(screen, &x, &y);
|
||||||
|
@ -27,13 +27,13 @@ struct screen {
|
|||||||
SDL_Renderer *renderer;
|
SDL_Renderer *renderer;
|
||||||
SDL_Texture *texture;
|
SDL_Texture *texture;
|
||||||
struct sc_opengl gl;
|
struct sc_opengl gl;
|
||||||
struct sc_size frame_size;
|
struct size frame_size;
|
||||||
struct sc_size content_size; // rotated frame_size
|
struct size content_size; // rotated frame_size
|
||||||
|
|
||||||
bool resize_pending; // resize requested while fullscreen or maximized
|
bool resize_pending; // resize requested while fullscreen or maximized
|
||||||
// The content size the last time the window was not maximized or
|
// The content size the last time the window was not maximized or
|
||||||
// fullscreen (meaningful only when resize_pending is true)
|
// fullscreen (meaningful only when resize_pending is true)
|
||||||
struct sc_size windowed_content_size;
|
struct size windowed_content_size;
|
||||||
|
|
||||||
// client rotation: 0, 1, 2 or 3 (x90 degrees counterclockwise)
|
// client rotation: 0, 1, 2 or 3 (x90 degrees counterclockwise)
|
||||||
unsigned rotation;
|
unsigned rotation;
|
||||||
@ -44,14 +44,12 @@ struct screen {
|
|||||||
bool maximized;
|
bool maximized;
|
||||||
bool mipmaps;
|
bool mipmaps;
|
||||||
|
|
||||||
bool event_failed; // in case SDL_PushEvent() returned an error
|
|
||||||
|
|
||||||
AVFrame *frame;
|
AVFrame *frame;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct screen_params {
|
struct screen_params {
|
||||||
const char *window_title;
|
const char *window_title;
|
||||||
struct sc_size frame_size;
|
struct size frame_size;
|
||||||
bool always_on_top;
|
bool always_on_top;
|
||||||
|
|
||||||
int16_t window_x;
|
int16_t window_x;
|
||||||
@ -122,13 +120,13 @@ screen_handle_event(struct screen *screen, SDL_Event *event);
|
|||||||
|
|
||||||
// convert point from window coordinates to frame coordinates
|
// convert point from window coordinates to frame coordinates
|
||||||
// x and y are expressed in pixels
|
// x and y are expressed in pixels
|
||||||
struct sc_point
|
struct point
|
||||||
screen_convert_window_to_frame_coords(struct screen *screen,
|
screen_convert_window_to_frame_coords(struct screen *screen,
|
||||||
int32_t x, int32_t y);
|
int32_t x, int32_t y);
|
||||||
|
|
||||||
// convert point from drawable coordinates to frame coordinates
|
// convert point from drawable coordinates to frame coordinates
|
||||||
// x and y are expressed in pixels
|
// x and y are expressed in pixels
|
||||||
struct sc_point
|
struct point
|
||||||
screen_convert_drawable_to_frame_coords(struct screen *screen,
|
screen_convert_drawable_to_frame_coords(struct screen *screen,
|
||||||
int32_t x, int32_t y);
|
int32_t x, int32_t y);
|
||||||
|
|
||||||
|
101
app/src/server.c
101
app/src/server.c
@ -61,6 +61,44 @@ get_server_path(void) {
|
|||||||
return server_path;
|
return server_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
server_params_destroy(struct server_params *params) {
|
||||||
|
// The server stores a copy of the params provided by the user
|
||||||
|
free((char *) params->serial);
|
||||||
|
free((char *) params->crop);
|
||||||
|
free((char *) params->codec_options);
|
||||||
|
free((char *) params->encoder_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
server_params_copy(struct server_params *dst, const struct server_params *src) {
|
||||||
|
*dst = *src;
|
||||||
|
|
||||||
|
// The params reference user-allocated memory, so we must copy them to
|
||||||
|
// handle them from another thread
|
||||||
|
|
||||||
|
#define COPY(FIELD) \
|
||||||
|
dst->FIELD = NULL; \
|
||||||
|
if (src->FIELD) { \
|
||||||
|
dst->FIELD = strdup(src->FIELD); \
|
||||||
|
if (!dst->FIELD) { \
|
||||||
|
goto error; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
COPY(serial);
|
||||||
|
COPY(crop);
|
||||||
|
COPY(codec_options);
|
||||||
|
COPY(encoder_name);
|
||||||
|
#undef COPY
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
server_params_destroy(dst);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
push_server(const char *serial) {
|
push_server(const char *serial) {
|
||||||
char *server_path = get_server_path();
|
char *server_path = get_server_path();
|
||||||
@ -103,10 +141,11 @@ disable_tunnel_forward(const char *serial, uint16_t local_port) {
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
disable_tunnel(struct server *server) {
|
disable_tunnel(struct server *server) {
|
||||||
|
const char *serial = server->params.serial;
|
||||||
if (server->tunnel_forward) {
|
if (server->tunnel_forward) {
|
||||||
return disable_tunnel_forward(server->serial, server->local_port);
|
return disable_tunnel_forward(serial, server->local_port);
|
||||||
}
|
}
|
||||||
return disable_tunnel_reverse(server->serial);
|
return disable_tunnel_reverse(serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
static sc_socket
|
static sc_socket
|
||||||
@ -118,9 +157,10 @@ listen_on_port(uint16_t port) {
|
|||||||
static bool
|
static bool
|
||||||
enable_tunnel_reverse_any_port(struct server *server,
|
enable_tunnel_reverse_any_port(struct server *server,
|
||||||
struct sc_port_range port_range) {
|
struct sc_port_range port_range) {
|
||||||
|
const char *serial = server->params.serial;
|
||||||
uint16_t port = port_range.first;
|
uint16_t port = port_range.first;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (!enable_tunnel_reverse(server->serial, port)) {
|
if (!enable_tunnel_reverse(serial, port)) {
|
||||||
// the command itself failed, it will fail on any port
|
// the command itself failed, it will fail on any port
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -139,7 +179,7 @@ enable_tunnel_reverse_any_port(struct server *server,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// failure, disable tunnel and try another port
|
// failure, disable tunnel and try another port
|
||||||
if (!disable_tunnel_reverse(server->serial)) {
|
if (!disable_tunnel_reverse(serial)) {
|
||||||
LOGW("Could not remove reverse tunnel on port %" PRIu16, port);
|
LOGW("Could not remove reverse tunnel on port %" PRIu16, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,9 +205,11 @@ static bool
|
|||||||
enable_tunnel_forward_any_port(struct server *server,
|
enable_tunnel_forward_any_port(struct server *server,
|
||||||
struct sc_port_range port_range) {
|
struct sc_port_range port_range) {
|
||||||
server->tunnel_forward = true;
|
server->tunnel_forward = true;
|
||||||
|
|
||||||
|
const char *serial = server->params.serial;
|
||||||
uint16_t port = port_range.first;
|
uint16_t port = port_range.first;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (enable_tunnel_forward(server->serial, port)) {
|
if (enable_tunnel_forward(serial, port)) {
|
||||||
// success
|
// success
|
||||||
server->local_port = port;
|
server->local_port = port;
|
||||||
return true;
|
return true;
|
||||||
@ -229,6 +271,8 @@ log_level_to_server_string(enum sc_log_level level) {
|
|||||||
|
|
||||||
static process_t
|
static process_t
|
||||||
execute_server(struct server *server, const struct server_params *params) {
|
execute_server(struct server *server, const struct server_params *params) {
|
||||||
|
const char *serial = server->params.serial;
|
||||||
|
|
||||||
char max_size_string[6];
|
char max_size_string[6];
|
||||||
char bit_rate_string[11];
|
char bit_rate_string[11];
|
||||||
char max_fps_string[6];
|
char max_fps_string[6];
|
||||||
@ -286,7 +330,7 @@ execute_server(struct server *server, const struct server_params *params) {
|
|||||||
// Port: 5005
|
// Port: 5005
|
||||||
// Then click on "Debug"
|
// Then click on "Debug"
|
||||||
#endif
|
#endif
|
||||||
return adb_execute(server->serial, cmd, ARRAY_LEN(cmd));
|
return adb_execute(serial, cmd, ARRAY_LEN(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
static sc_socket
|
static sc_socket
|
||||||
@ -324,18 +368,25 @@ connect_to_server(uint16_t port, uint32_t attempts, uint32_t delay) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
server_init(struct server *server) {
|
server_init(struct server *server, const struct server_params *params) {
|
||||||
server->serial = NULL;
|
bool ok = server_params_copy(&server->params, params);
|
||||||
|
if (!ok) {
|
||||||
|
LOGE("Could not copy server params");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
server->process = PROCESS_NONE;
|
server->process = PROCESS_NONE;
|
||||||
|
|
||||||
bool ok = sc_mutex_init(&server->mutex);
|
ok = sc_mutex_init(&server->mutex);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
server_params_destroy(&server->params);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = sc_cond_init(&server->process_terminated_cond);
|
ok = sc_cond_init(&server->process_terminated_cond);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
sc_mutex_destroy(&server->mutex);
|
sc_mutex_destroy(&server->mutex);
|
||||||
|
server_params_destroy(&server->params);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,13 +426,8 @@ run_wait_server(void *data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
server_start(struct server *server, const struct server_params *params) {
|
server_start(struct server *server) {
|
||||||
if (params->serial) {
|
const struct server_params *params = &server->params;
|
||||||
server->serial = strdup(params->serial);
|
|
||||||
if (!server->serial) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!push_server(params->serial)) {
|
if (!push_server(params->serial)) {
|
||||||
/* server->serial will be freed on server_destroy() */
|
/* server->serial will be freed on server_destroy() */
|
||||||
@ -426,7 +472,8 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
device_read_info(sc_socket device_socket, struct server_info *info) {
|
device_read_info(sc_socket device_socket, char *device_name,
|
||||||
|
struct size *size) {
|
||||||
unsigned char buf[DEVICE_NAME_FIELD_LENGTH + 4];
|
unsigned char buf[DEVICE_NAME_FIELD_LENGTH + 4];
|
||||||
ssize_t r = net_recv_all(device_socket, buf, sizeof(buf));
|
ssize_t r = net_recv_all(device_socket, buf, sizeof(buf));
|
||||||
if (r < DEVICE_NAME_FIELD_LENGTH + 4) {
|
if (r < DEVICE_NAME_FIELD_LENGTH + 4) {
|
||||||
@ -435,17 +482,18 @@ device_read_info(sc_socket device_socket, struct server_info *info) {
|
|||||||
}
|
}
|
||||||
// in case the client sends garbage
|
// in case the client sends garbage
|
||||||
buf[DEVICE_NAME_FIELD_LENGTH - 1] = '\0';
|
buf[DEVICE_NAME_FIELD_LENGTH - 1] = '\0';
|
||||||
memcpy(info->device_name, (char *) buf, sizeof(info->device_name));
|
// strcpy is safe here, since name contains at least
|
||||||
|
// DEVICE_NAME_FIELD_LENGTH bytes and strlen(buf) < DEVICE_NAME_FIELD_LENGTH
|
||||||
info->frame_size.width = (buf[DEVICE_NAME_FIELD_LENGTH] << 8)
|
strcpy(device_name, (char *) buf);
|
||||||
| buf[DEVICE_NAME_FIELD_LENGTH + 1];
|
size->width = (buf[DEVICE_NAME_FIELD_LENGTH] << 8)
|
||||||
info->frame_size.height = (buf[DEVICE_NAME_FIELD_LENGTH + 2] << 8)
|
| buf[DEVICE_NAME_FIELD_LENGTH + 1];
|
||||||
| buf[DEVICE_NAME_FIELD_LENGTH + 3];
|
size->height = (buf[DEVICE_NAME_FIELD_LENGTH + 2] << 8)
|
||||||
|
| buf[DEVICE_NAME_FIELD_LENGTH + 3];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
server_connect_to(struct server *server, struct server_info *info) {
|
server_connect_to(struct server *server, char *device_name, struct size *size) {
|
||||||
if (!server->tunnel_forward) {
|
if (!server->tunnel_forward) {
|
||||||
server->video_socket = net_accept(server->server_socket);
|
server->video_socket = net_accept(server->server_socket);
|
||||||
if (server->video_socket == SC_INVALID_SOCKET) {
|
if (server->video_socket == SC_INVALID_SOCKET) {
|
||||||
@ -486,7 +534,7 @@ server_connect_to(struct server *server, struct server_info *info) {
|
|||||||
server->tunnel_enabled = false;
|
server->tunnel_enabled = false;
|
||||||
|
|
||||||
// The sockets will be closed on stop if device_read_info() fails
|
// The sockets will be closed on stop if device_read_info() fails
|
||||||
return device_read_info(server->video_socket, info);
|
return device_read_info(server->video_socket, device_name, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -556,7 +604,8 @@ server_destroy(struct server *server) {
|
|||||||
LOGW("Could not close control socket");
|
LOGW("Could not close control socket");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(server->serial);
|
|
||||||
|
server_params_destroy(&server->params);
|
||||||
sc_cond_destroy(&server->process_terminated_cond);
|
sc_cond_destroy(&server->process_terminated_cond);
|
||||||
sc_mutex_destroy(&server->mutex);
|
sc_mutex_destroy(&server->mutex);
|
||||||
}
|
}
|
||||||
|
@ -14,29 +14,6 @@
|
|||||||
#include "util/net.h"
|
#include "util/net.h"
|
||||||
#include "util/thread.h"
|
#include "util/thread.h"
|
||||||
|
|
||||||
#define DEVICE_NAME_FIELD_LENGTH 64
|
|
||||||
struct server_info {
|
|
||||||
char device_name[DEVICE_NAME_FIELD_LENGTH];
|
|
||||||
struct sc_size frame_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct server {
|
|
||||||
char *serial;
|
|
||||||
process_t process;
|
|
||||||
sc_thread wait_server_thread;
|
|
||||||
|
|
||||||
sc_mutex mutex;
|
|
||||||
sc_cond process_terminated_cond;
|
|
||||||
bool process_terminated;
|
|
||||||
|
|
||||||
sc_socket server_socket; // only used if !tunnel_forward
|
|
||||||
sc_socket video_socket;
|
|
||||||
sc_socket control_socket;
|
|
||||||
uint16_t local_port; // selected from port_range
|
|
||||||
bool tunnel_enabled;
|
|
||||||
bool tunnel_forward; // use "adb forward" instead of "adb reverse"
|
|
||||||
};
|
|
||||||
|
|
||||||
struct server_params {
|
struct server_params {
|
||||||
const char *serial;
|
const char *serial;
|
||||||
enum sc_log_level log_level;
|
enum sc_log_level log_level;
|
||||||
@ -56,17 +33,38 @@ struct server_params {
|
|||||||
bool power_off_on_close;
|
bool power_off_on_close;
|
||||||
};
|
};
|
||||||
|
|
||||||
// init default values
|
struct server {
|
||||||
|
// The internal allocated strings are copies owned by the server
|
||||||
|
struct server_params params;
|
||||||
|
|
||||||
|
process_t process;
|
||||||
|
sc_thread wait_server_thread;
|
||||||
|
|
||||||
|
sc_mutex mutex;
|
||||||
|
sc_cond process_terminated_cond;
|
||||||
|
bool process_terminated;
|
||||||
|
|
||||||
|
sc_socket server_socket; // only used if !tunnel_forward
|
||||||
|
sc_socket video_socket;
|
||||||
|
sc_socket control_socket;
|
||||||
|
uint16_t local_port; // selected from port_range
|
||||||
|
bool tunnel_enabled;
|
||||||
|
bool tunnel_forward; // use "adb forward" instead of "adb reverse"
|
||||||
|
};
|
||||||
|
|
||||||
|
// init the server with the given params
|
||||||
bool
|
bool
|
||||||
server_init(struct server *server);
|
server_init(struct server *server, const struct server_params *params);
|
||||||
|
|
||||||
// push, enable tunnel et start the server
|
// push, enable tunnel et start the server
|
||||||
bool
|
bool
|
||||||
server_start(struct server *server, const struct server_params *params);
|
server_start(struct server *server);
|
||||||
|
|
||||||
|
#define DEVICE_NAME_FIELD_LENGTH 64
|
||||||
// block until the communication with the server is established
|
// block until the communication with the server is established
|
||||||
|
// device_name must point to a buffer of at least DEVICE_NAME_FIELD_LENGTH bytes
|
||||||
bool
|
bool
|
||||||
server_connect_to(struct server *server, struct server_info *info);
|
server_connect_to(struct server *server, char *device_name, struct size *size);
|
||||||
|
|
||||||
// disconnect and kill the server process
|
// disconnect and kill the server process
|
||||||
void
|
void
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "util/strbuf.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
@ -211,81 +209,3 @@ utf8_from_wide_char(const wchar_t *ws) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char *wrap_lines(const char *input, unsigned columns, unsigned indent) {
|
|
||||||
assert(indent < columns);
|
|
||||||
|
|
||||||
struct sc_strbuf buf;
|
|
||||||
|
|
||||||
// The output string should not be a lot longer than the input string (just
|
|
||||||
// a few '\n' added), so this initial capacity should almost always avoid
|
|
||||||
// internal realloc() in string buffer
|
|
||||||
size_t cap = strlen(input) * 3 / 2;
|
|
||||||
|
|
||||||
if (!sc_strbuf_init(&buf, cap)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define APPEND(S,N) if (!sc_strbuf_append(&buf, S, N)) goto error
|
|
||||||
#define APPEND_CHAR(C) if (!sc_strbuf_append_char(&buf, C)) goto error
|
|
||||||
#define APPEND_N(C,N) if (!sc_strbuf_append_n(&buf, C, N)) goto error
|
|
||||||
#define APPEND_INDENT() if (indent) APPEND_N(' ', indent)
|
|
||||||
|
|
||||||
APPEND_INDENT();
|
|
||||||
|
|
||||||
// The last separator encountered, it must be inserted only conditionnaly,
|
|
||||||
// depending on the next token
|
|
||||||
char pending = 0;
|
|
||||||
|
|
||||||
// col tracks the current column in the current line
|
|
||||||
size_t col = indent;
|
|
||||||
while (*input) {
|
|
||||||
size_t sep_idx = strcspn(input, "\n ");
|
|
||||||
size_t new_col = col + sep_idx;
|
|
||||||
if (pending == ' ') {
|
|
||||||
// The pending space counts
|
|
||||||
++new_col;
|
|
||||||
}
|
|
||||||
bool wrap = new_col > columns;
|
|
||||||
|
|
||||||
char sep = input[sep_idx];
|
|
||||||
if (sep == ' ')
|
|
||||||
sep = ' ';
|
|
||||||
|
|
||||||
if (wrap) {
|
|
||||||
APPEND_CHAR('\n');
|
|
||||||
APPEND_INDENT();
|
|
||||||
col = indent;
|
|
||||||
} else if (pending) {
|
|
||||||
APPEND_CHAR(pending);
|
|
||||||
++col;
|
|
||||||
if (pending == '\n')
|
|
||||||
{
|
|
||||||
APPEND_INDENT();
|
|
||||||
col = indent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sep_idx) {
|
|
||||||
APPEND(input, sep_idx);
|
|
||||||
col += sep_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
pending = sep;
|
|
||||||
|
|
||||||
input += sep_idx;
|
|
||||||
if (*input != '\0') {
|
|
||||||
// Skip the separator
|
|
||||||
++input;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pending)
|
|
||||||
APPEND_CHAR(pending);
|
|
||||||
|
|
||||||
return buf.s;
|
|
||||||
|
|
||||||
error:
|
|
||||||
free(buf.s);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
@ -62,8 +62,4 @@ char *
|
|||||||
utf8_from_wide_char(const wchar_t *s);
|
utf8_from_wide_char(const wchar_t *s);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Wrap input lines at words boundaries (spaces) so that they fit in 'columns'
|
|
||||||
// columns, left-indented by 'indent' spaces
|
|
||||||
char *wrap_lines(const char *input, unsigned columns, unsigned indent);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
#include "strbuf.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_strbuf_init(struct sc_strbuf *buf, size_t init_cap) {
|
|
||||||
buf->s = malloc(init_cap + 1); // +1 for '\0'
|
|
||||||
if (!buf->s) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf->len = 0;
|
|
||||||
buf->cap = init_cap;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
sc_strbuf_reserve(struct sc_strbuf *buf, size_t len) {
|
|
||||||
if (buf->len + len > buf->cap) {
|
|
||||||
size_t new_cap = buf->cap * 3 / 2 + len;
|
|
||||||
char *s = realloc(buf->s, new_cap + 1); // +1 for '\0'
|
|
||||||
if (!s) {
|
|
||||||
// Leave the old buf->s
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
buf->s = s;
|
|
||||||
buf->cap = new_cap;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_strbuf_append(struct sc_strbuf *buf, const char *s, size_t len) {
|
|
||||||
assert(s);
|
|
||||||
assert(*s);
|
|
||||||
assert(strlen(s) >= len);
|
|
||||||
if (!sc_strbuf_reserve(buf, len)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&buf->s[buf->len], s, len);
|
|
||||||
buf->len += len;
|
|
||||||
buf->s[buf->len] = '\0';
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_strbuf_append_char(struct sc_strbuf *buf, const char c) {
|
|
||||||
if (!sc_strbuf_reserve(buf, 1)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf->s[buf->len] = c;
|
|
||||||
buf->len ++;
|
|
||||||
buf->s[buf->len] = '\0';
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_strbuf_append_n(struct sc_strbuf *buf, const char c, size_t n) {
|
|
||||||
if (!sc_strbuf_reserve(buf, n)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&buf->s[buf->len], c, n);
|
|
||||||
buf->len += n;
|
|
||||||
buf->s[buf->len] = '\0';
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
sc_strbuf_shrink(struct sc_strbuf *buf) {
|
|
||||||
assert(buf->len <= buf->cap);
|
|
||||||
if (buf->len != buf->cap) {
|
|
||||||
char *s = realloc(buf->s, buf->len + 1); // +1 for '\0'
|
|
||||||
assert(s); // decreasing the size may not fail
|
|
||||||
buf->s = s;
|
|
||||||
buf->cap = buf->len;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
#ifndef SC_STRBUF_H
|
|
||||||
#define SC_STRBUF_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
struct sc_strbuf {
|
|
||||||
char *s;
|
|
||||||
size_t len;
|
|
||||||
size_t cap;
|
|
||||||
};
|
|
||||||
|
|
||||||
// buf->s must be manually freed by the caller
|
|
||||||
bool
|
|
||||||
sc_strbuf_init(struct sc_strbuf *buf, size_t init_cap);
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_strbuf_append(struct sc_strbuf *buf, const char *s, size_t len);
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_strbuf_append_char(struct sc_strbuf *buf, const char c);
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_strbuf_append_n(struct sc_strbuf *buf, const char c, size_t n);
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
sc_strbuf_append_str(struct sc_strbuf *buf, const char *s) {
|
|
||||||
return sc_strbuf_append(buf, s, strlen(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append static string (i.e. the string size is known at compile time, for
|
|
||||||
// example a string literal)
|
|
||||||
#define sc_strbuf_append_staticstr(BUF, S) \
|
|
||||||
sc_strbuf_append(BUF, S, sizeof(S) - 1)
|
|
||||||
|
|
||||||
void
|
|
||||||
sc_strbuf_shrink(struct sc_strbuf *buf);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,51 +0,0 @@
|
|||||||
#include "term.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
# include <windows.h>
|
|
||||||
#else
|
|
||||||
# include <unistd.h>
|
|
||||||
# include <sys/ioctl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_term_get_size(unsigned *rows, unsigned *cols) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
||||||
|
|
||||||
bool ok =
|
|
||||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
|
||||||
if (!ok) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rows) {
|
|
||||||
assert(csbi.srWindow.Bottom >= csbi.srWindow.Top);
|
|
||||||
*rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cols) {
|
|
||||||
assert(csbi.srWindow.Right >= csbi.srWindow.Left);
|
|
||||||
*cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
struct winsize ws;
|
|
||||||
int r = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
|
|
||||||
if (r == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rows) {
|
|
||||||
*rows = ws.ws_row;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cols) {
|
|
||||||
*cols = ws.ws_col;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
#ifndef SC_TERM_H
|
|
||||||
#define SC_TERM_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_term_get_size(unsigned *rows, unsigned *cols);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,8 +1,6 @@
|
|||||||
#ifndef SC_TICK_H
|
#ifndef SC_TICK_H
|
||||||
#define SC_TICK_H
|
#define SC_TICK_H
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef int64_t sc_tick;
|
typedef int64_t sc_tick;
|
||||||
|
@ -359,7 +359,7 @@ sc_v4l2_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
sc_v4l2_sink_init(struct sc_v4l2_sink *vs, const char *device_name,
|
sc_v4l2_sink_init(struct sc_v4l2_sink *vs, const char *device_name,
|
||||||
struct sc_size frame_size, sc_tick buffering_time) {
|
struct size frame_size, sc_tick buffering_time) {
|
||||||
vs->device_name = strdup(device_name);
|
vs->device_name = strdup(device_name);
|
||||||
if (!vs->device_name) {
|
if (!vs->device_name) {
|
||||||
LOGE("Could not strdup v4l2 device name");
|
LOGE("Could not strdup v4l2 device name");
|
||||||
|
@ -18,7 +18,7 @@ struct sc_v4l2_sink {
|
|||||||
AVCodecContext *encoder_ctx;
|
AVCodecContext *encoder_ctx;
|
||||||
|
|
||||||
char *device_name;
|
char *device_name;
|
||||||
struct sc_size frame_size;
|
struct size frame_size;
|
||||||
sc_tick buffering_time;
|
sc_tick buffering_time;
|
||||||
|
|
||||||
sc_thread thread;
|
sc_thread thread;
|
||||||
@ -34,7 +34,7 @@ struct sc_v4l2_sink {
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
sc_v4l2_sink_init(struct sc_v4l2_sink *vs, const char *device_name,
|
sc_v4l2_sink_init(struct sc_v4l2_sink *vs, const char *device_name,
|
||||||
struct sc_size frame_size, sc_tick buffering_time);
|
struct size frame_size, sc_tick buffering_time);
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_v4l2_sink_destroy(struct sc_v4l2_sink *vs);
|
sc_v4l2_sink_destroy(struct sc_v4l2_sink *vs);
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
#include "common.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "util/strbuf.h"
|
|
||||||
|
|
||||||
static void test_strbuf_simple(void) {
|
|
||||||
struct sc_strbuf buf;
|
|
||||||
bool ok = sc_strbuf_init(&buf, 10);
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
ok = sc_strbuf_append_staticstr(&buf, "Hello");
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
ok = sc_strbuf_append_char(&buf, ' ');
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
ok = sc_strbuf_append_staticstr(&buf, "world");
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
ok = sc_strbuf_append_staticstr(&buf, "!\n");
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
ok = sc_strbuf_append_staticstr(&buf, "This is a test");
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
ok = sc_strbuf_append_n(&buf, '.', 3);
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
assert(!strcmp(buf.s, "Hello world!\nThis is a test..."));
|
|
||||||
|
|
||||||
sc_strbuf_shrink(&buf);
|
|
||||||
assert(buf.len == buf.cap);
|
|
||||||
assert(!strcmp(buf.s, "Hello world!\nThis is a test..."));
|
|
||||||
|
|
||||||
free(buf.s);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
(void) argc;
|
|
||||||
(void) argv;
|
|
||||||
|
|
||||||
test_strbuf_simple();
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -299,44 +299,6 @@ static void test_strlist_contains(void) {
|
|||||||
assert(strlist_contains("xyz", '\0', "xyz"));
|
assert(strlist_contains("xyz", '\0', "xyz"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_wrap_lines(void) {
|
|
||||||
const char *s = "This is a text to test line wrapping. The lines must be "
|
|
||||||
"wrapped at a space or a line break.\n"
|
|
||||||
"\n"
|
|
||||||
"This rectangle must remains a rectangle because it is "
|
|
||||||
"drawn in lines having lengths lower than the specified "
|
|
||||||
"number of columns:\n"
|
|
||||||
" +----+\n"
|
|
||||||
" | |\n"
|
|
||||||
" +----+\n";
|
|
||||||
|
|
||||||
// |---- 1 1 2 2|
|
|
||||||
// |0 5 0 5 0 3| <-- 24 columns
|
|
||||||
const char *expected = " This is a text to\n"
|
|
||||||
" test line wrapping.\n"
|
|
||||||
" The lines must be\n"
|
|
||||||
" wrapped at a space\n"
|
|
||||||
" or a line break.\n"
|
|
||||||
" \n"
|
|
||||||
" This rectangle must\n"
|
|
||||||
" remains a rectangle\n"
|
|
||||||
" because it is drawn\n"
|
|
||||||
" in lines having\n"
|
|
||||||
" lengths lower than\n"
|
|
||||||
" the specified number\n"
|
|
||||||
" of columns:\n"
|
|
||||||
" +----+\n"
|
|
||||||
" | |\n"
|
|
||||||
" +----+\n";
|
|
||||||
|
|
||||||
char *formatted = wrap_lines(s, 24, 4);
|
|
||||||
assert(formatted);
|
|
||||||
|
|
||||||
assert(!strcmp(formatted, expected));
|
|
||||||
|
|
||||||
free(formatted);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
(void) argc;
|
(void) argc;
|
||||||
(void) argv;
|
(void) argv;
|
||||||
@ -355,6 +317,5 @@ int main(int argc, char *argv[]) {
|
|||||||
test_parse_integers();
|
test_parse_integers();
|
||||||
test_parse_integer_with_suffix();
|
test_parse_integer_with_suffix();
|
||||||
test_strlist_contains();
|
test_strlist_contains();
|
||||||
test_wrap_lines();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.0.2'
|
classpath 'com.android.tools.build:gradle:4.0.1'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
@ -2,7 +2,7 @@ apply plugin: 'checkstyle'
|
|||||||
check.dependsOn 'checkstyle'
|
check.dependsOn 'checkstyle'
|
||||||
|
|
||||||
checkstyle {
|
checkstyle {
|
||||||
toolVersion = '9.0.1'
|
toolVersion = '6.19'
|
||||||
}
|
}
|
||||||
|
|
||||||
task checkstyle(type: Checkstyle) {
|
task checkstyle(type: Checkstyle) {
|
||||||
|
@ -37,14 +37,6 @@ page at http://checkstyle.sourceforge.net/config.html -->
|
|||||||
|
|
||||||
<module name="SuppressWarningsFilter"/>
|
<module name="SuppressWarningsFilter"/>
|
||||||
|
|
||||||
<module name="LineLength">
|
|
||||||
<!-- what is a good max value? -->
|
|
||||||
<property name="max" value="150" />
|
|
||||||
<!-- ignore lines like "$File: //depot/... $" -->
|
|
||||||
<property name="ignorePattern" value="\$File.*\$" />
|
|
||||||
<property name="severity" value="info" />
|
|
||||||
</module>
|
|
||||||
|
|
||||||
<module name="TreeWalker">
|
<module name="TreeWalker">
|
||||||
|
|
||||||
<!-- Checks for Naming Conventions. -->
|
<!-- Checks for Naming Conventions. -->
|
||||||
@ -80,6 +72,13 @@ page at http://checkstyle.sourceforge.net/config.html -->
|
|||||||
|
|
||||||
<!-- Checks for Size Violations. -->
|
<!-- Checks for Size Violations. -->
|
||||||
<!-- See http://checkstyle.sf.net/config_sizes.html -->
|
<!-- See http://checkstyle.sf.net/config_sizes.html -->
|
||||||
|
<module name="LineLength">
|
||||||
|
<!-- what is a good max value? -->
|
||||||
|
<property name="max" value="150" />
|
||||||
|
<!-- ignore lines like "$File: //depot/... $" -->
|
||||||
|
<property name="ignorePattern" value="\$File.*\$" />
|
||||||
|
<property name="severity" value="info" />
|
||||||
|
</module>
|
||||||
<module name="MethodLength" />
|
<module name="MethodLength" />
|
||||||
<module name="ParameterNumber">
|
<module name="ParameterNumber">
|
||||||
<property name="ignoreOverriddenMethods" value="true"/>
|
<property name="ignoreOverriddenMethods" value="true"/>
|
||||||
@ -153,6 +152,26 @@ page at http://checkstyle.sourceforge.net/config.html -->
|
|||||||
</module>
|
</module>
|
||||||
<module name="UpperEll" />
|
<module name="UpperEll" />
|
||||||
|
|
||||||
|
<module name="FileContentsHolder" />
|
||||||
|
<!-- Required by comment suppression filters -->
|
||||||
|
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="SuppressionFilter">
|
||||||
|
<!--<property name="file" value="team-props/checkstyle/checkstyle-suppressions.xml" />-->
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!-- Enable suppression comments -->
|
||||||
|
<module name="SuppressionCommentFilter">
|
||||||
|
<property name="offCommentFormat" value="CHECKSTYLE IGNORE\s+(\S+)" />
|
||||||
|
<property name="onCommentFormat" value="CHECKSTYLE END IGNORE\s+(\S+)" />
|
||||||
|
<property name="checkFormat" value="$1" />
|
||||||
|
</module>
|
||||||
|
<module name="SuppressWithNearbyCommentFilter">
|
||||||
|
<!-- Syntax is "SUPPRESS CHECKSTYLE name" -->
|
||||||
|
<property name="commentFormat" value="SUPPRESS CHECKSTYLE (\w+)" />
|
||||||
|
<property name="checkFormat" value="$1" />
|
||||||
|
<property name="influenceFormat" value="1" />
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
</module>
|
</module>
|
||||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 31
|
compileSdkVersion 30
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.genymobile.scrcpy"
|
applicationId "com.genymobile.scrcpy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 31
|
targetSdkVersion 30
|
||||||
versionCode 11900
|
versionCode 11900
|
||||||
versionName "1.19"
|
versionName "1.19"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
@ -2,6 +2,7 @@ package com.genymobile.scrcpy;
|
|||||||
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user