Compare commits

...

13 Commits
cross ... v1.2

Author SHA1 Message Date
Romain Vimont
8106bb8215 Bump version to 1.2 2018-05-28 22:32:14 +02:00
Romain Vimont
f1b3a40375 Rename SHA256SUM to SHA256SUMS
It contains the checksums for several files.
2018-05-28 22:32:14 +02:00
Romain Vimont
a63dd47f2d Make CreateProcess() flags depend on "noconsole"
On Windows, display the output of external commands (adb) when a console
is available.
2018-05-28 22:28:20 +02:00
Romain Vimont
6b4bbb1fb3 Update README and FAQ for the new Windows releases
Document how to generate a Windows release from Linux.

It solves the "no output" issue on Windows, so update the FAQ.
2018-05-28 20:45:54 +02:00
Romain Vimont
aedc2c2da9 Also build "noconsole" binary for Windows
On Windows, an application is either console or gui, it cannot be both.

Scrcpy should be both: it outputs important information to console, but
we still want to be able to ignore the console and launch it without a
visible cmd.exe window.

Therefore, build two binaries:
 - scrcpy.exe
 - scrcpy-noconsole.exe
2018-05-28 20:45:54 +02:00
Romain Vimont
106b87a4d2 Add cross-compilation scripts for Windows
Build the Windows binary from mingw on Linux, using the official
prebuilt binaries for ffmpeg, SDL2 and adb.

MSYS2 and all its packaged dll are not necessary anymore.
2018-05-28 20:45:54 +02:00
Romain Vimont
7d68316f45 Indicate that libs are included for Windows 2018-05-28 20:45:54 +02:00
Romain Vimont
e729b76176 Indicate that scrcpy also works over TCP/IP 2018-05-28 20:45:54 +02:00
Romain Vimont
6274cc3767 Document APK drag & drop
Add the drag & drop shortcut for installing an APK file to the README
and the help.
2018-05-28 20:45:54 +02:00
Romain Vimont
9c6f9b24f9 Quote apk path on Windows
Windows will parse the string, so the local name must be quoted.
2018-05-28 20:45:54 +02:00
Adonis Najimi
e2a2973990 Drag and drop to install apk files from computer
<https://github.com/Genymobile/scrcpy/pull/133>
2018-05-28 20:45:54 +02:00
Romain Vimont
41419a2e77 Fix net_send_all() warning
If len is not positive, w could be returned uninitialized.
2018-05-28 20:21:56 +02:00
Romain Vimont
e3d0a59c80 Fix proc_show_touches warning
In practice, proc_show_touches may not be used uninitialized, since it
checks the flag options->show_touches, but the compiler can't know that,
so initialize it to avoid the warning.
2018-05-28 20:21:50 +02:00
20 changed files with 643 additions and 108 deletions

23
FAQ.md
View File

