Compare commits

..

2 Commits

Author SHA1 Message Date
781bb7397b Copy server params
This is a preliminary step necessary to move the server to a separate
thread.
2021-10-27 23:40:52 +02:00
b876cf9ddc Reorder server and server_params
This will allow to define a server_params field in server.
2021-10-27 23:20:47 +02:00
45 changed files with 845 additions and 1677 deletions

View File

@ -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._

View File

@ -24,27 +24,18 @@ src = [
'src/server.c', 'src/server.c',
'src/stream.c', 'src/stream.c',
'src/video_buffer.c', 'src/video_buffer.c',
'src/util/file.c',
'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',
] ]
if host_machine.system() == 'windows' if host_machine.system() == 'windows'
src += [ src += [ 'src/sys/win/process.c' ]
'src/sys/win/file.c',
'src/sys/win/process.c',
]
else else
src += [ src += [ 'src/sys/unix/process.c' ]
'src/sys/unix/file.c',
'src/sys/unix/process.c',
]
endif endif
v4l2_support = host_machine.system() == 'linux' v4l2_support = host_machine.system() == 'linux'
@ -195,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',
@ -206,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', [
@ -216,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',
]], ]],
] ]

View File

@ -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

View File

@ -5,7 +5,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "util/file.h"
#include "util/log.h" #include "util/log.h"
#include "util/str_util.h" #include "util/str_util.h"
@ -69,7 +68,7 @@ show_adb_installation_msg() {
{"pacman", "pacman -S android-tools"}, {"pacman", "pacman -S android-tools"},
}; };
for (size_t i = 0; i < ARRAY_LEN(pkg_managers); ++i) { for (size_t i = 0; i < ARRAY_LEN(pkg_managers); ++i) {
if (sc_file_executable_exists(pkg_managers[i].binary)) { if (search_executable(pkg_managers[i].binary)) {
LOGI("You may install 'adb' by \"%s\"", pkg_managers[i].command); LOGI("You may install 'adb' by \"%s\"", pkg_managers[i].command);
return; return;
} }

File diff suppressed because it is too large Load Diff

View File

@ -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.
* *

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -8,8 +8,8 @@
#include "config.h" #include "config.h"
#include "compat.h" #include "compat.h"
#include "util/file.h"
#include "util/log.h" #include "util/log.h"
#include "util/process.h"
#include "util/str_util.h" #include "util/str_util.h"
#define SCRCPY_PORTABLE_ICON_FILENAME "icon.png" #define SCRCPY_PORTABLE_ICON_FILENAME "icon.png"
@ -46,7 +46,7 @@ get_icon_path(void) {
return NULL; return NULL;
} }
#else #else
char *icon_path = sc_file_get_local_path(SCRCPY_PORTABLE_ICON_FILENAME); char *icon_path = get_local_file_path(SCRCPY_PORTABLE_ICON_FILENAME);
if (!icon_path) { if (!icon_path) {
LOGE("Could not get icon path"); LOGE("Could not get icon path");
return NULL; return NULL;

View File

@ -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;

View File

@ -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),

View File

@ -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");

View File

@ -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);

View File

@ -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, &params)) {
if (!server_init(&s->server, &params)) {
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;
} }

View File

@ -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);

View File

@ -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);

View File

