Compare commits

..

5 Commits

Author SHA1 Message Date
Romain Vimont
73d098872c Do not warn on terminating the server
If the server is already dead, terminating it fails. This is expected.
2020-03-29 11:24:11 +02:00
Romain Vimont
943d264b35 Do not block on accept() if server died
The server may die before connecting to the client. In that case, the
client was blocked indefinitely (until Ctrl+C) on accept().

To avoid the problem, use a pipe and a select() before calling accept(),
so that the blocking call can be interrupted. Once accept() is called,
it is guaranteed not to block.
2020-03-29 11:24:11 +02:00
Romain Vimont
32dc192883 Wait server from a separate thread
Create a thread just to wait for the server process exit.

This paves the way to simply wake up a blocking accept() in a portable
way.
2020-03-28 23:56:37 +01:00
Romain Vimont
7732f1097b Refactor server_start() error handling
This will avoid more cleanup duplication.
2020-03-28 23:56:37 +01:00
e_vigurskiy
429153abfb Add display id parameter
Add --display command line parameter to specify a display id.
2020-03-28 23:56:37 +01:00
18 changed files with 183 additions and 172 deletions

1
.gitignore vendored
View File

@@ -2,4 +2,3 @@ build/
/dist/ /dist/
.idea/ .idea/
.gradle/ .gradle/
/x/

View File

