Compare commits

..

14 Commits

Author SHA1 Message Date
1ad679714b wip 2021-10-31 22:29:03 +01:00
612d9722a2 server_thread 2021-10-31 22:29:03 +01:00
1f8200e295 server_cbs 2021-10-31 22:29:03 +01:00
4ae7d1e9a9 server_info 2021-10-31 22:29:03 +01:00
49357566cb stopped_cond 2021-10-31 22:29:03 +01:00
c5d962b6fd Copy server params
This is a preliminary step necessary to move the server to a separate
thread.
2021-10-31 22:29:03 +01:00
54272a931a Reorder server and server_params
This will allow to define a server_params field in server.
2021-10-31 22:29:03 +01:00
59d1b0a269 Split socket creation and connect/listen
This will allow to assign the socket to a variable before connecting or
listening, so that it can be interrupted from another thread.
2021-10-31 22:28:07 +01:00
58ea238fb2 Remove unnecessary variable
Test directly if a record filename is provided, without an intermediate
boolean variable.
2021-10-31 12:45:59 +01:00
13c4aa1a3b 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.
2021-10-31 12:45:59 +01:00
caf594c90e Split SDL initialization
Initialize SDL_INIT_EVENTS first (very quick) and SDL_INIT_VIDEO after
the server (quite long).
2021-10-31 12:45:59 +01:00
688477ff65 Move SDL initialization
Inline SDL initialization in scrcpy(). This will allow to split
minimal initialization and video initialization.
2021-10-31 12:45:59 +01:00
ac539e1312 Set SDL hints before initialization
In theory, some SDL hints should be initialized before calling
SDL_Init().
2021-10-31 12:45:59 +01:00
a57c7d3a2b Extract SDL hints
Set all SDL hints in a separate function.
2021-10-31 12:45:56 +01:00
7 changed files with 133 additions and 77 deletions

View File

@ -43,6 +43,11 @@
# 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

@ -2,4 +2,3 @@
#define EVENT_STREAM_STOPPED (SDL_USEREVENT + 1) #define EVENT_STREAM_STOPPED (SDL_USEREVENT + 1)
#define EVENT_SERVER_CONNECTION_FAILED (SDL_USEREVENT + 2) #define EVENT_SERVER_CONNECTION_FAILED (SDL_USEREVENT + 2)
#define EVENT_SERVER_CONNECTED (SDL_USEREVENT + 3) #define EVENT_SERVER_CONNECTED (SDL_USEREVENT + 3)
#define EVENT_SERVER_DISCONNECTED (SDL_USEREVENT + 4)

View File

