Compare commits

...

120 Commits

Author SHA1 Message Date
Romain Vimont
34dd3c1688 Fix virtual display after Android 16 upgrade
Several methods have been added upstream to IDisplayWindowListener.aidl,
causing an AbstractMethodError when they are called on the listener
instance implemented by scrcpy.

Fixes #6234 <https://github.com/Genymobile/scrcpy/issues/6234>
Fixes #6331 <https://github.com/Genymobile/scrcpy/issues/6331>
2025-09-05 18:41:49 +02:00
Filip Buda
8057835a0d Catch CTRL_BREAK_EVENT signal on Windows
This ensures the process can terminate properly when a CTRL_BREAK_EVENT
signal is sent programmatically.

PR #6244 <https://github.com/Genymobile/scrcpy/pull/6244>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-08-12 18:17:32 +02:00
Yan
e47529ab9c Fix gl_context declared type
The field gl_context is initialized from SDL_GL_CreateContext(), which
returns a raw SDL_GLContext, not a pointer.

The type mismatch was silently ignored by SDL2 because SDL_GLContext
was defined as an alias to `void *` (in SDL3, it is instead an alias to
`struct SDL_GLContextState *`, so compilation fails).

Refs #3895 <https://github.com/Genymobile/scrcpy/pull/3895>
PR #6259 <https://github.com/Genymobile/scrcpy/pull/6259>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-08-12 18:08:22 +02:00
Romain Vimont
939c8e7f68 Simplify settings access
For Android >= 12, scrcpy executed "settings" commands (in a new
process) rather than using the ContentProvider directly, due to
permission issues [1].

However, these permission issues were resolved by introducing
FakeContext.getContentResolver() [2].

Therefore, remove the use of "settings" commands and use the
ContentProvider directly in all cases.

Refs [1] cc0902b13c
Refs [2] 91373d906b
Refs #6224 comment <https://github.com/Genymobile/scrcpy/issues/6224#issuecomment-3078418268>
2025-07-17 18:27:21 +02:00
Romain Vimont
eb576c44f8 Replace __WINDOWS__ by _WIN32
Replace the SDL2-specific preprocessor macro __WINDOWS__ by the
"standard" _WIN32 macro.
2025-07-17 18:23:25 +02:00
Romain Vimont
0522d02d40 Add missing includes
The headers were implicitly included recursively, but include them
explicitly.
2025-07-17 18:23:12 +02:00
Romain Vimont
30bfc80f9b Fix style for 80-char limit 2025-07-17 13:20:45 +02:00
Romain Vimont
c3d2ef1b1f Remove redundant ninja install for GA macOS runner
The ninja package is already installed, so this triggered a warning:

> ninja 1.13.0 is already installed and up-to-date. To reinstall 1.13.0,
> run: brew reinstall ninja
2025-07-17 13:20:45 +02:00
Romain Vimont
a79ddc35a7 Update platform-tools checksums
The release binaries of platform-tools_r36.0.0 have changed upstream.

Both releases versions are referenced from
<https://dl.google.com/android/repository/repository2-2.xml>

Refs #6214 <https://github.com/Genymobile/scrcpy/issues/6214>
Refs <https://issuetracker.google.com/issues/431119334>
2025-07-17 13:20:45 +02:00
Romain Vimont
04542a9f58 Fix window leak on icon error 2025-07-17 13:19:15 +02:00
Romain Vimont
8761dcb7a8 Fix SDL dependency script error message
Commit 360936248c mistakenly left an
additional 'H' when replacing $HOST with $DIRNAME.

Refs <https://github.com/Genymobile/scrcpy/pull/6216#issuecomment-3076069802>
2025-07-16 19:09:47 +02:00
Romain Vimont
f01231dff8 Update links to 3.3.1 2025-06-20 20:14:42 +02:00
Romain Vimont
5b18ce0d2e Bump version to 3.3.1 2025-06-20 19:54:16 +02:00
Romain Vimont
4841fdd1ef Add horizontal scrolling support for HID mouse
PR #6172 <https://github.com/Genymobile/scrcpy/pull/6172>
2025-06-20 19:54:16 +02:00
Romain Vimont
fc75319bb2 Fix HID mouse support with SDL precise scrolling
Over HID, only integral scroll values can be sent. When SDL precise
scrolling is active, scroll events may include fractional values (e.g.,
0.05), which are truncated to 0 in the HID event.

To fix the problem, use the integral scroll value reported by SDL, which
internally accumulates fractional deltas.

Fixes #6156 <https://github.com/Genymobile/scrcpy/issues/6156>
PR #6172 <https://github.com/Genymobile/scrcpy/pull/6172>
2025-06-20 19:54:16 +02:00
Romain Vimont
7c8bdccbdc Extend value range for SDK mouse scrolling
SDL precise scrolling can sometimes produce values greater than 1 or
less than -1.

On the wire, the value is encoded as a 16-bit fixed-point number.

Previously, the range was interpreted as [-1, 1], using 1 bit for the
integral part (the sign) and 15 bits for the fractional part.

To support larger values, interpret the range as [-16, 16] instead,
using 5 bits for the integral part and 11 bits for the fractional part
(which is more than enough).

PR #6172 <https://github.com/Genymobile/scrcpy/pull/6172>
2025-06-20 19:54:16 +02:00
Romain Vimont
9787fe5d26 Preserve original scroll values in mouse event
Clamp scroll values to [-1, 1] only for the SDK mouse.

HID mouse implementations perform their own clamping to [-127, 127] (in
hid_mouse.c).

PR #6172 <https://github.com/Genymobile/scrcpy/pull/6172>
2025-06-20 18:30:50 +02:00
Romain Vimont
98d30288f7 Prepare the main looper earlier
The looper must be initialized before listing apps, to avoid the
following error:

> Can't create handler inside thread that has not called
> Looper.prepare()

Refs 283326b2f6
Fixes #6165 <https://github.com/Genymobile/scrcpy/issues/6165>
2025-06-17 21:08:54 +02:00
Romain Vimont
d74cfd5711 Silence DiscouragedPrivateApi lint warning 2025-06-13 09:40:45 +02:00
Romain Vimont
cd3a5d50b6 Create ClipboardManager from the main thread
The ClipboardManager is instantiated by the first call to
ServiceManager.getClipboardManager().

Now that scrcpy uses android.content.ClipboardManager directly, it must
ensure that it is created on the main thread (or at least on a thread
with a Looper), to avoid the following error:

> Can't create handler inside thread that has not called
> Looper.prepare()

Refs 8a02e3c2f5
Fixes #6151 <https://github.com/Genymobile/scrcpy/issues/6151>
2025-06-13 09:37:32 +02:00
Romain Vimont
772f42134a Use Context.CLIPBOARD_SERVICE directly
The constant is defined in Context, not FakeContext.
2025-06-13 09:37:32 +02:00
berk ziya
38256d8ff9 Fix deprecated brew command
`brew cask` is an outdated command, replaced by `brew install --cask`.

Refs #5398 <https://github.com/Genymobile/scrcpy/pull/5398>
PR #6149 <https://github.com/Genymobile/scrcpy/pull/6149>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-06-12 15:30:04 +02:00
Romain Vimont
4e1cf13a50 Run a main looper in the cleanup process
Since a main looper is explicitly run in the main process, the
initialization of workarounds no longer calls
Looper.prepareMainLooper(), leading to a crash:

    java.lang.RuntimeException: Can't create handler inside thread
    Thread[main,5,main] that has not called Looper.prepare()

As a result, --power-off-on-close was broken.

Refs 283326b2f6
Fixes #6146 <https://github.com/Genymobile/scrcpy/issues/6146>
2025-06-12 09:10:11 +02:00
Romain Vimont
696402c68c Update links to 3.3 2025-06-11 22:15:30 +02:00
Romain Vimont
dc169e425e Bump version to 3.3 2025-06-11 19:39:48 +02:00
Romain Vimont
13fc75902a Merge branch 'master' into release 2025-06-11 19:39:09 +02:00
Romain Vimont
454beaa757 Upgrade libusb (1.0.29) 2025-06-11 19:39:02 +02:00
Romain Vimont
1a9ffb3814 Upgrade SDL (2.32.8) 2025-06-11 19:38:29 +02:00
Romain Vimont
ac16be54c8 Upgrade platform-tools (36.0.0) 2025-06-11 19:36:22 +02:00
Romain Vimont
8a02e3c2f5 Simplify ClipboardManager wrapper
Use the public ClipboardManager API, with the FakeContext as context.

This requires a running main looper, otherwise clipboard changes are not
processed.

Refs #6009 <https://github.com/Genymobile/scrcpy/pull/6009>
PR #6129 <https://github.com/Genymobile/scrcpy/pull/6129>

Suggested by: Simon Chan <1330321+yume-chan@users.noreply.github.com>
2025-06-11 17:47:37 +02:00
Romain Vimont
283326b2f6 Run a main looper
Instead of blocking the main thread until completion, run a looper.

This will allow the main thread to process any event posted to the main
looper.

Refs #6009 comment <https://github.com/Genymobile/scrcpy/pull/6009#issuecomment-2940810736>
PR #6129 <https://github.com/Genymobile/scrcpy/pull/6129>
2025-06-11 17:47:07 +02:00
Simon Chan
ca4f50c5ef Associate UHID devices to virtual displays
This allows the mouse pointer to appear on the correct display (only for
devices running Android 15+).

Fixes #5547 <https://github.com/Genymobile/scrcpy/issues/5547>
PR #6009 <https://github.com/Genymobile/scrcpy/pull/6009>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-06-05 20:34:11 +02:00
Romain Vimont
7a3fe830d4 Synchronize access to DisplayManager
The DisplayManager and its method getDisplayInfo() may be used from both
the Controller thread and the video (main) thread.

PR #6009 <https://github.com/Genymobile/scrcpy/pull/6009>
2025-06-05 20:34:11 +02:00
Romain Vimont
ee414231ed Cache getDisplayInfo method
Do not use reflection to retrieve the method for every call.

PR #6009 <https://github.com/Genymobile/scrcpy/pull/6009>
2025-06-05 20:34:11 +02:00
Simon Chan
41ed40f5f9 Simplify InputManager wrapper
Use the public InputManager API.

PR #6009 <https://github.com/Genymobile/scrcpy/pull/6009>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-06-05 20:34:11 +02:00
Colin Kinloch
d2cc930975 Add app name SDL hint
This allows pulseaudio to label the audio stream "scrcpy" rather than
"SDL Application".

PR #6107 <https://github.com/Genymobile/scrcpy/pull/6107>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-06-05 19:59:59 +02:00
Romain Vimont
52f5d08d1f Avoid calling wait(0)
Calling wait(0) results in waiting without a timeout, which is
unintended.

Refs #6009 comment <https://github.com/Genymobile/scrcpy/pull/6009#issuecomment-2935930294>
2025-06-03 21:15:11 +02:00
Romain Vimont
70bfa2cf39 Remove useless flag in zsh completion script
The -N flag is only useful after a pattern section (-p) to switch back
to listing command names.

Refs <https://zsh.sourceforge.io/Doc/Release/Completion-System.html>
2025-05-22 20:00:58 +02:00
hltdev8642
38f779d9d3 Escape parentheses in zsh completion script
PR #6079 <https://github.com/Genymobile/scrcpy/pull/6079>

Co-authored-by: Romain Vimont <rom@rom1v.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-05-22 20:00:53 +02:00
Romain Vimont
8cd63cb63e Report specific error for INJECT_EVENT permission
Some devices require a specific option to be enabled in Developer
Options to avoid a permission issue when injecting input events.

When this error occurs, hide the stack trace and print a human-readable
message explaining how to fix the issue.

PR #6080 <https://github.com/Genymobile/scrcpy/pull/6080>
2025-05-15 19:52:52 +02:00
Romain Vimont
cc309a2b34 Build static linux binary on Ubuntu 22.04
Ubuntu 20.04 is no longer available on GitHub Actions.

Refs <https://github.com/actions/runner-images/issues/11101>
Refs #6050 <https://github.com/Genymobile/scrcpy/pull/6050>

This reverts commit 69858c6f43.
2025-05-02 11:39:47 +02:00
Romain Vimont
91a4a74641 Move regex pattern initialization
If text == null, then the Pattern is not used.
2025-04-25 10:24:07 +02:00
Romain Vimont
48f38c4bb6 Fix default locked capture orientation
The default landscape locked orientation was reversed.

