Compare commits

..

4 Commits

Author SHA1 Message Date
5a2b929aac hack_virtual_display 2024-10-06 19:14:21 +02:00
9434718970 dpi 2024-10-06 19:05:36 +02:00
6ddcc98663 vdevents 2024-10-06 18:39:15 +02:00
19178e0df9 move to screencapture 2024-10-06 18:31:45 +02:00
226 changed files with 1951 additions and 6187 deletions

View File

@ -1,514 +0,0 @@
name: Build
on:
workflow_dispatch:
inputs:
name:
description: 'Version name (default is ref name)'
env:
# $VERSION is used by release scripts
VERSION: ${{ github.event.inputs.name || github.ref_name }}
jobs:
test-scrcpy-server:
runs-on: ubuntu-latest
env:
GRADLE: gradle # use native gradle instead of ./gradlew in scripts
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup JDK
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
- name: Test scrcpy-server
run: release/test_server.sh
build-scrcpy-server:
runs-on: ubuntu-latest
env:
GRADLE: gradle # use native gradle instead of ./gradlew in scripts
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup JDK
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
- name: Build
run: release/build_server.sh
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: scrcpy-server
path: release/work/build-server/server/scrcpy-server
test-build-scrcpy-server-without-gradle:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup JDK
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
- name: Build without gradle
run: server/build_without_gradle.sh
test-client:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \
libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \
libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \
libv4l-dev
- name: Test
run: release/test_client.sh
build-linux-x86_64:
runs-on: ubuntu-20.04
steps:
- name: Check architecture
run: |
arch=$(uname -m)
if [[ "$arch" != x86_64 ]]
then
echo "Unexpected architecture: $arch" >&2
exit 1
fi
- name: Checkout code
uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \
libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \
libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \
libv4l-dev
- name: Build
run: release/build_linux.sh x86_64
# upload-artifact does not preserve permissions
- name: Tar
run: |
cd release/work/build-linux-x86_64
mkdir dist-tar
cd dist-tar
tar -C .. -cvf dist.tar.gz dist/
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: build-linux-x86_64-intermediate
path: release/work/build-linux-x86_64/dist-tar/
build-win32:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \
libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \
libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \
mingw-w64 mingw-w64-tools libz-mingw-w64-dev
- name: Build
run: release/build_windows.sh 32
# upload-artifact does not preserve permissions
- name: Tar
run: |
cd release/work/build-win32
mkdir dist-tar
cd dist-tar
tar -C .. -cvf dist.tar.gz dist/
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: build-win32-intermediate
path: release/work/build-win32/dist-tar/
build-win64:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \
libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \
libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \
mingw-w64 mingw-w64-tools libz-mingw-w64-dev
- name: Build
run: release/build_windows.sh 64
# upload-artifact does not preserve permissions
- name: Tar
run: |
cd release/work/build-win64
mkdir dist-tar
cd dist-tar
tar -C .. -cvf dist.tar.gz dist/
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: build-win64-intermediate
path: release/work/build-win64/dist-tar/
build-macos-aarch64:
runs-on: macos-latest
steps:
- name: Check architecture
run: |
arch=$(uname -m)
if [[ "$arch" != arm64 ]]
then
echo "Unexpected architecture: $arch" >&2
exit 1
fi
- name: Checkout code
uses: actions/checkout@v4
- name: Install dependencies
run: |
brew install meson ninja nasm libiconv zlib automake autoconf \
libtool
- name: Build
env:
# the default Xcode (and macOS SDK) version can be found at
# <https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md#xcode>
#
# then the minimal supported deployment target of that macOS SDK can be found at
# <https://developer.apple.com/support/xcode/#minimum-requirements>
MACOSX_DEPLOYMENT_TARGET: 10.13
run: release/build_macos.sh aarch64
# upload-artifact does not preserve permissions
- name: Tar
run: |
cd release/work/build-macos-aarch64
mkdir dist-tar
cd dist-tar
tar -C .. -cvf dist.tar.gz dist/
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: build-macos-aarch64-intermediate
path: release/work/build-macos-aarch64/dist-tar/
build-macos-x86_64:
runs-on: macos-13
steps:
- name: Check architecture
run: |
arch=$(uname -m)
if [[ "$arch" != x86_64 ]]
then
echo "Unexpected architecture: $arch" >&2
exit 1
fi
- name: Checkout code
uses: actions/checkout@v4
- name: Install dependencies
run: brew install meson ninja nasm libiconv zlib automake
# autoconf and libtool are already installed on macos-13
- name: Build
env:
# the default Xcode (and macOS SDK) version can be found at
# <https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode>
#
# then the minimal supported deployment target of that macOS SDK can be found at
# <https://developer.apple.com/support/xcode/#minimum-requirements>
MACOSX_DEPLOYMENT_TARGET: 10.13
run: release/build_macos.sh x86_64
# upload-artifact does not preserve permissions
- name: Tar
run: |
cd release/work/build-macos-x86_64
mkdir dist-tar
cd dist-tar
tar -C .. -cvf dist.tar.gz dist/
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: build-macos-x86_64-intermediate
path: release/work/build-macos-x86_64/dist-tar/
package-linux-x86_64:
needs:
- build-scrcpy-server
- build-linux-x86_64
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download scrcpy-server
uses: actions/download-artifact@v4
with:
name: scrcpy-server
path: release/work/build-server/server/
- name: Download build-linux-x86_64
uses: actions/download-artifact@v4
with:
name: build-linux-x86_64-intermediate
path: release/work/build-linux-x86_64/dist-tar/
# upload-artifact does not preserve permissions
- name: Detar
run: |
cd release/work/build-linux-x86_64
tar xf dist-tar/dist.tar.gz
- name: Package
run: release/package_client.sh linux-x86_64 tar.gz
- name: Upload release
uses: actions/upload-artifact@v4
with:
name: release-linux-x86_64
path: release/output/
package-win32:
needs:
- build-scrcpy-server
- build-win32
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download scrcpy-server
uses: actions/download-artifact@v4
with:
name: scrcpy-server
path: release/work/build-server/server/
- name: Download build-win32
uses: actions/download-artifact@v4
with:
name: build-win32-intermediate
path: release/work/build-win32/dist-tar/
# upload-artifact does not preserve permissions
- name: Detar
run: |
cd release/work/build-win32
tar xf dist-tar/dist.tar.gz
- name: Package
run: release/package_client.sh win32 zip
- name: Upload release
uses: actions/upload-artifact@v4
with:
name: release-win32
path: release/output/
package-win64:
needs:
- build-scrcpy-server
- build-win64
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download scrcpy-server
uses: actions/download-artifact@v4
with:
name: scrcpy-server
path: release/work/build-server/server/
- name: Download build-win64
uses: actions/download-artifact@v4
with:
name: build-win64-intermediate
path: release/work/build-win64/dist-tar/
# upload-artifact does not preserve permissions
- name: Detar
run: |
cd release/work/build-win64
tar xf dist-tar/dist.tar.gz
- name: Package
run: release/package_client.sh win64 zip
- name: Upload release
uses: actions/upload-artifact@v4
with:
name: release-win64
path: release/output
package-macos-aarch64:
needs:
- build-scrcpy-server
- build-macos-aarch64
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download scrcpy-server
uses: actions/download-artifact@v4
with:
name: scrcpy-server
path: release/work/build-server/server/
- name: Download build-macos-aarch64
uses: actions/download-artifact@v4
with:
name: build-macos-aarch64-intermediate
path: release/work/build-macos-aarch64/dist-tar/
# upload-artifact does not preserve permissions
- name: Detar
run: |
cd release/work/build-macos-aarch64
tar xf dist-tar/dist.tar.gz
- name: Package
run: release/package_client.sh macos-aarch64 tar.gz
- name: Upload release
uses: actions/upload-artifact@v4
with:
name: release-macos-aarch64
path: release/output/
package-macos-x86_64:
needs:
- build-scrcpy-server
- build-macos-x86_64
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download scrcpy-server
uses: actions/download-artifact@v4
with:
name: scrcpy-server
path: release/work/build-server/server/
- name: Download build-macos
uses: actions/download-artifact@v4
with:
name: build-macos-x86_64-intermediate
path: release/work/build-macos-x86_64/dist-tar/
# upload-artifact does not preserve permissions
- name: Detar
run: |
cd release/work/build-macos-x86_64
tar xf dist-tar/dist.tar.gz
- name: Package
run: release/package_client.sh macos-x86_64 tar.gz
- name: Upload release
uses: actions/upload-artifact@v4
with:
name: release-macos-x86_64
path: release/output/
release:
needs:
- build-scrcpy-server
- package-linux-x86_64
- package-win32
- package-win64
- package-macos-aarch64
- package-macos-x86_64
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download scrcpy-server
uses: actions/download-artifact@v4
with:
name: scrcpy-server
path: release/work/build-server/server/
- name: Download release-linux-x86_64
uses: actions/download-artifact@v4
with:
name: release-linux-x86_64
path: release/output/
- name: Download release-win32
uses: actions/download-artifact@v4
with:
name: release-win32
path: release/output/
- name: Download release-win64
uses: actions/download-artifact@v4
with:
name: release-win64
path: release/output/
- name: Download release-macos-aarch64
uses: actions/download-artifact@v4
with:
name: release-macos-aarch64
path: release/output/
- name: Download release-macos-x86_64
uses: actions/download-artifact@v4
with:
name: release-macos-x86_64
path: release/output/
- name: Package server
run: release/package_server.sh
- name: Generate checksums
run: release/generate_checksums.sh
- name: Upload release artifact
uses: actions/upload-artifact@v4
with:
name: scrcpy-release-${{ env.VERSION }}
path: release/output

View File

