Compare commits
1 Commits
file
...
push_event
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b790e15ad6 |
12
README.md
12
README.md
@@ -582,14 +582,6 @@ scrcpy --turn-screen-off --stay-awake
|
|||||||
scrcpy -Sw
|
scrcpy -Sw
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Power off on close
|
|
||||||
|
|
||||||
To turn the device screen off when closing scrcpy:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
scrcpy --power-off-on-close
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Show touches
|
#### Show touches
|
||||||
|
|
||||||
@@ -838,7 +830,7 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
|
|||||||
| Turn device screen on | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>o</kbd>
|
| Turn device screen on | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>o</kbd>
|
||||||
| Rotate device screen | <kbd>MOD</kbd>+<kbd>r</kbd>
|
| Rotate device screen | <kbd>MOD</kbd>+<kbd>r</kbd>
|
||||||
| Expand notification panel | <kbd>MOD</kbd>+<kbd>n</kbd> \| _5th-click³_
|
| Expand notification panel | <kbd>MOD</kbd>+<kbd>n</kbd> \| _5th-click³_
|
||||||
| Expand settings panel | <kbd>MOD</kbd>+<kbd>n</kbd>+<kbd>n</kbd> \| _Double-5th-click³_
|
| Expand settings panel | <kbd>MOD</kbd>+<kbd>n</kbd>+<kbd>n</kbd> \| _Double-5th-click³_
|
||||||
| Collapse panels | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>n</kbd>
|
| Collapse panels | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>n</kbd>
|
||||||
| Copy to clipboard⁴ | <kbd>MOD</kbd>+<kbd>c</kbd>
|
| Copy to clipboard⁴ | <kbd>MOD</kbd>+<kbd>c</kbd>
|
||||||
| Cut to clipboard⁴ | <kbd>MOD</kbd>+<kbd>x</kbd>
|
| Cut to clipboard⁴ | <kbd>MOD</kbd>+<kbd>x</kbd>
|
||||||
@@ -846,8 +838,6 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
|
|||||||
| Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
|
| Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
|
||||||
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
|
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
|
||||||
| Pinch-to-zoom | <kbd>Ctrl</kbd>+_click-and-move_
|
| Pinch-to-zoom | <kbd>Ctrl</kbd>+_click-and-move_
|
||||||
| Drag & drop APK file | Install APK from computer
|
|
||||||
| Drag & drop non-APK file | [Push file to device](#push-file-to-device)
|
|
||||||
|
|
||||||
_¹Double-click on black borders to remove them._
|
_¹Double-click on black borders to remove them._
|
||||||
_²Right-click turns the screen on if it was off, presses BACK otherwise._
|
_²Right-click turns the screen on if it was off, presses BACK otherwise._
|
||||||
|
|||||||
@@ -24,27 +24,18 @@ src = [
|
|||||||
'src/server.c',
|
'src/server.c',
|
||||||
'src/stream.c',
|
'src/stream.c',
|
||||||
'src/video_buffer.c',
|
'src/video_buffer.c',
|
||||||
'src/util/file.c',
|
|
||||||
'src/util/log.c',
|
'src/util/log.c',
|
||||||
'src/util/net.c',
|
'src/util/net.c',
|
||||||
'src/util/process.c',
|
'src/util/process.c',
|
||||||
'src/util/strbuf.c',
|
|
||||||
'src/util/str_util.c',
|
'src/util/str_util.c',
|
||||||
'src/util/term.c',
|
|
||||||
'src/util/thread.c',
|
'src/util/thread.c',
|
||||||
'src/util/tick.c',
|
'src/util/tick.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
if host_machine.system() == 'windows'
|
if host_machine.system() == 'windows'
|
||||||
src += [
|
src += [ 'src/sys/win/process.c' ]
|
||||||
'src/sys/win/file.c',
|
|
||||||
'src/sys/win/process.c',
|
|
||||||
]
|
|
||||||
else
|
else
|
||||||
src += [
|
src += [ 'src/sys/unix/process.c' ]
|
||||||
'src/sys/unix/file.c',
|
|
||||||
'src/sys/unix/process.c',
|
|
||||||
]
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
v4l2_support = host_machine.system() == 'linux'
|
v4l2_support = host_machine.system() == 'linux'
|
||||||
@@ -195,9 +186,7 @@ if get_option('buildtype') == 'debug'
|
|||||||
'tests/test_cli.c',
|
'tests/test_cli.c',
|
||||||
'src/cli.c',
|
'src/cli.c',
|
||||||
'src/options.c',
|
'src/options.c',
|
||||||
'src/util/strbuf.c',
|
|
||||||
'src/util/str_util.c',
|
'src/util/str_util.c',
|
||||||
'src/util/term.c',
|
|
||||||
]],
|
]],
|
||||||
['test_clock', [
|
['test_clock', [
|
||||||
'tests/test_clock.c',
|
'tests/test_clock.c',
|
||||||
@@ -206,7 +195,6 @@ if get_option('buildtype') == 'debug'
|
|||||||
['test_control_msg_serialize', [
|
['test_control_msg_serialize', [
|
||||||
'tests/test_control_msg_serialize.c',
|
'tests/test_control_msg_serialize.c',
|
||||||
'src/control_msg.c',
|
'src/control_msg.c',
|
||||||
'src/util/strbuf.c',
|
|
||||||
'src/util/str_util.c',
|
'src/util/str_util.c',
|
||||||
]],
|
]],
|
||||||
['test_device_msg_deserialize', [
|
['test_device_msg_deserialize', [
|
||||||
@@ -216,13 +204,8 @@ if get_option('buildtype') == 'debug'
|
|||||||
['test_queue', [
|
['test_queue', [
|
||||||
'tests/test_queue.c',
|
'tests/test_queue.c',
|
||||||
]],
|
]],
|
||||||
['test_strbuf', [
|
|
||||||
'tests/test_strbuf.c',
|
|
||||||
'src/util/strbuf.c',
|
|
||||||
]],
|
|
||||||
['test_strutil', [
|
['test_strutil', [
|
||||||
'tests/test_strutil.c',
|
'tests/test_strutil.c',
|
||||||
'src/util/strbuf.c',
|
|
||||||
'src/util/str_util.c',
|
'src/util/str_util.c',
|
||||||
]],
|
]],
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -136,10 +136,6 @@ Set the TCP port (range) used by the client to listen.
|
|||||||
|
|
||||||
Default is 27183:27199.
|
Default is 27183:27199.
|
||||||
|
|
||||||
.TP
|
|
||||||
.B \-\-power\-off\-on\-close
|
|
||||||
Turn the device screen off when closing scrcpy.
|
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-\-prefer\-text
|
.B \-\-prefer\-text
|
||||||
Inject alpha characters and space as text events instead of key events.
|
Inject alpha characters and space as text events instead of key events.
|
||||||
@@ -368,10 +364,6 @@ Pinch-to-zoom from the center of the screen
|
|||||||
.B Drag & drop APK file
|
.B Drag & drop APK file
|
||||||
Install APK from computer
|
Install APK from computer
|
||||||
|
|
||||||
.TP
|
|
||||||
.B Drag & drop non-APK file
|
|
||||||
Push file to device (see \fB\-\-push\-target\fR)
|
|
||||||
|
|
||||||
|
|
||||||
.SH Environment variables
|
.SH Environment variables
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "util/file.h"
|
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/str_util.h"
|
#include "util/str_util.h"
|
||||||
|
|
||||||
@@ -69,7 +68,7 @@ show_adb_installation_msg() {
|
|||||||
{"pacman", "pacman -S android-tools"},
|
{"pacman", "pacman -S android-tools"},
|
||||||
};
|
};
|
||||||
for (size_t i = 0; i < ARRAY_LEN(pkg_managers); ++i) {
|
for (size_t i = 0; i < ARRAY_LEN(pkg_managers); ++i) {
|
||||||
if (sc_file_executable_exists(pkg_managers[i].binary)) {
|
if (search_executable(pkg_managers[i].binary)) {
|
||||||
LOGI("You may install 'adb' by \"%s\"", pkg_managers[i].command);
|
LOGI("You may install 'adb' by \"%s\"", pkg_managers[i].command);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
1189
app/src/cli.c
1189
app/src/cli.c
File diff suppressed because it is too large
Load Diff
@@ -43,11 +43,6 @@
|
|||||||
# define SCRCPY_SDL_HAS_WINDOW_ALWAYS_ON_TOP
|
# define SCRCPY_SDL_HAS_WINDOW_ALWAYS_ON_TOP
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 6)
|
|
||||||
// <https://github.com/libsdl-org/SDL/commit/d7a318de563125e5bb465b1000d6bc9576fbc6fc>
|
|
||||||
# define SCRCPY_SDL_HAS_HINT_TOUCH_MOUSE_EVENTS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 8)
|
#if SDL_VERSION_ATLEAST(2, 0, 8)
|
||||||
// <https://hg.libsdl.org/SDL/rev/dfde5d3f9781>
|
// <https://hg.libsdl.org/SDL/rev/dfde5d3f9781>
|
||||||
# define SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
|
# define SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "util/file.h"
|
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
#include "util/process.h"
|
||||||
#include "util/str_util.h"
|
#include "util/str_util.h"
|
||||||
|
|
||||||
#define SCRCPY_PORTABLE_ICON_FILENAME "icon.png"
|
#define SCRCPY_PORTABLE_ICON_FILENAME "icon.png"
|
||||||
@@ -46,7 +46,7 @@ get_icon_path(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
char *icon_path = sc_file_get_local_path(SCRCPY_PORTABLE_ICON_FILENAME);
|
char *icon_path = get_local_file_path(SCRCPY_PORTABLE_ICON_FILENAME);
|
||||||
if (!icon_path) {
|
if (!icon_path) {
|
||||||
LOGE("Could not get icon path");
|
LOGE("Could not get icon path");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
115
app/src/scrcpy.c
115
app/src/scrcpy.c
@@ -57,30 +57,44 @@ struct scrcpy {
|
|||||||
struct input_manager input_manager;
|
struct input_manager input_manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void
|
|
||||||
push_event(uint32_t type, const char *name) {
|
|
||||||
SDL_Event event;
|
|
||||||
event.type = type;
|
|
||||||
int ret = SDL_PushEvent(&event);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOGE("Could not post %s event: %s", name, SDL_GetError());
|
|
||||||
// What could we do?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#define PUSH_EVENT(TYPE) push_event(TYPE, # TYPE)
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
|
BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
|
||||||
if (ctrl_type == CTRL_C_EVENT) {
|
if (ctrl_type == CTRL_C_EVENT) {
|
||||||
PUSH_EVENT(SDL_QUIT);
|
SDL_Event event;
|
||||||
|
event.type = SDL_QUIT;
|
||||||
|
int ret = SDL_PushEvent(&event);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOGW("Could not post SDL_QUIT event: %s", SDL_GetError());
|
||||||
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
static void
|
// init SDL and set appropriate hints
|
||||||
sdl_set_hints(const char *render_driver) {
|
static bool
|
||||||
|
sdl_init_and_configure(bool display, const char *render_driver,
|
||||||
|
bool disable_screensaver) {
|
||||||
|
uint32_t flags = display ? SDL_INIT_VIDEO : SDL_INIT_EVENTS;
|
||||||
|
if (SDL_Init(flags)) {
|
||||||
|
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
atexit(SDL_Quit);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Clean up properly on Ctrl+C on Windows
|
||||||
|
bool ok = SetConsoleCtrlHandler(windows_ctrl_handler, TRUE);
|
||||||
|
if (!ok) {
|
||||||
|
LOGW("Could not set Ctrl+C handler");
|
||||||
|
}
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
if (!display) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (render_driver && !SDL_SetHint(SDL_HINT_RENDER_DRIVER, render_driver)) {
|
if (render_driver && !SDL_SetHint(SDL_HINT_RENDER_DRIVER, render_driver)) {
|
||||||
LOGW("Could not set render driver");
|
LOGW("Could not set render driver");
|
||||||
@@ -98,15 +112,6 @@ sdl_set_hints(const char *render_driver) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SCRCPY_SDL_HAS_HINT_TOUCH_MOUSE_EVENTS
|
|
||||||
// Disable synthetic mouse events from touch events
|
|
||||||
// Touch events with id SDL_TOUCH_MOUSEID are ignored anyway, but it is
|
|
||||||
// better not to generate them in the first place.
|
|
||||||
if (!SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0")) {
|
|
||||||
LOGW("Could not disable synthetic mouse events");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
|
#ifdef SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
|
||||||
// Disable compositor bypassing on X11
|
// Disable compositor bypassing on X11
|
||||||
if (!SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0")) {
|
if (!SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0")) {
|
||||||
@@ -118,21 +123,6 @@ sdl_set_hints(const char *render_driver) {
|
|||||||
if (!SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0")) {
|
if (!SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0")) {
|
||||||
LOGW("Could not disable minimize on focus loss");
|
LOGW("Could not disable minimize on focus loss");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sdl_configure(bool display, bool disable_screensaver) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
// Clean up properly on Ctrl+C on Windows
|
|
||||||
bool ok = SetConsoleCtrlHandler(windows_ctrl_handler, TRUE);
|
|
||||||
if (!ok) {
|
|
||||||
LOGW("Could not set Ctrl+C handler");
|
|
||||||
}
|
|
||||||
#endif // _WIN32
|
|
||||||
|
|
||||||
if (!display) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disable_screensaver) {
|
if (disable_screensaver) {
|
||||||
LOGD("Screensaver disabled");
|
LOGD("Screensaver disabled");
|
||||||
@@ -141,6 +131,8 @@ sdl_configure(bool display, bool disable_screensaver) {
|
|||||||
LOGD("Screensaver enabled");
|
LOGD("Screensaver enabled");
|
||||||
SDL_EnableScreenSaver();
|
SDL_EnableScreenSaver();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@@ -259,7 +251,13 @@ stream_on_eos(struct stream *stream, void *userdata) {
|
|||||||
(void) stream;
|
(void) stream;
|
||||||
(void) userdata;
|
(void) userdata;
|
||||||
|
|
||||||
PUSH_EVENT(EVENT_STREAM_STOPPED);
|
SDL_Event stop_event;
|
||||||
|
stop_event.type = EVENT_STREAM_STOPPED;
|
||||||
|
int ret = SDL_PushEvent(&stop_event);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOGE("Could not post stream stopped event: %s", SDL_GetError());
|
||||||
|
// XXX What could we do?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -267,14 +265,6 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
static struct scrcpy scrcpy;
|
static struct scrcpy scrcpy;
|
||||||
struct scrcpy *s = &scrcpy;
|
struct scrcpy *s = &scrcpy;
|
||||||
|
|
||||||
// Minimal SDL initialization
|
|
||||||
if (SDL_Init(SDL_INIT_EVENTS)) {
|
|
||||||
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
atexit(SDL_Quit);
|
|
||||||
|
|
||||||
if (!server_init(&s->server)) {
|
if (!server_init(&s->server)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -295,6 +285,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
bool controller_started = false;
|
bool controller_started = false;
|
||||||
bool screen_initialized = false;
|
bool screen_initialized = false;
|
||||||
|
|
||||||
|
bool record = !!options->record_filename;
|
||||||
struct server_params params = {
|
struct server_params params = {
|
||||||
.serial = options->serial,
|
.serial = options->serial,
|
||||||
.log_level = options->log_level,
|
.log_level = options->log_level,
|
||||||
@@ -319,21 +310,15 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
|
|
||||||
server_started = true;
|
server_started = true;
|
||||||
|
|
||||||
if (options->display) {
|
if (!sdl_init_and_configure(options->display, options->render_driver,
|
||||||
sdl_set_hints(options->render_driver);
|
options->disable_screensaver)) {
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize SDL video in addition if display is enabled
|
|
||||||
if (options->display && SDL_Init(SDL_INIT_VIDEO)) {
|
|
||||||
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
sdl_configure(options->display, options->disable_screensaver);
|
char device_name[DEVICE_NAME_FIELD_LENGTH];
|
||||||
|
struct sc_size frame_size;
|
||||||
|
|
||||||
struct server_info info;
|
if (!server_connect_to(&s->server, device_name, &frame_size)) {
|
||||||
|
|
||||||
if (!server_connect_to(&s->server, &info)) {
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,11 +341,11 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct recorder *rec = NULL;
|
struct recorder *rec = NULL;
|
||||||
if (options->record_filename) {
|
if (record) {
|
||||||
if (!recorder_init(&s->recorder,
|
if (!recorder_init(&s->recorder,
|
||||||
options->record_filename,
|
options->record_filename,
|
||||||
options->record_format,
|
options->record_format,
|
||||||
info.frame_size)) {
|
frame_size)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
rec = &s->recorder;
|
rec = &s->recorder;
|
||||||
@@ -406,11 +391,11 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
|
|
||||||
if (options->display) {
|
if (options->display) {
|
||||||
const char *window_title =
|
const char *window_title =
|
||||||
options->window_title ? options->window_title : info.device_name;
|
options->window_title ? options->window_title : device_name;
|
||||||
|
|
||||||
struct screen_params screen_params = {
|
struct screen_params screen_params = {
|
||||||
.window_title = window_title,
|
.window_title = window_title,
|
||||||
.frame_size = info.frame_size,
|
.frame_size = frame_size,
|
||||||
.always_on_top = options->always_on_top,
|
.always_on_top = options->always_on_top,
|
||||||
.window_x = options->window_x,
|
.window_x = options->window_x,
|
||||||
.window_y = options->window_y,
|
.window_y = options->window_y,
|
||||||
@@ -433,8 +418,8 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
|
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
if (options->v4l2_device) {
|
if (options->v4l2_device) {
|
||||||
if (!sc_v4l2_sink_init(&s->v4l2_sink, options->v4l2_device,
|
if (!sc_v4l2_sink_init(&s->v4l2_sink, options->v4l2_device, frame_size,
|
||||||
info.frame_size, options->v4l2_buffer)) {
|
options->v4l2_buffer)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -302,8 +302,7 @@ sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped,
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Post the event on the UI thread
|
// Post the event on the UI thread
|
||||||
int ret = SDL_PushEvent(&new_frame_event);
|
if (SDL_PushEvent(&new_frame_event) < 0) {
|
||||||
if (ret < 0) {
|
|
||||||
LOGW("Could not post new frame event: %s", SDL_GetError());
|
LOGW("Could not post new frame event: %s", SDL_GetError());
|
||||||
screen->event_failed = true;
|
screen->event_failed = true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <SDL2/SDL_platform.h>
|
#include <SDL2/SDL_platform.h>
|
||||||
|
|
||||||
#include "adb.h"
|
#include "adb.h"
|
||||||
#include "util/file.h"
|
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/net.h"
|
#include "util/net.h"
|
||||||
#include "util/str_util.h"
|
#include "util/str_util.h"
|
||||||
@@ -49,7 +48,7 @@ get_server_path(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
char *server_path = sc_file_get_local_path(SERVER_FILENAME);
|
char *server_path = get_local_file_path(SERVER_FILENAME);
|
||||||
if (!server_path) {
|
if (!server_path) {
|
||||||
LOGE("Could not get local file path, "
|
LOGE("Could not get local file path, "
|
||||||
"using " SERVER_FILENAME " from current directory");
|
"using " SERVER_FILENAME " from current directory");
|
||||||
@@ -68,7 +67,7 @@ push_server(const char *serial) {
|
|||||||
if (!server_path) {
|
if (!server_path) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!sc_file_is_regular(server_path)) {
|
if (!is_regular_file(server_path)) {
|
||||||
LOGE("'%s' does not exist or is not a regular file\n", server_path);
|
LOGE("'%s' does not exist or is not a regular file\n", server_path);
|
||||||
free(server_path);
|
free(server_path);
|
||||||
return false;
|
return false;
|
||||||
@@ -427,7 +426,8 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
device_read_info(sc_socket device_socket, struct server_info *info) {
|
device_read_info(sc_socket device_socket, char *device_name,
|
||||||
|
struct sc_size *size) {
|
||||||
unsigned char buf[DEVICE_NAME_FIELD_LENGTH + 4];
|
unsigned char buf[DEVICE_NAME_FIELD_LENGTH + 4];
|
||||||
ssize_t r = net_recv_all(device_socket, buf, sizeof(buf));
|
ssize_t r = net_recv_all(device_socket, buf, sizeof(buf));
|
||||||
if (r < DEVICE_NAME_FIELD_LENGTH + 4) {
|
if (r < DEVICE_NAME_FIELD_LENGTH + 4) {
|
||||||
@@ -436,17 +436,19 @@ device_read_info(sc_socket device_socket, struct server_info *info) {
|
|||||||
}
|
}
|
||||||
// in case the client sends garbage
|
// in case the client sends garbage
|
||||||
buf[DEVICE_NAME_FIELD_LENGTH - 1] = '\0';
|
buf[DEVICE_NAME_FIELD_LENGTH - 1] = '\0';
|
||||||
memcpy(info->device_name, (char *) buf, sizeof(info->device_name));
|
// strcpy is safe here, since name contains at least
|
||||||
|
// DEVICE_NAME_FIELD_LENGTH bytes and strlen(buf) < DEVICE_NAME_FIELD_LENGTH
|
||||||
info->frame_size.width = (buf[DEVICE_NAME_FIELD_LENGTH] << 8)
|
strcpy(device_name, (char *) buf);
|
||||||
| buf[DEVICE_NAME_FIELD_LENGTH + 1];
|
size->width = (buf[DEVICE_NAME_FIELD_LENGTH] << 8)
|
||||||
info->frame_size.height = (buf[DEVICE_NAME_FIELD_LENGTH + 2] << 8)
|
| buf[DEVICE_NAME_FIELD_LENGTH + 1];
|
||||||
| buf[DEVICE_NAME_FIELD_LENGTH + 3];
|
size->height = (buf[DEVICE_NAME_FIELD_LENGTH + 2] << 8)
|
||||||
|
| buf[DEVICE_NAME_FIELD_LENGTH + 3];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
server_connect_to(struct server *server, struct server_info *info) {
|
server_connect_to(struct server *server, char *device_name,
|
||||||
|
struct sc_size *size) {
|
||||||
if (!server->tunnel_forward) {
|
if (!server->tunnel_forward) {
|
||||||
server->video_socket = net_accept(server->server_socket);
|
server->video_socket = net_accept(server->server_socket);
|
||||||
if (server->video_socket == SC_INVALID_SOCKET) {
|
if (server->video_socket == SC_INVALID_SOCKET) {
|
||||||
@@ -487,7 +489,7 @@ server_connect_to(struct server *server, struct server_info *info) {
|
|||||||
server->tunnel_enabled = false;
|
server->tunnel_enabled = false;
|
||||||
|
|
||||||
// The sockets will be closed on stop if device_read_info() fails
|
// The sockets will be closed on stop if device_read_info() fails
|
||||||
return device_read_info(server->video_socket, info);
|
return device_read_info(server->video_socket, device_name, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -14,12 +14,6 @@
|
|||||||
#include "util/net.h"
|
#include "util/net.h"
|
||||||
#include "util/thread.h"
|
#include "util/thread.h"
|
||||||
|
|
||||||
#define DEVICE_NAME_FIELD_LENGTH 64
|
|
||||||
struct server_info {
|
|
||||||
char device_name[DEVICE_NAME_FIELD_LENGTH];
|
|
||||||
struct sc_size frame_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct server {
|
struct server {
|
||||||
char *serial;
|
char *serial;
|
||||||
process_t process;
|
process_t process;
|
||||||
@@ -64,9 +58,12 @@ server_init(struct server *server);
|
|||||||
bool
|
bool
|
||||||
server_start(struct server *server, const struct server_params *params);
|
server_start(struct server *server, const struct server_params *params);
|
||||||
|
|
||||||
|
#define DEVICE_NAME_FIELD_LENGTH 64
|
||||||
// block until the communication with the server is established
|
// block until the communication with the server is established
|
||||||
|
// device_name must point to a buffer of at least DEVICE_NAME_FIELD_LENGTH bytes
|
||||||
bool
|
bool
|
||||||
server_connect_to(struct server *server, struct server_info *info);
|
server_connect_to(struct server *server, char *device_name,
|
||||||
|
struct sc_size *size);
|
||||||
|
|
||||||
// disconnect and kill the server process
|
// disconnect and kill the server process
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
#include "util/file.h"
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_file_executable_exists(const char *file) {
|
|
||||||
char *path = getenv("PATH");
|
|
||||||
if (!path)
|
|
||||||
return false;
|
|
||||||
path = strdup(path);
|
|
||||||
if (!path)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bool ret = false;
|
|
||||||
size_t file_len = strlen(file);
|
|
||||||
char *saveptr;
|
|
||||||
for (char *dir = strtok_r(path, ":", &saveptr); dir;
|
|
||||||
dir = strtok_r(NULL, ":", &saveptr)) {
|
|
||||||
size_t dir_len = strlen(dir);
|
|
||||||
char *fullpath = malloc(dir_len + file_len + 2);
|
|
||||||
if (!fullpath)
|
|
||||||
continue;
|
|
||||||
memcpy(fullpath, dir, dir_len);
|
|
||||||
fullpath[dir_len] = '/';
|
|
||||||
memcpy(fullpath + dir_len + 1, file, file_len + 1);
|
|
||||||
|
|
||||||
struct stat sb;
|
|
||||||
bool fullpath_executable = stat(fullpath, &sb) == 0 &&
|
|
||||||
sb.st_mode & S_IXUSR;
|
|
||||||
free(fullpath);
|
|
||||||
if (fullpath_executable) {
|
|
||||||
ret = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(path);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
sc_file_get_executable_path(void) {
|
|
||||||
// <https://stackoverflow.com/a/1024937/1987178>
|
|
||||||
#ifdef __linux__
|
|
||||||
char buf[PATH_MAX + 1]; // +1 for the null byte
|
|
||||||
ssize_t len = readlink("/proc/self/exe", buf, PATH_MAX);
|
|
||||||
if (len == -1) {
|
|
||||||
perror("readlink");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
buf[len] = '\0';
|
|
||||||
return strdup(buf);
|
|
||||||
#else
|
|
||||||
// in practice, we only need this feature for portable builds, only used on
|
|
||||||
// Windows, so we don't care implementing it for every platform
|
|
||||||
// (it's useful to have a working version on Linux for debugging though)
|
|
||||||
return NULL;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_file_is_regular(const char *path) {
|
|
||||||
struct stat path_stat;
|
|
||||||
|
|
||||||
if (stat(path, &path_stat)) {
|
|
||||||
perror("stat");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return S_ISREG(path_stat.st_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -3,13 +3,53 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
|
bool
|
||||||
|
search_executable(const char *file) {
|
||||||
|
char *path = getenv("PATH");
|
||||||
|
if (!path)
|
||||||
|
return false;
|
||||||
|
path = strdup(path);
|
||||||
|
if (!path)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool ret = false;
|
||||||
|
size_t file_len = strlen(file);
|
||||||
|
char *saveptr;
|
||||||
|
for (char *dir = strtok_r(path, ":", &saveptr); dir;
|
||||||
|
dir = strtok_r(NULL, ":", &saveptr)) {
|
||||||
|
size_t dir_len = strlen(dir);
|
||||||
|
char *fullpath = malloc(dir_len + file_len + 2);
|
||||||
|
if (!fullpath)
|
||||||
|
continue;
|
||||||
|
memcpy(fullpath, dir, dir_len);
|
||||||
|
fullpath[dir_len] = '/';
|
||||||
|
memcpy(fullpath + dir_len + 1, file, file_len + 1);
|
||||||
|
|
||||||
|
struct stat sb;
|
||||||
|
bool fullpath_executable = stat(fullpath, &sb) == 0 &&
|
||||||
|
sb.st_mode & S_IXUSR;
|
||||||
|
free(fullpath);
|
||||||
|
if (fullpath_executable) {
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
enum process_result
|
enum process_result
|
||||||
process_execute_redirect(const char *const argv[], pid_t *pid, int *pipe_stdin,
|
process_execute_redirect(const char *const argv[], pid_t *pid, int *pipe_stdin,
|
||||||
int *pipe_stdout, int *pipe_stderr) {
|
int *pipe_stdout, int *pipe_stderr) {
|
||||||
@@ -192,6 +232,37 @@ process_close(pid_t pid) {
|
|||||||
process_wait(pid, true); // ignore exit code
|
process_wait(pid, true); // ignore exit code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
get_executable_path(void) {
|
||||||
|
// <https://stackoverflow.com/a/1024937/1987178>
|
||||||
|
#ifdef __linux__
|
||||||
|
char buf[PATH_MAX + 1]; // +1 for the null byte
|
||||||
|
ssize_t len = readlink("/proc/self/exe", buf, PATH_MAX);
|
||||||
|
if (len == -1) {
|
||||||
|
perror("readlink");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
buf[len] = '\0';
|
||||||
|
return strdup(buf);
|
||||||
|
#else
|
||||||
|
// in practice, we only need this feature for portable builds, only used on
|
||||||
|
// Windows, so we don't care implementing it for every platform
|
||||||
|
// (it's useful to have a working version on Linux for debugging though)
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_regular_file(const char *path) {
|
||||||
|
struct stat path_stat;
|
||||||
|
|
||||||
|
if (stat(path, &path_stat)) {
|
||||||
|
perror("stat");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return S_ISREG(path_stat.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
read_pipe(int pipe, char *data, size_t len) {
|
read_pipe(int pipe, char *data, size_t len) {
|
||||||
return read(pipe, data, len);
|
return read(pipe, data, len);
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
#include "util/file.h"
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "util/log.h"
|
|
||||||
#include "util/str_util.h"
|
|
||||||
|
|
||||||
char *
|
|
||||||
sc_file_get_executable_path(void) {
|
|
||||||
HMODULE hModule = GetModuleHandleW(NULL);
|
|
||||||
if (!hModule) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
WCHAR buf[MAX_PATH + 1]; // +1 for the null byte
|
|
||||||
int len = GetModuleFileNameW(hModule, buf, MAX_PATH);
|
|
||||||
if (!len) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
buf[len] = '\0';
|
|
||||||
return utf8_from_wide_char(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_file_is_regular(const char *path) {
|
|
||||||
wchar_t *wide_path = utf8_to_wide_char(path);
|
|
||||||
if (!wide_path) {
|
|
||||||
LOGC("Could not allocate wide char string");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct _stat path_stat;
|
|
||||||
int r = _wstat(wide_path, &path_stat);
|
|
||||||
free(wide_path);
|
|
||||||
|
|
||||||
if (r) {
|
|
||||||
perror("stat");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return S_ISREG(path_stat.st_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "util/process.h"
|
#include "util/process.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/str_util.h"
|
#include "util/str_util.h"
|
||||||
@@ -173,6 +174,40 @@ process_close(HANDLE handle) {
|
|||||||
(void) closed;
|
(void) closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
get_executable_path(void) {
|
||||||
|
HMODULE hModule = GetModuleHandleW(NULL);
|
||||||
|
if (!hModule) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
WCHAR buf[MAX_PATH + 1]; // +1 for the null byte
|
||||||
|
int len = GetModuleFileNameW(hModule, buf, MAX_PATH);
|
||||||
|
if (!len) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
buf[len] = '\0';
|
||||||
|
return utf8_from_wide_char(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_regular_file(const char *path) {
|
||||||
|
wchar_t *wide_path = utf8_to_wide_char(path);
|
||||||
|
if (!wide_path) {
|
||||||
|
LOGC("Could not allocate wide char string");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _stat path_stat;
|
||||||
|
int r = _wstat(wide_path, &path_stat);
|
||||||
|
free(wide_path);
|
||||||
|
|
||||||
|
if (r) {
|
||||||
|
perror("stat");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return S_ISREG(path_stat.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
read_pipe(HANDLE pipe, char *data, size_t len) {
|
read_pipe(HANDLE pipe, char *data, size_t len) {
|
||||||
DWORD r;
|
DWORD r;
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
#include "file.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "util/log.h"
|
|
||||||
|
|
||||||
char *
|
|
||||||
sc_file_get_local_path(const char *name) {
|
|
||||||
char *executable_path = sc_file_get_executable_path();
|
|
||||||
if (!executable_path) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// dirname() does not work correctly everywhere, so get the parent
|
|
||||||
// directory manually.
|
|
||||||
// See <https://github.com/Genymobile/scrcpy/issues/2619>
|
|
||||||
char *p = strrchr(executable_path, SC_PATH_SEPARATOR);
|
|
||||||
if (!p) {
|
|
||||||
LOGE("Unexpected executable path: \"%s\" (it should contain a '%c')",
|
|
||||||
executable_path, SC_PATH_SEPARATOR);
|
|
||||||
free(executable_path);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*p = '\0'; // modify executable_path in place
|
|
||||||
char *dir = executable_path;
|
|
||||||
size_t dirlen = strlen(dir);
|
|
||||||
size_t namelen = strlen(name);
|
|
||||||
|
|
||||||
size_t len = dirlen + namelen + 2; // +2: '/' and '\0'
|
|
||||||
char *file_path = malloc(len);
|
|
||||||
if (!file_path) {
|
|
||||||
LOGE("Could not alloc path");
|
|
||||||
free(executable_path);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(file_path, dir, dirlen);
|
|
||||||
file_path[dirlen] = SC_PATH_SEPARATOR;
|
|
||||||
// namelen + 1 to copy the final '\0'
|
|
||||||
memcpy(&file_path[dirlen + 1], name, namelen + 1);
|
|
||||||
|
|
||||||
free(executable_path);
|
|
||||||
|
|
||||||
return file_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
#ifndef SC_FILE_H
|
|
||||||
#define SC_FILE_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
# define SC_PATH_SEPARATOR '\\'
|
|
||||||
#else
|
|
||||||
# define SC_PATH_SEPARATOR '/'
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
/**
|
|
||||||
* Indicate if an executable exists using $PATH
|
|
||||||
*
|
|
||||||
* In practice, it is only used to know if a package manager is available on
|
|
||||||
* the system. It is only implemented on Linux.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
sc_file_executable_exists(const char *file);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the absolute path of the executable (the scrcpy binary)
|
|
||||||
*
|
|
||||||
* The result must be freed by the caller using free(). It may return NULL on
|
|
||||||
* error.
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
sc_file_get_executable_path(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the absolute path of a file in the same directory as the executable
|
|
||||||
*
|
|
||||||
* The result must be freed by the caller using free(). It may return NULL on
|
|
||||||
* error.
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
sc_file_get_local_path(const char *name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicate if the file exists and is not a directory
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
sc_file_is_regular(const char *path);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -21,6 +21,47 @@ process_check_success(process_t proc, const char *name, bool close) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
get_local_file_path(const char *name) {
|
||||||
|
char *executable_path = get_executable_path();
|
||||||
|
if (!executable_path) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dirname() does not work correctly everywhere, so get the parent
|
||||||
|
// directory manually.
|
||||||
|
// See <https://github.com/Genymobile/scrcpy/issues/2619>
|
||||||
|
char *p = strrchr(executable_path, PATH_SEPARATOR);
|
||||||
|
if (!p) {
|
||||||
|
LOGE("Unexpected executable path: \"%s\" (it should contain a '%c')",
|
||||||
|
executable_path, PATH_SEPARATOR);
|
||||||
|
free(executable_path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = '\0'; // modify executable_path in place
|
||||||
|
char *dir = executable_path;
|
||||||
|
size_t dirlen = strlen(dir);
|
||||||
|
size_t namelen = strlen(name);
|
||||||
|
|
||||||
|
size_t len = dirlen + namelen + 2; // +2: '/' and '\0'
|
||||||
|
char *file_path = malloc(len);
|
||||||
|
if (!file_path) {
|
||||||
|
LOGE("Could not alloc path");
|
||||||
|
free(executable_path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(file_path, dir, dirlen);
|
||||||
|
file_path[dirlen] = PATH_SEPARATOR;
|
||||||
|
// namelen + 1 to copy the final '\0'
|
||||||
|
memcpy(&file_path[dirlen + 1], name, namelen + 1);
|
||||||
|
|
||||||
|
free(executable_path);
|
||||||
|
|
||||||
|
return file_path;
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
read_pipe_all(pipe_t pipe, char *data, size_t len) {
|
read_pipe_all(pipe_t pipe, char *data, size_t len) {
|
||||||
size_t copied = 0;
|
size_t copied = 0;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
// not needed here, but winsock2.h must never be included AFTER windows.h
|
// not needed here, but winsock2.h must never be included AFTER windows.h
|
||||||
# include <winsock2.h>
|
# include <winsock2.h>
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
|
# define PATH_SEPARATOR '\\'
|
||||||
# define PRIexitcode "lu"
|
# define PRIexitcode "lu"
|
||||||
// <https://stackoverflow.com/a/44383330/1987178>
|
// <https://stackoverflow.com/a/44383330/1987178>
|
||||||
# define PRIsizet "Iu"
|
# define PRIsizet "Iu"
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
|
# define PATH_SEPARATOR '/'
|
||||||
# define PRIsizet "zu"
|
# define PRIsizet "zu"
|
||||||
# define PRIexitcode "d"
|
# define PRIexitcode "d"
|
||||||
# define PROCESS_NONE -1
|
# define PROCESS_NONE -1
|
||||||
@@ -71,6 +73,26 @@ process_close(process_t pid);
|
|||||||
bool
|
bool
|
||||||
process_check_success(process_t proc, const char *name, bool close);
|
process_check_success(process_t proc, const char *name, bool close);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
// only used to find package manager, not implemented for Windows
|
||||||
|
bool
|
||||||
|
search_executable(const char *file);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// return the absolute path of the executable (the scrcpy binary)
|
||||||
|
// may be NULL on error; to be freed by free()
|
||||||
|
char *
|
||||||
|
get_executable_path(void);
|
||||||
|
|
||||||
|
// Return the absolute path of a file in the same directory as he executable.
|
||||||
|
// May be NULL on error. To be freed by free().
|
||||||
|
char *
|
||||||
|
get_local_file_path(const char *name);
|
||||||
|
|
||||||
|
// returns true if the file exists and is not a directory
|
||||||
|
bool
|
||||||
|
is_regular_file(const char *path);
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
read_pipe(pipe_t pipe, char *data, size_t len);
|
read_pipe(pipe_t pipe, char *data, size_t len);
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "util/strbuf.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
@@ -211,81 +209,3 @@ utf8_from_wide_char(const wchar_t *ws) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char *sc_str_wrap_lines(const char *input, unsigned columns, unsigned indent) {
|
|
||||||
assert(indent < columns);
|
|
||||||
|
|
||||||
struct sc_strbuf buf;
|
|
||||||
|
|
||||||
// The output string should not be much longer than the input string (just
|
|
||||||
// a few '\n' added), so this initial capacity should hopefully almost
|
|
||||||
// always avoid internal realloc() in string buffer
|
|
||||||
size_t cap = strlen(input) * 3 / 2;
|
|
||||||
|
|
||||||
if (!sc_strbuf_init(&buf, cap)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define APPEND(S,N) if (!sc_strbuf_append(&buf, S, N)) goto error
|
|
||||||
#define APPEND_CHAR(C) if (!sc_strbuf_append_char(&buf, C)) goto error
|
|
||||||
#define APPEND_N(C,N) if (!sc_strbuf_append_n(&buf, C, N)) goto error
|
|
||||||
#define APPEND_INDENT() if (indent) APPEND_N(' ', indent)
|
|
||||||
|
|
||||||
APPEND_INDENT();
|
|
||||||
|
|
||||||
// The last separator encountered, it must be inserted only conditionnaly,
|
|
||||||
// depending on the next token
|
|
||||||
char pending = 0;
|
|
||||||
|
|
||||||
// col tracks the current column in the current line
|
|
||||||
size_t col = indent;
|
|
||||||
while (*input) {
|
|
||||||
size_t sep_idx = strcspn(input, "\n ");
|
|
||||||
size_t new_col = col + sep_idx;
|
|
||||||
if (pending == ' ') {
|
|
||||||
// The pending space counts
|
|
||||||
++new_col;
|
|
||||||
}
|
|
||||||
bool wrap = new_col > columns;
|
|
||||||
|
|
||||||
char sep = input[sep_idx];
|
|
||||||
if (sep == ' ')
|
|
||||||
sep = ' ';
|
|
||||||
|
|
||||||
if (wrap) {
|
|
||||||
APPEND_CHAR('\n');
|
|
||||||
APPEND_INDENT();
|
|
||||||
col = indent;
|
|
||||||
} else if (pending) {
|
|
||||||
APPEND_CHAR(pending);
|
|
||||||
++col;
|
|
||||||
if (pending == '\n')
|
|
||||||
{
|
|
||||||
APPEND_INDENT();
|
|
||||||
col = indent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sep_idx) {
|
|
||||||
APPEND(input, sep_idx);
|
|
||||||
col += sep_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
pending = sep;
|
|
||||||
|
|
||||||
input += sep_idx;
|
|
||||||
if (*input != '\0') {
|
|
||||||
// Skip the separator
|
|
||||||
++input;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pending)
|
|
||||||
APPEND_CHAR(pending);
|
|
||||||
|
|
||||||
return buf.s;
|
|
||||||
|
|
||||||
error:
|
|
||||||
free(buf.s);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -62,12 +62,4 @@ char *
|
|||||||
utf8_from_wide_char(const wchar_t *s);
|
utf8_from_wide_char(const wchar_t *s);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrap input lines to fit in `columns` columns
|
|
||||||
*
|
|
||||||
* Break input lines at word boundaries (spaces) so that they fit in `columns`
|
|
||||||
* columns, left-indented by `indent` spaces.
|
|
||||||
*/
|
|
||||||
char *sc_str_wrap_lines(const char *input, unsigned columns, unsigned indent);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
#include "strbuf.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_strbuf_init(struct sc_strbuf *buf, size_t init_cap) {
|
|
||||||
buf->s = malloc(init_cap + 1); // +1 for '\0'
|
|
||||||
if (!buf->s) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf->len = 0;
|
|
||||||
buf->cap = init_cap;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
sc_strbuf_reserve(struct sc_strbuf *buf, size_t len) {
|
|
||||||
if (buf->len + len > buf->cap) {
|
|
||||||
size_t new_cap = buf->cap * 3 / 2 + len;
|
|
||||||
char *s = realloc(buf->s, new_cap + 1); // +1 for '\0'
|
|
||||||
if (!s) {
|
|
||||||
// Leave the old buf->s
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
buf->s = s;
|
|
||||||
buf->cap = new_cap;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_strbuf_append(struct sc_strbuf *buf, const char *s, size_t len) {
|
|
||||||
assert(s);
|
|
||||||
assert(*s);
|
|
||||||
assert(strlen(s) >= len);
|
|
||||||
if (!sc_strbuf_reserve(buf, len)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&buf->s[buf->len], s, len);
|
|
||||||
buf->len += len;
|
|
||||||
buf->s[buf->len] = '\0';
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_strbuf_append_char(struct sc_strbuf *buf, const char c) {
|
|
||||||
if (!sc_strbuf_reserve(buf, 1)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf->s[buf->len] = c;
|
|
||||||
buf->len ++;
|
|
||||||
buf->s[buf->len] = '\0';
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_strbuf_append_n(struct sc_strbuf *buf, const char c, size_t n) {
|
|
||||||
if (!sc_strbuf_reserve(buf, n)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&buf->s[buf->len], c, n);
|
|
||||||
buf->len += n;
|
|
||||||
buf->s[buf->len] = '\0';
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
sc_strbuf_shrink(struct sc_strbuf *buf) {
|
|
||||||
assert(buf->len <= buf->cap);
|
|
||||||
if (buf->len != buf->cap) {
|
|
||||||
char *s = realloc(buf->s, buf->len + 1); // +1 for '\0'
|
|
||||||
assert(s); // decreasing the size may not fail
|
|
||||||
buf->s = s;
|
|
||||||
buf->cap = buf->len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#ifndef SC_STRBUF_H
|
|
||||||
#define SC_STRBUF_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
struct sc_strbuf {
|
|
||||||
char *s;
|
|
||||||
size_t len;
|
|
||||||
size_t cap;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the string buffer
|
|
||||||
*
|
|
||||||
* `buf->s` must be manually freed by the caller.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
sc_strbuf_init(struct sc_strbuf *buf, size_t init_cap);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append a string
|
|
||||||
*
|
|
||||||
* Append `len` characters from `s` to the buffer.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
sc_strbuf_append(struct sc_strbuf *buf, const char *s, size_t len);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append a char
|
|
||||||
*
|
|
||||||
* Append a single character to the buffer.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
sc_strbuf_append_char(struct sc_strbuf *buf, const char c);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append a char `n` times
|
|
||||||
*
|
|
||||||
* Append the same characters `n` times to the buffer.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
sc_strbuf_append_n(struct sc_strbuf *buf, const char c, size_t n);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append a NUL-terminated string
|
|
||||||
*/
|
|
||||||
static inline bool
|
|
||||||
sc_strbuf_append_str(struct sc_strbuf *buf, const char *s) {
|
|
||||||
return sc_strbuf_append(buf, s, strlen(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append a static string
|
|
||||||
*
|
|
||||||
* Append a string whose size is known at compile time (for
|
|
||||||
* example a string literal).
|
|
||||||
*/
|
|
||||||
#define sc_strbuf_append_staticstr(BUF, S) \
|
|
||||||
sc_strbuf_append(BUF, S, sizeof(S) - 1)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shrink the buffer capacity to its current length
|
|
||||||
*
|
|
||||||
* This resizes `buf->s` to fit the content.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
sc_strbuf_shrink(struct sc_strbuf *buf);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#include "term.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
# include <windows.h>
|
|
||||||
#else
|
|
||||||
# include <unistd.h>
|
|
||||||
# include <sys/ioctl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool
|
|
||||||
sc_term_get_size(unsigned *rows, unsigned *cols) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
||||||
|
|
||||||
bool ok =
|
|
||||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
|
||||||
if (!ok) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rows) {
|
|
||||||
assert(csbi.srWindow.Bottom >= csbi.srWindow.Top);
|
|
||||||
*rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cols) {
|
|
||||||
assert(csbi.srWindow.Right >= csbi.srWindow.Left);
|
|
||||||
*cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
struct winsize ws;
|
|
||||||
int r = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
|
|
||||||
if (r == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rows) {
|
|
||||||
*rows = ws.ws_row;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cols) {
|
|
||||||
*cols = ws.ws_col;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#ifndef SC_TERM_H
|
|
||||||
#define SC_TERM_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the terminal dimensions
|
|
||||||
*
|
|
||||||
* Return false if the dimensions could not be retrieved.
|
|
||||||
*
|
|
||||||
* Otherwise, return true, and:
|
|
||||||
* - if `rows` is not NULL, then the number of rows is written to `*rows`.
|
|
||||||
* - if `columns` is not NULL, then the number of columns is written to
|
|
||||||
* `*columns`.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
sc_term_get_size(unsigned *rows, unsigned *cols);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
#ifndef SC_TICK_H
|
#ifndef SC_TICK_H
|
||||||
#define SC_TICK_H
|
#define SC_TICK_H
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef int64_t sc_tick;
|
typedef int64_t sc_tick;
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
#include "common.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "util/strbuf.h"
|
|
||||||
|
|
||||||
static void test_strbuf_simple(void) {
|
|
||||||
struct sc_strbuf buf;
|
|
||||||
bool ok = sc_strbuf_init(&buf, 10);
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
ok = sc_strbuf_append_staticstr(&buf, "Hello");
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
ok = sc_strbuf_append_char(&buf, ' ');
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
ok = sc_strbuf_append_staticstr(&buf, "world");
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
ok = sc_strbuf_append_staticstr(&buf, "!\n");
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
ok = sc_strbuf_append_staticstr(&buf, "This is a test");
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
ok = sc_strbuf_append_n(&buf, '.', 3);
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
assert(!strcmp(buf.s, "Hello world!\nThis is a test..."));
|
|
||||||
|
|
||||||
sc_strbuf_shrink(&buf);
|
|
||||||
assert(buf.len == buf.cap);
|
|
||||||
assert(!strcmp(buf.s, "Hello world!\nThis is a test..."));
|
|
||||||
|
|
||||||
free(buf.s);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
(void) argc;
|
|
||||||
(void) argv;
|
|
||||||
|
|
||||||
test_strbuf_simple();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -299,44 +299,6 @@ static void test_strlist_contains(void) {
|
|||||||
assert(strlist_contains("xyz", '\0', "xyz"));
|
assert(strlist_contains("xyz", '\0', "xyz"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_wrap_lines(void) {
|
|
||||||
const char *s = "This is a text to test line wrapping. The lines must be "
|
|
||||||
"wrapped at a space or a line break.\n"
|
|
||||||
"\n"
|
|
||||||
"This rectangle must remains a rectangle because it is "
|
|
||||||
"drawn in lines having lengths lower than the specified "
|
|
||||||
"number of columns:\n"
|
|
||||||
" +----+\n"
|
|
||||||
" | |\n"
|
|
||||||
" +----+\n";
|
|
||||||
|
|
||||||
// |---- 1 1 2 2|
|
|
||||||
// |0 5 0 5 0 3| <-- 24 columns
|
|
||||||
const char *expected = " This is a text to\n"
|
|
||||||
" test line wrapping.\n"
|
|
||||||
" The lines must be\n"
|
|
||||||
" wrapped at a space\n"
|
|
||||||
" or a line break.\n"
|
|
||||||
" \n"
|
|
||||||
" This rectangle must\n"
|
|
||||||
" remains a rectangle\n"
|
|
||||||
" because it is drawn\n"
|
|
||||||
" in lines having\n"
|
|
||||||
" lengths lower than\n"
|
|
||||||
" the specified number\n"
|
|
||||||
" of columns:\n"
|
|
||||||
" +----+\n"
|
|
||||||
" | |\n"
|
|
||||||
" +----+\n";
|
|
||||||
|
|
||||||
char *formatted = sc_str_wrap_lines(s, 24, 4);
|
|
||||||
assert(formatted);
|
|
||||||
|
|
||||||
assert(!strcmp(formatted, expected));
|
|
||||||
|
|
||||||
free(formatted);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
(void) argc;
|
(void) argc;
|
||||||
(void) argv;
|
(void) argv;
|
||||||
@@ -355,6 +317,5 @@ int main(int argc, char *argv[]) {
|
|||||||
test_parse_integers();
|
test_parse_integers();
|
||||||
test_parse_integer_with_suffix();
|
test_parse_integer_with_suffix();
|
||||||
test_strlist_contains();
|
test_strlist_contains();
|
||||||
test_wrap_lines();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user