Compare commits
26 Commits
fix_build_
...
windows_dp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6312ea9c98 | ||
|
|
feb250a973 | ||
|
|
d049671908 | ||
|
|
0685c491cd | ||
|
|
892cfe943e | ||
|
|
29570ee819 | ||
|
|
cfcbc2ac21 | ||
|
|
2cb4e04209 | ||
|
|
878ffffc36 | ||
|
|
f0361fc8b3 | ||
|
|
b5d4ec61fc | ||
|
|
3ada5c51bc | ||
|
|
09c55b0f93 | ||
|
|
682a691173 | ||
|
|
ddb9396743 | ||
|
|
cabcbc2b15 | ||
|
|
80fe12a95f | ||
|
|
099c546580 | ||
|
|
dca2c5f94f | ||
|
|
90cf956f57 | ||
|
|
36c8778d2d | ||
|
|
ae90ef22db | ||
|
|
d80bc25eba | ||
|
|
daa06abd34 | ||
|
|
94702a4309 | ||
|
|
65fbec9643 |
@@ -49,9 +49,11 @@ conf.set('_XOPEN_SOURCE', '700')
|
||||
conf.set('_GNU_SOURCE', true)
|
||||
|
||||
if host_machine.system() == 'windows'
|
||||
windows = import('windows')
|
||||
src += [
|
||||
'src/sys/win/file.c',
|
||||
'src/sys/win/process.c',
|
||||
windows.compile_resources('scrcpy-windows.rc'),
|
||||
]
|
||||
conf.set('_WIN32_WINNT', '0x0600')
|
||||
conf.set('WINVER', '0x0600')
|
||||
@@ -80,14 +82,16 @@ endif
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
if not get_option('crossbuild_windows')
|
||||
crossbuild_windows = meson.is_cross_build() and host_machine.system() == 'windows'
|
||||
|
||||
if not crossbuild_windows
|
||||
|
||||
# native build
|
||||
dependencies = [
|
||||
dependency('libavformat'),
|
||||
dependency('libavcodec'),
|
||||
dependency('libavformat', version: '>= 57.33'),
|
||||
dependency('libavcodec', version: '>= 57.37'),
|
||||
dependency('libavutil'),
|
||||
dependency('sdl2'),
|
||||
dependency('sdl2', version: '>= 2.0.5'),
|
||||
]
|
||||
|
||||
if v4l2_support
|
||||
|
||||
9
app/scrcpy-windows.manifest
Normal file
9
app/scrcpy-windows.manifest
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<asmv3:application>
|
||||
<asmv3:windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
||||
23
app/scrcpy-windows.rc
Normal file
23
app/scrcpy-windows.rc
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <winuser.h>
|
||||
|
||||
0 ICON "../data/icon.ico"
|
||||
1 RT_MANIFEST "scrcpy-windows.manifest"
|
||||
2 VERSIONINFO
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904E4"
|
||||
BEGIN
|
||||
VALUE "FileDescription", "Display and control your Android device"
|
||||
VALUE "InternalName", "scrcpy"
|
||||
VALUE "LegalCopyright", "Romain Vimont, Genymobile"
|
||||
VALUE "OriginalFilename", "scrcpy.exe"
|
||||
VALUE "ProductName", "scrcpy"
|
||||
VALUE "ProductVersion", "1.21"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1252
|
||||
END
|
||||
END
|
||||
10
app/scrcpy.1
10
app/scrcpy.1
@@ -413,11 +413,15 @@ Push file to device (see \fB\-\-push\-target\fR)
|
||||
|
||||
.TP
|
||||
.B ADB
|
||||
Specify the path to adb.
|
||||
Path to adb.
|
||||
|
||||
.TP
|
||||
.B SCRCPY_ICON_PATH
|
||||
Path to the program icon.
|
||||
|
||||
.TP
|
||||
.B SCRCPY_SERVER_PATH
|
||||
Specify the path to server binary.
|
||||
Path to the server binary.
|
||||
|
||||
|
||||
.SH AUTHORS
|
||||
@@ -442,7 +446,7 @@ Copyright \(co 2018 Genymobile
|
||||
Genymobile
|
||||
.UE
|
||||
|
||||
Copyright \(co 2018\-2020
|
||||
Copyright \(co 2018\-2021
|
||||
.MT rom@rom1v.com
|
||||
Romain Vimont
|
||||
.ME
|
||||
|
||||
@@ -373,7 +373,7 @@ bool
|
||||
sc_aoa_start(struct sc_aoa *aoa) {
|
||||
LOGD("Starting AOA thread");
|
||||
|
||||
bool ok = sc_thread_create(&aoa->thread, run_aoa_thread, "aoa_thread", aoa);
|
||||
bool ok = sc_thread_create(&aoa->thread, run_aoa_thread, "scrcpy-aoa", aoa);
|
||||
if (!ok) {
|
||||
LOGC("Could not start AOA thread");
|
||||
return false;
|
||||
|
||||
@@ -71,6 +71,11 @@ struct sc_shortcut {
|
||||
const char *text;
|
||||
};
|
||||
|
||||
struct sc_envvar {
|
||||
const char *name;
|
||||
const char *text;
|
||||
};
|
||||
|
||||
struct sc_getopt_adapter {
|
||||
char *optstring;
|
||||
struct option *longopts;
|
||||
@@ -585,6 +590,21 @@ static const struct sc_shortcut shortcuts[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sc_envvar envvars[] = {
|
||||
{
|
||||
.name = "ADB",
|
||||
.text = "Path to adb executable",
|
||||
},
|
||||
{
|
||||
.name = "SCRCPY_ICON_PATH",
|
||||
.text = "Path to the program icon",
|
||||
},
|
||||
{
|
||||
.name = "SCRCPY_SERVER_PATH",
|
||||
.text = "Path to the server binary",
|
||||
}
|
||||
};
|
||||
|
||||
static char *
|
||||
sc_getopt_adapter_create_optstring(void) {
|
||||
struct sc_strbuf buf;
|
||||
@@ -678,7 +698,7 @@ sc_getopt_adapter_init(struct sc_getopt_adapter *adapter) {
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
sc_getopt_adapter_destroy(struct sc_getopt_adapter *adapter) {
|
||||
@@ -776,7 +796,7 @@ print_shortcuts_intro(unsigned cols) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s\n", intro);
|
||||
printf("\n%s\n", intro);
|
||||
free(intro);
|
||||
}
|
||||
|
||||
@@ -804,6 +824,23 @@ print_shortcut(const struct sc_shortcut *shortcut, unsigned cols) {
|
||||
free(text);
|
||||
}
|
||||
|
||||
static void
|
||||
print_envvar(const struct sc_envvar *envvar, unsigned cols) {
|
||||
assert(cols > 8); // sc_str_wrap_lines() requires indent < columns
|
||||
assert(envvar->name);
|
||||
assert(envvar->text);
|
||||
|
||||
printf("\n %s\n", envvar->name);
|
||||
char *text = sc_str_wrap_lines(envvar->text, cols, 8);
|
||||
if (!text) {
|
||||
printf("<ERROR>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s\n", text);
|
||||
free(text);
|
||||
}
|
||||
|
||||
void
|
||||
scrcpy_print_usage(const char *arg0) {
|
||||
#define SC_TERM_COLS_DEFAULT 80
|
||||
@@ -831,11 +868,17 @@ scrcpy_print_usage(const char *arg0) {
|
||||
}
|
||||
|
||||
// Print shortcuts section
|
||||
printf("\nShortcuts:\n\n");
|
||||
printf("\nShortcuts:\n");
|
||||
print_shortcuts_intro(cols);
|
||||
for (size_t i = 0; i < ARRAY_LEN(shortcuts); ++i) {
|
||||
print_shortcut(&shortcuts[i], cols);
|
||||
}
|
||||
|
||||
// Print environment variables section
|
||||
printf("\nEnvironment variables:\n");
|
||||
for (size_t i = 0; i < ARRAY_LEN(envvars); ++i) {
|
||||
print_envvar(&envvars[i], cols);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
|
||||
@@ -35,15 +35,6 @@
|
||||
# define SCRCPY_LAVF_HAS_AVFORMATCONTEXT_URL
|
||||
#endif
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 5)
|
||||
// <https://wiki.libsdl.org/SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH>
|
||||
# define SCRCPY_SDL_HAS_HINT_MOUSE_FOCUS_CLICKTHROUGH
|
||||
// <https://wiki.libsdl.org/SDL_GetDisplayUsableBounds>
|
||||
# define SCRCPY_SDL_HAS_GET_DISPLAY_USABLE_BOUNDS
|
||||
// <https://wiki.libsdl.org/SDL_WindowFlags>
|
||||
# define SCRCPY_SDL_HAS_WINDOW_ALWAYS_ON_TOP
|
||||
#endif
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 6)
|
||||
// <https://github.com/libsdl-org/SDL/commit/d7a318de563125e5bb465b1000d6bc9576fbc6fc>
|
||||
# define SCRCPY_SDL_HAS_HINT_TOUCH_MOUSE_EVENTS
|
||||
|
||||
@@ -41,7 +41,7 @@ static const char *const android_motionevent_action_labels[] = {
|
||||
"pointer-up",
|
||||
"hover-move",
|
||||
"scroll",
|
||||
"hover-enter"
|
||||
"hover-enter",
|
||||
"hover-exit",
|
||||
"btn-press",
|
||||
"btn-release",
|
||||
@@ -69,7 +69,7 @@ write_position(uint8_t *buf, const struct sc_position *position) {
|
||||
buffer_write16be(&buf[10], position->screen_size.height);
|
||||
}
|
||||
|
||||
// write length (2 bytes) + string (non nul-terminated)
|
||||
// write length (4 bytes) + string (non null-terminated)
|
||||
static size_t
|
||||
write_string(const char *utf8, size_t max_len, unsigned char *buf) {
|
||||
size_t len = sc_str_utf8_truncation_index(utf8, max_len);
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
#define CONTROL_MSG_MAX_SIZE (1 << 18) // 256k
|
||||
|
||||
#define CONTROL_MSG_INJECT_TEXT_MAX_LENGTH 300
|
||||
// type: 1 byte; paste flag: 1 byte; length: 4 bytes
|
||||
#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (CONTROL_MSG_MAX_SIZE - 6)
|
||||
// type: 1 byte; sequence: 8 bytes; paste flag: 1 byte; length: 4 bytes
|
||||
#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (CONTROL_MSG_MAX_SIZE - 14)
|
||||
|
||||
#define POINTER_ID_MOUSE UINT64_C(-1)
|
||||
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-2)
|
||||
|
||||
@@ -110,7 +110,7 @@ controller_start(struct controller *controller) {
|
||||
LOGD("Starting controller thread");
|
||||
|
||||
bool ok = sc_thread_create(&controller->thread, run_controller,
|
||||
"controller", controller);
|
||||
"scrcpy-ctl", controller);
|
||||
if (!ok) {
|
||||
LOGC("Could not start controller thread");
|
||||
return false;
|
||||
|
||||
@@ -46,6 +46,8 @@ decoder_open(struct decoder *decoder, const AVCodec *codec) {
|
||||
return false;
|
||||
}
|
||||
|
||||
decoder->codec_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
|
||||
|
||||
if (avcodec_open2(decoder->codec_ctx, codec, NULL) < 0) {
|
||||
LOGE("Could not open codec");
|
||||
avcodec_free_context(&decoder->codec_ctx);
|
||||
|
||||
@@ -154,7 +154,7 @@ file_handler_start(struct file_handler *file_handler) {
|
||||
LOGD("Starting file_handler thread");
|
||||
|
||||
bool ok = sc_thread_create(&file_handler->thread, run_file_handler,
|
||||
"file_handler", file_handler);
|
||||
"scrcpy-file", file_handler);
|
||||
if (!ok) {
|
||||
LOGC("Could not start file_handler thread");
|
||||
return false;
|
||||
|
||||
@@ -108,7 +108,7 @@ fps_counter_start(struct fps_counter *counter) {
|
||||
// same thread, no need to lock
|
||||
if (!counter->thread_started) {
|
||||
bool ok = sc_thread_create(&counter->thread, run_fps_counter,
|
||||
"fps counter", counter);
|
||||
"scrcpy-fps", counter);
|
||||
if (!ok) {
|
||||
LOGE("Could not start FPS counter thread");
|
||||
return false;
|
||||
|
||||
@@ -111,8 +111,8 @@ bool
|
||||
receiver_start(struct receiver *receiver) {
|
||||
LOGD("Starting receiver thread");
|
||||
|
||||
bool ok = sc_thread_create(&receiver->thread, run_receiver, "receiver",
|
||||
receiver);
|
||||
bool ok = sc_thread_create(&receiver->thread, run_receiver,
|
||||
"scrcpy-receiver", receiver);
|
||||
if (!ok) {
|
||||
LOGC("Could not start receiver thread");
|
||||
return false;
|
||||
|
||||
@@ -287,8 +287,8 @@ recorder_open(struct recorder *recorder, const AVCodec *input_codec) {
|
||||
}
|
||||
|
||||
LOGD("Starting recorder thread");
|
||||
ok = sc_thread_create(&recorder->thread, run_recorder, "recorder",
|
||||
recorder);
|
||||
ok = sc_thread_create(&recorder->thread, run_recorder, "scrcpy-recorder",
|
||||
recorder);
|
||||
if (!ok) {
|
||||
LOGC("Could not start recorder thread");
|
||||
goto error_avio_close;
|
||||
|
||||
@@ -94,12 +94,10 @@ sdl_set_hints(const char *render_driver) {
|
||||
LOGW("Could not enable linear filtering");
|
||||
}
|
||||
|
||||
#ifdef SCRCPY_SDL_HAS_HINT_MOUSE_FOCUS_CLICKTHROUGH
|
||||
// Handle a click to gain focus as any other click
|
||||
if (!SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1")) {
|
||||
LOGW("Could not enable mouse focus clickthrough");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SCRCPY_SDL_HAS_HINT_TOUCH_MOUSE_EVENTS
|
||||
// Disable synthetic mouse events from touch events
|
||||
|
||||
@@ -64,12 +64,7 @@ set_window_size(struct screen *screen, struct sc_size new_size) {
|
||||
static bool
|
||||
get_preferred_display_bounds(struct sc_size *bounds) {
|
||||
SDL_Rect rect;
|
||||
#ifdef SCRCPY_SDL_HAS_GET_DISPLAY_USABLE_BOUNDS
|
||||
# define GET_DISPLAY_BOUNDS(i, r) SDL_GetDisplayUsableBounds((i), (r))
|
||||
#else
|
||||
# define GET_DISPLAY_BOUNDS(i, r) SDL_GetDisplayBounds((i), (r))
|
||||
#endif
|
||||
if (GET_DISPLAY_BOUNDS(0, &rect)) {
|
||||
if (SDL_GetDisplayUsableBounds(0, &rect)) {
|
||||
LOGW("Could not get display usable bounds: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
@@ -394,12 +389,7 @@ screen_init(struct screen *screen, const struct screen_params *params) {
|
||||
| SDL_WINDOW_RESIZABLE
|
||||
| SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
if (params->always_on_top) {
|
||||
#ifdef SCRCPY_SDL_HAS_WINDOW_ALWAYS_ON_TOP
|
||||
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
|
||||
#else
|
||||
LOGW("The 'always on top' flag is not available "
|
||||
"(compile with SDL >= 2.0.5 to enable it)");
|
||||
#endif
|
||||
}
|
||||
if (params->window_borderless) {
|
||||
window_flags |= SDL_WINDOW_BORDERLESS;
|
||||
|
||||
@@ -232,7 +232,7 @@ execute_server(struct sc_server *server,
|
||||
ADD_PARAM("power_off_on_close=%s", STRBOOL(params->power_off_on_close));
|
||||
}
|
||||
if (!params->clipboard_autosync) {
|
||||
// By defaut, clipboard_autosync is true
|
||||
// By default, clipboard_autosync is true
|
||||
ADD_PARAM("clipboard_autosync=%s", STRBOOL(params->clipboard_autosync));
|
||||
}
|
||||
|
||||
@@ -388,6 +388,7 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
|
||||
assert(tunnel->enabled);
|
||||
|
||||
const char *serial = server->params.serial;
|
||||
bool control = server->params.control;
|
||||
|
||||
sc_socket video_socket = SC_SOCKET_NONE;
|
||||
sc_socket control_socket = SC_SOCKET_NONE;
|
||||
@@ -397,9 +398,12 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
control_socket = net_accept_intr(&server->intr, tunnel->server_socket);
|
||||
if (control_socket == SC_SOCKET_NONE) {
|
||||
goto fail;
|
||||
if (control) {
|
||||
control_socket =
|
||||
net_accept_intr(&server->intr, tunnel->server_socket);
|
||||
if (control_socket == SC_SOCKET_NONE) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint32_t tunnel_host = server->params.tunnel_host;
|
||||
@@ -420,15 +424,18 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// we know that the device is listening, we don't need several attempts
|
||||
control_socket = net_socket();
|
||||
if (control_socket == SC_SOCKET_NONE) {
|
||||
goto fail;
|
||||
}
|
||||
bool ok = net_connect_intr(&server->intr, control_socket, tunnel_host,
|
||||
tunnel_port);
|
||||
if (!ok) {
|
||||
goto fail;
|
||||
if (control) {
|
||||
// we know that the device is listening, we don't need several
|
||||
// attempts
|
||||
control_socket = net_socket();
|
||||
if (control_socket == SC_SOCKET_NONE) {
|
||||
goto fail;
|
||||
}
|
||||
bool ok = net_connect_intr(&server->intr, control_socket,
|
||||
tunnel_host, tunnel_port);
|
||||
if (!ok) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,7 +449,7 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
|
||||
}
|
||||
|
||||
assert(video_socket != SC_SOCKET_NONE);
|
||||
assert(control_socket != SC_SOCKET_NONE);
|
||||
assert(!control || control_socket != SC_SOCKET_NONE);
|
||||
|
||||
server->video_socket = video_socket;
|
||||
server->control_socket = control_socket;
|
||||
@@ -756,6 +763,17 @@ run_server(void *data) {
|
||||
}
|
||||
sc_mutex_unlock(&server->mutex);
|
||||
|
||||
// Interrupt sockets to wake up socket blocking calls on the server
|
||||
assert(server->video_socket != SC_SOCKET_NONE);
|
||||
net_interrupt(server->video_socket);
|
||||
net_close(server->video_socket);
|
||||
|
||||
if (server->control_socket != SC_SOCKET_NONE) {
|
||||
// There is no control_socket if --no-control is set
|
||||
net_interrupt(server->control_socket);
|
||||
net_close(server->control_socket);
|
||||
}
|
||||
|
||||
// Give some delay for the server to terminate properly
|
||||
#define WATCHDOG_DELAY SC_TICK_FROM_SEC(1)
|
||||
sc_tick deadline = sc_tick_now() + WATCHDOG_DELAY;
|
||||
@@ -786,7 +804,8 @@ error_connection_failed:
|
||||
|
||||
bool
|
||||
sc_server_start(struct sc_server *server) {
|
||||
bool ok = sc_thread_create(&server->thread, run_server, "server", server);
|
||||
bool ok =
|
||||
sc_thread_create(&server->thread, run_server, "scrcpy-server", server);
|
||||
if (!ok) {
|
||||
LOGE("Could not create server thread");
|
||||
return false;
|
||||
|
||||
@@ -284,7 +284,8 @@ bool
|
||||
stream_start(struct stream *stream) {
|
||||
LOGD("Starting stream thread");
|
||||
|
||||
bool ok = sc_thread_create(&stream->thread, run_stream, "stream", stream);
|
||||
bool ok =
|
||||
sc_thread_create(&stream->thread, run_stream, "scrcpy-stream", stream);
|
||||
if (!ok) {
|
||||
LOGC("Could not start stream thread");
|
||||
return false;
|
||||
|
||||
@@ -119,6 +119,13 @@ sc_process_execute_p(const char *const argv[], sc_pid *pid, unsigned flags,
|
||||
|
||||
close(internal[0]);
|
||||
enum sc_process_result err;
|
||||
|
||||
// Somehow SDL masks many signals - undo them for other processes
|
||||
// https://github.com/libsdl-org/SDL/blob/release-2.0.18/src/thread/pthread/SDL_systhread.c#L167
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||
|
||||
if (fcntl(internal[1], F_SETFD, FD_CLOEXEC) == 0) {
|
||||
execvp(argv[0], (char *const *) argv);
|
||||
perror("exec");
|
||||
|
||||
@@ -64,7 +64,7 @@ sc_process_observer_init(struct sc_process_observer *observer, sc_pid pid,
|
||||
observer->listener_userdata = listener_userdata;
|
||||
observer->terminated = false;
|
||||
|
||||
ok = sc_thread_create(&observer->thread, run_observer, "process_observer",
|
||||
ok = sc_thread_create(&observer->thread, run_observer, "scrcpy-proc",
|
||||
observer);
|
||||
if (!ok) {
|
||||
sc_cond_destroy(&observer->cond_terminated);
|
||||
|
||||
@@ -240,7 +240,7 @@ sc_str_wrap_lines(const char *input, unsigned columns, unsigned indent) {
|
||||
|
||||
APPEND_INDENT();
|
||||
|
||||
// The last separator encountered, it must be inserted only conditionnaly,
|
||||
// The last separator encountered, it must be inserted only conditionally,
|
||||
// depending on the next token
|
||||
char pending = 0;
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
bool
|
||||
sc_thread_create(sc_thread *thread, sc_thread_fn fn, const char *name,
|
||||
void *userdata) {
|
||||
// The thread name length is limited on some systems. Never use a name
|
||||
// longer than 16 bytes (including the final '\0')
|
||||
assert(strlen(name) <= 15);
|
||||
|
||||
SDL_Thread *sdl_thread = SDL_CreateThread(fn, name, userdata);
|
||||
if (!sdl_thread) {
|
||||
LOG_OOM();
|
||||
|
||||
@@ -1,16 +1,55 @@
|
||||
#include "tick.h"
|
||||
|
||||
#include <SDL2/SDL_timer.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
sc_tick
|
||||
sc_tick_now(void) {
|
||||
// SDL_GetTicks() resolution is in milliseconds, but sc_tick are expressed
|
||||
// in microseconds to store PTS without precision loss.
|
||||
//
|
||||
// As an alternative, SDL_GetPerformanceCounter() and
|
||||
// SDL_GetPerformanceFrequency() could be used, but:
|
||||
// - the conversions (avoiding overflow) are expansive, since the
|
||||
// frequency is not known at compile time;
|
||||
// - in practice, we don't need more precision for now.
|
||||
return (sc_tick) SDL_GetTicks() * 1000;
|
||||
#ifndef _WIN32
|
||||
// Maximum sc_tick precision (microsecond)
|
||||
struct timespec ts;
|
||||
int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (ret) {
|
||||
abort();
|
||||
}
|
||||
|
||||
return SC_TICK_FROM_SEC(ts.tv_sec) + SC_TICK_FROM_NS(ts.tv_nsec);
|
||||
#else
|
||||
LARGE_INTEGER c;
|
||||
|
||||
// On systems that run Windows XP or later, the function will always
|
||||
// succeed and will thus never return zero.
|
||||
// <https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter>
|
||||
// <https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancefrequency>
|
||||
|
||||
BOOL ok = QueryPerformanceCounter(&c);
|
||||
assert(ok);
|
||||
(void) ok;
|
||||
|
||||
LONGLONG counter = c.QuadPart;
|
||||
|
||||
static LONGLONG frequency;
|
||||
if (!frequency) {
|
||||
// Initialize on first call
|
||||
LARGE_INTEGER f;
|
||||
ok = QueryPerformanceFrequency(&f);
|
||||
assert(ok);
|
||||
frequency = f.QuadPart;
|
||||
assert(frequency);
|
||||
}
|
||||
|
||||
if (frequency % SC_TICK_FREQ == 0) {
|
||||
// Expected case (typically frequency = 10000000, i.e. 100ns precision)
|
||||
sc_tick div = frequency / SC_TICK_FREQ;
|
||||
return SC_TICK_FROM_US(counter / div);
|
||||
}
|
||||
|
||||
// Split the division to avoid overflow
|
||||
sc_tick secs = SC_TICK_FROM_SEC(counter / frequency);
|
||||
sc_tick subsec = SC_TICK_FREQ * (counter % frequency) / frequency;
|
||||
return secs + subsec;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ typedef int64_t sc_tick;
|
||||
#define SC_TICK_FREQ 1000000 // microsecond
|
||||
|
||||
// To be adapted if SC_TICK_FREQ changes
|
||||
#define SC_TICK_TO_NS(tick) ((tick) * 1000)
|
||||
#define SC_TICK_TO_US(tick) (tick)
|
||||
#define SC_TICK_TO_MS(tick) ((tick) / 1000)
|
||||
#define SC_TICK_TO_SEC(tick) ((tick) / 1000000)
|
||||
#define SC_TICK_FROM_NS(ns) ((ns) / 1000)
|
||||
#define SC_TICK_FROM_US(us) (us)
|
||||
#define SC_TICK_FROM_MS(ms) ((ms) * 1000)
|
||||
#define SC_TICK_FROM_SEC(sec) ((sec) * 1000000)
|
||||
|
||||
@@ -272,7 +272,7 @@ sc_v4l2_sink_open(struct sc_v4l2_sink *vs) {
|
||||
vs->stopped = false;
|
||||
|
||||
LOGD("Starting v4l2 thread");
|
||||
ok = sc_thread_create(&vs->thread, run_v4l2_sink, "v4l2", vs);
|
||||
ok = sc_thread_create(&vs->thread, run_v4l2_sink, "scrcpy-v4l2", vs);
|
||||
if (!ok) {
|
||||
LOGC("Could not start v4l2 thread");
|
||||
goto error_av_packet_free;
|
||||
|
||||
@@ -170,7 +170,7 @@ bool
|
||||
sc_video_buffer_start(struct sc_video_buffer *vb) {
|
||||
if (vb->buffering_time) {
|
||||
bool ok =
|
||||
sc_thread_create(&vb->b.thread, run_buffering, "buffering", vb);
|
||||
sc_thread_create(&vb->b.thread, run_buffering, "scrcpy-vbuf", vb);
|
||||
if (!ok) {
|
||||
LOGE("Could not start buffering thread");
|
||||
return false;
|
||||
|
||||
@@ -169,4 +169,4 @@ int main(int argc, char *argv[]) {
|
||||
test_options2();
|
||||
test_parse_shortcut_mods();
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ static void test_serialize_inject_text_long(void) {
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||
char text[CONTROL_MSG_INJECT_TEXT_MAX_LENGTH + 1];
|
||||
memset(text, 'a', sizeof(text));
|
||||
memset(text, 'a', CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
|
||||
text[CONTROL_MSG_INJECT_TEXT_MAX_LENGTH] = '\0';
|
||||
msg.inject_text.text = text;
|
||||
|
||||
@@ -250,6 +250,40 @@ static void test_serialize_set_clipboard(void) {
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_set_clipboard_long(void) {
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
.set_clipboard = {
|
||||
.sequence = UINT64_C(0x0102030405060708),
|
||||
.paste = true,
|
||||
.text = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
char text[CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH + 1];
|
||||
memset(text, 'a', CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH);
|
||||
text[CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH] = '\0';
|
||||
msg.set_clipboard.text = text;
|
||||
|
||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||
size_t size = control_msg_serialize(&msg, buf);
|
||||
assert(size == CONTROL_MSG_MAX_SIZE);
|
||||
|
||||
unsigned char expected[CONTROL_MSG_MAX_SIZE] = {
|
||||
CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // sequence
|
||||
1, // paste
|
||||
// text length
|
||||
CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH >> 24,
|
||||
(CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH >> 16) & 0xff,
|
||||
(CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH >> 8) & 0xff,
|
||||
CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH & 0xff,
|
||||
};
|
||||
memset(expected + 14, 'a', CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH);
|
||||
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
||||
static void test_serialize_set_screen_power_mode(void) {
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
||||
@@ -299,6 +333,7 @@ int main(int argc, char *argv[]) {
|
||||
test_serialize_collapse_panels();
|
||||
test_serialize_get_clipboard();
|
||||
test_serialize_set_clipboard();
|
||||
test_serialize_set_clipboard_long();
|
||||
test_serialize_set_screen_power_mode();
|
||||
test_serialize_rotate_device();
|
||||
return 0;
|
||||
|
||||
34
bump_version
Executable file
34
bump_version
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# This script bump scrcpy version by editing all the necessary files.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# ./bump_version 1.23.4
|
||||
#
|
||||
# Then check the diff manually to confirm that everything is ok.
|
||||
|
||||
set -e
|
||||
|
||||
if [[ $# != 1 ]]
|
||||
then
|
||||
echo "Syntax: $0 <version>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION="$1"
|
||||
|
||||
a=( ${VERSION//./ } )
|
||||
MAJOR="${a[0]:-0}"
|
||||
MINOR="${a[1]:-0}"
|
||||
PATCH="${a[2]:-0}"
|
||||
|
||||
# If VERSION is 1.23.4, then VERSION_CODE is 12304
|
||||
VERSION_CODE="$(( $MAJOR * 10000 + $MINOR * 100 + "$PATCH" ))"
|
||||
|
||||
echo "$VERSION: major=$MAJOR minor=$MINOR patch=$PATCH [versionCode=$VERSION_CODE]"
|
||||
sed -i "s/^\(\s*version: \)'[^']*'/\1'$VERSION'/" meson.build
|
||||
sed -i "s/^\(\s*versionCode \).*/\1$VERSION_CODE/;s/^\(\s*versionName \).*/\1\"$VERSION\"/" server/build.gradle
|
||||
sed -i "s/^\(SCRCPY_VERSION_NAME=\).*/\1$VERSION/" server/build_without_gradle.sh
|
||||
sed -i "s/^\(\s*VALUE \"ProductVersion\", \)\"[^\"]*\"/\1\"$VERSION\"/" app/scrcpy-windows.rc
|
||||
echo done
|
||||
@@ -7,6 +7,7 @@ cpp = 'i686-w64-mingw32-g++'
|
||||
ar = 'i686-w64-mingw32-ar'
|
||||
strip = 'i686-w64-mingw32-strip'
|
||||
pkgconfig = 'i686-w64-mingw32-pkg-config'
|
||||
windres = 'i686-w64-mingw32-windres'
|
||||
|
||||
[host_machine]
|
||||
system = 'windows'
|
||||
|
||||
@@ -7,6 +7,7 @@ cpp = 'x86_64-w64-mingw32-g++'
|
||||
ar = 'x86_64-w64-mingw32-ar'
|
||||
strip = 'x86_64-w64-mingw32-strip'
|
||||
pkgconfig = 'x86_64-w64-mingw32-pkg-config'
|
||||
windres = 'x86_64-w64-mingw32-windres'
|
||||
|
||||
[host_machine]
|
||||
system = 'windows'
|
||||
|
||||
BIN
data/icon.ico
Normal file
BIN
data/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
@@ -1,6 +1,5 @@
|
||||
option('compile_app', type: 'boolean', value: true, description: 'Build the client')
|
||||
option('compile_server', type: 'boolean', value: true, description: 'Build the server')
|
||||
option('crossbuild_windows', type: 'boolean', value: false, description: 'Build for Windows from Linux')
|
||||
option('prebuilt_server', type: 'string', description: 'Path of the prebuilt server')
|
||||
option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable')
|
||||
option('server_debugger', type: 'boolean', value: false, description: 'Run a server debugger and wait for a client to be attached')
|
||||
|
||||
@@ -70,7 +70,6 @@ build-win32: prepare-deps-win32
|
||||
meson "$(WIN32_BUILD_DIR)" \
|
||||
--cross-file cross_win32.txt \
|
||||
--buildtype release --strip -Db_lto=true \
|
||||
-Dcrossbuild_windows=true \
|
||||
-Dcompile_server=false \
|
||||
-Dportable=true )
|
||||
ninja -C "$(WIN32_BUILD_DIR)"
|
||||
@@ -83,7 +82,6 @@ build-win64: prepare-deps-win64
|
||||
meson "$(WIN64_BUILD_DIR)" \
|
||||
--cross-file cross_win64.txt \
|
||||
--buildtype release --strip -Db_lto=true \
|
||||
-Dcrossbuild_windows=true \
|
||||
-Dcompile_server=false \
|
||||
-Dportable=true )
|
||||
ninja -C "$(WIN64_BUILD_DIR)"
|
||||
|
||||
@@ -30,8 +30,13 @@ public final class DesktopConnection implements Closeable {
|
||||
private DesktopConnection(LocalSocket videoSocket, LocalSocket controlSocket) throws IOException {
|
||||
this.videoSocket = videoSocket;
|
||||
this.controlSocket = controlSocket;
|
||||
controlInputStream = controlSocket.getInputStream();
|
||||
controlOutputStream = controlSocket.getOutputStream();
|
||||
if (controlSocket != null) {
|
||||
controlInputStream = controlSocket.getInputStream();
|
||||
controlOutputStream = controlSocket.getOutputStream();
|
||||
} else {
|
||||
controlInputStream = null;
|
||||
controlOutputStream = null;
|
||||
}
|
||||
videoFd = videoSocket.getFileDescriptor();
|
||||
}
|
||||
|
||||
@@ -41,31 +46,35 @@ public final class DesktopConnection implements Closeable {
|
||||
return localSocket;
|
||||
}
|
||||
|
||||
public static DesktopConnection open(Device device, boolean tunnelForward) throws IOException {
|
||||
public static DesktopConnection open(Device device, boolean tunnelForward, boolean control) throws IOException {
|
||||
LocalSocket videoSocket;
|
||||
LocalSocket controlSocket;
|
||||
LocalSocket controlSocket = null;
|
||||
if (tunnelForward) {
|
||||
LocalServerSocket localServerSocket = new LocalServerSocket(SOCKET_NAME);
|
||||
try {
|
||||
videoSocket = localServerSocket.accept();
|
||||
// send one byte so the client may read() to detect a connection error
|
||||
videoSocket.getOutputStream().write(0);
|
||||
try {
|
||||
controlSocket = localServerSocket.accept();
|
||||
} catch (IOException | RuntimeException e) {
|
||||
videoSocket.close();
|
||||
throw e;
|
||||
if (control) {
|
||||
try {
|
||||
controlSocket = localServerSocket.accept();
|
||||
} catch (IOException | RuntimeException e) {
|
||||
videoSocket.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
localServerSocket.close();
|
||||
}
|
||||
} else {
|
||||
videoSocket = connect(SOCKET_NAME);
|
||||
try {
|
||||
controlSocket = connect(SOCKET_NAME);
|
||||
} catch (IOException | RuntimeException e) {
|
||||
videoSocket.close();
|
||||
throw e;
|
||||
if (control) {
|
||||
try {
|
||||
controlSocket = connect(SOCKET_NAME);
|
||||
} catch (IOException | RuntimeException e) {
|
||||
videoSocket.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +88,11 @@ public final class DesktopConnection implements Closeable {
|
||||
videoSocket.shutdownInput();
|
||||
videoSocket.shutdownOutput();
|
||||
videoSocket.close();
|
||||
controlSocket.shutdownInput();
|
||||
controlSocket.shutdownOutput();
|
||||
controlSocket.close();
|
||||
if (controlSocket != null) {
|
||||
controlSocket.shutdownInput();
|
||||
controlSocket.shutdownOutput();
|
||||
controlSocket.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void send(String deviceName, int width, int height) throws IOException {
|
||||
|
||||
@@ -66,14 +66,15 @@ public final class Server {
|
||||
Thread initThread = startInitThread(options);
|
||||
|
||||
boolean tunnelForward = options.isTunnelForward();
|
||||
boolean control = options.getControl();
|
||||
|
||||
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) {
|
||||
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward, control)) {
|
||||
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions,
|
||||
options.getEncoderName());
|
||||
|
||||
Thread controllerThread = null;
|
||||
Thread deviceMessageSenderThread = null;
|
||||
if (options.getControl()) {
|
||||
if (control) {
|
||||
final Controller controller = new Controller(device, connection, options.getClipboardAutosync());
|
||||
|
||||
// asynchronous
|
||||
|
||||
Reference in New Issue
Block a user