Compare commits
1 Commits
finger.5
...
buffered_r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d113b6ef61 |
8
BUILD.md
8
BUILD.md
@@ -43,7 +43,7 @@ Install the required packages from your package manager.
|
|||||||
sudo apt install ffmpeg libsdl2-2.0-0
|
sudo apt install ffmpeg libsdl2-2.0-0
|
||||||
|
|
||||||
# client build dependencies
|
# client build dependencies
|
||||||
sudo apt install gcc git pkg-config meson ninja-build \
|
sudo apt install make gcc git pkg-config meson ninja-build \
|
||||||
libavcodec-dev libavformat-dev libavutil-dev \
|
libavcodec-dev libavformat-dev libavutil-dev \
|
||||||
libsdl2-dev
|
libsdl2-dev
|
||||||
|
|
||||||
@@ -234,10 +234,10 @@ You can then [run](README.md#run) _scrcpy_.
|
|||||||
|
|
||||||
## Prebuilt server
|
## Prebuilt server
|
||||||
|
|
||||||
- [`scrcpy-server-v1.10.jar`][direct-scrcpy-server]
|
- [`scrcpy-server-v1.9.jar`][direct-scrcpy-server]
|
||||||
_(SHA-256: cbeb1a4e046f1392c1dc73c3ccffd7f86dec4636b505556ea20929687a119390)_
|
_(SHA-256: ad7e539f100e48259b646f26982bc63e0a60a81ac87ae135e242855bef69bd1a)_
|
||||||
|
|
||||||
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.10/scrcpy-server-v1.10.jar
|
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.9/scrcpy-server-v1.9.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:
|
||||||
|
|||||||
44
FAQ.md
44
FAQ.md
@@ -1,5 +1,10 @@
|
|||||||
# Frequently Asked Questions
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
## Common issues
|
||||||
|
|
||||||
|
The application is very young, it is not unlikely that you encounter problems
|
||||||
|
with it.
|
||||||
|
|
||||||
Here are the common reported problems and their status.
|
Here are the common reported problems and their status.
|
||||||
|
|
||||||
|
|
||||||
@@ -15,13 +20,9 @@ Windows may need some [drivers] to detect your device.
|
|||||||
[drivers]: https://developer.android.com/studio/run/oem-usb.html
|
[drivers]: https://developer.android.com/studio/run/oem-usb.html
|
||||||
|
|
||||||
|
|
||||||
### I can only mirror, I cannot interact with the device
|
### Mouse clicks do not work
|
||||||
|
|
||||||
On some devices, you may need to enable an option to allow [simulating input].
|
On some devices, you may need to enable an option to allow [simulating input].
|
||||||
In developer options, enable:
|
|
||||||
|
|
||||||
> **USB debugging (Security settings)**
|
|
||||||
> _Allow granting permissions and simulating input via USB debugging_
|
|
||||||
|
|
||||||
[simulating input]: https://github.com/Genymobile/scrcpy/issues/70#issuecomment-373286323
|
[simulating input]: https://github.com/Genymobile/scrcpy/issues/70#issuecomment-373286323
|
||||||
|
|
||||||
@@ -42,16 +43,6 @@ meson x --buildtype release -Dhidpi_support=false
|
|||||||
However, the video will be displayed at lower resolution.
|
However, the video will be displayed at lower resolution.
|
||||||
|
|
||||||
|
|
||||||
### The quality is low on HiDPI display
|
|
||||||
|
|
||||||
On Windows, you may need to configure the [scaling behavior].
|
|
||||||
|
|
||||||
> `scrcpy.exe` > Properties > Compatibility > Change high DPI settings >
|
|
||||||
> Override high DPI scaling behavior > Scaling performed by: _Application_.
|
|
||||||
|
|
||||||
[scaling behavior]: https://github.com/Genymobile/scrcpy/issues/40#issuecomment-424466723
|
|
||||||
|
|
||||||
|
|
||||||
### KWin compositor crashes
|
### KWin compositor crashes
|
||||||
|
|
||||||
On Plasma Desktop, compositor is disabled while _scrcpy_ is running.
|
On Plasma Desktop, compositor is disabled while _scrcpy_ is running.
|
||||||
@@ -59,26 +50,3 @@ On Plasma Desktop, compositor is disabled while _scrcpy_ is running.
|
|||||||
As a workaround, [disable "Block compositing"][kwin].
|
As a workaround, [disable "Block compositing"][kwin].
|
||||||
|
|
||||||
[kwin]: https://github.com/Genymobile/scrcpy/issues/114#issuecomment-378778613
|
[kwin]: https://github.com/Genymobile/scrcpy/issues/114#issuecomment-378778613
|
||||||
|
|
||||||
|
|
||||||
### I get an error "Could not open video stream"
|
|
||||||
|
|
||||||
There may be many reasons. One common cause is that the hardware encoder of your
|
|
||||||
device is not able to encode at the given definition:
|
|
||||||
|
|
||||||
```
|
|
||||||
ERROR: Exception on thread Thread[main,5,main]
|
|
||||||
android.media.MediaCodec$CodecException: Error 0xfffffc0e
|
|
||||||
...
|
|
||||||
Exit due to uncaughtException in main thread:
|
|
||||||
ERROR: Could not open video stream
|
|
||||||
INFO: Initial texture: 1080x2336
|
|
||||||
```
|
|
||||||
|
|
||||||
Just try with a lower definition:
|
|
||||||
|
|
||||||
```
|
|
||||||
scrcpy -m 1920
|
|
||||||
scrcpy -m 1024
|
|
||||||
scrcpy -m 800
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -100,38 +100,37 @@ 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.4-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
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/avcodec-58.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/avformat-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/swresample-3.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/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.10/i686-w64-mingw32/bin/SDL2.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
|
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.4-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
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/avcodec-58.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/avformat-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/swresample-3.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/swscale-5.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/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.10/x86_64-w64-mingw32/bin/SDL2.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
|
zip-win32: dist-win32
|
||||||
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \
|
cd "$(DIST)"; \
|
||||||
zip -r "../$(WIN32_TARGET)" .
|
zip -r "$(WIN32_TARGET)" "$(WIN32_TARGET_DIR)"
|
||||||
|
|
||||||
zip-win64: dist-win64
|
zip-win64: dist-win64
|
||||||
cd "$(DIST)/$(WIN64_TARGET_DIR)"; \
|
cd "$(DIST)"; \
|
||||||
zip -r "../$(WIN64_TARGET)" .
|
zip -r "$(WIN64_TARGET)" "$(WIN64_TARGET_DIR)"
|
||||||
|
|
||||||
sums:
|
sums:
|
||||||
cd "$(DIST)"; \
|
cd "$(DIST)"; \
|
||||||
|
|||||||
65
README.md
65
README.md
@@ -1,4 +1,4 @@
|
|||||||
# scrcpy (v1.10)
|
# scrcpy (v1.9)
|
||||||
|
|
||||||
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,17 +6,6 @@ It works on _GNU/Linux_, _Windows_ and _macOS_.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@@ -62,13 +51,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.10.zip`][direct-win32]
|
- [`scrcpy-win32-v1.9.zip`][direct-win32]
|
||||||
_(SHA-256: f98b400b3764404b33b212e9762dd6f1593ddb766c1480fc2609c94768e4a8e1)_
|
_(SHA-256: 3234f7fbcc26b9e399f50b5ca9ed085708954c87fda1b0dd32719d6e7dd861ef)_
|
||||||
- [`scrcpy-win64-v1.10.zip`][direct-win64]
|
- [`scrcpy-win64-v1.9.zip`][direct-win64]
|
||||||
_(SHA-256: 95de34575d873c7e95dfcfb5e74d0f6af4f70b2a5bc6fde0f48d1a05480e3a44)_
|
_(SHA-256: 0088eca1811ea7c7ac350d636c8465b266e6c830bb268770ff88fddbb493077e)_
|
||||||
|
|
||||||
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.10/scrcpy-win32-v1.10.zip
|
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.9/scrcpy-win32-v1.9.zip
|
||||||
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.10/scrcpy-win64-v1.10.zip
|
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.9/scrcpy-win64-v1.9.zip
|
||||||
|
|
||||||
You can also [build the app manually][BUILD].
|
You can also [build the app manually][BUILD].
|
||||||
|
|
||||||
@@ -326,26 +315,26 @@ Also see [issue #14].
|
|||||||
|
|
||||||
## Shortcuts
|
## Shortcuts
|
||||||
|
|
||||||
| Action | Shortcut | Shortcut (macOS)
|
| Action | Shortcut |
|
||||||
| -------------------------------------- |:----------------------------- |:-----------------------------
|
| -------------------------------------- |:---------------------------- |
|
||||||
| Switch fullscreen mode | `Ctrl`+`f` | `Cmd`+`f`
|
| Switch fullscreen mode | `Ctrl`+`f` |
|
||||||
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` | `Cmd`+`g`
|
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` |
|
||||||
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ | `Cmd`+`x` \| _Double-click¹_
|
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ |
|
||||||
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_
|
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ |
|
||||||
| Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ | `Cmd`+`b` \| _Right-click²_
|
| Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ |
|
||||||
| Click on `APP_SWITCH` | `Ctrl`+`s` | `Cmd`+`s`
|
| Click on `APP_SWITCH` | `Ctrl`+`s` |
|
||||||
| Click on `MENU` | `Ctrl`+`m` | `Ctrl`+`m`
|
| Click on `MENU` | `Ctrl`+`m` |
|
||||||
| Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ | `Cmd`+`↑` _(up)_
|
| Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ (`Cmd`+`↑` on macOS) |
|
||||||
| Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ | `Cmd`+`↓` _(down)_
|
| Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ (`Cmd`+`↓` on macOS) |
|
||||||
| Click on `POWER` | `Ctrl`+`p` | `Cmd`+`p`
|
| Click on `POWER` | `Ctrl`+`p` |
|
||||||
| Power on | _Right-click²_ | _Right-click²_
|
| Power on | _Right-click²_ |
|
||||||
| Turn device screen off (keep mirroring)| `Ctrl`+`o` | `Cmd`+`o`
|
| Turn device screen off (keep mirroring)| `Ctrl`+`o` |
|
||||||
| Expand notification panel | `Ctrl`+`n` | `Cmd`+`n`
|
| Expand notification panel | `Ctrl`+`n` |
|
||||||
| Collapse notification panel | `Ctrl`+`Shift`+`n` | `Cmd`+`Shift`+`n`
|
| Collapse notification panel | `Ctrl`+`Shift`+`n` |
|
||||||
| Copy device clipboard to computer | `Ctrl`+`c` | `Cmd`+`c`
|
| Copy device clipboard to computer | `Ctrl`+`c` |
|
||||||
| Paste computer clipboard to device | `Ctrl`+`v` | `Cmd`+`v`
|
| Paste computer clipboard to device | `Ctrl`+`v` |
|
||||||
| Copy computer clipboard to device | `Ctrl`+`Shift`+`v` | `Cmd`+`Shift`+`v`
|
| Copy computer clipboard to device | `Ctrl`+`Shift`+`v` |
|
||||||
| Enable/disable FPS counter (on stdout) | `Ctrl`+`i` | `Cmd`+`i`
|
| Enable/disable FPS counter (on stdout) | `Ctrl`+`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._
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
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',
|
||||||
|
'src/convert.c',
|
||||||
'src/decoder.c',
|
'src/decoder.c',
|
||||||
'src/device.c',
|
'src/device.c',
|
||||||
'src/device_msg.c',
|
'src/device_msg.c',
|
||||||
'src/event_converter.c',
|
|
||||||
'src/file_handler.c',
|
'src/file_handler.c',
|
||||||
'src/fps_counter.c',
|
'src/fps_counter.c',
|
||||||
'src/input_manager.c',
|
'src/input_manager.c',
|
||||||
@@ -150,9 +151,6 @@ 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'
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
buffer_write16be(uint8_t *buf, uint16_t value) {
|
buffer_write16be(uint8_t *buf, uint16_t value) {
|
||||||
buf[0] = value >> 8;
|
buf[0] = value >> 8;
|
||||||
@@ -20,12 +18,6 @@ buffer_write32be(uint8_t *buf, uint32_t value) {
|
|||||||
buf[3] = value;
|
buf[3] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
|
||||||
buffer_write64be(uint8_t *buf, uint64_t value) {
|
|
||||||
buffer_write32be(buf, value >> 32);
|
|
||||||
buffer_write32be(&buf[4], (uint32_t) value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint16_t
|
static inline uint16_t
|
||||||
buffer_read16be(const uint8_t *buf) {
|
buffer_read16be(const uint8_t *buf) {
|
||||||
return (buf[0] << 8) | buf[1];
|
return (buf[0] << 8) | buf[1];
|
||||||
|
|||||||
72
app/src/buffered_reader.c
Normal file
72
app/src/buffered_reader.c
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
29
app/src/buffered_reader.h
Normal file
29
app/src/buffered_reader.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#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
|
||||||
@@ -5,10 +5,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
// To define a circular buffer type of 20 ints:
|
// To define a circular buffer type of 20 ints:
|
||||||
// struct cbuf_int CBUF(int, 20);
|
// typedef CBUF(int, 20) my_cbuf_t;
|
||||||
//
|
//
|
||||||
// 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) { \
|
||||||
@@ -37,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) \
|
||||||
({ \
|
({ \
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
|
|||||||
@@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
# define NO_EXIT_CODE -1
|
# define NO_EXIT_CODE -1
|
||||||
|
|
||||||
enum process_result {
|
enum process_result {
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
||||||
#define MIN(X,Y) (X) < (Y) ? (X) : (Y)
|
#define MIN(X,Y) (X) < (Y) ? (X) : (Y)
|
||||||
#define MAX(X,Y) (X) > (Y) ? (X) : (Y)
|
#define MAX(X,Y) (X) > (Y) ? (X) : (Y)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
#include "control_msg.h"
|
#include "control_msg.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "buffer_util.h"
|
#include "buffer_util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
@@ -25,16 +23,6 @@ write_string(const char *utf8, size_t max_len, unsigned char *buf) {
|
|||||||
return 2 + len;
|
return 2 + len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t
|
|
||||||
to_fixed_point_16(float f) {
|
|
||||||
SDL_assert(f >= 0.0f && f <= 1.0f);
|
|
||||||
uint32_t u = f * 0x1p16f; // 2^16
|
|
||||||
if (u >= 0xffff) {
|
|
||||||
u = 0xffff;
|
|
||||||
}
|
|
||||||
return (uint16_t) u;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
||||||
buf[0] = msg->type;
|
buf[0] = msg->type;
|
||||||
@@ -54,14 +42,6 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
|||||||
buffer_write32be(&buf[2], msg->inject_mouse_event.buttons);
|
buffer_write32be(&buf[2], msg->inject_mouse_event.buttons);
|
||||||
write_position(&buf[6], &msg->inject_mouse_event.position);
|
write_position(&buf[6], &msg->inject_mouse_event.position);
|
||||||
return 18;
|
return 18;
|
||||||
case CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT:
|
|
||||||
buf[1] = msg->inject_touch_event.action;
|
|
||||||
buffer_write64be(&buf[2], msg->inject_touch_event.finger_id);
|
|
||||||
write_position(&buf[10], &msg->inject_touch_event.position);
|
|
||||||
uint16_t pressure =
|
|
||||||
to_fixed_point_16(msg->inject_touch_event.pressure);
|
|
||||||
buffer_write16be(&buf[22], pressure);
|
|
||||||
return 24;
|
|
||||||
case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
||||||
write_position(&buf[1], &msg->inject_scroll_event.position);
|
write_position(&buf[1], &msg->inject_scroll_event.position);
|
||||||
buffer_write32be(&buf[13],
|
buffer_write32be(&buf[13],
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "android/input.h"
|
#include "android/input.h"
|
||||||
#include "android/keycodes.h"
|
#include "android/keycodes.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
@@ -19,7 +18,6 @@ enum control_msg_type {
|
|||||||
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||||
CONTROL_MSG_TYPE_INJECT_TEXT,
|
CONTROL_MSG_TYPE_INJECT_TEXT,
|
||||||
CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT,
|
CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT,
|
||||||
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
|
||||||
CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||||
CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
||||||
CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
||||||
@@ -51,12 +49,6 @@ struct control_msg {
|
|||||||
enum android_motionevent_buttons buttons;
|
enum android_motionevent_buttons buttons;
|
||||||
struct position position;
|
struct position position;
|
||||||
} inject_mouse_event;
|
} inject_mouse_event;
|
||||||
struct {
|
|
||||||
enum android_motionevent_action action;
|
|
||||||
uint64_t finger_id;
|
|
||||||
struct position position;
|
|
||||||
float pressure;
|
|
||||||
} inject_touch_event;
|
|
||||||
struct {
|
struct {
|
||||||
struct position position;
|
struct position position;
|
||||||
int32_t hscroll;
|
int32_t hscroll;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "cbuf.h"
|
#include "cbuf.h"
|
||||||
#include "control_msg.h"
|
#include "control_msg.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#include "event_converter.h"
|
#include "convert.h"
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#define MAP(FROM, TO) case FROM: *to = TO; return true
|
#define MAP(FROM, TO) case FROM: *to = TO; return true
|
||||||
#define FAIL default: return false
|
#define FAIL default: return false
|
||||||
@@ -33,6 +31,7 @@ autocomplete_metastate(enum android_metastate metastate) {
|
|||||||
return metastate;
|
return metastate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static enum android_metastate
|
static enum android_metastate
|
||||||
convert_meta_state(SDL_Keymod mod) {
|
convert_meta_state(SDL_Keymod mod) {
|
||||||
enum android_metastate metastate = 0;
|
enum android_metastate metastate = 0;
|
||||||
@@ -159,7 +158,8 @@ convert_mouse_buttons(uint32_t state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to) {
|
input_key_from_sdl_to_android(const SDL_KeyboardEvent *from,
|
||||||
|
struct control_msg *to) {
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
to->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||||
|
|
||||||
if (!convert_keycode_action(from->type, &to->inject_keycode.action)) {
|
if (!convert_keycode_action(from->type, &to->inject_keycode.action)) {
|
||||||
@@ -177,8 +177,9 @@ convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
convert_mouse_button(const SDL_MouseButtonEvent *from, struct size screen_size,
|
mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from,
|
||||||
struct control_msg *to) {
|
struct size screen_size,
|
||||||
|
struct control_msg *to) {
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT;
|
to->type = CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT;
|
||||||
|
|
||||||
if (!convert_mouse_action(from->type, &to->inject_mouse_event.action)) {
|
if (!convert_mouse_action(from->type, &to->inject_mouse_event.action)) {
|
||||||
@@ -195,8 +196,9 @@ convert_mouse_button(const SDL_MouseButtonEvent *from, struct size screen_size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
convert_mouse_motion(const SDL_MouseMotionEvent *from, struct size screen_size,
|
mouse_motion_from_sdl_to_android(const SDL_MouseMotionEvent *from,
|
||||||
struct control_msg *to) {
|
struct size screen_size,
|
||||||
|
struct control_msg *to) {
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT;
|
to->type = CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT;
|
||||||
to->inject_mouse_event.action = AMOTION_EVENT_ACTION_MOVE;
|
to->inject_mouse_event.action = AMOTION_EVENT_ACTION_MOVE;
|
||||||
to->inject_mouse_event.buttons = convert_mouse_buttons(from->state);
|
to->inject_mouse_event.buttons = convert_mouse_buttons(from->state);
|
||||||
@@ -207,37 +209,10 @@ convert_mouse_motion(const SDL_MouseMotionEvent *from, struct size screen_size,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
convert_touch_action(SDL_EventType from, enum android_motionevent_action *to) {
|
|
||||||
switch (from) {
|
|
||||||
MAP(SDL_FINGERMOTION, AMOTION_EVENT_ACTION_MOVE);
|
|
||||||
MAP(SDL_FINGERDOWN, AMOTION_EVENT_ACTION_DOWN);
|
|
||||||
MAP(SDL_FINGERUP, AMOTION_EVENT_ACTION_UP);
|
|
||||||
FAIL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
convert_touch(const SDL_TouchFingerEvent *from, struct size screen_size,
|
mouse_wheel_from_sdl_to_android(const SDL_MouseWheelEvent *from,
|
||||||
struct control_msg *to) {
|
struct position position,
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
|
struct control_msg *to) {
|
||||||
|
|
||||||
if (!convert_touch_action(from->type, &to->inject_touch_event.action)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
to->inject_touch_event.finger_id = from->fingerId;
|
|
||||||
to->inject_touch_event.position.screen_size = screen_size;
|
|
||||||
// SDL touch event coordinates are normalized in the range [0; 1]
|
|
||||||
to->inject_touch_event.position.point.x = from->x * screen_size.width;
|
|
||||||
to->inject_touch_event.position.point.y = from->y * screen_size.height;
|
|
||||||
to->inject_touch_event.pressure = from->pressure;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct position position,
|
|
||||||
struct control_msg *to) {
|
|
||||||
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
|
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
|
||||||
|
|
||||||
to->inject_scroll_event.position = position;
|
to->inject_scroll_event.position = position;
|
||||||
41
app/src/convert.h
Normal file
41
app/src/convert.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef CONVERT_H
|
||||||
|
#define CONVERT_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <SDL2/SDL_events.h>
|
||||||
|
|
||||||
|
#include "control_msg.h"
|
||||||
|
|
||||||
|
struct complete_mouse_motion_event {
|
||||||
|
SDL_MouseMotionEvent *mouse_motion_event;
|
||||||
|
struct size screen_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct complete_mouse_wheel_event {
|
||||||
|
SDL_MouseWheelEvent *mouse_wheel_event;
|
||||||
|
struct point position;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool
|
||||||
|
input_key_from_sdl_to_android(const SDL_KeyboardEvent *from,
|
||||||
|
struct control_msg *to);
|
||||||
|
|
||||||
|
bool
|
||||||
|
mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from,
|
||||||
|
struct size screen_size,
|
||||||
|
struct control_msg *to);
|
||||||
|
|
||||||
|
// the video size may be different from the real device size, so we need the
|
||||||
|
// size to which the absolute position apply, to scale it accordingly
|
||||||
|
bool
|
||||||
|
mouse_motion_from_sdl_to_android(const SDL_MouseMotionEvent *from,
|
||||||
|
struct size screen_size,
|
||||||
|
struct control_msg *to);
|
||||||
|
|
||||||
|
// on Android, a scroll event requires the current mouse position
|
||||||
|
bool
|
||||||
|
mouse_wheel_from_sdl_to_android(const SDL_MouseWheelEvent *from,
|
||||||
|
struct position position,
|
||||||
|
struct control_msg *to);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -8,8 +8,8 @@
|
|||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
#include "config.h"
|
||||||
#include "buffer_util.h"
|
#include "buffer_util.h"
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
#include "lock_util.h"
|
#include "lock_util.h"
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
struct video_buffer;
|
struct video_buffer;
|
||||||
|
|
||||||
struct decoder {
|
struct decoder {
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#include "device.h"
|
#include "device.h"
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <SDL2/SDL_assert.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "buffer_util.h"
|
#include "buffer_util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#define DEVICE_MSG_TEXT_MAX_LENGTH 4093
|
#define DEVICE_MSG_TEXT_MAX_LENGTH 4093
|
||||||
#define DEVICE_MSG_SERIALIZED_MAX_SIZE (3 + DEVICE_MSG_TEXT_MAX_LENGTH)
|
#define DEVICE_MSG_SERIALIZED_MAX_SIZE (3 + DEVICE_MSG_TEXT_MAX_LENGTH)
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
#ifndef CONVERT_H
|
|
||||||
#define CONVERT_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <SDL2/SDL_events.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "control_msg.h"
|
|
||||||
|
|
||||||
struct complete_mouse_motion_event {
|
|
||||||
SDL_MouseMotionEvent *mouse_motion_event;
|
|
||||||
struct size screen_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct complete_mouse_wheel_event {
|
|
||||||
SDL_MouseWheelEvent *mouse_wheel_event;
|
|
||||||
struct point position;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool
|
|
||||||
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to);
|
|
||||||
|
|
||||||
bool
|
|
||||||
convert_mouse_button(const SDL_MouseButtonEvent *from, struct size screen_size,
|
|
||||||
struct control_msg *to);
|
|
||||||
|
|
||||||
// the video size may be different from the real device size, so we need the
|
|
||||||
// size to which the absolute position apply, to scale it accordingly
|
|
||||||
bool
|
|
||||||
convert_mouse_motion(const SDL_MouseMotionEvent *from, struct size screen_size,
|
|
||||||
struct control_msg *to);
|
|
||||||
|
|
||||||
bool
|
|
||||||
convert_touch(const SDL_TouchFingerEvent *from, struct size screen_size,
|
|
||||||
struct control_msg *to);
|
|
||||||
|
|
||||||
// on Android, a scroll event requires the current mouse position
|
|
||||||
bool
|
|
||||||
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct position position,
|
|
||||||
struct control_msg *to);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "cbuf.h"
|
#include "cbuf.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <SDL2/SDL_assert.h>
|
#include <SDL2/SDL_assert.h>
|
||||||
#include <SDL2/SDL_timer.h>
|
#include <SDL2/SDL_timer.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "lock_util.h"
|
#include "lock_util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
struct fps_counter {
|
struct fps_counter {
|
||||||
SDL_Thread *thread;
|
SDL_Thread *thread;
|
||||||
SDL_mutex *mutex;
|
SDL_mutex *mutex;
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
|
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <SDL2/SDL_assert.h>
|
||||||
|
#include "convert.h"
|
||||||
#include "config.h"
|
|
||||||
#include "event_converter.h"
|
|
||||||
#include "lock_util.h"
|
#include "lock_util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
@@ -104,7 +102,7 @@ press_back_or_turn_screen_on(struct controller *controller) {
|
|||||||
msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
|
msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request 'press back or turn screen on'");
|
LOGW("Could not request 'turn screen on'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,27 +242,16 @@ 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 shortcuts involve Alt, and it must not be forwarded to the device
|
// no shortcut involves Alt or Meta, and they should not be forwarded
|
||||||
|
// 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 || cmd) {
|
if (ctrl | meta) {
|
||||||
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;
|
||||||
@@ -272,59 +259,63 @@ 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 && cmd && !shift && !repeat) {
|
if (control && ctrl && !meta && !shift && !repeat) {
|
||||||
action_back(controller, action);
|
action_back(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_s:
|
case SDLK_s:
|
||||||
if (control && cmd && !shift && !repeat) {
|
if (control && ctrl && !meta && !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 && cmd && !shift && !repeat) {
|
if (control && ctrl && !meta && !shift && !repeat) {
|
||||||
action_power(controller, action);
|
action_power(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_o:
|
case SDLK_o:
|
||||||
if (control && cmd && !shift && down) {
|
if (control && ctrl && !shift && !meta && 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:
|
||||||
if (control && cmd && !shift) {
|
#ifdef __APPLE__
|
||||||
|
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:
|
||||||
if (control && cmd && !shift) {
|
#ifdef __APPLE__
|
||||||
|
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 && cmd && !shift && !repeat && down) {
|
if (control && ctrl && !meta && !shift && !repeat && down) {
|
||||||
request_device_clipboard(controller);
|
request_device_clipboard(controller);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_v:
|
case SDLK_v:
|
||||||
if (control && cmd && !repeat && down) {
|
if (control && ctrl && !meta && !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);
|
||||||
@@ -335,29 +326,29 @@ input_manager_process_key(struct input_manager *input_manager,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_f:
|
case SDLK_f:
|
||||||
if (!shift && cmd && !repeat && down) {
|
if (ctrl && !meta && !shift && !repeat && down) {
|
||||||
screen_switch_fullscreen(input_manager->screen);
|
screen_switch_fullscreen(input_manager->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_x:
|
case SDLK_x:
|
||||||
if (!shift && cmd && !repeat && down) {
|
if (ctrl && !meta && !shift && !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 (!shift && cmd && !repeat && down) {
|
if (ctrl && !meta && !shift && !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 (!shift && cmd && !repeat && down) {
|
if (ctrl && !meta && !shift && !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 && cmd && !repeat && down) {
|
if (control && ctrl && !meta && !repeat && down) {
|
||||||
if (shift) {
|
if (shift) {
|
||||||
collapse_notification_panel(controller);
|
collapse_notification_panel(controller);
|
||||||
} else {
|
} else {
|
||||||
@@ -375,7 +366,7 @@ input_manager_process_key(struct input_manager *input_manager,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
if (convert_input_key(event, &msg)) {
|
if (input_key_from_sdl_to_android(event, &msg)) {
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!controller_push_msg(controller, &msg)) {
|
||||||
LOGW("Could not request 'inject keycode'");
|
LOGW("Could not request 'inject keycode'");
|
||||||
}
|
}
|
||||||
@@ -389,29 +380,16 @@ input_manager_process_mouse_motion(struct input_manager *input_manager,
|
|||||||
// do not send motion events when no button is pressed
|
// do not send motion events when no button is pressed
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event->which == SDL_TOUCH_MOUSEID) {
|
|
||||||
// simulated from touch events, so it's a duplicate
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
if (convert_mouse_motion(event, input_manager->screen->frame_size, &msg)) {
|
if (mouse_motion_from_sdl_to_android(event,
|
||||||
|
input_manager->screen->frame_size,
|
||||||
|
&msg)) {
|
||||||
if (!controller_push_msg(input_manager->controller, &msg)) {
|
if (!controller_push_msg(input_manager->controller, &msg)) {
|
||||||
LOGW("Could not request 'inject mouse motion event'");
|
LOGW("Could not request 'inject mouse motion event'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
input_manager_process_touch(struct input_manager *input_manager,
|
|
||||||
const SDL_TouchFingerEvent *event) {
|
|
||||||
struct control_msg msg;
|
|
||||||
if (convert_touch(event, input_manager->screen->frame_size, &msg)) {
|
|
||||||
if (!controller_push_msg(input_manager->controller, &msg)) {
|
|
||||||
LOGW("Could not request 'inject touch event'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_outside_device_screen(struct input_manager *input_manager, int x, int y)
|
is_outside_device_screen(struct input_manager *input_manager, int x, int y)
|
||||||
{
|
{
|
||||||
@@ -449,7 +427,9 @@ input_manager_process_mouse_button(struct input_manager *input_manager,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
if (convert_mouse_button(event, input_manager->screen->frame_size, &msg)) {
|
if (mouse_button_from_sdl_to_android(event,
|
||||||
|
input_manager->screen->frame_size,
|
||||||
|
&msg)) {
|
||||||
if (!controller_push_msg(input_manager->controller, &msg)) {
|
if (!controller_push_msg(input_manager->controller, &msg)) {
|
||||||
LOGW("Could not request 'inject mouse button event'");
|
LOGW("Could not request 'inject mouse button event'");
|
||||||
}
|
}
|
||||||
@@ -464,7 +444,7 @@ input_manager_process_mouse_wheel(struct input_manager *input_manager,
|
|||||||
.point = get_mouse_point(input_manager->screen),
|
.point = get_mouse_point(input_manager->screen),
|
||||||
};
|
};
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
if (convert_mouse_wheel(event, position, &msg)) {
|
if (mouse_wheel_from_sdl_to_android(event, position, &msg)) {
|
||||||
if (!controller_push_msg(input_manager->controller, &msg)) {
|
if (!controller_push_msg(input_manager->controller, &msg)) {
|
||||||
LOGW("Could not request 'inject mouse wheel event'");
|
LOGW("Could not request 'inject mouse wheel event'");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "fps_counter.h"
|
#include "fps_counter.h"
|
||||||
@@ -29,10 +28,6 @@ void
|
|||||||
input_manager_process_mouse_motion(struct input_manager *input_manager,
|
input_manager_process_mouse_motion(struct input_manager *input_manager,
|
||||||
const SDL_MouseMotionEvent *event);
|
const SDL_MouseMotionEvent *event);
|
||||||
|
|
||||||
void
|
|
||||||
input_manager_process_touch(struct input_manager *input_manager,
|
|
||||||
const SDL_TouchFingerEvent *event);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
input_manager_process_mouse_button(struct input_manager *input_manager,
|
input_manager_process_mouse_button(struct input_manager *input_manager,
|
||||||
const SDL_MouseButtonEvent *event,
|
const SDL_MouseButtonEvent *event,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
#define SDL_MAIN_HANDLED // avoid link error on Linux Windows Subsystem
|
#define SDL_MAIN_HANDLED // avoid link error on Linux Windows Subsystem
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
#include "config.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "recorder.h"
|
#include "recorder.h"
|
||||||
|
|
||||||
@@ -35,11 +35,6 @@ 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"
|
||||||
@@ -120,13 +115,13 @@ static void usage(const char *arg0) {
|
|||||||
"\n"
|
"\n"
|
||||||
"Shortcuts:\n"
|
"Shortcuts:\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+f\n"
|
" Ctrl+f\n"
|
||||||
" switch fullscreen mode\n"
|
" switch fullscreen mode\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+g\n"
|
" Ctrl+g\n"
|
||||||
" resize window to 1:1 (pixel-perfect)\n"
|
" resize window to 1:1 (pixel-perfect)\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+x\n"
|
" Ctrl+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"
|
||||||
@@ -134,48 +129,48 @@ static void usage(const char *arg0) {
|
|||||||
" Middle-click\n"
|
" Middle-click\n"
|
||||||
" click on HOME\n"
|
" click on HOME\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+b\n"
|
" Ctrl+b\n"
|
||||||
" " CTRL_OR_CMD "+Backspace\n"
|
" Ctrl+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_OR_CMD "+s\n"
|
" Ctrl+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_OR_CMD "+Up\n"
|
" Ctrl+Up\n"
|
||||||
" click on VOLUME_UP\n"
|
" click on VOLUME_UP\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+Down\n"
|
" Ctrl+Down\n"
|
||||||
" click on VOLUME_DOWN\n"
|
" click on VOLUME_DOWN\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+p\n"
|
" Ctrl+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_OR_CMD "+o\n"
|
" Ctrl+o\n"
|
||||||
" turn device screen off (keep mirroring)\n"
|
" turn device screen off (keep mirroring)\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+n\n"
|
" Ctrl+n\n"
|
||||||
" expand notification panel\n"
|
" expand notification panel\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+Shift+n\n"
|
" Ctrl+Shift+n\n"
|
||||||
" collapse notification panel\n"
|
" collapse notification panel\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+c\n"
|
" Ctrl+c\n"
|
||||||
" copy device clipboard to computer\n"
|
" copy device clipboard to computer\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+v\n"
|
" Ctrl+v\n"
|
||||||
" paste computer clipboard to device\n"
|
" paste computer clipboard to device\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+Shift+v\n"
|
" Ctrl+Shift+v\n"
|
||||||
" copy computer clipboard to device\n"
|
" copy computer clipboard to device\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " CTRL_OR_CMD "+i\n"
|
" Ctrl+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"
|
||||||
@@ -328,7 +323,7 @@ parse_args(struct args *args, int argc, char *argv[]) {
|
|||||||
{"push-target", required_argument, NULL,
|
{"push-target", required_argument, NULL,
|
||||||
OPT_PUSH_TARGET},
|
OPT_PUSH_TARGET},
|
||||||
{"record", required_argument, NULL, 'r'},
|
{"record", required_argument, NULL, 'r'},
|
||||||
{"record-format", required_argument, NULL, 'F'},
|
{"record-format", required_argument, NULL, 'f'},
|
||||||
{"render-expired-frames", no_argument, NULL,
|
{"render-expired-frames", no_argument, NULL,
|
||||||
OPT_RENDER_EXPIRED_FRAMES},
|
OPT_RENDER_EXPIRED_FRAMES},
|
||||||
{"serial", required_argument, NULL, 's'},
|
{"serial", required_argument, NULL, 's'},
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#ifdef __WINDOWS__
|
#ifdef __WINDOWS__
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
typedef int socket_t;
|
typedef int socket_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
net_init(void);
|
net_init(void);
|
||||||
|
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
// generic intrusive FIFO queue
|
|
||||||
#ifndef QUEUE_H
|
|
||||||
#define QUEUE_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <SDL2/SDL_assert.h>
|
|
||||||
|
|
||||||
#include "config.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
|
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
// receive events from the device
|
// receive events from the device
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
#include <libavutil/time.h>
|
#include <libavutil/time.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <SDL2/SDL_assert.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
#include "config.h"
|
||||||
#include "lock_util.h"
|
#include "lock_util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
@@ -33,15 +33,11 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,12 +48,59 @@ record_packet_delete(struct record_packet *rec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
recorder_queue_clear(struct recorder_queue *queue) {
|
recorder_queue_init(struct recorder_queue *queue) {
|
||||||
while (!queue_is_empty(queue)) {
|
queue->first = NULL;
|
||||||
struct record_packet *rec;
|
// queue->last is undefined if queue->first == NULL
|
||||||
queue_take(queue, next, &rec);
|
}
|
||||||
record_packet_delete(rec);
|
|
||||||
|
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
|
||||||
|
recorder_queue_clear(struct recorder_queue *queue) {
|
||||||
|
struct record_packet *rec = queue->first;
|
||||||
|
while (rec) {
|
||||||
|
struct record_packet *current = rec;
|
||||||
|
rec = rec->next;
|
||||||
|
record_packet_delete(current);
|
||||||
|
}
|
||||||
|
queue->first = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -86,13 +129,12 @@ recorder_init(struct recorder *recorder,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
queue_init(&recorder->queue);
|
recorder_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;
|
||||||
}
|
}
|
||||||
@@ -254,50 +296,25 @@ run_recorder(void *data) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
mutex_lock(recorder->mutex);
|
mutex_lock(recorder->mutex);
|
||||||
|
|
||||||
while (!recorder->stopped && queue_is_empty(&recorder->queue)) {
|
while (!recorder->stopped &&
|
||||||
|
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 && queue_is_empty(&recorder->queue)) {
|
if (recorder->stopped && recorder_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;
|
struct record_packet *rec = recorder_queue_take(&recorder->queue);
|
||||||
queue_take(&recorder->queue, next, &rec);
|
|
||||||
|
|
||||||
mutex_unlock(recorder->mutex);
|
mutex_unlock(recorder->mutex);
|
||||||
|
|
||||||
// recorder->previous is only written from this thread, no need to lock
|
bool ok = recorder_write(recorder, &rec->packet);
|
||||||
struct record_packet *previous = recorder->previous;
|
record_packet_delete(rec);
|
||||||
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");
|
||||||
|
|
||||||
@@ -352,15 +369,9 @@ recorder_push(struct recorder *recorder, const AVPacket *packet) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct record_packet *rec = record_packet_new(packet);
|
bool ok = recorder_queue_push(&recorder->queue, 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 true;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,7 @@
|
|||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "queue.h"
|
|
||||||
|
|
||||||
enum recorder_format {
|
enum recorder_format {
|
||||||
RECORDER_FORMAT_MP4 = 1,
|
RECORDER_FORMAT_MP4 = 1,
|
||||||
@@ -20,7 +18,10 @@ struct record_packet {
|
|||||||
struct record_packet *next;
|
struct record_packet *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct recorder_queue QUEUE(struct record_packet);
|
struct recorder_queue {
|
||||||
|
struct record_packet *first;
|
||||||
|
struct record_packet *last; // undefined if first is NULL
|
||||||
|
};
|
||||||
|
|
||||||
struct recorder {
|
struct recorder {
|
||||||
char *filename;
|
char *filename;
|
||||||
@@ -35,12 +36,6 @@ 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
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
@@ -181,11 +180,6 @@ handle_event(SDL_Event *event, bool control) {
|
|||||||
input_manager_process_mouse_button(&input_manager, &event->button,
|
input_manager_process_mouse_button(&input_manager, &event->button,
|
||||||
control);
|
control);
|
||||||
break;
|
break;
|
||||||
case SDL_FINGERMOTION:
|
|
||||||
case SDL_FINGERDOWN:
|
|
||||||
case SDL_FINGERUP:
|
|
||||||
input_manager_process_touch(&input_manager, &event->tfinger);
|
|
||||||
break;
|
|
||||||
case SDL_DROPFILE: {
|
case SDL_DROPFILE: {
|
||||||
if (!control) {
|
if (!control) {
|
||||||
break;
|
break;
|
||||||
@@ -303,6 +297,7 @@ 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;
|
||||||
@@ -364,7 +359,10 @@ scrcpy(const struct scrcpy_options *options) {
|
|||||||
|
|
||||||
av_log_set_callback(av_log_callback);
|
av_log_set_callback(av_log_callback);
|
||||||
|
|
||||||
stream_init(&stream, server.video_socket, dec, rec);
|
if (!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
|
||||||
@@ -443,6 +441,9 @@ 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <recorder.h>
|
#include <recorder.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
struct scrcpy_options {
|
struct scrcpy_options {
|
||||||
const char *serial;
|
const char *serial;
|
||||||
const char *crop;
|
const char *crop;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "icon.xpm"
|
#include "icon.xpm"
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
struct video_buffer;
|
struct video_buffer;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
#include <SDL2/SDL_stdinc.h>
|
#include <SDL2/SDL_stdinc.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
xstrncpy(char *dest, const char *src, size_t n) {
|
xstrncpy(char *dest, const char *src, size_t n) {
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
// like strncpy, except:
|
// like strncpy, except:
|
||||||
// - it copies at most n-1 chars
|
// - it copies at most n-1 chars
|
||||||
// - the dest string is nul-terminated
|
// - the dest string is nul-terminated
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
#include "config.h"
|
||||||
#include "buffer_util.h"
|
#include "buffer_util.h"
|
||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "recorder.h"
|
#include "recorder.h"
|
||||||
|
|
||||||
#define BUFSIZE 0x10000
|
#define STREAM_BUFSIZE 0x10000
|
||||||
|
|
||||||
#define HEADER_SIZE 12
|
#define HEADER_SIZE 12
|
||||||
#define NO_PTS UINT64_C(-1)
|
#define NO_PTS UINT64_C(-1)
|
||||||
@@ -37,7 +37,8 @@ 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 = net_recv_all(stream->socket, header, HEADER_SIZE);
|
ssize_t r =
|
||||||
|
buffered_reader_recv_all(&stream->buffered_reader, header, HEADER_SIZE);
|
||||||
if (r < HEADER_SIZE) {
|
if (r < HEADER_SIZE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -51,7 +52,7 @@ stream_recv_packet(struct stream *stream, AVPacket *packet) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = net_recv_all(stream->socket, packet->data, len);
|
r = buffered_reader_recv_all(&stream->buffered_reader, packet->data, len);
|
||||||
if (r < len) {
|
if (r < len) {
|
||||||
av_packet_unref(packet);
|
av_packet_unref(packet);
|
||||||
return false;
|
return false;
|
||||||
@@ -267,13 +268,23 @@ end:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
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
|
||||||
|
|||||||
@@ -7,13 +7,14 @@
|
|||||||
#include <SDL2/SDL_atomic.h>
|
#include <SDL2/SDL_atomic.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
|
|
||||||
#include "config.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;
|
||||||
@@ -26,10 +27,13 @@ struct stream {
|
|||||||
AVPacket pending;
|
AVPacket pending;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
bool
|
||||||
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);
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
net_init(void) {
|
net_init(void) {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
struct index {
|
struct index {
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
SDL_Surface *
|
SDL_Surface *
|
||||||
read_xpm(char *xpm[]);
|
read_xpm(char *xpm[]);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <SDL2/SDL_mutex.h>
|
#include <SDL2/SDL_mutex.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "fps_counter.h"
|
#include "fps_counter.h"
|
||||||
|
|
||||||
// forward declarations
|
// forward declarations
|
||||||
|
|||||||
@@ -99,41 +99,6 @@ static void test_serialize_inject_mouse_event(void) {
|
|||||||
};
|
};
|
||||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
}
|
}
|
||||||
#include <stdio.h>
|
|
||||||
static void test_serialize_inject_touch_event(void) {
|
|
||||||
struct control_msg msg = {
|
|
||||||
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
|
||||||
.inject_touch_event = {
|
|
||||||
.action = AMOTION_EVENT_ACTION_DOWN,
|
|
||||||
.finger_id = 0x1234567887654321L,
|
|
||||||
.position = {
|
|
||||||
.point = {
|
|
||||||
.x = 100,
|
|
||||||
.y = 200,
|
|
||||||
},
|
|
||||||
.screen_size = {
|
|
||||||
.width = 1080,
|
|
||||||
.height = 1920,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.pressure = 1.0f,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
|
|
||||||
int size = control_msg_serialize(&msg, buf);
|
|
||||||
assert(size == 24);
|
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
|
||||||
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
|
||||||
0x00, // AKEY_EVENT_ACTION_DOWN
|
|
||||||
0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21, // finger id
|
|
||||||
0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0xc8, // 100 200
|
|
||||||
0x04, 0x38, 0x07, 0x80, // 1080 1920
|
|
||||||
0xff, 0xff, // pressure
|
|
||||||
};
|
|
||||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_serialize_inject_scroll_event(void) {
|
static void test_serialize_inject_scroll_event(void) {
|
||||||
struct control_msg msg = {
|
struct control_msg msg = {
|
||||||
@@ -272,7 +237,6 @@ int main(void) {
|
|||||||
test_serialize_inject_text();
|
test_serialize_inject_text();
|
||||||
test_serialize_inject_text_long();
|
test_serialize_inject_text_long();
|
||||||
test_serialize_inject_mouse_event();
|
test_serialize_inject_mouse_event();
|
||||||
test_serialize_inject_touch_event();
|
|
||||||
test_serialize_inject_scroll_event();
|
test_serialize_inject_scroll_event();
|
||||||
test_serialize_back_or_screen_on();
|
test_serialize_back_or_screen_on();
|
||||||
test_serialize_expand_notification_panel();
|
test_serialize_expand_notification_panel();
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.4.2'
|
classpath 'com.android.tools.build:gradle:3.3.0'
|
||||||
|
|
||||||
// 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
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ cpu = 'i686'
|
|||||||
endian = 'little'
|
endian = 'little'
|
||||||
|
|
||||||
[properties]
|
[properties]
|
||||||
prebuilt_ffmpeg_shared = 'ffmpeg-4.1.4-win32-shared'
|
prebuilt_ffmpeg_shared = 'ffmpeg-4.1.3-win32-shared'
|
||||||
prebuilt_ffmpeg_dev = 'ffmpeg-4.1.4-win32-dev'
|
prebuilt_ffmpeg_dev = 'ffmpeg-4.1.3-win32-dev'
|
||||||
prebuilt_sdl2 = 'SDL2-2.0.10/i686-w64-mingw32'
|
prebuilt_sdl2 = 'SDL2-2.0.8/i686-w64-mingw32'
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ cpu = 'x86_64'
|
|||||||
endian = 'little'
|
endian = 'little'
|
||||||
|
|
||||||
[properties]
|
[properties]
|
||||||
prebuilt_ffmpeg_shared = 'ffmpeg-4.1.4-win64-shared'
|
prebuilt_ffmpeg_shared = 'ffmpeg-4.1.3-win64-shared'
|
||||||
prebuilt_ffmpeg_dev = 'ffmpeg-4.1.4-win64-dev'
|
prebuilt_ffmpeg_dev = 'ffmpeg-4.1.3-win64-dev'
|
||||||
prebuilt_sdl2 = 'SDL2-2.0.10/x86_64-w64-mingw32'
|
prebuilt_sdl2 = 'SDL2-2.0.8/x86_64-w64-mingw32'
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,6 @@
|
|||||||
|
#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
|
||||||
|
|||||||
116
gradlew
vendored
116
gradlew
vendored
@@ -1,20 +1,4 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
@@ -22,6 +6,42 @@
|
|||||||
##
|
##
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn ( ) {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die ( ) {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
PRG="$0"
|
||||||
@@ -40,46 +60,6 @@ cd "`dirname \"$PRG\"`/" >/dev/null
|
|||||||
APP_HOME="`pwd -P`"
|
APP_HOME="`pwd -P`"
|
||||||
cd "$SAVED" >/dev/null
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
|
||||||
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.
|
|
||||||
MAX_FD="maximum"
|
|
||||||
|
|
||||||
warn () {
|
|
||||||
echo "$*"
|
|
||||||
}
|
|
||||||
|
|
||||||
die () {
|
|
||||||
echo
|
|
||||||
echo "$*"
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
|
||||||
cygwin=false
|
|
||||||
msys=false
|
|
||||||
darwin=false
|
|
||||||
nonstop=false
|
|
||||||
case "`uname`" in
|
|
||||||
CYGWIN* )
|
|
||||||
cygwin=true
|
|
||||||
;;
|
|
||||||
Darwin* )
|
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
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.
|
||||||
@@ -105,7 +85,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" -a "$nonstop" = "false" ] ; then
|
if [ "$cygwin" = "false" -a "$darwin" = "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
|
||||||
@@ -170,19 +150,11 @@ if $cygwin ; then
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||||
save () {
|
function splitJvmOpts() {
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
JVM_OPTS=("$@")
|
||||||
echo " "
|
|
||||||
}
|
}
|
||||||
APP_ARGS=$(save "$@")
|
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||||
|
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||||
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
30
gradlew.bat
vendored
@@ -1,19 +1,3 @@
|
|||||||
@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
|
||||||
@@ -24,14 +8,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
|
||||||
|
|
||||||
@@ -62,9 +46,10 @@ echo location of your Java installation.
|
|||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:init
|
:init
|
||||||
@rem Get command-line arguments, handling Windows variants
|
@rem Get command-line arguments, handling Windowz 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.
|
||||||
@@ -75,6 +60,11 @@ 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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
project('scrcpy', 'c',
|
project('scrcpy', 'c',
|
||||||
version: '1.10',
|
version: '1.9',
|
||||||
meson_version: '>= 0.37',
|
meson_version: '>= 0.37',
|
||||||
default_options: 'c_std=c11')
|
default_options: 'c_std=c11')
|
||||||
|
|
||||||
|
|||||||
@@ -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.4-win32-shared.zip \
|
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.1.3-win32-shared.zip \
|
||||||
596608277f6b937c3dea7c46e854665d75b3de56790bae07f655ca331440f003 \
|
8ea472d673370d5e87517a75587abfa6f189ee4f82e8da21fdbc49d0db0c1a89 \
|
||||||
ffmpeg-4.1.4-win32-shared
|
ffmpeg-4.1.3-win32-shared
|
||||||
|
|
||||||
prepare-ffmpeg-dev-win32:
|
prepare-ffmpeg-dev-win32:
|
||||||
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.1.4-win32-dev.zip \
|
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.1.3-win32-dev.zip \
|
||||||
a80c86e263cfad26e202edfa5e6e939a2c88843ae26f031d3e0d981a39fd03fb \
|
e16d3150b6ccf0b71908f5b964cb8c051d79053c8f5cd6d777d617ab4f03613a \
|
||||||
ffmpeg-4.1.4-win32-dev
|
ffmpeg-4.1.3-win32-dev
|
||||||
|
|
||||||
prepare-ffmpeg-shared-win64:
|
prepare-ffmpeg-shared-win64:
|
||||||
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.1.4-win64-shared.zip \
|
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.1.3-win64-shared.zip \
|
||||||
a90889871de2cab8a79b392591313a188189a353f69dde1db98aebe20b280989 \
|
0b974578e07d974c4bafb36c7ed0b46e46b001d38b149455089c13b57ddefe5d \
|
||||||
ffmpeg-4.1.4-win64-shared
|
ffmpeg-4.1.3-win64-shared
|
||||||
|
|
||||||
prepare-ffmpeg-dev-win64:
|
prepare-ffmpeg-dev-win64:
|
||||||
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.1.4-win64-dev.zip \
|
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.1.3-win64-dev.zip \
|
||||||
6c9d53f9e94ce1821e975ec668e5b9d6e9deb4a45d0d7e30264685d3dfbbb068 \
|
334b473467db096a5b74242743592a73e120a137232794508e4fc55593696a5b \
|
||||||
ffmpeg-4.1.4-win64-dev
|
ffmpeg-4.1.3-win64-dev
|
||||||
|
|
||||||
prepare-sdl2:
|
prepare-sdl2:
|
||||||
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.10-mingw.tar.gz \
|
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.8-mingw.tar.gz \
|
||||||
a90a7cddaec4996f4d7be6d80c57ec69b062e132bffc513965f99217f603274a \
|
ffff7305d634aff5e1df5b7bb935435c3a02c8b03ad94a1a2be9169a558a7961 \
|
||||||
SDL2-2.0.10
|
SDL2-2.0.8
|
||||||
|
|
||||||
prepare-adb:
|
prepare-adb:
|
||||||
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r29.0.2-windows.zip \
|
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r29.0.1-windows.zip \
|
||||||
d78f02e5e2c9c4c1d046dcd4e6fbdf586e5f57ef66eb0da5c2b49d745d85d5ee \
|
2334f92cf571fd2d9bf6ff7c637765bee5d8323e0bd8e051e15927d87b54b4e8 \
|
||||||
platform-tools
|
platform-tools
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ android {
|
|||||||
applicationId "com.genymobile.scrcpy"
|
applicationId "com.genymobile.scrcpy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 11
|
versionCode 10
|
||||||
versionName "1.10"
|
versionName "1.9"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ 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
|
||||||
|
|||||||
@@ -8,14 +8,13 @@ public final class ControlMessage {
|
|||||||
public static final int TYPE_INJECT_KEYCODE = 0;
|
public static final int TYPE_INJECT_KEYCODE = 0;
|
||||||
public static final int TYPE_INJECT_TEXT = 1;
|
public static final int TYPE_INJECT_TEXT = 1;
|
||||||
public static final int TYPE_INJECT_MOUSE_EVENT = 2;
|
public static final int TYPE_INJECT_MOUSE_EVENT = 2;
|
||||||
public static final int TYPE_INJECT_TOUCH_EVENT = 3;
|
public static final int TYPE_INJECT_SCROLL_EVENT = 3;
|
||||||
public static final int TYPE_INJECT_SCROLL_EVENT = 4;
|
public static final int TYPE_BACK_OR_SCREEN_ON = 4;
|
||||||
public static final int TYPE_BACK_OR_SCREEN_ON = 5;
|
public static final int TYPE_EXPAND_NOTIFICATION_PANEL = 5;
|
||||||
public static final int TYPE_EXPAND_NOTIFICATION_PANEL = 6;
|
public static final int TYPE_COLLAPSE_NOTIFICATION_PANEL = 6;
|
||||||
public static final int TYPE_COLLAPSE_NOTIFICATION_PANEL = 7;
|
public static final int TYPE_GET_CLIPBOARD = 7;
|
||||||
public static final int TYPE_GET_CLIPBOARD = 8;
|
public static final int TYPE_SET_CLIPBOARD = 8;
|
||||||
public static final int TYPE_SET_CLIPBOARD = 9;
|
public static final int TYPE_SET_SCREEN_POWER_MODE = 9;
|
||||||
public static final int TYPE_SET_SCREEN_POWER_MODE = 10;
|
|
||||||
|
|
||||||
private int type;
|
private int type;
|
||||||
private String text;
|
private String text;
|
||||||
@@ -23,8 +22,6 @@ public final class ControlMessage {
|
|||||||
private int action; // KeyEvent.ACTION_* or MotionEvent.ACTION_* or POWER_MODE_*
|
private int action; // KeyEvent.ACTION_* or MotionEvent.ACTION_* or POWER_MODE_*
|
||||||
private int keycode; // KeyEvent.KEYCODE_*
|
private int keycode; // KeyEvent.KEYCODE_*
|
||||||
private int buttons; // MotionEvent.BUTTON_*
|
private int buttons; // MotionEvent.BUTTON_*
|
||||||
private long fingerId;
|
|
||||||
private float pressure;
|
|
||||||
private Position position;
|
private Position position;
|
||||||
private int hScroll;
|
private int hScroll;
|
||||||
private int vScroll;
|
private int vScroll;
|
||||||
@@ -33,70 +30,60 @@ public final class ControlMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createInjectKeycode(int action, int keycode, int metaState) {
|
public static ControlMessage createInjectKeycode(int action, int keycode, int metaState) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = TYPE_INJECT_KEYCODE;
|
event.type = TYPE_INJECT_KEYCODE;
|
||||||
msg.action = action;
|
event.action = action;
|
||||||
msg.keycode = keycode;
|
event.keycode = keycode;
|
||||||
msg.metaState = metaState;
|
event.metaState = metaState;
|
||||||
return msg;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createInjectText(String text) {
|
public static ControlMessage createInjectText(String text) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = TYPE_INJECT_TEXT;
|
event.type = TYPE_INJECT_TEXT;
|
||||||
msg.text = text;
|
event.text = text;
|
||||||
return msg;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createInjectMouseEvent(int action, int buttons, Position position) {
|
public static ControlMessage createInjectMouseEvent(int action, int buttons, Position position) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = TYPE_INJECT_MOUSE_EVENT;
|
event.type = TYPE_INJECT_MOUSE_EVENT;
|
||||||
msg.action = action;
|
event.action = action;
|
||||||
msg.buttons = buttons;
|
event.buttons = buttons;
|
||||||
msg.position = position;
|
event.position = position;
|
||||||
return msg;
|
return event;
|
||||||
}
|
|
||||||
|
|
||||||
public static ControlMessage createInjectTouchEvent(int action, long fingerId, Position position, float pressure) {
|
|
||||||
ControlMessage msg = new ControlMessage();
|
|
||||||
msg.type = TYPE_INJECT_TOUCH_EVENT;
|
|
||||||
msg.action = action;
|
|
||||||
msg.fingerId = fingerId;
|
|
||||||
msg.pressure = pressure;
|
|
||||||
msg.position = position;
|
|
||||||
return msg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll) {
|
public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = TYPE_INJECT_SCROLL_EVENT;
|
event.type = TYPE_INJECT_SCROLL_EVENT;
|
||||||
msg.position = position;
|
event.position = position;
|
||||||
msg.hScroll = hScroll;
|
event.hScroll = hScroll;
|
||||||
msg.vScroll = vScroll;
|
event.vScroll = vScroll;
|
||||||
return msg;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createSetClipboard(String text) {
|
public static ControlMessage createSetClipboard(String text) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = TYPE_SET_CLIPBOARD;
|
event.type = TYPE_SET_CLIPBOARD;
|
||||||
msg.text = text;
|
event.text = text;
|
||||||
return msg;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mode one of the {@code Device.SCREEN_POWER_MODE_*} constants
|
* @param mode one of the {@code Device.SCREEN_POWER_MODE_*} constants
|
||||||
*/
|
*/
|
||||||
public static ControlMessage createSetScreenPowerMode(int mode) {
|
public static ControlMessage createSetScreenPowerMode(int mode) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = TYPE_SET_SCREEN_POWER_MODE;
|
event.type = TYPE_SET_SCREEN_POWER_MODE;
|
||||||
msg.action = mode;
|
event.action = mode;
|
||||||
return msg;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createEmpty(int type) {
|
public static ControlMessage createEmpty(int type) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
msg.type = type;
|
event.type = type;
|
||||||
return msg;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getType() {
|
public int getType() {
|
||||||
@@ -123,14 +110,6 @@ public final class ControlMessage {
|
|||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getFingerId() {
|
|
||||||
return fingerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getPressure() {
|
|
||||||
return pressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Position getPosition() {
|
public Position getPosition() {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ public class ControlMessageReader {
|
|||||||
|
|
||||||
private static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 9;
|
private static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 9;
|
||||||
private static final int INJECT_MOUSE_EVENT_PAYLOAD_LENGTH = 17;
|
private static final int INJECT_MOUSE_EVENT_PAYLOAD_LENGTH = 17;
|
||||||
private static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 21;
|
|
||||||
private static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
private static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
||||||
private static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
private static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
||||||
|
|
||||||
@@ -63,9 +62,6 @@ public class ControlMessageReader {
|
|||||||
case ControlMessage.TYPE_INJECT_MOUSE_EVENT:
|
case ControlMessage.TYPE_INJECT_MOUSE_EVENT:
|
||||||
msg = parseInjectMouseEvent();
|
msg = parseInjectMouseEvent();
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
|
||||||
msg = parseInjectTouchEvent();
|
|
||||||
break;
|
|
||||||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||||
msg = parseInjectScrollEvent();
|
msg = parseInjectScrollEvent();
|
||||||
break;
|
break;
|
||||||
@@ -134,20 +130,6 @@ public class ControlMessageReader {
|
|||||||
return ControlMessage.createInjectMouseEvent(action, buttons, position);
|
return ControlMessage.createInjectMouseEvent(action, buttons, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ControlMessage parseInjectTouchEvent() {
|
|
||||||
if (buffer.remaining() < INJECT_TOUCH_EVENT_PAYLOAD_LENGTH) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int action = toUnsigned(buffer.get());
|
|
||||||
long fingerId = buffer.getLong();
|
|
||||||
Position position = readPosition(buffer);
|
|
||||||
// 16 bits fixed-point
|
|
||||||
int pressureInt = toUnsigned(buffer.getShort());
|
|
||||||
// convert it to a float between 0 and 1 (0x1p16f is 2^16 as float)
|
|
||||||
float pressure = pressureInt == 0xffff ? 1f : (pressureInt / 0x1p16f);
|
|
||||||
return ControlMessage.createInjectTouchEvent(action, fingerId, position, pressure);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ControlMessage parseInjectScrollEvent() {
|
private ControlMessage parseInjectScrollEvent() {
|
||||||
if (buffer.remaining() < INJECT_SCROLL_EVENT_PAYLOAD_LENGTH) {
|
if (buffer.remaining() < INJECT_SCROLL_EVENT_PAYLOAD_LENGTH) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ import java.io.IOException;
|
|||||||
|
|
||||||
public class Controller {
|
public class Controller {
|
||||||
|
|
||||||
private static final int MAX_FINGERS = 10;
|
|
||||||
|
|
||||||
private final Device device;
|
private final Device device;
|
||||||
private final DesktopConnection connection;
|
private final DesktopConnection connection;
|
||||||
private final DeviceMessageSender sender;
|
private final DeviceMessageSender sender;
|
||||||
@@ -22,55 +20,35 @@ public class Controller {
|
|||||||
private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
|
private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
|
||||||
|
|
||||||
private long lastMouseDown;
|
private long lastMouseDown;
|
||||||
private final MotionEvent.PointerProperties[] mousePointerProperties = {new MotionEvent.PointerProperties()};
|
private final MotionEvent.PointerProperties[] pointerProperties = {new MotionEvent.PointerProperties()};
|
||||||
private final MotionEvent.PointerCoords[] mousePointerCoords = {new MotionEvent.PointerCoords()};
|
private final MotionEvent.PointerCoords[] pointerCoords = {new MotionEvent.PointerCoords()};
|
||||||
|
|
||||||
private long lastTouchDown;
|
|
||||||
private final FingersState fingersState = new FingersState(MAX_FINGERS);
|
|
||||||
private final MotionEvent.PointerProperties[] touchPointerProperties = new MotionEvent.PointerProperties[MAX_FINGERS];
|
|
||||||
private final MotionEvent.PointerCoords[] touchPointerCoords = new MotionEvent.PointerCoords[MAX_FINGERS];
|
|
||||||
|
|
||||||
public Controller(Device device, DesktopConnection connection) {
|
public Controller(Device device, DesktopConnection connection) {
|
||||||
this.device = device;
|
this.device = device;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
initMousePointer();
|
initPointer();
|
||||||
initTouchPointers();
|
|
||||||
sender = new DeviceMessageSender(connection);
|
sender = new DeviceMessageSender(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initMousePointer() {
|
private void initPointer() {
|
||||||
MotionEvent.PointerProperties props = mousePointerProperties[0];
|
MotionEvent.PointerProperties props = pointerProperties[0];
|
||||||
props.id = 0;
|
props.id = 0;
|
||||||
props.toolType = MotionEvent.TOOL_TYPE_FINGER;
|
props.toolType = MotionEvent.TOOL_TYPE_FINGER;
|
||||||
|
|
||||||
MotionEvent.PointerCoords coords = mousePointerCoords[0];
|
MotionEvent.PointerCoords coords = pointerCoords[0];
|
||||||
coords.orientation = 0;
|
coords.orientation = 0;
|
||||||
coords.pressure = 1;
|
coords.pressure = 1;
|
||||||
coords.size = 1;
|
coords.size = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initTouchPointers() {
|
private void setPointerCoords(Point point) {
|
||||||
for (int i = 0; i < MAX_FINGERS; ++i) {
|
MotionEvent.PointerCoords coords = pointerCoords[0];
|
||||||
MotionEvent.PointerProperties props = new MotionEvent.PointerProperties();
|
|
||||||
props.toolType = MotionEvent.TOOL_TYPE_FINGER;
|
|
||||||
|
|
||||||
MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
|
|
||||||
coords.orientation = 0;
|
|
||||||
coords.size = 1;
|
|
||||||
|
|
||||||
touchPointerProperties[i] = props;
|
|
||||||
touchPointerCoords[i] = coords;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setMousePointerCoords(Point point) {
|
|
||||||
MotionEvent.PointerCoords coords = mousePointerCoords[0];
|
|
||||||
coords.x = point.getX();
|
coords.x = point.getX();
|
||||||
coords.y = point.getY();
|
coords.y = point.getY();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setScroll(int hScroll, int vScroll) {
|
private void setScroll(int hScroll, int vScroll) {
|
||||||
MotionEvent.PointerCoords coords = mousePointerCoords[0];
|
MotionEvent.PointerCoords coords = pointerCoords[0];
|
||||||
coords.setAxisValue(MotionEvent.AXIS_HSCROLL, hScroll);
|
coords.setAxisValue(MotionEvent.AXIS_HSCROLL, hScroll);
|
||||||
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
|
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
|
||||||
}
|
}
|
||||||
@@ -112,9 +90,6 @@ public class Controller {
|
|||||||
case ControlMessage.TYPE_INJECT_MOUSE_EVENT:
|
case ControlMessage.TYPE_INJECT_MOUSE_EVENT:
|
||||||
injectMouse(msg.getAction(), msg.getButtons(), msg.getPosition());
|
injectMouse(msg.getAction(), msg.getButtons(), msg.getPosition());
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
|
||||||
injectTouch(msg.getAction(), msg.getFingerId(), msg.getPosition(), msg.getPressure());
|
|
||||||
break;
|
|
||||||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||||
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
|
||||||
break;
|
break;
|
||||||
@@ -183,52 +158,8 @@ public class Controller {
|
|||||||
// ignore event
|
// ignore event
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setMousePointerCoords(point);
|
setPointerCoords(point);
|
||||||
MotionEvent event = MotionEvent.obtain(lastMouseDown, now, action, 1, mousePointerProperties, mousePointerCoords, 0, buttons, 1f, 1f, 0, 0,
|
MotionEvent event = MotionEvent.obtain(lastMouseDown, now, action, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, 0, 0,
|
||||||
InputDevice.SOURCE_TOUCHSCREEN, 0);
|
|
||||||
return injectEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean injectTouch(int action, long fingerId, Position position, float pressure) {
|
|
||||||
long now = SystemClock.uptimeMillis();
|
|
||||||
|
|
||||||
Point point = device.getPhysicalPoint(position);
|
|
||||||
if (point == null) {
|
|
||||||
// ignore event
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fingerIndex = fingersState.getFingerIndex(fingerId);
|
|
||||||
if (fingerIndex == -1) {
|
|
||||||
Ln.w("Too many fingers for touch event");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Finger finger = fingersState.get(fingerIndex);
|
|
||||||
finger.setPoint(point);
|
|
||||||
finger.setPressure(pressure);
|
|
||||||
finger.setUp(action == MotionEvent.ACTION_UP);
|
|
||||||
|
|
||||||
// FAIL: action_up will always remove the finger, and the event will not be written!
|
|
||||||
int pointerCount = fingersState.update(touchPointerProperties, touchPointerCoords);
|
|
||||||
fingersState.cleanUp();
|
|
||||||
|
|
||||||
Ln.d("pointerCount = " + pointerCount);
|
|
||||||
for (int i = 0; i < pointerCount; ++i) {
|
|
||||||
Ln.d("props = " + touchPointerProperties[i].id);
|
|
||||||
Ln.d("coords = " + touchPointerCoords[i].x + "," + touchPointerCoords[i].y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pointerCount > 1) {
|
|
||||||
if (action == MotionEvent.ACTION_UP) {
|
|
||||||
action = MotionEvent.ACTION_POINTER_UP | (fingerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
|
|
||||||
} else if (action == MotionEvent.ACTION_DOWN) {
|
|
||||||
action = MotionEvent.ACTION_POINTER_DOWN | (fingerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
|
|
||||||
}
|
|
||||||
} else if (action == MotionEvent.ACTION_DOWN) {
|
|
||||||
lastTouchDown = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, touchPointerProperties, touchPointerCoords, 0, 0, 1f, 1f, 0, 0,
|
|
||||||
InputDevice.SOURCE_TOUCHSCREEN, 0);
|
InputDevice.SOURCE_TOUCHSCREEN, 0);
|
||||||
return injectEvent(event);
|
return injectEvent(event);
|
||||||
}
|
}
|
||||||
@@ -240,9 +171,9 @@ public class Controller {
|
|||||||
// ignore event
|
// ignore event
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setMousePointerCoords(point);
|
setPointerCoords(point);
|
||||||
setScroll(hScroll, vScroll);
|
setScroll(hScroll, vScroll);
|
||||||
MotionEvent event = MotionEvent.obtain(lastMouseDown, now, MotionEvent.ACTION_SCROLL, 1, mousePointerProperties, mousePointerCoords, 0, 0, 1f, 1f, 0,
|
MotionEvent event = MotionEvent.obtain(lastMouseDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, 0,
|
||||||
0, InputDevice.SOURCE_MOUSE, 0);
|
0, InputDevice.SOURCE_MOUSE, 0);
|
||||||
return injectEvent(event);
|
return injectEvent(event);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
package com.genymobile.scrcpy;
|
|
||||||
|
|
||||||
public class Finger {
|
|
||||||
|
|
||||||
private final long id;
|
|
||||||
private final int localId;
|
|
||||||
private Point point;
|
|
||||||
private float pressure;
|
|
||||||
private boolean up;
|
|
||||||
|
|
||||||
public Finger(long id, int localId) {
|
|
||||||
this.id = id;
|
|
||||||
this.localId = localId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLocalId() {
|
|
||||||
return localId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Point getPoint() {
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPoint(Point point) {
|
|
||||||
this.point = point;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getPressure() {
|
|
||||||
return pressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPressure(float pressure) {
|
|
||||||
this.pressure = pressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUp() {
|
|
||||||
return up;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUp(boolean up) {
|
|
||||||
this.up = up;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
package com.genymobile.scrcpy;
|
|
||||||
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class FingersState {
|
|
||||||
|
|
||||||
private final List<Finger> fingers = new ArrayList<>();
|
|
||||||
private int maxFingers;
|
|
||||||
|
|
||||||
public FingersState(int maxFingers) {
|
|
||||||
this.maxFingers = maxFingers;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int indexOf(long id) {
|
|
||||||
for (int i = 0; i < fingers.size(); ++i) {
|
|
||||||
Finger finger = fingers.get(i);
|
|
||||||
if (finger.getId() == id) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isLocalIdAvailable(int localId) {
|
|
||||||
for (int i = 0; i < fingers.size(); ++i) {
|
|
||||||
Finger finger = fingers.get(i);
|
|
||||||
if (finger.getLocalId() == localId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int nextUnusedLocalId() {
|
|
||||||
for (int localId = 0; localId < maxFingers; ++localId) {
|
|
||||||
if (isLocalIdAvailable(localId)) {
|
|
||||||
return localId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Finger get(int index) {
|
|
||||||
return fingers.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFingerIndex(long id) {
|
|
||||||
int index = indexOf(id);
|
|
||||||
if (index != -1) {
|
|
||||||
// already exists, return it
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
if (fingers.size() >= maxFingers) {
|
|
||||||
// it's full
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
// id 0 is reserved for mouse events
|
|
||||||
int localId = nextUnusedLocalId();
|
|
||||||
if (localId == -1) {
|
|
||||||
throw new AssertionError("fingers.size() < maxFingers implies that a local id is available");
|
|
||||||
}
|
|
||||||
Finger finger = new Finger(id, localId);
|
|
||||||
fingers.add(finger);
|
|
||||||
// return the index of the finger
|
|
||||||
return fingers.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the motion event parameters.
|
|
||||||
*
|
|
||||||
* @param props the pointer properties
|
|
||||||
* @param coords the pointer coordinates
|
|
||||||
* @return The number of items initialized (the number of fingers).
|
|
||||||
*/
|
|
||||||
public int update(MotionEvent.PointerProperties[] props, MotionEvent.PointerCoords[] coords) {
|
|
||||||
for (int i = 0; i < fingers.size(); ++i) {
|
|
||||||
Finger finger = fingers.get(i);
|
|
||||||
|
|
||||||
// id 0 is reserved for mouse events
|
|
||||||
props[i].id = finger.getLocalId();
|
|
||||||
|
|
||||||
Point point = finger.getPoint();
|
|
||||||
coords[i].x = point.getX();
|
|
||||||
coords[i].y = point.getY();
|
|
||||||
coords[i].pressure = finger.getPressure();
|
|
||||||
}
|
|
||||||
return fingers.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cleanUp() {
|
|
||||||
for (int i = fingers.size() - 1; i >= 0; --i) {
|
|
||||||
Finger finger = fingers.get(i);
|
|
||||||
if (finger.isUp()) {
|
|
||||||
fingers.remove(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,7 @@ public final class WindowManager {
|
|||||||
try {
|
try {
|
||||||
Class<?> cls = manager.getClass();
|
Class<?> cls = manager.getClass();
|
||||||
try {
|
try {
|
||||||
return (Integer) cls.getMethod("getRotation").invoke(manager);
|
return (Integer) manager.getClass().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
|
||||||
|
|||||||
@@ -77,63 +77,24 @@ public class ControlMessageReaderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("checkstyle:MagicNumber")
|
|
||||||
public void testParseMouseEvent() throws IOException {
|
public void testParseMouseEvent() throws IOException {
|
||||||
ControlMessageReader reader = new ControlMessageReader();
|
ControlMessageReader reader = new ControlMessageReader();
|
||||||
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
DataOutputStream dos = new DataOutputStream(bos);
|
DataOutputStream dos = new DataOutputStream(bos);
|
||||||
dos.writeByte(ControlMessage.TYPE_INJECT_MOUSE_EVENT);
|
dos.writeByte(ControlMessage.TYPE_INJECT_KEYCODE);
|
||||||
dos.writeByte(MotionEvent.ACTION_DOWN);
|
dos.writeByte(MotionEvent.ACTION_DOWN);
|
||||||
dos.writeInt(MotionEvent.BUTTON_PRIMARY);
|
dos.writeInt(MotionEvent.BUTTON_PRIMARY);
|
||||||
dos.writeInt(100);
|
dos.writeInt(KeyEvent.META_CTRL_ON);
|
||||||
dos.writeInt(200);
|
|
||||||
dos.writeShort(1080);
|
|
||||||
dos.writeShort(1920);
|
|
||||||
|
|
||||||
byte[] packet = bos.toByteArray();
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
reader.readFrom(new ByteArrayInputStream(packet));
|
reader.readFrom(new ByteArrayInputStream(packet));
|
||||||
ControlMessage event = reader.next();
|
ControlMessage event = reader.next();
|
||||||
|
|
||||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_MOUSE_EVENT, event.getType());
|
Assert.assertEquals(ControlMessage.TYPE_INJECT_KEYCODE, event.getType());
|
||||||
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
|
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
|
||||||
Assert.assertEquals(MotionEvent.BUTTON_PRIMARY, event.getButtons());
|
Assert.assertEquals(MotionEvent.BUTTON_PRIMARY, event.getKeycode());
|
||||||
Assert.assertEquals(100, event.getPosition().getPoint().getX());
|
Assert.assertEquals(KeyEvent.META_CTRL_ON, event.getMetaState());
|
||||||
Assert.assertEquals(200, event.getPosition().getPoint().getY());
|
|
||||||
Assert.assertEquals(1080, event.getPosition().getScreenSize().getWidth());
|
|
||||||
Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("checkstyle:MagicNumber")
|
|
||||||
public void testParseTouchEvent() throws IOException {
|
|
||||||
ControlMessageReader reader = new ControlMessageReader();
|
|
||||||
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
||||||
DataOutputStream dos = new DataOutputStream(bos);
|
|
||||||
dos.writeByte(ControlMessage.TYPE_INJECT_TOUCH_EVENT);
|
|
||||||
dos.writeByte(MotionEvent.ACTION_DOWN);
|
|
||||||
dos.writeLong(-42); // fingerId
|
|
||||||
dos.writeInt(100);
|
|
||||||
dos.writeInt(200);
|
|
||||||
dos.writeShort(1080);
|
|
||||||
dos.writeShort(1920);
|
|
||||||
dos.writeShort(0xffff); // pressure
|
|
||||||
|
|
||||||
byte[] packet = bos.toByteArray();
|
|
||||||
|
|
||||||
reader.readFrom(new ByteArrayInputStream(packet));
|
|
||||||
ControlMessage event = reader.next();
|
|
||||||
|
|
||||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_TOUCH_EVENT, event.getType());
|
|
||||||
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
|
|
||||||
Assert.assertEquals(-42, event.getFingerId());
|
|
||||||
Assert.assertEquals(100, event.getPosition().getPoint().getX());
|
|
||||||
Assert.assertEquals(200, event.getPosition().getPoint().getY());
|
|
||||||
Assert.assertEquals(1080, event.getPosition().getScreenSize().getWidth());
|
|
||||||
Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight());
|
|
||||||
Assert.assertEquals(1f, event.getPressure(), 0f); // must be exact
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user