@@ -23,7 +23,7 @@ fps_counter_init(struct fps_counter *counter) {
} }
counter->thread = NULL; counter->thread = NULL;
atomic_init(&counter->started, 0); SDL_AtomicSet(&counter->started, 0);
// no need to initialize the other fields, they are unused until started // no need to initialize the other fields, they are unused until started
return true; return true;
@@ -35,16 +35,6 @@ fps_counter_destroy(struct fps_counter *counter) {
SDL_DestroyMutex(counter->mutex); SDL_DestroyMutex(counter->mutex);
} }
static inline bool
is_started(struct fps_counter *counter) {
return atomic_load_explicit(&counter->started, memory_order_acquire);
}
static inline void
set_started(struct fps_counter *counter, bool started) {
atomic_store_explicit(&counter->started, started, memory_order_release);
}
// must be called with mutex locked // must be called with mutex locked
static void static void
display_fps(struct fps_counter *counter) { display_fps(struct fps_counter *counter) {
@@ -80,10 +70,10 @@ run_fps_counter(void *data) {
mutex_lock(counter->mutex); mutex_lock(counter->mutex);
while (!counter->interrupted) { while (!counter->interrupted) {
while (!counter->interrupted && !is_started(counter)) { while (!counter->interrupted && !SDL_AtomicGet(&counter->started)) {
cond_wait(counter->state_cond, counter->mutex); cond_wait(counter->state_cond, counter->mutex);
} }
while (!counter->interrupted && is_started(counter)) { while (!counter->interrupted && SDL_AtomicGet(&counter->started)) {
uint32_t now = SDL_GetTicks(); uint32_t now = SDL_GetTicks();
check_interval_expired(counter, now); check_interval_expired(counter, now);
@@ -106,7 +96,7 @@ fps_counter_start(struct fps_counter *counter) {
counter->nr_skipped = 0; counter->nr_skipped = 0;
mutex_unlock(counter->mutex); mutex_unlock(counter->mutex);
set_started(counter, true); SDL_AtomicSet(&counter->started, 1);
cond_signal(counter->state_cond); cond_signal(counter->state_cond);
// counter->thread is always accessed from the same thread, no need to lock // counter->thread is always accessed from the same thread, no need to lock
@@ -124,13 +114,13 @@ fps_counter_start(struct fps_counter *counter) {
void void
fps_counter_stop(struct fps_counter *counter) { fps_counter_stop(struct fps_counter *counter) {
set_started(counter, false); SDL_AtomicSet(&counter->started, 0);
cond_signal(counter->state_cond); cond_signal(counter->state_cond);
} }
bool bool
fps_counter_is_started(struct fps_counter *counter) { fps_counter_is_started(struct fps_counter *counter) {
return is_started(counter); return SDL_AtomicGet(&counter->started);
} }
void void
@@ -155,7 +145,7 @@ fps_counter_join(struct fps_counter *counter) {
void void
fps_counter_add_rendered_frame(struct fps_counter *counter) { fps_counter_add_rendered_frame(struct fps_counter *counter) {
if (!is_started(counter)) { if (!SDL_AtomicGet(&counter->started)) {
return; return;
} }
@@ -168,7 +158,7 @@ fps_counter_add_rendered_frame(struct fps_counter *counter) {
void void
fps_counter_add_skipped_frame(struct fps_counter *counter) { fps_counter_add_skipped_frame(struct fps_counter *counter) {
if (!is_started(counter)) { if (!SDL_AtomicGet(&counter->started)) {
return; return;
} }

View File

@@ -1,9 +1,9 @@
#ifndef FPSCOUNTER_H #ifndef FPSCOUNTER_H
#define FPSCOUNTER_H #define FPSCOUNTER_H
#include <stdatomic.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <SDL2/SDL_atomic.h>
#include <SDL2/SDL_mutex.h> #include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h> #include <SDL2/SDL_thread.h>
@@ -16,7 +16,7 @@ struct fps_counter {
// atomic so that we can check without locking the mutex // atomic so that we can check without locking the mutex
// if the FPS counter is disabled, we don't want to lock unnecessarily // if the FPS counter is disabled, we don't want to lock unnecessarily
atomic_bool started; SDL_atomic_t started;
// the following fields are protected by the mutex // the following fields are protected by the mutex
bool interrupted; bool interrupted;

View File

@@ -7,6 +7,33 @@
#include "util/lock.h" #include "util/lock.h"
#include "util/log.h" #include "util/log.h"
// Convert window coordinates (as provided by SDL_GetMouseState() to renderer
// coordinates (as provided in SDL mouse events)
//
// See my question:
// <https://stackoverflow.com/questions/49111054/how-to-get-mouse-position-on-mouse-wheel-event>
static void
convert_to_renderer_coordinates(SDL_Renderer *renderer, int *x, int *y) {
SDL_Rect viewport;
float scale_x, scale_y;
SDL_RenderGetViewport(renderer, &viewport);
SDL_RenderGetScale(renderer, &scale_x, &scale_y);
*x = (int) (*x / scale_x) - viewport.x;
*y = (int) (*y / scale_y) - viewport.y;
}
static struct point
get_mouse_point(struct screen *screen) {
int x;
int y;
SDL_GetMouseState(&x, &y);
convert_to_renderer_coordinates(screen->renderer, &x, &y);
return (struct point) {
.x = x,
.y = y,
};
}
static const int ACTION_DOWN = 1; static const int ACTION_DOWN = 1;
static const int ACTION_UP = 1 << 1; static const int ACTION_UP = 1 << 1;
@@ -400,8 +427,8 @@ convert_mouse_motion(const SDL_MouseMotionEvent *from, struct screen *screen,
to->inject_touch_event.action = AMOTION_EVENT_ACTION_MOVE; to->inject_touch_event.action = AMOTION_EVENT_ACTION_MOVE;
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE; to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
to->inject_touch_event.position.screen_size = screen->frame_size; to->inject_touch_event.position.screen_size = screen->frame_size;
to->inject_touch_event.position.point = to->inject_touch_event.position.point.x = from->x;
screen_convert_to_frame_coords(screen, from->x, from->y); to->inject_touch_event.position.point.y = from->y;
to->inject_touch_event.pressure = 1.f; to->inject_touch_event.pressure = 1.f;
to->inject_touch_event.buttons = convert_mouse_buttons(from->state); to->inject_touch_event.buttons = convert_mouse_buttons(from->state);
@@ -437,14 +464,12 @@ convert_touch(const SDL_TouchFingerEvent *from, struct screen *screen,
} }
struct size frame_size = screen->frame_size; struct size frame_size = screen->frame_size;
// SDL touch event coordinates are normalized in the range [0; 1]
float x = from->x * frame_size.width;
float y = from->y * frame_size.height;
to->inject_touch_event.pointer_id = from->fingerId; to->inject_touch_event.pointer_id = from->fingerId;
to->inject_touch_event.position.screen_size = frame_size; to->inject_touch_event.position.screen_size = frame_size;
to->inject_touch_event.position.point = // SDL touch event coordinates are normalized in the range [0; 1]
screen_convert_to_frame_coords(screen, x, y); to->inject_touch_event.position.point.x = from->x * frame_size.width;
to->inject_touch_event.position.point.y = from->y * frame_size.height;
to->inject_touch_event.pressure = from->pressure; to->inject_touch_event.pressure = from->pressure;
to->inject_touch_event.buttons = 0; to->inject_touch_event.buttons = 0;
return true; return true;
@@ -479,8 +504,8 @@ convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen,
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE; to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
to->inject_touch_event.position.screen_size = screen->frame_size; to->inject_touch_event.position.screen_size = screen->frame_size;
to->inject_touch_event.position.point = to->inject_touch_event.position.point.x = from->x;
screen_convert_to_frame_coords(screen, from->x, from->y); to->inject_touch_event.position.point.y = from->y;
to->inject_touch_event.pressure = 1.f; to->inject_touch_event.pressure = 1.f;
to->inject_touch_event.buttons = to->inject_touch_event.buttons =
convert_mouse_buttons(SDL_BUTTON(from->button)); convert_mouse_buttons(SDL_BUTTON(from->button));
@@ -532,15 +557,9 @@ input_manager_process_mouse_button(struct input_manager *im,
static bool static bool
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen, convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen,
struct control_msg *to) { struct control_msg *to) {
// mouse_x and mouse_y are expressed in pixels relatice to the window
int mouse_x;
int mouse_y;
SDL_GetMouseState(&mouse_x, &mouse_y);
struct position position = { struct position position = {
.screen_size = screen->frame_size, .screen_size = screen->frame_size,
.point = screen_convert_to_frame_coords(screen, mouse_x, mouse_y), .point = get_mouse_point(screen),
}; };
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT; to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;

View File

@@ -149,32 +149,6 @@ get_initial_optimal_size(struct size frame_size, uint16_t req_width,
return window_size; return window_size;
} }
static void
update_frame_rect(struct screen *screen) {
int ww;
int wh;
SDL_GL_GetDrawableSize(screen->window, &ww, &wh);
// 32 bits because we need to multiply two 16 bits values
uint32_t fw = screen->frame_size.width;
uint32_t fh = screen->frame_size.height;
SDL_Rect *rect = &screen->rect;
bool keep_width = fw * wh > fh * ww;
if (keep_width) {
rect->x = 0;
rect->w = ww;
rect->h = ww * fh / fw;
rect->y = (wh - rect->h) / 2;
} else {
rect->y = 0;
rect->h = wh;
rect->w = wh * fw / fh;
rect->x = (ww - rect->w) / 2;
}
}
void void
screen_init(struct screen *screen) { screen_init(struct screen *screen) {
*screen = (struct screen) SCREEN_INITIALIZER; *screen = (struct screen) SCREEN_INITIALIZER;
@@ -232,6 +206,13 @@ screen_init_rendering(struct screen *screen, const char *window_title,
return false; return false;
} }
if (SDL_RenderSetLogicalSize(screen->renderer, frame_size.width,
frame_size.height)) {
LOGE("Could not set renderer logical size: %s", SDL_GetError());
screen_destroy(screen);
return false;
}
SDL_Surface *icon = read_xpm(icon_xpm); SDL_Surface *icon = read_xpm(icon_xpm);
if (icon) { if (icon) {
SDL_SetWindowIcon(screen->window, icon); SDL_SetWindowIcon(screen->window, icon);
@@ -277,6 +258,12 @@ static bool
prepare_for_frame(struct screen *screen, struct 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) {
if (SDL_RenderSetLogicalSize(screen->renderer, new_frame_size.width,
new_frame_size.height)) {
LOGE("Could not set renderer logical size: %s", SDL_GetError());
return false;
}
// frame dimension changed, destroy texture // frame dimension changed, destroy texture
SDL_DestroyTexture(screen->texture); SDL_DestroyTexture(screen->texture);
@@ -322,7 +309,6 @@ screen_update_frame(struct screen *screen, struct video_buffer *vb) {
mutex_unlock(vb->mutex); mutex_unlock(vb->mutex);
return false; return false;
} }
update_frame_rect(screen);
update_texture(screen, frame); update_texture(screen, frame);
mutex_unlock(vb->mutex); mutex_unlock(vb->mutex);
@@ -330,16 +316,10 @@ screen_update_frame(struct screen *screen, struct video_buffer *vb) {
return true; return true;
} }
void
screen_window_resized(struct screen *screen) {
update_frame_rect(screen);
screen_render(screen);
}
void void
screen_render(struct screen *screen) { screen_render(struct screen *screen) {
SDL_RenderClear(screen->renderer); SDL_RenderClear(screen->renderer);
SDL_RenderCopy(screen->renderer, screen->texture, NULL, &screen->rect); SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL);
SDL_RenderPresent(screen->renderer); SDL_RenderPresent(screen->renderer);
} }
@@ -411,7 +391,7 @@ screen_handle_window_event(struct screen *screen,
// window is maximized or fullscreen is enabled. // window is maximized or fullscreen is enabled.
screen->windowed_window_size = get_window_size(screen->window); screen->windowed_window_size = get_window_size(screen->window);
} }
screen_window_resized(screen); screen_render(screen);
break; break;
case SDL_WINDOWEVENT_MAXIMIZED: case SDL_WINDOWEVENT_MAXIMIZED:
// The backup size must be non-nul. // The backup size must be non-nul.
@@ -432,11 +412,3 @@ screen_handle_window_event(struct screen *screen,
break; break;
} }
} }
struct point
screen_convert_to_frame_coords(struct screen *screen, float x, float y) {
struct point out;
out.x = (x - screen->rect.x) * screen->frame_size.width / screen->rect.w;
out.y = (y - screen->rect.y) * screen->frame_size.height / screen->rect.h;
return out;
}

View File

@@ -22,7 +22,6 @@ struct screen {
// Since we receive the event SIZE_CHANGED before MAXIMIZED, we must be // Since we receive the event SIZE_CHANGED before MAXIMIZED, we must be
// able to revert the size to its non-maximized value. // able to revert the size to its non-maximized value.
struct size windowed_window_size_backup; struct size windowed_window_size_backup;
struct SDL_Rect rect;
bool has_frame; bool has_frame;
bool fullscreen; bool fullscreen;
bool maximized; bool maximized;
@@ -45,12 +44,6 @@ struct screen {
.width = 0, \ .width = 0, \
.height = 0, \ .height = 0, \
}, \ }, \
.rect = { \
.x = 0, \
.y = 0, \
.w = 0, \
.h = 0, \
}, \
.has_frame = false, \ .has_frame = false, \
.fullscreen = false, \ .fullscreen = false, \
.maximized = false, \ .maximized = false, \
@@ -81,10 +74,6 @@ screen_destroy(struct screen *screen);
bool bool
screen_update_frame(struct screen *screen, struct video_buffer *vb); screen_update_frame(struct screen *screen, struct video_buffer *vb);
// update content after window resizing
void
screen_window_resized(struct screen *screen);
// render the texture to the renderer // render the texture to the renderer
void void
screen_render(struct screen *screen); screen_render(struct screen *screen);
@@ -105,9 +94,4 @@ screen_resize_to_pixel_perfect(struct screen *screen);
void void
screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event); screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event);
// convert point from window coordinates to frame coordinates
// x and y are expressed in pixels (float to allow partial pixels)
struct point
screen_convert_to_frame_coords(struct screen *screen, float x, float y);
#endif #endif

View File

@@ -5,6 +5,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <libgen.h> #include <libgen.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include <SDL2/SDL_thread.h> #include <SDL2/SDL_thread.h>
#include <SDL2/SDL_timer.h> #include <SDL2/SDL_timer.h>
#include <SDL2/SDL_platform.h> #include <SDL2/SDL_platform.h>
@@ -318,12 +319,14 @@ connect_to_server(uint16_t port, uint32_t attempts, uint32_t delay) {
} }
static void static void
close_socket(socket_t socket) { close_socket(socket_t *socket) {
assert(socket != INVALID_SOCKET); assert(*socket != INVALID_SOCKET);
net_shutdown(socket, SHUT_RDWR); net_shutdown(*socket, SHUT_RDWR);
if (!net_close(socket)) { if (!net_close(*socket)) {
LOGW("Could not close socket"); LOGW("Could not close socket");
return;
} }
*socket = INVALID_SOCKET;
} }
void void
@@ -335,14 +338,10 @@ static int
run_wait_server(void *data) { run_wait_server(void *data) {
struct server *server = data; struct server *server = data;
cmd_simple_wait(server->process, NULL); // ignore exit code cmd_simple_wait(server->process, NULL); // ignore exit code
// no need for synchronization, server_socket is initialized before this
// thread was created // wake up any net_select_interruptible()
if (server->server_socket != INVALID_SOCKET close(server->pipe_intr[1]);
&& !atomic_flag_test_and_set(&server->server_socket_closed)) {
// On Linux, accept() is unblocked by shutdown(), but on Windows, it is
// unblocked by closesocket(). Therefore, call both (close_socket()).
close_socket(server->server_socket);
}
LOGD("Server terminated"); LOGD("Server terminated");
return 0; return 0;
} }
@@ -367,38 +366,36 @@ server_start(struct server *server, const char *serial,
goto error1; goto error1;
} }
// server will connect to our server socket bool ok = net_pipe(server->pipe_intr);
server->process = execute_server(server, params); if (!ok) {
if (server->process == PROCESS_NONE) { perror("pipe");
goto error2; goto error2;
} }
// If the server process dies before connecting to the server socket, then // server will connect to our server socket
// the client will be stuck forever on accept(). To avoid the problem, we server->process = execute_server(server, params);
// must be able to wake up the accept() call when the server dies. To keep if (server->process == PROCESS_NONE) {
// things simple and multiplatform, just spawn a new thread waiting for the goto error3;
// server process and calling shutdown()/close() on the server socket if }
// necessary to wake up any accept() blocking call.
server->wait_server_thread = server->wait_server_thread =
SDL_CreateThread(run_wait_server, "wait-server", server); SDL_CreateThread(run_wait_server, "wait-server", server);
if (!server->wait_server_thread) { if (!server->wait_server_thread) {
cmd_terminate(server->process); cmd_terminate(server->process);
cmd_simple_wait(server->process, NULL); // ignore exit code cmd_simple_wait(server->process, NULL); // ignore exit code
goto error2; goto error3;
} }
server->tunnel_enabled = true; server->tunnel_enabled = true;
return true; return true;
error3:
close(server->pipe_intr[0]);
close(server->pipe_intr[1]);
error2: error2:
if (!server->tunnel_forward) { if (!server->tunnel_forward) {
bool was_closed = close_socket(&server->server_socket);
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);
error1: error1:
@@ -409,23 +406,34 @@ error1:
bool bool
server_connect_to(struct server *server) { server_connect_to(struct server *server) {
if (!server->tunnel_forward) { if (!server->tunnel_forward) {
bool acceptable = net_select_interruptible(server->server_socket,
server->pipe_intr[0]);
if (!acceptable) {
// the process died, accept() would never succeed
return false;
}
server->video_socket = net_accept(server->server_socket); server->video_socket = net_accept(server->server_socket);
if (server->video_socket == INVALID_SOCKET) { if (server->video_socket == INVALID_SOCKET) {
return false; return false;
} }
acceptable = net_select_interruptible(server->server_socket,
server->pipe_intr[0]);
if (!acceptable) {
// the process died, accept() would never succeed
return false;
}
server->control_socket = net_accept(server->server_socket); server->control_socket = net_accept(server->server_socket);
if (server->control_socket == INVALID_SOCKET) { if (server->control_socket == INVALID_SOCKET) {
// the video_socket will be cleaned up on destroy // the video_socket will be cleaned up on destroy
return false; return false;
} }
close(server->pipe_intr[0]);
// 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)) { close_socket(&server->server_socket);
// close it from here
close_socket(server->server_socket);
// otherwise, it is closed by run_wait_server()
}
} else { } else {
uint32_t attempts = 100; uint32_t attempts = 100;
uint32_t delay = 100; // ms uint32_t delay = 100; // ms
@@ -452,15 +460,14 @@ server_connect_to(struct server *server) {
void void
server_stop(struct server *server) { server_stop(struct server *server) {
if (server->server_socket != INVALID_SOCKET if (server->server_socket != INVALID_SOCKET) {
&& !atomic_flag_test_and_set(&server->server_socket_closed)) { close_socket(&server->server_socket);
close_socket(server->server_socket);
} }
if (server->video_socket != INVALID_SOCKET) { if (server->video_socket != INVALID_SOCKET) {
close_socket(server->video_socket); close_socket(&server->video_socket);
} }
if (server->control_socket != INVALID_SOCKET) { if (server->control_socket != INVALID_SOCKET) {
close_socket(server->control_socket); close_socket(&server->control_socket);
} }
assert(server->process != PROCESS_NONE); assert(server->process != PROCESS_NONE);

View File

@@ -1,7 +1,6 @@
#ifndef SERVER_H #ifndef SERVER_H
#define SERVER_H #define SERVER_H
#include <stdatomic.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <SDL2/SDL_thread.h> #include <SDL2/SDL_thread.h>
@@ -15,7 +14,7 @@ struct server {
char *serial; char *serial;
process_t process; process_t process;
SDL_Thread *wait_server_thread; SDL_Thread *wait_server_thread;
atomic_flag server_socket_closed; int pipe_intr[2]; // to wake up blocking accept() on process exit
socket_t server_socket; // only used if !tunnel_forward socket_t server_socket; // only used if !tunnel_forward
socket_t video_socket; socket_t video_socket;
socket_t control_socket; socket_t control_socket;
@@ -28,8 +27,6 @@ struct server {
#define SERVER_INITIALIZER { \ #define SERVER_INITIALIZER { \
.serial = NULL, \ .serial = NULL, \
.process = PROCESS_NONE, \ .process = PROCESS_NONE, \
.wait_server_thread = NULL, \
.server_socket_closed = ATOMIC_FLAG_INIT, \
.server_socket = INVALID_SOCKET, \ .server_socket = INVALID_SOCKET, \
.video_socket = INVALID_SOCKET, \ .video_socket = INVALID_SOCKET, \
.control_socket = INVALID_SOCKET, \ .control_socket = INVALID_SOCKET, \

View File

@@ -1,16 +1,22 @@
#include "net.h" #include "net.h"
#include <assert.h>
#include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <SDL2/SDL_platform.h> #include <SDL2/SDL_platform.h>
#include "config.h" #include "config.h"
#include "common.h"
#include "log.h" #include "log.h"
#ifdef __WINDOWS__ #ifdef __WINDOWS__
# include <io.h>
# include <winsock2.h>
typedef int socklen_t; typedef int socklen_t;
#else #else
# include <sys/types.h> # include <sys/select.h>
# include <sys/socket.h> # include <sys/socket.h>
# include <sys/types.h>
# include <netinet/in.h> # include <netinet/in.h>
# include <arpa/inet.h> # include <arpa/inet.h>
# include <unistd.h> # include <unistd.h>
@@ -145,3 +151,38 @@ net_close(socket_t socket) {
return !close(socket); return !close(socket);
#endif #endif
} }
bool
net_select_interruptible(int fd, int fd_intr) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
FD_SET(fd_intr, &rfds);
int nfds = MAX(fd, fd_intr) + 1;
// use select() because it's available on supported platforms
int r = select(nfds, &rfds, NULL, NULL, NULL);
if (r == -1) {
// failure
return false;
}
assert(r > 0);
if (FD_ISSET(fd_intr, &rfds)) {
// interrupted is set
return false;
}
assert(FD_ISSET(fd, &rfds));
return true;
}
bool
net_pipe(int fds[static 2]) {
#ifdef __WINDOWS__
return !_pipe(fds, 4096, 0);
#else
return !pipe(fds);
#endif
}

View File

@@ -54,4 +54,12 @@ net_shutdown(socket_t socket, int how);
bool bool
net_close(socket_t socket); net_close(socket_t socket);
// wait for fd or fd_intr to be readable
// return true if fd is readable and fd_intr is not
bool
net_select_interruptible(int fd, int fd_intr);
bool
net_pipe(int fd[static 2]);
#endif #endif

View File

@@ -7,7 +7,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.6.2' classpath 'com.android.tools.build:gradle:3.4.2'
// 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

Binary file not shown.

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-6.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

33
gradlew vendored
View File

@@ -125,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi fi
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`
@@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
else else
eval `echo args$i`="\"$arg\"" eval `echo args$i`="\"$arg\""
fi fi
i=`expr $i + 1` i=$((i+1))
done done
case $i in case $i in
0) set -- ;; (0) set -- ;;
1) set -- "$args0" ;; (1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;; (2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;; (3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
@@ -175,9 +175,14 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }
APP_ARGS=`save "$@"` APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules # Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

3
gradlew.bat vendored
View File

@@ -29,9 +29,6 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

View File

@@ -163,10 +163,8 @@ public final class Device {
} }
public void setClipboardText(String text) { public void setClipboardText(String text) {
boolean ok = serviceManager.getClipboardManager().setText(text); serviceManager.getClipboardManager().setText(text);
if (ok) { Ln.i("Device clipboard set");
Ln.i("Device clipboard set");
}
} }
/** /**
@@ -178,10 +176,8 @@ public final class Device {
Ln.e("Could not get built-in display"); Ln.e("Could not get built-in display");
return; return;
} }
boolean ok = SurfaceControl.setDisplayPowerMode(d, mode); SurfaceControl.setDisplayPowerMode(d, mode);
if (ok) { Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on"));
Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on"));
}
} }
/** /**

View File

@@ -74,15 +74,13 @@ public class ClipboardManager {
} }
} }
public boolean setText(CharSequence text) { public void setText(CharSequence text) {
try { try {
Method method = getSetPrimaryClipMethod(); Method method = getSetPrimaryClipMethod();
ClipData clipData = ClipData.newPlainText(null, text); ClipData clipData = ClipData.newPlainText(null, text);
setPrimaryClip(method, manager, clipData); setPrimaryClip(method, manager, clipData);
return true;
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
Ln.e("Could not invoke method", e); Ln.e("Could not invoke method", e);
return false;
} }
} }
} }

View File

@@ -121,14 +121,12 @@ public final class SurfaceControl {
return setDisplayPowerModeMethod; return setDisplayPowerModeMethod;
} }
public static boolean setDisplayPowerMode(IBinder displayToken, int mode) { public static void setDisplayPowerMode(IBinder displayToken, int mode) {
try { try {
Method method = getSetDisplayPowerModeMethod(); Method method = getSetDisplayPowerModeMethod();
method.invoke(null, displayToken, mode); method.invoke(null, displayToken, mode);
return true;
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
Ln.e("Could not invoke method", e); Ln.e("Could not invoke method", e);
return false;
} }
} }