diff --git a/app/meson.build b/app/meson.build index 673892cd..02d24a34 100644 --- a/app/meson.build +++ b/app/meson.build @@ -93,8 +93,8 @@ conf.set_quoted('SCRCPY_VERSION', meson.project_version()) # the prefix used during configuration (meson --prefix=PREFIX) conf.set_quoted('PREFIX', get_option('prefix')) -# build a "portable" version (with scrcpy-server.jar accessible from the -# current directory) +# build a "portable" version (with scrcpy-server.jar accessible from the same +# directory as the executable) conf.set('PORTABLE', get_option('portable')) # the default client TCP port for the "adb reverse" tunnel diff --git a/app/src/command.h b/app/src/command.h index 3453ca10..db6358da 100644 --- a/app/src/command.h +++ b/app/src/command.h @@ -9,6 +9,7 @@ // not needed here, but winsock2.h must never be included AFTER windows.h # include # include +# define PATH_SEPARATOR '\\' # define PRIexitcode "lu" // # ifdef _WIN64 @@ -23,6 +24,7 @@ #else # include +# define PATH_SEPARATOR '/' # define PRIsizet "zu" # define PRIexitcode "d" # define PROCESS_NONE -1 @@ -76,4 +78,9 @@ adb_install(const char *serial, const char *local); bool process_check_success(process_t proc, const char *name); +// return the absolute path of the executable (the scrcpy binary) +// may be NULL on error; to be freed by SDL_free +char * +get_executable_path(void); + #endif diff --git a/app/src/server.c b/app/src/server.c index e7c8712e..d0599bef 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -2,32 +2,67 @@ #include #include +#include #include #include #include #include "config.h" +#include "command.h" #include "log.h" #include "net.h" #define SOCKET_NAME "scrcpy" #define SERVER_FILENAME "scrcpy-server.jar" -#ifdef PORTABLE -# define DEFAULT_SERVER_PATH SERVER_FILENAME -#else -# define DEFAULT_SERVER_PATH PREFIX "/share/scrcpy/" SERVER_FILENAME -#endif - +#define DEFAULT_SERVER_PATH PREFIX "/share/scrcpy/" SERVER_FLENAME #define DEVICE_SERVER_PATH "/data/local/tmp/" SERVER_FILENAME static const char * get_server_path(void) { - const char *server_path = getenv("SCRCPY_SERVER_PATH"); - if (!server_path) { - server_path = DEFAULT_SERVER_PATH; + const char *server_path_env = getenv("SCRCPY_SERVER_PATH"); + if (server_path_env) { + LOGD("Using SCRCPY_SERVER_PATH: %s", server_path_env); + // if the envvar is set, use it + return server_path_env; } + +#ifndef PORTABLE + LOGD("Using server: " DEFAULT_SERVER_PATH); + // the absolute path is hardcoded + return DEFAULT_SERVER_PATH; +#else + // use scrcpy-server.jar in the same directory as the executable + char *executable_path = get_executable_path(); + if (!executable_path) { + LOGE("Cannot get executable path, " + "using " SERVER_FILENAME " from current directory"); + // not found, use current directory + return SERVER_FILENAME; + } + char *dir = dirname(executable_path); + size_t dirlen = strlen(dir); + + // sizeof(SERVER_FILENAME) gives statically the size including the null byte + size_t len = dirlen + 1 + sizeof(SERVER_FILENAME); + char *server_path = SDL_malloc(len); + if (!server_path) { + LOGE("Cannot alloc server path string, " + "using " SERVER_FILENAME " from current directory"); + SDL_free(executable_path); + return SERVER_FILENAME; + } + + memcpy(server_path, dir, dirlen); + server_path[dirlen] = PATH_SEPARATOR; + memcpy(&server_path[dirlen + 1], SERVER_FILENAME, sizeof(SERVER_FILENAME)); + // the final null byte has been copied with SERVER_FILENAME + + SDL_free(executable_path); + + LOGD("Using server (portable): %s", server_path); return server_path; +#endif } static bool diff --git a/app/src/sys/unix/command.c b/app/src/sys/unix/command.c index fa41571e..55aea5e8 100644 --- a/app/src/sys/unix/command.c +++ b/app/src/sys/unix/command.c @@ -1,9 +1,15 @@ +// for portability #define _POSIX_SOURCE // for kill() +#define _BSD_SOURCE // for readlink() + +// modern glibc will complain without this +#define _DEFAULT_SOURCE #include "command.h" #include #include +#include #include #include #include @@ -98,3 +104,23 @@ cmd_simple_wait(pid_t pid, int *exit_code) { } return !code; } + +char * +get_executable_path(void) { +// +#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 SDL_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 +} diff --git a/app/src/sys/win/command.c b/app/src/sys/win/command.c index 8434dc98..484ce9f0 100644 --- a/app/src/sys/win/command.c +++ b/app/src/sys/win/command.c @@ -75,3 +75,18 @@ cmd_simple_wait(HANDLE handle, DWORD *exit_code) { } return !code; } + +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); +} diff --git a/meson_options.txt b/meson_options.txt index 90b4293c..a443ccb2 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -3,6 +3,6 @@ option('build_server', type: 'boolean', value: true, description: 'Build the ser option('crossbuild_windows', type: 'boolean', value: false, description: 'Build for Windows from Linux') option('windows_noconsole', type: 'boolean', value: false, description: 'Disable console on Windows (pass -mwindows flag)') option('prebuilt_server', type: 'string', description: 'Path of the prebuilt server') -option('portable', type: 'boolean', description: 'Use scrcpy-server.jar from the current directory') +option('portable', type: 'boolean', description: 'Use scrcpy-server.jar from the same directory as the scrcpy executable') option('skip_frames', type: 'boolean', value: true, description: 'Always display the most recent frame') option('hidpi_support', type: 'boolean', value: true, description: 'Enable High DPI support')