Compare commits

..

23 Commits

Author SHA1 Message Date
Romain Vimont
a9c8fa305d Fix segfault on recording with old FFmpeg
The AVPacket fields side_data and side_data_elems were not initialized
by av_packet_ref() in old FFmpeg versions (prior to [1]).

As a consequence, on av_packet_unref(), side_data was freed, causing a
segfault.

Fixes <https://github.com/Genymobile/scrcpy/issues/707>

[1]: <http://git.videolan.org/gitweb.cgi/ffmpeg.git/?p=ffmpeg.git;a=commitdiff;h=3b4026e15110547892d5d770b6b43c9e34df458f>
2019-08-13 18:58:36 +02:00
Romain Vimont
20b3f101a4 Print gradle output on compiling
Enable the attribute "console" of custom_target() introduced in meson
0.48. This allows to get a feedback of what gradle does (which can takes
a very long time).

This produces warnings because we declare to support meson >= 0.37, but
we don't want to stop supporting older versions for that. Older versions
just ignore the option:

> WARNING: Unknown keyword arguments in target scrcpy-server: console

Newer meson versions use it, but warn because we declare supporting
older versions:

> WARNING: Project targetting '>= 0.37' but tried to use feature
> introduced in '0.48.0': console arg in custom_target

Meson does not support conditional branches to suppress such warnings,
so just keep the warnings.
2019-08-09 15:15:28 +02:00
Romain Vimont
686733023b Merge pull request #708 from toddsierens/patch-1
Update WindowManager.java
2019-08-08 23:24:47 +02:00
toddsierens
27eacc3c11 Update WindowManager.java 2019-08-08 17:17:48 -04:00
Romain Vimont
8507fea271 Record a packet with its duration
Record a packet only once the following has been received, so that we
can set its duration before muxing it.

Fixes <https://github.com/Genymobile/scrcpy/issues/702>
2019-08-08 18:57:52 +02:00
Romain Vimont
8a08ca0f3d Merge pull request #692 from msfjarvis/msf/uprev-dependencies
Upgrade build dependencies
2019-08-07 23:42:06 +02:00
Harsh Shandilya
44fb692a64 Uprev Gradle wrapper to latest stable
Signed-off-by: Harsh Shandilya <msfjarvis@gmail.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2019-08-07 23:41:36 +02:00
Harsh Shandilya
3f77c29c95 Uprev AGP to latest stable
Signed-off-by: Harsh Shandilya <msfjarvis@gmail.com>
2019-08-07 23:20:16 +02:00
Romain Vimont
9a50b65b33 Merge pull request #695 from schwabe/schwabe/fix_null_queue
Fix building on OS X (missing NULL in queue.h)
2019-08-05 15:26:06 +02:00
Arne Schwabe
c05056343b Fix building on OS X (missing NULL in queue.h)
Headers seem to be a bit different in Apple land and you need to include
stddef.h explicitly to the NULL declaration.