@@ -7,29 +7,8 @@ with it.
Here are the common reported problems and their status.
### On Windows, I have no output in the console
When run in `cmd.exe`, the application does not print anything. Even `scrcpy
--help` have no output. We don't know why yet.
However, if you run the very same `scrcpy.exe` from
[MSYS2](https://www.msys2.org/) (`mingw64`), then it correctly prints output.
As a workaround, redirect outputs to files, so that you can read the files
afterwards:
```bash
scrcpy >stdout 2>stderr
type stdout
type stderr
```
_Note that all SDL logs are printed to stderr._
### On Windows, when I start the application, nothing happens
The previous problem does not help to get a clue about the cause.
### On Windows, my device is not detected
The most common is your device not being detected by `adb`, or is unauthorized.
Check everything is ok by calling:

View File

@@ -1,65 +0,0 @@
# This makefile provides recipes to build a "portable" version of scrcpy.
#
# Here, "portable" means that the client and server binaries are expected to be
# anywhere, but in the same directory, instead of well-defined separate
# locations (e.g. /usr/bin/scrcpy and /usr/share/scrcpy/scrcpy-server.jar).
#
# In particular, this implies to change the location from where the client push
# the server to the device.
#
# "make release-portable" builds a zip containing the client and the server.
#
# On Windows with MSYS2/mingw64, execute:
# GRADLE="$PWD/gradlew" mingw32-make release-portable
#
# This is a simple Makefile because Meson is not flexible enough to execute some
# arbitrary commands.
.PHONY: default clean build-portable release-portable dist-portable dist-portable-zip sums test check
GRADLE ?= ./gradlew
PORTABLE_BUILD_DIR := build-portable
DIST := dist
TARGET_DIR := scrcpy
VERSION := $(shell git describe --tags --always)
TARGET := $(TARGET_DIR)-$(VERSION).zip
default:
@echo 'You must specify a target. Try: make release-portable'
clean:
$(GRADLE) clean
rm -rf "$(PORTABLE_BUILD_DIR)" "$(DIST)"
build-portable:
[ -d "$(PORTABLE_BUILD_DIR)" ] || ( mkdir "$(PORTABLE_BUILD_DIR)" && \
meson "$(PORTABLE_BUILD_DIR)" \
--buildtype release --strip -Db_lto=true \
-Doverride_server_path=scrcpy-server.jar )
ninja -C "$(PORTABLE_BUILD_DIR)"
release-portable: clean dist-portable-zip sums
@echo "Release created in $(DIST)/."
dist-portable: build-portable
mkdir -p "$(DIST)/$(TARGET_DIR)"
cp "$(PORTABLE_BUILD_DIR)"/server/scrcpy-server.jar "$(DIST)/$(TARGET_DIR)/"
cp "$(PORTABLE_BUILD_DIR)"/app/scrcpy "$(DIST)/$(TARGET_DIR)/"
dist-portable-zip: dist-portable
cd "$(DIST)"; \
zip -r "$(TARGET)" "$(TARGET_DIR)"
sums:
cd "$(DIST)"; \
sha256sum *.zip > SHA256SUM.txt
test: build-portable
$(GRADLE) test
ninja -C "$(PORTABLE_BUILD_DIR)" test
check: build-portable
$(GRADLE) check
ninja -C "$(PORTABLE_BUILD_DIR)" test

131
Makefile.CrossWindows Normal file
View File

@@ -0,0 +1,131 @@
# This makefile provides recipes to build a "portable" version of scrcpy for
# Windows.
#
# Here, "portable" means that the client and server binaries are expected to be
# anywhere, but in the same directory, instead of well-defined separate
# locations (e.g. /usr/bin/scrcpy and /usr/share/scrcpy/scrcpy-server.jar).
#
# In particular, this implies to change the location from where the client push
# the server to the device.
.PHONY: default clean \
build-server \
prepare-deps-win32 prepare-deps-win64 \
build-win32 build-win32-noconsole \
build-win64 build-win64-noconsole \
dist-win32 dist-win64 \
zip-win32 zip-win64 \
sums release
GRADLE ?= ./gradlew
SERVER_BUILD_DIR := build-server
WIN32_BUILD_DIR := build-win32
WIN32_NOCONSOLE_BUILD_DIR := build-win32-noconsole
WIN64_BUILD_DIR := build-win64
WIN64_NOCONSOLE_BUILD_DIR := build-win64-noconsole
DIST := dist
WIN32_TARGET_DIR := scrcpy-win32
WIN64_TARGET_DIR := scrcpy-win64
VERSION := $(shell git describe --tags --always)
WIN32_TARGET := $(WIN32_TARGET_DIR)-$(VERSION).zip
WIN64_TARGET := $(WIN64_TARGET_DIR)-$(VERSION).zip
release: clean zip-win32 zip-win64 sums
@echo "Release created in $(DIST)/."
clean:
$(GRADLE) clean
rm -rf "$(SERVER_BUILD_DIR)" "$(WIN32_BUILD_DIR)" "$(WIN64_BUILD_DIR)" "$(DIST)"
build-server:
[ -d "$(SERVER_BUILD_DIR)" ] || ( mkdir "$(SERVER_BUILD_DIR)" && \
meson "$(SERVER_BUILD_DIR)" \
--buildtype release -Dbuild_app=false )
ninja -C "$(SERVER_BUILD_DIR)"
prepare-deps-win32:
-$(MAKE) -C prebuilt-deps prepare-win32
build-win32: prepare-deps-win32
[ -d "$(WIN32_BUILD_DIR)" ] || ( mkdir "$(WIN32_BUILD_DIR)" && \
meson "$(WIN32_BUILD_DIR)" \
--cross-file cross_win32.txt \
--buildtype release --strip -Db_lto=true \
-Dbuild_server=false \
-Doverride_server_path=scrcpy-server.jar )
ninja -C "$(WIN32_BUILD_DIR)"
build-win32-noconsole: prepare-deps-win32
[ -d "$(WIN32_NOCONSOLE_BUILD_DIR)" ] || ( mkdir "$(WIN32_NOCONSOLE_BUILD_DIR)" && \
meson "$(WIN32_NOCONSOLE_BUILD_DIR)" \
--cross-file cross_win32.txt \
--buildtype release --strip -Db_lto=true \
-Dbuild_server=false \
-Dwindows_noconsole=true \
-Doverride_server_path=scrcpy-server.jar )
ninja -C "$(WIN32_NOCONSOLE_BUILD_DIR)"
prepare-deps-win64:
-$(MAKE) -C prebuilt-deps prepare-win64
build-win64: prepare-deps-win64
[ -d "$(WIN64_BUILD_DIR)" ] || ( mkdir "$(WIN64_BUILD_DIR)" && \
meson "$(WIN64_BUILD_DIR)" \
--cross-file cross_win64.txt \
--buildtype release --strip -Db_lto=true \
-Dbuild_server=false \
-Doverride_server_path=scrcpy-server.jar )
ninja -C "$(WIN64_BUILD_DIR)"
build-win64-noconsole: prepare-deps-win64
[ -d "$(WIN64_NOCONSOLE_BUILD_DIR)" ] || ( mkdir "$(WIN64_NOCONSOLE_BUILD_DIR)" && \
meson "$(WIN64_NOCONSOLE_BUILD_DIR)" \
--cross-file cross_win64.txt \
--buildtype release --strip -Db_lto=true \
-Dbuild_server=false \
-Dwindows_noconsole=true \
-Doverride_server_path=scrcpy-server.jar )
ninja -C "$(WIN64_NOCONSOLE_BUILD_DIR)"
dist-win32: build-server build-win32 build-win32-noconsole
mkdir -p "$(DIST)/$(WIN32_TARGET_DIR)"
cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server.jar "$(DIST)/$(WIN32_TARGET_DIR)/"
cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
cp "$(WIN32_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/scrcpy-noconsole.exe"
cp prebuilt-deps/ffmpeg-4.0-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.0-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.0-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.0-win32-shared/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/SDL2-2.0.8/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
dist-win64: build-server build-win64 build-win64-noconsole
mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)"
cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server.jar "$(DIST)/$(WIN64_TARGET_DIR)/"
cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
cp "$(WIN64_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/scrcpy-noconsole.exe"
cp prebuilt-deps/ffmpeg-4.0-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.0-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.0-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.0-win64-shared/bin/swresample-3.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/SDL2-2.0.8/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
zip-win32: dist-win32
cd "$(DIST)"; \
zip -r "$(WIN32_TARGET)" "$(WIN32_TARGET_DIR)"
zip-win64: dist-win64
cd "$(DIST)"; \
zip -r "$(WIN64_TARGET)" "$(WIN64_TARGET_DIR)"
sums:
cd "$(DIST)"; \
sha256sum *.zip > SHA256SUMS.txt