Fixes #6010 <https://github.com/Genymobile/scrcpy/issues/6010>
2025-04-24 16:12:28 +02:00
Romain Vimont
6875e9aa88 Revert "Fix AudioRecord package name for Android 16"
This reverts commit c27d116a66.

This commit breaks audio on Android 16 beta 4.

Refs #5960 comment <https://github.com/Genymobile/scrcpy/issues/5960#issuecomment-2816608015>
Fixes #6021 <https://github.com/Genymobile/scrcpy/issues/6021>
2025-04-24 16:05:13 +02:00
Nicholas Wilson
c5ed2cfc28 Replace "licence" with "license" in README
Although "licence" is correct in British English, the rest of the
statement uses "license," so change it for consistency.

PR #6017 <https://github.com/Genymobile/scrcpy/pull/6017>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-04-19 10:30:29 +02:00
Romain Vimont
1a0d300786 Add missing --screen-off-timeout doc in manpage
Refs eff5b4b219
2025-04-14 18:07:37 +02:00
Romain Vimont
d2447b5c19 Fix --screen-off-timeout bash completion
Only the option must be auto-completed, not its value.
2025-04-14 18:05:08 +02:00
Romain Vimont
5900e9e39c Remove irrelevant link in FAQ 2025-04-07 10:30:56 +02:00
Romain Vimont
882003f314 Fix segfault on SDL event without window
Since #5804, controls have been enabled even with --no-window. As a
result, the Android clipboard is synchronized with the computer, causing
SDL to trigger an SDL_CLIPBOARDUPDATE event.

This event is ignored by scrcpy, but it was still transmitted to the
sc_screen instance, even if it had not been initialized.

Fix the issue by calling sc_screen_handle_event() only when a screen
instance exists.

Refs #5804 <https://github.com/Genymobile/scrcpy/pull/5804>
Fixes #5970 <https://github.com/Genymobile/scrcpy/issues/5970>
2025-04-03 08:15:55 +02:00
Romain Vimont
db9dc6ae83 Make the snap version as obsolete
The version of scrcpy packaged in snap is currently 1.25.

Refs <https://snapcraft.io/scrcpy>
2025-04-01 11:04:34 +02:00
Romain Vimont
e0f37f834b Update links to 3.2 2025-03-29 16:15:14 +01:00
Romain Vimont
89b624770c Bump version to 3.2 2025-03-29 15:45:28 +01:00
Romain Vimont
79227af89f Merge branch 'master' into release 2025-03-29 15:44:29 +01:00
Romain Vimont
5d12d9071d Upgrade FFmpeg (7.1.1) 2025-03-29 15:34:48 +01:00
Romain Vimont
b7add42154 Upgrade SDL (2.32.2)
Also apply this additional patch to fix the build:
<6be87ceb33>
2025-03-29 15:34:20 +01:00
Romain Vimont
dd1bfae4e0 Upgrade libusb (1.0.28) 2025-03-29 15:02:38 +01:00
Romain Vimont
bef2d8473b Add more audio sources
Expose more audio sources from MediaRecorder.AudioSource.

Refs <https://developer.android.com/reference/android/media/MediaRecorder.AudioSource>

Fixes #5412 <https://github.com/Genymobile/scrcpy/issues/5412>
Fixes #5670 <https://github.com/Genymobile/scrcpy/issues/5670>
PR #5870 <https://github.com/Genymobile/scrcpy/pull/5870>
2025-03-29 14:54:35 +01:00
Romain Vimont
609719bde0 Refactor audio sources
Store the target audio source integer (one of the constants from
android.media.MediaRecorder.AudioSource) in the AudioSource enum (or -1
if not relevant).

This will simplify adding new audio sources.

PR #5870 <https://github.com/Genymobile/scrcpy/pull/5870>
2025-03-29 14:54:35 +01:00
Romain Vimont
3a0703f428 Handle audio stream discontinuities
The audio regulator assumed a continuous audio stream. But some audio
sources (like the "voice call" audio source) do not produce any packets
on silence, breaking this assumption.

Use PTS to detect such discontinuities.

PR #5870 <https://github.com/Genymobile/scrcpy/pull/5870>
2025-03-29 14:54:35 +01:00
Romain Vimont
245981281e Fix PTS produced by the default opus/flac encoders
The default OPUS and FLAC encoders on Android rewrite the input PTS so
that they exactly match the number of samples.

As a consequence:
 - audio clock drift is not compensated
 - implicit silences (without packets) are ignored

To work around this behavior, generate new PTS based on the current time
(after encoding) and the packet duration.

PR #5870 <https://github.com/Genymobile/scrcpy/pull/5870>
2025-03-29 14:45:05 +01:00
Romain Vimont
1d25338119 Report underflow samples in verbose mode
Report the number of silence samples inserted due to underflow every
second, along with the other metrics.

PR #5870 <https://github.com/Genymobile/scrcpy/pull/5870>
2025-03-29 14:40:52 +01:00
Romain Vimont
457c7fe5cf Disable audio regulator underflow logs
Only enable them if SC_AUDIO_REGULATOR_DEBUG is set, as they may spam
the output.

PR #5870 <https://github.com/Genymobile/scrcpy/pull/5870>
2025-03-29 14:39:43 +01:00
Romain Vimont
7998811fa5 Mention that no Android app is required 2025-03-09 21:16:17 +01:00
Romain Vimont
7044122fc5 Simplify wording in README 2025-03-09 21:10:21 +01:00
Romain Vimont
c63d9e1803 Work around broken display listener on Android 15
A recent Android 15 upgrade broke the display listener (again). Use the
alternative method for Android >= 14.

Fixes #5908 <https://github.com/Genymobile/scrcpy/issues/5908>
2025-03-07 18:40:28 +01:00
Romain Vimont
d892a9aac5 Disable checkstyle line length warning
Checkstyle reports a warning because the line containing a long URL is
more than 150 characters. But we can't split the URL, so disable the
warning.
2025-02-22 12:22:45 +01:00
chengjian.scj
fd8bef68b7 Add --display-ime-policy option
Add an option to select where the IME should be displayed.

Possible values are "local", "fallback" and "hide".

PR #5703 <https://github.com/Genymobile/scrcpy/pull/5703>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-02-22 11:05:22 +01:00
Sam Listopad II
986328ff9e Allow controls with --no-window
Without a window, mouse and keyboard events may not be received, but
the control channel is still necessary for other features:

 * --turn-screen-off
 * --stay-awake
 * --show-touches
 * --power-off-on-close
 * --start-app

Fixes #5803 <https://github.com/Genymobile/scrcpy/issues/5803>
PR #5804 <https://github.com/Genymobile/scrcpy/pull/5804>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-01-31 10:11:17 +01:00
Romain Vimont
0ba9d35705 Mention virtual display destruction
The new virtual display does not persist after scrcpy exits.
2025-01-15 10:54:57 +01:00
Romain Vimont
cac8e9c821 Happy new year 2025! 2025-01-01 15:01:18 +01:00
Jaime J. Denizard
1c7680f689 Fix some grammatical issues in documentation
PR #5722 <https://github.com/Genymobile/scrcpy/pull/5722>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2025-01-01 14:59:11 +01:00
Simon Chan
c27d116a66 Fix AudioRecord package name for Android 16
Since commit 9f91a5eebb4520b9333576e946b3911d0f946a04 in frameworks/av
(AOSP), an AudioRecord can be created only if the declared package name
in the AttributionSource is "shell" (for the shell UID):
 - <7c4e6991ac/services/audiopolicy/permission/NativePermissionController.cpp (129)>
 - <7c4e6991ac/services/audiopolicy/permission/NativePermissionController.cpp (40)>

Refs <9f91a5eebb%5E%21/>
Fixes #5698 <https://github.com/Genymobile/scrcpy/issues/5698>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-12-23 19:41:25 +01:00
Romain Vimont
eac711ace6 Remove unused rotation and fold listeners
IRotationWatcher and IDisplayFoldListener are no longer used since
commit 39d51ff2cc.
2024-12-23 12:51:27 +01:00
Romain Vimont
af15c72f9c Cleanup includes
Improved manually with the help of neovim LSP warnings and iwyu:

    iwyu -Ibuilddir/app/ -Iapp/src/ app/src/XXX.c
2024-12-23 12:19:47 +01:00
Simon Chan
5b1229a55f Support older macOS versions in CI build
Fixes #5649 <https://github.com/Genymobile/scrcpy/issues/5649>
Fixes #5697<https://github.com/Genymobile/scrcpy/pull/5697>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-12-23 11:58:06 +01:00
Romain Vimont
69858c6f43 Build static linux binary on Ubuntu 20.04
Use the oldest Ubuntu version currently available in GitHub Actions to
ensure maximum compatibility with older systems.

Refs 95c4f03c1b
Refs #5689 <https://github.com/Genymobile/scrcpy/issues/5689>
2024-12-23 11:01:42 +01:00
Romain Vimont
e0423653c8 Remove useless null check
The method CameraManager.getCameraIdList() is annotated with @NonNull.

This fixes a warning reported by Android Studio.
2024-12-23 10:58:59 +01:00
Romain Vimont
5387644160 Ignore low-FPS ranges if not available
Do not report an error if the returned FPS ranges array is null.

Refs #5669 <https://github.com/Genymobile/scrcpy/pull/5669>
2024-12-22 21:17:51 +01:00
Simon Chan
2f44da76f4 Filter out non-backward-compatible cameras
PR #5669 <https://github.com/Genymobile/scrcpy/pull/5669>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-12-22 21:17:05 +01:00
Romain Vimont
95c4f03c1b Build static linux binary on Ubuntu 22.04
On Github Actions, ubuntu-latest now points to ubuntu-24.04, which uses
a newer version of glibc (2.39). As a result, the binaries fail to work
on systems with older versions of glibc, such as Debian Bookworm.

To ensure better compatibility, continue building the static Linux
binary on Ubuntu 22.04 (with glibc 2.35).

Fixes #5689 <https://github.com/Genymobile/scrcpy/issues/5689>
2024-12-22 15:49:46 +01:00
Romain Vimont
fb47b87eeb Fix pipe read return value
The function incorrectly returned false, whereas its return type is
ssize_t.
2024-12-20 20:57:20 +01:00
Markus
5ae01749bf Reintroduce WinGet install note
This semantically reverts c27ab46efb.

WinGet package has been fixed by:
<https://github.com/microsoft/winget-pkgs/pull/196442>

Refs #4027 <https://github.com/Genymobile/scrcpy/issues/4027>
PR #5686 <https://github.com/Genymobile/scrcpy/pull/5686>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-12-19 18:34:20 +01:00
Romain Vimont
1fd57ede1f Move "screen off timeout" section in documentation
Place the "screen off timeout" section right after "stay awake", as they
serve a similar purpose.
2024-12-17 13:09:24 +01:00
Romain Vimont
48fc18e380 Add must-know tips
All users should be aware of the main shortcuts and the most important
setting to improve performance.
2024-12-17 12:24:50 +01:00
Romain Vimont
ea6a94d355 Fix mouse documentation formatting
Make the format consistent with the shortcuts documentation.
2024-12-17 12:21:57 +01:00
Romain Vimont
dc2fcc46f5 Add workaround for Pico 4 Ultra
Make ActivityThread.isSystem() return true to avoid a
NullPointerException later.

Refs #5659 comment <https://github.com/Genymobile/scrcpy/issues/5659#issuecomment-2540963953>
Fixes #5659 <https://github.com/Genymobile/scrcpy/issues/5659>
2024-12-14 10:27:38 +01:00
Romain Vimont
69264703b1 Add missing comments in workarounds
The implementation of workarounds uses a lot of reflection code. For
better readability, always write the equivalent using direct Java code.
2024-12-14 10:27:38 +01:00
Colin Kinloch
ec4e826976 Set icon and server env paths for meson devenv
This allows users to compile and run the project in a dev environment.

    meson setup x
    meson compile -C x
    meson devenv -C x
    scrcpy

This is an alternative to `./run x`.

PR #5658 <https://github.com/Genymobile/scrcpy/pull/5658>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-12-12 18:15:26 +01:00
Romain Vimont
17e205e54f Replace meson join_paths() by '/'
A new '/' operator was introduced in Meson 0.49 to replace join_paths():
 - <https://mesonbuild.com/Reference-manual_functions.html#join_paths>
 - <https://mesonbuild.com/Syntax.html#string-path-building>