This also makes the code a bit more correct, as stddef.h is the header
in the C standard that defines NULL
(https://en.cppreference.com/w/cpp/header/cstddef).
2019-08-05 15:02:05 +02:00
Romain Vimont
9bcee4ea42 Update links to v1.10 in README and BUILD 2019-08-04 22:03:45 +02:00
Romain Vimont
c28619e4e8 Bump version to 1.10 2019-08-04 16:41:04 +02:00
Romain Vimont
8969444ff2 List scrcpy characteristics in README
They were listed in the blog post introducing scrcpy:
<https://blog.rom1v.com/2018/03/introducing-scrcpy/>
2019-08-04 16:23:42 +02:00
Romain Vimont
b54f0bfe48 Upgrade SDL (2.0.10) for Windows
Include the latest version of SDL in Windows releases.
2019-08-04 16:23:42 +02:00
Romain Vimont
0aec1e502e Update platform-tools (29.0.2) for Windows
Include the latest version of adb in Windows releases.
2019-08-04 16:23:42 +02:00
Romain Vimont
c3a58ad10f Upgrade FFmpeg (4.1.4) for Windows
Include the latest version of FFmpeg in Windows releases.
2019-08-04 16:23:42 +02:00
Romain Vimont
b0184f2869 Initialize queue "last" field
The compiler is not always able to see that "last" is always initialized
before being used, so always initialize it.
2019-08-04 16:22:39 +02:00
Romain Vimont
e2ac996183 Use Cmd instead of Ctrl on macOS when possible
Fixes <https://github.com/Genymobile/scrcpy/issues/642>
2019-08-03 23:13:44 +02:00
Romain Vimont
5e4ccfd832 Use generic FIFO queue for recording
Replace the specific recording queue by the new generic FIFO queue
implementation.
2019-08-01 23:15:47 +02:00
Romain Vimont
53b6ee2cf4 Add generic intrusive FIFO queue
We need several FIFO queues (a queue of packets, a queue of messages,
etc.).

Some of them are implemented using cbuf, a generic circular buffer. But
for recording, we need to store the packets in an unbounded queue until
they are written, so the queue was implemented manually.

Create a generic implementation (using macros) to avoid reimplementing
it every time.
2019-08-01 23:14:50 +02:00
Romain Vimont
26213f1031 Fix cbuf documentation 2019-08-01 22:50:03 +02:00
Romain Vimont
96b5067cbf Remove unnecessary backslash in cbuf 2019-08-01 22:08:34 +02:00
Romain Vimont
421e4be399 Remove root directory from Windows zip releases
Put the scrcpy files at the root of the zip archive. This avoids an
unnecessary level of directories when extracting.
2019-07-31 16:35:14 +02:00
28 changed files with 413 additions and 367 deletions

View File

@@ -234,10 +234,10 @@ You can then [run](README.md#run) _scrcpy_.
## Prebuilt server ## Prebuilt server
- [`scrcpy-server-v1.9.jar`][direct-scrcpy-server] - [`scrcpy-server-v1.10.jar`][direct-scrcpy-server]
_(SHA-256: ad7e539f100e48259b646f26982bc63e0a60a81ac87ae135e242855bef69bd1a)_ _(SHA-256: cbeb1a4e046f1392c1dc73c3ccffd7f86dec4636b505556ea20929687a119390)_
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.9/scrcpy-server-v1.9.jar [direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.10/scrcpy-server-v1.10.jar
Download the prebuilt server somewhere, and specify its path during the Meson Download the prebuilt server somewhere, and specify its path during the Meson
configuration: configuration:

View File

@@ -100,37 +100,38 @@ dist-win32: build-server build-win32 build-win32-noconsole
cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server.jar "$(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_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 "$(WIN32_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/scrcpy-noconsole.exe"
cp prebuilt-deps/ffmpeg-4.1.3-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.1.4-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1.3-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.1.4-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1.3-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.1.4-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1.3-win32-shared/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.1.4-win32-shared/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1.4-win32-shared/bin/swscale-5.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/adb.exe "$(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/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.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)/" cp prebuilt-deps/SDL2-2.0.10/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
dist-win64: build-server build-win64 build-win64-noconsole dist-win64: build-server build-win64 build-win64-noconsole
mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)" mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)"
cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server.jar "$(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_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 "$(WIN64_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/scrcpy-noconsole.exe"
cp prebuilt-deps/ffmpeg-4.1.3-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.1.4-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1.3-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.1.4-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1.3-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.1.4-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1.3-win64-shared/bin/swresample-3.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.1.4-win64-shared/bin/swresample-3.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/ffmpeg-4.1.3-win64-shared/bin/swscale-5.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/ffmpeg-4.1.4-win64-shared/bin/swscale-5.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/adb.exe "$(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/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp prebuilt-deps/platform-tools/AdbWinUsbApi.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)/" cp prebuilt-deps/SDL2-2.0.10/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
zip-win32: dist-win32 zip-win32: dist-win32
cd "$(DIST)"; \ cd "$(DIST)/$(WIN32_TARGET_DIR)"; \
zip -r "$(WIN32_TARGET)" "$(WIN32_TARGET_DIR)" zip -r "../$(WIN32_TARGET)" .
zip-win64: dist-win64 zip-win64: dist-win64
cd "$(DIST)"; \ cd "$(DIST)/$(WIN64_TARGET_DIR)"; \
zip -r "$(WIN64_TARGET)" "$(WIN64_TARGET_DIR)" zip -r "../$(WIN64_TARGET)" .
sums: sums:
cd "$(DIST)"; \ cd "$(DIST)"; \

View File

@@ -1,4 +1,4 @@
# scrcpy (v1.9) # scrcpy (v1.10)
This application provides display and control of Android devices connected on This application provides display and control of Android devices connected on
USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access. USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access.
@@ -6,6 +6,17 @@ It works on _GNU/Linux_, _Windows_ and _macOS_.
![screenshot](assets/screenshot-debian-600.jpg) ![screenshot](assets/screenshot-debian-600.jpg)
It focuses on:
- **lightness** (native, displays only the device screen)
- **performance** (30~60fps)
- **quality** (1920×1080 or above)
- **low latency** ([35~70ms][lowlatency])
- **low startup time** (~1 second to display the first image)
- **non-intrusiveness** (nothing is left installed on the device)
[lowlatency]: https://github.com/Genymobile/scrcpy/pull/646
## Requirements ## Requirements
@@ -51,13 +62,13 @@ For Gentoo, an [Ebuild] is available: [`scrcpy/`][ebuild-link].
For Windows, for simplicity, prebuilt archives with all the dependencies For Windows, for simplicity, prebuilt archives with all the dependencies
(including `adb`) are available: (including `adb`) are available:
- [`scrcpy-win32-v1.9.zip`][direct-win32] - [`scrcpy-win32-v1.10.zip`][direct-win32]
_(SHA-256: 3234f7fbcc26b9e399f50b5ca9ed085708954c87fda1b0dd32719d6e7dd861ef)_ _(SHA-256: f98b400b3764404b33b212e9762dd6f1593ddb766c1480fc2609c94768e4a8e1)_
- [`scrcpy-win64-v1.9.zip`][direct-win64] - [`scrcpy-win64-v1.10.zip`][direct-win64]
_(SHA-256: 0088eca1811ea7c7ac350d636c8465b266e6c830bb268770ff88fddbb493077e)_ _(SHA-256: 95de34575d873c7e95dfcfb5e74d0f6af4f70b2a5bc6fde0f48d1a05480e3a44)_
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.9/scrcpy-win32-v1.9.zip [direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.10/scrcpy-win32-v1.10.zip
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.9/scrcpy-win64-v1.9.zip [direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.10/scrcpy-win64-v1.10.zip
You can also [build the app manually][BUILD]. You can also [build the app manually][BUILD].
@@ -315,26 +326,26 @@ Also see [issue #14].
## Shortcuts ## Shortcuts
| Action | Shortcut | | Action | Shortcut | Shortcut (macOS)
| -------------------------------------- |:---------------------------- | | -------------------------------------- |:----------------------------- |:-----------------------------
| Switch fullscreen mode | `Ctrl`+`f` | | Switch fullscreen mode | `Ctrl`+`f` | `Cmd`+`f`
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` | | Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` | `Cmd`+`g`
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ | | Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ | `Cmd`+`x` \| _Double-click¹_
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | | Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_
| Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ | | Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ | `Cmd`+`b` \| _Right-click²_
| Click on `APP_SWITCH` | `Ctrl`+`s` | | Click on `APP_SWITCH` | `Ctrl`+`s` | `Cmd`+`s`
| Click on `MENU` | `Ctrl`+`m` | | Click on `MENU` | `Ctrl`+`m` | `Ctrl`+`m`
| Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ (`Cmd`+`↑` on macOS) | | Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ | `Cmd`+`↑` _(up)_
| Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ (`Cmd`+`↓` on macOS) | | Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ | `Cmd`+`↓` _(down)_
| Click on `POWER` | `Ctrl`+`p` | | Click on `POWER` | `Ctrl`+`p` | `Cmd`+`p`
| Power on | _Right-click²_ | | Power on | _Right-click²_ | _Right-click²_
| Turn device screen off (keep mirroring)| `Ctrl`+`o` | | Turn device screen off (keep mirroring)| `Ctrl`+`o` | `Cmd`+`o`
| Expand notification panel | `Ctrl`+`n` | | Expand notification panel | `Ctrl`+`n` | `Cmd`+`n`
| Collapse notification panel | `Ctrl`+`Shift`+`n` | | Collapse notification panel | `Ctrl`+`Shift`+`n` | `Cmd`+`Shift`+`n`
| Copy device clipboard to computer | `Ctrl`+`c` | | Copy device clipboard to computer | `Ctrl`+`c` | `Cmd`+`c`
| Paste computer clipboard to device | `Ctrl`+`v` | | Paste computer clipboard to device | `Ctrl`+`v` | `Cmd`+`v`
| Copy computer clipboard to device | `Ctrl`+`Shift`+`v` | | Copy computer clipboard to device | `Ctrl`+`Shift`+`v` | `Cmd`+`Shift`+`v`
| Enable/disable FPS counter (on stdout) | `Ctrl`+`i` | | Enable/disable FPS counter (on stdout) | `Ctrl`+`i` | `Cmd`+`i`
_¹Double-click on black borders to remove them._ _¹Double-click on black borders to remove them._
_²Right-click turns the screen on if it was off, presses BACK otherwise._ _²Right-click turns the screen on if it was off, presses BACK otherwise._

View File

@@ -1,6 +1,5 @@
src = [ src = [
'src/main.c', 'src/main.c',
'src/buffered_reader.c',
'src/command.c', 'src/command.c',
'src/control_msg.c', 'src/control_msg.c',
'src/controller.c', 'src/controller.c',
@@ -151,6 +150,9 @@ tests = [
'tests/test_device_msg_deserialize.c', 'tests/test_device_msg_deserialize.c',
'src/device_msg.c' 'src/device_msg.c'
]], ]],
['test_queue', [
'tests/test_queue.c',
]],
['test_strutil', [ ['test_strutil', [
'tests/test_strutil.c', 'tests/test_strutil.c',
'src/str_util.c' 'src/str_util.c'

View File

@@ -1,72 +0,0 @@
#include "buffered_reader.h"
#include <SDL2/SDL_assert.h>
#include "log.h"
bool
buffered_reader_init(struct buffered_reader *reader, socket_t socket,
size_t bufsize) {
reader->buf = SDL_malloc(bufsize);
if (!reader->buf) {
LOGC("Could not allocate buffer");
return false;
}
reader->socket = socket;
reader->bufsize = bufsize;
reader->offset = 0;
reader->len = 0;
return true;
}
void
buffered_reader_destroy(struct buffered_reader *reader) {
SDL_free(reader->buf);
}
static ssize_t
buffered_reader_fill(struct buffered_reader *reader) {
SDL_assert(!reader->len);
ssize_t r = net_recv(reader->socket, reader->buf, reader->bufsize);
if (r > 0) {
reader->offset = 0;
reader->len = r;
}
return r;
}
ssize_t
buffered_reader_recv(struct buffered_reader *reader, void *buf, size_t count) {
if (!reader->len) {
// read from the socket
ssize_t r = buffered_reader_fill(reader);
if (r <= 0) {
return r;
}
}
size_t r = count < reader->len ? count : reader->len;
memcpy(buf, reader->buf + reader->offset, r);
reader->offset += r;
reader->len -= r;
return r;
}
ssize_t
buffered_reader_recv_all(struct buffered_reader *reader, void *buf,
size_t count) {
size_t done = 0;
while (done < count) {
ssize_t r = buffered_reader_recv(reader, buf, count - done);
if (r <= 0) {
// if there was some data, return them immediately
return done ? done : r;
}
done += r;
buf += r;
}
return done;
}

View File

@@ -1,29 +0,0 @@
#ifndef BUFFERED_READER_H
#define BUFFERED_READER_H
#include "common.h"
#include "net.h"
struct buffered_reader {
socket_t socket;
void *buf;
size_t bufsize;
size_t offset;
size_t len;
};
bool
buffered_reader_init(struct buffered_reader *reader, socket_t socket,
size_t bufsize);
void
buffered_reader_destroy(struct buffered_reader *reader);
ssize_t
buffered_reader_recv(struct buffered_reader *reader, void *buf, size_t count);
ssize_t
buffered_reader_recv_all(struct buffered_reader *reader, void *buf,
size_t count);
#endif

View File

@@ -6,7 +6,7 @@
#include <unistd.h> #include <unistd.h>
// To define a circular buffer type of 20 ints: // To define a circular buffer type of 20 ints:
// typedef CBUF(int, 20) my_cbuf_t; // struct cbuf_int CBUF(int, 20);
// //
// data has length CAP + 1 to distinguish empty vs full. // data has length CAP + 1 to distinguish empty vs full.
#define CBUF(TYPE, CAP) { \ #define CBUF(TYPE, CAP) { \
@@ -35,7 +35,7 @@
(PCBUF)->head = ((PCBUF)->head + 1) % cbuf_size_(PCBUF); \ (PCBUF)->head = ((PCBUF)->head + 1) % cbuf_size_(PCBUF); \
} \ } \
ok; \ ok; \
}) \ })
#define cbuf_take(PCBUF, PITEM) \ #define cbuf_take(PCBUF, PITEM) \
({ \ ({ \

View File

@@ -242,16 +242,27 @@ input_manager_process_key(struct input_manager *input_manager,
bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT); bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI); bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI);
// use Cmd on macOS, Ctrl on other platforms
#ifdef __APPLE__
bool cmd = !ctrl && meta;
#else
if (meta) {
// no shortcuts involve Meta on platforms other than macOS, and it must
// not be forwarded to the device
return;
}
bool cmd = ctrl; // && !meta, already guaranteed
#endif
if (alt) { if (alt) {
// no shortcut involves Alt or Meta, and they should not be forwarded // no shortcuts involve Alt, and it must not be forwarded to the device
// to the device
return; return;
} }
struct controller *controller = input_manager->controller; struct controller *controller = input_manager->controller;
// capture all Ctrl events // capture all Ctrl events
if (ctrl | meta) { if (ctrl || cmd) {
SDL_Keycode keycode = event->keysym.sym; SDL_Keycode keycode = event->keysym.sym;
bool down = event->type == SDL_KEYDOWN; bool down = event->type == SDL_KEYDOWN;
int action = down ? ACTION_DOWN : ACTION_UP; int action = down ? ACTION_DOWN : ACTION_UP;
@@ -259,63 +270,59 @@ input_manager_process_key(struct input_manager *input_manager,
bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT); bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
switch (keycode) { switch (keycode) {
case SDLK_h: case SDLK_h:
// Ctrl+h on all platform, since Cmd+h is already captured by
// the system on macOS to hide the window
if (control && ctrl && !meta && !shift && !repeat) { if (control && ctrl && !meta && !shift && !repeat) {
action_home(controller, action); action_home(controller, action);
} }
return; return;
case SDLK_b: // fall-through case SDLK_b: // fall-through
case SDLK_BACKSPACE: case SDLK_BACKSPACE:
if (control && ctrl && !meta && !shift && !repeat) { if (control && cmd && !shift && !repeat) {
action_back(controller, action); action_back(controller, action);
} }
return; return;
case SDLK_s: case SDLK_s:
if (control && ctrl && !meta && !shift && !repeat) { if (control && cmd && !shift && !repeat) {
action_app_switch(controller, action); action_app_switch(controller, action);
} }
return; return;
case SDLK_m: case SDLK_m:
// Ctrl+m on all platform, since Cmd+m is already captured by
// the system on macOS to minimize the window
if (control && ctrl && !meta && !shift && !repeat) { if (control && ctrl && !meta && !shift && !repeat) {
action_menu(controller, action); action_menu(controller, action);
} }
return; return;
case SDLK_p: case SDLK_p:
if (control && ctrl && !meta && !shift && !repeat) { if (control && cmd && !shift && !repeat) {
action_power(controller, action); action_power(controller, action);
} }
return; return;
case SDLK_o: case SDLK_o:
if (control && ctrl && !shift && !meta && down) { if (control && cmd && !shift && down) {
set_screen_power_mode(controller, SCREEN_POWER_MODE_OFF); set_screen_power_mode(controller, SCREEN_POWER_MODE_OFF);
} }
return; return;
case SDLK_DOWN: case SDLK_DOWN:
#ifdef __APPLE__ if (control && cmd && !shift) {
if (control && !ctrl && meta && !shift) {
#else
if (control && ctrl && !meta && !shift) {
#endif
// forward repeated events // forward repeated events
action_volume_down(controller, action); action_volume_down(controller, action);
} }
return; return;
case SDLK_UP: case SDLK_UP:
#ifdef __APPLE__ if (control && cmd && !shift) {
if (control && !ctrl && meta && !shift) {
#else
if (control && ctrl && !meta && !shift) {
#endif
// forward repeated events // forward repeated events
action_volume_up(controller, action); action_volume_up(controller, action);
} }
return; return;
case SDLK_c: case SDLK_c:
if (control && ctrl && !meta && !shift && !repeat && down) { if (control && cmd && !shift && !repeat && down) {
request_device_clipboard(controller); request_device_clipboard(controller);
} }
return; return;
case SDLK_v: case SDLK_v:
if (control && ctrl && !meta && !repeat && down) { if (control && cmd && !repeat && down) {
if (shift) { if (shift) {
// store the text in the device clipboard // store the text in the device clipboard
set_device_clipboard(controller); set_device_clipboard(controller);
@@ -326,29 +333,29 @@ input_manager_process_key(struct input_manager *input_manager,
} }
return; return;
case SDLK_f: case SDLK_f:
if (ctrl && !meta && !shift && !repeat && down) { if (!shift && cmd && !repeat && down) {
screen_switch_fullscreen(input_manager->screen); screen_switch_fullscreen(input_manager->screen);
} }
return; return;
case SDLK_x: case SDLK_x:
if (ctrl && !meta && !shift && !repeat && down) { if (!shift && cmd && !repeat && down) {
screen_resize_to_fit(input_manager->screen); screen_resize_to_fit(input_manager->screen);
} }
return; return;
case SDLK_g: case SDLK_g:
if (ctrl && !meta && !shift && !repeat && down) { if (!shift && cmd && !repeat && down) {
screen_resize_to_pixel_perfect(input_manager->screen); screen_resize_to_pixel_perfect(input_manager->screen);
} }
return; return;
case SDLK_i: case SDLK_i:
if (ctrl && !meta && !shift && !repeat && down) { if (!shift && cmd && !repeat && down) {
struct fps_counter *fps_counter = struct fps_counter *fps_counter =
input_manager->video_buffer->fps_counter; input_manager->video_buffer->fps_counter;
switch_fps_counter_state(fps_counter); switch_fps_counter_state(fps_counter);
} }
return; return;
case SDLK_n: case SDLK_n:
if (control && ctrl && !meta && !repeat && down) { if (control && cmd && !repeat && down) {
if (shift) { if (shift) {
collapse_notification_panel(controller); collapse_notification_panel(controller);
} else { } else {

View File

@@ -35,6 +35,11 @@ struct args {
}; };
static void usage(const char *arg0) { static void usage(const char *arg0) {
#ifdef __APPLE__
# define CTRL_OR_CMD "Cmd"
#else
# define CTRL_OR_CMD "Ctrl"
#endif
fprintf(stderr, fprintf(stderr,
"Usage: %s [options]\n" "Usage: %s [options]\n"
"\n" "\n"
@@ -115,13 +120,13 @@ static void usage(const char *arg0) {
"\n" "\n"
"Shortcuts:\n" "Shortcuts:\n"
"\n" "\n"
" Ctrl+f\n" " " CTRL_OR_CMD "+f\n"
" switch fullscreen mode\n" " switch fullscreen mode\n"
"\n" "\n"
" Ctrl+g\n" " " CTRL_OR_CMD "+g\n"
" resize window to 1:1 (pixel-perfect)\n" " resize window to 1:1 (pixel-perfect)\n"
"\n" "\n"
" Ctrl+x\n" " " CTRL_OR_CMD "+x\n"
" Double-click on black borders\n" " Double-click on black borders\n"
" resize window to remove black borders\n" " resize window to remove black borders\n"
"\n" "\n"
@@ -129,48 +134,48 @@ static void usage(const char *arg0) {
" Middle-click\n" " Middle-click\n"
" click on HOME\n" " click on HOME\n"
"\n" "\n"
" Ctrl+b\n" " " CTRL_OR_CMD "+b\n"
" Ctrl+Backspace\n" " " CTRL_OR_CMD "+Backspace\n"
" Right-click (when screen is on)\n" " Right-click (when screen is on)\n"
" click on BACK\n" " click on BACK\n"
"\n" "\n"
" Ctrl+s\n" " " CTRL_OR_CMD "+s\n"
" click on APP_SWITCH\n" " click on APP_SWITCH\n"
"\n" "\n"
" Ctrl+m\n" " Ctrl+m\n"
" click on MENU\n" " click on MENU\n"
"\n" "\n"
" Ctrl+Up\n" " " CTRL_OR_CMD "+Up\n"
" click on VOLUME_UP\n" " click on VOLUME_UP\n"
"\n" "\n"
" Ctrl+Down\n" " " CTRL_OR_CMD "+Down\n"
" click on VOLUME_DOWN\n" " click on VOLUME_DOWN\n"
"\n" "\n"
" Ctrl+p\n" " " CTRL_OR_CMD "+p\n"
" click on POWER (turn screen on/off)\n" " click on POWER (turn screen on/off)\n"
"\n" "\n"
" Right-click (when screen is off)\n" " Right-click (when screen is off)\n"
" power on\n" " power on\n"
"\n" "\n"
" Ctrl+o\n" " " CTRL_OR_CMD "+o\n"
" turn device screen off (keep mirroring)\n" " turn device screen off (keep mirroring)\n"
"\n" "\n"
" Ctrl+n\n" " " CTRL_OR_CMD "+n\n"
" expand notification panel\n" " expand notification panel\n"
"\n" "\n"
" Ctrl+Shift+n\n" " " CTRL_OR_CMD "+Shift+n\n"
" collapse notification panel\n" " collapse notification panel\n"
"\n" "\n"
" Ctrl+c\n" " " CTRL_OR_CMD "+c\n"
" copy device clipboard to computer\n" " copy device clipboard to computer\n"
"\n" "\n"
" Ctrl+v\n" " " CTRL_OR_CMD "+v\n"
" paste computer clipboard to device\n" " paste computer clipboard to device\n"
"\n" "\n"
" Ctrl+Shift+v\n" " " CTRL_OR_CMD "+Shift+v\n"
" copy computer clipboard to device\n" " copy computer clipboard to device\n"
"\n" "\n"
" Ctrl+i\n" " " CTRL_OR_CMD "+i\n"
" enable/disable FPS counter (print frames/second in logs)\n" " enable/disable FPS counter (print frames/second in logs)\n"
"\n" "\n"
" Drag & drop APK file\n" " Drag & drop APK file\n"

75
app/src/queue.h Normal file
View File

@@ -0,0 +1,75 @@
// generic intrusive FIFO queue
#ifndef QUEUE_H
#define QUEUE_H
#include <stdbool.h>
#include <stddef.h>
#include <SDL2/SDL_assert.h>
// To define a queue type of "struct foo":
// struct queue_foo QUEUE(struct foo);
#define QUEUE(TYPE) { \
TYPE *first; \
TYPE *last; \
}
#define queue_init(PQ) \
(void) ((PQ)->first = (PQ)->last = NULL)
#define queue_is_empty(PQ) \
!(PQ)->first
// NEXTFIELD is the field in the ITEM type used for intrusive linked-list
//
// For example:
// struct foo {
// int value;
// struct foo *next;
// };
//
// // define the type "struct my_queue"
// struct my_queue QUEUE(struct foo);
//
// struct my_queue queue;
// queue_init(&queue);
//
// struct foo v1 = { .value = 42 };
// struct foo v2 = { .value = 27 };
//
// queue_push(&queue, next, v1);
// queue_push(&queue, next, v2);
//
// struct foo *foo;
// queue_take(&queue, next, &foo);
// assert(foo->value == 42);
// queue_take(&queue, next, &foo);
// assert(foo->value == 27);
// assert(queue_is_empty(&queue));
//
// push a new item into the queue
#define queue_push(PQ, NEXTFIELD, ITEM) \
(void) ({ \
(ITEM)->NEXTFIELD = NULL; \
if (queue_is_empty(PQ)) { \
(PQ)->first = (PQ)->last = (ITEM); \
} else { \
(PQ)->last->NEXTFIELD = (ITEM); \
(PQ)->last = (ITEM); \
} \
})
// take the next item and remove it from the queue (the queue must not be empty)
// the result is stored in *(PITEM)
// (without typeof(), we could not store a local variable having the correct
// type so that we can "return" it)
#define queue_take(PQ, NEXTFIELD, PITEM) \
(void) ({ \
SDL_assert(!queue_is_empty(PQ)); \
*(PITEM) = (PQ)->first; \
(PQ)->first = (PQ)->first->NEXTFIELD; \
})
// no need to update (PQ)->last if the queue is left empty:
// (PQ)->last is undefined if !(PQ)->first anyway
#endif

View File

@@ -33,11 +33,15 @@ record_packet_new(const AVPacket *packet) {
if (!rec) { if (!rec) {
return NULL; return NULL;
} }
// av_packet_ref() does not initialize all fields in old FFmpeg versions
// See <https://github.com/Genymobile/scrcpy/issues/707>
av_init_packet(&rec->packet);
if (av_packet_ref(&rec->packet, packet)) { if (av_packet_ref(&rec->packet, packet)) {
SDL_free(rec); SDL_free(rec);
return NULL; return NULL;
} }
rec->next = NULL;
return rec; return rec;
} }
@@ -47,60 +51,13 @@ record_packet_delete(struct record_packet *rec) {
SDL_free(rec); SDL_free(rec);
} }
static void
recorder_queue_init(struct recorder_queue *queue) {
queue->first = NULL;
// queue->last is undefined if queue->first == NULL
}
static inline bool
recorder_queue_is_empty(struct recorder_queue *queue) {
return !queue->first;
}
static bool
recorder_queue_push(struct recorder_queue *queue, const AVPacket *packet) {
struct record_packet *rec = record_packet_new(packet);
if (!rec) {
LOGC("Could not allocate record packet");
return false;
}
rec->next = NULL;
if (recorder_queue_is_empty(queue)) {
queue->first = queue->last = rec;
} else {
// chain rec after the (current) last packet
queue->last->next = rec;
// the last packet is now rec
queue->last = rec;
}
return true;
}
static inline struct record_packet *
recorder_queue_take(struct recorder_queue *queue) {
SDL_assert(!recorder_queue_is_empty(queue));
struct record_packet *rec = queue->first;
SDL_assert(rec);
queue->first = rec->next;
// no need to update queue->last if the queue is left empty:
// queue->last is undefined if queue->first == NULL
return rec;
}
static void static void
recorder_queue_clear(struct recorder_queue *queue) { recorder_queue_clear(struct recorder_queue *queue) {
struct record_packet *rec = queue->first; while (!queue_is_empty(queue)) {
while (rec) { struct record_packet *rec;
struct record_packet *current = rec; queue_take(queue, next, &rec);
rec = rec->next; record_packet_delete(rec);
record_packet_delete(current);
} }
queue->first = NULL;
} }
bool bool
@@ -129,12 +86,13 @@ recorder_init(struct recorder *recorder,
return false; return false;
} }
recorder_queue_init(&recorder->queue); queue_init(&recorder->queue);
recorder->stopped = false; recorder->stopped = false;
recorder->failed = false; recorder->failed = false;
recorder->format = format; recorder->format = format;
recorder->declared_frame_size = declared_frame_size; recorder->declared_frame_size = declared_frame_size;
recorder->header_written = false; recorder->header_written = false;
recorder->previous = NULL;
return true; return true;
} }
@@ -296,25 +254,50 @@ run_recorder(void *data) {
for (;;) { for (;;) {
mutex_lock(recorder->mutex); mutex_lock(recorder->mutex);
while (!recorder->stopped && while (!recorder->stopped && queue_is_empty(&recorder->queue)) {
recorder_queue_is_empty(&recorder->queue)) {
cond_wait(recorder->queue_cond, recorder->mutex); cond_wait(recorder->queue_cond, recorder->mutex);
} }
// if stopped is set, continue to process the remaining events (to // if stopped is set, continue to process the remaining events (to
// finish the recording) before actually stopping // finish the recording) before actually stopping
if (recorder->stopped && recorder_queue_is_empty(&recorder->queue)) { if (recorder->stopped && queue_is_empty(&recorder->queue)) {
mutex_unlock(recorder->mutex); mutex_unlock(recorder->mutex);
struct record_packet *last = recorder->previous;
if (last) {
// assign an arbitrary duration to the last packet
last->packet.duration = 100000;
bool ok = recorder_write(recorder, &last->packet);
if (!ok) {
// failing to write the last frame is not very serious, no
// future frame may depend on it, so the resulting file
// will still be valid
LOGW("Could not record last packet");
}
record_packet_delete(last);
}
break; break;
} }
struct record_packet *rec = recorder_queue_take(&recorder->queue); struct record_packet *rec;
queue_take(&recorder->queue, next, &rec);
mutex_unlock(recorder->mutex); mutex_unlock(recorder->mutex);
bool ok = recorder_write(recorder, &rec->packet); // recorder->previous is only written from this thread, no need to lock
record_packet_delete(rec); struct record_packet *previous = recorder->previous;
recorder->previous = rec;
if (!previous) {
// we just received the first packet
continue;
}
// we now know the duration of the previous packet
previous->packet.duration = rec->packet.pts - previous->packet.pts;
bool ok = recorder_write(recorder, &previous->packet);
record_packet_delete(previous);
if (!ok) { if (!ok) {
LOGE("Could not record packet"); LOGE("Could not record packet");
@@ -369,9 +352,15 @@ recorder_push(struct recorder *recorder, const AVPacket *packet) {
return false; return false;
} }
bool ok = recorder_queue_push(&recorder->queue, packet); struct record_packet *rec = record_packet_new(packet);
if (!rec) {
LOGC("Could not allocate record packet");
return false;
}
queue_push(&recorder->queue, next, rec);
cond_signal(recorder->queue_cond); cond_signal(recorder->queue_cond);
mutex_unlock(recorder->mutex); mutex_unlock(recorder->mutex);
return ok; return true;
} }

View File

@@ -7,6 +7,7 @@
#include <SDL2/SDL_thread.h> #include <SDL2/SDL_thread.h>
#include "common.h" #include "common.h"
#include "queue.h"
enum recorder_format { enum recorder_format {
RECORDER_FORMAT_MP4 = 1, RECORDER_FORMAT_MP4 = 1,
@@ -18,10 +19,7 @@ struct record_packet {
struct record_packet *next; struct record_packet *next;
}; };
struct recorder_queue { struct recorder_queue QUEUE(struct record_packet);
struct record_packet *first;
struct record_packet *last; // undefined if first is NULL
};
struct recorder { struct recorder {
char *filename; char *filename;
@@ -36,6 +34,12 @@ struct recorder {
bool stopped; // set on recorder_stop() by the stream reader bool stopped; // set on recorder_stop() by the stream reader
bool failed; // set on packet write failure bool failed; // set on packet write failure
struct recorder_queue queue; struct recorder_queue queue;
// we can write a packet only once we received the next one so that we can
// set its duration (next_pts - current_pts)
// "previous" is only accessed from the recorder thread, so it does not
// need to be protected by the mutex
struct record_packet *previous;
}; };
bool bool

View File

@@ -297,7 +297,6 @@ scrcpy(const struct scrcpy_options *options) {
bool video_buffer_initialized = false; bool video_buffer_initialized = false;
bool file_handler_initialized = false; bool file_handler_initialized = false;
bool recorder_initialized = false; bool recorder_initialized = false;
bool stream_initialized = false;
bool stream_started = false; bool stream_started = false;
bool controller_initialized = false; bool controller_initialized = false;
bool controller_started = false; bool controller_started = false;
@@ -359,10 +358,7 @@ scrcpy(const struct scrcpy_options *options) {
av_log_set_callback(av_log_callback); av_log_set_callback(av_log_callback);
if (!stream_init(&stream, server.video_socket, dec, rec)) { stream_init(&stream, server.video_socket, dec, rec);
goto end;
}
stream_initialized = true;
// now we consumed the header values, the socket receives the video stream // now we consumed the header values, the socket receives the video stream
// start the stream // start the stream
@@ -441,9 +437,6 @@ end:
if (stream_started) { if (stream_started) {
stream_join(&stream); stream_join(&stream);
} }
if (stream_initialized) {
stream_destroy(&stream);
}
if (controller_started) { if (controller_started) {
controller_join(&controller); controller_join(&controller);
} }

View File

@@ -17,7 +17,7 @@
#include "log.h" #include "log.h"
#include "recorder.h" #include "recorder.h"
#define STREAM_BUFSIZE 0x10000 #define BUFSIZE 0x10000
#define HEADER_SIZE 12 #define HEADER_SIZE 12
#define NO_PTS UINT64_C(-1) #define NO_PTS UINT64_C(-1)
@@ -37,8 +37,7 @@ stream_recv_packet(struct stream *stream, AVPacket *packet) {
// It is followed by <packet_size> bytes containing the packet/frame. // It is followed by <packet_size> bytes containing the packet/frame.
uint8_t header[HEADER_SIZE]; uint8_t header[HEADER_SIZE];
ssize_t r = ssize_t r = net_recv_all(stream->socket, header, HEADER_SIZE);
buffered_reader_recv_all(&stream->buffered_reader, header, HEADER_SIZE);
if (r < HEADER_SIZE) { if (r < HEADER_SIZE) {
return false; return false;
} }
@@ -52,7 +51,7 @@ stream_recv_packet(struct stream *stream, AVPacket *packet) {
return false; return false;
} }
r = buffered_reader_recv_all(&stream->buffered_reader, packet->data, len); r = net_recv_all(stream->socket, packet->data, len);
if (r < len) { if (r < len) {
av_packet_unref(packet); av_packet_unref(packet);
return false; return false;
@@ -268,23 +267,13 @@ end:
return 0; return 0;
} }
bool void
stream_init(struct stream *stream, socket_t socket, stream_init(struct stream *stream, socket_t socket,
struct decoder *decoder, struct recorder *recorder) { struct decoder *decoder, struct recorder *recorder) {
if (!buffered_reader_init(&stream->buffered_reader, socket,
STREAM_BUFSIZE)) {
return false;
}
stream->socket = socket; stream->socket = socket;
stream->decoder = decoder, stream->decoder = decoder,
stream->recorder = recorder; stream->recorder = recorder;
stream->has_pending = false; stream->has_pending = false;
return true;
}
void
stream_destroy(struct stream *stream) {
buffered_reader_destroy(&stream->buffered_reader);
} }
bool bool

View File

@@ -7,14 +7,12 @@
#include <SDL2/SDL_atomic.h> #include <SDL2/SDL_atomic.h>
#include <SDL2/SDL_thread.h> #include <SDL2/SDL_thread.h>
#include "buffered_reader.h"
#include "net.h" #include "net.h"
struct video_buffer; struct video_buffer;
struct stream { struct stream {
socket_t socket; socket_t socket;
struct buffered_reader buffered_reader;
struct video_buffer *video_buffer; struct video_buffer *video_buffer;
SDL_Thread *thread; SDL_Thread *thread;
struct decoder *decoder; struct decoder *decoder;
@@ -27,13 +25,10 @@ struct stream {
AVPacket pending; AVPacket pending;
}; };
bool void
stream_init(struct stream *stream, socket_t socket, stream_init(struct stream *stream, socket_t socket,
struct decoder *decoder, struct recorder *recorder); struct decoder *decoder, struct recorder *recorder);
void
stream_destroy(struct stream *stream);
bool bool
stream_start(struct stream *stream); stream_start(struct stream *stream);

38
app/tests/test_queue.c Normal file
View File

@@ -0,0 +1,38 @@
#include <assert.h>
#include <queue.h>
struct foo {
int value;
struct foo *next;
};
static void test_queue(void) {
struct my_queue QUEUE(struct foo) queue;
queue_init(&queue);
assert(queue_is_empty(&queue));
struct foo v1 = { .value = 42 };
struct foo v2 = { .value = 27 };
queue_push(&queue, next, &v1);
queue_push(&queue, next, &v2);
struct foo *foo;
assert(!queue_is_empty(&queue));
queue_take(&queue, next, &foo);
assert(foo->value == 42);
assert(!queue_is_empty(&queue));
queue_take(&queue, next, &foo);
assert(foo->value == 27);
assert(queue_is_empty(&queue));
}
int main(void) {
test_queue();
return 0;
}

View File

@@ -7,7 +7,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.3.0' classpath 'com.android.tools.build:gradle:3.4.2'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

View File

@@ -15,6 +15,6 @@ cpu = 'i686'
endian = 'little' endian = 'little'
[properties] [properties]
prebuilt_ffmpeg_shared = 'ffmpeg-4.1.3-win32-shared' prebuilt_ffmpeg_shared = 'ffmpeg-4.1.4-win32-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.1.3-win32-dev' prebuilt_ffmpeg_dev = 'ffmpeg-4.1.4-win32-dev'
prebuilt_sdl2 = 'SDL2-2.0.8/i686-w64-mingw32' prebuilt_sdl2 = 'SDL2-2.0.10/i686-w64-mingw32'

View File

@@ -15,6 +15,6 @@ cpu = 'x86_64'
endian = 'little' endian = 'little'
[properties] [properties]
prebuilt_ffmpeg_shared = 'ffmpeg-4.1.3-win64-shared' prebuilt_ffmpeg_shared = 'ffmpeg-4.1.4-win64-shared'
prebuilt_ffmpeg_dev = 'ffmpeg-4.1.3-win64-dev' prebuilt_ffmpeg_dev = 'ffmpeg-4.1.4-win64-dev'
prebuilt_sdl2 = 'SDL2-2.0.8/x86_64-w64-mingw32' prebuilt_sdl2 = 'SDL2-2.0.10/x86_64-w64-mingw32'

Binary file not shown.

View File

@@ -1,6 +1,5 @@
#Thu Apr 18 11:45:59 CEST 2019
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip

84
gradlew vendored
View File

@@ -1,4 +1,20 @@
#!/usr/bin/env bash #!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
############################################################################## ##############################################################################
## ##
@@ -6,12 +22,30 @@
## ##
############################################################################## ##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Attempt to set APP_HOME
DEFAULT_JVM_OPTS="" # Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD="maximum"
@@ -30,6 +64,7 @@ die ( ) {
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false
case "`uname`" in case "`uname`" in
CYGWIN* ) CYGWIN* )
cygwin=true cygwin=true
@@ -40,26 +75,11 @@ case "`uname`" in
MINGW* ) MINGW* )
msys=true msys=true
;; ;;
NONSTOP* )
nonstop=true
;;
esac esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -85,7 +105,7 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n` MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -150,11 +170,19 @@ if $cygwin ; then
esac esac
fi fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules # Escape application args
function splitJvmOpts() { save () {
JVM_OPTS=("$@") for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
} }
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS APP_ARGS=$(save "$@")
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" # Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

30
gradlew.bat vendored
View File

@@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@@ -8,14 +24,14 @@
@rem Set local scope for the variables with windows NT shell @rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +62,9 @@ echo location of your Java installation.
goto fail goto fail
:init :init
@rem Get command-line arguments, handling Windowz variants @rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args :win9xME_args
@rem Slurp the command line arguments. @rem Slurp the command line arguments.
@@ -60,11 +75,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%* set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute :execute
@rem Setup the command line @rem Setup the command line

View File

@@ -1,5 +1,5 @@
project('scrcpy', 'c', project('scrcpy', 'c',
version: '1.9', version: '1.10',
meson_version: '>= 0.37', meson_version: '>= 0.37',
default_options: 'c_std=c11') default_options: 'c_std=c11')

View File

@@ -10,31 +10,31 @@ prepare-win32: prepare-sdl2 prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32
prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb
prepare-ffmpeg-shared-win32: prepare-ffmpeg-shared-win32:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.1.3-win32-shared.zip \ @./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.1.4-win32-shared.zip \
8ea472d673370d5e87517a75587abfa6f189ee4f82e8da21fdbc49d0db0c1a89 \ 596608277f6b937c3dea7c46e854665d75b3de56790bae07f655ca331440f003 \
ffmpeg-4.1.3-win32-shared ffmpeg-4.1.4-win32-shared
prepare-ffmpeg-dev-win32: prepare-ffmpeg-dev-win32:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.1.3-win32-dev.zip \ @./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.1.4-win32-dev.zip \
e16d3150b6ccf0b71908f5b964cb8c051d79053c8f5cd6d777d617ab4f03613a \ a80c86e263cfad26e202edfa5e6e939a2c88843ae26f031d3e0d981a39fd03fb \
ffmpeg-4.1.3-win32-dev ffmpeg-4.1.4-win32-dev
prepare-ffmpeg-shared-win64: prepare-ffmpeg-shared-win64:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.1.3-win64-shared.zip \ @./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.1.4-win64-shared.zip \
0b974578e07d974c4bafb36c7ed0b46e46b001d38b149455089c13b57ddefe5d \ a90889871de2cab8a79b392591313a188189a353f69dde1db98aebe20b280989 \
ffmpeg-4.1.3-win64-shared ffmpeg-4.1.4-win64-shared
prepare-ffmpeg-dev-win64: prepare-ffmpeg-dev-win64:
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.1.3-win64-dev.zip \ @./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.1.4-win64-dev.zip \
334b473467db096a5b74242743592a73e120a137232794508e4fc55593696a5b \ 6c9d53f9e94ce1821e975ec668e5b9d6e9deb4a45d0d7e30264685d3dfbbb068 \
ffmpeg-4.1.3-win64-dev ffmpeg-4.1.4-win64-dev
prepare-sdl2: prepare-sdl2:
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.8-mingw.tar.gz \ @./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.10-mingw.tar.gz \
ffff7305d634aff5e1df5b7bb935435c3a02c8b03ad94a1a2be9169a558a7961 \ a90a7cddaec4996f4d7be6d80c57ec69b062e132bffc513965f99217f603274a \
SDL2-2.0.8 SDL2-2.0.10
prepare-adb: prepare-adb:
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r29.0.1-windows.zip \ @./prepare-dep https://dl.google.com/android/repository/platform-tools_r29.0.2-windows.zip \
2334f92cf571fd2d9bf6ff7c637765bee5d8323e0bd8e051e15927d87b54b4e8 \ d78f02e5e2c9c4c1d046dcd4e6fbdf586e5f57ef66eb0da5c2b49d745d85d5ee \
platform-tools platform-tools

View File

@@ -6,8 +6,8 @@ android {
applicationId "com.genymobile.scrcpy" applicationId "com.genymobile.scrcpy"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 29
versionCode 10 versionCode 11
versionName "1.9" versionName "1.10"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {

View File

@@ -6,6 +6,7 @@ if prebuilt_server == ''
build_always: true, # gradle is responsible for tracking source changes build_always: true, # gradle is responsible for tracking source changes
output: 'scrcpy-server.jar', output: 'scrcpy-server.jar',
command: [find_program('./scripts/build-wrapper.sh'), meson.current_source_dir(), '@OUTPUT@', get_option('buildtype')], command: [find_program('./scripts/build-wrapper.sh'), meson.current_source_dir(), '@OUTPUT@', get_option('buildtype')],
console: true,
install: true, install: true,
install_dir: 'share/scrcpy') install_dir: 'share/scrcpy')
else else

View File

@@ -14,7 +14,7 @@ public final class WindowManager {
try { try {
Class<?> cls = manager.getClass(); Class<?> cls = manager.getClass();
try { try {
return (Integer) manager.getClass().getMethod("getRotation").invoke(manager); return (Integer) cls.getMethod("getRotation").invoke(manager);
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
// method changed since this commit: // method changed since this commit:
// https://android.googlesource.com/platform/frameworks/base/+/8ee7285128c3843401d4c4d0412cd66e86ba49e3%5E%21/#F2 // https://android.googlesource.com/platform/frameworks/base/+/8ee7285128c3843401d4c4d0412cd66e86ba49e3%5E%21/#F2