@ -8,7 +8,6 @@
#include <SDL2/SDL_platform.h> #include <SDL2/SDL_platform.h>
#include "adb.h" #include "adb.h"
#include "util/file.h"
#include "util/log.h" #include "util/log.h"
#include "util/net.h" #include "util/net.h"
#include "util/str_util.h" #include "util/str_util.h"
@ -49,7 +48,7 @@ get_server_path(void) {
return NULL; return NULL;
} }
#else #else
char *server_path = sc_file_get_local_path(SERVER_FILENAME); char *server_path = get_local_file_path(SERVER_FILENAME);
if (!server_path) { if (!server_path) {
LOGE("Could not get local file path, " LOGE("Could not get local file path, "
"using " SERVER_FILENAME " from current directory"); "using " SERVER_FILENAME " from current directory");
@ -62,13 +61,51 @@ 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();
if (!server_path) { if (!server_path) {
return false; return false;
} }
if (!sc_file_is_regular(server_path)) { if (!is_regular_file(server_path)) {
LOGE("'%s' does not exist or is not a regular file\n", server_path); LOGE("'%s' does not exist or is not a regular file\n", server_path);
free(server_path); free(server_path);
return false; return false;
@ -104,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
@ -119,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;
} }
@ -140,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);
} }
@ -166,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;
@ -230,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];
@ -287,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
@ -325,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;
} }
@ -376,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() */
@ -427,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) {
@ -436,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) {
@ -487,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
@ -557,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);
} }

View File

@ -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

View File

@ -1,75 +0,0 @@
#include "util/file.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
bool
sc_file_executable_exists(const char *file) {
char *path = getenv("PATH");
if (!path)
return false;
path = strdup(path);
if (!path)
return false;
bool ret = false;
size_t file_len = strlen(file);
char *saveptr;
for (char *dir = strtok_r(path, ":", &saveptr); dir;
dir = strtok_r(NULL, ":", &saveptr)) {
size_t dir_len = strlen(dir);
char *fullpath = malloc(dir_len + file_len + 2);
if (!fullpath)
continue;
memcpy(fullpath, dir, dir_len);
fullpath[dir_len] = '/';
memcpy(fullpath + dir_len + 1, file, file_len + 1);
struct stat sb;
bool fullpath_executable = stat(fullpath, &sb) == 0 &&
sb.st_mode & S_IXUSR;
free(fullpath);
if (fullpath_executable) {
ret = true;
break;
}
}
free(path);
return ret;
}
char *
sc_file_get_executable_path(void) {
// <https://stackoverflow.com/a/1024937/1987178>
#ifdef __linux__
char buf[PATH_MAX + 1]; // +1 for the null byte
ssize_t len = readlink("/proc/self/exe", buf, PATH_MAX);
if (len == -1) {
perror("readlink");
return NULL;
}
buf[len] = '\0';
return strdup(buf);
#else
// in practice, we only need this feature for portable builds, only used on
// Windows, so we don't care implementing it for every platform
// (it's useful to have a working version on Linux for debugging though)
return NULL;
#endif
}
bool
sc_file_is_regular(const char *path) {
struct stat path_stat;
if (stat(path, &path_stat)) {
perror("stat");
return false;
}
return S_ISREG(path_stat.st_mode);
}

View File