Refs #5658 <https://github.com/Genymobile/scrcpy/pull/5658>
2024-12-12 18:15:26 +01:00
Romain Vimont
f751274b17 Define both pkg-config and pkgconfig for meson
In Meson cross-files, "pkgconfig" was deprecated in favor of
"pkg-config" in meson 1.3.0.

The new name is used since 85a94dd4b5 to
avoid a warning, but then it fails with older versions of meson.

To avoid the problem, define both pkg-config and pkgconfig.

> For backward compatibility it is still allowed to define both with the
> same value, in that case no deprecation warning is printed.

<https://mesonbuild.com/Release-notes-for-1-3-0.html#machine-files-pkgconfig-field-deprecated-and-replaced-by-pkgconfig>
2024-12-12 18:09:31 +01:00
Romain Vimont
6469054b15 Revert "Remove apt update on GitHub Actions"
This reverts commit 678025b316.

This avoids spurious errors on the CI:

    E: Unable to fetch some archives, maybe run apt-get update or try
    with --fix-missing?
2024-12-12 18:09:31 +01:00
Romain Vimont
0e2d084751 Update links to 3.1 2024-12-09 22:42:34 +01:00
Romain Vimont
754f4fc6fe Bump version to 3.1 2024-12-09 22:29:51 +01:00
Romain Vimont
aca6d30af5 Include dav1d in releases
Scrcpy supports AV1, but no decoder was provided in binary releases.

Include dav1d:
 - <https://www.videolan.org/projects/dav1d.html>
 - <https://code.videolan.org/videolan/dav1d>

Fixes #4744 <https://github.com/Genymobile/scrcpy/issues/4744>
PR #5644 <https://github.com/Genymobile/scrcpy/pull/5644>
2024-12-09 19:07:18 +01:00
Romain Vimont
f2018e026c Remove broken macOS flags
Due to a typo (a space was missing before the second '-L'), the
resulting LDFLAGS value was broken:

    "-L/opt/homebrew/opt/zlib/lib-L/opt/homebrew/opt/libiconv/lib"

This proves that the flag was useless. Remove it.

Refs #5517 comment <https://github.com/Genymobile/scrcpy/pull/5517#issuecomment-2495522201>
PR #5644 <https://github.com/Genymobile/scrcpy/pull/5644>
2024-12-09 19:07:03 +01:00
Simon Chan
a507b4f559 Fix DisplayControl classpath
Use the full system server classpath to load DisplayControl, so that
turning the screen off on Android 14+ does not crash on certain devices.

Refs #4544 comment <https://github.com/Genymobile/scrcpy/issues/4544#issuecomment-2526999714>
Fixes #4544 <https://github.com/Genymobile/scrcpy/issues/4544>
Fixes #5274 <https://github.com/Genymobile/scrcpy/issues/5274>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-12-09 10:29:20 +01:00
Romain Vimont
a9aadc95df Start cleanup process with setsid()
Reimplement c59a3c3169 using Os.setsid().

Refs #5613 comment <https://github.com/Genymobile/scrcpy/pull/5613#issuecomment-2527045669>

Suggested-by: Simon Chan <1330321+yume-chan@users.noreply.github.com>
2024-12-09 09:33:08 +01:00
Romain Vimont
28b5bfb90e Revert "Start cleanup process with setsid or nohup"
This reverts commit c59a3c3169.

The next commit will use Os.setsid() instead.
2024-12-09 09:29:29 +01:00
Romain Vimont
65256d7cc7 Upgrade SDL (2.30.10) 2024-12-08 18:17:07 +01:00
Romain Vimont
328bb74f80 Log gamepad added/removed
Add a log when a gamepad is added or removed.

PR #5623 <https://github.com/Genymobile/scrcpy/pull/5623>
2024-12-08 18:16:57 +01:00
Withoutruless
7418fd0662 Use Xbox 360 gamepad name
Some games do not work without a known gamepad name.

Fixes #5362 <https://github.com/Genymobile/scrcpy/issues/5362>
Refs #5623 comment <https://github.com/Genymobile/scrcpy/pull/5623#issuecomment-2525685323>
PR #5623 <https://github.com/Genymobile/scrcpy/pull/5623>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-12-08 17:01:03 +01:00
Romain Vimont
0a09518a49 Use Xbox 360 gamepad USB ids
Use the vendorId and productId of an Xbox 360 controller for better
support (the HID gamepad protocol used in scrcpy is similar to that of
the Xbox 360 controller).

Fixes #5362 <https://github.com/Genymobile/scrcpy/issues/5362>
PR #5623 <https://github.com/Genymobile/scrcpy/pull/5623>
2024-12-08 11:00:18 +01:00
Romain Vimont
27a5934a1d Define UHID vendorId and productId from the client
Let the client choose the USB ids, that it transmits in UHID_CREATE
requests.

PR #5623 <https://github.com/Genymobile/scrcpy/pull/5623>
2024-12-08 11:00:18 +01:00
Romain Vimont
86a68fac6c Fix gamepad axis initial values
By default, initialize axis to 0, which is represented by 0x8000 as a
16-bit unsigned value.

PR #5623 <https://github.com/Genymobile/scrcpy/pull/5623>
2024-12-08 11:00:18 +01:00
Romain Vimont
1786f28e6f Fix gamepad HID descriptor
Use Z and Rz for L2/R2, which are more widely supported than
Brake/Accelerator.

The right stick must then be bound to Rx and Ry.

Fixes #5362 <https://github.com/Genymobile/scrcpy/issues/5362>
PR #5623 <https://github.com/Genymobile/scrcpy/pull/5623>
2024-12-08 11:00:18 +01:00
Romain Vimont
9cf4d52721 Fix HID gamepad comments
PR #5623 <https://github.com/Genymobile/scrcpy/pull/5623>
2024-12-08 11:00:18 +01:00
Romain Vimont
4bd1c5981d Split gamepad device added/removed events
Use two separate callbacks for gamepad device added and gamepad device
removed.

It looks cleaner.

PR #5623 <https://github.com/Genymobile/scrcpy/pull/5623>
2024-12-08 11:00:18 +01:00
Romain Vimont
c59a3c3169 Start cleanup process with setsid or nohup
If available, start the cleanup process in a new session to reduce the
likelihood of it being terminated along with the scrcpy server process
on some devices.

The binaries setsid and nohup are often available, but it is not
guaranteed.

Refs #5601 <https://github.com/Genymobile/scrcpy/issues/5601>
PR #5613 <https://github.com/Genymobile/scrcpy/pull/5613>
2024-12-08 10:58:22 +01:00
Romain Vimont
2780e0bd7b Do not interrupt cleanup configuration
Some options, such as --show-touches or --stay-awake, modify Android
settings and must be restored upon exit.

If scrcpy terminates (e.g. due to an early error) in the middle of the
clean up configuration, the device may be left in an inconsistent state
(some settings might be changed but not restored).

This issue can be reproduced with high probability by forcing scrcpy to
fail:

    scrcpy --show-touches --video-encoder=fail

To prevent this problem, ensure that the clean up thread is not
interrupted until the clean up process is started.

Refs #5601 <https://github.com/Genymobile/scrcpy/issues/5601>
PR #5613 <https://github.com/Genymobile/scrcpy/pull/5613>
2024-12-08 10:58:07 +01:00
Romain Vimont
6c6607d404 Add --no-vd-destroy-content
Add an option to disable the following flag for virtual displays:

    DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL

With this option, when the virtual display is closed, the running apps
are moved to the main display rather than being destroyed.

PR #5615 <https://github.com/Genymobile/scrcpy/pull/5615>
2024-12-08 09:33:03 +01:00
Romain Vimont
988174805c Fix boolean assignment
On --no-vd-system-decoration, the boolean option must be set to false.

It was wrongly assigned from optarg (this worked because optarg is NULL
at this point, so it was converted to false).

PR #5615 <https://github.com/Genymobile/scrcpy/pull/5615>
2024-12-08 09:26:53 +01:00
Romain Vimont
f90dc216d1 Refactor virtual display properties initialization
Following the changes from the previous commit, the behavior is now
identical when mirroring the main display or using the SurfaceControl
API.

Factorize the code to perform the initialization in a single location.

Refs #5605 <https://github.com/Genymobile/scrcpy/issues/5605>
PR #5614 <https://github.com/Genymobile/scrcpy/pull/5614>
2024-12-07 20:09:14 +01:00
Romain Vimont
97fa77c76c Inject main display events to the original display
When mirroring a secondary display, touch and scroll events must be sent
to the mirroring virtual display id (with coordinates relative to the
virtual display size), rather than to the original display (with
coordinates relative to the original display size).

This behavior, introduced by d19396718e,
was also applied for the main display for consistency. However, it
causes some UI elements to become unclickable.

To minimize inconveniences, restore the previous behavior when mirroring
the main display: send all events to the original display id (0) with
coordinates relative to the original display size.

Fixes #5545 <https://github.com/Genymobile/scrcpy/issues/5545>
Fixes #5605 <https://github.com/Genymobile/scrcpy/issues/5605>
Fixes #5616 <https://github.com/Genymobile/scrcpy/issues/5616>
Refs #4598 <https://github.com/Genymobile/scrcpy/issues/4598>
Refs #5137 <https://github.com/Genymobile/scrcpy/issues/5137>
Refs #5370 <https://github.com/Genymobile/scrcpy/pull/5370>
PR #5614 <https://github.com/Genymobile/scrcpy/pull/5614>
2024-12-07 20:08:49 +01:00
Romain Vimont
baa10ed0a3 Update links to 3.0.2 2024-12-04 22:48:27 +01:00
Romain Vimont
2ed2247e8f Bump version to 3.0.2
The version was not bumped for 3.0.1.
2024-12-04 22:35:25 +01:00
Romain Vimont
5febb1e9fb Update links to 3.0.1 2024-12-04 21:46:06 +01:00
Romain Vimont
5c3626ed47 Handle broken pipe errors specifically
Since 9555d3a537, a capture/encoding error
was sometimes logged on exit.
2024-12-04 18:38:23 +01:00
Romain Vimont
0e473eb005 Reset TCP/IP connection with a '+' prefix
When running scrcpy with --tcpip=xx.xx.xx.xx, to make sure a new working
connection is established, it was first disconnected by a call to:

    adb disconnect <addr>

However, this caused all running instances connected to that address to
be killed. Running several instances of scrcpy on the same device is now
useful with virtual displays, so change the default behavior to NOT
disconnect.

To force a reconnection, a '+' prefix can be added:

    scrcpy --tcpip=+192.168.0.x

Fixes #5562 <https://github.com/Genymobile/scrcpy/issues/5562>
2024-12-04 13:16:51 +01:00
Romain Vimont
b26b4fb745 Document launchers in virtual displays
Mention how to start a launcher in a virtual display.

Refs #5592 <https://github.com/Genymobile/scrcpy/issues/5592>
2024-12-04 13:10:35 +01:00
Romain Vimont
9555d3a537 Retry capture on IOException
If the capture fails with an IOException, retry with a lower resolution.

Fixes #5539 <https://github.com/Genymobile/scrcpy/issues/5539>
2024-12-03 23:06:33 +01:00
185 changed files with 1738 additions and 1005 deletions

View File

@@ -74,6 +74,7 @@ jobs:
- 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 \
@@ -83,7 +84,7 @@ jobs:
run: release/test_client.sh
build-linux-x86_64:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: Check architecture
run: |
@@ -99,6 +100,7 @@ jobs:
- 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 \
@@ -129,14 +131,12 @@ jobs:
- 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: Workaround for old meson version run by Github Actions
run: sed -i 's/^pkg-config/pkgconfig/' cross_win32.txt
- name: Build
run: release/build_windows.sh 32
@@ -162,14 +162,12 @@ jobs:
- 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: Workaround for old meson version run by Github Actions
run: sed -i 's/^pkg-config/pkgconfig/' cross_win64.txt
- name: Build
run: release/build_windows.sh 64
@@ -204,10 +202,16 @@ jobs:
- name: Install dependencies
run: |
brew install meson ninja nasm libiconv zlib automake autoconf \
libtool
brew install meson 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
@@ -240,10 +244,17 @@ jobs:
uses: actions/checkout@v4
- name: Install dependencies
run: brew install meson ninja nasm libiconv zlib automake
run: brew install meson 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

5
FAQ.md
View File