@ -79,29 +79,8 @@ BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
} }
#endif // _WIN32 #endif // _WIN32
// init SDL and set appropriate hints static void
static bool sdl_set_hints(const char *render_driver) {
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");
@ -119,6 +98,15 @@ sdl_init_and_configure(bool display, 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")) {
@ -130,6 +118,21 @@ sdl_init_and_configure(bool display, 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");
@ -138,8 +141,6 @@ sdl_init_and_configure(bool display, const char *render_driver,
LOGD("Screensaver enabled"); LOGD("Screensaver enabled");
SDL_EnableScreenSaver(); SDL_EnableScreenSaver();
} }
return true;
} }
static bool static bool
@ -158,10 +159,6 @@ static enum event_result
handle_event(struct scrcpy *s, const struct scrcpy_options *options, handle_event(struct scrcpy *s, const struct scrcpy_options *options,
SDL_Event *event) { SDL_Event *event) {
switch (event->type) { switch (event->type) {
case EVENT_SERVER_DISCONNECTED:
LOGD("Server disconnected");
// Do nothing, will be managed by the "stream stopped" event
break;
case EVENT_STREAM_STOPPED: case EVENT_STREAM_STOPPED:
LOGD("Video stream stopped"); LOGD("Video stream stopped");
return EVENT_RESULT_STOPPED_BY_EOS; return EVENT_RESULT_STOPPED_BY_EOS;
@ -224,9 +221,6 @@ static bool
await_for_server(void) { await_for_server(void) {
SDL_Event event; SDL_Event event;
while (SDL_WaitEvent(&event)) { while (SDL_WaitEvent(&event)) {
// Should never receive disconnected event before connected
assert(event.type != EVENT_SERVER_DISCONNECTED);
switch (event.type) { switch (event.type) {
case SDL_QUIT: case SDL_QUIT:
LOGD("User requested to quit"); LOGD("User requested to quit");
@ -312,7 +306,8 @@ server_on_disconnected(struct server *server, void *userdata) {
(void) server; (void) server;
(void) userdata; (void) userdata;
PUSH_EVENT(EVENT_SERVER_DISCONNECTED); LOGD("Server disconnected");
// Do nothing, will be managed by the "stream stopped" event
} }
bool bool
@ -320,6 +315,14 @@ 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);
bool ret = false; bool ret = false;
bool server_started = false; bool server_started = false;
@ -336,7 +339,6 @@ 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,
@ -365,18 +367,24 @@ scrcpy(struct scrcpy_options *options) {
return false; return false;
} }
// TODO SDL_Init(SDL_INIT_EVENTS) before starting server
if (!server_start(&s->server)) { if (!server_start(&s->server)) {
goto end; goto end;
} }
server_started = true; server_started = true;
if (!sdl_init_and_configure(options->display, options->render_driver, if (options->display) {
options->disable_screensaver)) { sdl_set_hints(options->render_driver);
}
// 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);
// Await for server without blocking Ctrl+C handling // Await for server without blocking Ctrl+C handling
if (!await_for_server()) { if (!await_for_server()) {
goto end; goto end;
@ -403,7 +411,7 @@ scrcpy(struct scrcpy_options *options) {
} }
struct recorder *rec = NULL; struct recorder *rec = NULL;
if (record) { if (options->record_filename) {
if (!recorder_init(&s->recorder, if (!recorder_init(&s->recorder,
options->record_filename, options->record_filename,
options->record_format, options->record_format,

View File

@ -148,10 +148,10 @@ disable_tunnel(struct server *server) {
return disable_tunnel_reverse(serial); return disable_tunnel_reverse(serial);
} }
static sc_socket static bool
listen_on_port(uint16_t port) { listen_on_port(sc_socket socket, uint16_t port) {
#define IPV4_LOCALHOST 0x7F000001 #define IPV4_LOCALHOST 0x7F000001
return net_listen(IPV4_LOCALHOST, port, 1); return net_listen(socket, IPV4_LOCALHOST, port, 1);
} }
static bool static bool
@ -171,11 +171,17 @@ enable_tunnel_reverse_any_port(struct server *server,
// client can listen before starting the server app, so there is no // client can listen before starting the server app, so there is no
// need to try to connect until the server socket is listening on the // need to try to connect until the server socket is listening on the
// device. // device.
server->server_socket = listen_on_port(port); sc_socket server_socket = net_socket();
if (server->server_socket != SC_INVALID_SOCKET) { if (server_socket != SC_INVALID_SOCKET) {
// success bool ok = listen_on_port(server_socket, port);
server->local_port = port; if (ok) {
return true; // success
server->server_socket = server_socket;
server->local_port = port;
return true;
}
net_close(server_socket);
} }
// failure, disable tunnel and try another port // failure, disable tunnel and try another port
@ -335,11 +341,17 @@ execute_server(struct server *server, const struct server_params *params) {
static sc_socket static sc_socket
connect_and_read_byte(uint16_t port) { connect_and_read_byte(uint16_t port) {
sc_socket socket = net_connect(IPV4_LOCALHOST, port); sc_socket socket = net_socket();
if (socket == SC_INVALID_SOCKET) { if (socket == SC_INVALID_SOCKET) {
return SC_INVALID_SOCKET; return SC_INVALID_SOCKET;
} }
bool ok = net_connect(socket, IPV4_LOCALHOST, port);
if (!ok) {
net_close(socket);
return SC_INVALID_SOCKET;
}
char byte; char byte;
// the connection may succeed even if the server behind the "adb tunnel" // the connection may succeed even if the server behind the "adb tunnel"
// is not listening, so read one byte to detect a working connection // is not listening, so read one byte to detect a working connection
@ -352,7 +364,8 @@ connect_and_read_byte(uint16_t port) {
} }
static sc_socket static sc_socket
connect_to_server(uint16_t port, uint32_t attempts, uint32_t delay) { connect_to_server(struct server *server, uint32_t attempts, sc_tick delay) {
uint16_t port = server->local_port;
do { do {
LOGD("Remaining connection attempts: %d", (int) attempts); LOGD("Remaining connection attempts: %d", (int) attempts);
sc_socket socket = connect_and_read_byte(port); sc_socket socket = connect_and_read_byte(port);
@ -360,9 +373,17 @@ connect_to_server(uint16_t port, uint32_t attempts, uint32_t delay) {
// it worked! // it worked!
return socket; return socket;
} }
// TODO use mutex + condvar + bool stopped
if (attempts) { if (attempts) {
SDL_Delay(delay); sc_mutex_lock(&server->mutex);
// Ignore timedwait return (spurious wake ups are harmless)
sc_cond_timedwait(&server->stopped_cond, &server->mutex,
sc_tick_now() + delay);
bool stopped = server->stopped;
sc_mutex_unlock(&server->mutex);
if (stopped) {
LOGI("Connection attempt stopped");
break;
}
} }
} while (--attempts > 0); } while (--attempts > 0);
return SC_INVALID_SOCKET; return SC_INVALID_SOCKET;
@ -392,7 +413,16 @@ server_init(struct server *server, const struct server_params *params,
return false; return false;
} }
ok = sc_cond_init(&server->stopped_cond);
if (!ok) {
sc_cond_destroy(&server->process_terminated_cond);
sc_mutex_destroy(&server->mutex);
server_params_destroy(&server->params);
return false;
}
server->process_terminated = false; server->process_terminated = false;
server->stopped = false;
server->connected = false; server->connected = false;
server->server_socket = SC_INVALID_SOCKET; server->server_socket = SC_INVALID_SOCKET;
@ -539,19 +569,23 @@ server_connect_to(struct server *server, struct server_info *info) {
server->server_socket = SC_INVALID_SOCKET; server->server_socket = SC_INVALID_SOCKET;
} else { } else {
uint32_t attempts = 100; uint32_t attempts = 100;
uint32_t delay = 100; // ms sc_tick delay = SC_TICK_FROM_MS(100);
server->video_socket = server->video_socket = connect_to_server(server, attempts, delay);
connect_to_server(server->local_port, attempts, delay);
if (server->video_socket == SC_INVALID_SOCKET) { if (server->video_socket == SC_INVALID_SOCKET) {
return false; return false;
} }
// we know that the device is listening, we don't need several attempts // we know that the device is listening, we don't need several attempts
server->control_socket = server->control_socket = net_socket();
net_connect(IPV4_LOCALHOST, server->local_port);
if (server->control_socket == SC_INVALID_SOCKET) { if (server->control_socket == SC_INVALID_SOCKET) {
return false; return false;
} }
bool ok = net_connect(server->control_socket, IPV4_LOCALHOST,
server->local_port);
if (!ok) {
net_close(server->control_socket);
return false;
}
} }
// we don't need the adb tunnel anymore // we don't need the adb tunnel anymore
@ -564,6 +598,11 @@ server_connect_to(struct server *server, struct server_info *info) {
void void
server_stop(struct server *server) { server_stop(struct server *server) {
sc_mutex_lock(&server->mutex);
server->stopped = true;
sc_cond_signal(&server->stopped_cond);
sc_mutex_unlock(&server->mutex);
if (server->server_socket != SC_INVALID_SOCKET) { if (server->server_socket != SC_INVALID_SOCKET) {
if (!net_interrupt(server->server_socket)) { if (!net_interrupt(server->server_socket)) {
LOGW("Could not interrupt server socket"); LOGW("Could not interrupt server socket");
@ -631,6 +670,7 @@ server_destroy(struct server *server) {
} }
server_params_destroy(&server->params); server_params_destroy(&server->params);
sc_cond_destroy(&server->stopped_cond);
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

@ -47,9 +47,13 @@ struct server {
sc_thread thread; sc_thread thread;
sc_mutex mutex; sc_mutex mutex;
sc_cond process_terminated_cond; sc_cond process_terminated_cond;
bool process_terminated; bool process_terminated;
sc_cond stopped_cond;
bool stopped;
bool connected; // written by connect_thread bool connected; // written by connect_thread
struct server_info info; // initialized once connected struct server_info info; // initialized once connected

View File

@ -94,13 +94,18 @@ net_perror(const char *s) {
} }
sc_socket sc_socket
net_connect(uint32_t addr, uint16_t port) { net_socket(void) {
sc_raw_socket raw_sock = socket(AF_INET, SOCK_STREAM, 0); sc_raw_socket raw_sock = socket(AF_INET, SOCK_STREAM, 0);
sc_socket sock = wrap(raw_sock); sc_socket sock = wrap(raw_sock);
if (sock == SC_INVALID_SOCKET) { if (sock == SC_INVALID_SOCKET) {
net_perror("socket"); net_perror("socket");
return SC_INVALID_SOCKET;
} }
return sock;
}
bool
net_connect(sc_socket socket, uint32_t addr, uint16_t port) {
sc_raw_socket raw_sock = unwrap(socket);
SOCKADDR_IN sin; SOCKADDR_IN sin;
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
@ -109,21 +114,15 @@ net_connect(uint32_t addr, uint16_t port) {
if (connect(raw_sock, (SOCKADDR *) &sin, sizeof(sin)) == SOCKET_ERROR) { if (connect(raw_sock, (SOCKADDR *) &sin, sizeof(sin)) == SOCKET_ERROR) {
net_perror("connect"); net_perror("connect");
net_close(sock); return false;
return SC_INVALID_SOCKET;
} }
return sock; return true;
} }
sc_socket bool
net_listen(uint32_t addr, uint16_t port, int backlog) { net_listen(sc_socket socket, uint32_t addr, uint16_t port, int backlog) {
sc_raw_socket raw_sock = socket(AF_INET, SOCK_STREAM, 0); sc_raw_socket raw_sock = unwrap(socket);
sc_socket sock = wrap(raw_sock);
if (sock == SC_INVALID_SOCKET) {
net_perror("socket");
return SC_INVALID_SOCKET;
}
int reuse = 1; int reuse = 1;
if (setsockopt(raw_sock, SOL_SOCKET, SO_REUSEADDR, (const void *) &reuse, if (setsockopt(raw_sock, SOL_SOCKET, SO_REUSEADDR, (const void *) &reuse,
@ -138,17 +137,15 @@ net_listen(uint32_t addr, uint16_t port, int backlog) {
if (bind(raw_sock, (SOCKADDR *) &sin, sizeof(sin)) == SOCKET_ERROR) { if (bind(raw_sock, (SOCKADDR *) &sin, sizeof(sin)) == SOCKET_ERROR) {
net_perror("bind"); net_perror("bind");
net_close(sock); return false;
return SC_INVALID_SOCKET;
} }
if (listen(raw_sock, backlog) == SOCKET_ERROR) { if (listen(raw_sock, backlog) == SOCKET_ERROR) {
net_perror("listen"); net_perror("listen");
net_close(sock); return false;
return SC_INVALID_SOCKET;
} }
return sock; return true;
} }
sc_socket sc_socket

View File

@ -31,10 +31,13 @@ void
net_cleanup(void); net_cleanup(void);
sc_socket sc_socket
net_connect(uint32_t addr, uint16_t port); net_socket(void);
sc_socket bool
net_listen(uint32_t addr, uint16_t port, int backlog); net_connect(sc_socket socket, uint32_t addr, uint16_t port);
bool
net_listen(sc_socket socket, uint32_t addr, uint16_t port, int backlog);
sc_socket sc_socket
net_accept(sc_socket server_socket); net_accept(sc_socket server_socket);