Compare commits

..

8 Commits

Author SHA1 Message Date
Romain Vimont
fbdcc42dbb Replace Ctrl by Meta for volume shortcuts on MacOS
Ctrl+UP and Ctrl+DOWN are already used by the window manager on MacOS.

Use Cmd key instead (like on VLC).
2018-10-24 19:11:48 +02:00
Romain Vimont
aa10721c9e Refactor to support Meta in shortcuts
Move the Ctrl and Meta key down checks to each shortcut individually, so
that we can add a shortcut involving Meta.
2018-10-24 19:08:40 +02:00
Romain Vimont
0b92b93358 Capture Alt and Meta keys
Alt and Meta keys should not be forwarded to the device. For now, they
are not used for shortcuts, but they could be.
2018-10-24 19:08:36 +02:00
Romain Vimont
c20245630e Factorize Windows command building
Extract command line building to a separate method.
2018-10-21 18:57:06 +02:00
Romain Vimont
b882322f73 Work around Os.write() not updating position
ByteBuffer position is not updated as expected by Os.write() on old
Android versions. Count the remaining bytes manually.

Fixes <https://github.com/Genymobile/scrcpy/issues/291>.
2018-10-09 08:43:17 +02:00
Romain Vimont
8875955921 Support paths containing spaces on Windows
Quote the arguments of "adb push" to support paths which contain spaces
on Windows.

Fixes <https://github.com/Genymobile/scrcpy/issues/288>.
2018-10-04 21:01:23 +02:00
Romain Vimont
ff4430b2a3 Declare fun(void) functions with no parameters
This is not C++.
2018-10-04 17:04:20 +02:00
Romain Vimont
cea176c210 Update links to v1.4 in README and BUILD 2018-10-04 00:13:28 +02:00
11 changed files with 149 additions and 67 deletions

View File