@@ -166,14 +166,13 @@ Rebooting the device is necessary once this option is set.
### Special characters do not work
The default text injection method is [limited to ASCII characters][text-input].
A trick allows to also inject some [accented characters][accented-characters],
The default text injection method is limited to ASCII characters. A trick allows
to also inject some [accented characters][accented-characters],
but that's all. See [#37].
To avoid the problem, [change the keyboard mode to simulate a physical
keyboard][hid].
[text-input]: https://github.com/Genymobile/scrcpy/issues?q=is%3Aopen+is%3Aissue+label%3Aunicode
[accented-characters]: https://blog.rom1v.com/2018/03/introducing-scrcpy/#handle-accented-characters
[#37]: https://github.com/Genymobile/scrcpy/issues/37
[hid]: doc/keyboard.md#physical-keyboard-simulation

View File

@@ -188,7 +188,7 @@
identification within third-party archives.
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2024 Romain Vimont
Copyright (C) 2018-2025 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -2,16 +2,16 @@
source for the project. Do not download releases from random websites, even if
their name contains `scrcpy`.**
# scrcpy (v3.0)
# scrcpy (v3.3.1)
<img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />
_pronounced "**scr**een **c**o**py**"_
This application mirrors Android devices (video and audio) connected via
USB or [over TCP/IP](doc/connection.md#tcpip-wireless), and allows to control the
device with the keyboard and the mouse of the computer. It does not require any
_root_ access. It works on _Linux_, _Windows_ and _macOS_.
This application mirrors Android devices (video and audio) connected via USB or
[TCP/IP](doc/connection.md#tcpip-wireless) and allows control using the
computer's keyboard and mouse. It does not require _root_ access or an app
installed on the device. It works on _Linux_, _Windows_, and _macOS_.
![screenshot](assets/screenshot-debian-600.jpg)
@@ -58,7 +58,7 @@ Make sure you [enabled USB debugging][enable-adb] on your device(s).
On some devices (especially Xiaomi), you might get the following error:
```
java.lang.SecurityException: Injecting input events requires the caller (or the source of the instrumentation, if any) to have the INJECT_EVENTS permission.
Injecting input events requires the caller (or the source of the instrumentation, if any) to have the INJECT_EVENTS permission.
```
In that case, you need to enable [an additional option][control] `USB debugging
@@ -78,6 +78,16 @@ Note that USB debugging is not required to run scrcpy in [OTG mode](doc/otg.md).
- [macOS](doc/macos.md)
## Must-know tips
- [Reducing resolution](doc/video.md#size) may greatly improve performance
(`scrcpy -m1024`)
- [_Right-click_](doc/mouse.md#mouse-bindings) triggers `BACK`
- [_Middle-click_](doc/mouse.md#mouse-bindings) triggers `HOME`
- <kbd>Alt</kbd>+<kbd>f</kbd> toggles [fullscreen](doc/window.md#fullscreen)
- There are many other [shortcuts](doc/shortcuts.md)
## Usage examples
There are a lot of options, [documented](#user-documentation) in separate pages.
@@ -197,10 +207,10 @@ work][donate]:
[donate]: https://blog.rom1v.com/about/#support-my-open-source-work
## Licence
## License
Copyright (C) 2018 Genymobile
Copyright (C) 2018-2024 Romain Vimont
Copyright (C) 2018-2025 Romain Vimont
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -23,6 +23,7 @@ _scrcpy() {
-d --select-usb
--disable-screensaver
--display-id=
--display-ime-policy=
--display-orientation=
-e --select-tcpip
-f --fullscreen
@@ -57,6 +58,7 @@ _scrcpy() {
--no-mipmaps
--no-mouse-hover
--no-power-on
--no-vd-destroy-content
--no-vd-system-decorations
--no-video
--no-video-playback
@@ -120,7 +122,7 @@ _scrcpy() {
return
;;
--audio-source)
COMPREPLY=($(compgen -W 'output mic playback' -- "$cur"))
COMPREPLY=($(compgen -W 'output playback mic mic-unprocessed mic-camcorder mic-voice-recognition mic-voice-communication voice-call voice-call-uplink voice-call-downlink voice-performance' -- "$cur"))
return
;;
--camera-facing)
@@ -147,6 +149,10 @@ _scrcpy() {
COMPREPLY=($(compgen -W '0 90 180 270 flip0 flip90 flip180 flip270' -- "$cur"))
return
;;
--display-ime-policy)
COMPREPLY=($(compgen -W 'local fallback hide' -- "$cur"))
return
;;
--record-orientation)
COMPREPLY=($(compgen -W '0 90 180 270' -- "$cur"))
return
@@ -199,6 +205,7 @@ _scrcpy() {
|-p|--port \
|--push-target \
|--rotation \
|--screen-off-timeout \
|--tunnel-host \
|--tunnel-port \
|--v4l2-buffer \

View File

@@ -1,4 +1,4 @@
#compdef -N scrcpy -N scrcpy.exe
#compdef scrcpy scrcpy.exe
#
# name: scrcpy
# auth: hltdev [hltdev8642@gmail.com]
@@ -11,12 +11,12 @@ arguments=(
'--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-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-options=[Set a list of comma-separated key\:type=value options for the device audio encoder]'
'--audio-dup=[Duplicate audio]'
'--audio-encoder=[Use a specific MediaCodec audio encoder]'
'--audio-source=[Select the audio source]:source:(output mic playback)'
'--audio-source=[Select the audio source]:source:(output playback mic mic-unprocessed mic-camcorder mic-voice-recognition mic-voice-communication voice-call voice-call-uplink voice-call-downlink voice-performance)'
'--audio-output-buffer=[Configure the size of the SDL audio output buffer (in milliseconds)]'
{-b,--video-bit-rate=}'[Encode the video at the given bit-rate]'
'--camera-ar=[Select the camera size by its aspect ratio]'
@@ -30,14 +30,15 @@ arguments=(
{-d,--select-usb}'[Use USB device]'
'--disable-screensaver[Disable screensaver while scrcpy is running]'
'--display-id=[Specify the display id to mirror]'
'--display-ime-policy[Set the policy for selecting where the IME should be displayed]'
'--display-orientation=[Set the initial display orientation]:orientation values:(0 90 180 270 flip0 flip90 flip180 flip270)'
{-e,--select-tcpip}'[Use TCP/IP device]'
{-f,--fullscreen}'[Start in fullscreen]'
'--force-adb-forward[Do not attempt to use \"adb reverse\" to connect to the device]'
'-G[Use UHID/AOA gamepad (same as --gamepad=uhid or --gamepad=aoa, depending on OTG mode)]'
'-G[Use UHID/AOA gamepad \(same as --gamepad=uhid or --gamepad=aoa, depending on OTG mode\)]'
'--gamepad=[Set the gamepad input mode]:mode:(disabled uhid aoa)'
{-h,--help}'[Print the help]'
'-K[Use UHID/AOA keyboard (same as --keyboard=uhid or --keyboard=aoa, depending on OTG mode)]'
'-K[Use UHID/AOA keyboard \(same as --keyboard=uhid or --keyboard=aoa, depending on OTG mode\)]'
'--keyboard=[Set the keyboard input mode]:mode:(disabled sdk uhid aoa)'
'--kill-adb-on-close[Kill adb when scrcpy terminates]'
'--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]'
@@ -47,7 +48,7 @@ arguments=(
'--list-displays[List displays available on the device]'
'--list-encoders[List video and audio encoders available on the device]'
{-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]'
'--mouse=[Set the mouse input mode]:mode:(disabled sdk uhid aoa)'
'--mouse-bind=[Configure bindings of secondary clicks]'
@@ -63,6 +64,7 @@ arguments=(
'--no-mipmaps[Disable the generation of mipmaps]'
'--no-mouse-hover[Do not forward mouse hover events]'
'--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-playback[Disable video playback]'

View File

@@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR"
. common
VERSION=35.0.2
VERSION=36.0.0
FILENAME=platform-tools_r$VERSION-linux.zip
PROJECT_DIR=platform-tools-$VERSION-linux
SHA256SUM=acfdcccb123a8718c46c46c059b2f621140194e5ec1ac9d81715be3d6ab6cd0a
SHA256SUM=0ead642c943ffe79701fccca8f5f1c69c4ce4f43df2eefee553f6ccb27cbfbe8
cd "$SOURCES_DIR"

View File

@@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR"
. common
VERSION=35.0.2
VERSION=36.0.0
FILENAME=platform-tools_r$VERSION-darwin.zip
PROJECT_DIR=platform-tools-$VERSION-darwin
SHA256SUM=1820078db90bf21628d257ff052528af1c61bb48f754b3555648f5652fa35d78
SHA256SUM=d3e9fa1df3345cf728586908426615a60863d2632f73f1ce14f0f1349ef000fd
cd "$SOURCES_DIR"

View File

@@ -4,10 +4,10 @@ DEPS_DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DEPS_DIR"
. common
VERSION=35.0.2
VERSION=36.0.0
FILENAME=platform-tools_r$VERSION-win.zip
PROJECT_DIR=platform-tools-$VERSION-windows
SHA256SUM=2975a3eac0b19182748d64195375ad056986561d994fffbdc64332a516300bb9
SHA256SUM=12c2841f354e92a0eb2fd7bf6f0f9bf8538abce7bd6b060ac8349d6f6a61107c
cd "$SOURCES_DIR"

68
app/deps/dav1d.sh Executable file
View File

@@ -0,0 +1,68 @@
#!/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

@@ -5,10 +5,10 @@ cd "$DEPS_DIR"
. common
process_args "$@"
VERSION=7.1
VERSION=7.1.1
FILENAME=ffmpeg-$VERSION.tar.xz
PROJECT_DIR=ffmpeg-$VERSION
SHA256SUM=40973D44970DBC83EF302B0609F2E74982BE2D85916DD2EE7472D30678A7ABE6
SHA256SUM=733984395e0dbbe5c046abda2dc49a5544e7e0e1e2366bba849222ae9e3a03b1
cd "$SOURCES_DIR"
@@ -40,16 +40,14 @@ else
export LDFLAGS='-static-libgcc -static'
elif [[ "$HOST" == "macos" ]]
then
export LDFLAGS="$LDFLAGS -L/opt/homebrew/opt/zlib/lib"
export CPPFLAGS="$CPPFLAGS -I/opt/homebrew/opt/zlib/include"
export LDFLAGS="$LDFLAGS-L/opt/homebrew/opt/libiconv/lib"
export CPPFLAGS="$CPPFLAGS -I/opt/homebrew/opt/libiconv/include"
export PKG_CONFIG_PATH="/opt/homebrew/opt/zlib/lib/pkgconfig"
fi
export PKG_CONFIG_PATH="$INSTALL_DIR/$DIRNAME/lib/pkgconfig:$PKG_CONFIG_PATH"
conf=(
--prefix="$INSTALL_DIR/$DIRNAME"
--pkg-config-flags="--static"
--extra-cflags="-O2 -fPIC"
--disable-programs
--disable-doc
@@ -62,9 +60,11 @@ else
--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

View File

@@ -5,10 +5,10 @@ cd "$DEPS_DIR"
. common
process_args "$@"
VERSION=1.0.27
VERSION=1.0.29
FILENAME=libusb-$VERSION.tar.gz
PROJECT_DIR=libusb-$VERSION
SHA256SUM=e8f18a7a36ecbb11fb820bd71540350d8f61bcd9db0d2e8c18a6fb80b214a3de
SHA256SUM=7c2dd39c0b2589236e48c93247c986ae272e27570942b4163cb00a060fcf1b74
cd "$SOURCES_DIR"

View File

@@ -5,10 +5,10 @@ cd "$DEPS_DIR"
. common
process_args "$@"
VERSION=2.30.9
VERSION=2.32.8
FILENAME=SDL-$VERSION.tar.gz
PROJECT_DIR=SDL-release-$VERSION
SHA256SUM=682a055004081e37d81a7d4ce546c3ee3ef2e0e6a675ed2651e430ccd14eb407
SHA256SUM=dd35e05644ae527848d02433bec24dd0ea65db59faecf1a0e5d1880c533dac2c
cd "$SOURCES_DIR"
@@ -28,7 +28,7 @@ export CXXFLAGS="$CFLAGS"
if [[ -d "$DIRNAME" ]]
then
echo "'$PWD/$HDIRNAME' already exists, not reconfigured"
echo "'$PWD/$DIRNAME' already exists, not reconfigured"
cd "$DIRNAME"
else
mkdir "$DIRNAME"

View File

@@ -192,19 +192,19 @@ datadir = get_option('datadir') # by default 'share'
install_man('scrcpy.1')
install_data('data/icon.png',
rename: 'scrcpy.png',
install_dir: join_paths(datadir, 'icons/hicolor/256x256/apps'))
install_dir: datadir / 'icons/hicolor/256x256/apps')
install_data('data/zsh-completion/_scrcpy',
install_dir: join_paths(datadir, 'zsh/site-functions'))
install_dir: datadir / 'zsh/site-functions')
install_data('data/bash-completion/scrcpy',
install_dir: join_paths(datadir, 'bash-completion/completions'))
install_dir: datadir / 'bash-completion/completions')
# Desktop entry file for application launchers
if host_machine.system() == 'linux'
# Install a launcher (ex: /usr/local/share/applications/scrcpy.desktop)
install_data('data/scrcpy.desktop',
install_dir: join_paths(datadir, 'applications'))
install_dir: datadir / 'applications')
install_data('data/scrcpy-console.desktop',
install_dir: join_paths(datadir, 'applications'))
install_dir: datadir / 'applications')
endif
@@ -279,3 +279,9 @@ if get_option('buildtype') == 'debug'
test(t[0], exe)
endforeach
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 "OriginalFilename", "scrcpy.exe"
VALUE "ProductName", "scrcpy"
VALUE "ProductVersion", "3.0"
VALUE "ProductVersion", "3.3.1"
END
END
BLOCK "VarFileInfo"

View File

@@ -67,13 +67,19 @@ The available encoders can be listed by \fB\-\-list\-encoders\fR.
.TP
.BI "\-\-audio\-source " source
Select the audio source (output, mic or playback).
Select the audio source. Possible values are:
The "output" source forwards the whole audio output, and disables playback on the device.
The "playback" source captures the audio playback (Android apps can opt-out, so the whole output is not necessarily captured).
The "mic" source captures the microphone.
- "output": forwards the whole audio output, and disables playback on the device.
- "playback": captures the audio playback (Android apps can opt-out, so the whole output is not necessarily captured).
- "mic": captures the microphone.
- "mic-unprocessed": captures the microphone unprocessed (raw) sound.
- "mic-camcorder": captures the microphone tuned for video recording, with the same orientation as the camera if available.
- "mic-voice-recognition": captures the microphone tuned for voice recognition.
- "mic-voice-communication": captures the microphone tuned for voice communications (it will for instance take advantage of echo cancellation or automatic gain control if available).
- "voice-call": captures voice call.
- "voice-call-uplink": captures voice call uplink only.
- "voice-call-downlink": captures voice call downlink only.
- "voice-performance": captures audio meant to be processed for live performance (karaoke), includes both the microphone and the device playback.
Default is output.
@@ -161,6 +167,19 @@ The available display ids can be listed by \fB\-\-list\-displays\fR.
Default is 0.
.TP
.BI "\-\-display\-ime\-policy " value
Set the policy for selecting where the IME should be displayed.
Possible values are "local", "fallback" and "hide":
- "local" means that the IME should appear on the local display.
- "fallback" means that the IME should appear on a fallback display (the default display).
- "hide" means that the IME should be hidden.
By default, the IME policy is left unchanged.
.TP
.BI "\-\-display\-orientation " value
Set the initial display orientation.
@@ -369,6 +388,12 @@ Do not forward mouse hover (mouse motion without any clicks) events.
.B \-\-no\-power\-on
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.
@@ -383,7 +408,7 @@ Disable video playback on the computer.
.TP
.B \-\-no\-window
Disable scrcpy window. Implies --no-video-playback and --no-control.
Disable scrcpy window. Implies --no-video-playback.
.TP
.BI "\-\-orientation " value
@@ -485,6 +510,10 @@ The device serial number. Mandatory only if several devices are connected to adb
.B \-S, \-\-turn\-screen\-off
Turn the device screen off immediately.
.TP
.B "\-\-screen\-off\-timeout " seconds
Set the screen off timeout while scrcpy is running (restore the initial value on exit).
.TP
.BI "\-\-shortcut\-mod " key\fR[+...]][,...]
Specify the modifiers to use for scrcpy shortcuts. Possible keys are "lctrl", "rctrl", "lalt", "ralt", "lsuper" and "rsuper".
@@ -518,13 +547,15 @@ Enable "show touches" on start, restore the initial value on exit.
It only shows physical touches (not clicks from scrcpy).
.TP
.BI "\-\-tcpip\fR[=\fIip\fR[:\fIport\fR]]
Configure and reconnect the device over TCP/IP.
.BI "\-\-tcpip\fR[=[+]\fIip\fR[:\fIport\fR]]
Configure and connect 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 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
.BI "\-\-time\-limit " seconds
Set the maximum mirroring time, in seconds.
@@ -821,7 +852,7 @@ Report bugs to <https://github.com/Genymobile/scrcpy/issues>.
.SH COPYRIGHT
Copyright \(co 2018 Genymobile <https://www.genymobile.com>
Copyright \(co 2018\-2024 Romain Vimont <rom@rom1v.com>
Copyright \(co 2018\-2025 Romain Vimont <rom@rom1v.com>
Licensed under the Apache License, Version 2.0.

View File

@@ -4,9 +4,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "adb_device.h"
#include "adb_parser.h"
#include "adb/adb_device.h"
#include "adb/adb_parser.h"
#include "util/env.h"
#include "util/file.h"
#include "util/log.h"
@@ -102,14 +103,14 @@ argv_to_string(const char *const *argv, char *buf, size_t bufsize) {
static void
show_adb_installation_msg(void) {
#ifndef __WINDOWS__
#ifndef _WIN32
static const struct {
const char *binary;
const char *command;
} pkg_managers[] = {
{"apt", "apt install adb"},
{"apt-get", "apt-get install adb"},
{"brew", "brew cask install android-platform-tools"},
{"brew", "brew install --cask android-platform-tools"},
{"dnf", "dnf install android-tools"},
{"emerge", "emerge dev-util/android-tools"},
{"pacman", "pacman -S android-tools"},
@@ -330,7 +331,7 @@ sc_adb_reverse_remove(struct sc_intr *intr, const char *serial,
bool
sc_adb_push(struct sc_intr *intr, const char *serial, const char *local,
const char *remote, unsigned flags) {
#ifdef __WINDOWS__
#ifdef _WIN32
// Windows will parse the string, so the paths must be quoted
// (see sys/win/command.c)
local = sc_str_quote(local);
@@ -350,7 +351,7 @@ sc_adb_push(struct sc_intr *intr, const char *serial, const char *local,
sc_pid pid = sc_adb_execute(argv, flags);
#ifdef __WINDOWS__
#ifdef _WIN32
free((void *) remote);
free((void *) local);
#endif
@@ -361,7 +362,7 @@ sc_adb_push(struct sc_intr *intr, const char *serial, const char *local,
bool
sc_adb_install(struct sc_intr *intr, const char *serial, const char *local,
unsigned flags) {
#ifdef __WINDOWS__
#ifdef _WIN32
// Windows will parse the string, so the local name must be quoted
// (see sys/win/command.c)
local = sc_str_quote(local);
@@ -376,7 +377,7 @@ sc_adb_install(struct sc_intr *intr, const char *serial, const char *local,
sc_pid pid = sc_adb_execute(argv, flags);
#ifdef __WINDOWS__
#ifdef _WIN32
free((void *) local);
#endif
@@ -412,7 +413,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
// case of failure. As a workaround, check if its output starts with
// "connected".
// "connected" or "already connected".
char buf[128];
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf) - 1);
sc_pipe_close(pout);
@@ -429,7 +430,8 @@ sc_adb_connect(struct sc_intr *intr, const char *ip_port, unsigned flags) {
assert((size_t) r < sizeof(buf));
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)) {
// "adb connect" also prints errors to stdout. Since we capture it,
// re-print the error to stderr.

View File

@@ -6,7 +6,7 @@
#include <stdbool.h>
#include <inttypes.h>
#include "adb_device.h"
#include "adb/adb_device.h"
#include "util/intr.h"
#define SC_ADB_NO_STDOUT (1 << 0)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,9 @@
#include "audio_regulator.h"
#include <assert.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
@@ -72,8 +76,10 @@ sc_audio_regulator_pull(struct sc_audio_regulator *ar, uint8_t *out,
// Wait until the buffer is filled up to at least target_buffering
// before playing
if (buffered_samples < ar->target_buffering) {
LOGV("[Audio] Inserting initial buffering silence: %" PRIu32
#ifdef SC_AUDIO_REGULATOR_DEBUG
LOGD("[Audio] Inserting initial buffering silence: %" PRIu32
" samples", out_samples);
#endif
// Delay playback starting to reach the target buffering. Fill the
// whole buffer with silence (len is small compared to the
// arbitrary margin value).
@@ -94,8 +100,10 @@ sc_audio_regulator_pull(struct sc_audio_regulator *ar, uint8_t *out,
// dropped to keep the latency minimal. However, this would cause very
// audible glitches, so let the clock compensation restore the target
// latency.
#ifdef SC_AUDIO_REGULATOR_DEBUG
LOGD("[Audio] Buffer underflow, inserting silence: %" PRIu32 " samples",
silence);
#endif
memset(out + TO_BYTES(read), 0, TO_BYTES(silence));
bool received = atomic_load_explicit(&ar->received,
@@ -133,6 +141,36 @@ bool
sc_audio_regulator_push(struct sc_audio_regulator *ar, const AVFrame *frame) {
SwrContext *swr_ctx = ar->swr_ctx;
uint32_t input_samples = frame->nb_samples;
assert(frame->pts >= 0);
int64_t pts = frame->pts;
if (ar->next_expected_pts && pts - ar->next_expected_pts > 100000) {
LOGV("[Audio] Discontinuity detected: %" PRIi64 "µs",
pts - ar->next_expected_pts);
// More than 100ms: consider it as a discontinuity
// (typically because silence packets were not captured)
uint32_t can_read = sc_audiobuf_can_read(&ar->buf);
if (input_samples + can_read < ar->target_buffering) {
// Adjust buffering to the target value directly
uint32_t silence = ar->target_buffering - can_read - input_samples;
sc_audiobuf_write_silence(&ar->buf, silence);
}
// Reset state
ar->avg_buffering.avg = ar->target_buffering;
int ret = swr_set_compensation(swr_ctx, 0, 0);
(void) ret;
assert(!ret); // disabling compensation should never fail
ar->compensation_active = false;
ar->samples_since_resync = 0;
atomic_store_explicit(&ar->underflow, 0, memory_order_relaxed);
}
int64_t packet_duration = input_samples * INT64_C(1000000)
/ ar->sample_rate;
ar->next_expected_pts = pts + packet_duration;
int64_t swr_delay = swr_get_delay(swr_ctx, ar->sample_rate);
// No need to av_rescale_rnd(), input and output sample rates are the same.
// Add more space (256) for clock compensation.
@@ -205,6 +243,7 @@ sc_audio_regulator_push(struct sc_audio_regulator *ar, const AVFrame *frame) {
if (played) {
underflow = atomic_exchange_explicit(&ar->underflow, 0,
memory_order_relaxed);
ar->underflow_report += underflow;
max_buffered_samples = ar->target_buffering * 11 / 10
+ 60 * ar->sample_rate / 1000 /* 60 ms */;
@@ -251,7 +290,7 @@ sc_audio_regulator_push(struct sc_audio_regulator *ar, const AVFrame *frame) {
}
// Number of samples added (or removed, if negative) for compensation
int32_t instant_compensation = (int32_t) written - frame->nb_samples;
int32_t instant_compensation = (int32_t) written - input_samples;
// Inserting silence instantly increases buffering
int32_t inserted_silence = (int32_t) underflow;
// Dropping input samples instantly decreases buffering
@@ -307,7 +346,9 @@ sc_audio_regulator_push(struct sc_audio_regulator *ar, const AVFrame *frame) {
int abs_max_diff = distance / 50;
diff = CLAMP(diff, -abs_max_diff, abs_max_diff);
LOGV("[Audio] Buffering: target=%" PRIu32 " avg=%f cur=%" PRIu32
" compensation=%d", ar->target_buffering, avg, can_read, diff);
" compensation=%d (underflow=%" PRIu32 ")",
ar->target_buffering, avg, can_read, diff, ar->underflow_report);
ar->underflow_report = 0;
int ret = swr_set_compensation(swr_ctx, diff, distance);
if (ret < 0) {
@@ -390,7 +431,9 @@ sc_audio_regulator_init(struct sc_audio_regulator *ar, size_t sample_size,
atomic_init(&ar->played, false);
atomic_init(&ar->received, false);
atomic_init(&ar->underflow, 0);
ar->underflow_report = 0;
ar->compensation_active = false;
ar->next_expected_pts = 0;
return true;

View File

@@ -5,6 +5,8 @@
#include <stdatomic.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <libavcodec/avcodec.h>
#include <libswresample/swresample.h>
#include "util/audiobuf.h"
@@ -44,6 +46,9 @@ struct sc_audio_regulator {
// Number of silence samples inserted since the last received packet
atomic_uint_least32_t underflow;
// Number of silence samples inserted since the last log
uint32_t underflow_report;
// Non-zero compensation applied (only used by the receiver thread)
bool compensation_active;
@@ -52,6 +57,9 @@ struct sc_audio_regulator {
// Set to true the first time samples are pulled by the player
atomic_bool played;
// PTS of the next expected packet (useful to detect discontinuities)
int64_t next_expected_pts;
};
bool

View File

@@ -5,6 +5,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "options.h"
@@ -13,6 +14,7 @@
#include "util/str.h"
#include "util/strbuf.h"
#include "util/term.h"
#include "util/tick.h"
#define STR_IMPL_(x) #x
#define STR(x) STR_IMPL_(x)
@@ -110,6 +112,8 @@ enum {
OPT_CAPTURE_ORIENTATION,
OPT_ANGLE,
OPT_NO_VD_SYSTEM_DECORATIONS,
OPT_NO_VD_DESTROY_CONTENT,
OPT_DISPLAY_IME_POLICY,
};
struct sc_option {
@@ -213,13 +217,31 @@ static const struct sc_option options[] = {
.longopt_id = OPT_AUDIO_SOURCE,
.longopt = "audio-source",
.argdesc = "source",
.text = "Select the audio source (output, mic or playback).\n"
"The \"output\" source forwards the whole audio output, and "
"disables playback on the device.\n"
"The \"playback\" source captures the audio playback (Android "
"apps can opt-out, so the whole output is not necessarily "
.text = "Select the audio source. Possible values are:\n"
" - \"output\": forwards the whole audio output, and disables "
"playback on the device.\n"
" - \"playback\": captures the audio playback (Android apps "
"can opt-out, so the whole output is not necessarily "
"captured).\n"
"The \"mic\" source captures the microphone.\n"
" - \"mic\": captures the microphone.\n"
" - \"mic-unprocessed\": captures the microphone unprocessed "
"(raw) sound.\n"
" - \"mic-camcorder\": captures the microphone tuned for video "
"recording, with the same orientation as the camera if "
"available.\n"
" - \"mic-voice-recognition\": captures the microphone tuned "
"for voice recognition.\n"
" - \"mic-voice-communication\": captures the microphone tuned "
"for voice communications (it will for instance take advantage "
"of echo cancellation or automatic gain control if "
"available).\n"
" - \"voice-call\": captures voice call.\n"
" - \"voice-call-uplink\": captures voice call uplink only.\n"
" - \"voice-call-downlink\": captures voice call downlink "
"only.\n"
" - \"voice-performance\": captures audio meant to be "
"processed for live performance (karaoke), includes both the "
"microphone and the device playback.\n"
"Default is output.",
},
{
@@ -363,6 +385,19 @@ static const struct sc_option options[] = {
" scrcpy --list-displays\n"
"Default is 0.",
},
{
.longopt_id = OPT_DISPLAY_IME_POLICY,
.longopt = "display-ime-policy",
.argdesc = "value",
.text = "Set the policy for selecting where the IME should be "
"displayed.\n"
"Possible values are \"local\", \"fallback\" and \"hide\".\n"
"\"local\" means that the IME should appear on the local "
"display.\n"
"\"fallback\" means that the IME should appear on a fallback "
"display (the default display).\n"
"\"hide\" means that the IME should be hidden.",
},
{
.longopt_id = OPT_DISPLAY_ORIENTATION,
.longopt = "display-orientation",
@@ -659,6 +694,15 @@ static const struct sc_option options[] = {
.longopt = "no-power-on",
.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",
@@ -677,8 +721,7 @@ static const struct sc_option options[] = {
{
.longopt_id = OPT_NO_WINDOW,
.longopt = "no-window",
.text = "Disable scrcpy window. Implies --no-video-playback and "
"--no-control.",
.text = "Disable scrcpy window. Implies --no-video-playback.",
},
{
.longopt_id = OPT_ORIENTATION,
@@ -860,16 +903,17 @@ static const struct sc_option options[] = {
{
.longopt_id = OPT_TCPIP,
.longopt = "tcpip",
.argdesc = "ip[:port]",
.argdesc = "[+]ip[:port]",
.optional_arg = true,
.text = "Configure and reconnect the device over TCP/IP.\n"
.text = "Configure and connect the device over TCP/IP.\n"
"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).\n"
"If no destination address is provided, then scrcpy attempts "
"to find the IP address of the current device (typically "
"connected over USB), enables TCP/IP mode, then connects to "
"this address before starting.",
"this address before starting.\n"
"Prefix the address with a '+' to force a reconnection.",
},
{
.longopt_id = OPT_TIME_LIMIT,
@@ -1602,6 +1646,25 @@ parse_audio_output_buffer(const char *s, sc_tick *tick) {
return true;
}
static bool
parse_display_ime_policy(const char *s, enum sc_display_ime_policy *policy) {
if (!strcmp(s, "local")) {
*policy = SC_DISPLAY_IME_POLICY_LOCAL;
return true;
}
if (!strcmp(s, "fallback")) {
*policy = SC_DISPLAY_IME_POLICY_FALLBACK;
return true;
}
if (!strcmp(s, "hide")) {
*policy = SC_DISPLAY_IME_POLICY_HIDE;
return true;
}
LOGE("Unsupported display IME policy: %s (expected local, fallback or "
"hide)", s);
return false;
}
static bool
parse_orientation(const char *s, enum sc_orientation *orientation) {
if (!strcmp(s, "0")) {
@@ -1991,8 +2054,50 @@ parse_audio_source(const char *optarg, enum sc_audio_source *source) {
return true;
}
LOGE("Unsupported audio source: %s (expected output, mic or playback)",
optarg);
if (!strcmp(optarg, "mic-unprocessed")) {
*source = SC_AUDIO_SOURCE_MIC_UNPROCESSED;
return true;
}
if (!strcmp(optarg, "mic-camcorder")) {
*source = SC_AUDIO_SOURCE_MIC_CAMCORDER;
return true;
}
if (!strcmp(optarg, "mic-voice-recognition")) {
*source = SC_AUDIO_SOURCE_MIC_VOICE_RECOGNITION;
return true;
}
if (!strcmp(optarg, "mic-voice-communication")) {
*source = SC_AUDIO_SOURCE_MIC_VOICE_COMMUNICATION;
return true;
}
if (!strcmp(optarg, "voice-call")) {
*source = SC_AUDIO_SOURCE_VOICE_CALL;
return true;
}
if (!strcmp(optarg, "voice-call-uplink")) {
*source = SC_AUDIO_SOURCE_VOICE_CALL_UPLINK;
return true;
}
if (!strcmp(optarg, "voice-call-downlink")) {
*source = SC_AUDIO_SOURCE_VOICE_CALL_DOWNLINK;
return true;
}
if (!strcmp(optarg, "voice-performance")) {
*source = SC_AUDIO_SOURCE_VOICE_PERFORMANCE;
return true;
}
LOGE("Unsupported audio source: %s (expected output, mic, playback, "
"mic-unprocessed, mic-camcorder, mic-voice-recognition, "
"mic-voice-communication, voice-call, voice-call-uplink, "
"voice-call-downlink, voice-performance)", optarg);
return false;
}
@@ -2704,8 +2809,17 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
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 = optarg;
opts->vd_system_decorations = false;
break;
case OPT_DISPLAY_IME_POLICY:
if (!parse_display_ime_policy(optarg,
&opts->display_ime_policy)) {
return false;
}
break;
default:
// getopt prints the error message on stderr
@@ -2745,9 +2859,10 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
#endif
if (!opts->window) {
// Without window, there cannot be any video playback or control
// Without window, there cannot be any video playback
opts->video_playback = false;
opts->control = false;
// Controls are still possible, allowing for options like
// --turn-screen-off
}
if (!opts->video) {
@@ -2962,6 +3077,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}
if (opts->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINED) {
LOGE("--display-ime-policy is only available with "
"--video-source=display");
return false;
}
if (opts->camera_id && opts->camera_facing != SC_CAMERA_FACING_ANY) {
LOGE("Cannot specify both --camera-id and --camera-facing");
return false;
@@ -3003,6 +3124,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}
if (opts->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINED
&& opts->display_id == 0 && !opts->new_display) {
LOGE("--display-ime-policy is only supported on a secondary display");
return false;
}
if (opts->audio && opts->audio_source == SC_AUDIO_SOURCE_AUTO) {
// Select the audio source according to the video source
if (opts->video_source == SC_VIDEO_SOURCE_DISPLAY) {

View File

@@ -75,6 +75,14 @@
# define SCRCPY_SDL_HAS_THREAD_PRIORITY_TIME_CRITICAL
#endif
#if SDL_VERSION_ATLEAST(2, 0, 18)
# define SCRCPY_SDL_HAS_HINT_APP_NAME
#endif
#if SDL_VERSION_ATLEAST(2, 0, 14)
# define SCRCPY_SDL_HAS_HINT_AUDIO_DEVICE_APP_NAME
#endif
#ifndef HAVE_STRDUP
char *strdup(const char *s);
#endif

View File

@@ -127,10 +127,14 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
return 32;
case SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
write_position(&buf[1], &msg->inject_scroll_event.position);
int16_t hscroll =
sc_float_to_i16fp(msg->inject_scroll_event.hscroll);
int16_t vscroll =
sc_float_to_i16fp(msg->inject_scroll_event.vscroll);
// Accept values in the range [-16, 16].
// Normalize to [-1, 1] in order to use sc_float_to_i16fp().
float hscroll_norm = msg->inject_scroll_event.hscroll / 16;
hscroll_norm = CLAMP(hscroll_norm, -1, 1);
float vscroll_norm = msg->inject_scroll_event.vscroll / 16;
vscroll_norm = CLAMP(vscroll_norm, -1, 1);
int16_t hscroll = sc_float_to_i16fp(hscroll_norm);
int16_t vscroll = sc_float_to_i16fp(vscroll_norm);
sc_write16be(&buf[13], (uint16_t) hscroll);
sc_write16be(&buf[15], (uint16_t) vscroll);
sc_write32be(&buf[17], msg->inject_scroll_event.buttons);
@@ -152,8 +156,10 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
return 2;
case SC_CONTROL_MSG_TYPE_UHID_CREATE:
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 = 3;
size_t index = 7;
index += write_string_tiny(&buf[index], msg->uhid_create.name, 127);
sc_write16be(&buf[index], msg->uhid_create.report_desc_size);
@@ -278,9 +284,13 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
// Quote only if name is not null
const char *name = msg->uhid_create.name;
const char *quote = name ? "\"" : "";
LOG_CMSG("UHID create [%" PRIu16 "] name=%s%s%s "
"report_desc_size=%" PRIu16, msg->uhid_create.id,
quote, name, quote, msg->uhid_create.report_desc_size);
LOG_CMSG("UHID create [%" PRIu16 "] %04" PRIx16 ":%04" PRIx16
" name=%s%s%s report_desc_size=%" PRIu16,
msg->uhid_create.id,
msg->uhid_create.vendor_id,
msg->uhid_create.product_id,
quote, name, quote,
msg->uhid_create.report_desc_size);
break;
}
case SC_CONTROL_MSG_TYPE_UHID_INPUT: {

View File

@@ -94,6 +94,8 @@ struct sc_control_msg {
} set_display_power;
struct {
uint16_t id;
uint16_t vendor_id;
uint16_t product_id;
const char *name; // pointer to static data
uint16_t report_desc_size;
const uint8_t *report_desc; // pointer to static data

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,8 @@
#include "common.h"
#include <stdbool.h>
#include <libavformat/avformat.h>
#include <stdint.h>
#include <libavutil/frame.h>
#include <SDL2/SDL.h>
#include "coords.h"
@@ -21,7 +22,7 @@ struct sc_display {
struct sc_opengl gl;
#ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE
SDL_GLContext *gl_context;
SDL_GLContext gl_context;
#endif
bool mipmaps;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
#include "hid_keyboard.h"
#include <assert.h>
#include <string.h>
#include "util/log.h"
@@ -335,7 +336,6 @@ 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) {
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_size = sizeof(SC_HID_KEYBOARD_REPORT_DESC);
}

View File

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

View File

@@ -1,8 +1,10 @@
#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 wheel motion
#define SC_HID_MOUSE_INPUT_SIZE 4
// 1 byte for wheel motion, 1 byte for hozizontal scrolling
#define SC_HID_MOUSE_INPUT_SIZE 5
/**
* Mouse descriptor from the specification:
@@ -73,6 +75,21 @@ static const uint8_t SC_HID_MOUSE_REPORT_DESC[] = {
// Input (Data, Variable, Relative): 3 position bytes (X, Y, Wheel)
0x81, 0x06,
// Usage Page (Consumer Page)
0x05, 0x0C,
// Usage(AC Pan)
0x0A, 0x38, 0x02,
// Logical Minimum (-127)
0x15, 0x81,
// Logical Maximum (127)
0x25, 0x7F,
// Report Size (8)
0x75, 0x08,
// Report Count (1)
0x95, 0x01,
// Input (Data, Variable, Relative): 1 byte (AC Pan)
0x81, 0x06,
// End Collection
0xC0,
@@ -158,7 +175,8 @@ sc_hid_mouse_generate_input_from_motion(struct sc_hid_input *hid_input,
data[0] = sc_hid_buttons_from_buttons_state(event->buttons_state);
data[1] = CLAMP(event->xrel, -127, 127);
data[2] = CLAMP(event->yrel, -127, 127);
data[3] = 0; // wheel coordinates only used for scrolling
data[3] = 0; // no vertical scrolling
data[4] = 0; // no horizontal scrolling
}
void
@@ -170,27 +188,31 @@ sc_hid_mouse_generate_input_from_click(struct sc_hid_input *hid_input,
data[0] = sc_hid_buttons_from_buttons_state(event->buttons_state);
data[1] = 0; // no x motion
data[2] = 0; // no y motion
data[3] = 0; // wheel coordinates only used for scrolling
data[3] = 0; // no vertical scrolling
data[4] = 0; // no horizontal scrolling
}
void
bool
sc_hid_mouse_generate_input_from_scroll(struct sc_hid_input *hid_input,
const struct sc_mouse_scroll_event *event) {
if (!event->vscroll_int && !event->hscroll_int) {
// Need a full integral value for HID
return false;
}
sc_hid_mouse_input_init(hid_input);
uint8_t *data = hid_input->data;
data[0] = 0; // buttons state irrelevant (and unknown)
data[1] = 0; // no x motion
data[2] = 0; // no y motion
// In practice, vscroll is always -1, 0 or 1, but in theory other values
// are possible
data[3] = CLAMP(event->vscroll, -127, 127);
// Horizontal scrolling ignored
data[3] = CLAMP(event->vscroll_int, -127, 127);
data[4] = CLAMP(event->hscroll_int, -127, 127);
return true;
}
void sc_hid_mouse_generate_open(struct sc_hid_open *hid_open) {
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_size = sizeof(SC_HID_MOUSE_REPORT_DESC);
}

View File

@@ -3,8 +3,6 @@
#include "common.h"
#include <stdbool.h>
#include "hid/hid_event.h"
#include "input_events.h"
@@ -24,7 +22,7 @@ void
sc_hid_mouse_generate_input_from_click(struct sc_hid_input *hid_input,
const struct sc_mouse_click_event *event);
void
bool
sc_hid_mouse_generate_input_from_scroll(struct sc_hid_input *hid_input,
const struct sc_mouse_scroll_event *event);

View File

@@ -2,17 +2,22 @@
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/pixdesc.h>
#include <libavutil/pixfmt.h>
#include <SDL2/SDL.h>
#include "config.h"
#include "compat.h"
#include "util/env.h"
#include "util/file.h"
#ifdef PORTABLE
# include "util/file.h"
#endif
#include "util/log.h"
#include "util/str.h"
#define SCRCPY_PORTABLE_ICON_FILENAME "icon.png"
#define SCRCPY_DEFAULT_ICON_PATH \

View File

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

View File

@@ -9,7 +9,6 @@
#include <SDL2/SDL_events.h>
#include "coords.h"
#include "options.h"
/* The representation of input events in scrcpy is very close to the SDL API,
* for simplicity.
@@ -394,6 +393,8 @@ struct sc_mouse_scroll_event {
struct sc_position position;
float hscroll;
float vscroll;
int32_t hscroll_int;
int32_t vscroll_int;
uint8_t buttons_state; // bitwise-OR of sc_mouse_button values
};
@@ -412,18 +413,12 @@ struct sc_touch_event {
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>:
// The ID value starts at 0 and increments from there. The value -1 is an
// invalid ID.
#define SC_GAMEPAD_ID_INVALID UINT32_C(-1)
struct sc_gamepad_device_event {
enum sc_gamepad_device_event_type type;
uint32_t gamepad_id;
};
@@ -503,16 +498,6 @@ sc_mouse_buttons_state_from_sdl(uint32_t 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
sc_gamepad_axis_from_sdl(uint8_t axis) {
if (axis <= SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {

View File

@@ -1,8 +1,12 @@
#include "input_manager.h"
#include <assert.h>
#include <SDL2/SDL_keycode.h>
#include <stdlib.h>
#include <string.h>
#include <SDL2/SDL.h>
#include "android/input.h"
#include "android/keycodes.h"
#include "input_events.h"
#include "screen.h"
#include "shortcut_mod.h"
@@ -893,12 +897,14 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
struct sc_mouse_scroll_event evt = {
.position = sc_input_manager_get_position(im, mouse_x, mouse_y),
#if SDL_VERSION_ATLEAST(2, 0, 18)
.hscroll = CLAMP(event->preciseX, -1.0f, 1.0f),
.vscroll = CLAMP(event->preciseY, -1.0f, 1.0f),
.hscroll = event->preciseX,
.vscroll = event->preciseY,
#else
.hscroll = CLAMP(event->x, -1, 1),
.vscroll = CLAMP(event->y, -1, 1),
.hscroll = event->x,
.vscroll = event->y,
#endif
.hscroll_int = event->x,
.vscroll_int = event->y,
.buttons_state = im->mouse_buttons_state,
};
@@ -908,7 +914,6 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
static void
sc_input_manager_process_gamepad_device(struct sc_input_manager *im,
const SDL_ControllerDeviceEvent *event) {
SDL_JoystickID id;
if (event->type == SDL_CONTROLLERDEVICEADDED) {
SDL_GameController *gc = SDL_GameControllerOpen(event->which);
if (!gc) {
@@ -923,9 +928,12 @@ sc_input_manager_process_gamepad_device(struct sc_input_manager *im,
return;
}
id = SDL_JoystickInstanceID(joystick);
struct sc_gamepad_device_event evt = {
.gamepad_id = SDL_JoystickInstanceID(joystick),
};
im->gp->ops->process_gamepad_added(im->gp, &evt);
} else if (event->type == SDL_CONTROLLERDEVICEREMOVED) {
id = event->which;
SDL_JoystickID id = event->which;
SDL_GameController *gc = SDL_GameControllerFromInstanceID(id);
if (gc) {
@@ -933,16 +941,15 @@ sc_input_manager_process_gamepad_device(struct sc_input_manager *im,
} else {
LOGW("Unknown gamepad device removed");
}
struct sc_gamepad_device_event evt = {
.gamepad_id = id,
};
im->gp->ops->process_gamepad_removed(im->gp, &evt);
} else {
// Nothing to do
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,7 @@
#include "options.h"
#include <stddef.h>
const struct scrcpy_options scrcpy_options_default = {
.serial = NULL,
.crop = NULL,
@@ -54,6 +56,7 @@ const struct scrcpy_options scrcpy_options_default = {
.capture_orientation_lock = SC_ORIENTATION_UNLOCKED,
.display_orientation = SC_ORIENTATION_0,
.record_orientation = SC_ORIENTATION_0,
.display_ime_policy = SC_DISPLAY_IME_POLICY_UNDEFINED,
.window_x = SC_WINDOW_POSITION_UNDEFINED,
.window_y = SC_WINDOW_POSITION_UNDEFINED,
.window_width = 0,
@@ -108,6 +111,7 @@ const struct scrcpy_options scrcpy_options_default = {
.new_display = NULL,
.start_app = NULL,
.angle = NULL,
.vd_destroy_content = true,
.vd_system_decorations = true,
};

View File

@@ -5,7 +5,6 @@
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "util/tick.h"
@@ -60,6 +59,14 @@ enum sc_audio_source {
SC_AUDIO_SOURCE_OUTPUT,
SC_AUDIO_SOURCE_MIC,
SC_AUDIO_SOURCE_PLAYBACK,
SC_AUDIO_SOURCE_MIC_UNPROCESSED,
SC_AUDIO_SOURCE_MIC_CAMCORDER,
SC_AUDIO_SOURCE_MIC_VOICE_RECOGNITION,
SC_AUDIO_SOURCE_MIC_VOICE_COMMUNICATION,
SC_AUDIO_SOURCE_VOICE_CALL,
SC_AUDIO_SOURCE_VOICE_CALL_UPLINK,
SC_AUDIO_SOURCE_VOICE_CALL_DOWNLINK,
SC_AUDIO_SOURCE_VOICE_PERFORMANCE,
};
enum sc_camera_facing {
@@ -90,6 +97,13 @@ enum sc_orientation_lock {
SC_ORIENTATION_LOCKED_INITIAL, // lock to initial device orientation
};
enum sc_display_ime_policy {
SC_DISPLAY_IME_POLICY_UNDEFINED,
SC_DISPLAY_IME_POLICY_LOCAL,
SC_DISPLAY_IME_POLICY_FALLBACK,
SC_DISPLAY_IME_POLICY_HIDE,
};
static inline bool
sc_orientation_is_mirror(enum sc_orientation orientation) {
assert(!(orientation & ~7));
@@ -252,6 +266,7 @@ struct scrcpy_options {
enum sc_orientation_lock capture_orientation_lock;
enum sc_orientation display_orientation;
enum sc_orientation record_orientation;
enum sc_display_ime_policy display_ime_policy;
int16_t window_x; // SC_WINDOW_POSITION_UNDEFINED for "auto"
int16_t window_y; // SC_WINDOW_POSITION_UNDEFINED for "auto"
uint16_t window_width;
@@ -310,6 +325,7 @@ struct scrcpy_options {
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;
};

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,9 @@
#include "recorder.h"
#include <assert.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/time.h>

View File

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

View File

@@ -1,10 +1,11 @@
#include "scrcpy.h"
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libavformat/avformat.h>
#include <sys/time.h>
#include <SDL2/SDL.h>
#ifdef _WIN32
@@ -37,9 +38,9 @@
#endif
#include "util/acksync.h"
#include "util/log.h"
#include "util/net.h"
#include "util/rand.h"
#include "util/timeout.h"
#include "util/tick.h"
#ifdef HAVE_V4L2
# include "v4l2_sink.h"
#endif
@@ -92,7 +93,7 @@ struct scrcpy {
#ifdef _WIN32
static BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
if (ctrl_type == CTRL_C_EVENT) {
if (ctrl_type == CTRL_C_EVENT || ctrl_type == CTRL_BREAK_EVENT) {
sc_push_event(SDL_QUIT);
return TRUE;
}
@@ -106,6 +107,17 @@ sdl_set_hints(const char *render_driver) {
LOGW("Could not set render driver");
}
// App name used in various contexts (such as PulseAudio)
#if defined(SCRCPY_SDL_HAS_HINT_APP_NAME)
if (!SDL_SetHint(SDL_HINT_APP_NAME, "scrcpy")) {
LOGW("Could not set app name");
}
#elif defined(SCRCPY_SDL_HAS_HINT_AUDIO_DEVICE_APP_NAME)
if (!SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "scrcpy")) {
LOGW("Could not set audio device app name");
}
#endif
// Linear filtering
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) {
LOGW("Could not enable linear filtering");
@@ -164,7 +176,7 @@ sdl_configure(bool video_playback, bool disable_screensaver) {
}
static enum scrcpy_exit_code
event_loop(struct scrcpy *s) {
event_loop(struct scrcpy *s, bool has_screen) {
SDL_Event event;
while (SDL_WaitEvent(&event)) {
switch (event.type) {
@@ -196,7 +208,7 @@ event_loop(struct scrcpy *s) {
break;
}
default:
if (!sc_screen_handle_event(&s->screen, &event)) {
if (has_screen && !sc_screen_handle_event(&s->screen, &event)) {
return SCRCPY_EXIT_FAILURE;
}
break;
@@ -435,6 +447,7 @@ scrcpy(struct scrcpy_options *options) {
.control = options->control,
.display_id = options->display_id,
.new_display = options->new_display,
.display_ime_policy = options->display_ime_policy,
.video = options->video,
.audio = options->audio,
.audio_dup = options->audio_dup,
@@ -458,6 +471,7 @@ scrcpy(struct scrcpy_options *options) {
.power_on = options->power_on,
.kill_adb_on_close = options->kill_adb_on_close,
.camera_high_speed = options->camera_high_speed,
.vd_destroy_content = options->vd_destroy_content,
.vd_system_decorations = options->vd_system_decorations,
.list = options->list,
};
@@ -930,7 +944,7 @@ aoa_complete:
}
}
ret = event_loop(s);
ret = event_loop(s, options->window);
terminate_event_loop();
LOGD("quit...");

View File

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

View File

@@ -225,7 +225,7 @@ sc_screen_render_novideo(struct sc_screen *screen) {
(void) res; // any error already logged
}
#if defined(__APPLE__) || defined(__WINDOWS__)
#if defined(__APPLE__) || defined(_WIN32)
# define CONTINUOUS_RESIZING_WORKAROUND
#endif
@@ -409,7 +409,7 @@ sc_screen_init(struct sc_screen *screen,
} else {
// without video, the icon is used as window content, it must be present
LOGE("Could not load icon");
goto error_destroy_fps_counter;
goto error_destroy_window;
}
SDL_Surface *icon_novideo = params->video ? NULL : icon;

View File

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

View File

@@ -1,19 +1,18 @@
#include "server.h"
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <SDL2/SDL_timer.h>
#include <SDL2/SDL_platform.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "adb/adb.h"
#include "util/binary.h"
#include "util/env.h"
#include "util/file.h"
#include "util/log.h"
#include "util/net_intr.h"
#include "util/process_intr.h"
#include "util/process.h"
#include "util/str.h"
#define SC_SERVER_FILENAME "scrcpy-server"
@@ -150,12 +149,43 @@ sc_server_get_audio_source_name(enum sc_audio_source audio_source) {
return "mic";
case SC_AUDIO_SOURCE_PLAYBACK:
return "playback";
case SC_AUDIO_SOURCE_MIC_UNPROCESSED:
return "mic-unprocessed";
case SC_AUDIO_SOURCE_MIC_CAMCORDER:
return "mic-camcorder";
case SC_AUDIO_SOURCE_MIC_VOICE_RECOGNITION:
return "mic-voice-recognition";
case SC_AUDIO_SOURCE_MIC_VOICE_COMMUNICATION:
return "mic-voice-communication";
case SC_AUDIO_SOURCE_VOICE_CALL:
return "voice-call";
case SC_AUDIO_SOURCE_VOICE_CALL_UPLINK:
return "voice-call-uplink";
case SC_AUDIO_SOURCE_VOICE_CALL_DOWNLINK:
return "voice-call-downlink";
case SC_AUDIO_SOURCE_VOICE_PERFORMANCE:
return "voice-performance";
default:
assert(!"unexpected audio source");
return NULL;
}
}
static const char *
sc_server_get_display_ime_policy_name(enum sc_display_ime_policy policy) {
switch (policy) {
case SC_DISPLAY_IME_POLICY_LOCAL:
return "local";
case SC_DISPLAY_IME_POLICY_FALLBACK:
return "fallback";
case SC_DISPLAY_IME_POLICY_HIDE:
return "hide";
default:
assert(!"unexpected display IME policy");
return NULL;
}
}
static bool
validate_string(const char *s) {
// The parameters values are passed as command line arguments to adb, so
@@ -377,6 +407,13 @@ execute_server(struct sc_server *server,
VALIDATE_STRING(params->new_display);
ADD_PARAM("new_display=%s", params->new_display);
}
if (params->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINED) {
ADD_PARAM("display_ime_policy=%s",
sc_server_get_display_ime_policy_name(params->display_ime_policy));
}
if (!params->vd_destroy_content) {
ADD_PARAM("vd_destroy_content=false");
}
if (!params->vd_system_decorations) {
ADD_PARAM("vd_system_decorations=false");
}
@@ -829,11 +866,14 @@ sc_server_switch_to_tcpip(struct sc_server *server, const char *serial) {
}
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;
// Error expected if not connected, do not report any error
sc_adb_disconnect(intr, ip_port, SC_ADB_SILENT);
if (disconnect) {
// Error expected if not connected, do not report any error
sc_adb_disconnect(intr, ip_port, SC_ADB_SILENT);
}
LOGI("Connecting to %s...", ip_port);
@@ -849,7 +889,7 @@ sc_server_connect_to_tcpip(struct sc_server *server, const char *ip_port) {
static bool
sc_server_configure_tcpip_known_address(struct sc_server *server,
const char *addr) {
const char *addr, bool disconnect) {
// Append ":5555" if no port is present
bool contains_port = strchr(addr, ':');
char *ip_port = contains_port ? strdup(addr)
@@ -860,7 +900,7 @@ sc_server_configure_tcpip_known_address(struct sc_server *server,
}
server->serial = ip_port;
return sc_server_connect_to_tcpip(server, ip_port);
return sc_server_connect_to_tcpip(server, ip_port, disconnect);
}
static bool
@@ -885,7 +925,7 @@ sc_server_configure_tcpip_unknown_address(struct sc_server *server,
}
server->serial = ip_port;
return sc_server_connect_to_tcpip(server, ip_port);
return sc_server_connect_to_tcpip(server, ip_port, false);
}
static void
@@ -972,7 +1012,13 @@ run_server(void *data) {
sc_adb_device_destroy(&device);
}
} else {
ok = sc_server_configure_tcpip_known_address(server, params->tcpip_dst);
// If the user passed a '+' (--tcpip=+ip), then disconnect first
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) {
goto error_connection_failed;
}

View File

@@ -1,19 +1,17 @@
#ifndef SERVER_H
#define SERVER_H
#ifndef SC_SERVER_H
#define SC_SERVER_H
#include "common.h"
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include "adb/adb_tunnel.h"
#include "coords.h"
#include "options.h"
#include "util/intr.h"
#include "util/log.h"
#include "util/net.h"
#include "util/thread.h"
#include "util/tick.h"
#define SC_DEVICE_NAME_FIELD_LENGTH 64
struct sc_server_info {
@@ -52,6 +50,7 @@ struct sc_server_params {
bool control;
uint32_t display_id;
const char *new_display;
enum sc_display_ime_policy display_ime_policy;
bool video;
bool audio;
bool audio_dup;
@@ -69,6 +68,7 @@ struct sc_server_params {
bool power_on;
bool kill_adb_on_close;
bool camera_high_speed;
bool vd_destroy_content;
bool vd_system_decorations;
uint8_t list;
};

View File

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

View File

@@ -1,10 +1,11 @@
#include "util/file.h"
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef __APPLE__
# include <mach-o/dyld.h> // for _NSGetExecutablePath()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,10 @@
#include "gamepad_uhid.h"
#include <assert.h>
#include <inttypes.h>
#include <string.h>
#include <SDL2/SDL_gamecontroller.h>
#include "hid/hid_gamepad.h"
#include "input_events.h"
#include "util/log.h"
@@ -7,6 +12,11 @@
/** Downcast gamepad processor to sc_gamepad_uhid */
#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
sc_gamepad_uhid_send_input(struct sc_gamepad_uhid *gamepad,
const struct sc_hid_input *hid_input,
@@ -30,7 +40,9 @@ sc_gamepad_uhid_send_open(struct sc_gamepad_uhid *gamepad,
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE;
msg.uhid_create.id = hid_open->hid_id;
msg.uhid_create.name = hid_open->name;
msg.uhid_create.vendor_id = SC_GAMEPAD_UHID_VENDOR_ID;
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_size = hid_open->report_desc_size;
@@ -52,29 +64,39 @@ sc_gamepad_uhid_send_close(struct sc_gamepad_uhid *gamepad,
}
static void
sc_gamepad_processor_process_gamepad_device(struct sc_gamepad_processor *gp,
sc_gamepad_processor_process_gamepad_added(struct sc_gamepad_processor *gp,
const struct sc_gamepad_device_event *event) {
struct sc_gamepad_uhid *gamepad = DOWNCAST(gp);
if (event->type == SC_GAMEPAD_DEVICE_ADDED) {
struct sc_hid_open hid_open;
if (!sc_hid_gamepad_generate_open(&gamepad->hid, &hid_open,
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);
struct sc_hid_open hid_open;
if (!sc_hid_gamepad_generate_open(&gamepad->hid, &hid_open,
event->gamepad_id)) {
return;
}
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
@@ -114,7 +136,8 @@ sc_gamepad_uhid_init(struct sc_gamepad_uhid *gamepad,
gamepad->controller = controller;
static const struct sc_gamepad_processor_ops ops = {
.process_gamepad_device = sc_gamepad_processor_process_gamepad_device,
.process_gamepad_added = sc_gamepad_processor_process_gamepad_added,
.process_gamepad_removed = sc_gamepad_processor_process_gamepad_removed,
.process_gamepad_axis = sc_gamepad_processor_process_gamepad_axis,
.process_gamepad_button = sc_gamepad_processor_process_gamepad_button,
};

View File

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

View File

@@ -1,6 +1,12 @@
#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/thread.h"
/** Downcast key processor to keyboard_uhid */
#define DOWNCAST(KP) container_of(KP, struct sc_keyboard_uhid, key_processor)
@@ -141,7 +147,9 @@ sc_keyboard_uhid_init(struct sc_keyboard_uhid *kb,
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE;
msg.uhid_create.id = SC_HID_ID_KEYBOARD;
msg.uhid_create.name = hid_open.name;
msg.uhid_create.vendor_id = 0;
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_size = hid_open.report_desc_size;
if (!sc_controller_push_msg(controller, &msg)) {

View File

@@ -1,5 +1,8 @@
#include "mouse_uhid.h"
#include <assert.h>
#include <string.h>
#include "hid/hid_mouse.h"
#include "input_events.h"
#include "util/log.h"
@@ -52,7 +55,9 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
struct sc_mouse_uhid *mouse = DOWNCAST(mp);
struct sc_hid_input hid_input;
sc_hid_mouse_generate_input_from_scroll(&hid_input, event);
if (!sc_hid_mouse_generate_input_from_scroll(&hid_input, event)) {
return;
}
sc_mouse_uhid_send_input(mouse, &hid_input, "mouse scroll");
}
@@ -81,7 +86,9 @@ sc_mouse_uhid_init(struct sc_mouse_uhid *mouse,
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE;
msg.uhid_create.id = SC_HID_ID_MOUSE;
msg.uhid_create.name = hid_open.name;
msg.uhid_create.vendor_id = 0;
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_size = hid_open.report_desc_size;
if (!sc_controller_push_msg(controller, &msg)) {

View File

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

View File

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

View File

@@ -1,13 +1,16 @@
#include "util/log.h"
#include "aoa_hid.h"
#include <assert.h>
#include <inttypes.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 "util/log.h"
#include "util/str.h"
#include "util/tick.h"
#include "util/vector.h"
// See <https://source.android.com/devices/accessories/aoa2#hid-support>.

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
#include "mouse_aoa.h"
#include <assert.h>
#include <stddef.h>
#include "hid/hid_mouse.h"
#include "input_events.h"
@@ -41,7 +42,9 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
struct sc_mouse_aoa *mouse = DOWNCAST(mp);
struct sc_hid_input hid_input;
sc_hid_mouse_generate_input_from_scroll(&hid_input, event);
if (!sc_hid_mouse_generate_input_from_scroll(&hid_input, event)) {
return;
}
if (!sc_aoa_push_input(mouse->aoa, &hid_input)) {
LOGW("Could not push AOA HID input (mouse scroll)");

View File

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

View File

@@ -1,10 +1,19 @@
#include "scrcpy_otg.h"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <SDL2/SDL.h>
#include "adb/adb.h"
#ifdef _WIN32
# include "adb/adb.h"
#endif
#include "events.h"
#include "screen_otg.h"
#include "usb/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"
struct scrcpy_otg {

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