Similar to UHID keyboard and mouse, but for gamepads.
Can be enabled with --gamepad=uhid or -G.
It is not enabled by default because not all devices support UHID
(there is a permission error on old Android versions).
PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
This message will be sent on gamepad disconnection.
Contrary to keyboard and mouse devices, which are registered once and
unregistered when scrcpy exists, each physical gamepad is mapped with
its own HID id, and they can be plugged and unplugged dynamically.
PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
Trigger SDL_CONTROLLERDEVICEADDED for all gamepads already connected
when scrcpy starts. We want to handle both the gamepads initially
connected and the gamepads connected while scrcpy is running.
This is not racy, because this event may not be trigged automatically
until SDL events are "pumped" (SDL_PumpEvents/SDL_WaitEvent).
PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
Introduce a gamepad processor trait, similar to the keyboard processor
and mouse processor traits.
Handle gamepad events received from SDL, convert them to scrcpy-specific
gamepad events, and forward them to the gamepad processor.
Further commits will provide AOA and UHID implementations of the gamepad
processor trait.
PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
Co-authored-by: Luiz Henrique Laurini <luizhenriquelaurini@gmail.com>
Mouse and keyboard events with unknown button/keycode/scancode cannot be
handled properly. Discard them without forwarding them to the
keyboard or mouse processors.
This can happen for example if a more recent version of SDL introduces
new enum values.
PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
Now that the AOA open/close are asynchronous, an open error did not make
scrcpy exit anymore.
Add a mechanism to exit if the AOA device could not be opened
asynchronously.
PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
Pushing a close event from the keyboard_aoa or mouse_aoa implementation
was racy, because the AOA thread might be stopped before these events
were processed.
Instead, keep the list of open AOA devices to close them automatically
from the AOA thread before exiting.
PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
This allows to handle HID open/close reports at the same place as HID
input reports (in the HID layer).
This will be especially useful to manage HID gamepads, to avoid
implementing one part in the HID layer and another part in the gamepad
processor implementation.
PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
For AOA keyboard and mouse, only input reports were asynchronous.
Register/unregister were called from the main thread.
This had the benefit to fail immediately if the AOA registration failed,
but we want to open/close AOA devices dynamically in order to add
gamepad support.
PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
The HID ids (accessory ids or UHID ids) were defined by the keyboard and
mouse implementations.
Instead, define them in the common HID part, and make that id part of
the sc_hid_event.
This prepares the introduction of gamepad support, which will handle
several gamepads (and ids) in the common HID gamepad code.
PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
Control messages are queued from the main thread and sent to the device
from a separate thread.
When the queue is full, messages are just dropped. This avoids to
accumulate too much delay between the client and the device in case of
network issue.
However, some messages should not be dropped: for example, dropping a
UHID_CREATE message would make all further UHID_INPUT messages invalid.
Therefore, mark these messages as non-droppable.
A non-droppable event is queued anyway (resizing the queue if
necessary, unless the allocation fails).
PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
This allows to schedule a runnable to be executed on the main thread,
until the event loop is explicitly terminated.
It is guaranteed that all accepted runnables will be executed (this
avoids possible memory leaks if a runnable owns resources).
PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
It is better to disable Nagle's algorithm to avoid unnecessary latency
for control messages. (I'm not sure this has any impact for a local TCP
socket though.)
Many parsing and formatting C functions like strtof() and asprintf() are
locale-dependent. Forcing a C locale just for the conversions in a way
that works on all platforms is a mess.
In practice, this is not a problem, scrcpy always uses the C locale,
because it never calls:
setlocale(LC_ALL, "");
But the max-fps option should not depend on the locale configuration
anyway.
Since the value is parsed by the client in Java anyway, just forward the
string value as is.
Android accepts a float value, there is no reason to limit the option
to be an integer.
In particular, it allows to capture at a rate lower than 1 fps. For
example, to capture 1 frame every 5 seconds:
scrcpy --video-source=camera --max-fps=0.2
It was already possible to pass a float manually:
scrcpy --video-source=camera \
--video-codec-options=max-fps-to-encoder:float=0.2
But accepting a float directly for --max-fps is more convenient.
Refs <https://developer.android.com/reference/android/media/MediaFormat#KEY_MAX_FPS_TO_ENCODER>
A video width or height of 0 triggered an assert.
Fail explicitly instead: the server may actually send this size in
practice (for example on cropping with small dimensions, even if the
requested crop size is not 0).
The delay buffer clock estimates the clock offset between the PTS and
the frame decoded date using an "Exponentially Weighted Moving Average"
(EWMA).
But for the first frames, the clock have less than SC_CLOCK_RANGE
points to average. Since the timing for the first frames are typically
the worst ones, give more weight to the last point for the estimation.
Once SC_CLOCK_RANGE points are available (i.e. when SC_CLOCK_RANGE ==
clock->range), the new estimation is equivalent to the previous version.
The delay buffer `stopped` field was not initialized.
Since it practice the unique instance of sc_delay_buffer is initialized
in static memory, the flag was initialized to false as a side effect.
But with commit fd0f432e87, in debug mode
only, the delay buffer was broken.
"Could not" implies that the system tried to disable the option but
encountered an issue or failure.
"Cannot" indicates a rule or restriction, meaning it's not possible to
perform the action at all.
In Java, control messages were parsed using manual buffering, which was
convoluted and error-prone.
Instead, read the socket directly through a DataInputStream and a
BufferedInputStream. Symmetrically, use a DataOutputStream and a
BufferedOutputStream to write messages.