Compare commits

...

6 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
34eb10ea0b Define v4l2 option field only if HAVE_V4L2 2021-10-27 18:43:47 +02:00
27fa23846d Define default options as const struct
This is more readable than a macro, and we could ifdef some fields.
2021-10-27 18:43:47 +02:00
e4d5c1ce36 Move scrcpy option structs to options.h
This will allow to define symbols in options.c without all the
dependencies of scrcpy.c.
2021-10-27 18:43:47 +02:00
ac23bec144 Expose socket interruption
On Linux, socket functions are unblocked by shutdown(), but on Windows
they are unblocked by closesocket().

Expose net_interrupt() and net_close() to abstract these differences:
 - net_interrupt() calls shutdown() on Linux and closesocket() on
   Windows (if not already called);
 - net_close() calls close() on Linux and closesocket() on Windows (if
   not already called).

This simplifies the server code, and prevents a data race on close
(reported by TSAN) on Linux (but does not fix it on Windows):

    WARNING: ThreadSanitizer: data race (pid=836124)
      Write of size 8 at 0x7ba0000000d0 by main thread:
        #0 close ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1690 (libtsan.so.0+0x359d8)
        #1 net_close ../app/src/util/net.c:211 (scrcpy+0x1c76b)
        #2 close_socket ../app/src/server.c:330 (scrcpy+0x19442)
        #3 server_stop ../app/src/server.c:522 (scrcpy+0x19e33)
        #4 scrcpy ../app/src/scrcpy.c:532 (scrcpy+0x156fc)
        #5 main ../app/src/main.c:92 (scrcpy+0x622a)

      Previous read of size 8 at 0x7ba0000000d0 by thread T6:
        #0 recv ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:6603 (libtsan.so.0+0x4f4a6)
        #1 net_recv ../app/src/util/net.c:167 (scrcpy+0x1c5a7)
        #2 run_receiver ../app/src/receiver.c:76 (scrcpy+0x12819)
        #3 <null> <null> (libSDL2-2.0.so.0+0x84f40)
2021-10-27 18:41:58 +02:00
20 changed files with 343 additions and 257 deletions

View File

