Compare commits
11 Commits
issue5542.
...
exec_path
Author | SHA1 | Date | |
---|---|---|---|
aea6a371aa | |||
dc6c279b1e | |||
6d0ac3626d | |||
beee42fb06 | |||
131372d2c4 | |||
0fd7534bd5 | |||
36574d2ee7 | |||
3b2b3625e4 | |||
b2cdaa4bdc | |||
d01373c03c | |||
ff06b6dcc1 |
@ -1,6 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
cd "$(dirname ${BASH_SOURCE[0]})"
|
|
||||||
export ADB="${ADB:-./adb}"
|
|
||||||
export SCRCPY_SERVER_PATH="${SCRCPY_SERVER_PATH:-./scrcpy-server}"
|
|
||||||
export SCRCPY_ICON_PATH="${SCRCPY_ICON_PATH:-./icon.png}"
|
|
||||||
./scrcpy_bin "$@"
|
|
@ -46,6 +46,7 @@ src = [
|
|||||||
'src/util/acksync.c',
|
'src/util/acksync.c',
|
||||||
'src/util/audiobuf.c',
|
'src/util/audiobuf.c',
|
||||||
'src/util/average.c',
|
'src/util/average.c',
|
||||||
|
'src/util/env.c',
|
||||||
'src/util/file.c',
|
'src/util/file.c',
|
||||||
'src/util/intmap.c',
|
'src/util/intmap.c',
|
||||||
'src/util/intr.c',
|
'src/util/intr.c',
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "adb_device.h"
|
#include "adb_device.h"
|
||||||
#include "adb_parser.h"
|
#include "adb_parser.h"
|
||||||
|
#include "util/env.h"
|
||||||
#include "util/file.h"
|
#include "util/file.h"
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/process_intr.h"
|
#include "util/process_intr.h"
|
||||||
@ -24,15 +25,45 @@
|
|||||||
*/
|
*/
|
||||||
#define SC_ADB_COMMAND(...) { sc_adb_get_executable(), __VA_ARGS__, NULL }
|
#define SC_ADB_COMMAND(...) { sc_adb_get_executable(), __VA_ARGS__, NULL }
|
||||||
|
|
||||||
static const char *adb_executable;
|
static char *adb_executable;
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_adb_init(void) {
|
||||||
|
adb_executable = sc_get_env("ADB");
|
||||||
|
if (adb_executable) {
|
||||||
|
LOGD("Using adb: %s", adb_executable);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(PORTABLE) || defined(_WIN32)
|
||||||
|
adb_executable = strdup("adb");
|
||||||
|
if (!adb_executable) {
|
||||||
|
LOG_OOM();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// For portable builds, use the absolute path to the adb executable
|
||||||
|
// in the same directory as scrcpy (except on Windows, where "adb"
|
||||||
|
// is sufficient)
|
||||||
|
adb_executable = sc_file_get_local_path("adb");
|
||||||
|
if (!adb_executable) {
|
||||||
|
// Error already logged
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGD("Using adb (portable): %s", adb_executable);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_adb_destroy(void) {
|
||||||
|
free(adb_executable);
|
||||||
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
sc_adb_get_executable(void) {
|
sc_adb_get_executable(void) {
|
||||||
if (!adb_executable) {
|
|
||||||
adb_executable = getenv("ADB");
|
|
||||||
if (!adb_executable)
|
|
||||||
adb_executable = "adb";
|
|
||||||
}
|
|
||||||
return adb_executable;
|
return adb_executable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,12 @@
|
|||||||
|
|
||||||
#define SC_ADB_SILENT (SC_ADB_NO_STDOUT | SC_ADB_NO_STDERR | SC_ADB_NO_LOGERR)
|
#define SC_ADB_SILENT (SC_ADB_NO_STDOUT | SC_ADB_NO_STDERR | SC_ADB_NO_LOGERR)
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_adb_init(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_adb_destroy(void);
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
sc_adb_get_executable(void);
|
sc_adb_get_executable(void);
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
#include "util/env.h"
|
||||||
#include "util/file.h"
|
#include "util/file.h"
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/str.h"
|
#include "util/str.h"
|
||||||
@ -19,35 +20,22 @@
|
|||||||
|
|
||||||
static char *
|
static char *
|
||||||
get_icon_path(void) {
|
get_icon_path(void) {
|
||||||
#ifdef __WINDOWS__
|
char *icon_path = sc_get_env("SCRCPY_ICON_PATH");
|
||||||
const wchar_t *icon_path_env = _wgetenv(L"SCRCPY_ICON_PATH");
|
if (icon_path) {
|
||||||
#else
|
|
||||||
const char *icon_path_env = getenv("SCRCPY_ICON_PATH");
|
|
||||||
#endif
|
|
||||||
if (icon_path_env) {
|
|
||||||
// if the envvar is set, use it
|
// if the envvar is set, use it
|
||||||
#ifdef __WINDOWS__
|
|
||||||
char *icon_path = sc_str_from_wchars(icon_path_env);
|
|
||||||
#else
|
|
||||||
char *icon_path = strdup(icon_path_env);
|
|
||||||
#endif
|
|
||||||
if (!icon_path) {
|
|
||||||
LOG_OOM();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
LOGD("Using SCRCPY_ICON_PATH: %s", icon_path);
|
LOGD("Using SCRCPY_ICON_PATH: %s", icon_path);
|
||||||
return icon_path;
|
return icon_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef PORTABLE
|
#ifndef PORTABLE
|
||||||
LOGD("Using icon: " SCRCPY_DEFAULT_ICON_PATH);
|
LOGD("Using icon: " SCRCPY_DEFAULT_ICON_PATH);
|
||||||
char *icon_path = strdup(SCRCPY_DEFAULT_ICON_PATH);
|
icon_path = strdup(SCRCPY_DEFAULT_ICON_PATH);
|
||||||
if (!icon_path) {
|
if (!icon_path) {
|
||||||
LOG_OOM();
|
LOG_OOM();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
char *icon_path = sc_file_get_local_path(SCRCPY_PORTABLE_ICON_FILENAME);
|
icon_path = sc_file_get_local_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;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "adb/adb.h"
|
#include "adb/adb.h"
|
||||||
#include "util/binary.h"
|
#include "util/binary.h"
|
||||||
|
#include "util/env.h"
|
||||||
#include "util/file.h"
|
#include "util/file.h"
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/net_intr.h"
|
#include "util/net_intr.h"
|
||||||
@ -25,35 +26,22 @@
|
|||||||
|
|
||||||
static char *
|
static char *
|
||||||
get_server_path(void) {
|
get_server_path(void) {
|
||||||
#ifdef __WINDOWS__
|
char *server_path = sc_get_env("SCRCPY_SERVER_PATH");
|
||||||
const wchar_t *server_path_env = _wgetenv(L"SCRCPY_SERVER_PATH");
|
if (server_path) {
|
||||||
#else
|
|
||||||
const char *server_path_env = getenv("SCRCPY_SERVER_PATH");
|
|
||||||
#endif
|
|
||||||
if (server_path_env) {
|
|
||||||
// if the envvar is set, use it
|
// if the envvar is set, use it
|
||||||
#ifdef __WINDOWS__
|
|
||||||
char *server_path = sc_str_from_wchars(server_path_env);
|
|
||||||
#else
|
|
||||||
char *server_path = strdup(server_path_env);
|
|
||||||
#endif
|
|
||||||
if (!server_path) {
|
|
||||||
LOG_OOM();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
LOGD("Using SCRCPY_SERVER_PATH: %s", server_path);
|
LOGD("Using SCRCPY_SERVER_PATH: %s", server_path);
|
||||||
return server_path;
|
return server_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef PORTABLE
|
#ifndef PORTABLE
|
||||||
LOGD("Using server: " SC_SERVER_PATH_DEFAULT);
|
LOGD("Using server: " SC_SERVER_PATH_DEFAULT);
|
||||||
char *server_path = strdup(SC_SERVER_PATH_DEFAULT);
|
server_path = strdup(SC_SERVER_PATH_DEFAULT);
|
||||||
if (!server_path) {
|
if (!server_path) {
|
||||||
LOG_OOM();
|
LOG_OOM();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
char *server_path = sc_file_get_local_path(SC_SERVER_FILENAME);
|
server_path = sc_file_get_local_path(SC_SERVER_FILENAME);
|
||||||
if (!server_path) {
|
if (!server_path) {
|
||||||
LOGE("Could not get local file path, "
|
LOGE("Could not get local file path, "
|
||||||
"using " SC_SERVER_FILENAME " from current directory");
|
"using " SC_SERVER_FILENAME " from current directory");
|
||||||
@ -497,14 +485,21 @@ sc_server_init(struct sc_server *server, const struct sc_server_params *params,
|
|||||||
// end of the program
|
// end of the program
|
||||||
server->params = *params;
|
server->params = *params;
|
||||||
|
|
||||||
bool ok = sc_mutex_init(&server->mutex);
|
bool ok = sc_adb_init();
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ok = sc_mutex_init(&server->mutex);
|
||||||
|
if (!ok) {
|
||||||
|
sc_adb_destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ok = sc_cond_init(&server->cond_stopped);
|
ok = sc_cond_init(&server->cond_stopped);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
sc_mutex_destroy(&server->mutex);
|
sc_mutex_destroy(&server->mutex);
|
||||||
|
sc_adb_destroy();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,6 +507,7 @@ sc_server_init(struct sc_server *server, const struct sc_server_params *params,
|
|||||||
if (!ok) {
|
if (!ok) {
|
||||||
sc_cond_destroy(&server->cond_stopped);
|
sc_cond_destroy(&server->cond_stopped);
|
||||||
sc_mutex_destroy(&server->mutex);
|
sc_mutex_destroy(&server->mutex);
|
||||||
|
sc_adb_destroy();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1153,4 +1149,6 @@ sc_server_destroy(struct sc_server *server) {
|
|||||||
sc_intr_destroy(&server->intr);
|
sc_intr_destroy(&server->intr);
|
||||||
sc_cond_destroy(&server->cond_stopped);
|
sc_cond_destroy(&server->cond_stopped);
|
||||||
sc_mutex_destroy(&server->mutex);
|
sc_mutex_destroy(&server->mutex);
|
||||||
|
|
||||||
|
sc_adb_destroy();
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# include <mach-o/dyld.h> // for _NSGetExecutablePath()
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
@ -60,11 +63,22 @@ sc_file_get_executable_path(void) {
|
|||||||
}
|
}
|
||||||
buf[len] = '\0';
|
buf[len] = '\0';
|
||||||
return strdup(buf);
|
return strdup(buf);
|
||||||
#else
|
#elif defined(__APPLE__)
|
||||||
// in practice, we only need this feature for portable builds, only used on
|
char buf[PATH_MAX];
|
||||||
// Windows, so we don't care implementing it for every platform
|
uint32_t bufsize = PATH_MAX;
|
||||||
// (it's useful to have a working version on Linux for debugging though)
|
if (_NSGetExecutablePath(buf, &bufsize) != 0) {
|
||||||
|
LOGE("Executable path buffer too small; need %u bytes", bufsize);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
return realpath(buf, NULL);
|
||||||
|
#else
|
||||||
|
// "_" is often used to store the full path of the command being executed
|
||||||
|
char *path = getenv("_");
|
||||||
|
if (!path) {
|
||||||
|
LOGE("Could not determine executable path");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return strdup(path);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,9 +95,14 @@ scrcpy_otg(struct scrcpy_options *options) {
|
|||||||
// On Windows, only one process could open a USB device
|
// On Windows, only one process could open a USB device
|
||||||
// <https://github.com/Genymobile/scrcpy/issues/2773>
|
// <https://github.com/Genymobile/scrcpy/issues/2773>
|
||||||
LOGI("Killing adb server (if any)...");
|
LOGI("Killing adb server (if any)...");
|
||||||
|
if (sc_adb_init()) {
|
||||||
unsigned flags = SC_ADB_NO_STDOUT | SC_ADB_NO_STDERR | SC_ADB_NO_LOGERR;
|
unsigned flags = SC_ADB_NO_STDOUT | SC_ADB_NO_STDERR | SC_ADB_NO_LOGERR;
|
||||||
// uninterruptible (intr == NULL), but in practice it's very quick
|
// uninterruptible (intr == NULL), but in practice it's very quick
|
||||||
sc_adb_kill_server(NULL, flags);
|
sc_adb_kill_server(NULL, flags);
|
||||||
|
sc_adb_destroy();
|
||||||
|
} else {
|
||||||
|
LOGW("Could not call adb executable, adb server not killed");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct sc_usb_callbacks cbs = {
|
static const struct sc_usb_callbacks cbs = {
|
||||||
|
29
app/src/util/env.c
Normal file
29
app/src/util/env.c
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include "env.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "util/str.h"
|
||||||
|
|
||||||
|
char *
|
||||||
|
sc_get_env(const char *varname) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
wchar_t *w_varname = sc_str_to_wchars(varname);
|
||||||
|
if (!w_varname) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
const wchar_t *value = _wgetenv(w_varname);
|
||||||
|
free(w_varname);
|
||||||
|
if (!value) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sc_str_from_wchars(value);
|
||||||
|
#else
|
||||||
|
const char *value = getenv(varname);
|
||||||
|
if (!value) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strdup(value);
|
||||||
|
#endif
|
||||||
|
}
|
12
app/src/util/env.h
Normal file
12
app/src/util/env.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef SC_ENV_H
|
||||||
|
#define SC_ENV_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
// Return the value of the environment variable (may be NULL).
|
||||||
|
//
|
||||||
|
// The returned value must be freed by the caller.
|
||||||
|
char *
|
||||||
|
sc_get_env(const char *varname);
|
||||||
|
|
||||||
|
#endif
|
@ -9,8 +9,6 @@
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <ws2tcpip.h>
|
# include <ws2tcpip.h>
|
||||||
typedef int socklen_t;
|
typedef int socklen_t;
|
||||||
typedef SOCKET sc_raw_socket;
|
|
||||||
# define SC_RAW_SOCKET_NONE INVALID_SOCKET
|
|
||||||
#else
|
#else
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
# include <sys/socket.h>
|
# include <sys/socket.h>
|
||||||
@ -23,8 +21,6 @@
|
|||||||
typedef struct sockaddr_in SOCKADDR_IN;
|
typedef struct sockaddr_in SOCKADDR_IN;
|
||||||
typedef struct sockaddr SOCKADDR;
|
typedef struct sockaddr SOCKADDR;
|
||||||
typedef struct in_addr IN_ADDR;
|
typedef struct in_addr IN_ADDR;
|
||||||
typedef int sc_raw_socket;
|
|
||||||
# define SC_RAW_SOCKET_NONE -1
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -47,17 +43,26 @@ net_cleanup(void) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
sc_raw_socket_close(sc_raw_socket raw_sock) {
|
||||||
|
#ifndef _WIN32
|
||||||
|
return !close(raw_sock);
|
||||||
|
#else
|
||||||
|
return !closesocket(raw_sock);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static inline sc_socket
|
static inline sc_socket
|
||||||
wrap(sc_raw_socket sock) {
|
wrap(sc_raw_socket sock) {
|
||||||
#ifdef _WIN32
|
#ifdef SC_SOCKET_CLOSE_ON_INTERRUPT
|
||||||
if (sock == INVALID_SOCKET) {
|
if (sock == SC_RAW_SOCKET_NONE) {
|
||||||
return SC_SOCKET_NONE;
|
return SC_SOCKET_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_socket_windows *socket = malloc(sizeof(*socket));
|
struct sc_socket_wrapper *socket = malloc(sizeof(*socket));
|
||||||
if (!socket) {
|
if (!socket) {
|
||||||
LOG_OOM();
|
LOG_OOM();
|
||||||
closesocket(sock);
|
sc_raw_socket_close(sock);
|
||||||
return SC_SOCKET_NONE;
|
return SC_SOCKET_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,9 +77,9 @@ wrap(sc_raw_socket sock) {
|
|||||||
|
|
||||||
static inline sc_raw_socket
|
static inline sc_raw_socket
|
||||||
unwrap(sc_socket socket) {
|
unwrap(sc_socket socket) {
|
||||||
#ifdef _WIN32
|
#ifdef SC_SOCKET_CLOSE_ON_INTERRUPT
|
||||||
if (socket == SC_SOCKET_NONE) {
|
if (socket == SC_SOCKET_NONE) {
|
||||||
return INVALID_SOCKET;
|
return SC_RAW_SOCKET_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return socket->socket;
|
return socket->socket;
|
||||||
@ -83,17 +88,6 @@ unwrap(sc_socket socket) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HAVE_SOCK_CLOEXEC // avoid unused-function warning
|
|
||||||
static inline bool
|
|
||||||
sc_raw_socket_close(sc_raw_socket raw_sock) {
|
|
||||||
#ifndef _WIN32
|
|
||||||
return !close(raw_sock);
|
|
||||||
#else
|
|
||||||
return !closesocket(raw_sock);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HAVE_SOCK_CLOEXEC
|
#ifndef HAVE_SOCK_CLOEXEC
|
||||||
// If SOCK_CLOEXEC does not exist, the flag must be set manually once the
|
// If SOCK_CLOEXEC does not exist, the flag must be set manually once the
|
||||||
// socket is created
|
// socket is created
|
||||||
@ -248,9 +242,9 @@ net_interrupt(sc_socket socket) {
|
|||||||
|
|
||||||
sc_raw_socket raw_sock = unwrap(socket);
|
sc_raw_socket raw_sock = unwrap(socket);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef SC_SOCKET_CLOSE_ON_INTERRUPT
|
||||||
if (!atomic_flag_test_and_set(&socket->closed)) {
|
if (!atomic_flag_test_and_set(&socket->closed)) {
|
||||||
return !closesocket(raw_sock);
|
return sc_raw_socket_close(raw_sock);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
@ -262,15 +256,15 @@ bool
|
|||||||
net_close(sc_socket socket) {
|
net_close(sc_socket socket) {
|
||||||
sc_raw_socket raw_sock = unwrap(socket);
|
sc_raw_socket raw_sock = unwrap(socket);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef SC_SOCKET_CLOSE_ON_INTERRUPT
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
if (!atomic_flag_test_and_set(&socket->closed)) {
|
if (!atomic_flag_test_and_set(&socket->closed)) {
|
||||||
ret = !closesocket(raw_sock);
|
ret = sc_raw_socket_close(raw_sock);
|
||||||
}
|
}
|
||||||
free(socket);
|
free(socket);
|
||||||
return ret;
|
return ret;
|
||||||
#else
|
#else
|
||||||
return !close(raw_sock);
|
return sc_raw_socket_close(raw_sock);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,21 +7,36 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
# include <winsock2.h>
|
# include <winsock2.h>
|
||||||
|
typedef SOCKET sc_raw_socket;
|
||||||
|
# define SC_RAW_SOCKET_NONE INVALID_SOCKET
|
||||||
|
#else // not _WIN32
|
||||||
|
# include <sys/socket.h>
|
||||||
|
typedef int sc_raw_socket;
|
||||||
|
# define SC_RAW_SOCKET_NONE -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(__APPLE__)
|
||||||
|
// On Windows and macOS, shutdown() does not interrupt accept() or read()
|
||||||
|
// calls, so net_interrupt() must call close() instead, and net_close() must
|
||||||
|
// behave accordingly.
|
||||||
|
// This causes a small race condition (once the socket is closed, its
|
||||||
|
// handle becomes invalid and may in theory be reassigned before another
|
||||||
|
// thread calls accept() or read()), but it is deemed acceptable as a
|
||||||
|
// workaround.
|
||||||
|
# define SC_SOCKET_CLOSE_ON_INTERRUPT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SC_SOCKET_CLOSE_ON_INTERRUPT
|
||||||
# include <stdatomic.h>
|
# include <stdatomic.h>
|
||||||
# define SC_SOCKET_NONE NULL
|
# define SC_SOCKET_NONE NULL
|
||||||
typedef struct sc_socket_windows {
|
typedef struct sc_socket_wrapper {
|
||||||
SOCKET socket;
|
sc_raw_socket socket;
|
||||||
atomic_flag closed;
|
atomic_flag closed;
|
||||||
} *sc_socket;
|
} *sc_socket;
|
||||||
|
#else
|
||||||
#else // not _WIN32
|
|
||||||
|
|
||||||
# include <sys/socket.h>
|
|
||||||
# define SC_SOCKET_NONE -1
|
# define SC_SOCKET_NONE -1
|
||||||
typedef int sc_socket;
|
typedef sc_raw_socket sc_socket;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define IPV4_LOCALHOST 0x7F000001
|
#define IPV4_LOCALHOST 0x7F000001
|
||||||
|
@ -36,8 +36,7 @@ ninja -C "$LINUX_BUILD_DIR"
|
|||||||
|
|
||||||
# Group intermediate outputs into a 'dist' directory
|
# Group intermediate outputs into a 'dist' directory
|
||||||
mkdir -p "$LINUX_BUILD_DIR/dist"
|
mkdir -p "$LINUX_BUILD_DIR/dist"
|
||||||
cp "$LINUX_BUILD_DIR"/app/scrcpy "$LINUX_BUILD_DIR/dist/scrcpy_bin"
|
cp "$LINUX_BUILD_DIR"/app/scrcpy "$LINUX_BUILD_DIR/dist/"
|
||||||
cp app/data/icon.png "$LINUX_BUILD_DIR/dist/"
|
cp app/data/icon.png "$LINUX_BUILD_DIR/dist/"
|
||||||
cp app/data/scrcpy_static_wrapper.sh "$LINUX_BUILD_DIR/dist/scrcpy"
|
|
||||||
cp app/scrcpy.1 "$LINUX_BUILD_DIR/dist/"
|
cp app/scrcpy.1 "$LINUX_BUILD_DIR/dist/"
|
||||||
cp -r "$ADB_INSTALL_DIR"/. "$LINUX_BUILD_DIR/dist/"
|
cp -r "$ADB_INSTALL_DIR"/. "$LINUX_BUILD_DIR/dist/"
|
||||||
|
@ -36,8 +36,7 @@ ninja -C "$MACOS_BUILD_DIR"
|
|||||||
|
|
||||||
# Group intermediate outputs into a 'dist' directory
|
# Group intermediate outputs into a 'dist' directory
|
||||||
mkdir -p "$MACOS_BUILD_DIR/dist"
|
mkdir -p "$MACOS_BUILD_DIR/dist"
|
||||||
cp "$MACOS_BUILD_DIR"/app/scrcpy "$MACOS_BUILD_DIR/dist/scrcpy_bin"
|
cp "$MACOS_BUILD_DIR"/app/scrcpy "$MACOS_BUILD_DIR/dist/"
|
||||||
cp app/data/icon.png "$MACOS_BUILD_DIR/dist/"
|
cp app/data/icon.png "$MACOS_BUILD_DIR/dist/"
|
||||||
cp app/data/scrcpy_static_wrapper.sh "$MACOS_BUILD_DIR/dist/scrcpy"
|
|
||||||
cp app/scrcpy.1 "$MACOS_BUILD_DIR/dist/"
|
cp app/scrcpy.1 "$MACOS_BUILD_DIR/dist/"
|
||||||
cp -r "$ADB_INSTALL_DIR"/. "$MACOS_BUILD_DIR/dist/"
|
cp -r "$ADB_INSTALL_DIR"/. "$MACOS_BUILD_DIR/dist/"
|
||||||
|
@ -40,7 +40,7 @@ case "$FORMAT" in
|
|||||||
zip -r "$OUTPUT_DIR/$TARGET_DIRNAME.zip" "$TARGET_DIRNAME"
|
zip -r "$OUTPUT_DIR/$TARGET_DIRNAME.zip" "$TARGET_DIRNAME"
|
||||||
;;
|
;;
|
||||||
tar.gz)
|
tar.gz)
|
||||||
tar cvf "$OUTPUT_DIR/$TARGET_DIRNAME.tar.gz" "$TARGET_DIRNAME"
|
tar cvzf "$OUTPUT_DIR/$TARGET_DIRNAME.tar.gz" "$TARGET_DIRNAME"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Invalid format (expected zip or tar.gz): $FORMAT" >&2
|
echo "Invalid format (expected zip or tar.gz): $FORMAT" >&2
|
||||||
|
@ -21,6 +21,7 @@ import android.content.IOnPrimaryClipChangedListener;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.util.Pair;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
import android.view.KeyCharacterMap;
|
import android.view.KeyCharacterMap;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@ -350,24 +351,47 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
return successCount;
|
return successCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean injectTouch(int action, long pointerId, Position position, float pressure, int actionButton, int buttons) {
|
private Pair<Point, Integer> getEventPointAndDisplayId(Position position) {
|
||||||
long now = SystemClock.uptimeMillis();
|
|
||||||
|
|
||||||
// it hides the field on purpose, to read it with atomic access
|
// it hides the field on purpose, to read it with atomic access
|
||||||
@SuppressWarnings("checkstyle:HiddenField")
|
@SuppressWarnings("checkstyle:HiddenField")
|
||||||
DisplayData displayData = this.displayData.get();
|
DisplayData displayData = this.displayData.get();
|
||||||
assert displayData != null : "Cannot receive a touch event without a display";
|
// In scrcpy, displayData should never be null (a touch event can only be generated from the client when a video frame is present).
|
||||||
|
// However, it is possible to send events without video playback when using scrcpy-server alone (except for virtual displays).
|
||||||
|
assert displayData != null || displayId != Device.DISPLAY_ID_NONE : "Cannot receive a positional event without a display";
|
||||||
|
|
||||||
Point point = displayData.positionMapper.map(position);
|
Point point;
|
||||||
|
int targetDisplayId;
|
||||||
|
if (displayData != null) {
|
||||||
|
point = displayData.positionMapper.map(position);
|
||||||
if (point == null) {
|
if (point == null) {
|
||||||
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
||||||
Size eventSize = position.getScreenSize();
|
Size eventSize = position.getScreenSize();
|
||||||
Size currentSize = displayData.positionMapper.getVideoSize();
|
Size currentSize = displayData.positionMapper.getVideoSize();
|
||||||
Ln.v("Ignore touch event generated for size " + eventSize + " (current size is " + currentSize + ")");
|
Ln.v("Ignore positional event generated for size " + eventSize + " (current size is " + currentSize + ")");
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
targetDisplayId = displayData.virtualDisplayId;
|
||||||
|
} else {
|
||||||
|
// No display, use the raw coordinates
|
||||||
|
point = position.getPoint();
|
||||||
|
targetDisplayId = displayId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair.create(point, targetDisplayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean injectTouch(int action, long pointerId, Position position, float pressure, int actionButton, int buttons) {
|
||||||
|
long now = SystemClock.uptimeMillis();
|
||||||
|
|
||||||
|
Pair<Point, Integer> pair = getEventPointAndDisplayId(position);
|
||||||
|
if (pair == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Point point = pair.first;
|
||||||
|
int targetDisplayId = pair.second;
|
||||||
|
|
||||||
int pointerIndex = pointersState.getPointerIndex(pointerId);
|
int pointerIndex = pointersState.getPointerIndex(pointerId);
|
||||||
if (pointerIndex == -1) {
|
if (pointerIndex == -1) {
|
||||||
Ln.w("Too many pointers for touch event");
|
Ln.w("Too many pointers for touch event");
|
||||||
@ -421,7 +445,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
// First button pressed: ACTION_DOWN
|
// First button pressed: ACTION_DOWN
|
||||||
MotionEvent downEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_DOWN, pointerCount, pointerProperties,
|
MotionEvent downEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_DOWN, pointerCount, pointerProperties,
|
||||||
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
|
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
|
||||||
if (!Device.injectEvent(downEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
|
if (!Device.injectEvent(downEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -432,7 +456,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
if (!InputManager.setActionButton(pressEvent, actionButton)) {
|
if (!InputManager.setActionButton(pressEvent, actionButton)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Device.injectEvent(pressEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
|
if (!Device.injectEvent(pressEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,7 +470,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
if (!InputManager.setActionButton(releaseEvent, actionButton)) {
|
if (!InputManager.setActionButton(releaseEvent, actionButton)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Device.injectEvent(releaseEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
|
if (!Device.injectEvent(releaseEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,7 +478,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
// Last button released: ACTION_UP
|
// Last button released: ACTION_UP
|
||||||
MotionEvent upEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_UP, pointerCount, pointerProperties,
|
MotionEvent upEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_UP, pointerCount, pointerProperties,
|
||||||
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
|
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
|
||||||
if (!Device.injectEvent(upEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
|
if (!Device.injectEvent(upEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -465,27 +489,20 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
|
|
||||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
|
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
|
||||||
DEFAULT_DEVICE_ID, 0, source, 0);
|
DEFAULT_DEVICE_ID, 0, source, 0);
|
||||||
return Device.injectEvent(event, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC);
|
return Device.injectEvent(event, targetDisplayId, Device.INJECT_MODE_ASYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean injectScroll(Position position, float hScroll, float vScroll, int buttons) {
|
private boolean injectScroll(Position position, float hScroll, float vScroll, int buttons) {
|
||||||
long now = SystemClock.uptimeMillis();
|
long now = SystemClock.uptimeMillis();
|
||||||
|
|
||||||
// it hides the field on purpose, to read it with atomic access
|
Pair<Point, Integer> pair = getEventPointAndDisplayId(position);
|
||||||
@SuppressWarnings("checkstyle:HiddenField")
|
if (pair == null) {
|
||||||
DisplayData displayData = this.displayData.get();
|
|
||||||
assert displayData != null : "Cannot receive a scroll event without a display";
|
|
||||||
|
|
||||||
Point point = displayData.positionMapper.map(position);
|
|
||||||
if (point == null) {
|
|
||||||
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
|
||||||
Size eventSize = position.getScreenSize();
|
|
||||||
Size currentSize = displayData.positionMapper.getVideoSize();
|
|
||||||
Ln.v("Ignore scroll event generated for size " + eventSize + " (current size is " + currentSize + ")");
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Point point = pair.first;
|
||||||
|
int targetDisplayId = pair.second;
|
||||||
|
|
||||||
MotionEvent.PointerProperties props = pointerProperties[0];
|
MotionEvent.PointerProperties props = pointerProperties[0];
|
||||||
props.id = 0;
|
props.id = 0;
|
||||||
|
|
||||||
@ -497,7 +514,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
|
|
||||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
|
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
|
||||||
DEFAULT_DEVICE_ID, 0, InputDevice.SOURCE_MOUSE, 0);
|
DEFAULT_DEVICE_ID, 0, InputDevice.SOURCE_MOUSE, 0);
|
||||||
return Device.injectEvent(event, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC);
|
return Device.injectEvent(event, targetDisplayId, Device.INJECT_MODE_ASYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user