@ -3,13 +3,53 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h>
#include <signal.h> #include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#include "util/log.h" #include "util/log.h"
bool
search_executable(const char *file) {
char *path = getenv("PATH");
if (!path)
return false;
path = strdup(path);
if (!path)
return false;
bool ret = false;
size_t file_len = strlen(file);
char *saveptr;
for (char *dir = strtok_r(path, ":", &saveptr); dir;
dir = strtok_r(NULL, ":", &saveptr)) {
size_t dir_len = strlen(dir);
char *fullpath = malloc(dir_len + file_len + 2);
if (!fullpath)
continue;
memcpy(fullpath, dir, dir_len);
fullpath[dir_len] = '/';
memcpy(fullpath + dir_len + 1, file, file_len + 1);
struct stat sb;
bool fullpath_executable = stat(fullpath, &sb) == 0 &&
sb.st_mode & S_IXUSR;
free(fullpath);
if (fullpath_executable) {
ret = true;
break;
}
}
free(path);
return ret;
}
enum process_result enum process_result
process_execute_redirect(const char *const argv[], pid_t *pid, int *pipe_stdin, process_execute_redirect(const char *const argv[], pid_t *pid, int *pipe_stdin,
int *pipe_stdout, int *pipe_stderr) { int *pipe_stdout, int *pipe_stderr) {
@ -192,6 +232,37 @@ process_close(pid_t pid) {
process_wait(pid, true); // ignore exit code process_wait(pid, true); // ignore exit code
} }
char *
get_executable_path(void) {
// <https://stackoverflow.com/a/1024937/1987178>
#ifdef __linux__
char buf[PATH_MAX + 1]; // +1 for the null byte
ssize_t len = readlink("/proc/self/exe", buf, PATH_MAX);
if (len == -1) {
perror("readlink");
return NULL;
}
buf[len] = '\0';
return strdup(buf);
#else
// in practice, we only need this feature for portable builds, only used on
// Windows, so we don't care implementing it for every platform
// (it's useful to have a working version on Linux for debugging though)
return NULL;
#endif
}
bool
is_regular_file(const char *path) {
struct stat path_stat;
if (stat(path, &path_stat)) {
perror("stat");
return false;
}
return S_ISREG(path_stat.st_mode);
}
ssize_t ssize_t
read_pipe(int pipe, char *data, size_t len) { read_pipe(int pipe, char *data, size_t len) {
return read(pipe, data, len); return read(pipe, data, len);

View File

@ -1,43 +0,0 @@
#include "util/file.h"
#include <windows.h>
#include <sys/stat.h>
#include "util/log.h"
#include "util/str_util.h"
char *
sc_file_get_executable_path(void) {
HMODULE hModule = GetModuleHandleW(NULL);
if (!hModule) {
return NULL;
}
WCHAR buf[MAX_PATH + 1]; // +1 for the null byte
int len = GetModuleFileNameW(hModule, buf, MAX_PATH);
if (!len) {
return NULL;
}
buf[len] = '\0';
return utf8_from_wide_char(buf);
}
bool
sc_file_is_regular(const char *path) {
wchar_t *wide_path = utf8_to_wide_char(path);
if (!wide_path) {
LOGC("Could not allocate wide char string");
return false;
}
struct _stat path_stat;
int r = _wstat(wide_path, &path_stat);
free(wide_path);
if (r) {
perror("stat");
return false;
}
return S_ISREG(path_stat.st_mode);
}

View File

@ -1,6 +1,7 @@
#include "util/process.h" #include "util/process.h"
#include <assert.h> #include <assert.h>
#include <sys/stat.h>
#include "util/log.h" #include "util/log.h"
#include "util/str_util.h" #include "util/str_util.h"
@ -173,6 +174,40 @@ process_close(HANDLE handle) {
(void) closed; (void) closed;
} }
char *
get_executable_path(void) {
HMODULE hModule = GetModuleHandleW(NULL);
if (!hModule) {
return NULL;
}
WCHAR buf[MAX_PATH + 1]; // +1 for the null byte
int len = GetModuleFileNameW(hModule, buf, MAX_PATH);
if (!len) {
return NULL;
}
buf[len] = '\0';
return utf8_from_wide_char(buf);
}
bool
is_regular_file(const char *path) {
wchar_t *wide_path = utf8_to_wide_char(path);
if (!wide_path) {
LOGC("Could not allocate wide char string");
return false;
}
struct _stat path_stat;
int r = _wstat(wide_path, &path_stat);
free(wide_path);
if (r) {
perror("stat");
return false;
}
return S_ISREG(path_stat.st_mode);
}
ssize_t ssize_t
read_pipe(HANDLE pipe, char *data, size_t len) { read_pipe(HANDLE pipe, char *data, size_t len) {
DWORD r; DWORD r;

View File

@ -1,48 +0,0 @@
#include "file.h"
#include <stdlib.h>
#include <string.h>
#include "util/log.h"
char *
sc_file_get_local_path(const char *name) {
char *executable_path = sc_file_get_executable_path();
if (!executable_path) {
return NULL;
}
// dirname() does not work correctly everywhere, so get the parent
// directory manually.
// See <https://github.com/Genymobile/scrcpy/issues/2619>
char *p = strrchr(executable_path, SC_PATH_SEPARATOR);
if (!p) {
LOGE("Unexpected executable path: \"%s\" (it should contain a '%c')",
executable_path, SC_PATH_SEPARATOR);
free(executable_path);
return NULL;
}
*p = '\0'; // modify executable_path in place
char *dir = executable_path;
size_t dirlen = strlen(dir);
size_t namelen = strlen(name);
size_t len = dirlen + namelen + 2; // +2: '/' and '\0'
char *file_path = malloc(len);
if (!file_path) {
LOGE("Could not alloc path");
free(executable_path);
return NULL;
}
memcpy(file_path, dir, dirlen);
file_path[dirlen] = SC_PATH_SEPARATOR;
// namelen + 1 to copy the final '\0'
memcpy(&file_path[dirlen + 1], name, namelen + 1);
free(executable_path);
return file_path;
}

View File

@ -1,49 +0,0 @@
#ifndef SC_FILE_H
#define SC_FILE_H
#include "common.h"
#include <stdbool.h>
#ifdef _WIN32
# define SC_PATH_SEPARATOR '\\'
#else
# define SC_PATH_SEPARATOR '/'
#endif
#ifndef _WIN32
/**
* Indicate if an executable exists using $PATH
*
* In practice, it is only used to know if a package manager is available on
* the system. It is only implemented on Linux.
*/
bool
sc_file_executable_exists(const char *file);
#endif
/**
* Return the absolute path of the executable (the scrcpy binary)
*
* The result must be freed by the caller using free(). It may return NULL on
* error.
*/
char *
sc_file_get_executable_path(void);
/**
* Return the absolute path of a file in the same directory as the executable
*
* The result must be freed by the caller using free(). It may return NULL on
* error.
*/
char *
sc_file_get_local_path(const char *name);
/**
* Indicate if the file exists and is not a directory
*/
bool
sc_file_is_regular(const char *path);
#endif

View File

@ -21,6 +21,47 @@ process_check_success(process_t proc, const char *name, bool close) {
return true; return true;
} }
char *
get_local_file_path(const char *name) {
char *executable_path = get_executable_path();
if (!executable_path) {
return NULL;
}
// dirname() does not work correctly everywhere, so get the parent
// directory manually.
// See <https://github.com/Genymobile/scrcpy/issues/2619>
char *p = strrchr(executable_path, PATH_SEPARATOR);
if (!p) {
LOGE("Unexpected executable path: \"%s\" (it should contain a '%c')",
executable_path, PATH_SEPARATOR);
free(executable_path);
return NULL;
}
*p = '\0'; // modify executable_path in place
char *dir = executable_path;
size_t dirlen = strlen(dir);
size_t namelen = strlen(name);
size_t len = dirlen + namelen + 2; // +2: '/' and '\0'
char *file_path = malloc(len);
if (!file_path) {
LOGE("Could not alloc path");
free(executable_path);
return NULL;
}
memcpy(file_path, dir, dirlen);
file_path[dirlen] = PATH_SEPARATOR;
// namelen + 1 to copy the final '\0'
memcpy(&file_path[dirlen + 1], name, namelen + 1);
free(executable_path);
return file_path;
}
ssize_t ssize_t
read_pipe_all(pipe_t pipe, char *data, size_t len) { read_pipe_all(pipe_t pipe, char *data, size_t len) {
size_t copied = 0; size_t copied = 0;

View File

@ -10,6 +10,7 @@
// not needed here, but winsock2.h must never be included AFTER windows.h // not needed here, but winsock2.h must never be included AFTER windows.h
# include <winsock2.h> # include <winsock2.h>
# include <windows.h> # include <windows.h>
# define PATH_SEPARATOR '\\'
# define PRIexitcode "lu" # define PRIexitcode "lu"
// <https://stackoverflow.com/a/44383330/1987178> // <https://stackoverflow.com/a/44383330/1987178>
# define PRIsizet "Iu" # define PRIsizet "Iu"
@ -22,6 +23,7 @@
#else #else
# include <sys/types.h> # include <sys/types.h>
# define PATH_SEPARATOR '/'
# define PRIsizet "zu" # define PRIsizet "zu"
# define PRIexitcode "d" # define PRIexitcode "d"
# define PROCESS_NONE -1 # define PROCESS_NONE -1
@ -71,6 +73,26 @@ process_close(process_t pid);
bool bool
process_check_success(process_t proc, const char *name, bool close); process_check_success(process_t proc, const char *name, bool close);
#ifndef _WIN32
// only used to find package manager, not implemented for Windows
bool
search_executable(const char *file);
#endif
// return the absolute path of the executable (the scrcpy binary)
// may be NULL on error; to be freed by free()
char *
get_executable_path(void);
// Return the absolute path of a file in the same directory as he executable.
// May be NULL on error. To be freed by free().
char *
get_local_file_path(const char *name);
// returns true if the file exists and is not a directory
bool
is_regular_file(const char *path);
ssize_t ssize_t
read_pipe(pipe_t pipe, char *data, size_t len); read_pipe(pipe_t pipe, char *data, size_t len);

View File

@ -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 *sc_str_wrap_lines(const char *input, unsigned columns, unsigned indent) {
assert(indent < columns);
struct sc_strbuf buf;
// The output string should not be much longer than the input string (just
// a few '\n' added), so this initial capacity should hopefully 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;
}

View File

@ -62,12 +62,4 @@ char *
utf8_from_wide_char(const wchar_t *s); utf8_from_wide_char(const wchar_t *s);
#endif #endif
/**
* Wrap input lines to fit in `columns` columns
*
* Break input lines at word boundaries (spaces) so that they fit in `columns`
* columns, left-indented by `indent` spaces.
*/
char *sc_str_wrap_lines(const char *input, unsigned columns, unsigned indent);
#endif #endif

View File

@ -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;
}
}

View File

@ -1,73 +0,0 @@
#ifndef SC_STRBUF_H
#define SC_STRBUF_H
#include "common.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
struct sc_strbuf {
char *s;
size_t len;
size_t cap;
};
/**
* Initialize the string buffer
*
* `buf->s` must be manually freed by the caller.
*/
bool
sc_strbuf_init(struct sc_strbuf *buf, size_t init_cap);
/**
* Append a string
*
* Append `len` characters from `s` to the buffer.
*/
bool
sc_strbuf_append(struct sc_strbuf *buf, const char *s, size_t len);
/**
* Append a char
*
* Append a single character to the buffer.
*/
bool
sc_strbuf_append_char(struct sc_strbuf *buf, const char c);
/**
* Append a char `n` times
*
* Append the same characters `n` times to the buffer.
*/
bool
sc_strbuf_append_n(struct sc_strbuf *buf, const char c, size_t n);
/**
* Append a NUL-terminated string
*/
static inline bool
sc_strbuf_append_str(struct sc_strbuf *buf, const char *s) {
return sc_strbuf_append(buf, s, strlen(s));
}
/**
* Append a static string
*
* Append a string whose 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)
/**
* Shrink the buffer capacity to its current length
*
* This resizes `buf->s` to fit the content.
*/
void
sc_strbuf_shrink(struct sc_strbuf *buf);
#endif

View File

@ -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
}

View File

@ -1,21 +0,0 @@
#ifndef SC_TERM_H
#define SC_TERM_H
#include "common.h"
#include <stdbool.h>
/**
* Return the terminal dimensions
*
* Return false if the dimensions could not be retrieved.
*
* Otherwise, return true, and:
* - if `rows` is not NULL, then the number of rows is written to `*rows`.
* - if `columns` is not NULL, then the number of columns is written to
* `*columns`.
*/
bool
sc_term_get_size(unsigned *rows, unsigned *cols);
#endif

View File

@ -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;

View File

@ -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");

View File

@ -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);

View File

@ -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;
}

View File

@ -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 = sc_str_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;
} }

View File

@ -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

View File

@ -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) {

View File

@ -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>

View File

@ -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

View File

@ -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"

View File

@ -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;