@ -2,7 +2,7 @@
source for the project. Do not download releases from random websites, even if source for the project. Do not download releases from random websites, even if
their name contains `scrcpy`.** their name contains `scrcpy`.**
# scrcpy (v3.1) # scrcpy (v2.7)
<img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" /> <img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />
@ -31,7 +31,6 @@ It focuses on:
Its features include: Its features include:
- [audio forwarding](doc/audio.md) (Android 11+) - [audio forwarding](doc/audio.md) (Android 11+)
- [recording](doc/recording.md) - [recording](doc/recording.md)
- [virtual display](doc/virtual_display.md)
- mirroring with [Android device screen off](doc/device.md#turn-screen-off) - mirroring with [Android device screen off](doc/device.md#turn-screen-off)
- [copy-paste](doc/control.md#copy-paste) in both directions - [copy-paste](doc/control.md#copy-paste) in both directions
- [configurable quality](doc/video.md) - [configurable quality](doc/video.md)
@ -74,7 +73,7 @@ Note that USB debugging is not required to run scrcpy in [OTG mode](doc/otg.md).
## Get the app ## Get the app
- [Linux](doc/linux.md) - [Linux](doc/linux.md)
- [Windows](doc/windows.md) (read [how to run](doc/windows.md#run)) - [Windows](doc/windows.md)
- [macOS](doc/macos.md) - [macOS](doc/macos.md)
@ -92,12 +91,6 @@ Here are just some common examples.
scrcpy --video-codec=h265 -m1920 --max-fps=60 --no-audio -K # short version scrcpy --video-codec=h265 -m1920 --max-fps=60 --no-audio -K # short version
``` ```
- Start VLC in a new virtual display (separate from the device display):
```bash
scrcpy --new-display=1920x1080 --start-app=org.videolan.vlc
```
- Record the device camera in H.265 at 1920x1080 (and microphone) to an MP4 - Record the device camera in H.265 at 1920x1080 (and microphone) to an MP4
file: file:
@ -141,7 +134,6 @@ documented in the following pages:
- [Device](doc/device.md) - [Device](doc/device.md)
- [Window](doc/window.md) - [Window](doc/window.md)
- [Recording](doc/recording.md) - [Recording](doc/recording.md)
- [Virtual display](doc/virtual_display.md)
- [Tunnels](doc/tunnels.md) - [Tunnels](doc/tunnels.md)
- [OTG](doc/otg.md) - [OTG](doc/otg.md)
- [Camera](doc/camera.md) - [Camera](doc/camera.md)
@ -181,7 +173,6 @@ to your problem immediately.
You can also use: You can also use:
- Reddit: [`r/scrcpy`](https://www.reddit.com/r/scrcpy) - Reddit: [`r/scrcpy`](https://www.reddit.com/r/scrcpy)
- BlueSky: [`@scrcpy.bsky.social`](https://bsky.app/profile/scrcpy.bsky.social)
- Twitter: [`@scrcpy_app`](https://twitter.com/scrcpy_app) - Twitter: [`@scrcpy_app`](https://twitter.com/scrcpy_app)

View File

@ -2,7 +2,6 @@ _scrcpy() {
local cur prev words cword local cur prev words cword
local opts=" local opts="
--always-on-top --always-on-top
--angle
--audio-bit-rate= --audio-bit-rate=
--audio-buffer= --audio-buffer=
--audio-codec= --audio-codec=
@ -18,10 +17,10 @@ _scrcpy() {
--camera-fps= --camera-fps=
--camera-high-speed --camera-high-speed
--camera-size= --camera-size=
--capture-orientation=
--crop= --crop=
-d --select-usb -d --select-usb
--disable-screensaver --disable-screensaver
--display-buffer=
--display-id= --display-id=
--display-orientation= --display-orientation=
-e --select-tcpip -e --select-tcpip
@ -34,11 +33,12 @@ _scrcpy() {
--keyboard= --keyboard=
--kill-adb-on-close --kill-adb-on-close
--legacy-paste --legacy-paste
--list-apps
--list-camera-sizes --list-camera-sizes
--list-cameras --list-cameras
--list-displays --list-displays
--list-encoders --list-encoders
--lock-video-orientation
--lock-video-orientation=
-m --max-size= -m --max-size=
-M -M
--max-fps= --max-fps=
@ -46,8 +46,6 @@ _scrcpy() {
--mouse-bind= --mouse-bind=
-n --no-control -n --no-control
-N --no-playback -N --no-playback
--new-display
--new-display=
--no-audio --no-audio
--no-audio-playback --no-audio-playback
--no-cleanup --no-cleanup
@ -57,8 +55,6 @@ _scrcpy() {
--no-mipmaps --no-mipmaps
--no-mouse-hover --no-mouse-hover
--no-power-on --no-power-on
--no-vd-destroy-content
--no-vd-system-decorations
--no-video --no-video
--no-video-playback --no-video-playback
--orientation= --orientation=
@ -79,9 +75,7 @@ _scrcpy() {
--rotation= --rotation=
-s --serial= -s --serial=
-S --turn-screen-off -S --turn-screen-off
--screen-off-timeout=
--shortcut-mod= --shortcut-mod=
--start-app=
-t --show-touches -t --show-touches
--tcpip --tcpip
--tcpip= --tcpip=
@ -92,7 +86,6 @@ _scrcpy() {
--v4l2-sink= --v4l2-sink=
-v --version -v --version
-V --verbosity= -V --verbosity=
--video-buffer=
--video-codec= --video-codec=
--video-codec-options= --video-codec-options=
--video-encoder= --video-encoder=
@ -140,10 +133,6 @@ _scrcpy() {
COMPREPLY=($(compgen -W 'disabled uhid aoa' -- "$cur")) COMPREPLY=($(compgen -W 'disabled uhid aoa' -- "$cur"))
return return
;; ;;
--capture-orientation)
COMPREPLY=($(compgen -W '0 90 180 270 flip0 flip90 flip180 flip270 @0 @90 @180 @270 @flip0 @flip90 @flip180 @flip270' -- "$cur"))
return
;;
--orientation|--display-orientation) --orientation|--display-orientation)
COMPREPLY=($(compgen -W '0 90 180 270 flip0 flip90 flip180 flip270' -- "$cur")) COMPREPLY=($(compgen -W '0 90 180 270 flip0 flip90 flip180 flip270' -- "$cur"))
return return
@ -152,6 +141,10 @@ _scrcpy() {
COMPREPLY=($(compgen -W '0 90 180 270' -- "$cur")) COMPREPLY=($(compgen -W '0 90 180 270' -- "$cur"))
return return
;; ;;
--lock-video-orientation)
COMPREPLY=($(compgen -W 'unlocked initial 0 90 180 270' -- "$cur"))
return
;;
--pause-on-exit) --pause-on-exit)
COMPREPLY=($(compgen -W 'true false if-error' -- "$cur")) COMPREPLY=($(compgen -W 'true false if-error' -- "$cur"))
return return
@ -194,9 +187,9 @@ _scrcpy() {
|--camera-size \ |--camera-size \
|--crop \ |--crop \
|--display-id \ |--display-id \
|--display-buffer \
|--max-fps \ |--max-fps \
|-m|--max-size \ |-m|--max-size \
|--new-display \
|-p|--port \ |-p|--port \
|--push-target \ |--push-target \
|--rotation \ |--rotation \
@ -204,7 +197,6 @@ _scrcpy() {
|--tunnel-port \ |--tunnel-port \
|--v4l2-buffer \ |--v4l2-buffer \
|--v4l2-sink \ |--v4l2-sink \
|--video-buffer \
|--video-codec-options \ |--video-codec-options \
|--video-encoder \ |--video-encoder \
|--tcpip \ |--tcpip \

View File

@ -9,7 +9,6 @@ local arguments
arguments=( arguments=(
'--always-on-top[Make scrcpy window always on top \(above other windows\)]' '--always-on-top[Make scrcpy window always on top \(above other windows\)]'
'--angle=[Rotate the video content by a custom angle, in degrees]'
'--audio-bit-rate=[Encode the audio at the given bit-rate]' '--audio-bit-rate=[Encode the audio at the given bit-rate]'
'--audio-buffer=[Configure the audio buffering delay (in milliseconds)]' '--audio-buffer=[Configure the audio buffering delay (in milliseconds)]'
'--audio-codec=[Select the audio codec]:codec:(opus aac flac raw)' '--audio-codec=[Select the audio codec]:codec:(opus aac flac raw)'
@ -25,10 +24,10 @@ arguments=(
'--camera-facing=[Select the device camera by its facing direction]:facing:(front back external)' '--camera-facing=[Select the device camera by its facing direction]:facing:(front back external)'
'--camera-fps=[Specify the camera capture frame rate]' '--camera-fps=[Specify the camera capture frame rate]'
'--camera-size=[Specify an explicit camera capture size]' '--camera-size=[Specify an explicit camera capture size]'
'--capture-orientation=[Set the capture video orientation]:orientation:(0 90 180 270 flip0 flip90 flip180 flip270 @0 @90 @180 @270 @flip0 @flip90 @flip180 @flip270)'
'--crop=[\[width\:height\:x\:y\] Crop the device screen on the server]' '--crop=[\[width\:height\:x\:y\] Crop the device screen on the server]'
{-d,--select-usb}'[Use USB device]' {-d,--select-usb}'[Use USB device]'
'--disable-screensaver[Disable screensaver while scrcpy is running]' '--disable-screensaver[Disable screensaver while scrcpy is running]'
'--display-buffer=[Add a buffering delay \(in milliseconds\) before displaying]'
'--display-id=[Specify the display id to mirror]' '--display-id=[Specify the display id to mirror]'
'--display-orientation=[Set the initial display orientation]:orientation values:(0 90 180 270 flip0 flip90 flip180 flip270)' '--display-orientation=[Set the initial display orientation]:orientation values:(0 90 180 270 flip0 flip90 flip180 flip270)'
{-e,--select-tcpip}'[Use TCP/IP device]' {-e,--select-tcpip}'[Use TCP/IP device]'
@ -41,11 +40,11 @@ arguments=(
'--keyboard=[Set the keyboard input mode]:mode:(disabled sdk uhid aoa)' '--keyboard=[Set the keyboard input mode]:mode:(disabled sdk uhid aoa)'
'--kill-adb-on-close[Kill adb when scrcpy terminates]' '--kill-adb-on-close[Kill adb when scrcpy terminates]'
'--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]' '--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]'
'--list-apps[List Android apps installed on the device]'
'--list-camera-sizes[List the valid camera capture sizes]' '--list-camera-sizes[List the valid camera capture sizes]'
'--list-cameras[List cameras available on the device]' '--list-cameras[List cameras available on the device]'
'--list-displays[List displays available on the device]' '--list-displays[List displays available on the device]'
'--list-encoders[List video and audio encoders available on the device]' '--list-encoders[List video and audio encoders available on the device]'
'--lock-video-orientation=[Lock video orientation]:orientation:(unlocked initial 0 90 180 270)'
{-m,--max-size=}'[Limit both the width and height of the video to value]' {-m,--max-size=}'[Limit both the width and height of the video to value]'
'-M[Use UHID/AOA mouse (same as --mouse=uhid or --mouse=aoa, depending on OTG mode)]' '-M[Use UHID/AOA mouse (same as --mouse=uhid or --mouse=aoa, depending on OTG mode)]'
'--max-fps=[Limit the frame rate of screen capture]' '--max-fps=[Limit the frame rate of screen capture]'
@ -53,7 +52,6 @@ arguments=(
'--mouse-bind=[Configure bindings of secondary clicks]' '--mouse-bind=[Configure bindings of secondary clicks]'
{-n,--no-control}'[Disable device control \(mirror the device in read only\)]' {-n,--no-control}'[Disable device control \(mirror the device in read only\)]'
{-N,--no-playback}'[Disable video and audio playback]' {-N,--no-playback}'[Disable video and audio playback]'
'--new-display=[Create a new display]'
'--no-audio[Disable audio forwarding]' '--no-audio[Disable audio forwarding]'
'--no-audio-playback[Disable audio playback]' '--no-audio-playback[Disable audio playback]'
'--no-cleanup[Disable device cleanup actions on exit]' '--no-cleanup[Disable device cleanup actions on exit]'
@ -63,8 +61,6 @@ arguments=(
'--no-mipmaps[Disable the generation of mipmaps]' '--no-mipmaps[Disable the generation of mipmaps]'
'--no-mouse-hover[Do not forward mouse hover events]' '--no-mouse-hover[Do not forward mouse hover events]'
'--no-power-on[Do not power on the device on start]' '--no-power-on[Do not power on the device on start]'
'--no-vd-destroy-content[Disable virtual display "destroy content on removal" flag]'
'--no-vd-system-decorations[Disable virtual display system decorations flag]'
'--no-video[Disable video forwarding]' '--no-video[Disable video forwarding]'
'--no-video-playback[Disable video playback]' '--no-video-playback[Disable video playback]'
'--orientation=[Set the video orientation]:orientation values:(0 90 180 270 flip0 flip90 flip180 flip270)' '--orientation=[Set the video orientation]:orientation values:(0 90 180 270 flip0 flip90 flip180 flip270)'
@ -83,9 +79,7 @@ arguments=(
'--require-audio=[Make scrcpy fail if audio is enabled but does not work]' '--require-audio=[Make scrcpy fail if audio is enabled but does not work]'
{-s,--serial=}'[The device serial number \(mandatory for multiple devices only\)]:serial:($("${ADB-adb}" devices | awk '\''$2 == "device" {print $1}'\''))' {-s,--serial=}'[The device serial number \(mandatory for multiple devices only\)]:serial:($("${ADB-adb}" devices | awk '\''$2 == "device" {print $1}'\''))'
{-S,--turn-screen-off}'[Turn the device screen off immediately]' {-S,--turn-screen-off}'[Turn the device screen off immediately]'
'--screen-off-timeout=[Set the screen off timeout in seconds]'
'--shortcut-mod=[\[key1,key2+key3,...\] Specify the modifiers to use for scrcpy shortcuts]:shortcut mod:(lctrl rctrl lalt ralt lsuper rsuper)' '--shortcut-mod=[\[key1,key2+key3,...\] Specify the modifiers to use for scrcpy shortcuts]:shortcut mod:(lctrl rctrl lalt ralt lsuper rsuper)'
'--start-app=[Start an Android app]'
{-t,--show-touches}'[Show physical touches]' {-t,--show-touches}'[Show physical touches]'
'--tcpip[\(optional \[ip\:port\]\) Configure and connect the device over TCP/IP]' '--tcpip[\(optional \[ip\:port\]\) Configure and connect the device over TCP/IP]'
'--time-limit=[Set the maximum mirroring time, in seconds]' '--time-limit=[Set the maximum mirroring time, in seconds]'
@ -95,7 +89,6 @@ arguments=(
'--v4l2-sink=[\[\/dev\/videoN\] Output to v4l2loopback device]' '--v4l2-sink=[\[\/dev\/videoN\] Output to v4l2loopback device]'
{-v,--version}'[Print the version of scrcpy]' {-v,--version}'[Print the version of scrcpy]'
{-V,--verbosity=}'[Set the log level]:verbosity:(verbose debug info warn error)' {-V,--verbosity=}'[Set the log level]:verbosity:(verbose debug info warn error)'
'--video-buffer=[Add a buffering delay \(in milliseconds\) before displaying video frames]'
'--video-codec=[Select the video codec]:codec:(h264 h265 av1)' '--video-codec=[Select the video codec]:codec:(h264 h265 av1)'
'--video-codec-options=[Set a list of comma-separated key\:type=value options for the device video encoder]' '--video-codec-options=[Set a list of comma-separated key\:type=value options for the device video encoder]'
'--video-encoder=[Use a specific MediaCodec video encoder]' '--video-encoder=[Use a specific MediaCodec video encoder]'

View File

@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR" cd "$DEPS_DIR"
. common . common
VERSION=35.0.2 VERSION=35.0.0
FILENAME=platform-tools_r$VERSION-win.zip FILENAME=platform-tools_r$VERSION-windows.zip
PROJECT_DIR=platform-tools-$VERSION-windows PROJECT_DIR=platform-tools-$VERSION
SHA256SUM=2975a3eac0b19182748d64195375ad056986561d994fffbdc64332a516300bb9 SHA256SUM=7ab78a8f8b305ae4d0de647d99c43599744de61a0838d3a47bda0cdffefee87e
cd "$SOURCES_DIR" cd "$SOURCES_DIR"
@ -27,6 +27,6 @@ else
rmdir "$ZIP_PREFIX" rmdir "$ZIP_PREFIX"
fi fi
mkdir -p "$INSTALL_DIR/adb-windows" mkdir -p "$INSTALL_DIR/$HOST/bin"
cd "$INSTALL_DIR/adb-windows" cd "$INSTALL_DIR/$HOST/bin"
cp -r "$SOURCES_DIR/$PROJECT_DIR"/. "$INSTALL_DIR/adb-windows/" cp -r "$SOURCES_DIR/$PROJECT_DIR"/. "$INSTALL_DIR/$HOST/bin/"

View File

@ -1,29 +0,0 @@
#!/usr/bin/env bash
set -ex
DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR"
. common
VERSION=35.0.2
FILENAME=platform-tools_r$VERSION-linux.zip
PROJECT_DIR=platform-tools-$VERSION-linux
SHA256SUM=acfdcccb123a8718c46c46c059b2f621140194e5ec1ac9d81715be3d6ab6cd0a
cd "$SOURCES_DIR"
if [[ -d "$PROJECT_DIR" ]]
then
echo "$PWD/$PROJECT_DIR" found
else
get_file "https://dl.google.com/android/repository/$FILENAME" "$FILENAME" "$SHA256SUM"
mkdir -p "$PROJECT_DIR"
cd "$PROJECT_DIR"
ZIP_PREFIX=platform-tools
unzip "../$FILENAME" "$ZIP_PREFIX"/adb
mv "$ZIP_PREFIX"/* .
rmdir "$ZIP_PREFIX"
fi
mkdir -p "$INSTALL_DIR/adb-linux"
cd "$INSTALL_DIR/adb-linux"
cp -r "$SOURCES_DIR/$PROJECT_DIR"/. "$INSTALL_DIR/adb-linux/"

View File

@ -1,29 +0,0 @@
#!/usr/bin/env bash
set -ex
DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR"
. common
VERSION=35.0.2
FILENAME=platform-tools_r$VERSION-darwin.zip
PROJECT_DIR=platform-tools-$VERSION-darwin
SHA256SUM=1820078db90bf21628d257ff052528af1c61bb48f754b3555648f5652fa35d78
cd "$SOURCES_DIR"
if [[ -d "$PROJECT_DIR" ]]
then
echo "$PWD/$PROJECT_DIR" found
else
get_file "https://dl.google.com/android/repository/$FILENAME" "$FILENAME" "$SHA256SUM"
mkdir -p "$PROJECT_DIR"
cd "$PROJECT_DIR"
ZIP_PREFIX=platform-tools
unzip "../$FILENAME" "$ZIP_PREFIX"/adb
mv "$ZIP_PREFIX"/* .
rmdir "$ZIP_PREFIX"
fi
mkdir -p "$INSTALL_DIR/adb-macos"
cd "$INSTALL_DIR/adb-macos"
cp -r "$SOURCES_DIR/$PROJECT_DIR"/. "$INSTALL_DIR/adb-macos/"

View File

@ -1,47 +1,25 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# This file is intended to be sourced by other scripts, not executed # This file is intended to be sourced by other scripts, not executed
process_args() { if [[ $# != 1 ]]
if [[ $# != 3 ]] then
then # <host>: win32 or win64
# <host>: win32 or win64 echo "Syntax: $0 <host>" >&2
# <build_type>: native or cross exit 1
# <link_type>: static or shared fi
echo "Syntax: $0 <host> <build_type> <link_type>" >&2
exit 1
fi
HOST="$1" HOST="$1"
BUILD_TYPE="$2" # native or cross
LINK_TYPE="$3" # static or shared
DIRNAME="$HOST-$BUILD_TYPE-$LINK_TYPE"
if [[ "$BUILD_TYPE" != native && "$BUILD_TYPE" != cross ]] if [[ "$HOST" = win32 ]]
then then
echo "Unsupported build type (expected native or cross): $BUILD_TYPE" >&2 HOST_TRIPLET=i686-w64-mingw32
exit 1 elif [[ "$HOST" = win64 ]]
fi then
HOST_TRIPLET=x86_64-w64-mingw32
if [[ "$LINK_TYPE" != static && "$LINK_TYPE" != shared ]] else
then echo "Unsupported host: $HOST" >&2
echo "Unsupported link type (expected static or shared): $LINK_TYPE" >&2 exit 1
exit 1 fi
fi
if [[ "$BUILD_TYPE" == cross ]]
then
if [[ "$HOST" = win32 ]]
then
HOST_TRIPLET=i686-w64-mingw32
elif [[ "$HOST" = win64 ]]
then
HOST_TRIPLET=x86_64-w64-mingw32
else
echo "Unsupported cross-build to host: $HOST" >&2
exit 1
fi
fi
}
DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR" cd "$DEPS_DIR"
@ -59,7 +37,7 @@ checksum() {
local file="$1" local file="$1"
local sum="$2" local sum="$2"
echo "$file: verifying checksum..." echo "$file: verifying checksum..."
echo "$sum $file" | shasum -a256 -c echo "$sum $file" | sha256sum -c
} }
get_file() { get_file() {

View File

@ -1,68 +0,0 @@
#!/usr/bin/env bash
set -ex
DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR"
. common
process_args "$@"
VERSION=1.5.0
FILENAME=dav1d-$VERSION.tar.gz
PROJECT_DIR=dav1d-$VERSION
SHA256SUM=78b15d9954b513ea92d27f39362535ded2243e1b0924fde39f37a31ebed5f76b
cd "$SOURCES_DIR"
if [[ -d "$PROJECT_DIR" ]]
then
echo "$PWD/$PROJECT_DIR" found
else
get_file "https://code.videolan.org/videolan/dav1d/-/archive/$VERSION/$FILENAME" "$FILENAME" "$SHA256SUM"
tar xf "$FILENAME" # First level directory is "$PROJECT_DIR"
fi
mkdir -p "$BUILD_DIR/$PROJECT_DIR"
cd "$BUILD_DIR/$PROJECT_DIR"
if [[ -d "$DIRNAME" ]]
then
echo "'$PWD/$DIRNAME' already exists, not reconfigured"
cd "$DIRNAME"
else
mkdir "$DIRNAME"
cd "$DIRNAME"
conf=(
--prefix="$INSTALL_DIR/$DIRNAME"
--libdir=lib
-Denable_tests=false
-Denable_tools=false
# Always build dav1d statically
--default-library=static
)
if [[ "$BUILD_TYPE" == cross ]]
then
case "$HOST" in
win32)
conf+=(
--cross-file="$SOURCES_DIR/$PROJECT_DIR/package/crossfiles/i686-w64-mingw32.meson"
)
;;
win64)
conf+=(
--cross-file="$SOURCES_DIR/$PROJECT_DIR/package/crossfiles/x86_64-w64-mingw32.meson"
)
;;
*)
echo "Unsupported host: $HOST" >&2
exit 1
esac
fi
meson setup . "$SOURCES_DIR/$PROJECT_DIR" "${conf[@]}"
fi
ninja
ninja install

View File

@ -3,12 +3,11 @@ set -ex
DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR" cd "$DEPS_DIR"
. common . common
process_args "$@"
VERSION=7.1 VERSION=7.0.2
FILENAME=ffmpeg-$VERSION.tar.xz FILENAME=ffmpeg-$VERSION.tar.xz
PROJECT_DIR=ffmpeg-$VERSION PROJECT_DIR=ffmpeg-$VERSION
SHA256SUM=40973D44970DBC83EF302B0609F2E74982BE2D85916DD2EE7472D30678A7ABE6 SHA256SUM=8646515b638a3ad303e23af6a3587734447cb8fc0a0c064ecdb8e95c4fd8b389
cd "$SOURCES_DIR" cd "$SOURCES_DIR"
@ -23,121 +22,68 @@ fi
mkdir -p "$BUILD_DIR/$PROJECT_DIR" mkdir -p "$BUILD_DIR/$PROJECT_DIR"
cd "$BUILD_DIR/$PROJECT_DIR" cd "$BUILD_DIR/$PROJECT_DIR"
if [[ -d "$DIRNAME" ]] if [[ "$HOST" = win32 ]]
then then
echo "'$PWD/$DIRNAME' already exists, not reconfigured" ARCH=x86
cd "$DIRNAME" elif [[ "$HOST" = win64 ]]
then
ARCH=x86_64
else else
mkdir "$DIRNAME" echo "Unsupported host: $HOST" >&2
cd "$DIRNAME" exit 1
fi
if [[ "$HOST" == win* ]] # -static-libgcc to avoid missing libgcc_s_dw2-1.dll
then # -static to avoid dynamic dependency to zlib
# -static-libgcc to avoid missing libgcc_s_dw2-1.dll export CFLAGS='-static-libgcc -static'
# -static to avoid dynamic dependency to zlib export CXXFLAGS="$CFLAGS"
export CFLAGS='-static-libgcc -static' export LDFLAGS='-static-libgcc -static'
export CXXFLAGS="$CFLAGS"
export LDFLAGS='-static-libgcc -static'
elif [[ "$HOST" == "macos" ]]
then
export PKG_CONFIG_PATH="/opt/homebrew/opt/zlib/lib/pkgconfig"
fi
export PKG_CONFIG_PATH="$INSTALL_DIR/$DIRNAME/lib/pkgconfig:$PKG_CONFIG_PATH" if [[ -d "$HOST" ]]
then
echo "'$PWD/$HOST' already exists, not reconfigured"
cd "$HOST"
else
mkdir "$HOST"
cd "$HOST"
conf=( "$SOURCES_DIR/$PROJECT_DIR"/configure \
--prefix="$INSTALL_DIR/$DIRNAME" --prefix="$INSTALL_DIR/$HOST" \
--pkg-config-flags="--static" --enable-cross-compile \
--extra-cflags="-O2 -fPIC" --target-os=mingw32 \
--disable-programs --arch="$ARCH" \
--disable-doc --cross-prefix="${HOST_TRIPLET}-" \
--disable-swscale --cc="${HOST_TRIPLET}-gcc" \
--disable-postproc --extra-cflags="-O2 -fPIC" \
--disable-avfilter --enable-shared \
--disable-network --disable-static \
--disable-everything --disable-programs \
--disable-doc \
--disable-swscale \
--disable-postproc \
--disable-avfilter \
--disable-avdevice \
--disable-network \
--disable-everything \
--enable-swresample \
--enable-decoder=h264 \
--enable-decoder=hevc \
--enable-decoder=av1 \
--enable-decoder=pcm_s16le \
--enable-decoder=opus \
--enable-decoder=aac \
--enable-decoder=flac \
--enable-decoder=png \
--enable-protocol=file \
--enable-demuxer=image2 \
--enable-parser=png \
--enable-zlib \
--enable-muxer=matroska \
--enable-muxer=mp4 \
--enable-muxer=opus \
--enable-muxer=flac \
--enable-muxer=wav \
--disable-vulkan --disable-vulkan
--disable-vaapi
--disable-vdpau
--enable-swresample
--enable-libdav1d
--enable-decoder=h264
--enable-decoder=hevc
--enable-decoder=av1
--enable-decoder=libdav1d
--enable-decoder=pcm_s16le
--enable-decoder=opus
--enable-decoder=aac
--enable-decoder=flac
--enable-decoder=png
--enable-protocol=file
--enable-demuxer=image2
--enable-parser=png
--enable-zlib
--enable-muxer=matroska
--enable-muxer=mp4
--enable-muxer=opus
--enable-muxer=flac
--enable-muxer=wav
)
if [[ "$HOST" == linux ]]
then
conf+=(
--enable-libv4l2
--enable-outdev=v4l2
--enable-encoder=rawvideo
)
else
# libavdevice is only used for V4L2 on Linux
conf+=(
--disable-avdevice
)
fi
if [[ "$LINK_TYPE" == static ]]
then
conf+=(
--enable-static
--disable-shared
)
else
conf+=(
--disable-static
--enable-shared
)
fi
if [[ "$BUILD_TYPE" == cross ]]
then
conf+=(
--enable-cross-compile
--cross-prefix="${HOST_TRIPLET}-"
--cc="${HOST_TRIPLET}-gcc"
)
case "$HOST" in
win32)
conf+=(
--target-os=mingw32
--arch=x86
)
;;
win64)
conf+=(
--target-os=mingw32
--arch=x86_64
)
;;
*)
echo "Unsupported host: $HOST" >&2
exit 1
esac
fi
"$SOURCES_DIR/$PROJECT_DIR"/configure "${conf[@]}"
fi fi
make -j make -j

View File

@ -3,7 +3,6 @@ set -ex
DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR" cd "$DEPS_DIR"
. common . common
process_args "$@"
VERSION=1.0.27 VERSION=1.0.27
FILENAME=libusb-$VERSION.tar.gz FILENAME=libusb-$VERSION.tar.gz
@ -26,40 +25,20 @@ cd "$BUILD_DIR/$PROJECT_DIR"
export CFLAGS='-O2' export CFLAGS='-O2'
export CXXFLAGS="$CFLAGS" export CXXFLAGS="$CFLAGS"
if [[ -d "$DIRNAME" ]] if [[ -d "$HOST" ]]
then then
echo "'$PWD/$DIRNAME' already exists, not reconfigured" echo "'$PWD/$HOST' already exists, not reconfigured"
cd "$DIRNAME" cd "$HOST"
else else
mkdir "$DIRNAME" mkdir "$HOST"
cd "$DIRNAME" cd "$HOST"
conf=(
--prefix="$INSTALL_DIR/$DIRNAME"
)
if [[ "$LINK_TYPE" == static ]]
then
conf+=(
--enable-static
--disable-shared
)
else
conf+=(
--disable-static
--enable-shared
)
fi
if [[ "$BUILD_TYPE" == cross ]]
then
conf+=(
--host="$HOST_TRIPLET"
)
fi
"$SOURCES_DIR/$PROJECT_DIR"/bootstrap.sh "$SOURCES_DIR/$PROJECT_DIR"/bootstrap.sh
"$SOURCES_DIR/$PROJECT_DIR"/configure "${conf[@]}" "$SOURCES_DIR/$PROJECT_DIR"/configure \
--prefix="$INSTALL_DIR/$HOST" \
--host="$HOST_TRIPLET" \
--enable-shared \
--disable-static
fi fi
make -j make -j

View File

@ -3,12 +3,11 @@ set -ex
DEPS_DIR=$(dirname ${BASH_SOURCE[0]}) DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR" cd "$DEPS_DIR"
. common . common
process_args "$@"
VERSION=2.30.10 VERSION=2.30.7
FILENAME=SDL-$VERSION.tar.gz FILENAME=SDL-$VERSION.tar.gz
PROJECT_DIR=SDL-release-$VERSION PROJECT_DIR=SDL-release-$VERSION
SHA256SUM=35a8b9c4f3635d85762b904ac60ca4e0806bff89faeb269caafbe80860d67168 SHA256SUM=1578c96f62c9ae36b64e431b2aa0e0b0fd07c275dedbc694afc38e19056688f5
cd "$SOURCES_DIR" cd "$SOURCES_DIR"
@ -26,54 +25,23 @@ cd "$BUILD_DIR/$PROJECT_DIR"
export CFLAGS='-O2' export CFLAGS='-O2'
export CXXFLAGS="$CFLAGS" export CXXFLAGS="$CFLAGS"
if [[ -d "$DIRNAME" ]] if [[ -d "$HOST" ]]
then then
echo "'$PWD/$HDIRNAME' already exists, not reconfigured" echo "'$PWD/$HOST' already exists, not reconfigured"
cd "$DIRNAME" cd "$HOST"
else else
mkdir "$DIRNAME" mkdir "$HOST"
cd "$DIRNAME" cd "$HOST"
conf=( "$SOURCES_DIR/$PROJECT_DIR"/configure \
--prefix="$INSTALL_DIR/$DIRNAME" --prefix="$INSTALL_DIR/$HOST" \
) --host="$HOST_TRIPLET" \
--enable-shared \
if [[ "$HOST" == linux ]] --disable-static
then
conf+=(
--enable-video-wayland
--enable-video-x11
)
fi
if [[ "$LINK_TYPE" == static ]]
then
conf+=(
--enable-static
--disable-shared
)
else
conf+=(
--disable-static
--enable-shared
)
fi
if [[ "$BUILD_TYPE" == cross ]]
then
conf+=(
--host="$HOST_TRIPLET"
)
fi
"$SOURCES_DIR/$PROJECT_DIR"/configure "${conf[@]}"
fi fi
make -j make -j
# There is no "make install-strip" # There is no "make install-strip"
make install make install
# Strip manually # Strip manually
if [[ "$LINK_TYPE" == shared && "$HOST" == win* ]] ${HOST_TRIPLET}-strip "$INSTALL_DIR/$HOST/bin/SDL2.dll"
then
${HOST_TRIPLET}-strip "$INSTALL_DIR/$DIRNAME/bin/SDL2.dll"
fi

View File

@ -46,7 +46,6 @@ src = [
'src/util/acksync.c', 'src/util/acksync.c',
'src/util/audiobuf.c', 'src/util/audiobuf.c',
'src/util/average.c', 'src/util/average.c',
'src/util/env.c',
'src/util/file.c', 'src/util/file.c',
'src/util/intmap.c', 'src/util/intmap.c',
'src/util/intr.c', 'src/util/intr.c',
@ -110,22 +109,20 @@ endif
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
static = get_option('static')
dependencies = [ dependencies = [
dependency('libavformat', version: '>= 57.33', static: static), dependency('libavformat', version: '>= 57.33'),
dependency('libavcodec', version: '>= 57.37', static: static), dependency('libavcodec', version: '>= 57.37'),
dependency('libavutil', static: static), dependency('libavutil'),
dependency('libswresample', static: static), dependency('libswresample'),
dependency('sdl2', version: '>= 2.0.5', static: static), dependency('sdl2', version: '>= 2.0.5'),
] ]
if v4l2_support if v4l2_support
dependencies += dependency('libavdevice', static: static) dependencies += dependency('libavdevice')
endif endif
if usb_support if usb_support
dependencies += dependency('libusb-1.0', static: static) dependencies += dependency('libusb-1.0')
endif endif
if host_machine.system() == 'windows' if host_machine.system() == 'windows'
@ -170,6 +167,9 @@ conf.set('DEFAULT_LOCAL_PORT_RANGE_LAST', '27199')
# run a server debugger and wait for a client to be attached # run a server debugger and wait for a client to be attached
conf.set('SERVER_DEBUGGER', get_option('server_debugger')) conf.set('SERVER_DEBUGGER', get_option('server_debugger'))
# select the debugger method ('old' for Android < 9, 'new' for Android >= 9)
conf.set('SERVER_DEBUGGER_METHOD_NEW', get_option('server_debugger_method') == 'new')
# enable V4L2 support (linux only) # enable V4L2 support (linux only)
conf.set('HAVE_V4L2', v4l2_support) conf.set('HAVE_V4L2', v4l2_support)
@ -192,19 +192,19 @@ datadir = get_option('datadir') # by default 'share'
install_man('scrcpy.1') install_man('scrcpy.1')
install_data('data/icon.png', install_data('data/icon.png',
rename: 'scrcpy.png', rename: 'scrcpy.png',
install_dir: datadir / 'icons/hicolor/256x256/apps') install_dir: join_paths(datadir, 'icons/hicolor/256x256/apps'))
install_data('data/zsh-completion/_scrcpy', install_data('data/zsh-completion/_scrcpy',
install_dir: datadir / 'zsh/site-functions') install_dir: join_paths(datadir, 'zsh/site-functions'))
install_data('data/bash-completion/scrcpy', install_data('data/bash-completion/scrcpy',
install_dir: datadir / 'bash-completion/completions') install_dir: join_paths(datadir, 'bash-completion/completions'))
# Desktop entry file for application launchers # Desktop entry file for application launchers
if host_machine.system() == 'linux' if host_machine.system() == 'linux'
# Install a launcher (ex: /usr/local/share/applications/scrcpy.desktop) # Install a launcher (ex: /usr/local/share/applications/scrcpy.desktop)
install_data('data/scrcpy.desktop', install_data('data/scrcpy.desktop',
install_dir: datadir / 'applications') install_dir: join_paths(datadir, 'applications'))
install_data('data/scrcpy-console.desktop', install_data('data/scrcpy-console.desktop',
install_dir: datadir / 'applications') install_dir: join_paths(datadir, 'applications'))
endif endif
@ -279,9 +279,3 @@ if get_option('buildtype') == 'debug'
test(t[0], exe) test(t[0], exe)
endforeach endforeach
endif endif
if meson.version().version_compare('>= 0.58.0')
devenv = environment()
devenv.set('SCRCPY_ICON_PATH', meson.current_source_dir() / 'data/icon.png')
meson.add_devenv(devenv)
endif

View File

@ -13,7 +13,7 @@ BEGIN
VALUE "LegalCopyright", "Romain Vimont, Genymobile" VALUE "LegalCopyright", "Romain Vimont, Genymobile"
VALUE "OriginalFilename", "scrcpy.exe" VALUE "OriginalFilename", "scrcpy.exe"
VALUE "ProductName", "scrcpy" VALUE "ProductName", "scrcpy"
VALUE "ProductVersion", "3.1" VALUE "ProductVersion", "2.7"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -19,10 +19,6 @@ provides display and control of Android devices connected on USB (or over TCP/IP
.B \-\-always\-on\-top .B \-\-always\-on\-top
Make scrcpy window always on top (above other windows). Make scrcpy window always on top (above other windows).
.TP
.BI "\-\-angle " degrees
Rotate the video content by a custom angle, in degrees (clockwise).
.TP .TP
.BI "\-\-audio\-bit\-rate " value .BI "\-\-audio\-bit\-rate " value
Encode the audio at the given bit rate, expressed in bits/s. Unit suffixes are supported: '\fBK\fR' (x1000) and '\fBM\fR' (x1000000). Encode the audio at the given bit rate, expressed in bits/s. Unit suffixes are supported: '\fBK\fR' (x1000) and '\fBM\fR' (x1000000).
@ -97,18 +93,6 @@ Select the camera size by its aspect ratio (+/- 10%).
Possible values are "sensor" (use the camera sensor aspect ratio), "\fInum\fR:\fIden\fR" (e.g. "4:3") and "\fIvalue\fR" (e.g. "1.6"). Possible values are "sensor" (use the camera sensor aspect ratio), "\fInum\fR:\fIden\fR" (e.g. "4:3") and "\fIvalue\fR" (e.g. "1.6").
.TP
.BI "\-\-camera\-facing " facing
Select the device camera by its facing direction.
Possible values are "front", "back" and "external".
.TP
.BI "\-\-camera\-fps " fps
Specify the camera capture frame rate.
If not specified, Android's default frame rate (30 fps) is used.
.TP .TP
.B \-\-camera\-high\-speed .B \-\-camera\-high\-speed
Enable high-speed camera capture mode. Enable high-speed camera capture mode.
@ -122,26 +106,28 @@ Specify the device camera id to mirror.
The available camera ids can be listed by \fB\-\-list\-cameras\fR. The available camera ids can be listed by \fB\-\-list\-cameras\fR.
.TP .TP
.BI "\-\-camera\-size " width\fRx\fIheight .BI "\-\-camera\-facing " facing
Specify an explicit camera capture size. Select the device camera by its facing direction.
Possible values are "front", "back" and "external".
.TP .TP
.BI "\-\-capture\-orientation " value .BI "\-\-camera\-fps " fps
Possible values are 0, 90, 180, 270, flip0, flip90, flip180 and flip270, possibly prefixed by '@'. Specify the camera capture frame rate.
The number represents the clockwise rotation in degrees; the "flip" keyword applies a horizontal flip before the rotation. If not specified, Android's default frame rate (30 fps) is used.
If a leading '@' is passed (@90) for display capture, then the rotation is locked, and is relative to the natural device orientation. .TP
.BI "\-\-camera\-size " width\fRx\fIheight
If '@' is passed alone, then the rotation is locked to the initial device orientation. Specify an explicit camera capture size.
Default is 0.
.TP .TP
.BI "\-\-crop " width\fR:\fIheight\fR:\fIx\fR:\fIy .BI "\-\-crop " width\fR:\fIheight\fR:\fIx\fR:\fIy
Crop the device screen on the server. Crop the device screen on the server.
The values are expressed in the device natural orientation (typically, portrait for a phone, landscape for a tablet). The values are expressed in the device natural orientation (typically, portrait for a phone, landscape for a tablet). Any
.B \-\-max\-size
value is computed on the cropped size.
.TP .TP
.B \-d, \-\-select\-usb .B \-d, \-\-select\-usb
@ -153,6 +139,12 @@ Also see \fB\-e\fR (\fB\-\-select\-tcpip\fR).
.BI "\-\-disable\-screensaver" .BI "\-\-disable\-screensaver"
Disable screensaver while scrcpy is running. Disable screensaver while scrcpy is running.
.TP
.BI "\-\-display\-buffer " ms
Add a buffering delay (in milliseconds) before displaying. This increases latency to compensate for jitter.
Default is 0 (no buffering).
.TP .TP
.BI "\-\-display\-id " id .BI "\-\-display\-id " id
Specify the device display id to mirror. Specify the device display id to mirror.
@ -235,10 +227,6 @@ Inject computer clipboard text as a sequence of key events on Ctrl+v (like MOD+S
This is a workaround for some devices not behaving as expected when setting the device clipboard programmatically. This is a workaround for some devices not behaving as expected when setting the device clipboard programmatically.
.TP
.B \-\-list\-apps
List Android apps installed on the device.
.TP .TP
.B \-\-list\-camera\-sizes .B \-\-list\-camera\-sizes
List the valid camera capture sizes. List the valid camera capture sizes.
@ -255,6 +243,16 @@ List video and audio encoders available on the device.
.B \-\-list\-displays .B \-\-list\-displays
List displays available on the device. List displays available on the device.
.TP
\fB\-\-lock\-video\-orientation\fR[=\fIvalue\fR]
Lock capture video orientation to \fIvalue\fR.
Possible values are "unlocked", "initial" (locked to the initial orientation), 0, 90, 180, and 270. The values represent the clockwise rotation from the natural device orientation, in degrees.
Default is "unlocked".
Passing the option without argument is equivalent to passing "initial".
.TP .TP
.BI "\-m, \-\-max\-size " value .BI "\-m, \-\-max\-size " value
Limit both the width and height of the video to \fIvalue\fR. The other dimension is computed so that the device aspect\-ratio is preserved. Limit both the width and height of the video to \fIvalue\fR. The other dimension is computed so that the device aspect\-ratio is preserved.
@ -316,17 +314,6 @@ Disable device control (mirror the device in read\-only).
.B \-N, \-\-no\-playback .B \-N, \-\-no\-playback
Disable video and audio playback on the computer (equivalent to \fB\-\-no\-video\-playback \-\-no\-audio\-playback\fR). Disable video and audio playback on the computer (equivalent to \fB\-\-no\-video\-playback \-\-no\-audio\-playback\fR).
.TP
\fB\-\-new\-display\fR[=[\fIwidth\fRx\fIheight\fR][/\fIdpi\fR]]
Create a new display with the specified resolution and density. If not provided, they default to the main display dimensions and DPI.
Examples:
\-\-new\-display=1920x1080
\-\-new\-display=1920x1080/420
\-\-new\-display # main display size and density
\-\-new\-display=/240 # main display size and 240 dpi
.TP .TP
.B \-\-no\-audio .B \-\-no\-audio
Disable audio forwarding. Disable audio forwarding.
@ -369,16 +356,6 @@ Do not forward mouse hover (mouse motion without any clicks) events.
.B \-\-no\-power\-on .B \-\-no\-power\-on
Do not power on the device on start. Do not power on the device on start.
.TP
.B \-\-no\-vd\-destroy\-content
Disable virtual display "destroy content on removal" flag.
With this option, when the virtual display is closed, the running apps are moved to the main display rather than being destroyed.
.TP
.B \-\-no\-vd\-system\-decorations
Disable virtual display system decorations flag.
.TP .TP
.B \-\-no\-video .B \-\-no\-video
Disable video forwarding. Disable video forwarding.
@ -501,22 +478,6 @@ For example, to use either LCtrl or LSuper for scrcpy shortcuts, pass "lctrl,lsu
Default is "lalt,lsuper" (left-Alt or left-Super). Default is "lalt,lsuper" (left-Alt or left-Super).
.TP
.BI "\-\-start\-app " name
Start an Android app, by its exact package name.
Add a '?' prefix to select an app whose name starts with the given name, case-insensitive (retrieving app names on the device may take some time):
scrcpy --start-app=?firefox
Add a '+' prefix to force-stop before starting the app:
scrcpy --new-display --start-app=+org.mozilla.firefox
Both prefixes can be used, in that order:
scrcpy --start-app=+?firefox
.TP .TP
.B \-t, \-\-show\-touches .B \-t, \-\-show\-touches
Enable "show touches" on start, restore the initial value on exit. Enable "show touches" on start, restore the initial value on exit.
@ -524,15 +485,13 @@ Enable "show touches" on start, restore the initial value on exit.
It only shows physical touches (not clicks from scrcpy). It only shows physical touches (not clicks from scrcpy).
.TP .TP
.BI "\-\-tcpip\fR[=[+]\fIip\fR[:\fIport\fR]] .BI "\-\-tcpip\fR[=\fIip\fR[:\fIport\fR]]
Configure and connect the device over TCP/IP. Configure and reconnect the device over TCP/IP.
If a destination address is provided, then scrcpy connects to this address before starting. The device must listen on the given TCP port (default is 5555). If a destination address is provided, then scrcpy connects to this address before starting. The device must listen on the given TCP port (default is 5555).
If no destination address is provided, then scrcpy attempts to find the IP address and adb port of the current device (typically connected over USB), enables TCP/IP mode if necessary, then connects to this address before starting. If no destination address is provided, then scrcpy attempts to find the IP address and adb port of the current device (typically connected over USB), enables TCP/IP mode if necessary, then connects to this address before starting.
Prefix the address with a '+' to force a reconnection.
.TP .TP
.BI "\-\-time\-limit " seconds .BI "\-\-time\-limit " seconds
Set the maximum mirroring time, in seconds. Set the maximum mirroring time, in seconds.
@ -563,19 +522,13 @@ Default is "info" for release builds, "debug" for debug builds.
.BI "\-\-v4l2-sink " /dev/videoN .BI "\-\-v4l2-sink " /dev/videoN
Output to v4l2loopback device. Output to v4l2loopback device.
It requires to lock the video orientation (see \fB\-\-lock\-video\-orientation\fR).
.TP .TP
.BI "\-\-v4l2-buffer " ms .BI "\-\-v4l2-buffer " ms
Add a buffering delay (in milliseconds) before pushing frames. This increases latency to compensate for jitter. Add a buffering delay (in milliseconds) before pushing frames. This increases latency to compensate for jitter.
This option is similar to \fB\-\-video\-buffer\fR, but specific to V4L2 sink. This option is similar to \fB\-\-display\-buffer\fR, but specific to V4L2 sink.
Default is 0 (no buffering).
.TP
.BI "\-\-video\-buffer " ms
Add a buffering delay (in milliseconds) before displaying video frames.
This increases latency to compensate for jitter.
Default is 0 (no buffering). Default is 0 (no buffering).
@ -684,10 +637,6 @@ Pause or re-pause display
.B MOD+Shift+z .B MOD+Shift+z
Unpause display Unpause display
.TP
.B MOD+Shift+r
Reset video capture/encoding
.TP .TP
.B MOD+g .B MOD+g
Resize window to 1:1 (pixel\-perfect) Resize window to 1:1 (pixel\-perfect)

View File

@ -4,11 +4,9 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/types.h>
#include "adb/adb_device.h" #include "adb_device.h"
#include "adb/adb_parser.h" #include "adb_parser.h"
#include "util/env.h"
#include "util/file.h" #include "util/file.h"
#include "util/log.h" #include "util/log.h"
#include "util/process_intr.h" #include "util/process_intr.h"
@ -26,45 +24,15 @@
*/ */
#define SC_ADB_COMMAND(...) { sc_adb_get_executable(), __VA_ARGS__, NULL } #define SC_ADB_COMMAND(...) { sc_adb_get_executable(), __VA_ARGS__, NULL }
static char *adb_executable; static const char *adb_executable;
bool
sc_adb_init(void) {
adb_executable = sc_get_env("ADB");
if (adb_executable) {
LOGD("Using adb: %s", adb_executable);
return true;
}
#if !defined(PORTABLE) || defined(_WIN32)
adb_executable = strdup("adb");
if (!adb_executable) {
LOG_OOM();
return false;
}
#else
// For portable builds, use the absolute path to the adb executable
// in the same directory as scrcpy (except on Windows, where "adb"
// is sufficient)
adb_executable = sc_file_get_local_path("adb");
if (!adb_executable) {
// Error already logged
return false;
}
LOGD("Using adb (portable): %s", adb_executable);
#endif
return true;
}
void
sc_adb_destroy(void) {
free(adb_executable);
}
const char * const char *
sc_adb_get_executable(void) { sc_adb_get_executable(void) {
if (!adb_executable) {
adb_executable = getenv("ADB");
if (!adb_executable)
adb_executable = "adb";
}
return adb_executable; return adb_executable;
} }
@ -413,7 +381,7 @@ sc_adb_connect(struct sc_intr *intr, const char *ip_port, unsigned flags) {
// "adb connect" always returns successfully (with exit code 0), even in // "adb connect" always returns successfully (with exit code 0), even in
// case of failure. As a workaround, check if its output starts with // case of failure. As a workaround, check if its output starts with
// "connected" or "already connected". // "connected".
char buf[128]; char buf[128];
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf) - 1); ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf) - 1);
sc_pipe_close(pout); sc_pipe_close(pout);
@ -430,8 +398,7 @@ sc_adb_connect(struct sc_intr *intr, const char *ip_port, unsigned flags) {
assert((size_t) r < sizeof(buf)); assert((size_t) r < sizeof(buf));
buf[r] = '\0'; buf[r] = '\0';
ok = !strncmp("connected", buf, sizeof("connected") - 1) ok = !strncmp("connected", buf, sizeof("connected") - 1);
|| !strncmp("already connected", buf, sizeof("already connected") - 1);
if (!ok && !(flags & SC_ADB_NO_STDERR)) { if (!ok && !(flags & SC_ADB_NO_STDERR)) {
// "adb connect" also prints errors to stdout. Since we capture it, // "adb connect" also prints errors to stdout. Since we capture it,
// re-print the error to stderr. // re-print the error to stderr.
@ -772,21 +739,3 @@ sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags) {
return sc_adb_parse_device_ip(buf); return sc_adb_parse_device_ip(buf);
} }
uint16_t
sc_adb_get_device_sdk_version(struct sc_intr *intr, const char *serial) {
char *sdk_version =
sc_adb_getprop(intr, serial, "ro.build.version.sdk", SC_ADB_SILENT);
if (!sdk_version) {
return 0;
}
long value;
bool ok = sc_str_parse_integer(sdk_version, &value);
free(sdk_version);
if (!ok || value < 0 || value > 0xFFFF) {
return 0;
}
return value;
}

View File

@ -6,7 +6,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <inttypes.h> #include <inttypes.h>
#include "adb/adb_device.h" #include "adb_device.h"
#include "util/intr.h" #include "util/intr.h"
#define SC_ADB_NO_STDOUT (1 << 0) #define SC_ADB_NO_STDOUT (1 << 0)
@ -15,12 +15,6 @@
#define SC_ADB_SILENT (SC_ADB_NO_STDOUT | SC_ADB_NO_STDERR | SC_ADB_NO_LOGERR) #define SC_ADB_SILENT (SC_ADB_NO_STDOUT | SC_ADB_NO_STDERR | SC_ADB_NO_LOGERR)
bool
sc_adb_init(void);
void
sc_adb_destroy(void);
const char * const char *
sc_adb_get_executable(void); sc_adb_get_executable(void);
@ -120,10 +114,4 @@ sc_adb_getprop(struct sc_intr *intr, const char *serial, const char *prop,
char * char *
sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags); sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags);
/**
* Return the device SDK version.
*/
uint16_t
sc_adb_get_device_sdk_version(struct sc_intr *intr, const char *serial);
#endif #endif

View File

@ -4,6 +4,7 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include "util/vector.h" #include "util/vector.h"

View File

@ -3,7 +3,6 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/types.h>
#include "util/log.h" #include "util/log.h"
#include "util/str.h" #include "util/str.h"

View File

@ -3,9 +3,9 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stddef.h>
#include "adb/adb_device.h" #include "adb_device.h"
/** /**
* Parse the available devices from the output of `adb devices` * Parse the available devices from the output of `adb devices`

View File

@ -1,11 +1,11 @@
#include "adb_tunnel.h" #include "adb_tunnel.h"
#include <assert.h> #include <assert.h>
#include <inttypes.h>
#include "adb/adb.h" #include "adb.h"
#include "util/log.h" #include "util/log.h"
#include "util/net_intr.h" #include "util/net_intr.h"
#include "util/process_intr.h"
static bool static bool
listen_on_port(struct sc_intr *intr, sc_socket socket, uint16_t port) { listen_on_port(struct sc_intr *intr, sc_socket socket, uint16_t port) {

View File

@ -3,7 +3,9 @@
#include "common.h" #include "common.h"
#include <SDL2/SDL_audio.h> #include <stdatomic.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
#include "audio_regulator.h" #include "audio_regulator.h"
#include "trait/frame_sink.h" #include "trait/frame_sink.h"

View File

@ -1,9 +1,5 @@
#include "audio_regulator.h" #include "audio_regulator.h"
#include <assert.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavutil/opt.h> #include <libavutil/opt.h>
@ -292,7 +288,7 @@ sc_audio_regulator_push(struct sc_audio_regulator *ar, const AVFrame *frame) {
// Enable compensation when the difference exceeds +/- 4ms. // Enable compensation when the difference exceeds +/- 4ms.
// Disable compensation when the difference is lower than +/- 1ms. // Disable compensation when the difference is lower than +/- 1ms.
int threshold = ar->compensation_active int threshold = ar->compensation != 0
? ar->sample_rate / 1000 /* 1ms */ ? ar->sample_rate / 1000 /* 1ms */
: ar->sample_rate * 4 / 1000; /* 4ms */ : ar->sample_rate * 4 / 1000; /* 4ms */
@ -313,12 +309,14 @@ sc_audio_regulator_push(struct sc_audio_regulator *ar, const AVFrame *frame) {
LOGV("[Audio] Buffering: target=%" PRIu32 " avg=%f cur=%" PRIu32 LOGV("[Audio] Buffering: target=%" PRIu32 " avg=%f cur=%" PRIu32
" compensation=%d", ar->target_buffering, avg, can_read, diff); " compensation=%d", ar->target_buffering, avg, can_read, diff);
int ret = swr_set_compensation(swr_ctx, diff, distance); if (diff != ar->compensation) {
if (ret < 0) { int ret = swr_set_compensation(swr_ctx, diff, distance);
LOGW("Resampling compensation failed: %d", ret); if (ret < 0) {
// not fatal LOGW("Resampling compensation failed: %d", ret);
} else { // not fatal
ar->compensation_active = diff != 0; } else {
ar->compensation = diff;
}
} }
} }
@ -394,7 +392,7 @@ sc_audio_regulator_init(struct sc_audio_regulator *ar, size_t sample_size,
atomic_init(&ar->played, false); atomic_init(&ar->played, false);
atomic_init(&ar->received, false); atomic_init(&ar->received, false);
atomic_init(&ar->underflow, 0); atomic_init(&ar->underflow, 0);
ar->compensation_active = false; ar->compensation = 0;
return true; return true;

View File

@ -5,8 +5,6 @@
#include <stdatomic.h> #include <stdatomic.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libswresample/swresample.h> #include <libswresample/swresample.h>
#include "util/audiobuf.h" #include "util/audiobuf.h"
@ -46,8 +44,8 @@ struct sc_audio_regulator {
// Number of silence samples inserted since the last received packet // Number of silence samples inserted since the last received packet
atomic_uint_least32_t underflow; atomic_uint_least32_t underflow;
// Non-zero compensation applied (only used by the receiver thread) // Current applied compensation value (only used by the receiver thread)
bool compensation_active; int compensation;
// Set to true the first time a sample is received // Set to true the first time a sample is received
atomic_bool received; atomic_bool received;

View File

@ -5,7 +5,6 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#include "options.h" #include "options.h"
@ -14,7 +13,6 @@
#include "util/str.h" #include "util/str.h"
#include "util/strbuf.h" #include "util/strbuf.h"
#include "util/term.h" #include "util/term.h"
#include "util/tick.h"
#define STR_IMPL_(x) #x #define STR_IMPL_(x) #x
#define STR(x) STR_IMPL_(x) #define STR(x) STR_IMPL_(x)
@ -52,7 +50,6 @@ enum {
OPT_POWER_OFF_ON_CLOSE, OPT_POWER_OFF_ON_CLOSE,
OPT_V4L2_SINK, OPT_V4L2_SINK,
OPT_DISPLAY_BUFFER, OPT_DISPLAY_BUFFER,
OPT_VIDEO_BUFFER,
OPT_V4L2_BUFFER, OPT_V4L2_BUFFER,
OPT_TUNNEL_HOST, OPT_TUNNEL_HOST,
OPT_TUNNEL_PORT, OPT_TUNNEL_PORT,
@ -105,14 +102,6 @@ enum {
OPT_NO_MOUSE_HOVER, OPT_NO_MOUSE_HOVER,
OPT_AUDIO_DUP, OPT_AUDIO_DUP,
OPT_GAMEPAD, OPT_GAMEPAD,
OPT_NEW_DISPLAY,
OPT_LIST_APPS,
OPT_START_APP,
OPT_SCREEN_OFF_TIMEOUT,
OPT_CAPTURE_ORIENTATION,
OPT_ANGLE,
OPT_NO_VD_SYSTEM_DECORATIONS,
OPT_NO_VD_DESTROY_CONTENT,
}; };
struct sc_option { struct sc_option {
@ -154,13 +143,6 @@ static const struct sc_option options[] = {
.longopt = "always-on-top", .longopt = "always-on-top",
.text = "Make scrcpy window always on top (above other windows).", .text = "Make scrcpy window always on top (above other windows).",
}, },
{
.longopt_id = OPT_ANGLE,
.longopt = "angle",
.argdesc = "degrees",
.text = "Rotate the video content by a custom angle, in degrees "
"(clockwise).",
},
{ {
.longopt_id = OPT_AUDIO_BIT_RATE, .longopt_id = OPT_AUDIO_BIT_RATE,
.longopt = "audio-bit-rate", .longopt = "audio-bit-rate",
@ -258,6 +240,14 @@ static const struct sc_option options[] = {
"ratio), \"<num>:<den>\" (e.g. \"4:3\") or \"<value>\" (e.g. " "ratio), \"<num>:<den>\" (e.g. \"4:3\") or \"<value>\" (e.g. "
"\"1.6\")." "\"1.6\")."
}, },
{
.longopt_id = OPT_CAMERA_ID,
.longopt = "camera-id",
.argdesc = "id",
.text = "Specify the device camera id to mirror.\n"
"The available camera ids can be listed by:\n"
" scrcpy --list-cameras",
},
{ {
.longopt_id = OPT_CAMERA_FACING, .longopt_id = OPT_CAMERA_FACING,
.longopt = "camera-facing", .longopt = "camera-facing",
@ -265,14 +255,6 @@ static const struct sc_option options[] = {
.text = "Select the device camera by its facing direction.\n" .text = "Select the device camera by its facing direction.\n"
"Possible values are \"front\", \"back\" and \"external\".", "Possible values are \"front\", \"back\" and \"external\".",
}, },
{
.longopt_id = OPT_CAMERA_FPS,
.longopt = "camera-fps",
.argdesc = "value",
.text = "Specify the camera capture frame rate.\n"
"If not specified, Android's default frame rate (30 fps) is "
"used.",
},
{ {
.longopt_id = OPT_CAMERA_HIGH_SPEED, .longopt_id = OPT_CAMERA_HIGH_SPEED,
.longopt = "camera-high-speed", .longopt = "camera-high-speed",
@ -280,14 +262,6 @@ static const struct sc_option options[] = {
"This mode is restricted to specific resolutions and frame " "This mode is restricted to specific resolutions and frame "
"rates, listed by --list-camera-sizes.", "rates, listed by --list-camera-sizes.",
}, },
{
.longopt_id = OPT_CAMERA_ID,
.longopt = "camera-id",
.argdesc = "id",
.text = "Specify the device camera id to mirror.\n"
"The available camera ids can be listed by:\n"
" scrcpy --list-cameras",
},
{ {
.longopt_id = OPT_CAMERA_SIZE, .longopt_id = OPT_CAMERA_SIZE,
.longopt = "camera-size", .longopt = "camera-size",
@ -295,21 +269,12 @@ static const struct sc_option options[] = {
.text = "Specify an explicit camera capture size.", .text = "Specify an explicit camera capture size.",
}, },
{ {
.longopt_id = OPT_CAPTURE_ORIENTATION, .longopt_id = OPT_CAMERA_FPS,
.longopt = "capture-orientation", .longopt = "camera-fps",
.argdesc = "value", .argdesc = "value",
.text = "Set the capture video orientation.\n" .text = "Specify the camera capture frame rate.\n"
"Possible values are 0, 90, 180, 270, flip0, flip90, flip180 " "If not specified, Android's default frame rate (30 fps) is "
"and flip270, possibly prefixed by '@'.\n" "used.",
"The number represents the clockwise rotation in degrees; the "
"flip\" keyword applies a horizontal flip before the "
"rotation.\n"
"If a leading '@' is passed (@90) for display capture, then "
"the rotation is locked, and is relative to the natural device "
"orientation.\n"
"If '@' is passed alone, then the rotation is locked to the "
"initial device orientation.\n"
"Default is 0.",
}, },
{ {
// Not really deprecated (--codec has never been released), but without // Not really deprecated (--codec has never been released), but without
@ -332,7 +297,8 @@ static const struct sc_option options[] = {
.argdesc = "width:height:x:y", .argdesc = "width:height:x:y",
.text = "Crop the device screen on the server.\n" .text = "Crop the device screen on the server.\n"
"The values are expressed in the device natural orientation " "The values are expressed in the device natural orientation "
"(typically, portrait for a phone, landscape for a tablet).", "(typically, portrait for a phone, landscape for a tablet). "
"Any --max-size value is computed on the cropped size.",
}, },
{ {
.shortopt = 'd', .shortopt = 'd',
@ -352,10 +318,12 @@ static const struct sc_option options[] = {
.argdesc = "id", .argdesc = "id",
}, },
{ {
// deprecated
.longopt_id = OPT_DISPLAY_BUFFER, .longopt_id = OPT_DISPLAY_BUFFER,
.longopt = "display-buffer", .longopt = "display-buffer",
.argdesc = "ms", .argdesc = "ms",
.text = "Add a buffering delay (in milliseconds) before displaying. "
"This increases latency to compensate for jitter.\n"
"Default is 0 (no buffering).",
}, },
{ {
.longopt_id = OPT_DISPLAY_ID, .longopt_id = OPT_DISPLAY_ID,
@ -474,11 +442,6 @@ static const struct sc_option options[] = {
"This is a workaround for some devices not behaving as " "This is a workaround for some devices not behaving as "
"expected when setting the device clipboard programmatically.", "expected when setting the device clipboard programmatically.",
}, },
{
.longopt_id = OPT_LIST_APPS,
.longopt = "list-apps",
.text = "List Android apps installed on the device.",
},
{ {
.longopt_id = OPT_LIST_CAMERAS, .longopt_id = OPT_LIST_CAMERAS,
.longopt = "list-cameras", .longopt = "list-cameras",
@ -500,10 +463,18 @@ static const struct sc_option options[] = {
.text = "List video and audio encoders available on the device.", .text = "List video and audio encoders available on the device.",
}, },
{ {
// deprecated
.longopt_id = OPT_LOCK_VIDEO_ORIENTATION, .longopt_id = OPT_LOCK_VIDEO_ORIENTATION,
.longopt = "lock-video-orientation", .longopt = "lock-video-orientation",
.argdesc = "value", .argdesc = "value",
.optional_arg = true,
.text = "Lock capture video orientation to value.\n"
"Possible values are \"unlocked\", \"initial\" (locked to the "
"initial orientation), 0, 90, 180 and 270. The values "
"represent the clockwise rotation from the natural device "
"orientation, in degrees.\n"
"Default is \"unlocked\".\n"
"Passing the option without argument is equivalent to passing "
"\"initial\".",
}, },
{ {
.shortopt = 'm', .shortopt = 'm',
@ -586,20 +557,6 @@ static const struct sc_option options[] = {
.text = "Disable video and audio playback on the computer (equivalent " .text = "Disable video and audio playback on the computer (equivalent "
"to --no-video-playback --no-audio-playback).", "to --no-video-playback --no-audio-playback).",
}, },
{
.longopt_id = OPT_NEW_DISPLAY,
.longopt = "new-display",
.argdesc = "[<width>x<height>][/<dpi>]",
.optional_arg = true,
.text = "Create a new display with the specified resolution and "
"density. If not provided, they default to the main display "
"dimensions and DPI.\n"
"Examples:\n"
" --new-display=1920x1080\n"
" --new-display=1920x1080/420 # force 420 dpi\n"
" --new-display # main display size and density\n"
" --new-display=/240 # main display size and 240 dpi",
},
{ {
.longopt_id = OPT_NO_AUDIO, .longopt_id = OPT_NO_AUDIO,
.longopt = "no-audio", .longopt = "no-audio",
@ -662,20 +619,6 @@ static const struct sc_option options[] = {
.longopt = "no-power-on", .longopt = "no-power-on",
.text = "Do not power on the device on start.", .text = "Do not power on the device on start.",
}, },
{
.longopt_id = OPT_NO_VD_DESTROY_CONTENT,
.longopt = "no-vd-destroy-content",
.text = "Disable virtual display \"destroy content on removal\" "
"flag.\n"
"With this option, when the virtual display is closed, the "
"running apps are moved to the main display rather than being "
"destroyed.",
},
{
.longopt_id = OPT_NO_VD_SYSTEM_DECORATIONS,
.longopt = "no-vd-system-decorations",
.text = "Disable virtual display system decorations flag.",
},
{ {
.longopt_id = OPT_NO_VIDEO, .longopt_id = OPT_NO_VIDEO,
.longopt = "no-video", .longopt = "no-video",
@ -828,13 +771,6 @@ static const struct sc_option options[] = {
.longopt = "turn-screen-off", .longopt = "turn-screen-off",
.text = "Turn the device screen off immediately.", .text = "Turn the device screen off immediately.",
}, },
{
.longopt_id = OPT_SCREEN_OFF_TIMEOUT,
.longopt = "screen-off-timeout",
.argdesc = "seconds",
.text = "Set the screen off timeout while scrcpy is running (restore "
"the initial value on exit).",
},
{ {
.longopt_id = OPT_SHORTCUT_MOD, .longopt_id = OPT_SHORTCUT_MOD,
.longopt = "shortcut-mod", .longopt = "shortcut-mod",
@ -848,20 +784,6 @@ static const struct sc_option options[] = {
"shortcuts, pass \"lctrl,lsuper\".\n" "shortcuts, pass \"lctrl,lsuper\".\n"
"Default is \"lalt,lsuper\" (left-Alt or left-Super).", "Default is \"lalt,lsuper\" (left-Alt or left-Super).",
}, },
{
.longopt_id = OPT_START_APP,
.longopt = "start-app",
.argdesc = "name",
.text = "Start an Android app, by its exact package name.\n"
"Add a '?' prefix to select an app whose name starts with the "
"given name, case-insensitive (retrieving app names on the "
"device may take some time):\n"
" scrcpy --start-app=?firefox\n"
"Add a '+' prefix to force-stop before starting the app:\n"
" scrcpy --new-display --start-app=+org.mozilla.firefox\n"
"Both prefixes can be used, in that order:\n"
" scrcpy --start-app=+?firefox",
},
{ {
.shortopt = 't', .shortopt = 't',
.longopt = "show-touches", .longopt = "show-touches",
@ -872,17 +794,16 @@ static const struct sc_option options[] = {
{ {
.longopt_id = OPT_TCPIP, .longopt_id = OPT_TCPIP,
.longopt = "tcpip", .longopt = "tcpip",
.argdesc = "[+]ip[:port]", .argdesc = "ip[:port]",
.optional_arg = true, .optional_arg = true,
.text = "Configure and connect the device over TCP/IP.\n" .text = "Configure and reconnect the device over TCP/IP.\n"
"If a destination address is provided, then scrcpy connects to " "If a destination address is provided, then scrcpy connects to "
"this address before starting. The device must listen on the " "this address before starting. The device must listen on the "
"given TCP port (default is 5555).\n" "given TCP port (default is 5555).\n"
"If no destination address is provided, then scrcpy attempts " "If no destination address is provided, then scrcpy attempts "
"to find the IP address of the current device (typically " "to find the IP address of the current device (typically "
"connected over USB), enables TCP/IP mode, then connects to " "connected over USB), enables TCP/IP mode, then connects to "
"this address before starting.\n" "this address before starting.",
"Prefix the address with a '+' to force a reconnection.",
}, },
{ {
.longopt_id = OPT_TIME_LIMIT, .longopt_id = OPT_TIME_LIMIT,
@ -930,6 +851,8 @@ static const struct sc_option options[] = {
.longopt = "v4l2-sink", .longopt = "v4l2-sink",
.argdesc = "/dev/videoN", .argdesc = "/dev/videoN",
.text = "Output to v4l2loopback device.\n" .text = "Output to v4l2loopback device.\n"
"It requires to lock the video orientation (see "
"--lock-video-orientation).\n"
"This feature is only available on Linux.", "This feature is only available on Linux.",
}, },
{ {
@ -938,20 +861,11 @@ static const struct sc_option options[] = {
.argdesc = "ms", .argdesc = "ms",
.text = "Add a buffering delay (in milliseconds) before pushing " .text = "Add a buffering delay (in milliseconds) before pushing "
"frames. This increases latency to compensate for jitter.\n" "frames. This increases latency to compensate for jitter.\n"
"This option is similar to --video-buffer, but specific to " "This option is similar to --display-buffer, but specific to "
"V4L2 sink.\n" "V4L2 sink.\n"
"Default is 0 (no buffering).\n" "Default is 0 (no buffering).\n"
"This option is only available on Linux.", "This option is only available on Linux.",
}, },
{
.longopt_id = OPT_VIDEO_BUFFER,
.longopt = "video-buffer",
.argdesc = "ms",
.text = "Add a buffering delay (in milliseconds) before displaying "
"video frames.\n"
"This increases latency to compensate for jitter.\n"
"Default is 0 (no buffering).",
},
{ {
.longopt_id = OPT_VIDEO_CODEC, .longopt_id = OPT_VIDEO_CODEC,
.longopt = "video-codec", .longopt = "video-codec",
@ -1063,10 +977,6 @@ static const struct sc_shortcut shortcuts[] = {
.shortcuts = { "MOD+Shift+z" }, .shortcuts = { "MOD+Shift+z" },
.text = "Unpause display", .text = "Unpause display",
}, },
{
.shortcuts = { "MOD+Shift+r" },
.text = "Reset video capture/encoding",
},
{ {
.shortcuts = { "MOD+g" }, .shortcuts = { "MOD+g" },
.text = "Resize window to 1:1 (pixel-perfect)", .text = "Resize window to 1:1 (pixel-perfect)",
@ -1615,6 +1525,78 @@ parse_audio_output_buffer(const char *s, sc_tick *tick) {
return true; return true;
} }
static bool
parse_lock_video_orientation(const char *s,
enum sc_lock_video_orientation *lock_mode) {
if (!s || !strcmp(s, "initial")) {
// Without argument, lock the initial orientation
*lock_mode = SC_LOCK_VIDEO_ORIENTATION_INITIAL;
return true;
}
if (!strcmp(s, "unlocked")) {
*lock_mode = SC_LOCK_VIDEO_ORIENTATION_UNLOCKED;
return true;
}
if (!strcmp(s, "0")) {
*lock_mode = SC_LOCK_VIDEO_ORIENTATION_0;
return true;
}
if (!strcmp(s, "90")) {
*lock_mode = SC_LOCK_VIDEO_ORIENTATION_90;
return true;
}
if (!strcmp(s, "180")) {
*lock_mode = SC_LOCK_VIDEO_ORIENTATION_180;
return true;
}
if (!strcmp(s, "270")) {
*lock_mode = SC_LOCK_VIDEO_ORIENTATION_270;
return true;
}
if (!strcmp(s, "1")) {
LOGW("--lock-video-orientation=1 is deprecated, use "
"--lock-video-orientation=270 instead.");
*lock_mode = SC_LOCK_VIDEO_ORIENTATION_270;
return true;
}
if (!strcmp(s, "2")) {
LOGW("--lock-video-orientation=2 is deprecated, use "
"--lock-video-orientation=180 instead.");
*lock_mode = SC_LOCK_VIDEO_ORIENTATION_180;
return true;
}
if (!strcmp(s, "3")) {
LOGW("--lock-video-orientation=3 is deprecated, use "
"--lock-video-orientation=90 instead.");
*lock_mode = SC_LOCK_VIDEO_ORIENTATION_90;
return true;
}
LOGE("Unsupported --lock-video-orientation value: %s (expected initial, "
"unlocked, 0, 90, 180 or 270).", s);
return false;
}
static bool
parse_rotation(const char *s, uint8_t *rotation) {
long value;
bool ok = parse_integer_arg(s, &value, false, 0, 3, "rotation");
if (!ok) {
return false;
}
*rotation = (uint8_t) value;
return true;
}
static bool static bool
parse_orientation(const char *s, enum sc_orientation *orientation) { parse_orientation(const char *s, enum sc_orientation *orientation) {
if (!strcmp(s, "0")) { if (!strcmp(s, "0")) {
@ -1654,32 +1636,6 @@ parse_orientation(const char *s, enum sc_orientation *orientation) {
return false; return false;
} }
static bool
parse_capture_orientation(const char *s, enum sc_orientation *orientation,
enum sc_orientation_lock *lock) {
if (*s == '\0') {
LOGE("Capture orientation may not be empty (expected 0, 90, 180, 270, "
"flip0, flip90, flip180 or flip270, possibly prefixed by '@')");
return false;
}
// Lock the orientation by a leading '@'
if (s[0] == '@') {
// Consume '@'
++s;
if (*s == '\0') {
// Only '@': lock to the initial orientation (orientation is unused)
*lock = SC_ORIENTATION_LOCKED_INITIAL;
return true;
}
*lock = SC_ORIENTATION_LOCKED_VALUE;
} else {
*lock = SC_ORIENTATION_UNLOCKED;
}
return parse_orientation(s, orientation);
}
static bool static bool
parse_window_position(const char *s, int16_t *position) { parse_window_position(const char *s, int16_t *position) {
// special value for "auto" // special value for "auto"
@ -2150,20 +2106,6 @@ parse_time_limit(const char *s, sc_tick *tick) {
return true; return true;
} }
static bool
parse_screen_off_timeout(const char *s, sc_tick *tick) {
long value;
// value in seconds, but must fit in 31 bits in milliseconds
bool ok = parse_integer_arg(s, &value, false, 0, 0x7FFFFFFF / 1000,
"screen off timeout");
if (!ok) {
return false;
}
*tick = SC_TICK_FROM_SEC(value);
return true;
}
static bool static bool
parse_pause_on_exit(const char *s, enum sc_pause_on_exit *pause_on_exit) { parse_pause_on_exit(const char *s, enum sc_pause_on_exit *pause_on_exit) {
if (!s || !strcmp(s, "true")) { if (!s || !strcmp(s, "true")) {
@ -2289,8 +2231,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
opts->crop = optarg; opts->crop = optarg;
break; break;
case OPT_DISPLAY: case OPT_DISPLAY:
LOGE("--display has been removed, use --display-id instead."); LOGW("--display is deprecated, use --display-id instead.");
return false; // fall through
case OPT_DISPLAY_ID: case OPT_DISPLAY_ID:
if (!parse_display_id(optarg, &opts->display_id)) { if (!parse_display_id(optarg, &opts->display_id)) {
return false; return false;
@ -2354,13 +2296,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
"--mouse=uhid instead."); "--mouse=uhid instead.");
return false; return false;
case OPT_LOCK_VIDEO_ORIENTATION: case OPT_LOCK_VIDEO_ORIENTATION:
LOGE("--lock-video-orientation has been removed, use " if (!parse_lock_video_orientation(optarg,
"--capture-orientation instead."); &opts->lock_video_orientation)) {
return false;
case OPT_CAPTURE_ORIENTATION:
if (!parse_capture_orientation(optarg,
&opts->capture_orientation,
&opts->capture_orientation_lock)) {
return false; return false;
} }
break; break;
@ -2378,9 +2315,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
opts->control = false; opts->control = false;
break; break;
case OPT_NO_DISPLAY: case OPT_NO_DISPLAY:
LOGE("--no-display has been removed, use --no-playback " LOGW("--no-display is deprecated, use --no-playback instead.");
"instead."); // fall through
return false;
case 'N': case 'N':
opts->video_playback = false; opts->video_playback = false;
opts->audio_playback = false; opts->audio_playback = false;
@ -2466,9 +2402,32 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
opts->key_inject_mode = SC_KEY_INJECT_MODE_RAW; opts->key_inject_mode = SC_KEY_INJECT_MODE_RAW;
break; break;
case OPT_ROTATION: case OPT_ROTATION:
LOGE("--rotation has been removed, use --orientation or " LOGW("--rotation is deprecated, use --display-orientation "
"--capture-orientation instead."); "instead.");
return false; uint8_t rotation;
if (!parse_rotation(optarg, &rotation)) {
return false;
}
assert(rotation <= 3);
switch (rotation) {
case 0:
opts->display_orientation = SC_ORIENTATION_0;
break;
case 1:
// rotation 1 was 90° counterclockwise, but orientation
// is expressed clockwise
opts->display_orientation = SC_ORIENTATION_270;
break;
case 2:
opts->display_orientation = SC_ORIENTATION_180;
break;
case 3:
// rotation 3 was 270° counterclockwise, but orientation
// is expressed clockwise
opts->display_orientation = SC_ORIENTATION_90;
break;
}
break;
case OPT_DISPLAY_ORIENTATION: case OPT_DISPLAY_ORIENTATION:
if (!parse_orientation(optarg, &opts->display_orientation)) { if (!parse_orientation(optarg, &opts->display_orientation)) {
return false; return false;
@ -2529,9 +2488,23 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
} }
break; break;
case OPT_FORWARD_ALL_CLICKS: case OPT_FORWARD_ALL_CLICKS:
LOGE("--forward-all-clicks has been removed, " LOGW("--forward-all-clicks is deprecated, "
"use --mouse-bind=++++ instead."); "use --mouse-bind=++++ instead.");
return false; opts->mouse_bindings = (struct sc_mouse_bindings) {
.pri = {
.right_click = SC_MOUSE_BINDING_CLICK,
.middle_click = SC_MOUSE_BINDING_CLICK,
.click4 = SC_MOUSE_BINDING_CLICK,
.click5 = SC_MOUSE_BINDING_CLICK,
},
.sec = {
.right_click = SC_MOUSE_BINDING_CLICK,
.middle_click = SC_MOUSE_BINDING_CLICK,
.click4 = SC_MOUSE_BINDING_CLICK,
.click5 = SC_MOUSE_BINDING_CLICK,
},
};
break;
case OPT_LEGACY_PASTE: case OPT_LEGACY_PASTE:
opts->legacy_paste = true; opts->legacy_paste = true;
break; break;
@ -2539,11 +2512,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
opts->power_off_on_close = true; opts->power_off_on_close = true;
break; break;
case OPT_DISPLAY_BUFFER: case OPT_DISPLAY_BUFFER:
LOGE("--display-buffer has been removed, use --video-buffer " if (!parse_buffering_time(optarg, &opts->display_buffer)) {
"instead.");
return false;
case OPT_VIDEO_BUFFER:
if (!parse_buffering_time(optarg, &opts->video_buffer)) {
return false; return false;
} }
break; break;
@ -2626,9 +2595,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case OPT_LIST_CAMERA_SIZES: case OPT_LIST_CAMERA_SIZES:
opts->list |= SC_OPTION_LIST_CAMERA_SIZES; opts->list |= SC_OPTION_LIST_CAMERA_SIZES;
break; break;
case OPT_LIST_APPS:
opts->list |= SC_OPTION_LIST_APPS;
break;
case OPT_REQUIRE_AUDIO: case OPT_REQUIRE_AUDIO:
opts->require_audio = true; opts->require_audio = true;
break; break;
@ -2702,27 +2668,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false; return false;
} }
break; break;
case OPT_NEW_DISPLAY:
opts->new_display = optarg ? optarg : "";
break;
case OPT_START_APP:
opts->start_app = optarg;
break;
case OPT_SCREEN_OFF_TIMEOUT:
if (!parse_screen_off_timeout(optarg,
&opts->screen_off_timeout)) {
return false;
}
break;
case OPT_ANGLE:
opts->angle = optarg;
break;
case OPT_NO_VD_DESTROY_CONTENT:
opts->vd_destroy_content = false;
break;
case OPT_NO_VD_SYSTEM_DECORATIONS:
opts->vd_system_decorations = false;
break;
default: default:
// getopt prints the error message on stderr // getopt prints the error message on stderr
return false; return false;
@ -2817,6 +2762,13 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false; return false;
} }
if (opts->lock_video_orientation ==
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
LOGI("Video orientation is locked for v4l2 sink. "
"See --lock-video-orientation.");
opts->lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_INITIAL;
}
// V4L2 could not handle size change. // V4L2 could not handle size change.
// Do not log because downsizing on error is the default behavior, // Do not log because downsizing on error is the default behavior,
// not an explicit request from the user. // not an explicit request from the user.
@ -2824,7 +2776,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
} }
if (opts->v4l2_buffer && !opts->v4l2_device) { if (opts->v4l2_buffer && !opts->v4l2_device) {
LOGE("V4L2 buffer value without V4L2 sink"); LOGE("V4L2 buffer value without V4L2 sink\n");
return false; return false;
} }
#endif #endif
@ -2843,8 +2795,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
if (otg) { if (otg) {
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_AOA; opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_AOA;
} else if (!opts->video_playback) { } else if (!opts->video_playback) {
LOGI("No video mirroring, SDK mouse disabled"); LOGI("No video mirroring, mouse mode switched to UHID");
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_DISABLED; opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_UHID;
} else { } else {
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_SDK; opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_SDK;
} }
@ -2896,18 +2848,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
} }
} }
if (opts->new_display) {
if (opts->video_source != SC_VIDEO_SOURCE_DISPLAY) {
LOGE("--new-display is only available with --video-source=display");
return false;
}
if (!opts->video) {
LOGE("--new-display is incompatible with --no-video");
return false;
}
}
if (otg) { if (otg) {
if (!opts->control) { if (!opts->control) {
LOGE("--no-control is not allowed in OTG mode"); LOGE("--no-control is not allowed in OTG mode");
@ -3014,11 +2954,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false; return false;
} }
if (opts->display_id != 0 && opts->new_display) {
LOGE("Cannot specify both --display-id and --new-display");
return false;
}
if (opts->audio && opts->audio_source == SC_AUDIO_SOURCE_AUTO) { if (opts->audio && opts->audio_source == SC_AUDIO_SOURCE_AUTO) {
// Select the audio source according to the video source // Select the audio source according to the video source
if (opts->video_source == SC_VIDEO_SOURCE_DISPLAY) { if (opts->video_source == SC_VIDEO_SOURCE_DISPLAY) {
@ -3151,10 +3086,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
LOGE("Cannot request power off on close if control is disabled"); LOGE("Cannot request power off on close if control is disabled");
return false; return false;
} }
if (opts->start_app) {
LOGE("Cannot start an Android app if control is disabled");
return false;
}
} }
# ifdef _WIN32 # ifdef _WIN32

View File

@ -22,6 +22,9 @@
#define MOTIONEVENT_ACTION_LABEL(value) \ #define MOTIONEVENT_ACTION_LABEL(value) \
ENUM_TO_LABEL(android_motionevent_action_labels, value) ENUM_TO_LABEL(android_motionevent_action_labels, value)
#define SCREEN_POWER_MODE_LABEL(value) \
ENUM_TO_LABEL(screen_power_mode_labels, value)
static const char *const android_keyevent_action_labels[] = { static const char *const android_keyevent_action_labels[] = {
"down", "down",
"up", "up",
@ -44,6 +47,14 @@ static const char *const android_motionevent_action_labels[] = {
"btn-release", "btn-release",
}; };
static const char *const screen_power_mode_labels[] = {
"off",
"doze",
"normal",
"doze-suspend",
"suspend",
};
static const char *const copy_key_labels[] = { static const char *const copy_key_labels[] = {
"none", "none",
"copy", "copy",
@ -147,15 +158,13 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
size_t len = write_string(&buf[10], msg->set_clipboard.text, size_t len = write_string(&buf[10], msg->set_clipboard.text,
SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH); SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH);
return 10 + len; return 10 + len;
case SC_CONTROL_MSG_TYPE_SET_DISPLAY_POWER: case SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
buf[1] = msg->set_display_power.on; buf[1] = msg->set_screen_power_mode.mode;
return 2; return 2;
case SC_CONTROL_MSG_TYPE_UHID_CREATE: case SC_CONTROL_MSG_TYPE_UHID_CREATE:
sc_write16be(&buf[1], msg->uhid_create.id); sc_write16be(&buf[1], msg->uhid_create.id);
sc_write16be(&buf[3], msg->uhid_create.vendor_id);
sc_write16be(&buf[5], msg->uhid_create.product_id);
size_t index = 7; size_t index = 3;
index += write_string_tiny(&buf[index], msg->uhid_create.name, 127); index += write_string_tiny(&buf[index], msg->uhid_create.name, 127);
sc_write16be(&buf[index], msg->uhid_create.report_desc_size); sc_write16be(&buf[index], msg->uhid_create.report_desc_size);
@ -174,16 +183,11 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
case SC_CONTROL_MSG_TYPE_UHID_DESTROY: case SC_CONTROL_MSG_TYPE_UHID_DESTROY:
sc_write16be(&buf[1], msg->uhid_destroy.id); sc_write16be(&buf[1], msg->uhid_destroy.id);
return 3; return 3;
case SC_CONTROL_MSG_TYPE_START_APP: {
size_t len = write_string_tiny(&buf[1], msg->start_app.name, 255);
return 1 + len;
}
case SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL: case SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
case SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL: case SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL:
case SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS: case SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS:
case SC_CONTROL_MSG_TYPE_ROTATE_DEVICE: case SC_CONTROL_MSG_TYPE_ROTATE_DEVICE:
case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS: case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
case SC_CONTROL_MSG_TYPE_RESET_VIDEO:
// no additional data // no additional data
return 1; return 1;
default: default:
@ -260,9 +264,9 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
msg->set_clipboard.paste ? "paste" : "nopaste", msg->set_clipboard.paste ? "paste" : "nopaste",
msg->set_clipboard.text); msg->set_clipboard.text);
break; break;
case SC_CONTROL_MSG_TYPE_SET_DISPLAY_POWER: case SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
LOG_CMSG("display power %s", LOG_CMSG("power mode %s",
msg->set_display_power.on ? "on" : "off"); SCREEN_POWER_MODE_LABEL(msg->set_screen_power_mode.mode));
break; break;
case SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL: case SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
LOG_CMSG("expand notification panel"); LOG_CMSG("expand notification panel");
@ -280,13 +284,9 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
// Quote only if name is not null // Quote only if name is not null
const char *name = msg->uhid_create.name; const char *name = msg->uhid_create.name;
const char *quote = name ? "\"" : ""; const char *quote = name ? "\"" : "";
LOG_CMSG("UHID create [%" PRIu16 "] %04" PRIx16 ":%04" PRIx16 LOG_CMSG("UHID create [%" PRIu16 "] name=%s%s%s "
" name=%s%s%s report_desc_size=%" PRIu16, "report_desc_size=%" PRIu16, msg->uhid_create.id,
msg->uhid_create.id, quote, name, quote, msg->uhid_create.report_desc_size);
msg->uhid_create.vendor_id,
msg->uhid_create.product_id,
quote, name, quote,
msg->uhid_create.report_desc_size);
break; break;
} }
case SC_CONTROL_MSG_TYPE_UHID_INPUT: { case SC_CONTROL_MSG_TYPE_UHID_INPUT: {
@ -308,12 +308,6 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS: case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
LOG_CMSG("open hard keyboard settings"); LOG_CMSG("open hard keyboard settings");
break; break;
case SC_CONTROL_MSG_TYPE_START_APP:
LOG_CMSG("start app \"%s\"", msg->start_app.name);
break;
case SC_CONTROL_MSG_TYPE_RESET_VIDEO:
LOG_CMSG("reset video");
break;
default: default:
LOG_CMSG("unknown type: %u", (unsigned) msg->type); LOG_CMSG("unknown type: %u", (unsigned) msg->type);
break; break;
@ -339,9 +333,6 @@ sc_control_msg_destroy(struct sc_control_msg *msg) {
case SC_CONTROL_MSG_TYPE_SET_CLIPBOARD: case SC_CONTROL_MSG_TYPE_SET_CLIPBOARD:
free(msg->set_clipboard.text); free(msg->set_clipboard.text);
break; break;
case SC_CONTROL_MSG_TYPE_START_APP:
free(msg->start_app.name);
break;
default: default:
// do nothing // do nothing
break; break;

View File

@ -35,14 +35,18 @@ enum sc_control_msg_type {
SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS, SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS,
SC_CONTROL_MSG_TYPE_GET_CLIPBOARD, SC_CONTROL_MSG_TYPE_GET_CLIPBOARD,
SC_CONTROL_MSG_TYPE_SET_CLIPBOARD, SC_CONTROL_MSG_TYPE_SET_CLIPBOARD,
SC_CONTROL_MSG_TYPE_SET_DISPLAY_POWER, SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
SC_CONTROL_MSG_TYPE_ROTATE_DEVICE, SC_CONTROL_MSG_TYPE_ROTATE_DEVICE,
SC_CONTROL_MSG_TYPE_UHID_CREATE, SC_CONTROL_MSG_TYPE_UHID_CREATE,
SC_CONTROL_MSG_TYPE_UHID_INPUT, SC_CONTROL_MSG_TYPE_UHID_INPUT,
SC_CONTROL_MSG_TYPE_UHID_DESTROY, SC_CONTROL_MSG_TYPE_UHID_DESTROY,
SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS, SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS,
SC_CONTROL_MSG_TYPE_START_APP, };
SC_CONTROL_MSG_TYPE_RESET_VIDEO,
enum sc_screen_power_mode {
// see <https://android.googlesource.com/platform/frameworks/base.git/+/pie-release-2/core/java/android/view/SurfaceControl.java#305>
SC_SCREEN_POWER_MODE_OFF = 0,
SC_SCREEN_POWER_MODE_NORMAL = 2,
}; };
enum sc_copy_key { enum sc_copy_key {
@ -90,12 +94,10 @@ struct sc_control_msg {
bool paste; bool paste;
} set_clipboard; } set_clipboard;
struct { struct {
bool on; enum sc_screen_power_mode mode;
} set_display_power; } set_screen_power_mode;
struct { struct {
uint16_t id; uint16_t id;
uint16_t vendor_id;
uint16_t product_id;
const char *name; // pointer to static data const char *name; // pointer to static data
uint16_t report_desc_size; uint16_t report_desc_size;
const uint8_t *report_desc; // pointer to static data const uint8_t *report_desc; // pointer to static data
@ -108,9 +110,6 @@ struct sc_control_msg {
struct { struct {
uint16_t id; uint16_t id;
} uhid_destroy; } uhid_destroy;
struct {
char *name;
} start_app;
}; };
}; };

View File

@ -1,9 +1,11 @@
#include "decoder.h" #include "decoder.h"
#include <errno.h> #include <libavcodec/avcodec.h>
#include <libavcodec/packet.h> #include <libavformat/avformat.h>
#include <libavutil/avutil.h> #include <libavutil/channel_layout.h>
#include "events.h"
#include "trait/frame_sink.h"
#include "util/log.h" #include "util/log.h"
/** Downcast packet_sink to decoder */ /** Downcast packet_sink to decoder */

View File

@ -3,11 +3,13 @@
#include "common.h" #include "common.h"
#include <libavcodec/avcodec.h>
#include "trait/frame_source.h" #include "trait/frame_source.h"
#include "trait/packet_sink.h" #include "trait/packet_sink.h"
#include <stdbool.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
struct sc_decoder { struct sc_decoder {
struct sc_packet_sink packet_sink; // packet sink trait struct sc_packet_sink packet_sink; // packet sink trait
struct sc_frame_source frame_source; // frame source trait struct sc_frame_source frame_source; // frame source trait

View File

@ -2,7 +2,9 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include "util/log.h" #include "util/log.h"

View File

@ -4,7 +4,6 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <libavutil/frame.h>
#include "clock.h" #include "clock.h"
#include "trait/frame_source.h" #include "trait/frame_source.h"

View File

@ -1,11 +1,14 @@
#include "demuxer.h" #include "demuxer.h"
#include <assert.h> #include <assert.h>
#include <inttypes.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h> #include <libavutil/channel_layout.h>
#include <libavutil/time.h>
#include <unistd.h>
#include "decoder.h"
#include "events.h"
#include "packet_merger.h" #include "packet_merger.h"
#include "recorder.h"
#include "util/binary.h" #include "util/binary.h"
#include "util/log.h" #include "util/log.h"

View File

@ -4,8 +4,12 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include "trait/packet_source.h" #include "trait/packet_source.h"
#include "trait/packet_sink.h"
#include "util/net.h" #include "util/net.h"
#include "util/thread.h" #include "util/thread.h"

View File

@ -3,9 +3,9 @@
#include "common.h" #include "common.h"
#include <stddef.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <sys/types.h> #include <unistd.h>
#define DEVICE_MSG_MAX_SIZE (1 << 18) // 256k #define DEVICE_MSG_MAX_SIZE (1 << 18) // 256k
// type: 1 byte; length: 4 bytes // type: 1 byte; length: 4 bytes

View File

@ -1,8 +1,6 @@
#include "display.h" #include "display.h"
#include <assert.h> #include <assert.h>
#include <inttypes.h>
#include <string.h>
#include <libavutil/pixfmt.h> #include <libavutil/pixfmt.h>
#include "util/log.h" #include "util/log.h"

View File

@ -4,8 +4,7 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <libavformat/avformat.h>
#include <libavutil/frame.h>
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include "coords.h" #include "coords.h"

View File

@ -1,7 +1,5 @@
#include "events.h" #include "events.h"
#include <assert.h>
#include "util/log.h" #include "util/log.h"
#include "util/thread.h" #include "util/thread.h"

View File

@ -5,7 +5,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <SDL2/SDL_events.h> #include <SDL_events.h>
enum { enum {
SC_EVENT_NEW_FRAME = SDL_USEREVENT, SC_EVENT_NEW_FRAME = SDL_USEREVENT,

View File

@ -1,11 +1,11 @@
#include "file_pusher.h" #include "file_pusher.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "adb/adb.h" #include "adb/adb.h"
#include "util/log.h" #include "util/log.h"
#include "util/process_intr.h"
#define DEFAULT_PUSH_TARGET "/sdcard/Download/" #define DEFAULT_PUSH_TARGET "/sdcard/Download/"

View File

@ -1,7 +1,6 @@
#include "fps_counter.h" #include "fps_counter.h"
#include <assert.h> #include <assert.h>
#include <stdint.h>
#include "util/log.h" #include "util/log.h"

View File

@ -5,9 +5,9 @@
#include <stdatomic.h> #include <stdatomic.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include "util/thread.h" #include "util/thread.h"
#include "util/tick.h"
struct sc_fps_counter { struct sc_fps_counter {
sc_thread thread; sc_thread thread;

View File

@ -1,6 +1,8 @@
#include "frame_buffer.h" #include "frame_buffer.h"
#include <assert.h> #include <assert.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include "util/log.h" #include "util/log.h"

View File

@ -4,7 +4,6 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <libavutil/frame.h>
#include "util/thread.h" #include "util/thread.h"

View File

@ -3,7 +3,6 @@
#include "common.h" #include "common.h"
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#define SC_HID_MAX_SIZE 15 #define SC_HID_MAX_SIZE 15
@ -16,6 +15,7 @@ struct sc_hid_input {
struct sc_hid_open { struct sc_hid_open {
uint16_t hid_id; uint16_t hid_id;
const char *name; // pointer to static memory
const uint8_t *report_desc; // pointer to static memory const uint8_t *report_desc; // pointer to static memory
size_t report_desc_size; size_t report_desc_size;
}; };

View File

@ -2,8 +2,6 @@
#include <assert.h> #include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <stddef.h>
#include <sys/types.h>
#include "util/binary.h" #include "util/binary.h"
#include "util/log.h" #include "util/log.h"
@ -54,10 +52,10 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = {
0x09, 0x30, 0x09, 0x30,
// Usage (Y) Left stick y // Usage (Y) Left stick y
0x09, 0x31, 0x09, 0x31,
// Usage (Rx) Right stick x // Usage (Z) Right stick x
0x09, 0x33, 0x09, 0x32,
// Usage (Ry) Right stick y // Usage (Rz) Right stick y
0x09, 0x34, 0x09, 0x35,
// Logical Minimum (0) // Logical Minimum (0)
0x15, 0x00, 0x15, 0x00,
// Logical Maximum (65535) // Logical Maximum (65535)
@ -67,15 +65,15 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = {
0x75, 0x10, 0x75, 0x10,
// Report Count (4) // Report Count (4)
0x95, 0x04, 0x95, 0x04,
// Input (Data, Variable, Absolute): 4x2 bytes (X, Y, Z, Rz) // Input (Data, Variable, Absolute): 4 bytes (X, Y, Z, Rz)
0x81, 0x02, 0x81, 0x02,
// Usage Page (Generic Desktop) // Usage Page (Simulation Controls)
0x05, 0x01, 0x05, 0x02,
// Usage (Z) // Usage (Brake)
0x09, 0x32, 0x09, 0xC5,
// Usage (Rz) // Usage (Accelerator)
0x09, 0x35, 0x09, 0xC4,
// Logical Minimum (0) // Logical Minimum (0)
0x15, 0x00, 0x15, 0x00,
// Logical Maximum (32767) // Logical Maximum (32767)
@ -84,7 +82,7 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = {
0x75, 0x10, 0x75, 0x10,
// Report Count (2) // Report Count (2)
0x95, 0x02, 0x95, 0x02,
// Input (Data, Variable, Absolute): 2x2 bytes (L2, R2) // Input (Data, Variable, Absolute): 2 bytes (L2, R2)
0x81, 0x02, 0x81, 0x02,
// Usage Page (Buttons) // Usage Page (Buttons)
@ -184,7 +182,7 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = {
* `------------- SC_GAMEPAD_BUTTON_RIGHT_STICK * `------------- SC_GAMEPAD_BUTTON_RIGHT_STICK
* *
* +---------------+ * +---------------+
* byte 14: |0 0 0 0 . . . .| hat switch (dpad) position (0-8) * byte 14: |0 0 0 . . . . .| hat switch (dpad) position (0-8)
* +---------------+ * +---------------+
* 9 possible positions and their values: * 9 possible positions and their values:
* 8 1 2 * 8 1 2
@ -193,19 +191,16 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = {
* (8 is top-left, 1 is top, 2 is top-right, etc.) * (8 is top-left, 1 is top, 2 is top-right, etc.)
*/ */
// [-32768 to 32767] -> [0 to 65535]
#define AXIS_RESCALE(V) (uint16_t) (((int32_t) V) + 0x8000)
static void static void
sc_hid_gamepad_slot_init(struct sc_hid_gamepad_slot *slot, sc_hid_gamepad_slot_init(struct sc_hid_gamepad_slot *slot,
uint32_t gamepad_id) { uint32_t gamepad_id) {
assert(gamepad_id != SC_GAMEPAD_ID_INVALID); assert(gamepad_id != SC_GAMEPAD_ID_INVALID);
slot->gamepad_id = gamepad_id; slot->gamepad_id = gamepad_id;
slot->buttons = 0; slot->buttons = 0;
slot->axis_left_x = AXIS_RESCALE(0); slot->axis_left_x = 0;
slot->axis_left_y = AXIS_RESCALE(0); slot->axis_left_y = 0;
slot->axis_right_x = AXIS_RESCALE(0); slot->axis_right_x = 0;
slot->axis_right_y = AXIS_RESCALE(0); slot->axis_right_y = 0;
slot->axis_left_trigger = 0; slot->axis_left_trigger = 0;
slot->axis_right_trigger = 0; slot->axis_right_trigger = 0;
} }
@ -248,8 +243,14 @@ sc_hid_gamepad_generate_open(struct sc_hid_gamepad *hid,
sc_hid_gamepad_slot_init(&hid->slots[slot_idx], gamepad_id); sc_hid_gamepad_slot_init(&hid->slots[slot_idx], gamepad_id);
SDL_GameController* game_controller =
SDL_GameControllerFromInstanceID(gamepad_id);
assert(game_controller);
const char *name = SDL_GameControllerName(game_controller);
uint16_t hid_id = sc_hid_gamepad_slot_get_id(slot_idx); uint16_t hid_id = sc_hid_gamepad_slot_get_id(slot_idx);
hid_open->hid_id = hid_id; hid_open->hid_id = hid_id;
hid_open->name = name;
hid_open->report_desc = SC_HID_GAMEPAD_REPORT_DESC; hid_open->report_desc = SC_HID_GAMEPAD_REPORT_DESC;
hid_open->report_desc_size = sizeof(SC_HID_GAMEPAD_REPORT_DESC); hid_open->report_desc_size = sizeof(SC_HID_GAMEPAD_REPORT_DESC);
@ -422,6 +423,8 @@ sc_hid_gamepad_generate_input_from_axis(struct sc_hid_gamepad *hid,
struct sc_hid_gamepad_slot *slot = &hid->slots[slot_idx]; struct sc_hid_gamepad_slot *slot = &hid->slots[slot_idx];
// [-32768 to 32767] -> [0 to 65535]
#define AXIS_RESCALE(V) (uint16_t) (((int32_t) V) + 0x8000)
switch (event->axis) { switch (event->axis) {
case SC_GAMEPAD_AXIS_LEFTX: case SC_GAMEPAD_AXIS_LEFTX:
slot->axis_left_x = AXIS_RESCALE(event->value); slot->axis_left_x = AXIS_RESCALE(event->value);

View File

@ -4,7 +4,6 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include "hid/hid_event.h" #include "hid/hid_event.h"
#include "input_events.h" #include "input_events.h"

View File

@ -1,6 +1,5 @@
#include "hid_keyboard.h" #include "hid_keyboard.h"
#include <assert.h>
#include <string.h> #include <string.h>
#include "util/log.h" #include "util/log.h"
@ -336,6 +335,7 @@ sc_hid_keyboard_generate_input_from_mods(struct sc_hid_input *hid_input,
void sc_hid_keyboard_generate_open(struct sc_hid_open *hid_open) { void sc_hid_keyboard_generate_open(struct sc_hid_open *hid_open) {
hid_open->hid_id = SC_HID_ID_KEYBOARD; hid_open->hid_id = SC_HID_ID_KEYBOARD;
hid_open->name = NULL; // No name specified after "scrcpy"
hid_open->report_desc = SC_HID_KEYBOARD_REPORT_DESC; hid_open->report_desc = SC_HID_KEYBOARD_REPORT_DESC;
hid_open->report_desc_size = sizeof(SC_HID_KEYBOARD_REPORT_DESC); hid_open->report_desc_size = sizeof(SC_HID_KEYBOARD_REPORT_DESC);
} }

View File

@ -4,7 +4,6 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include "hid/hid_event.h" #include "hid/hid_event.h"
#include "input_events.h" #include "input_events.h"

View File

@ -1,7 +1,5 @@
#include "hid_mouse.h" #include "hid_mouse.h"
#include <stdint.h>
// 1 byte for buttons + padding, 1 byte for X position, 1 byte for Y position, // 1 byte for buttons + padding, 1 byte for X position, 1 byte for Y position,
// 1 byte for wheel motion // 1 byte for wheel motion
#define SC_HID_MOUSE_INPUT_SIZE 4 #define SC_HID_MOUSE_INPUT_SIZE 4
@ -192,6 +190,7 @@ sc_hid_mouse_generate_input_from_scroll(struct sc_hid_input *hid_input,
void sc_hid_mouse_generate_open(struct sc_hid_open *hid_open) { void sc_hid_mouse_generate_open(struct sc_hid_open *hid_open) {
hid_open->hid_id = SC_HID_ID_MOUSE; hid_open->hid_id = SC_HID_ID_MOUSE;
hid_open->name = NULL; // No name specified after "scrcpy"
hid_open->report_desc = SC_HID_MOUSE_REPORT_DESC; hid_open->report_desc = SC_HID_MOUSE_REPORT_DESC;
hid_open->report_desc_size = sizeof(SC_HID_MOUSE_REPORT_DESC); hid_open->report_desc_size = sizeof(SC_HID_MOUSE_REPORT_DESC);
} }

View File

@ -3,6 +3,8 @@
#include "common.h" #include "common.h"
#include <stdbool.h>
#include "hid/hid_event.h" #include "hid/hid_event.h"
#include "input_events.h" #include "input_events.h"

View File

@ -2,22 +2,16 @@
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/pixdesc.h> #include <libavutil/pixdesc.h>
#include <libavutil/pixfmt.h> #include <libavutil/pixfmt.h>
#include <SDL2/SDL.h>
#include "config.h" #include "config.h"
#include "util/env.h" #include "compat.h"
#ifdef PORTABLE #include "util/file.h"
# include "util/file.h"
#endif
#include "util/log.h" #include "util/log.h"
#include "util/str.h"
#define SCRCPY_PORTABLE_ICON_FILENAME "icon.png" #define SCRCPY_PORTABLE_ICON_FILENAME "icon.png"
#define SCRCPY_DEFAULT_ICON_PATH \ #define SCRCPY_DEFAULT_ICON_PATH \
@ -25,22 +19,35 @@
static char * static char *
get_icon_path(void) { get_icon_path(void) {
char *icon_path = sc_get_env("SCRCPY_ICON_PATH"); #ifdef __WINDOWS__
if (icon_path) { const wchar_t *icon_path_env = _wgetenv(L"SCRCPY_ICON_PATH");
#else
const char *icon_path_env = getenv("SCRCPY_ICON_PATH");
#endif
if (icon_path_env) {
// if the envvar is set, use it // if the envvar is set, use it
#ifdef __WINDOWS__
char *icon_path = sc_str_from_wchars(icon_path_env);
#else
char *icon_path = strdup(icon_path_env);
#endif
if (!icon_path) {
LOG_OOM();
return NULL;
}
LOGD("Using SCRCPY_ICON_PATH: %s", icon_path); LOGD("Using SCRCPY_ICON_PATH: %s", icon_path);
return icon_path; return icon_path;
} }
#ifndef PORTABLE #ifndef PORTABLE
LOGD("Using icon: " SCRCPY_DEFAULT_ICON_PATH); LOGD("Using icon: " SCRCPY_DEFAULT_ICON_PATH);
icon_path = strdup(SCRCPY_DEFAULT_ICON_PATH); char *icon_path = strdup(SCRCPY_DEFAULT_ICON_PATH);
if (!icon_path) { if (!icon_path) {
LOG_OOM(); LOG_OOM();
return NULL; return NULL;
} }
#else #else
icon_path = sc_file_get_local_path(SCRCPY_PORTABLE_ICON_FILENAME); char *icon_path = sc_file_get_local_path(SCRCPY_PORTABLE_ICON_FILENAME);
if (!icon_path) { if (!icon_path) {
LOGE("Could not get icon path"); LOGE("Could not get icon path");
return NULL; return NULL;

View File

@ -3,7 +3,9 @@
#include "common.h" #include "common.h"
#include <SDL2/SDL_surface.h> #include <stdbool.h>
#include <SDL2/SDL.h>
#include <libavformat/avformat.h>
SDL_Surface * SDL_Surface *
scrcpy_icon_load(void); scrcpy_icon_load(void);

View File

@ -9,6 +9,7 @@
#include <SDL2/SDL_events.h> #include <SDL2/SDL_events.h>
#include "coords.h" #include "coords.h"
#include "options.h"
/* The representation of input events in scrcpy is very close to the SDL API, /* The representation of input events in scrcpy is very close to the SDL API,
* for simplicity. * for simplicity.
@ -411,12 +412,18 @@ struct sc_touch_event {
float pressure; float pressure;
}; };
enum sc_gamepad_device_event_type {
SC_GAMEPAD_DEVICE_ADDED,
SC_GAMEPAD_DEVICE_REMOVED,
};
// As documented in <https://wiki.libsdl.org/SDL2/SDL_JoystickID>: // As documented in <https://wiki.libsdl.org/SDL2/SDL_JoystickID>:
// The ID value starts at 0 and increments from there. The value -1 is an // The ID value starts at 0 and increments from there. The value -1 is an
// invalid ID. // invalid ID.
#define SC_GAMEPAD_ID_INVALID UINT32_C(-1) #define SC_GAMEPAD_ID_INVALID UINT32_C(-1)
struct sc_gamepad_device_event { struct sc_gamepad_device_event {
enum sc_gamepad_device_event_type type;
uint32_t gamepad_id; uint32_t gamepad_id;
}; };
@ -496,6 +503,16 @@ sc_mouse_buttons_state_from_sdl(uint32_t buttons_state) {
return buttons_state; return buttons_state;
} }
static inline enum sc_gamepad_device_event_type
sc_gamepad_device_event_type_from_sdl_type(uint32_t type) {
assert(type == SDL_CONTROLLERDEVICEADDED
|| type == SDL_CONTROLLERDEVICEREMOVED);
if (type == SDL_CONTROLLERDEVICEADDED) {
return SC_GAMEPAD_DEVICE_ADDED;
}
return SC_GAMEPAD_DEVICE_REMOVED;
}
static inline enum sc_gamepad_axis static inline enum sc_gamepad_axis
sc_gamepad_axis_from_sdl(uint8_t axis) { sc_gamepad_axis_from_sdl(uint8_t axis) {
if (axis <= SDL_CONTROLLER_AXIS_TRIGGERRIGHT) { if (axis <= SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {

View File

@ -1,12 +1,8 @@
#include "input_manager.h" #include "input_manager.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <SDL2/SDL_keycode.h>
#include <string.h>
#include <SDL2/SDL.h>
#include "android/input.h"
#include "android/keycodes.h"
#include "input_events.h" #include "input_events.h"
#include "screen.h" #include "screen.h"
#include "shortcut_mod.h" #include "shortcut_mod.h"
@ -207,12 +203,13 @@ set_device_clipboard(struct sc_input_manager *im, bool paste,
} }
static void static void
set_display_power(struct sc_input_manager *im, bool on) { set_screen_power_mode(struct sc_input_manager *im,
enum sc_screen_power_mode mode) {
assert(im->controller); assert(im->controller);
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_SET_DISPLAY_POWER; msg.type = SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
msg.set_display_power.on = on; msg.set_screen_power_mode.mode = mode;
if (!sc_controller_push_msg(im->controller, &msg)) { if (!sc_controller_push_msg(im->controller, &msg)) {
LOGW("Could not request 'set screen power mode'"); LOGW("Could not request 'set screen power mode'");
@ -288,18 +285,6 @@ open_hard_keyboard_settings(struct sc_input_manager *im) {
} }
} }
static void
reset_video(struct sc_input_manager *im) {
assert(im->controller);
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_RESET_VIDEO;
if (!sc_controller_push_msg(im->controller, &msg)) {
LOGW("Could not request reset video");
}
}
static void static void
apply_orientation_transform(struct sc_input_manager *im, apply_orientation_transform(struct sc_input_manager *im,
enum sc_orientation transform) { enum sc_orientation transform) {
@ -430,8 +415,10 @@ sc_input_manager_process_key(struct sc_input_manager *im,
return; return;
case SDLK_o: case SDLK_o:
if (control && !repeat && down && !paused) { if (control && !repeat && down && !paused) {
bool on = shift; enum sc_screen_power_mode mode = shift
set_display_power(im, on); ? SC_SCREEN_POWER_MODE_NORMAL
: SC_SCREEN_POWER_MODE_OFF;
set_screen_power_mode(im, mode);
} }
return; return;
case SDLK_z: case SDLK_z:
@ -537,12 +524,8 @@ sc_input_manager_process_key(struct sc_input_manager *im,
} }
return; return;
case SDLK_r: case SDLK_r:
if (control && !repeat && down && !paused) { if (control && !shift && !repeat && down && !paused) {
if (shift) { rotate_device(im);
reset_video(im);
} else {
rotate_device(im);
}
} }
return; return;
case SDLK_k: case SDLK_k:
@ -912,6 +895,7 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
static void static void
sc_input_manager_process_gamepad_device(struct sc_input_manager *im, sc_input_manager_process_gamepad_device(struct sc_input_manager *im,
const SDL_ControllerDeviceEvent *event) { const SDL_ControllerDeviceEvent *event) {
SDL_JoystickID id;
if (event->type == SDL_CONTROLLERDEVICEADDED) { if (event->type == SDL_CONTROLLERDEVICEADDED) {
SDL_GameController *gc = SDL_GameControllerOpen(event->which); SDL_GameController *gc = SDL_GameControllerOpen(event->which);
if (!gc) { if (!gc) {
@ -926,12 +910,9 @@ sc_input_manager_process_gamepad_device(struct sc_input_manager *im,
return; return;
} }
struct sc_gamepad_device_event evt = { id = SDL_JoystickInstanceID(joystick);
.gamepad_id = SDL_JoystickInstanceID(joystick),
};
im->gp->ops->process_gamepad_added(im->gp, &evt);
} else if (event->type == SDL_CONTROLLERDEVICEREMOVED) { } else if (event->type == SDL_CONTROLLERDEVICEREMOVED) {
SDL_JoystickID id = event->which; id = event->which;
SDL_GameController *gc = SDL_GameControllerFromInstanceID(id); SDL_GameController *gc = SDL_GameControllerFromInstanceID(id);
if (gc) { if (gc) {
@ -939,15 +920,16 @@ sc_input_manager_process_gamepad_device(struct sc_input_manager *im,
} else { } else {
LOGW("Unknown gamepad device removed"); LOGW("Unknown gamepad device removed");
} }
struct sc_gamepad_device_event evt = {
.gamepad_id = id,
};
im->gp->ops->process_gamepad_removed(im->gp, &evt);
} else { } else {
// Nothing to do // Nothing to do
return; return;
} }
struct sc_gamepad_device_event evt = {
.type = sc_gamepad_device_event_type_from_sdl_type(event->type),
.gamepad_id = id,
};
im->gp->ops->process_gamepad_device(im->gp, &evt);
} }
static void static void

View File

@ -4,12 +4,12 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL_events.h> #include <SDL2/SDL.h>
#include <SDL2/SDL_keycode.h>
#include "controller.h" #include "controller.h"
#include "file_pusher.h" #include "file_pusher.h"
#include "fps_counter.h"
#include "options.h" #include "options.h"
#include "trait/gamepad_processor.h" #include "trait/gamepad_processor.h"
#include "trait/key_processor.h" #include "trait/key_processor.h"

View File

@ -1,13 +1,8 @@
#include "keyboard_sdk.h" #include "keyboard_sdk.h"
#include <assert.h> #include <assert.h>
#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "android/input.h" #include "android/input.h"
#include "android/keycodes.h"
#include "control_msg.h" #include "control_msg.h"
#include "controller.h" #include "controller.h"
#include "input_events.h" #include "input_events.h"

View File

@ -1,6 +1,9 @@
#include "common.h" #include "common.h"
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <unistd.h>
#include <libavformat/avformat.h>
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2
# include <libavdevice/avdevice.h> # include <libavdevice/avdevice.h>
#endif #endif

View File

@ -92,8 +92,8 @@ sc_mouse_capture_set_active(struct sc_mouse_capture *mc, bool capture) {
SDL_GetGlobalMouseState(&mouse_x, &mouse_y); SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
int x, y, w, h; int x, y, w, h;
SDL_GetWindowPosition(mc->window, &x, &y); SDL_GetWindowPosition(window, &x, &y);
SDL_GetWindowSize(mc->window, &w, &h); SDL_GetWindowSize(window, &w, &h);
bool outside_window = mouse_x < x || mouse_x >= x + w bool outside_window = mouse_x < x || mouse_x >= x + w
|| mouse_y < y || mouse_y >= y + h; || mouse_y < y || mouse_y >= y + h;

View File

@ -1,12 +1,12 @@
#include "mouse_sdk.h" #include "mouse_sdk.h"
#include <assert.h> #include <assert.h>
#include <stdint.h>
#include "android/input.h" #include "android/input.h"
#include "control_msg.h" #include "control_msg.h"
#include "controller.h" #include "controller.h"
#include "input_events.h" #include "input_events.h"
#include "util/intmap.h"
#include "util/log.h" #include "util/log.h"
/** Downcast mouse processor to sc_mouse_sdk */ /** Downcast mouse processor to sc_mouse_sdk */

View File

@ -6,6 +6,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "controller.h" #include "controller.h"
#include "screen.h"
#include "trait/mouse_processor.h" #include "trait/mouse_processor.h"
struct sc_mouse_sdk { struct sc_mouse_sdk {

View File

@ -2,8 +2,7 @@
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include "SDL2/SDL.h"
#include <SDL2/SDL.h>
void void
sc_opengl_init(struct sc_opengl *gl) { sc_opengl_init(struct sc_opengl *gl) {

View File

@ -1,7 +1,5 @@
#include "options.h" #include "options.h"
#include <stddef.h>
const struct scrcpy_options scrcpy_options_default = { const struct scrcpy_options scrcpy_options_default = {
.serial = NULL, .serial = NULL,
.crop = NULL, .crop = NULL,
@ -52,8 +50,7 @@ const struct scrcpy_options scrcpy_options_default = {
.video_bit_rate = 0, .video_bit_rate = 0,
.audio_bit_rate = 0, .audio_bit_rate = 0,
.max_fps = NULL, .max_fps = NULL,
.capture_orientation = SC_ORIENTATION_0, .lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_UNLOCKED,
.capture_orientation_lock = SC_ORIENTATION_UNLOCKED,
.display_orientation = SC_ORIENTATION_0, .display_orientation = SC_ORIENTATION_0,
.record_orientation = SC_ORIENTATION_0, .record_orientation = SC_ORIENTATION_0,
.window_x = SC_WINDOW_POSITION_UNDEFINED, .window_x = SC_WINDOW_POSITION_UNDEFINED,
@ -61,11 +58,10 @@ const struct scrcpy_options scrcpy_options_default = {
.window_width = 0, .window_width = 0,
.window_height = 0, .window_height = 0,
.display_id = 0, .display_id = 0,
.video_buffer = 0, .display_buffer = 0,
.audio_buffer = -1, // depends on the audio format, .audio_buffer = -1, // depends on the audio format,
.audio_output_buffer = SC_TICK_FROM_MS(5), .audio_output_buffer = SC_TICK_FROM_MS(5),
.time_limit = 0, .time_limit = 0,
.screen_off_timeout = -1,
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2
.v4l2_device = NULL, .v4l2_device = NULL,
.v4l2_buffer = 0, .v4l2_buffer = 0,
@ -107,11 +103,6 @@ const struct scrcpy_options scrcpy_options_default = {
.window = true, .window = true,
.mouse_hover = true, .mouse_hover = true,
.audio_dup = false, .audio_dup = false,
.new_display = NULL,
.start_app = NULL,
.angle = NULL,
.vd_destroy_content = true,
.vd_system_decorations = true,
}; };
enum sc_orientation enum sc_orientation

View File

@ -5,6 +5,7 @@
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include "util/tick.h" #include "util/tick.h"
@ -83,12 +84,6 @@ enum sc_orientation { // v v v
SC_ORIENTATION_FLIP_270, // 1 1 1 SC_ORIENTATION_FLIP_270, // 1 1 1
}; };
enum sc_orientation_lock {
SC_ORIENTATION_UNLOCKED,
SC_ORIENTATION_LOCKED_VALUE, // lock to specified orientation
SC_ORIENTATION_LOCKED_INITIAL, // lock to initial device orientation
};
static inline bool static inline bool
sc_orientation_is_mirror(enum sc_orientation orientation) { sc_orientation_is_mirror(enum sc_orientation orientation) {
assert(!(orientation & ~7)); assert(!(orientation & ~7));
@ -135,6 +130,16 @@ sc_orientation_get_name(enum sc_orientation orientation) {
} }
} }
enum sc_lock_video_orientation {
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED = -1,
// lock the current orientation when scrcpy starts
SC_LOCK_VIDEO_ORIENTATION_INITIAL = -2,
SC_LOCK_VIDEO_ORIENTATION_0 = 0,
SC_LOCK_VIDEO_ORIENTATION_90 = 3,
SC_LOCK_VIDEO_ORIENTATION_180 = 2,
SC_LOCK_VIDEO_ORIENTATION_270 = 1,
};
enum sc_keyboard_input_mode { enum sc_keyboard_input_mode {
SC_KEYBOARD_INPUT_MODE_AUTO, SC_KEYBOARD_INPUT_MODE_AUTO,
SC_KEYBOARD_INPUT_MODE_UHID_OR_AOA, // normal vs otg mode SC_KEYBOARD_INPUT_MODE_UHID_OR_AOA, // normal vs otg mode
@ -246,9 +251,7 @@ struct scrcpy_options {
uint32_t video_bit_rate; uint32_t video_bit_rate;
uint32_t audio_bit_rate; uint32_t audio_bit_rate;
const char *max_fps; // float to be parsed by the server const char *max_fps; // float to be parsed by the server
const char *angle; // float to be parsed by the server enum sc_lock_video_orientation lock_video_orientation;
enum sc_orientation capture_orientation;
enum sc_orientation_lock capture_orientation_lock;
enum sc_orientation display_orientation; enum sc_orientation display_orientation;
enum sc_orientation record_orientation; enum sc_orientation record_orientation;
int16_t window_x; // SC_WINDOW_POSITION_UNDEFINED for "auto" int16_t window_x; // SC_WINDOW_POSITION_UNDEFINED for "auto"
@ -256,11 +259,10 @@ struct scrcpy_options {
uint16_t window_width; uint16_t window_width;
uint16_t window_height; uint16_t window_height;
uint32_t display_id; uint32_t display_id;
sc_tick video_buffer; sc_tick display_buffer;
sc_tick audio_buffer; sc_tick audio_buffer;
sc_tick audio_output_buffer; sc_tick audio_output_buffer;
sc_tick time_limit; sc_tick time_limit;
sc_tick screen_off_timeout;
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2
const char *v4l2_device; const char *v4l2_device;
sc_tick v4l2_buffer; sc_tick v4l2_buffer;
@ -302,15 +304,10 @@ struct scrcpy_options {
#define SC_OPTION_LIST_DISPLAYS 0x2 #define SC_OPTION_LIST_DISPLAYS 0x2
#define SC_OPTION_LIST_CAMERAS 0x4 #define SC_OPTION_LIST_CAMERAS 0x4
#define SC_OPTION_LIST_CAMERA_SIZES 0x8 #define SC_OPTION_LIST_CAMERA_SIZES 0x8
#define SC_OPTION_LIST_APPS 0x10
uint8_t list; uint8_t list;
bool window; bool window;
bool mouse_hover; bool mouse_hover;
bool audio_dup; bool audio_dup;
const char *new_display; // [<width>x<height>][/<dpi>] parsed by the server
const char *start_app;
bool vd_destroy_content;
bool vd_system_decorations;
}; };
extern const struct scrcpy_options scrcpy_options_default; extern const struct scrcpy_options scrcpy_options_default;

View File

@ -1,9 +1,5 @@
#include "packet_merger.h" #include "packet_merger.h"
#include <stdlib.h>
#include <string.h>
#include <libavutil/avutil.h>
#include "util/log.h" #include "util/log.h"
void void

View File

@ -5,7 +5,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <libavcodec/packet.h> #include <libavcodec/avcodec.h>
/** /**
* Config packets (containing the SPS/PPS) are sent in-band. A new config * Config packets (containing the SPS/PPS) are sent in-band. A new config

View File

@ -2,6 +2,7 @@
#include <assert.h> #include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdint.h>
#include <SDL2/SDL_clipboard.h> #include <SDL2/SDL_clipboard.h>
#include "device_msg.h" #include "device_msg.h"

View File

@ -1,9 +1,6 @@
#include "recorder.h" #include "recorder.h"
#include <assert.h> #include <assert.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavutil/time.h> #include <libavutil/time.h>
@ -146,14 +143,8 @@ sc_recorder_open_output_file(struct sc_recorder *recorder) {
return false; return false;
} }
char *file_url = sc_str_concat("file:", recorder->filename); int ret = avio_open(&recorder->ctx->pb, recorder->filename,
if (!file_url) { AVIO_FLAG_WRITE);
avformat_free_context(recorder->ctx);
return false;
}
int ret = avio_open(&recorder->ctx->pb, file_url, AVIO_FLAG_WRITE);
free(file_url);
if (ret < 0) { if (ret < 0) {
LOGE("Failed to open output file: %s", recorder->filename); LOGE("Failed to open output file: %s", recorder->filename);
avformat_free_context(recorder->ctx); avformat_free_context(recorder->ctx);

View File

@ -4,10 +4,9 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <libavcodec/packet.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include "coords.h"
#include "options.h" #include "options.h"
#include "trait/packet_sink.h" #include "trait/packet_sink.h"
#include "util/thread.h" #include "util/thread.h"

View File

@ -1,11 +1,10 @@
#include "scrcpy.h" #include "scrcpy.h"
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <libavformat/avformat.h>
#include <sys/time.h>
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#ifdef _WIN32 #ifdef _WIN32
@ -38,9 +37,9 @@
#endif #endif
#include "util/acksync.h" #include "util/acksync.h"
#include "util/log.h" #include "util/log.h"
#include "util/net.h"
#include "util/rand.h" #include "util/rand.h"
#include "util/timeout.h" #include "util/timeout.h"
#include "util/tick.h"
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2
# include "v4l2_sink.h" # include "v4l2_sink.h"
#endif #endif
@ -54,7 +53,7 @@ struct scrcpy {
struct sc_decoder video_decoder; struct sc_decoder video_decoder;
struct sc_decoder audio_decoder; struct sc_decoder audio_decoder;
struct sc_recorder recorder; struct sc_recorder recorder;
struct sc_delay_buffer video_buffer; struct sc_delay_buffer display_buffer;
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2
struct sc_v4l2_sink v4l2_sink; struct sc_v4l2_sink v4l2_sink;
struct sc_delay_buffer v4l2_buffer; struct sc_delay_buffer v4l2_buffer;
@ -429,13 +428,9 @@ scrcpy(struct scrcpy_options *options) {
.video_bit_rate = options->video_bit_rate, .video_bit_rate = options->video_bit_rate,
.audio_bit_rate = options->audio_bit_rate, .audio_bit_rate = options->audio_bit_rate,
.max_fps = options->max_fps, .max_fps = options->max_fps,
.angle = options->angle, .lock_video_orientation = options->lock_video_orientation,
.screen_off_timeout = options->screen_off_timeout,
.capture_orientation = options->capture_orientation,
.capture_orientation_lock = options->capture_orientation_lock,
.control = options->control, .control = options->control,
.display_id = options->display_id, .display_id = options->display_id,
.new_display = options->new_display,
.video = options->video, .video = options->video,
.audio = options->audio, .audio = options->audio,
.audio_dup = options->audio_dup, .audio_dup = options->audio_dup,
@ -459,8 +454,6 @@ scrcpy(struct scrcpy_options *options) {
.power_on = options->power_on, .power_on = options->power_on,
.kill_adb_on_close = options->kill_adb_on_close, .kill_adb_on_close = options->kill_adb_on_close,
.camera_high_speed = options->camera_high_speed, .camera_high_speed = options->camera_high_speed,
.vd_destroy_content = options->vd_destroy_content,
.vd_system_decorations = options->vd_system_decorations,
.list = options->list, .list = options->list,
}; };
@ -821,11 +814,11 @@ aoa_complete:
if (options->video_playback) { if (options->video_playback) {
struct sc_frame_source *src = &s->video_decoder.frame_source; struct sc_frame_source *src = &s->video_decoder.frame_source;
if (options->video_buffer) { if (options->display_buffer) {
sc_delay_buffer_init(&s->video_buffer, sc_delay_buffer_init(&s->display_buffer,
options->video_buffer, true); options->display_buffer, true);
sc_frame_source_add_sink(src, &s->video_buffer.frame_sink); sc_frame_source_add_sink(src, &s->display_buffer.frame_sink);
src = &s->video_buffer.frame_source; src = &s->display_buffer.frame_source;
} }
sc_frame_source_add_sink(src, &s->screen.frame_sink); sc_frame_source_add_sink(src, &s->screen.frame_sink);
@ -879,11 +872,11 @@ aoa_complete:
// everything is set up // everything is set up
if (options->control && options->turn_screen_off) { if (options->control && options->turn_screen_off) {
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_SET_DISPLAY_POWER; msg.type = SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
msg.set_display_power.on = false; msg.set_screen_power_mode.mode = SC_SCREEN_POWER_MODE_OFF;
if (!sc_controller_push_msg(&s->controller, &msg)) { if (!sc_controller_push_msg(&s->controller, &msg)) {
LOGW("Could not request 'set display power'"); LOGW("Could not request 'set screen power mode'");
} }
} }
@ -913,25 +906,6 @@ aoa_complete:
init_sdl_gamepads(); init_sdl_gamepads();
} }
if (options->control && options->start_app) {
assert(controller);
char *name = strdup(options->start_app);
if (!name) {
LOG_OOM();
goto end;
}
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_START_APP;
msg.start_app.name = name;
if (!sc_controller_push_msg(controller, &msg)) {
LOGW("Could not request start app '%s'", name);
free(name);
}
}
ret = event_loop(s); ret = event_loop(s);
terminate_event_loop(); terminate_event_loop();
LOGD("quit..."); LOGD("quit...");

View File

@ -3,6 +3,7 @@
#include "common.h" #include "common.h"
#include <stdbool.h>
#include "options.h" #include "options.h"
enum scrcpy_exit_code { enum scrcpy_exit_code {

View File

@ -1,14 +1,11 @@
#ifndef SC_SCREEN_H #ifndef SCREEN_H
#define SC_SCREEN_H #define SCREEN_H
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <libavcodec/avcodec.h> #include <libavformat/avformat.h>
#include <libavutil/frame.h>
#include <libavutil/pixfmt.h>
#include "controller.h" #include "controller.h"
#include "coords.h" #include "coords.h"
@ -17,6 +14,7 @@
#include "frame_buffer.h" #include "frame_buffer.h"
#include "input_manager.h" #include "input_manager.h"
#include "mouse_capture.h" #include "mouse_capture.h"
#include "opengl.h"
#include "options.h" #include "options.h"
#include "trait/key_processor.h" #include "trait/key_processor.h"
#include "trait/frame_sink.h" #include "trait/frame_sink.h"

View File

@ -1,18 +1,18 @@
#include "server.h" #include "server.h"
#include <assert.h> #include <assert.h>
#include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <SDL2/SDL_timer.h>
#include <string.h> #include <SDL2/SDL_platform.h>
#include <sys/types.h>
#include "adb/adb.h" #include "adb/adb.h"
#include "util/env.h" #include "util/binary.h"
#include "util/file.h" #include "util/file.h"
#include "util/log.h" #include "util/log.h"
#include "util/net_intr.h" #include "util/net_intr.h"
#include "util/process.h" #include "util/process_intr.h"
#include "util/str.h" #include "util/str.h"
#define SC_SERVER_FILENAME "scrcpy-server" #define SC_SERVER_FILENAME "scrcpy-server"
@ -25,22 +25,35 @@
static char * static char *
get_server_path(void) { get_server_path(void) {
char *server_path = sc_get_env("SCRCPY_SERVER_PATH"); #ifdef __WINDOWS__
if (server_path) { const wchar_t *server_path_env = _wgetenv(L"SCRCPY_SERVER_PATH");
#else
const char *server_path_env = getenv("SCRCPY_SERVER_PATH");
#endif
if (server_path_env) {
// if the envvar is set, use it // if the envvar is set, use it
#ifdef __WINDOWS__
char *server_path = sc_str_from_wchars(server_path_env);
#else
char *server_path = strdup(server_path_env);
#endif
if (!server_path) {
LOG_OOM();
return NULL;
}
LOGD("Using SCRCPY_SERVER_PATH: %s", server_path); LOGD("Using SCRCPY_SERVER_PATH: %s", server_path);
return server_path; return server_path;
} }
#ifndef PORTABLE #ifndef PORTABLE
LOGD("Using server: " SC_SERVER_PATH_DEFAULT); LOGD("Using server: " SC_SERVER_PATH_DEFAULT);
server_path = strdup(SC_SERVER_PATH_DEFAULT); char *server_path = strdup(SC_SERVER_PATH_DEFAULT);
if (!server_path) { if (!server_path) {
LOG_OOM(); LOG_OOM();
return NULL; return NULL;
} }
#else #else
server_path = sc_file_get_local_path(SC_SERVER_FILENAME); char *server_path = sc_file_get_local_path(SC_SERVER_FILENAME);
if (!server_path) { if (!server_path) {
LOGE("Could not get local file path, " LOGE("Could not get local file path, "
"using " SC_SERVER_FILENAME " from current directory"); "using " SC_SERVER_FILENAME " from current directory");
@ -53,6 +66,56 @@ get_server_path(void) {
return server_path; return server_path;
} }
static void
sc_server_params_destroy(struct sc_server_params *params) {
// The server stores a copy of the params provided by the user
free((char *) params->req_serial);
free((char *) params->crop);
free((char *) params->video_codec_options);
free((char *) params->audio_codec_options);
free((char *) params->video_encoder);
free((char *) params->audio_encoder);
free((char *) params->tcpip_dst);
free((char *) params->camera_id);
free((char *) params->camera_ar);
}
static bool
sc_server_params_copy(struct sc_server_params *dst,
const struct sc_server_params *src) {
*dst = *src;
// The params reference user-allocated memory, so we must copy them to
// handle them from another thread
#define COPY(FIELD) do { \
dst->FIELD = NULL; \
if (src->FIELD) { \
dst->FIELD = strdup(src->FIELD); \
if (!dst->FIELD) { \
goto error; \
} \
} \
} while(0)
COPY(req_serial);
COPY(crop);
COPY(video_codec_options);
COPY(audio_codec_options);
COPY(video_encoder);
COPY(audio_encoder);
COPY(tcpip_dst);
COPY(camera_id);
COPY(camera_ar);
#undef COPY
return true;
error:
sc_server_params_destroy(dst);
return false;
}
static bool static bool
push_server(struct sc_intr *intr, const char *serial) { push_server(struct sc_intr *intr, const char *serial) {
char *server_path = get_server_path(); char *server_path = get_server_path();
@ -188,31 +251,18 @@ execute_server(struct sc_server *server,
cmd[count++] = "app_process"; cmd[count++] = "app_process";
#ifdef SERVER_DEBUGGER #ifdef SERVER_DEBUGGER
uint16_t sdk_version = sc_adb_get_device_sdk_version(&server->intr, serial);
if (!sdk_version) {
LOGE("Could not determine SDK version");
return 0;
}
# define SERVER_DEBUGGER_PORT "5005" # define SERVER_DEBUGGER_PORT "5005"
const char *dbg; cmd[count++] =
if (sdk_version < 28) { # ifdef SERVER_DEBUGGER_METHOD_NEW
// Android < 9 /* Android 9 and above */
dbg = "-agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address=" "-XjdwpProvider:internal -XjdwpOptions:transport=dt_socket,suspend=y,"
SERVER_DEBUGGER_PORT; "server=y,address="
} else if (sdk_version < 30) { # else
// Android >= 9 && Android < 11 /* Android 8 and below */
dbg = "-XjdwpProvider:internal -XjdwpOptions:transport=dt_socket," "-agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address="
"suspend=y,server=y,address=" SERVER_DEBUGGER_PORT; # endif
} else { SERVER_DEBUGGER_PORT;
// Android >= 11
// Contrary to the other methods, this does not suspend on start.
// <https://github.com/Genymobile/scrcpy/pull/5466>
dbg = "-XjdwpProvider:adbconnection";
}
cmd[count++] = dbg;
#endif #endif
cmd[count++] = "/"; // unused cmd[count++] = "/"; // unused
cmd[count++] = "com.genymobile.scrcpy.Server"; cmd[count++] = "com.genymobile.scrcpy.Server";
cmd[count++] = SCRCPY_VERSION; cmd[count++] = SCRCPY_VERSION;
@ -274,21 +324,9 @@ execute_server(struct sc_server *server,
VALIDATE_STRING(params->max_fps); VALIDATE_STRING(params->max_fps);
ADD_PARAM("max_fps=%s", params->max_fps); ADD_PARAM("max_fps=%s", params->max_fps);
} }
if (params->angle) { if (params->lock_video_orientation != SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
VALIDATE_STRING(params->angle); ADD_PARAM("lock_video_orientation=%" PRIi8,
ADD_PARAM("angle=%s", params->angle); params->lock_video_orientation);
}
if (params->capture_orientation_lock != SC_ORIENTATION_UNLOCKED
|| params->capture_orientation != SC_ORIENTATION_0) {
if (params->capture_orientation_lock == SC_ORIENTATION_LOCKED_INITIAL) {
ADD_PARAM("capture_orientation=@");
} else {
const char *orient =
sc_orientation_get_name(params->capture_orientation);
bool locked =
params->capture_orientation_lock != SC_ORIENTATION_UNLOCKED;
ADD_PARAM("capture_orientation=%s%s", locked ? "@" : "", orient);
}
} }
if (server->tunnel.forward) { if (server->tunnel.forward) {
ADD_PARAM("tunnel_forward=true"); ADD_PARAM("tunnel_forward=true");
@ -332,11 +370,6 @@ execute_server(struct sc_server *server,
if (params->stay_awake) { if (params->stay_awake) {
ADD_PARAM("stay_awake=true"); ADD_PARAM("stay_awake=true");
} }
if (params->screen_off_timeout != -1) {
assert(params->screen_off_timeout >= 0);
uint64_t ms = SC_TICK_TO_MS(params->screen_off_timeout);
ADD_PARAM("screen_off_timeout=%" PRIu64, ms);
}
if (params->video_codec_options) { if (params->video_codec_options) {
VALIDATE_STRING(params->video_codec_options); VALIDATE_STRING(params->video_codec_options);
ADD_PARAM("video_codec_options=%s", params->video_codec_options); ADD_PARAM("video_codec_options=%s", params->video_codec_options);
@ -372,16 +405,6 @@ execute_server(struct sc_server *server,
// By default, power_on is true // By default, power_on is true
ADD_PARAM("power_on=false"); ADD_PARAM("power_on=false");
} }
if (params->new_display) {
VALIDATE_STRING(params->new_display);
ADD_PARAM("new_display=%s", params->new_display);
}
if (!params->vd_destroy_content) {
ADD_PARAM("vd_destroy_content=false");
}
if (!params->vd_system_decorations) {
ADD_PARAM("vd_system_decorations=false");
}
if (params->list & SC_OPTION_LIST_ENCODERS) { if (params->list & SC_OPTION_LIST_ENCODERS) {
ADD_PARAM("list_encoders=true"); ADD_PARAM("list_encoders=true");
} }
@ -394,23 +417,16 @@ execute_server(struct sc_server *server,
if (params->list & SC_OPTION_LIST_CAMERA_SIZES) { if (params->list & SC_OPTION_LIST_CAMERA_SIZES) {
ADD_PARAM("list_camera_sizes=true"); ADD_PARAM("list_camera_sizes=true");
} }
if (params->list & SC_OPTION_LIST_APPS) {
ADD_PARAM("list_apps=true");
}
#undef ADD_PARAM #undef ADD_PARAM
cmd[count++] = NULL; cmd[count++] = NULL;
#ifdef SERVER_DEBUGGER #ifdef SERVER_DEBUGGER
LOGI("Server debugger listening%s...", LOGI("Server debugger waiting for a client on device port "
sdk_version < 30 ? " on port " SERVER_DEBUGGER_PORT : ""); SERVER_DEBUGGER_PORT "...");
// For Android < 11, from the computer: // From the computer, run
// - run `adb forward tcp:5005 tcp:5005` // adb forward tcp:5005 tcp:5005
// For Android >= 11:
// - execute `adb jdwp` to get the jdwp port
// - run `adb forward tcp:5005 jdwp:XXXX` (replace XXXX)
//
// Then, from Android Studio: Run > Debug > Edit configurations... // Then, from Android Studio: Run > Debug > Edit configurations...
// On the left, click on '+', "Remote", with: // On the left, click on '+', "Remote", with:
// Host: localhost // Host: localhost
@ -483,25 +499,22 @@ connect_to_server(struct sc_server *server, unsigned attempts, sc_tick delay,
bool bool
sc_server_init(struct sc_server *server, const struct sc_server_params *params, sc_server_init(struct sc_server *server, const struct sc_server_params *params,
const struct sc_server_callbacks *cbs, void *cbs_userdata) { const struct sc_server_callbacks *cbs, void *cbs_userdata) {
// The allocated data in params (const char *) must remain valid until the bool ok = sc_server_params_copy(&server->params, params);
// end of the program
server->params = *params;
bool ok = sc_adb_init();
if (!ok) { if (!ok) {
LOG_OOM();
return false; return false;
} }
ok = sc_mutex_init(&server->mutex); ok = sc_mutex_init(&server->mutex);
if (!ok) { if (!ok) {
sc_adb_destroy(); sc_server_params_destroy(&server->params);
return false; return false;
} }
ok = sc_cond_init(&server->cond_stopped); ok = sc_cond_init(&server->cond_stopped);
if (!ok) { if (!ok) {
sc_mutex_destroy(&server->mutex); sc_mutex_destroy(&server->mutex);
sc_adb_destroy(); sc_server_params_destroy(&server->params);
return false; return false;
} }
@ -509,7 +522,7 @@ sc_server_init(struct sc_server *server, const struct sc_server_params *params,
if (!ok) { if (!ok) {
sc_cond_destroy(&server->cond_stopped); sc_cond_destroy(&server->cond_stopped);
sc_mutex_destroy(&server->mutex); sc_mutex_destroy(&server->mutex);
sc_adb_destroy(); sc_server_params_destroy(&server->params);
return false; return false;
} }
@ -831,14 +844,11 @@ sc_server_switch_to_tcpip(struct sc_server *server, const char *serial) {
} }
static bool static bool
sc_server_connect_to_tcpip(struct sc_server *server, const char *ip_port, sc_server_connect_to_tcpip(struct sc_server *server, const char *ip_port) {
bool disconnect) {
struct sc_intr *intr = &server->intr; struct sc_intr *intr = &server->intr;
if (disconnect) { // Error expected if not connected, do not report any error
// Error expected if not connected, do not report any error sc_adb_disconnect(intr, ip_port, SC_ADB_SILENT);
sc_adb_disconnect(intr, ip_port, SC_ADB_SILENT);
}
LOGI("Connecting to %s...", ip_port); LOGI("Connecting to %s...", ip_port);
@ -854,7 +864,7 @@ sc_server_connect_to_tcpip(struct sc_server *server, const char *ip_port,
static bool static bool
sc_server_configure_tcpip_known_address(struct sc_server *server, sc_server_configure_tcpip_known_address(struct sc_server *server,
const char *addr, bool disconnect) { const char *addr) {
// Append ":5555" if no port is present // Append ":5555" if no port is present
bool contains_port = strchr(addr, ':'); bool contains_port = strchr(addr, ':');
char *ip_port = contains_port ? strdup(addr) char *ip_port = contains_port ? strdup(addr)
@ -865,7 +875,7 @@ sc_server_configure_tcpip_known_address(struct sc_server *server,
} }
server->serial = ip_port; server->serial = ip_port;
return sc_server_connect_to_tcpip(server, ip_port, disconnect); return sc_server_connect_to_tcpip(server, ip_port);
} }
static bool static bool
@ -890,7 +900,7 @@ sc_server_configure_tcpip_unknown_address(struct sc_server *server,
} }
server->serial = ip_port; server->serial = ip_port;
return sc_server_connect_to_tcpip(server, ip_port, false); return sc_server_connect_to_tcpip(server, ip_port);
} }
static void static void
@ -977,13 +987,7 @@ run_server(void *data) {
sc_adb_device_destroy(&device); sc_adb_device_destroy(&device);
} }
} else { } else {
// If the user passed a '+' (--tcpip=+ip), then disconnect first ok = sc_server_configure_tcpip_known_address(server, params->tcpip_dst);
const char *tcpip_dst = params->tcpip_dst;
bool plus = tcpip_dst[0] == '+';
if (plus) {
++tcpip_dst;
}
ok = sc_server_configure_tcpip_known_address(server, tcpip_dst, plus);
if (!ok) { if (!ok) {
goto error_connection_failed; goto error_connection_failed;
} }
@ -1157,9 +1161,8 @@ sc_server_destroy(struct sc_server *server) {
free(server->serial); free(server->serial);
free(server->device_socket_name); free(server->device_socket_name);
sc_server_params_destroy(&server->params);
sc_intr_destroy(&server->intr); sc_intr_destroy(&server->intr);
sc_cond_destroy(&server->cond_stopped); sc_cond_destroy(&server->cond_stopped);
sc_mutex_destroy(&server->mutex); sc_mutex_destroy(&server->mutex);
sc_adb_destroy();
} }

View File

@ -1,17 +1,19 @@
#ifndef SC_SERVER_H #ifndef SERVER_H
#define SC_SERVER_H #define SERVER_H
#include "common.h" #include "common.h"
#include <stdatomic.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include "adb/adb_tunnel.h" #include "adb/adb_tunnel.h"
#include "coords.h"
#include "options.h" #include "options.h"
#include "util/intr.h" #include "util/intr.h"
#include "util/log.h"
#include "util/net.h" #include "util/net.h"
#include "util/thread.h" #include "util/thread.h"
#include "util/tick.h"
#define SC_DEVICE_NAME_FIELD_LENGTH 64 #define SC_DEVICE_NAME_FIELD_LENGTH 64
struct sc_server_info { struct sc_server_info {
@ -43,13 +45,9 @@ struct sc_server_params {
uint32_t video_bit_rate; uint32_t video_bit_rate;
uint32_t audio_bit_rate; uint32_t audio_bit_rate;
const char *max_fps; // float to be parsed by the server const char *max_fps; // float to be parsed by the server
const char *angle; // float to be parsed by the server int8_t lock_video_orientation;
sc_tick screen_off_timeout;
enum sc_orientation capture_orientation;
enum sc_orientation_lock capture_orientation_lock;
bool control; bool control;
uint32_t display_id; uint32_t display_id;
const char *new_display;
bool video; bool video;
bool audio; bool audio;
bool audio_dup; bool audio_dup;
@ -67,8 +65,6 @@ struct sc_server_params {
bool power_on; bool power_on;
bool kill_adb_on_close; bool kill_adb_on_close;
bool camera_high_speed; bool camera_high_speed;
bool vd_destroy_content;
bool vd_system_decorations;
uint8_t list; uint8_t list;
}; };

View File

@ -3,7 +3,6 @@
#include "common.h" #include "common.h"
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <SDL2/SDL_keycode.h> #include <SDL2/SDL_keycode.h>

View File

@ -1,15 +1,11 @@
#include "util/file.h" #include "util/file.h"
#include <limits.h> #include <limits.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#ifdef __APPLE__
# include <mach-o/dyld.h> // for _NSGetExecutablePath()
#endif
#include "util/log.h" #include "util/log.h"
@ -64,22 +60,11 @@ sc_file_get_executable_path(void) {
} }
buf[len] = '\0'; buf[len] = '\0';
return strdup(buf); return strdup(buf);
#elif defined(__APPLE__)
char buf[PATH_MAX];
uint32_t bufsize = PATH_MAX;
if (_NSGetExecutablePath(buf, &bufsize) != 0) {
LOGE("Executable path buffer too small; need %u bytes", bufsize);
return NULL;
}
return realpath(buf, NULL);
#else #else
// "_" is often used to store the full path of the command being executed // in practice, we only need this feature for portable builds, only used on
char *path = getenv("_"); // Windows, so we don't care implementing it for every platform
if (!path) { // (it's useful to have a working version on Linux for debugging though)
LOGE("Could not determine executable path"); return NULL;
return NULL;
}
return strdup(path);
#endif #endif
} }

View File

@ -4,8 +4,6 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <signal.h> #include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>

View File

@ -3,6 +3,7 @@
#include "common.h" #include "common.h"
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>

View File

@ -1,7 +1,5 @@
#include "frame_source.h" #include "frame_source.h"
#include <assert.h>
void void
sc_frame_source_init(struct sc_frame_source *source) { sc_frame_source_init(struct sc_frame_source *source) {
source->sink_count = 0; source->sink_count = 0;

View File

@ -3,9 +3,7 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include "frame_sink.h"
#include "trait/frame_sink.h"
#define SC_FRAME_SOURCE_MAX_SINKS 2 #define SC_FRAME_SOURCE_MAX_SINKS 2

View File

@ -3,6 +3,9 @@
#include "common.h" #include "common.h"
#include <assert.h>
#include <stdbool.h>
#include "input_events.h" #include "input_events.h"
/** /**
@ -17,22 +20,13 @@ struct sc_gamepad_processor {
struct sc_gamepad_processor_ops { struct sc_gamepad_processor_ops {
/** /**
* Process a gamepad device added event * Process a gamepad device added or removed
* *
* This function is mandatory. * This function is mandatory.
*/ */
void void
(*process_gamepad_added)(struct sc_gamepad_processor *gp, (*process_gamepad_device)(struct sc_gamepad_processor *gp,
const struct sc_gamepad_device_event *event); const struct sc_gamepad_device_event *event);
/**
* Process a gamepad device removed event
*
* This function is mandatory.
*/
void
(*process_gamepad_removed)(struct sc_gamepad_processor *gp,
const struct sc_gamepad_device_event *event);
/** /**
* Process a gamepad axis event * Process a gamepad axis event

View File

@ -3,6 +3,7 @@
#include "common.h" #include "common.h"
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include "input_events.h" #include "input_events.h"

View File

@ -3,6 +3,7 @@
#include "common.h" #include "common.h"
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include "input_events.h" #include "input_events.h"

View File

@ -3,6 +3,7 @@
#include "common.h" #include "common.h"
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>

View File

@ -1,7 +1,5 @@
#include "packet_source.h" #include "packet_source.h"
#include <assert.h>
void void
sc_packet_source_init(struct sc_packet_source *source) { sc_packet_source_init(struct sc_packet_source *source) {
source->sink_count = 0; source->sink_count = 0;

View File

@ -3,9 +3,7 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include "packet_sink.h"
#include "trait/packet_sink.h"
#define SC_PACKET_SOURCE_MAX_SINKS 2 #define SC_PACKET_SOURCE_MAX_SINKS 2

View File

@ -1,10 +1,5 @@
#include "gamepad_uhid.h" #include "gamepad_uhid.h"
#include <assert.h>
#include <inttypes.h>
#include <string.h>
#include <SDL2/SDL_gamecontroller.h>
#include "hid/hid_gamepad.h" #include "hid/hid_gamepad.h"
#include "input_events.h" #include "input_events.h"
#include "util/log.h" #include "util/log.h"
@ -12,11 +7,6 @@
/** Downcast gamepad processor to sc_gamepad_uhid */ /** Downcast gamepad processor to sc_gamepad_uhid */
#define DOWNCAST(GP) container_of(GP, struct sc_gamepad_uhid, gamepad_processor) #define DOWNCAST(GP) container_of(GP, struct sc_gamepad_uhid, gamepad_processor)
// Xbox 360
#define SC_GAMEPAD_UHID_VENDOR_ID UINT16_C(0x045e)
#define SC_GAMEPAD_UHID_PRODUCT_ID UINT16_C(0x028e)
#define SC_GAMEPAD_UHID_NAME "Microsoft X-Box 360 Pad"
static void static void
sc_gamepad_uhid_send_input(struct sc_gamepad_uhid *gamepad, sc_gamepad_uhid_send_input(struct sc_gamepad_uhid *gamepad,
const struct sc_hid_input *hid_input, const struct sc_hid_input *hid_input,
@ -40,9 +30,7 @@ sc_gamepad_uhid_send_open(struct sc_gamepad_uhid *gamepad,
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE; msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE;
msg.uhid_create.id = hid_open->hid_id; msg.uhid_create.id = hid_open->hid_id;
msg.uhid_create.vendor_id = SC_GAMEPAD_UHID_VENDOR_ID; msg.uhid_create.name = hid_open->name;
msg.uhid_create.product_id = SC_GAMEPAD_UHID_PRODUCT_ID;
msg.uhid_create.name = SC_GAMEPAD_UHID_NAME;
msg.uhid_create.report_desc = hid_open->report_desc; msg.uhid_create.report_desc = hid_open->report_desc;
msg.uhid_create.report_desc_size = hid_open->report_desc_size; msg.uhid_create.report_desc_size = hid_open->report_desc_size;
@ -64,39 +52,29 @@ sc_gamepad_uhid_send_close(struct sc_gamepad_uhid *gamepad,
} }
static void static void
sc_gamepad_processor_process_gamepad_added(struct sc_gamepad_processor *gp, sc_gamepad_processor_process_gamepad_device(struct sc_gamepad_processor *gp,
const struct sc_gamepad_device_event *event) { const struct sc_gamepad_device_event *event) {
struct sc_gamepad_uhid *gamepad = DOWNCAST(gp); struct sc_gamepad_uhid *gamepad = DOWNCAST(gp);
struct sc_hid_open hid_open; if (event->type == SC_GAMEPAD_DEVICE_ADDED) {
if (!sc_hid_gamepad_generate_open(&gamepad->hid, &hid_open, struct sc_hid_open hid_open;
event->gamepad_id)) { if (!sc_hid_gamepad_generate_open(&gamepad->hid, &hid_open,
return; event->gamepad_id)) {
return;
}
sc_gamepad_uhid_send_open(gamepad, &hid_open);
} else {
assert(event->type == SC_GAMEPAD_DEVICE_REMOVED);
struct sc_hid_close hid_close;
if (!sc_hid_gamepad_generate_close(&gamepad->hid, &hid_close,
event->gamepad_id)) {
return;
}
sc_gamepad_uhid_send_close(gamepad, &hid_close);
} }
SDL_GameController* game_controller =
SDL_GameControllerFromInstanceID(event->gamepad_id);
assert(game_controller);
const char *name = SDL_GameControllerName(game_controller);
LOGI("Gamepad added: [%" PRIu32 "] %s", event->gamepad_id, name);
sc_gamepad_uhid_send_open(gamepad, &hid_open);
}
static void
sc_gamepad_processor_process_gamepad_removed(struct sc_gamepad_processor *gp,
const struct sc_gamepad_device_event *event) {
struct sc_gamepad_uhid *gamepad = DOWNCAST(gp);
struct sc_hid_close hid_close;
if (!sc_hid_gamepad_generate_close(&gamepad->hid, &hid_close,
event->gamepad_id)) {
return;
}
LOGI("Gamepad removed: [%" PRIu32 "]", event->gamepad_id);
sc_gamepad_uhid_send_close(gamepad, &hid_close);
} }
static void static void
@ -136,8 +114,7 @@ sc_gamepad_uhid_init(struct sc_gamepad_uhid *gamepad,
gamepad->controller = controller; gamepad->controller = controller;
static const struct sc_gamepad_processor_ops ops = { static const struct sc_gamepad_processor_ops ops = {
.process_gamepad_added = sc_gamepad_processor_process_gamepad_added, .process_gamepad_device = sc_gamepad_processor_process_gamepad_device,
.process_gamepad_removed = sc_gamepad_processor_process_gamepad_removed,
.process_gamepad_axis = sc_gamepad_processor_process_gamepad_axis, .process_gamepad_axis = sc_gamepad_processor_process_gamepad_axis,
.process_gamepad_button = sc_gamepad_processor_process_gamepad_button, .process_gamepad_button = sc_gamepad_processor_process_gamepad_button,
}; };

View File

@ -3,6 +3,8 @@
#include "common.h" #include "common.h"
#include <stdbool.h>
#include "controller.h" #include "controller.h"
#include "hid/hid_gamepad.h" #include "hid/hid_gamepad.h"
#include "trait/gamepad_processor.h" #include "trait/gamepad_processor.h"

View File

@ -1,12 +1,6 @@
#include "keyboard_uhid.h" #include "keyboard_uhid.h"
#include <assert.h>
#include <string.h>
#include <SDL2/SDL_keyboard.h>
#include <SDL2/SDL_keycode.h>
#include "util/log.h" #include "util/log.h"
#include "util/thread.h"
/** Downcast key processor to keyboard_uhid */ /** Downcast key processor to keyboard_uhid */
#define DOWNCAST(KP) container_of(KP, struct sc_keyboard_uhid, key_processor) #define DOWNCAST(KP) container_of(KP, struct sc_keyboard_uhid, key_processor)
@ -147,9 +141,7 @@ sc_keyboard_uhid_init(struct sc_keyboard_uhid *kb,
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE; msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE;
msg.uhid_create.id = SC_HID_ID_KEYBOARD; msg.uhid_create.id = SC_HID_ID_KEYBOARD;
msg.uhid_create.vendor_id = 0; msg.uhid_create.name = hid_open.name;
msg.uhid_create.product_id = 0;
msg.uhid_create.name = NULL;
msg.uhid_create.report_desc = hid_open.report_desc; msg.uhid_create.report_desc = hid_open.report_desc;
msg.uhid_create.report_desc_size = hid_open.report_desc_size; msg.uhid_create.report_desc_size = hid_open.report_desc_size;
if (!sc_controller_push_msg(controller, &msg)) { if (!sc_controller_push_msg(controller, &msg)) {

View File

@ -1,8 +1,5 @@
#include "mouse_uhid.h" #include "mouse_uhid.h"
#include <assert.h>
#include <string.h>
#include "hid/hid_mouse.h" #include "hid/hid_mouse.h"
#include "input_events.h" #include "input_events.h"
#include "util/log.h" #include "util/log.h"
@ -84,9 +81,7 @@ sc_mouse_uhid_init(struct sc_mouse_uhid *mouse,
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE; msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE;
msg.uhid_create.id = SC_HID_ID_MOUSE; msg.uhid_create.id = SC_HID_ID_MOUSE;
msg.uhid_create.vendor_id = 0; msg.uhid_create.name = hid_open.name;
msg.uhid_create.product_id = 0;
msg.uhid_create.name = NULL;
msg.uhid_create.report_desc = hid_open.report_desc; msg.uhid_create.report_desc = hid_open.report_desc;
msg.uhid_create.report_desc_size = hid_open.report_desc_size; msg.uhid_create.report_desc_size = hid_open.report_desc_size;
if (!sc_controller_push_msg(controller, &msg)) { if (!sc_controller_push_msg(controller, &msg)) {

View File

@ -1,5 +1,6 @@
#include "uhid_output.h" #include "uhid_output.h"
#include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include "uhid/keyboard_uhid.h" #include "uhid/keyboard_uhid.h"

View File

@ -3,7 +3,7 @@
#include "common.h" #include "common.h"
#include <stddef.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
/** /**

View File

@ -1,16 +1,13 @@
#include "aoa_hid.h" #include "util/log.h"
#include <assert.h> #include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <libusb-1.0/libusb.h>
#include "aoa_hid.h"
#include "events.h" #include "events.h"
#include "util/log.h" #include "util/log.h"
#include "util/str.h" #include "util/str.h"
#include "util/tick.h"
#include "util/vector.h" #include "util/vector.h"
// See <https://source.android.com/devices/accessories/aoa2#hid-support>. // See <https://source.android.com/devices/accessories/aoa2#hid-support>.

View File

@ -3,13 +3,16 @@
#include "common.h" #include "common.h"
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <libusb-1.0/libusb.h>
#include "hid/hid_event.h" #include "hid/hid_event.h"
#include "usb/usb.h" #include "usb.h"
#include "util/acksync.h" #include "util/acksync.h"
#include "util/thread.h" #include "util/thread.h"
#include "util/tick.h"
#include "util/vecdeque.h" #include "util/vecdeque.h"
enum sc_aoa_event_type { enum sc_aoa_event_type {

View File

@ -1,7 +1,5 @@
#include "gamepad_aoa.h" #include "gamepad_aoa.h"
#include <stdbool.h>
#include "input_events.h" #include "input_events.h"
#include "util/log.h" #include "util/log.h"
@ -9,35 +7,33 @@
#define DOWNCAST(GP) container_of(GP, struct sc_gamepad_aoa, gamepad_processor) #define DOWNCAST(GP) container_of(GP, struct sc_gamepad_aoa, gamepad_processor)
static void static void
sc_gamepad_processor_process_gamepad_added(struct sc_gamepad_processor *gp, sc_gamepad_processor_process_gamepad_device(struct sc_gamepad_processor *gp,
const struct sc_gamepad_device_event *event) { const struct sc_gamepad_device_event *event) {
struct sc_gamepad_aoa *gamepad = DOWNCAST(gp); struct sc_gamepad_aoa *gamepad = DOWNCAST(gp);
struct sc_hid_open hid_open; if (event->type == SC_GAMEPAD_DEVICE_ADDED) {
if (!sc_hid_gamepad_generate_open(&gamepad->hid, &hid_open, struct sc_hid_open hid_open;
event->gamepad_id)) { if (!sc_hid_gamepad_generate_open(&gamepad->hid, &hid_open,
return; event->gamepad_id)) {
} return;
}
// exit_on_error: false (a gamepad open failure should not exit scrcpy) // exit_on_error: false (a gamepad open failure should not exit scrcpy)
if (!sc_aoa_push_open(gamepad->aoa, &hid_open, false)) { if (!sc_aoa_push_open(gamepad->aoa, &hid_open, false)) {
LOGW("Could not push AOA HID open (gamepad)"); LOGW("Could not push AOA HID open (gamepad)");
} }
} } else {
assert(event->type == SC_GAMEPAD_DEVICE_REMOVED);
static void struct sc_hid_close hid_close;
sc_gamepad_processor_process_gamepad_removed(struct sc_gamepad_processor *gp, if (!sc_hid_gamepad_generate_close(&gamepad->hid, &hid_close,
const struct sc_gamepad_device_event *event) { event->gamepad_id)) {
struct sc_gamepad_aoa *gamepad = DOWNCAST(gp); return;
}
struct sc_hid_close hid_close; if (!sc_aoa_push_close(gamepad->aoa, &hid_close)) {
if (!sc_hid_gamepad_generate_close(&gamepad->hid, &hid_close, LOGW("Could not push AOA HID close (gamepad)");
event->gamepad_id)) { }
return;
}
if (!sc_aoa_push_close(gamepad->aoa, &hid_close)) {
LOGW("Could not push AOA HID close (gamepad)");
} }
} }
@ -80,8 +76,7 @@ sc_gamepad_aoa_init(struct sc_gamepad_aoa *gamepad, struct sc_aoa *aoa) {
sc_hid_gamepad_init(&gamepad->hid); sc_hid_gamepad_init(&gamepad->hid);
static const struct sc_gamepad_processor_ops ops = { static const struct sc_gamepad_processor_ops ops = {
.process_gamepad_added = sc_gamepad_processor_process_gamepad_added, .process_gamepad_device = sc_gamepad_processor_process_gamepad_device,
.process_gamepad_removed = sc_gamepad_processor_process_gamepad_removed,
.process_gamepad_axis = sc_gamepad_processor_process_gamepad_axis, .process_gamepad_axis = sc_gamepad_processor_process_gamepad_axis,
.process_gamepad_button = sc_gamepad_processor_process_gamepad_button, .process_gamepad_button = sc_gamepad_processor_process_gamepad_button,
}; };

View File

@ -3,8 +3,10 @@
#include "common.h" #include "common.h"
#include <stdbool.h>
#include "aoa_hid.h"
#include "hid/hid_gamepad.h" #include "hid/hid_gamepad.h"
#include "usb/aoa_hid.h"
#include "trait/gamepad_processor.h" #include "trait/gamepad_processor.h"
struct sc_gamepad_aoa { struct sc_gamepad_aoa {

View File

@ -5,8 +5,8 @@
#include <stdbool.h> #include <stdbool.h>
#include "aoa_hid.h"
#include "hid/hid_keyboard.h" #include "hid/hid_keyboard.h"
#include "usb/aoa_hid.h"
#include "trait/key_processor.h" #include "trait/key_processor.h"
struct sc_keyboard_aoa { struct sc_keyboard_aoa {

View File

@ -1,7 +1,6 @@
#include "mouse_aoa.h" #include "mouse_aoa.h"
#include <assert.h> #include <assert.h>
#include <stddef.h>
#include "hid/hid_mouse.h" #include "hid/hid_mouse.h"
#include "input_events.h" #include "input_events.h"

View File

@ -5,7 +5,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "usb/aoa_hid.h" #include "aoa_hid.h"
#include "trait/mouse_processor.h" #include "trait/mouse_processor.h"
struct sc_mouse_aoa { struct sc_mouse_aoa {

View File

@ -1,19 +1,10 @@
#include "scrcpy_otg.h" #include "scrcpy_otg.h"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#ifdef _WIN32 #include "adb/adb.h"
# include "adb/adb.h"
#endif
#include "events.h" #include "events.h"
#include "usb/screen_otg.h" #include "screen_otg.h"
#include "usb/aoa_hid.h"
#include "usb/gamepad_aoa.h"
#include "usb/keyboard_aoa.h"
#include "usb/mouse_aoa.h"
#include "util/log.h" #include "util/log.h"
struct scrcpy_otg { struct scrcpy_otg {
@ -104,14 +95,9 @@ scrcpy_otg(struct scrcpy_options *options) {
// On Windows, only one process could open a USB device // On Windows, only one process could open a USB device
// <https://github.com/Genymobile/scrcpy/issues/2773> // <https://github.com/Genymobile/scrcpy/issues/2773>
LOGI("Killing adb server (if any)..."); LOGI("Killing adb server (if any)...");
if (sc_adb_init()) { unsigned flags = SC_ADB_NO_STDOUT | SC_ADB_NO_STDERR | SC_ADB_NO_LOGERR;
unsigned flags = SC_ADB_NO_STDOUT | SC_ADB_NO_STDERR | SC_ADB_NO_LOGERR; // uninterruptible (intr == NULL), but in practice it's very quick
// uninterruptible (intr == NULL), but in practice it's very quick sc_adb_kill_server(NULL, flags);
sc_adb_kill_server(NULL, flags);
sc_adb_destroy();
} else {
LOGW("Could not call adb executable, adb server not killed");
}
#endif #endif
static const struct sc_usb_callbacks cbs = { static const struct sc_usb_callbacks cbs = {

View File

@ -1,11 +1,7 @@
#include "screen_otg.h" #include "screen_otg.h"
#include <assert.h>
#include <stddef.h>
#include "icon.h" #include "icon.h"
#include "options.h" #include "options.h"
#include "util/acksync.h"
#include "util/log.h" #include "util/log.h"
static void static void
@ -179,6 +175,7 @@ sc_screen_otg_process_gamepad_device(struct sc_screen_otg *screen,
assert(screen->gamepad); assert(screen->gamepad);
struct sc_gamepad_processor *gp = &screen->gamepad->gamepad_processor; struct sc_gamepad_processor *gp = &screen->gamepad->gamepad_processor;
SDL_JoystickID id;
if (event->type == SDL_CONTROLLERDEVICEADDED) { if (event->type == SDL_CONTROLLERDEVICEADDED) {
SDL_GameController *gc = SDL_GameControllerOpen(event->which); SDL_GameController *gc = SDL_GameControllerOpen(event->which);
if (!gc) { if (!gc) {
@ -193,12 +190,9 @@ sc_screen_otg_process_gamepad_device(struct sc_screen_otg *screen,
return; return;
} }
struct sc_gamepad_device_event evt = { id = SDL_JoystickInstanceID(joystick);
.gamepad_id = SDL_JoystickInstanceID(joystick),
};
gp->ops->process_gamepad_added(gp, &evt);
} else if (event->type == SDL_CONTROLLERDEVICEREMOVED) { } else if (event->type == SDL_CONTROLLERDEVICEREMOVED) {
SDL_JoystickID id = event->which; id = event->which;
SDL_GameController *gc = SDL_GameControllerFromInstanceID(id); SDL_GameController *gc = SDL_GameControllerFromInstanceID(id);
if (gc) { if (gc) {
@ -206,12 +200,16 @@ sc_screen_otg_process_gamepad_device(struct sc_screen_otg *screen,
} else { } else {
LOGW("Unknown gamepad device removed"); LOGW("Unknown gamepad device removed");
} }
} else {
struct sc_gamepad_device_event evt = { // Nothing to do
.gamepad_id = id, return;
};
gp->ops->process_gamepad_removed(gp, &evt);
} }
struct sc_gamepad_device_event evt = {
.type = sc_gamepad_device_event_type_from_sdl_type(event->type),
.gamepad_id = id,
};
gp->ops->process_gamepad_device(gp, &evt);
} }
static void static void

Some files were not shown because too many files have changed in this diff Show More