@ -16,6 +16,7 @@ src = [
'src/keyboard_inject.c', 'src/keyboard_inject.c',
'src/mouse_inject.c', 'src/mouse_inject.c',
'src/opengl.c', 'src/opengl.c',
'src/options.c',
'src/receiver.c', 'src/receiver.c',
'src/recorder.c', 'src/recorder.c',
'src/scrcpy.c', 'src/scrcpy.c',
@ -184,6 +185,7 @@ if get_option('buildtype') == 'debug'
['test_cli', [ ['test_cli', [
'tests/test_cli.c', 'tests/test_cli.c',
'src/cli.c', 'src/cli.c',
'src/options.c',
'src/util/str_util.c', 'src/util/str_util.c',
]], ]],
['test_clock', [ ['test_clock', [

View File

@ -6,7 +6,6 @@
#include <libusb-1.0/libusb.h> #include <libusb-1.0/libusb.h>
#include "scrcpy.h"
#include "util/cbuf.h" #include "util/cbuf.h"
#include "util/thread.h" #include "util/thread.h"
#include "util/tick.h" #include "util/tick.h"

View File

@ -6,7 +6,7 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include "scrcpy.h" #include "options.h"
#include "util/log.h" #include "util/log.h"
#include "util/str_util.h" #include "util/str_util.h"

View File

@ -5,7 +5,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "scrcpy.h" #include "options.h"
struct scrcpy_cli_args { struct scrcpy_cli_args {
struct scrcpy_options opts; struct scrcpy_options opts;

View File

@ -9,7 +9,7 @@
#include "controller.h" #include "controller.h"
#include "fps_counter.h" #include "fps_counter.h"
#include "scrcpy.h" #include "options.h"
#include "screen.h" #include "screen.h"
#include "trait/key_processor.h" #include "trait/key_processor.h"
#include "trait/mouse_processor.h" #include "trait/mouse_processor.h"

View File

@ -6,7 +6,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "controller.h" #include "controller.h"
#include "scrcpy.h" #include "options.h"
#include "trait/key_processor.h" #include "trait/key_processor.h"
struct sc_keyboard_inject { struct sc_keyboard_inject {

View File

@ -1,5 +1,3 @@
#include "scrcpy.h"
#include "common.h" #include "common.h"
#include <assert.h> #include <assert.h>
@ -13,6 +11,8 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include "cli.h" #include "cli.h"
#include "options.h"
#include "scrcpy.h"
#include "util/log.h" #include "util/log.h"
static void static void
@ -48,7 +48,7 @@ main(int argc, char *argv[]) {
#endif #endif
struct scrcpy_cli_args args = { struct scrcpy_cli_args args = {
.opts = SCRCPY_OPTIONS_DEFAULT, .opts = scrcpy_options_default,
.help = false, .help = false,
.version = false, .version = false,
}; };

View File

@ -6,7 +6,6 @@
#include <stdbool.h> #include <stdbool.h>
#include "controller.h" #include "controller.h"
#include "scrcpy.h"
#include "screen.h" #include "screen.h"
#include "trait/mouse_processor.h" #include "trait/mouse_processor.h"

54
app/src/options.c Normal file
View File

@ -0,0 +1,54 @@
#include "options.h"
const struct scrcpy_options scrcpy_options_default = {
.serial = NULL,
.crop = NULL,
.record_filename = NULL,
.window_title = NULL,
.push_target = NULL,
.render_driver = NULL,
.codec_options = NULL,
.encoder_name = NULL,
#ifdef HAVE_V4L2
.v4l2_device = NULL,
#endif
.log_level = SC_LOG_LEVEL_INFO,
.record_format = SC_RECORD_FORMAT_AUTO,
.keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT,
.port_range = {
.first = DEFAULT_LOCAL_PORT_RANGE_FIRST,
.last = DEFAULT_LOCAL_PORT_RANGE_LAST,
},
.shortcut_mods = {
.data = {SC_MOD_LALT, SC_MOD_LSUPER},
.count = 2,
},
.max_size = 0,
.bit_rate = DEFAULT_BIT_RATE,
.max_fps = 0,
.lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_UNLOCKED,
.rotation = 0,
.window_x = SC_WINDOW_POSITION_UNDEFINED,
.window_y = SC_WINDOW_POSITION_UNDEFINED,
.window_width = 0,
.window_height = 0,
.display_id = 0,
.display_buffer = 0,
.v4l2_buffer = 0,
.show_touches = false,
.fullscreen = false,
.always_on_top = false,
.control = true,
.display = true,
.turn_screen_off = false,
.prefer_text = false,
.window_borderless = false,
.mipmaps = true,
.stay_awake = false,
.force_adb_forward = false,
.disable_screensaver = false,
.forward_key_repeat = true,
.forward_all_clicks = false,
.legacy_paste = false,
.power_off_on_close = false,
};

113
app/src/options.h Normal file
View File

@ -0,0 +1,113 @@
#ifndef SCRCPY_OPTIONS_H
#define SCRCPY_OPTIONS_H
#include "common.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "util/tick.h"
enum sc_log_level {
SC_LOG_LEVEL_VERBOSE,
SC_LOG_LEVEL_DEBUG,
SC_LOG_LEVEL_INFO,
SC_LOG_LEVEL_WARN,
SC_LOG_LEVEL_ERROR,
};
enum sc_record_format {
SC_RECORD_FORMAT_AUTO,
SC_RECORD_FORMAT_MP4,
SC_RECORD_FORMAT_MKV,
};
enum sc_lock_video_orientation {
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED = -1,
// lock the current orientation when scrcpy starts
SC_LOCK_VIDEO_ORIENTATION_INITIAL = -2,
SC_LOCK_VIDEO_ORIENTATION_0 = 0,
SC_LOCK_VIDEO_ORIENTATION_1,
SC_LOCK_VIDEO_ORIENTATION_2,
SC_LOCK_VIDEO_ORIENTATION_3,
};
enum sc_keyboard_input_mode {
SC_KEYBOARD_INPUT_MODE_INJECT,
SC_KEYBOARD_INPUT_MODE_HID,
};
#define SC_MAX_SHORTCUT_MODS 8
enum sc_shortcut_mod {
SC_MOD_LCTRL = 1 << 0,
SC_MOD_RCTRL = 1 << 1,
SC_MOD_LALT = 1 << 2,
SC_MOD_RALT = 1 << 3,
SC_MOD_LSUPER = 1 << 4,
SC_MOD_RSUPER = 1 << 5,
};
struct sc_shortcut_mods {
unsigned data[SC_MAX_SHORTCUT_MODS];
unsigned count;
};
struct sc_port_range {
uint16_t first;
uint16_t last;
};
#define SC_WINDOW_POSITION_UNDEFINED (-0x8000)
struct scrcpy_options {
const char *serial;
const char *crop;
const char *record_filename;
const char *window_title;
const char *push_target;
const char *render_driver;
const char *codec_options;
const char *encoder_name;
#ifdef HAVE_V4L2
const char *v4l2_device;
#endif
enum sc_log_level log_level;
enum sc_record_format record_format;
enum sc_keyboard_input_mode keyboard_input_mode;
struct sc_port_range port_range;
struct sc_shortcut_mods shortcut_mods;
uint16_t max_size;
uint32_t bit_rate;
uint16_t max_fps;
enum sc_lock_video_orientation lock_video_orientation;
uint8_t rotation;
int16_t window_x; // SC_WINDOW_POSITION_UNDEFINED for "auto"
int16_t window_y; // SC_WINDOW_POSITION_UNDEFINED for "auto"
uint16_t window_width;
uint16_t window_height;
uint32_t display_id;
sc_tick display_buffer;
sc_tick v4l2_buffer;
bool show_touches;
bool fullscreen;
bool always_on_top;
bool control;
bool display;
bool turn_screen_off;
bool prefer_text;
bool window_borderless;
bool mipmaps;
bool stay_awake;
bool force_adb_forward;
bool disable_screensaver;
bool forward_key_repeat;
bool forward_all_clicks;
bool legacy_paste;
bool power_off_on_close;
};
extern const struct scrcpy_options scrcpy_options_default;
#endif

View File

@ -7,7 +7,7 @@
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include "coords.h" #include "coords.h"
#include "scrcpy.h" #include "options.h"
#include "trait/packet_sink.h" #include "trait/packet_sink.h"
#include "util/queue.h" #include "util/queue.h"
#include "util/thread.h" #include "util/thread.h"

View File

@ -258,10 +258,6 @@ scrcpy(struct scrcpy_options *options) {
static struct scrcpy scrcpy; static struct scrcpy scrcpy;
struct scrcpy *s = &scrcpy; struct scrcpy *s = &scrcpy;
if (!server_init(&s->server)) {
return false;
}
bool ret = false; bool ret = false;
bool server_started = false; bool server_started = false;
@ -297,7 +293,12 @@ 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;
} }
@ -316,7 +317,7 @@ scrcpy(struct scrcpy_options *options) {
} }
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;
} }

View File

@ -4,158 +4,7 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include "options.h"
#include <stdint.h>
#include "util/tick.h"
enum sc_log_level {
SC_LOG_LEVEL_VERBOSE,
SC_LOG_LEVEL_DEBUG,
SC_LOG_LEVEL_INFO,
SC_LOG_LEVEL_WARN,
SC_LOG_LEVEL_ERROR,
};
enum sc_record_format {
SC_RECORD_FORMAT_AUTO,
SC_RECORD_FORMAT_MP4,
SC_RECORD_FORMAT_MKV,
};
enum sc_lock_video_orientation {
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED = -1,
// lock the current orientation when scrcpy starts
SC_LOCK_VIDEO_ORIENTATION_INITIAL = -2,
SC_LOCK_VIDEO_ORIENTATION_0 = 0,
SC_LOCK_VIDEO_ORIENTATION_1,
SC_LOCK_VIDEO_ORIENTATION_2,
SC_LOCK_VIDEO_ORIENTATION_3,
};
enum sc_keyboard_input_mode {
SC_KEYBOARD_INPUT_MODE_INJECT,
SC_KEYBOARD_INPUT_MODE_HID,
};
#define SC_MAX_SHORTCUT_MODS 8
enum sc_shortcut_mod {
SC_MOD_LCTRL = 1 << 0,
SC_MOD_RCTRL = 1 << 1,
SC_MOD_LALT = 1 << 2,
SC_MOD_RALT = 1 << 3,
SC_MOD_LSUPER = 1 << 4,
SC_MOD_RSUPER = 1 << 5,
};
struct sc_shortcut_mods {
unsigned data[SC_MAX_SHORTCUT_MODS];
unsigned count;
};
struct sc_port_range {
uint16_t first;
uint16_t last;
};
#define SC_WINDOW_POSITION_UNDEFINED (-0x8000)
struct scrcpy_options {
const char *serial;
const char *crop;
const char *record_filename;
const char *window_title;
const char *push_target;
const char *render_driver;
const char *codec_options;
const char *encoder_name;
const char *v4l2_device;
enum sc_log_level log_level;
enum sc_record_format record_format;
enum sc_keyboard_input_mode keyboard_input_mode;
struct sc_port_range port_range;
struct sc_shortcut_mods shortcut_mods;
uint16_t max_size;
uint32_t bit_rate;
uint16_t max_fps;
enum sc_lock_video_orientation lock_video_orientation;
uint8_t rotation;
int16_t window_x; // SC_WINDOW_POSITION_UNDEFINED for "auto"
int16_t window_y; // SC_WINDOW_POSITION_UNDEFINED for "auto"
uint16_t window_width;
uint16_t window_height;
uint32_t display_id;
sc_tick display_buffer;
sc_tick v4l2_buffer;
bool show_touches;
bool fullscreen;
bool always_on_top;
bool control;
bool display;
bool turn_screen_off;
bool prefer_text;
bool window_borderless;
bool mipmaps;
bool stay_awake;
bool force_adb_forward;
bool disable_screensaver;
bool forward_key_repeat;
bool forward_all_clicks;
bool legacy_paste;
bool power_off_on_close;
};
#define SCRCPY_OPTIONS_DEFAULT { \
.serial = NULL, \
.crop = NULL, \
.record_filename = NULL, \
.window_title = NULL, \
.push_target = NULL, \
.render_driver = NULL, \
.codec_options = NULL, \
.encoder_name = NULL, \
.v4l2_device = NULL, \
.log_level = SC_LOG_LEVEL_INFO, \
.record_format = SC_RECORD_FORMAT_AUTO, \
.keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT, \
.port_range = { \
.first = DEFAULT_LOCAL_PORT_RANGE_FIRST, \
.last = DEFAULT_LOCAL_PORT_RANGE_LAST, \
}, \
.shortcut_mods = { \
.data = {SC_MOD_LALT, SC_MOD_LSUPER}, \
.count = 2, \
}, \
.max_size = 0, \
.bit_rate = DEFAULT_BIT_RATE, \
.max_fps = 0, \
.lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_UNLOCKED, \
.rotation = 0, \
.window_x = SC_WINDOW_POSITION_UNDEFINED, \
.window_y = SC_WINDOW_POSITION_UNDEFINED, \
.window_width = 0, \
.window_height = 0, \
.display_id = 0, \
.display_buffer = 0, \
.v4l2_buffer = 0, \
.show_touches = false, \
.fullscreen = false, \
.always_on_top = false, \
.control = true, \
.display = true, \
.turn_screen_off = false, \
.prefer_text = false, \
.window_borderless = false, \
.mipmaps = true, \
.stay_awake = false, \
.force_adb_forward = false, \
.disable_screensaver = false, \
.forward_key_repeat = true, \
.forward_all_clicks = false, \
.legacy_paste = false, \
.power_off_on_close = false, \
}
bool bool
scrcpy(struct scrcpy_options *options); scrcpy(struct scrcpy_options *options);

View File

@ -6,7 +6,7 @@
#include "events.h" #include "events.h"
#include "icon.h" #include "icon.h"
#include "scrcpy.h" #include "options.h"
#include "video_buffer.h" #include "video_buffer.h"
#include "util/log.h" #include "util/log.h"

View File

@ -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
@ -323,30 +367,26 @@ connect_to_server(uint16_t port, uint32_t attempts, uint32_t delay) {
return SC_INVALID_SOCKET; return SC_INVALID_SOCKET;
} }
static void
close_socket(sc_socket socket) {
assert(socket != SC_INVALID_SOCKET);
net_shutdown(socket, SHUT_RDWR);
if (!net_close(socket)) {
LOGW("Could not close socket");
}
}
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);
server->process = PROCESS_NONE;
atomic_flag_clear_explicit(&server->server_socket_closed,
memory_order_relaxed);
bool ok = sc_mutex_init(&server->mutex);
if (!ok) { if (!ok) {
LOGE("Could not copy server params");
return false;
}
server->process = PROCESS_NONE;
ok = sc_mutex_init(&server->mutex);
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,24 +416,18 @@ run_wait_server(void *data) {
// no need for synchronization, server_socket is initialized before this // no need for synchronization, server_socket is initialized before this
// thread was created // thread was created
if (server->server_socket != SC_INVALID_SOCKET if (server->server_socket != SC_INVALID_SOCKET) {
&& !atomic_flag_test_and_set(&server->server_socket_closed)) { // Unblock any accept()
// On Linux, accept() is unblocked by shutdown(), but on Windows, it is net_interrupt(server->server_socket);
// unblocked by closesocket(). Therefore, call both (close_socket()).
close_socket(server->server_socket);
} }
LOGD("Server terminated"); LOGD("Server terminated");
return 0; return 0;
} }
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() */
@ -430,14 +464,8 @@ server_start(struct server *server, const struct server_params *params) {
return true; return true;
error: error:
if (!server->tunnel_forward) { // The server socket (if any) will be closed on server_destroy()
bool was_closed =
atomic_flag_test_and_set(&server->server_socket_closed);
// the thread is not started, the flag could not be already set
assert(!was_closed);
(void) was_closed;
close_socket(server->server_socket);
}
disable_tunnel(server); disable_tunnel(server);
return false; return false;
@ -479,11 +507,11 @@ server_connect_to(struct server *server, char *device_name, struct size *size) {
} }
// we don't need the server socket anymore // we don't need the server socket anymore
if (!atomic_flag_test_and_set(&server->server_socket_closed)) { if (!net_close(server->server_socket)) {
// close it from here LOGW("Could not close server socket on connect");
close_socket(server->server_socket);
// otherwise, it is closed by run_wait_server()
} }
// Do not attempt to close it again on server_destroy()
server->server_socket = SC_INVALID_SOCKET;
} else { } else {
uint32_t attempts = 100; uint32_t attempts = 100;
uint32_t delay = 100; // ms uint32_t delay = 100; // ms
@ -511,15 +539,20 @@ server_connect_to(struct server *server, char *device_name, struct size *size) {
void void
server_stop(struct server *server) { server_stop(struct server *server) {
if (server->server_socket != SC_INVALID_SOCKET if (server->server_socket != SC_INVALID_SOCKET) {
&& !atomic_flag_test_and_set(&server->server_socket_closed)) { if (!net_interrupt(server->server_socket)) {
close_socket(server->server_socket); LOGW("Could not interrupt server socket");
}
} }
if (server->video_socket != SC_INVALID_SOCKET) { if (server->video_socket != SC_INVALID_SOCKET) {
close_socket(server->video_socket); if (!net_interrupt(server->video_socket)) {
LOGW("Could not interrupt video socket");
}
} }
if (server->control_socket != SC_INVALID_SOCKET) { if (server->control_socket != SC_INVALID_SOCKET) {
close_socket(server->control_socket); if (!net_interrupt(server->control_socket)) {
LOGW("Could not interrupt control socket");
}
} }
assert(server->process != PROCESS_NONE); assert(server->process != PROCESS_NONE);
@ -556,7 +589,23 @@ server_stop(struct server *server) {
void void
server_destroy(struct server *server) { server_destroy(struct server *server) {
free(server->serial); if (server->server_socket != SC_INVALID_SOCKET) {
if (!net_close(server->server_socket)) {
LOGW("Could not close server socket");
}
}
if (server->video_socket != SC_INVALID_SOCKET) {
if (!net_close(server->video_socket)) {
LOGW("Could not close video socket");
}
}
if (server->control_socket != SC_INVALID_SOCKET) {
if (!net_close(server->control_socket)) {
LOGW("Could not close control socket");
}
}
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

@ -9,29 +9,11 @@
#include "adb.h" #include "adb.h"
#include "coords.h" #include "coords.h"
#include "scrcpy.h" #include "options.h"
#include "util/log.h" #include "util/log.h"
#include "util/net.h" #include "util/net.h"
#include "util/thread.h" #include "util/thread.h"
struct server {
char *serial;
process_t process;
sc_thread wait_server_thread;
atomic_flag server_socket_closed;
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;
@ -51,13 +33,32 @@ 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 #define DEVICE_NAME_FIELD_LENGTH 64
// block until the communication with the server is established // block until the communication with the server is established

View File

@ -5,7 +5,7 @@
#include <SDL2/SDL_log.h> #include <SDL2/SDL_log.h>
#include "scrcpy.h" #include "options.h"
#define LOGV(...) SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__) #define LOGV(...) SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
#define LOGD(...) SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__) #define LOGD(...) SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)

View File

@ -1,5 +1,6 @@
#include "net.h" #include "net.h"
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <SDL2/SDL_platform.h> #include <SDL2/SDL_platform.h>
@ -55,6 +56,7 @@ wrap(sc_raw_socket sock) {
} }
socket->socket = sock; socket->socket = sock;
socket->closed = (atomic_flag) ATOMIC_FLAG_INIT;
return socket; return socket;
#else #else
@ -195,18 +197,33 @@ net_send_all(sc_socket socket, const void *buf, size_t len) {
} }
bool bool
net_shutdown(sc_socket socket, int how) { net_interrupt(sc_socket socket) {
assert(socket != SC_INVALID_SOCKET);
sc_raw_socket raw_sock = unwrap(socket); sc_raw_socket raw_sock = unwrap(socket);
return !shutdown(raw_sock, how);
#ifdef __WINDOWS__
if (!atomic_flag_test_and_set(&socket->closed)) {
return !closesocket(raw_sock);
}
return true;
#else
return !shutdown(raw_sock, SHUT_RDWR);
#endif
} }
#include <errno.h>
bool bool
net_close(sc_socket socket) { net_close(sc_socket socket) {
sc_raw_socket raw_sock = unwrap(socket); sc_raw_socket raw_sock = unwrap(socket);
#ifdef __WINDOWS__ #ifdef __WINDOWS__
bool ret = true;
if (!atomic_flag_test_and_set(&socket->closed)) {
ret = !closesocket(raw_sock);
}
free(socket); free(socket);
return !closesocket(raw_sock); return ret;
#else #else
return !close(raw_sock); return !close(raw_sock);
#endif #endif

View File

@ -10,12 +10,11 @@
#ifdef __WINDOWS__ #ifdef __WINDOWS__
# include <winsock2.h> # include <winsock2.h>
# define SHUT_RD SD_RECEIVE # include <stdatomic.h>
# define SHUT_WR SD_SEND
# define SHUT_RDWR SD_BOTH
# define SC_INVALID_SOCKET NULL # define SC_INVALID_SOCKET NULL
typedef struct sc_socket_windows { typedef struct sc_socket_windows {
SOCKET socket; SOCKET socket;
atomic_flag closed;
} *sc_socket; } *sc_socket;
#else // not __WINDOWS__ #else // not __WINDOWS__
@ -53,10 +52,13 @@ net_send(sc_socket socket, const void *buf, size_t len);
ssize_t ssize_t
net_send_all(sc_socket socket, const void *buf, size_t len); net_send_all(sc_socket socket, const void *buf, size_t len);
// how is SHUT_RD (read), SHUT_WR (write) or SHUT_RDWR (both) // Shutdown the socket (or close on Windows) so that any blocking send() or
// recv() are interrupted.
bool bool
net_shutdown(sc_socket socket, int how); net_interrupt(sc_socket socket);
// Close the socket.
// A socket must always be closed, even if net_interrupt() has been called.
bool bool
net_close(sc_socket socket); net_close(sc_socket socket);

View File

@ -4,11 +4,11 @@
#include <string.h> #include <string.h>
#include "cli.h" #include "cli.h"
#include "scrcpy.h" #include "options.h"
static void test_flag_version(void) { static void test_flag_version(void) {
struct scrcpy_cli_args args = { struct scrcpy_cli_args args = {
.opts = SCRCPY_OPTIONS_DEFAULT, .opts = scrcpy_options_default,
.help = false, .help = false,
.version = false, .version = false,
}; };
@ -23,7 +23,7 @@ static void test_flag_version(void) {
static void test_flag_help(void) { static void test_flag_help(void) {
struct scrcpy_cli_args args = { struct scrcpy_cli_args args = {
.opts = SCRCPY_OPTIONS_DEFAULT, .opts = scrcpy_options_default,
.help = false, .help = false,
.version = false, .version = false,
}; };
@ -38,7 +38,7 @@ static void test_flag_help(void) {
static void test_options(void) { static void test_options(void) {
struct scrcpy_cli_args args = { struct scrcpy_cli_args args = {
.opts = SCRCPY_OPTIONS_DEFAULT, .opts = scrcpy_options_default,
.help = false, .help = false,
.version = false, .version = false,
}; };
@ -100,7 +100,7 @@ static void test_options(void) {
static void test_options2(void) { static void test_options2(void) {
struct scrcpy_cli_args args = { struct scrcpy_cli_args args = {
.opts = SCRCPY_OPTIONS_DEFAULT, .opts = scrcpy_options_default,
.help = false, .help = false,
.version = false, .version = false,
}; };