@@ -219,10 +219,10 @@ You can then [run](README.md#run) _scrcpy_.
## Prebuilt server ## Prebuilt server
- [`scrcpy-server-v1.3.jar`][direct-scrcpy-server]. - [`scrcpy-server-v1.4.jar`][direct-scrcpy-server]
_(SHA-256: 0f9a5a217f33f0ed7a1498ceb3c0cccf31c53533893aa952e674c1571d2740c1)_ _(SHA-256: 1ff7a72fcfe81dadccfab9d6f86c971cd7c7f38f17196748fe05480e301b443d)_
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.3/scrcpy-server-v1.3.jar [direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.4/scrcpy-server-v1.4.jar
Download the prebuilt server somewhere, and specify its path during the Meson Download the prebuilt server somewhere, and specify its path during the Meson
configuration: configuration:

View File

@@ -1,4 +1,4 @@
# scrcpy (v1.3) # scrcpy (v1.4)
This application provides display and control of Android devices connected on This application provides display and control of Android devices connected on
USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access. USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access.
@@ -42,13 +42,13 @@ For Gentoo, an [Ebuild] is available: [`scrcpy/`][ebuild-link].
For Windows, for simplicity, prebuilt archives with all the dependencies For Windows, for simplicity, prebuilt archives with all the dependencies
(including `adb`) are available: (including `adb`) are available:
- [`scrcpy-win32-v1.3.zip`][direct-win32]. - [`scrcpy-win32-v1.4.zip`][direct-win32]
_(SHA-256: 51a2990e631ed469a7a86ff38107d517a91d313fb3f8327eb7bc71dde40870b5)_ _(SHA-256: 1f72fa520980727e8943b7214b64c66b00b9b5267f7cffefb64fa37c3ca803cf)_
- [`scrcpy-win64-v1.3.zip`][direct-win64]. - [`scrcpy-win64-v1.4.zip`][direct-win64]
_(SHA-256: 0768a80d3d600d0bbcd220ca150ae88a3a58d1fe85c308a8c61f44480b711e43)_ _(SHA-256: 382f02bd8ed3db2cc7ab15aabdb83674744993b936d602b01e6959a150584a79)_
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.3/scrcpy-win32-v1.3.zip [direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.4/scrcpy-win32-v1.4.zip
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.3/scrcpy-win64-v1.3.zip [direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.4/scrcpy-win64-v1.4.zip
You can also [build the app manually][BUILD]. You can also [build the app manually][BUILD].
@@ -134,8 +134,8 @@ scrcpy -f
| click on `BACK` | `Ctrl`+`b` \| _Right-click²_ | | click on `BACK` | `Ctrl`+`b` \| _Right-click²_ |
| click on `APP_SWITCH` | `Ctrl`+`s` | | click on `APP_SWITCH` | `Ctrl`+`s` |
| click on `MENU` | `Ctrl`+`m` | | click on `MENU` | `Ctrl`+`m` |
| click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ | | click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ (`Cmd`+`↑` on MacOS) |
| click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ | | click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ (`Cmd`+`↓` on MacOS) |
| click on `POWER` | `Ctrl`+`p` | | click on `POWER` | `Ctrl`+`p` |
| turn screen on | _Right-click²_ | | turn screen on | _Right-click²_ |
| paste computer clipboard to device | `Ctrl`+`v` | | paste computer clipboard to device | `Ctrl`+`v` |

View File

@@ -6,10 +6,11 @@
#include "common.h" #include "common.h"
#include "log.h" #include "log.h"
#include "str_util.h"
static const char *adb_command; static const char *adb_command;
static inline const char *get_adb_command() { static inline const char *get_adb_command(void) {
if (!adb_command) { if (!adb_command) {
adb_command = getenv("ADB"); adb_command = getenv("ADB");
if (!adb_command) if (!adb_command)
@@ -89,23 +90,49 @@ process_t adb_reverse_remove(const char *serial, const char *device_socket_name)
} }
process_t adb_push(const char *serial, const char *local, const char *remote) { process_t adb_push(const char *serial, const char *local, const char *remote) {
#ifdef __WINDOWS__
// Windows will parse the string, so the paths must be quoted
// (see sys/win/command.c)
local = strquote(local);
if (!local) {
return PROCESS_NONE;
}
remote = strquote(remote);
if (!remote) {
free((void *) local);
return PROCESS_NONE;
}
#endif
const char *const adb_cmd[] = {"push", local, remote}; const char *const adb_cmd[] = {"push", local, remote};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd)); process_t proc = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
#ifdef __WINDOWS__
free((void *) remote);
free((void *) local);
#endif
return proc;
} }
process_t adb_install(const char *serial, const char *local) { process_t adb_install(const char *serial, const char *local) {
#ifdef __WINDOWS__ #ifdef __WINDOWS__
// Windows will parse the string, so the local name must be quoted (see sys/win/command.c) // Windows will parse the string, so the local name must be quoted
size_t len = strlen(local); // (see sys/win/command.c)
char quoted[len + 3]; local = strquote(local);
memcpy(&quoted[1], local, len); if (!local) {
quoted[0] = '"'; return PROCESS_NONE;
quoted[len + 1] = '"'; }
quoted[len + 2] = '\0';
local = quoted;
#endif #endif
const char *const adb_cmd[] = {"install", "-r", local}; const char *const adb_cmd[] = {"install", "-r", local};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd)); process_t proc = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
#ifdef __WINDOWS__
free((void *) local);
#endif
return proc;
} }
process_t adb_remove_path(const char *serial, const char *path) { process_t adb_remove_path(const char *serial, const char *path) {

View File

@@ -151,9 +151,17 @@ void input_manager_process_text_input(struct input_manager *input_manager,
void input_manager_process_key(struct input_manager *input_manager, void input_manager_process_key(struct input_manager *input_manager,
const SDL_KeyboardEvent *event) { const SDL_KeyboardEvent *event) {
SDL_bool ctrl = event->keysym.mod & (KMOD_LCTRL | KMOD_RCTRL); SDL_bool ctrl = event->keysym.mod & (KMOD_LCTRL | KMOD_RCTRL);
SDL_bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
SDL_bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI);
if (alt) {
// no shortcut involves Alt or Meta, and they should not be forwarded
// to the device
return;
}
// capture all Ctrl events // capture all Ctrl events
if (ctrl) { if (ctrl | meta) {
SDL_bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT); SDL_bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
if (shift) { if (shift) {
// currently, there is no shortcut involving SHIFT // currently, there is no shortcut involving SHIFT
@@ -165,61 +173,73 @@ void input_manager_process_key(struct input_manager *input_manager,
SDL_bool repeat = event->repeat; SDL_bool repeat = event->repeat;
switch (keycode) { switch (keycode) {
case SDLK_h: case SDLK_h:
if (!repeat) { if (ctrl && !meta && !repeat) {
action_home(input_manager->controller, action); action_home(input_manager->controller, action);
} }
return; return;
case SDLK_b: // fall-through case SDLK_b: // fall-through
case SDLK_BACKSPACE: case SDLK_BACKSPACE:
if (!repeat) { if (ctrl && !meta && !repeat) {
action_back(input_manager->controller, action); action_back(input_manager->controller, action);
} }
return; return;
case SDLK_s: case SDLK_s:
if (!repeat) { if (ctrl && !meta && !repeat) {
action_app_switch(input_manager->controller, action); action_app_switch(input_manager->controller, action);
} }
return; return;
case SDLK_m: case SDLK_m:
if (!repeat) { if (ctrl && !meta && !repeat) {
action_menu(input_manager->controller, action); action_menu(input_manager->controller, action);
} }
return; return;
case SDLK_p: case SDLK_p:
if (!repeat) { if (ctrl && !meta && !repeat) {
action_power(input_manager->controller, action); action_power(input_manager->controller, action);
} }
return; return;
case SDLK_DOWN: case SDLK_DOWN:
#ifdef __APPLE__
if (!ctrl && meta) {
#else
if (ctrl && !meta) {
#endif
// forward repeated events // forward repeated events
action_volume_down(input_manager->controller, action); action_volume_down(input_manager->controller, action);
}
return; return;
case SDLK_UP: case SDLK_UP:
#ifdef __APPLE__
if (!ctrl && meta) {
#else
if (ctrl && !meta) {
#endif
// forward repeated events // forward repeated events
action_volume_up(input_manager->controller, action); action_volume_up(input_manager->controller, action);
}
return; return;
case SDLK_v: case SDLK_v:
if (!repeat && event->type == SDL_KEYDOWN) { if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) {
clipboard_paste(input_manager->controller); clipboard_paste(input_manager->controller);
} }
return; return;
case SDLK_f: case SDLK_f:
if (!repeat && event->type == SDL_KEYDOWN) { if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) {
screen_switch_fullscreen(input_manager->screen); screen_switch_fullscreen(input_manager->screen);
} }
return; return;
case SDLK_x: case SDLK_x:
if (!repeat && event->type == SDL_KEYDOWN) { if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) {
screen_resize_to_fit(input_manager->screen); screen_resize_to_fit(input_manager->screen);
} }
return; return;
case SDLK_g: case SDLK_g:
if (!repeat && event->type == SDL_KEYDOWN) { if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) {
screen_resize_to_pixel_perfect(input_manager->screen); screen_resize_to_pixel_perfect(input_manager->screen);
} }
return; return;
case SDLK_i: case SDLK_i:
if (!repeat && event->type == SDL_KEYDOWN) { if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) {
switch_fps_counter_state(input_manager->frames); switch_fps_counter_state(input_manager->frames);
} }
return; return;

View File

@@ -1,5 +1,8 @@
#include "str_util.h" #include "str_util.h"
#include <stdlib.h>
#include <string.h>
size_t xstrncpy(char *dest, const char *src, size_t n) { size_t xstrncpy(char *dest, const char *src, size_t n) {
size_t i; size_t i;
for (i = 0; i < n - 1 && src[i] != '\0'; ++i) for (i = 0; i < n - 1 && src[i] != '\0'; ++i)
@@ -31,3 +34,16 @@ truncated:
dst[n - 1] = '\0'; dst[n - 1] = '\0';
return n; return n;
} }
char *strquote(const char *src) {
size_t len = strlen(src);
char *quoted = malloc(len + 3);
if (!quoted) {
return NULL;
}
memcpy(&quoted[1], src, len);
quoted[0] = '"';
quoted[len + 1] = '"';
quoted[len + 2] = '\0';
return quoted;
}

View File

@@ -16,4 +16,8 @@ size_t xstrncpy(char *dest, const char *src, size_t n);
// occurred, or n if truncated // occurred, or n if truncated
size_t xstrjoin(char *dst, const char *const tokens[], char sep, size_t n); size_t xstrjoin(char *dst, const char *const tokens[], char sep, size_t n);
// quote a string
// returns the new allocated string, to be freed by the caller
char *strquote(const char *src);
#endif #endif

View File

@@ -4,20 +4,27 @@
#include "log.h" #include "log.h"
#include "str_util.h" #include "str_util.h"
static int build_cmd(char *cmd, size_t len, const char *const argv[]) {
// Windows command-line parsing is WTF:
// <http://daviddeley.com/autohotkey/parameters/parameters.htm#WINPASS>
// only make it work for this very specific program
// (don't handle escaping nor quotes)
size_t ret = xstrjoin(cmd, argv, ' ', len);
if (ret >= len) {
LOGE("Command too long (%" PRIsizet " chars)", len - 1);
return -1;
}
return 0;
}
enum process_result cmd_execute(const char *path, const char *const argv[], HANDLE *handle) { enum process_result cmd_execute(const char *path, const char *const argv[], HANDLE *handle) {
STARTUPINFO si; STARTUPINFO si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si)); memset(&si, 0, sizeof(si));
si.cb = sizeof(si); si.cb = sizeof(si);
// Windows command-line parsing is WTF:
// <http://daviddeley.com/autohotkey/parameters/parameters.htm#WINPASS>
// only make it work for this very specific program
// (don't handle escaping nor quotes)
char cmd[256]; char cmd[256];
size_t ret = xstrjoin(cmd, argv, ' ', sizeof(cmd)); if (build_cmd(cmd, sizeof(cmd), argv)) {
if (ret >= sizeof(cmd)) {
LOGE("Command too long (%" PRIsizet " chars)", sizeof(cmd) - 1);
*handle = NULL; *handle = NULL;
return PROCESS_ERROR_GENERIC; return PROCESS_ERROR_GENERIC;
} }

View File

@@ -3,7 +3,7 @@
#include "control_event.h" #include "control_event.h"
static void test_control_event_queue_empty() { static void test_control_event_queue_empty(void) {
struct control_event_queue queue; struct control_event_queue queue;
SDL_bool init_ok = control_event_queue_init(&queue); SDL_bool init_ok = control_event_queue_init(&queue);
assert(init_ok); assert(init_ok);
@@ -25,7 +25,7 @@ static void test_control_event_queue_empty() {
control_event_queue_destroy(&queue); control_event_queue_destroy(&queue);
} }
static void test_control_event_queue_full() { static void test_control_event_queue_full(void) {
struct control_event_queue queue; struct control_event_queue queue;
SDL_bool init_ok = control_event_queue_init(&queue); SDL_bool init_ok = control_event_queue_init(&queue);
assert(init_ok); assert(init_ok);
@@ -43,7 +43,7 @@ static void test_control_event_queue_full() {
control_event_queue_destroy(&queue); control_event_queue_destroy(&queue);
} }
static void test_control_event_queue_push_take() { static void test_control_event_queue_push_take(void) {
struct control_event_queue queue; struct control_event_queue queue;
SDL_bool init_ok = control_event_queue_init(&queue); SDL_bool init_ok = control_event_queue_init(&queue);
assert(init_ok); assert(init_ok);
@@ -87,7 +87,7 @@ static void test_control_event_queue_push_take() {
control_event_queue_destroy(&queue); control_event_queue_destroy(&queue);
} }
int main() { int main(void) {
test_control_event_queue_empty(); test_control_event_queue_empty();
test_control_event_queue_full(); test_control_event_queue_full();
test_control_event_queue_push_take(); test_control_event_queue_push_take();

View File

@@ -3,7 +3,7 @@
#include "control_event.h" #include "control_event.h"
static void test_serialize_keycode_event() { static void test_serialize_keycode_event(void) {
struct control_event event = { struct control_event event = {
.type = CONTROL_EVENT_TYPE_KEYCODE, .type = CONTROL_EVENT_TYPE_KEYCODE,
.keycode_event = { .keycode_event = {
@@ -26,7 +26,7 @@ static void test_serialize_keycode_event() {
assert(!memcmp(buf, expected, sizeof(expected))); assert(!memcmp(buf, expected, sizeof(expected)));
} }
static void test_serialize_text_event() { static void test_serialize_text_event(void) {
struct control_event event = { struct control_event event = {
.type = CONTROL_EVENT_TYPE_TEXT, .type = CONTROL_EVENT_TYPE_TEXT,
.text_event = { .text_event = {
@@ -46,7 +46,7 @@ static void test_serialize_text_event() {
assert(!memcmp(buf, expected, sizeof(expected))); assert(!memcmp(buf, expected, sizeof(expected)));
} }
static void test_serialize_long_text_event() { static void test_serialize_long_text_event(void) {
struct control_event event; struct control_event event;
event.type = CONTROL_EVENT_TYPE_TEXT; event.type = CONTROL_EVENT_TYPE_TEXT;
char text[TEXT_MAX_LENGTH]; char text[TEXT_MAX_LENGTH];
@@ -66,7 +66,7 @@ static void test_serialize_long_text_event() {
assert(!memcmp(buf, expected, sizeof(expected))); assert(!memcmp(buf, expected, sizeof(expected)));
} }
static void test_serialize_mouse_event() { static void test_serialize_mouse_event(void) {
struct control_event event = { struct control_event event = {
.type = CONTROL_EVENT_TYPE_MOUSE, .type = CONTROL_EVENT_TYPE_MOUSE,
.mouse_event = { .mouse_event = {
@@ -99,7 +99,7 @@ static void test_serialize_mouse_event() {
assert(!memcmp(buf, expected, sizeof(expected))); assert(!memcmp(buf, expected, sizeof(expected)));
} }
static void test_serialize_scroll_event() { static void test_serialize_scroll_event(void) {
struct control_event event = { struct control_event event = {
.type = CONTROL_EVENT_TYPE_SCROLL, .type = CONTROL_EVENT_TYPE_SCROLL,
.scroll_event = { .scroll_event = {
@@ -132,11 +132,10 @@ static void test_serialize_scroll_event() {
assert(!memcmp(buf, expected, sizeof(expected))); assert(!memcmp(buf, expected, sizeof(expected)));
} }
int main() { int main(void) {
test_serialize_keycode_event(); test_serialize_keycode_event();
test_serialize_text_event(); test_serialize_text_event();
test_serialize_long_text_event(); test_serialize_long_text_event();
test_serialize_mouse_event(); test_serialize_mouse_event();
test_serialize_scroll_event(); test_serialize_scroll_event();
return 0;
} }

View File

@@ -3,7 +3,7 @@
#include "str_util.h" #include "str_util.h"
static void test_xstrncpy_simple() { static void test_xstrncpy_simple(void) {
char s[] = "xxxxxxxxxx"; char s[] = "xxxxxxxxxx";
size_t w = xstrncpy(s, "abcdef", sizeof(s)); size_t w = xstrncpy(s, "abcdef", sizeof(s));
@@ -20,7 +20,7 @@ static void test_xstrncpy_simple() {
assert(!strcmp("abcdef", s)); assert(!strcmp("abcdef", s));
} }
static void test_xstrncpy_just_fit() { static void test_xstrncpy_just_fit(void) {
char s[] = "xxxxxx"; char s[] = "xxxxxx";
size_t w = xstrncpy(s, "abcdef", sizeof(s)); size_t w = xstrncpy(s, "abcdef", sizeof(s));
@@ -34,7 +34,7 @@ static void test_xstrncpy_just_fit() {
assert(!strcmp("abcdef", s)); assert(!strcmp("abcdef", s));
} }
static void test_xstrncpy_truncated() { static void test_xstrncpy_truncated(void) {
char s[] = "xxx"; char s[] = "xxx";
size_t w = xstrncpy(s, "abcdef", sizeof(s)); size_t w = xstrncpy(s, "abcdef", sizeof(s));
@@ -48,7 +48,7 @@ static void test_xstrncpy_truncated() {
assert(!strncmp("abcdef", s, 3)); assert(!strncmp("abcdef", s, 3));
} }
static void test_xstrjoin_simple() { static void test_xstrjoin_simple(void) {
const char *const tokens[] = { "abc", "de", "fghi", NULL }; const char *const tokens[] = { "abc", "de", "fghi", NULL };
char s[] = "xxxxxxxxxxxxxx"; char s[] = "xxxxxxxxxxxxxx";
size_t w = xstrjoin(s, tokens, ' ', sizeof(s)); size_t w = xstrjoin(s, tokens, ' ', sizeof(s));
@@ -66,7 +66,7 @@ static void test_xstrjoin_simple() {
assert(!strcmp("abc de fghi", s)); assert(!strcmp("abc de fghi", s));
} }
static void test_xstrjoin_just_fit() { static void test_xstrjoin_just_fit(void) {
const char *const tokens[] = { "abc", "de", "fghi", NULL }; const char *const tokens[] = { "abc", "de", "fghi", NULL };
char s[] = "xxxxxxxxxxx"; char s[] = "xxxxxxxxxxx";
size_t w = xstrjoin(s, tokens, ' ', sizeof(s)); size_t w = xstrjoin(s, tokens, ' ', sizeof(s));
@@ -81,7 +81,7 @@ static void test_xstrjoin_just_fit() {
assert(!strcmp("abc de fghi", s)); assert(!strcmp("abc de fghi", s));
} }
static void test_xstrjoin_truncated_in_token() { static void test_xstrjoin_truncated_in_token(void) {
const char *const tokens[] = { "abc", "de", "fghi", NULL }; const char *const tokens[] = { "abc", "de", "fghi", NULL };
char s[] = "xxxxx"; char s[] = "xxxxx";
size_t w = xstrjoin(s, tokens, ' ', sizeof(s)); size_t w = xstrjoin(s, tokens, ' ', sizeof(s));
@@ -96,7 +96,7 @@ static void test_xstrjoin_truncated_in_token() {
assert(!strcmp("abc d", s)); assert(!strcmp("abc d", s));
} }
static void test_xstrjoin_truncated_before_sep() { static void test_xstrjoin_truncated_before_sep(void) {
const char *const tokens[] = { "abc", "de", "fghi", NULL }; const char *const tokens[] = { "abc", "de", "fghi", NULL };
char s[] = "xxxxxx"; char s[] = "xxxxxx";
size_t w = xstrjoin(s, tokens, ' ', sizeof(s)); size_t w = xstrjoin(s, tokens, ' ', sizeof(s));
@@ -111,7 +111,7 @@ static void test_xstrjoin_truncated_before_sep() {
assert(!strcmp("abc de", s)); assert(!strcmp("abc de", s));
} }
static void test_xstrjoin_truncated_after_sep() { static void test_xstrjoin_truncated_after_sep(void) {
const char *const tokens[] = { "abc", "de", "fghi", NULL }; const char *const tokens[] = { "abc", "de", "fghi", NULL };
char s[] = "xxxxxxx"; char s[] = "xxxxxxx";
size_t w = xstrjoin(s, tokens, ' ', sizeof(s)); size_t w = xstrjoin(s, tokens, ' ', sizeof(s));
@@ -126,7 +126,7 @@ static void test_xstrjoin_truncated_after_sep() {
assert(!strcmp("abc de ", s)); assert(!strcmp("abc de ", s));
} }
int main() { int main(void) {
test_xstrncpy_simple(); test_xstrncpy_simple();
test_xstrncpy_just_fit(); test_xstrncpy_just_fit();
test_xstrncpy_truncated(); test_xstrncpy_truncated();

View File

@@ -14,9 +14,18 @@ public class IO {
} }
public static void writeFully(FileDescriptor fd, ByteBuffer from) throws IOException { public static void writeFully(FileDescriptor fd, ByteBuffer from) throws IOException {
while (from.hasRemaining()) { // ByteBuffer position is not updated as expected by Os.write() on old Android versions, so
// count the remaining bytes manually.
// See <https://github.com/Genymobile/scrcpy/issues/291>.
int remaining = from.remaining();
while (remaining > 0) {
try { try {
Os.write(fd, from); int w = Os.write(fd, from);
if (BuildConfig.DEBUG && w < 0) {
// w should not be negative, since an exception is thrown on error
throw new AssertionError("Os.write() returned a negative value (" + w + ")");
}
remaining -= w;
} catch (ErrnoException e) { } catch (ErrnoException e) {
if (e.errno != OsConstants.EINTR) { if (e.errno != OsConstants.EINTR) {
throw new IOException(e); throw new IOException(e);