View File

@@ -1,8 +1,8 @@
# scrcpy
This application provides display and control of Android devices connected on
USB. It does not require any _root_ access. It works on _GNU/Linux_, _Windows_
and _MacOS_.
USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access.
It works on _GNU/Linux_, _Windows_ and _MacOS_.
![screenshot](assets/screenshot-debian-600.jpg)
@@ -23,7 +23,8 @@ and extract the following files to a directory accessible from your `PATH`:
Make sure you [enabled adb debugging][enable-adb] on your device(s).
The client requires [FFmpeg] and [LibSDL2].
The client requires [FFmpeg] and [LibSDL2]. On Windows, they are included in the
[prebuilt application](#windows).
[adb]: https://developer.android.com/studio/command-line/adb.html
[enable-adb]: https://developer.android.com/studio/command-line/adb.html#Enabling
@@ -90,8 +91,37 @@ For Windows, for simplicity, a prebuilt archive with all the dependencies
_(It's just a portable version including _dll_ copied from MSYS2.)_
Instead, you may want to build it manually. You need [MSYS2] to build the
project. From an MSYS2 terminal, install the required packages:
Instead, you may want to build it manually.
##### Cross-compile from Linux
This is the preferred method (and the way the release is built).
From _Debian_, install _mingw_:
```bash
sudo apt install mingw-w64 mingw-w64-tools
```
You also need the JDK to build the server:
```bash
sudo apt install openjdk-8-jdk
```
Then generate the releases:
```bash
make -f Makefile.CrossWindows
```
It will generate win32 and win64 releases into `dist/`.
##### In MSYS2
From Windows, you need [MSYS2] to build the project. From an MSYS2 terminal,
install the required packages:
[MSYS2]: http://www.msys2.org/
@@ -300,6 +330,7 @@ To run without installing:
| turn screen on | _Right-click²_ |
| paste computer clipboard to device | `Ctrl`+`v` |
| enable/disable FPS counter (on stdout) | `Ctrl`+`i` |
| install APK from computer | drag & drop APK file |
_¹Double-click on black borders to remove them._
_²Right-click turns the screen on if it was off, presses BACK otherwise._
@@ -345,5 +376,8 @@ Read the [developers page].
## Articles
- [Introducing scrcpy](https://blog.rom1v.com/2018/03/introducing-scrcpy/)
- [Scrcpy now works wirelessly](https://www.genymotion.com/blog/open-source-project-scrcpy-now-works-wirelessly/)
- [Introducing scrcpy][article-intro]
- [Scrcpy now works wirelessly][article-tcpip]
[article-intro]: https://blog.rom1v.com/2018/03/introducing-scrcpy/
[article-tcpip]: https://www.genymotion.com/blog/open-source-project-scrcpy-now-works-wirelessly/

View File

@@ -9,6 +9,7 @@ src = [
'src/fpscounter.c',
'src/frames.c',
'src/inputmanager.c',
'src/installer.c',
'src/lockutil.c',
'src/net.c',
'src/scrcpy.c',
@@ -18,12 +19,54 @@ src = [
'src/tinyxpm.c',
]
dependencies = [
dependency('libavformat'),
dependency('libavcodec'),
dependency('libavutil'),
dependency('sdl2'),
]
if not meson.is_cross_build()
# native build
dependencies = [
dependency('libavformat'),
dependency('libavcodec'),
dependency('libavutil'),
dependency('sdl2'),
]
else
# cross-compile mingw32 build (from Linux to Windows)
cc = meson.get_compiler('c')
prebuilt_sdl2 = meson.get_cross_property('prebuilt_sdl2')
sdl2_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2 + '/bin'
sdl2_lib_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2 + '/lib'
sdl2_include_dir = '../prebuilt-deps/' + prebuilt_sdl2 + '/include'
sdl2 = declare_dependency(
dependencies: [
cc.find_library('SDL2', dirs: sdl2_bin_dir),
cc.find_library('SDL2main', dirs: sdl2_lib_dir),
],
include_directories: include_directories(sdl2_include_dir)
)
prebuilt_ffmpeg_shared = meson.get_cross_property('prebuilt_ffmpeg_shared')
prebuilt_ffmpeg_dev = meson.get_cross_property('prebuilt_ffmpeg_dev')
ffmpeg_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_ffmpeg_shared + '/bin'
ffmpeg_include_dir = '../prebuilt-deps/' + prebuilt_ffmpeg_dev + '/include'
ffmpeg = declare_dependency(
dependencies: [
cc.find_library('avcodec-58', dirs: ffmpeg_bin_dir),
cc.find_library('avformat-58', dirs: ffmpeg_bin_dir),
cc.find_library('avutil-56', dirs: ffmpeg_bin_dir),
],
include_directories: include_directories(ffmpeg_include_dir)
)
dependencies = [
ffmpeg,
sdl2,
cc.find_library('mingw32')
]
endif
cc = meson.get_compiler('c')
@@ -42,7 +85,7 @@ conf = configuration_data()
conf.set('BUILD_DEBUG', get_option('buildtype') == 'debug')
# the version, updated on release
conf.set_quoted('SCRCPY_VERSION', '1.1')
conf.set_quoted('SCRCPY_VERSION', '1.2')
# the prefix used during configuration (meson --prefix=PREFIX)
conf.set_quoted('PREFIX', get_option('prefix'))
@@ -83,10 +126,22 @@ conf.set('SKIP_FRAMES', get_option('skip_frames'))
# enable High DPI support
conf.set('HIDPI_SUPPORT', get_option('hidpi_support'))
# disable console on Windows
conf.set('WINDOWS_NOCONSOLE', get_option('windows_noconsole'))
configure_file(configuration: conf, output: 'config.h')
src_dir = include_directories('src')
executable('scrcpy', src, dependencies: dependencies, include_directories: src_dir, install: true)
if get_option('windows_noconsole')
c_args = [ '-mwindows' ]
link_args = [ '-mwindows' ]
else
c_args = []
link_args = []
endif
executable('scrcpy', src, dependencies: dependencies, include_directories: src_dir, install: true, c_args: c_args, link_args: link_args)
### TESTS

View File

@@ -72,6 +72,21 @@ process_t adb_push(const char *serial, const char *local, const char *remote) {
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
}
process_t adb_install(const char *serial, const char *local) {
#ifdef __WINDOWS__
// Windows will parse the string, so the local name must be quoted (see sys/win/command.c)
size_t len = strlen(local);
char quoted[len + 3];
memcpy(&quoted[1], local, len);
quoted[0] = '"';
quoted[len + 1] = '"';
quoted[len + 2] = '\0';
local = quoted;
#endif
const char *const adb_cmd[] = {"install", "-r", local};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
}
process_t adb_remove_path(const char *serial, const char *path) {
const char *const adb_cmd[] = {"shell", "rm", path};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));

View File

@@ -42,6 +42,7 @@ process_t adb_forward_remove(const char *serial, uint16_t local_port);
process_t adb_reverse(const char *serial, const char *device_socket_name, uint16_t local_port);
process_t adb_reverse_remove(const char *serial, const char *device_socket_name);
process_t adb_push(const char *serial, const char *local, const char *remote);
process_t adb_install(const char *serial, const char *local);
process_t adb_remove_path(const char *serial, const char *path);
// convenience function to wait for a successful process execution

178
app/src/installer.c Normal file
View File

@@ -0,0 +1,178 @@
#include "installer.h"
#include <string.h>
#include "command.h"
#include "lockutil.h"
#include "log.h"
// NOTE(adopi) this can be more generic:
// it could be used with a command queue instead of a filename queue
// then we would have a generic invoker (useful if we want to handle more async commands)
SDL_bool apk_queue_is_empty(const struct apk_queue *queue) {
return queue->head == queue->tail;
}
SDL_bool apk_queue_is_full(const struct apk_queue *queue) {
return (queue->head + 1) % APK_QUEUE_SIZE == queue->tail;
}
SDL_bool apk_queue_init(struct apk_queue *queue) {
queue->head = 0;
queue->tail = 0;
return SDL_TRUE;
}
void apk_queue_destroy(struct apk_queue *queue) {
int i = queue->tail;
while (i != queue->head) {
SDL_free(queue->data[i]);
i = (i + 1) % APK_QUEUE_SIZE;
}
}
SDL_bool apk_queue_push(struct apk_queue *queue, const char *apk) {
if (apk_queue_is_full(queue)) {
return SDL_FALSE;
}
queue->data[queue->head] = SDL_strdup(apk);
queue->head = (queue->head + 1) % APK_QUEUE_SIZE;
return SDL_TRUE;
}
SDL_bool apk_queue_take(struct apk_queue *queue, char **apk) {
if (apk_queue_is_empty(queue)) {
return SDL_FALSE;
}
// transfer ownership
*apk = queue->data[queue->tail];
queue->tail = (queue->tail + 1) % APK_QUEUE_SIZE;
return SDL_TRUE;
}
SDL_bool installer_init(struct installer *installer, const char *serial) {
if (!apk_queue_init(&installer->queue)) {
return SDL_FALSE;
}
if (!(installer->mutex = SDL_CreateMutex())) {
return SDL_FALSE;
}
if (!(installer->event_cond = SDL_CreateCond())) {
SDL_DestroyMutex(installer->mutex);
return SDL_FALSE;
}
if (serial) {
installer->serial = SDL_strdup(serial);
if (!installer->serial) {
LOGW("Cannot strdup serial");
return SDL_FALSE;
}
} else {
installer->serial = NULL;
}
// lazy initialization
installer->initialized = SDL_FALSE;
installer->stopped = SDL_FALSE;
return SDL_TRUE;
}
void installer_destroy(struct installer *installer) {
SDL_DestroyCond(installer->event_cond);
SDL_DestroyMutex(installer->mutex);
apk_queue_destroy(&installer->queue);
SDL_free((void *) installer->serial);
}
SDL_bool installer_install_apk(struct installer *installer, const char *apk) {
SDL_bool res;
// start installer if it's used for the first time
if (!installer->initialized) {
if (!installer_start(installer)) {
return SDL_FALSE;
}
installer->initialized = SDL_TRUE;
}
mutex_lock(installer->mutex);
SDL_bool was_empty = apk_queue_is_empty(&installer->queue);
res = apk_queue_push(&installer->queue, apk);
if (was_empty) {
cond_signal(installer->event_cond);
}
mutex_unlock(installer->mutex);
return res;
}
static int run_installer(void *data) {
struct installer *installer = data;
for (;;) {
mutex_lock(installer->mutex);
while (!installer->stopped && apk_queue_is_empty(&installer->queue)) {
cond_wait(installer->event_cond, installer->mutex);
}
if (installer->stopped) {
// stop immediately, do not process further events
mutex_unlock(installer->mutex);
break;
}
char *current_apk;
#ifdef BUILD_DEBUG
bool non_empty = apk_queue_take(&installer->queue, &current_apk);
SDL_assert(non_empty);
#else
apk_queue_take(&installer->queue, &current_apk);
#endif
LOGI("Installing %s...", current_apk);
process_t process = adb_install(installer->serial, current_apk);
installer->current_process = process;
mutex_unlock(installer->mutex);
if (process_check_success(process, "adb install")) {
LOGI("%s installed successfully", current_apk);
} else {
LOGE("Failed to install %s", current_apk);
}
SDL_free(current_apk);
}
return 0;
}
SDL_bool installer_start(struct installer *installer) {
LOGD("Starting installer thread");
installer->thread = SDL_CreateThread(run_installer, "installer", installer);
if (!installer->thread) {
LOGC("Could not start installer thread");
return SDL_FALSE;
}
return SDL_TRUE;
}
void installer_stop(struct installer *installer) {
mutex_lock(installer->mutex);
installer->stopped = SDL_TRUE;
cond_signal(installer->event_cond);
if (installer->current_process != PROCESS_NONE) {
if (!cmd_terminate(installer->current_process)) {
LOGW("Cannot terminate install process");
}
cmd_simple_wait(installer->current_process, NULL);
installer->current_process = PROCESS_NONE;
}
mutex_unlock(installer->mutex);
}
void installer_join(struct installer *installer) {
SDL_WaitThread(installer->thread, NULL);
}

40
app/src/installer.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef APK_INSTALLER_H
#define APK_INSTALLER_H
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_stdinc.h>
#include <SDL2/SDL_thread.h>
#include "command.h"
#define APK_QUEUE_SIZE 16
// NOTE(AdoPi) apk_queue and control_event can use a generic queue
struct apk_queue {
char *data[APK_QUEUE_SIZE];
int tail;
int head;
};
struct installer {
const char *serial;
SDL_Thread *thread;
SDL_mutex *mutex;
SDL_cond *event_cond;
SDL_bool stopped;
SDL_bool initialized;
process_t current_process;
struct apk_queue queue;
};
SDL_bool installer_init(struct installer *installer, const char *serial);
void installer_destroy(struct installer *installer);
SDL_bool installer_start(struct installer *installer);
void installer_stop(struct installer *installer);
void installer_join(struct installer *installer);
// install an apk
SDL_bool installer_install_apk(struct installer *installer, const char *filename);
#endif

View File

@@ -98,6 +98,9 @@ static void usage(const char *arg0) {
"\n"
" Ctrl+i\n"
" enable/disable FPS counter (print frames/second in logs)\n"
"\n"
" Drag & drop APK file\n"
" install APK from computer\n"
"\n",
arg0,
DEFAULT_BIT_RATE,

View File

@@ -87,7 +87,7 @@ ssize_t net_send(socket_t socket, const void *buf, size_t len) {
}
ssize_t net_send_all(socket_t socket, const void *buf, size_t len) {
ssize_t w;
ssize_t w = 0;
while (len > 0) {
w = send(socket, buf, len, 0);
if (w == -1) {

View File

@@ -22,12 +22,14 @@
#include "screen.h"
#include "server.h"
#include "tinyxpm.h"
#include "installer.h"
static struct server server = SERVER_INITIALIZER;
static struct screen screen = SCREEN_INITIALIZER;
static struct frames frames;
static struct decoder decoder;
static struct controller controller;
static struct installer installer;
static struct input_manager input_manager = {
.controller = &controller,
@@ -102,6 +104,9 @@ static void event_loop(void) {
case SDL_MOUSEBUTTONUP:
input_manager_process_mouse_button(&input_manager, &event.button);
break;
case SDL_DROPFILE:
installer_install_apk(&installer, event.drop.file);
break;
}
}
}
@@ -125,7 +130,7 @@ SDL_bool scrcpy(const struct scrcpy_options *options) {
return SDL_FALSE;
}
process_t proc_show_touches;
process_t proc_show_touches = PROCESS_NONE;
SDL_bool show_touches_waited;
if (options->show_touches) {
LOGI("Enable show_touches");
@@ -169,6 +174,12 @@ SDL_bool scrcpy(const struct scrcpy_options *options) {
goto finally_destroy_server;
}
if (!installer_init(&installer, server.serial)) {
ret = SDL_FALSE;
server_stop(&server);
goto finally_destroy_frames;
}
decoder_init(&decoder, &frames, device_socket);
// now we consumed the header values, the socket receives the video stream
@@ -176,7 +187,7 @@ SDL_bool scrcpy(const struct scrcpy_options *options) {
if (!decoder_start(&decoder)) {
ret = SDL_FALSE;
server_stop(&server);
goto finally_destroy_frames;
goto finally_destroy_installer;
}
if (!controller_init(&controller, device_socket)) {
@@ -214,6 +225,10 @@ finally_stop_decoder:
// stop the server before decoder_join() to wake up the decoder
server_stop(&server);
decoder_join(&decoder);
finally_destroy_installer:
installer_stop(&installer);
installer_join(&installer);
installer_destroy(&installer);
finally_destroy_frames:
frames_destroy(&frames);
finally_destroy_server:

View File

@@ -1,5 +1,6 @@
#include "command.h"
#include "config.h"
#include "log.h"
#include "strutil.h"
@@ -20,7 +21,12 @@ HANDLE cmd_execute(const char *path, const char *const argv[]) {
return NULL;
}
if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
#ifdef WINDOWS_NOCONSOLE
int flags = CREATE_NO_WINDOW;
#else
int flags = 0;
#endif
if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi)) {
return NULL;
}

20
cross_win32.txt Normal file
View File

@@ -0,0 +1,20 @@
# apt install mingw-w64 mingw-w64-tools
[binaries]
name = 'mingw'
c = '/usr/bin/i686-w64-mingw32-gcc'
cpp = '/usr/bin/i686-w64-mingw32-g++'
ar = '/usr/bin/i686-w64-mingw32-ar'
strip = '/usr/bin/i686-w64-mingw32-strip'
pkgconfig = '/usr/bin/i686-w64-mingw32-pkg-config'
[host_machine]
system = 'windows'
cpu_family = 'x86'
cpu = 'i686'
endian = 'little'
[properties]
prebuilt_ffmpeg_shared = 'ffmpeg-4.0-win32-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.0-win32-dev'
prebuilt_sdl2 = 'SDL2-2.0.8/i686-w64-mingw32'

20
cross_win64.txt Normal file
View File

@@ -0,0 +1,20 @@
# apt install mingw-w64 mingw-w64-tools
[binaries]
name = 'mingw'
c = '/usr/bin/x86_64-w64-mingw32-gcc'
cpp = '/usr/bin/x86_64-w64-mingw32-g++'
ar = '/usr/bin/x86_64-w64-mingw32-ar'
strip = '/usr/bin/x86_64-w64-mingw32-strip'
pkgconfig = '/usr/bin/x86_64-w64-mingw32-pkg-config'
[host_machine]
system = 'windows'
cpu_family = 'x86'
cpu = 'x86_64'
endian = 'little'
[properties]
prebuilt_ffmpeg_shared = 'ffmpeg-4.0-win64-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.0-win64-dev'
prebuilt_sdl2 = 'SDL2-2.0.8/x86_64-w64-mingw32'

View File

@@ -1,5 +1,6 @@
option('build_app', type: 'boolean', value: true, description: 'Build the client')
option('build_server', type: 'boolean', value: true, description: 'Build the server')
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('override_server_path', type: 'string', description: 'Hardcoded path to find the server at runtime')
option('skip_frames', type: 'boolean', value: true, description: 'Always display the most recent frame')

4
prebuilt-deps/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*
!/.gitignore
!/Makefile
!/prepare-dep

40
prebuilt-deps/Makefile Normal file
View File

@@ -0,0 +1,40 @@
.PHONY: prepare-win32 prepare-win64 \
prepare-ffmpeg-shared-win32 \
prepare-ffmpeg-dev-win32 \
prepare-ffmpeg-shared-win64 \
prepare-ffmpeg-dev-win64 \
prepare-sdl2 \
prepare-adb
prepare-win32: prepare-sdl2 prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32 prepare-adb
prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb
prepare-ffmpeg-shared-win32:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.0-win32-shared.zip \
530c92df0ca14c35901b4b681847d62da3c50a0cc9b7ced37b04968f6b5c243d \
ffmpeg-4.0-win32-shared
prepare-ffmpeg-dev-win32:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.0-win32-dev.zip \
e2f5200b5e73c4d0abb9b89c4ffc0438f92a0aadc54c81cf57e18c81a9f11c6b \
ffmpeg-4.0-win32-dev
prepare-ffmpeg-shared-win64:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.0-win64-shared.zip \
8fe2d344463dbefc2db4239a4203a55ed0324faceaae57276a40c4fabda84c37 \
ffmpeg-4.0-win64-shared
prepare-ffmpeg-dev-win64:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.0-win64-dev.zip \
facced738eabfc53fa92834dea8b24426f64db61298688fed480145945be07fa \
ffmpeg-4.0-win64-dev
prepare-sdl2:
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.8-mingw.tar.gz \
ffff7305d634aff5e1df5b7bb935435c3a02c8b03ad94a1a2be9169a558a7961 \
SDL2-2.0.8
prepare-adb:
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r27.0.1-windows.zip \
880662adfb0d6911ff250b9e13930ae1a4110fc36d5866afd4f8f56d935f7939 \
platform-tools

58
prebuilt-deps/prepare-dep Executable file
View File

@@ -0,0 +1,58 @@
#!/bin/bash
set -e
url="$1"
sum="$2"
dir="$3"
checksum() {
local file="$1"
local sum="$2"
echo "$file: verifying checksum..."
echo "$sum $file" | sha256sum -c
}
get_file() {
local url="$1"
local file="$2"
local sum="$3"
if [[ -f "$file" ]]
then
echo "$file: found"
else
echo "$file: not found, downloading..."
wget "$url" -O "$file"
fi
checksum "$file" "$sum"
}
extract() {
local file="$1"
echo "Extracting $file..."
if [[ "$file" == *.zip ]]
then
unzip -q "$file"
elif [[ "$file" == *.tar.gz ]]
then
tar xf "$file"
else
echo "Unsupported file: $file"
return 1
fi
}
get_dep() {
local url="$1"
local sum="$2"
local dir="$3"
local file="${url##*/}"
if [[ -d "$dir" ]]
then
echo "$dir: found"
else
echo "$dir: not found"
get_file "$url" "$file" "$sum"
extract "$file"
fi
}
get_dep "$1" "$2" "$3"

View File

@@ -6,8 +6,8 @@ android {
applicationId "com.genymobile.scrcpy"
minSdkVersion 21
targetSdkVersion 27
versionCode 2
versionName "1.1"
versionCode 3
versionName "1.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {