2
0
mirror of https://github.com/Genymobile/scrcpy synced 2025-08-22 01:47:37 +00:00

Compare commits

...

15 Commits

Author SHA1 Message Date
Romain Vimont
8bde814155 Build SDL3 for test step on Github Actions
The latest Ubuntu does not provide the SDL3 package yet.
2025-07-17 18:35:25 +02:00
Romain Vimont
9cf66950c5 Upgrade SDL build script for SDL3 2025-07-17 18:35:25 +02:00
Romain Vimont
77cd5d9aef Migrate from SDL2 to SDL3
Refs <https://wiki.libsdl.org/SDL3/README-migration>
2025-07-17 18:35:25 +02:00
Romain Vimont
d7e0d36fe6 Improve color space conversion
Use both the color space and color range from FFmpeg to determine the
appropriate SDL YUV conversion mode.
2025-07-17 18:35:25 +02:00
Romain Vimont
0b840dcef1 Set color range during texture creation
This prepares for the migration to SDL3, where the color range can only
be specified at the time of texture creation.
2025-07-17 18:35:25 +02:00
Romain Vimont
023fad8bad Create texture on first frame
The texture was created as soon as the initial video size was known,
even before the first frame arrived.

However, texture creation will require other data, such as the color
range, which is only available once the first frame is received.

Therefore, delay texture creation until the first frame.
2025-07-17 18:35:25 +02:00
Romain Vimont
6a0529ca13 Remove prepare_for_frame()
Inline the function body in its only caller.

This will simplify further refactors.
2025-07-17 18:35:25 +02:00
Romain Vimont
78c4674d97 Handle mouse integer scrolling locally
Do not rely on SDL to expose integer scroll values, as they are not
available in all versions.

It was reimplemented in SDL 3.2.12 by this commit:
<0447c2f3c3>

Refs #6156 <https://github.com/Genymobile/scrcpy/issues/6156>
2025-07-17 18:35:25 +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] cc0902b13c87fc98b1ed90b0700cc53ac4d7ee3c
Refs [2] 91373d906b100349de959f49172d4605f66f64b2
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
49 changed files with 816 additions and 753 deletions

View File

@ -80,8 +80,16 @@ jobs:
libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \
libv4l-dev
# SDL3 is not available in Ubuntu yet
- name: Install SDL3
run: |
app/deps/sdl.sh linux native shared
- name: Test
run: release/test_client.sh
run: |
export PKG_CONFIG_PATH="$PWD"/app/deps/work/install/linux-native-shared/lib/pkgconfig
export LD_LIBRARY_PATH="$PWD"/app/deps/work/install/linux-native-shared/lib
release/test_client.sh
build-linux-x86_64:
runs-on: ubuntu-22.04
@ -202,8 +210,7 @@ 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:
@ -245,7 +252,7 @@ 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

View File

@ -7,7 +7,7 @@ cd "$DEPS_DIR"
VERSION=36.0.0
FILENAME=platform-tools_r$VERSION-darwin.zip
PROJECT_DIR=platform-tools-$VERSION-darwin
SHA256SUM=b241878e6ec20650b041bf715ea05f7d5dc73bd24529464bd9cf68946e3132bd
SHA256SUM=d3e9fa1df3345cf728586908426615a60863d2632f73f1ce14f0f1349ef000fd
cd "$SOURCES_DIR"

View File

@ -7,7 +7,7 @@ cd "$DEPS_DIR"
VERSION=36.0.0
FILENAME=platform-tools_r$VERSION-win.zip
PROJECT_DIR=platform-tools-$VERSION-windows
SHA256SUM=24bd8bebbbb58b9870db202b5c6775c4a49992632021c60750d9d8ec8179d5f0
SHA256SUM=12c2841f354e92a0eb2fd7bf6f0f9bf8538abce7bd6b060ac8349d6f6a61107c
cd "$SOURCES_DIR"

View File

@ -5,10 +5,10 @@ cd "$DEPS_DIR"
. common
process_args "$@"
VERSION=2.32.8
VERSION=3.2.18
FILENAME=SDL-$VERSION.tar.gz
PROJECT_DIR=SDL-release-$VERSION
SHA256SUM=dd35e05644ae527848d02433bec24dd0ea65db59faecf1a0e5d1880c533dac2c
SHA256SUM=51539fa13e546bc50c632beed3f34257de2baa38a4c642048de56377903b4265
cd "$SOURCES_DIR"
@ -35,45 +35,48 @@ else
cd "$DIRNAME"
conf=(
--prefix="$INSTALL_DIR/$DIRNAME"
-DCMAKE_INSTALL_PREFIX="$INSTALL_DIR/$DIRNAME"
)
if [[ "$HOST" == linux ]]
then
conf+=(
--enable-video-wayland
--enable-video-x11
-DSDL_WAYLAND=ON
-DSDL_X11=ON
)
fi
if [[ "$LINK_TYPE" == static ]]
then
conf+=(
--enable-static
--disable-shared
-DBUILD_SHARED_LIBS=OFF
)
else
conf+=(
--disable-static
--enable-shared
-DBUILD_SHARED_LIBS=ON
)
fi
if [[ "$BUILD_TYPE" == cross ]]
then
if [[ "$HOST" = win32 ]]
then
TOOLCHAIN_FILENAME="cmake-toolchain-mingw64-i686.cmake"
elif [[ "$HOST" = win64 ]]
then
TOOLCHAIN_FILENAME="cmake-toolchain-mingw64-x86_64.cmake"
else
echo "Unsupported cross-build to host: $HOST" >&2
exit 1
fi
conf+=(
--host="$HOST_TRIPLET"
-DCMAKE_TOOLCHAIN_FILE="$SOURCES_DIR/$PROJECT_DIR/build-scripts/$TOOLCHAIN_FILENAME"
)
fi
"$SOURCES_DIR/$PROJECT_DIR"/configure "${conf[@]}"
cmake "$SOURCES_DIR/$PROJECT_DIR" "${conf[@]}"
fi
make -j
# There is no "make install-strip"
make install
# Strip manually
if [[ "$LINK_TYPE" == shared && "$HOST" == win* ]]
then
${HOST_TRIPLET}-strip "$INSTALL_DIR/$DIRNAME/bin/SDL2.dll"
fi
cmake --build .
cmake --install .

View File

@ -63,6 +63,7 @@ src = [
'src/util/thread.c',
'src/util/tick.c',
'src/util/timeout.c',
'src/util/window.c',
]
conf = configuration_data()
@ -117,7 +118,7 @@ dependencies = [
dependency('libavcodec', version: '>= 57.37', static: static),
dependency('libavutil', static: static),
dependency('libswresample', static: static),
dependency('sdl2', version: '>= 2.0.5', static: static),
dependency('sdl3', version: '>= 3.2.0', static: static),
]
if v4l2_support

View File

@ -103,7 +103,7 @@ 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;
@ -331,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);
@ -351,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
@ -362,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);
@ -377,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

View File

@ -1,23 +1,40 @@
#include "audio_player.h"
#include "util/log.h"
#include "SDL3/SDL_hints.h"
/** Downcast frame_sink to sc_audio_player */
#define DOWNCAST(SINK) container_of(SINK, struct sc_audio_player, frame_sink)
#define SC_SDL_SAMPLE_FMT AUDIO_F32
#define SC_SDL_SAMPLE_FMT SDL_AUDIO_F32LE
static void SDLCALL
sc_audio_player_sdl_callback(void *userdata, uint8_t *stream, int len_int) {
sc_audio_player_stream_callback(void *userdata, SDL_AudioStream *stream,
int additional_amount, int total_amount) {
(void) total_amount;
struct sc_audio_player *ap = userdata;
assert(len_int > 0);
size_t len = len_int;
if (additional_amount > 0) {
size_t len = additional_amount;
assert(len % ap->audioreg.sample_size == 0);
uint32_t out_samples = len / ap->audioreg.sample_size;
assert(len <= ap->aout_buffer_size);
if (len > ap->aout_buffer_size) {
// Just in case for release builds
LOGE("Unexpected SDL audio behavior: too much data requested");
len = ap->aout_buffer_size;
}
sc_audio_regulator_pull(&ap->audioreg, stream, out_samples);
assert(len % ap->audioreg.sample_size == 0);
uint32_t out_samples = len / ap->audioreg.sample_size;
sc_audio_regulator_pull(&ap->audioreg, ap->aout_buffer, out_samples);
bool ok = SDL_PutAudioStreamData(stream, ap->aout_buffer, len);
SDL_stack_free(data);
if (!ok) {
LOGW("Audio stream error: %s", SDL_GetError());
}
}
}
static bool
@ -61,23 +78,42 @@ sc_audio_player_frame_sink_open(struct sc_frame_sink *sink,
/ SC_TICK_FREQ;
assert(aout_samples <= 0xFFFF);
SDL_AudioSpec desired = {
.freq = ctx->sample_rate,
.format = SC_SDL_SAMPLE_FMT,
.channels = nb_channels,
.samples = aout_samples,
.callback = sc_audio_player_sdl_callback,
.userdata = ap,
};
SDL_AudioSpec obtained;
ap->device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0);
if (!ap->device) {
LOGE("Could not open audio device: %s", SDL_GetError());
char str[5 + 1]; // max 65535
int r = snprintf(str, sizeof(str), "%" PRIu16, (uint16_t) aout_samples);
assert(r >= 0 && (size_t) r < sizeof(str));
(void) r;
if (!SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, str)) {
LOGE("Could not set audio output buffer");
sc_audio_regulator_destroy(&ap->audioreg);
return false;
}
ap->aout_buffer_size = aout_samples * sample_size;
ap->aout_buffer = malloc(ap->aout_buffer_size);
if (!ap->aout_buffer) {
sc_audio_regulator_destroy(&ap->audioreg);
return false;
}
SDL_AudioSpec spec = {
.freq = ctx->sample_rate,
.format = SC_SDL_SAMPLE_FMT,
.channels = nb_channels,
};
ap->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK,
&spec,
sc_audio_player_stream_callback, ap);
if (!ap->stream) {
LOGE("Could not open audio device: %s", SDL_GetError());
free(ap->aout_buffer);
sc_audio_regulator_destroy(&ap->audioreg);
return false;
}
ap->device = SDL_GetAudioStreamDevice(ap->stream);
assert(ap->device);
// The thread calling open() is the thread calling push(), which fills the
// audio buffer consumed by the SDL audio thread.
ok = sc_thread_set_priority(SC_THREAD_PRIORITY_TIME_CRITICAL);
@ -86,7 +122,7 @@ sc_audio_player_frame_sink_open(struct sc_frame_sink *sink,
(void) ok; // We don't care if it worked, at least we tried
}
SDL_PauseAudioDevice(ap->device, 0);
SDL_ResumeAudioDevice(ap->device);
return true;
}
@ -95,11 +131,16 @@ static void
sc_audio_player_frame_sink_close(struct sc_frame_sink *sink) {
struct sc_audio_player *ap = DOWNCAST(sink);
assert(ap->stream);
assert(ap->device);
SDL_PauseAudioDevice(ap->device, 1);
SDL_CloseAudioDevice(ap->device);
SDL_PauseAudioDevice(ap->device);
// ap->device is owned by ap->stream
SDL_DestroyAudioStream(ap->stream);
sc_audio_regulator_destroy(&ap->audioreg);
free(ap->aout_buffer);
}
void

View File

@ -3,7 +3,7 @@
#include "common.h"
#include <SDL2/SDL_audio.h>
#include <SDL3/SDL_audio.h>
#include "audio_regulator.h"
#include "trait/frame_sink.h"
@ -22,7 +22,11 @@ struct sc_audio_player {
// SDL audio output buffer size
sc_tick output_buffer_duration;
SDL_AudioDeviceID device;
uint8_t *aout_buffer;
size_t aout_buffer_size;
SDL_AudioStream *stream;
SDL_AudioDeviceID device; // owned by the audio stream
struct sc_audio_regulator audioreg;
};

View File

@ -6,7 +6,7 @@
#include <libavcodec/version.h>
#include <libavformat/version.h>
#include <libavutil/version.h>
#include <SDL2/SDL_version.h>
#include <SDL3/SDL_version.h>
#ifndef _WIN32
# define PRIu64_ PRIu64
@ -61,28 +61,6 @@
# define SCRCPY_LAVC_HAS_CODECPAR_CODEC_SIDEDATA
#endif
#if SDL_VERSION_ATLEAST(2, 0, 6)
// <https://github.com/libsdl-org/SDL/commit/d7a318de563125e5bb465b1000d6bc9576fbc6fc>
# define SCRCPY_SDL_HAS_HINT_TOUCH_MOUSE_EVENTS
#endif
#if SDL_VERSION_ATLEAST(2, 0, 8)
// <https://hg.libsdl.org/SDL/rev/dfde5d3f9781>
# define SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
#endif
#if SDL_VERSION_ATLEAST(2, 0, 16)
# 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

@ -12,8 +12,11 @@ sc_display_init_novideo_icon(struct sc_display *display,
SDL_Surface *icon_novideo) {
assert(icon_novideo);
if (SDL_RenderSetLogicalSize(display->renderer,
icon_novideo->w, icon_novideo->h)) {
bool ok = SDL_SetRenderLogicalPresentation(display->renderer,
icon_novideo->w,
icon_novideo->h,
SDL_LOGICAL_PRESENTATION_LETTERBOX);
if (!ok) {
LOGW("Could not set renderer logical size: %s", SDL_GetError());
// don't fail
}
@ -31,16 +34,13 @@ sc_display_init_novideo_icon(struct sc_display *display,
bool
sc_display_init(struct sc_display *display, SDL_Window *window,
SDL_Surface *icon_novideo, bool mipmaps) {
display->renderer =
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
display->renderer = SDL_CreateRenderer(window, NULL);
if (!display->renderer) {
LOGE("Could not create renderer: %s", SDL_GetError());
return false;
}
SDL_RendererInfo renderer_info;
int r = SDL_GetRendererInfo(display->renderer, &renderer_info);
const char *renderer_name = r ? NULL : renderer_info.name;
const char *renderer_name = SDL_GetRendererName(display->renderer);
LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)");
display->mipmaps = false;
@ -94,14 +94,13 @@ sc_display_init(struct sc_display *display, SDL_Window *window,
display->texture = NULL;
display->pending.flags = 0;
display->pending.frame = NULL;
display->has_frame = false;
if (icon_novideo) {
// Without video, set a static scrcpy icon as window content
bool ok = sc_display_init_novideo_icon(display, icon_novideo);
if (!ok) {
#ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE
SDL_GL_DeleteContext(display->gl_context);
SDL_GL_DestroyContext(display->gl_context);
#endif
SDL_DestroyRenderer(display->renderer);
return false;
@ -117,7 +116,7 @@ sc_display_destroy(struct sc_display *display) {
av_frame_free(&display->pending.frame);
}
#ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE
SDL_GL_DeleteContext(display->gl_context);
SDL_GL_DestroyContext(display->gl_context);
#endif
if (display->texture) {
SDL_DestroyTexture(display->texture);
@ -125,13 +124,62 @@ sc_display_destroy(struct sc_display *display) {
SDL_DestroyRenderer(display->renderer);
}
static enum SDL_Colorspace
sc_display_to_sdl_color_space(enum AVColorSpace color_space,
enum AVColorRange color_range) {
bool full_range = color_range == AVCOL_RANGE_JPEG;
switch (color_space) {
case AVCOL_SPC_BT709:
return full_range ? SDL_COLORSPACE_BT709_FULL
: SDL_COLORSPACE_BT709_LIMITED;
case AVCOL_SPC_BT470BG:
case AVCOL_SPC_SMPTE170M:
return full_range ? SDL_COLORSPACE_BT601_FULL
: SDL_COLORSPACE_BT601_LIMITED;
case AVCOL_SPC_BT2020_NCL:
case AVCOL_SPC_BT2020_CL:
return full_range ? SDL_COLORSPACE_BT2020_FULL
: SDL_COLORSPACE_BT2020_LIMITED;
default:
return SDL_COLORSPACE_JPEG;
}
}
static SDL_Texture *
sc_display_create_texture(struct sc_display *display,
struct sc_size size) {
struct sc_size size, enum AVColorSpace color_space,
enum AVColorRange color_range) {
SDL_PropertiesID props = SDL_CreateProperties();
if (!props) {
return NULL;
}
enum SDL_Colorspace sdl_color_space =
sc_display_to_sdl_color_space(color_space, color_range);
bool ok =
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER,
SDL_PIXELFORMAT_YV12);
ok &= SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER,
SDL_TEXTUREACCESS_STREAMING);
ok &= SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER,
size.width);
ok &= SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER,
size.height);
ok &= SDL_SetNumberProperty(props,
SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER,
sdl_color_space);
if (!ok) {
LOGE("Could not set texture properties");
SDL_DestroyProperties(props);
return NULL;
}
SDL_Renderer *renderer = display->renderer;
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
SDL_TEXTUREACCESS_STREAMING,
size.width, size.height);
SDL_Texture *texture = SDL_CreateTextureWithProperties(renderer, props);
SDL_DestroyProperties(props);
if (!texture) {
LOGD("Could not create texture: %s", SDL_GetError());
return NULL;
@ -140,24 +188,46 @@ sc_display_create_texture(struct sc_display *display,
if (display->mipmaps) {
struct sc_opengl *gl = &display->gl;
SDL_GL_BindTexture(texture, NULL, NULL);
SDL_PropertiesID props = SDL_GetTextureProperties(texture);
if (!props) {
LOGE("Could not get texture properties: %s", SDL_GetError());
SDL_DestroyTexture(texture);
return NULL;
}
int64_t texture_id =
SDL_GetNumberProperty(props,
SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER, 0);
SDL_DestroyProperties(props);
if (!texture_id) {
LOGE("Could not get texture id: %s", SDL_GetError());
SDL_DestroyTexture(texture);
return NULL;
}
assert(!(texture_id & ~0xFFFFFFFF)); // fits in uint32_t
display->texture_id = texture_id;
gl->BindTexture(GL_TEXTURE_2D, display->texture_id);
// Enable trilinear filtering for downscaling
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
gl->TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -1.f);
SDL_GL_UnbindTexture(texture);
gl->BindTexture(GL_TEXTURE_2D, 0);
}
return texture;
}
static inline void
sc_display_set_pending_size(struct sc_display *display, struct sc_size size) {
sc_display_set_pending_texture(struct sc_display *display,
struct sc_size size,
enum AVColorRange color_range) {
assert(!display->texture);
display->pending.size = size;
display->pending.flags |= SC_DISPLAY_PENDING_FLAG_SIZE;
display->pending.texture.size = size;
display->pending.texture.color_range = color_range;
display->pending.flags |= SC_DISPLAY_PENDING_FLAG_TEXTURE;
}
static bool
@ -183,15 +253,18 @@ sc_display_set_pending_frame(struct sc_display *display, const AVFrame *frame) {
static bool
sc_display_apply_pending(struct sc_display *display) {
if (display->pending.flags & SC_DISPLAY_PENDING_FLAG_SIZE) {
if (display->pending.flags & SC_DISPLAY_PENDING_FLAG_TEXTURE) {
assert(!display->texture);
display->texture =
sc_display_create_texture(display, display->pending.size);
sc_display_create_texture(display,
display->pending.texture.size,
display->pending.texture.color_space,
display->pending.texture.color_range);
if (!display->texture) {
return false;
}
display->pending.flags &= ~SC_DISPLAY_PENDING_FLAG_SIZE;
display->pending.flags &= ~SC_DISPLAY_PENDING_FLAG_TEXTURE;
}
if (display->pending.flags & SC_DISPLAY_PENDING_FLAG_FRAME) {
@ -209,15 +282,18 @@ sc_display_apply_pending(struct sc_display *display) {
}
static bool
sc_display_set_texture_size_internal(struct sc_display *display,
struct sc_size size) {
sc_display_prepare_texture_internal(struct sc_display *display,
struct sc_size size,
enum AVColorSpace color_space,
enum AVColorRange color_range) {
assert(size.width && size.height);
if (display->texture) {
SDL_DestroyTexture(display->texture);
}
display->texture = sc_display_create_texture(display, size);
display->texture =
sc_display_create_texture(display, size, color_space, color_range);
if (!display->texture) {
return false;
}
@ -227,10 +303,13 @@ sc_display_set_texture_size_internal(struct sc_display *display,
}
enum sc_display_result
sc_display_set_texture_size(struct sc_display *display, struct sc_size size) {
bool ok = sc_display_set_texture_size_internal(display, size);
sc_display_prepare_texture(struct sc_display *display, struct sc_size size,
enum AVColorSpace color_space,
enum AVColorRange color_range) {
bool ok = sc_display_prepare_texture_internal(display, size, color_space,
color_range);
if (!ok) {
sc_display_set_pending_size(display, size);
sc_display_set_pending_texture(display, size, color_range);
return SC_DISPLAY_RESULT_PENDING;
}
@ -238,38 +317,25 @@ sc_display_set_texture_size(struct sc_display *display, struct sc_size size) {
return SC_DISPLAY_RESULT_OK;
}
static SDL_YUV_CONVERSION_MODE
sc_display_to_sdl_color_range(enum AVColorRange color_range) {
return color_range == AVCOL_RANGE_JPEG ? SDL_YUV_CONVERSION_JPEG
: SDL_YUV_CONVERSION_AUTOMATIC;
}
static bool
sc_display_update_texture_internal(struct sc_display *display,
const AVFrame *frame) {
if (!display->has_frame) {
// First frame
display->has_frame = true;
// Configure YUV color range conversion
SDL_YUV_CONVERSION_MODE sdl_color_range =
sc_display_to_sdl_color_range(frame->color_range);
SDL_SetYUVConversionMode(sdl_color_range);
}
int ret = SDL_UpdateYUVTexture(display->texture, NULL,
bool ok = SDL_UpdateYUVTexture(display->texture, NULL,
frame->data[0], frame->linesize[0],
frame->data[1], frame->linesize[1],
frame->data[2], frame->linesize[2]);
if (ret) {
if (!ok) {
LOGD("Could not update texture: %s", SDL_GetError());
return false;
}
if (display->mipmaps) {
SDL_GL_BindTexture(display->texture, NULL, NULL);
assert(display->texture_id);
struct sc_opengl *gl = &display->gl;
gl->BindTexture(GL_TEXTURE_2D, display->texture_id);
display->gl.GenerateMipmap(GL_TEXTURE_2D);
SDL_GL_UnbindTexture(display->texture);
gl->BindTexture(GL_TEXTURE_2D, 0);
}
return true;
@ -307,8 +373,10 @@ sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
SDL_Texture *texture = display->texture;
if (orientation == SC_ORIENTATION_0) {
int ret = SDL_RenderCopy(renderer, texture, NULL, geometry);
if (ret) {
SDL_FRect frect;
SDL_RectToFRect(geometry, &frect);
bool ok = SDL_RenderTexture(renderer, texture, NULL, &frect);
if (!ok) {
LOGE("Could not render texture: %s", SDL_GetError());
return SC_DISPLAY_RESULT_ERROR;
}
@ -316,24 +384,22 @@ sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
unsigned cw_rotation = sc_orientation_get_rotation(orientation);
double angle = 90 * cw_rotation;
const SDL_Rect *dstrect = NULL;
SDL_Rect rect;
SDL_FRect frect;
if (sc_orientation_is_swap(orientation)) {
rect.x = geometry->x + (geometry->w - geometry->h) / 2;
rect.y = geometry->y + (geometry->h - geometry->w) / 2;
rect.w = geometry->h;
rect.h = geometry->w;
dstrect = &rect;
frect.x = geometry->x + (geometry->w - geometry->h) / 2.f;
frect.y = geometry->y + (geometry->h - geometry->w) / 2.f;
frect.w = geometry->h;
frect.h = geometry->w;
} else {
dstrect = geometry;
SDL_RectToFRect(geometry, &frect);
}
SDL_RendererFlip flip = sc_orientation_is_mirror(orientation)
SDL_FlipMode flip = sc_orientation_is_mirror(orientation)
? SDL_FLIP_HORIZONTAL : 0;
int ret = SDL_RenderCopyEx(renderer, texture, NULL, dstrect, angle,
NULL, flip);
if (ret) {
bool ok = SDL_RenderTextureRotated(renderer, texture, NULL, &frect,
angle, NULL, flip);
if (!ok) {
LOGE("Could not render texture: %s", SDL_GetError());
return SC_DISPLAY_RESULT_ERROR;
}

View File

@ -6,7 +6,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <libavutil/frame.h>
#include <SDL2/SDL.h>
#include <SDL3/SDL.h>
#include "coords.h"
#include "opengl.h"
@ -26,16 +26,19 @@ struct sc_display {
#endif
bool mipmaps;
uint32_t texture_id; // only set if mipmaps is enabled
struct {
#define SC_DISPLAY_PENDING_FLAG_SIZE 1
#define SC_DISPLAY_PENDING_FLAG_TEXTURE 1
#define SC_DISPLAY_PENDING_FLAG_FRAME 2
int8_t flags;
struct sc_size size;
struct {
struct sc_size size;
enum AVColorSpace color_space;
enum AVColorRange color_range;
} texture;
AVFrame *frame;
} pending;
bool has_frame;
};
enum sc_display_result {
@ -52,7 +55,9 @@ void
sc_display_destroy(struct sc_display *display);
enum sc_display_result
sc_display_set_texture_size(struct sc_display *display, struct sc_size size);
sc_display_prepare_texture(struct sc_display *display, struct sc_size size,
enum AVColorSpace color_space,
enum AVColorRange color_range);
enum sc_display_result
sc_display_update_texture(struct sc_display *display, const AVFrame *frame);

View File

@ -9,11 +9,8 @@ bool
sc_push_event_impl(uint32_t type, const char *name) {
SDL_Event event;
event.type = type;
int ret = SDL_PushEvent(&event);
// ret < 0: error (queue full)
// ret == 0: event was filtered
// ret == 1: success
if (ret != 1) {
bool ok = SDL_PushEvent(&event);
if (!ok) {
LOGE("Could not post %s event: %s", name, SDL_GetError());
return false;
}
@ -30,34 +27,25 @@ sc_post_to_main_thread(sc_runnable_fn run, void *userdata) {
.data2 = userdata,
},
};
int ret = SDL_PushEvent(&event);
// ret < 0: error (queue full)
// ret == 0: event was filtered
// ret == 1: success
if (ret != 1) {
if (ret == 0) {
// if ret == 0, this is expected on exit, log in debug mode
LOGD("Could not post runnable to main thread (filtered)");
} else {
assert(ret < 0);
LOGW("Could not post runnable to main thread: %s", SDL_GetError());
}
bool ok = SDL_PushEvent(&event);
if (!ok) {
LOGW("Could not post runnable to main thread: %s", SDL_GetError());
return false;
}
return true;
}
static int SDLCALL
static bool SDLCALL
task_event_filter(void *userdata, SDL_Event *event) {
(void) userdata;
if (event->type == SC_EVENT_RUN_ON_MAIN_THREAD) {
// Reject this event type from now on
return 0;
return false;
}
return 1;
return true;
}
void

View File

@ -5,10 +5,10 @@
#include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL_events.h>
#include <SDL3/SDL_events.h>
enum {
SC_EVENT_NEW_FRAME = SDL_USEREVENT,
SC_EVENT_NEW_FRAME = SDL_EVENT_USER,
SC_EVENT_RUN_ON_MAIN_THREAD,
SC_EVENT_DEVICE_DISCONNECTED,
SC_EVENT_SERVER_CONNECTION_FAILED,
@ -16,7 +16,6 @@ enum {
SC_EVENT_USB_DEVICE_DISCONNECTED,
SC_EVENT_DEMUXER_ERROR,
SC_EVENT_RECORDER_ERROR,
SC_EVENT_SCREEN_INIT_SIZE,
SC_EVENT_TIME_LIMIT_REACHED,
SC_EVENT_CONTROLLER_ERROR,
SC_EVENT_AOA_OPEN_ERROR,

View File

@ -166,6 +166,12 @@ sc_hid_buttons_from_buttons_state(uint8_t buttons_state) {
return c;
}
void
sc_hid_mouse_init(struct sc_hid_mouse *hid) {
hid->residual_hscroll = 0;
hid->residual_vscroll = 0;
}
void
sc_hid_mouse_generate_input_from_motion(struct sc_hid_input *hid_input,
const struct sc_mouse_motion_event *event) {
@ -192,22 +198,37 @@ sc_hid_mouse_generate_input_from_click(struct sc_hid_input *hid_input,
data[4] = 0; // no horizontal scrolling
}
static int8_t
consume_scroll_integer(float *scroll) {
float value = CLAMP(*scroll, -127, 127);
int8_t consume = value; // truncate towards 0
float residual = value - consume;
*scroll = residual;
return consume;
}
bool
sc_hid_mouse_generate_input_from_scroll(struct sc_hid_input *hid_input,
sc_hid_mouse_generate_input_from_scroll(struct sc_hid_mouse *hid,
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
sc_hid_mouse_input_init(hid_input);
hid->residual_hscroll += event->hscroll;
hid->residual_vscroll += event->vscroll;
int8_t hscroll = consume_scroll_integer(&hid->residual_hscroll);
int8_t vscroll = consume_scroll_integer(&hid->residual_vscroll);
if (!hscroll && !vscroll) {
// Not enough scrolling to inject a scroll event
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
data[3] = CLAMP(event->vscroll_int, -127, 127);
data[4] = CLAMP(event->hscroll_int, -127, 127);
data[3] = vscroll;
data[4] = hscroll;
return true;
}

View File

@ -8,6 +8,13 @@
#define SC_HID_ID_MOUSE 2
struct sc_hid_mouse {
float residual_hscroll;
float residual_vscroll;
};
void sc_hid_mouse_init(struct sc_hid_mouse *hid);
void
sc_hid_mouse_generate_open(struct sc_hid_open *hid_open);
@ -23,7 +30,8 @@ sc_hid_mouse_generate_input_from_click(struct sc_hid_input *hid_input,
const struct sc_mouse_click_event *event);
bool
sc_hid_mouse_generate_input_from_scroll(struct sc_hid_input *hid_input,
sc_hid_mouse_generate_input_from_scroll(struct sc_hid_mouse *hid,
struct sc_hid_input *hid_input,
const struct sc_mouse_scroll_event *event);
#endif

View File

@ -10,7 +10,7 @@
#include <libavutil/avutil.h>
#include <libavutil/pixdesc.h>
#include <libavutil/pixfmt.h>
#include <SDL2/SDL.h>
#include <SDL3/SDL.h>
#include "config.h"
#include "util/env.h"
@ -156,13 +156,7 @@ free_ctx:
return result;
}
#if !SDL_VERSION_ATLEAST(2, 0, 10)
// SDL_PixelFormatEnum has been introduced in SDL 2.0.10. Use int for older SDL
// versions.
typedef int SDL_PixelFormatEnum;
#endif
static SDL_PixelFormatEnum
static SDL_PixelFormat
to_sdl_pixel_format(enum AVPixelFormat fmt) {
switch (fmt) {
case AV_PIX_FMT_RGB24: return SDL_PIXELFORMAT_RGB24;
@ -172,13 +166,11 @@ to_sdl_pixel_format(enum AVPixelFormat fmt) {
case AV_PIX_FMT_ABGR: return SDL_PIXELFORMAT_ABGR32;
case AV_PIX_FMT_BGRA: return SDL_PIXELFORMAT_BGRA32;
case AV_PIX_FMT_RGB565BE: return SDL_PIXELFORMAT_RGB565;
case AV_PIX_FMT_RGB555BE: return SDL_PIXELFORMAT_RGB555;
case AV_PIX_FMT_RGB555BE: return SDL_PIXELFORMAT_XRGB1555;
case AV_PIX_FMT_BGR565BE: return SDL_PIXELFORMAT_BGR565;
case AV_PIX_FMT_BGR555BE: return SDL_PIXELFORMAT_BGR555;
case AV_PIX_FMT_RGB444BE: return SDL_PIXELFORMAT_RGB444;
#if SDL_VERSION_ATLEAST(2, 0, 12)
case AV_PIX_FMT_BGR444BE: return SDL_PIXELFORMAT_BGR444;
#endif
case AV_PIX_FMT_BGR555BE: return SDL_PIXELFORMAT_XBGR1555;
case AV_PIX_FMT_RGB444BE: return SDL_PIXELFORMAT_XRGB4444;
case AV_PIX_FMT_BGR444BE: return SDL_PIXELFORMAT_XBGR4444;
case AV_PIX_FMT_PAL8: return SDL_PIXELFORMAT_INDEX8;
default: return SDL_PIXELFORMAT_UNKNOWN;
}
@ -203,20 +195,16 @@ load_from_path(const char *path) {
goto error;
}
SDL_PixelFormatEnum format = to_sdl_pixel_format(frame->format);
SDL_PixelFormat format = to_sdl_pixel_format(frame->format);
if (format == SDL_PIXELFORMAT_UNKNOWN) {
LOGE("Unsupported icon pixel format: %s (%d)", desc->name,
frame->format);
goto error;
}
int bits_per_pixel = av_get_bits_per_pixel(desc);
SDL_Surface *surface =
SDL_CreateRGBSurfaceWithFormatFrom(frame->data[0],
frame->width, frame->height,
bits_per_pixel,
frame->linesize[0],
format);
SDL_CreateSurfaceFrom(frame->width, frame->height, format,
frame->data[0], frame->linesize[0]);
if (!surface) {
LOGE("Could not create icon surface");
@ -248,17 +236,35 @@ load_from_path(const char *path) {
#endif
}
SDL_Palette *palette = surface->format->palette;
assert(palette);
int ret = SDL_SetPaletteColors(palette, colors, 0, 256);
if (ret) {
SDL_Palette *palette = SDL_CreateSurfacePalette(surface);
if (!palette) {
LOGE("Could not create palette");
SDL_DestroySurface(surface);
goto error;
}
bool ok = SDL_SetPaletteColors(palette, colors, 0, 256);
if (!ok) {
LOGE("Could not set palette colors");
SDL_FreeSurface(surface);
SDL_DestroySurface(surface);
goto error;
}
}
surface->userdata = frame; // frame owns the data
SDL_PropertiesID props = SDL_GetSurfaceProperties(surface);
if (!props) {
LOGE("Could not get surface properties: %s", SDL_GetError());
SDL_DestroySurface(surface);
goto error;
}
// frame owns the data
bool ok = SDL_SetPointerProperty(props, "sc_frame", frame);
if (!ok) {
LOGE("Could not get surface properties: %s", SDL_GetError());
SDL_DestroySurface(surface);
goto error;
}
return surface;
@ -281,8 +287,10 @@ scrcpy_icon_load(void) {
void
scrcpy_icon_destroy(SDL_Surface *icon) {
AVFrame *frame = icon->userdata;
SDL_PropertiesID props = SDL_GetSurfaceProperties(icon);
assert(props);
AVFrame *frame = SDL_GetPointerProperty(props, "sc_frame", NULL);
assert(frame);
av_frame_free(&frame);
SDL_FreeSurface(icon);
SDL_DestroySurface(icon);
}

View File

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

View File

@ -6,7 +6,7 @@
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL_events.h>
#include <SDL3/SDL_events.h>
#include "coords.h"
@ -43,17 +43,17 @@
*/
enum sc_mod {
SC_MOD_LSHIFT = KMOD_LSHIFT,
SC_MOD_RSHIFT = KMOD_RSHIFT,
SC_MOD_LCTRL = KMOD_LCTRL,
SC_MOD_RCTRL = KMOD_RCTRL,
SC_MOD_LALT = KMOD_LALT,
SC_MOD_RALT = KMOD_RALT,
SC_MOD_LGUI = KMOD_LGUI,
SC_MOD_RGUI = KMOD_RGUI,
SC_MOD_LSHIFT = SDL_KMOD_LSHIFT,
SC_MOD_RSHIFT = SDL_KMOD_RSHIFT,
SC_MOD_LCTRL = SDL_KMOD_LCTRL,
SC_MOD_RCTRL = SDL_KMOD_RCTRL,
SC_MOD_LALT = SDL_KMOD_LALT,
SC_MOD_RALT = SDL_KMOD_RALT,
SC_MOD_LGUI = SDL_KMOD_LGUI,
SC_MOD_RGUI = SDL_KMOD_RGUI,
SC_MOD_NUM = KMOD_NUM,
SC_MOD_CAPS = KMOD_CAPS,
SC_MOD_NUM = SDL_KMOD_NUM,
SC_MOD_CAPS = SDL_KMOD_CAPS,
};
enum sc_action {
@ -70,12 +70,12 @@ enum sc_keycode {
SC_KEYCODE_TAB = SDLK_TAB,
SC_KEYCODE_SPACE = SDLK_SPACE,
SC_KEYCODE_EXCLAIM = SDLK_EXCLAIM,
SC_KEYCODE_QUOTEDBL = SDLK_QUOTEDBL,
SC_KEYCODE_QUOTEDBL = SDLK_DBLAPOSTROPHE,
SC_KEYCODE_HASH = SDLK_HASH,
SC_KEYCODE_PERCENT = SDLK_PERCENT,
SC_KEYCODE_DOLLAR = SDLK_DOLLAR,
SC_KEYCODE_AMPERSAND = SDLK_AMPERSAND,
SC_KEYCODE_QUOTE = SDLK_QUOTE,
SC_KEYCODE_QUOTE = SDLK_APOSTROPHE,
SC_KEYCODE_LEFTPAREN = SDLK_LEFTPAREN,
SC_KEYCODE_RIGHTPAREN = SDLK_RIGHTPAREN,
SC_KEYCODE_ASTERISK = SDLK_ASTERISK,
@ -107,33 +107,33 @@ enum sc_keycode {
SC_KEYCODE_RIGHTBRACKET = SDLK_RIGHTBRACKET,
SC_KEYCODE_CARET = SDLK_CARET,
SC_KEYCODE_UNDERSCORE = SDLK_UNDERSCORE,
SC_KEYCODE_BACKQUOTE = SDLK_BACKQUOTE,
SC_KEYCODE_a = SDLK_a,
SC_KEYCODE_b = SDLK_b,
SC_KEYCODE_c = SDLK_c,
SC_KEYCODE_d = SDLK_d,
SC_KEYCODE_e = SDLK_e,
SC_KEYCODE_f = SDLK_f,
SC_KEYCODE_g = SDLK_g,
SC_KEYCODE_h = SDLK_h,
SC_KEYCODE_i = SDLK_i,
SC_KEYCODE_j = SDLK_j,
SC_KEYCODE_k = SDLK_k,
SC_KEYCODE_l = SDLK_l,
SC_KEYCODE_m = SDLK_m,
SC_KEYCODE_n = SDLK_n,
SC_KEYCODE_o = SDLK_o,
SC_KEYCODE_p = SDLK_p,
SC_KEYCODE_q = SDLK_q,
SC_KEYCODE_r = SDLK_r,
SC_KEYCODE_s = SDLK_s,
SC_KEYCODE_t = SDLK_t,
SC_KEYCODE_u = SDLK_u,
SC_KEYCODE_v = SDLK_v,
SC_KEYCODE_w = SDLK_w,
SC_KEYCODE_x = SDLK_x,
SC_KEYCODE_y = SDLK_y,
SC_KEYCODE_z = SDLK_z,
SC_KEYCODE_BACKQUOTE = SDLK_GRAVE,
SC_KEYCODE_a = SDLK_A,
SC_KEYCODE_b = SDLK_B,
SC_KEYCODE_c = SDLK_C,
SC_KEYCODE_d = SDLK_D,
SC_KEYCODE_e = SDLK_E,
SC_KEYCODE_f = SDLK_F,
SC_KEYCODE_g = SDLK_G,
SC_KEYCODE_h = SDLK_H,
SC_KEYCODE_i = SDLK_I,
SC_KEYCODE_j = SDLK_J,
SC_KEYCODE_k = SDLK_K,
SC_KEYCODE_l = SDLK_L,
SC_KEYCODE_m = SDLK_M,
SC_KEYCODE_n = SDLK_N,
SC_KEYCODE_o = SDLK_O,
SC_KEYCODE_p = SDLK_P,
SC_KEYCODE_q = SDLK_Q,
SC_KEYCODE_r = SDLK_R,
SC_KEYCODE_s = SDLK_S,
SC_KEYCODE_t = SDLK_T,
SC_KEYCODE_u = SDLK_U,
SC_KEYCODE_v = SDLK_V,
SC_KEYCODE_w = SDLK_W,
SC_KEYCODE_x = SDLK_X,
SC_KEYCODE_y = SDLK_Y,
SC_KEYCODE_z = SDLK_Z,
SC_KEYCODE_CAPSLOCK = SDLK_CAPSLOCK,
@ -315,11 +315,11 @@ enum sc_scancode {
// to avoid unnecessary conversions (and confusion).
enum sc_mouse_button {
SC_MOUSE_BUTTON_UNKNOWN = 0,
SC_MOUSE_BUTTON_LEFT = SDL_BUTTON(SDL_BUTTON_LEFT),
SC_MOUSE_BUTTON_RIGHT = SDL_BUTTON(SDL_BUTTON_RIGHT),
SC_MOUSE_BUTTON_MIDDLE = SDL_BUTTON(SDL_BUTTON_MIDDLE),
SC_MOUSE_BUTTON_X1 = SDL_BUTTON(SDL_BUTTON_X1),
SC_MOUSE_BUTTON_X2 = SDL_BUTTON(SDL_BUTTON_X2),
SC_MOUSE_BUTTON_LEFT = SDL_BUTTON_MASK(SDL_BUTTON_LEFT),
SC_MOUSE_BUTTON_RIGHT = SDL_BUTTON_MASK(SDL_BUTTON_RIGHT),
SC_MOUSE_BUTTON_MIDDLE = SDL_BUTTON_MASK(SDL_BUTTON_MIDDLE),
SC_MOUSE_BUTTON_X1 = SDL_BUTTON_MASK(SDL_BUTTON_X1),
SC_MOUSE_BUTTON_X2 = SDL_BUTTON_MASK(SDL_BUTTON_X2),
};
// Use the naming from SDL3 for gamepad axis and buttons:
@ -327,31 +327,31 @@ enum sc_mouse_button {
enum sc_gamepad_axis {
SC_GAMEPAD_AXIS_UNKNOWN = -1,
SC_GAMEPAD_AXIS_LEFTX = SDL_CONTROLLER_AXIS_LEFTX,
SC_GAMEPAD_AXIS_LEFTY = SDL_CONTROLLER_AXIS_LEFTY,
SC_GAMEPAD_AXIS_RIGHTX = SDL_CONTROLLER_AXIS_RIGHTX,
SC_GAMEPAD_AXIS_RIGHTY = SDL_CONTROLLER_AXIS_RIGHTY,
SC_GAMEPAD_AXIS_LEFT_TRIGGER = SDL_CONTROLLER_AXIS_TRIGGERLEFT,
SC_GAMEPAD_AXIS_RIGHT_TRIGGER = SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
SC_GAMEPAD_AXIS_LEFTX = SDL_GAMEPAD_AXIS_LEFTX,
SC_GAMEPAD_AXIS_LEFTY = SDL_GAMEPAD_AXIS_LEFTY,
SC_GAMEPAD_AXIS_RIGHTX = SDL_GAMEPAD_AXIS_RIGHTX,
SC_GAMEPAD_AXIS_RIGHTY = SDL_GAMEPAD_AXIS_RIGHTY,
SC_GAMEPAD_AXIS_LEFT_TRIGGER = SDL_GAMEPAD_AXIS_LEFT_TRIGGER,
SC_GAMEPAD_AXIS_RIGHT_TRIGGER = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER,
};
enum sc_gamepad_button {
SC_GAMEPAD_BUTTON_UNKNOWN = -1,
SC_GAMEPAD_BUTTON_SOUTH = SDL_CONTROLLER_BUTTON_A,
SC_GAMEPAD_BUTTON_EAST = SDL_CONTROLLER_BUTTON_B,
SC_GAMEPAD_BUTTON_WEST = SDL_CONTROLLER_BUTTON_X,
SC_GAMEPAD_BUTTON_NORTH = SDL_CONTROLLER_BUTTON_Y,
SC_GAMEPAD_BUTTON_BACK = SDL_CONTROLLER_BUTTON_BACK,
SC_GAMEPAD_BUTTON_GUIDE = SDL_CONTROLLER_BUTTON_GUIDE,
SC_GAMEPAD_BUTTON_START = SDL_CONTROLLER_BUTTON_START,
SC_GAMEPAD_BUTTON_LEFT_STICK = SDL_CONTROLLER_BUTTON_LEFTSTICK,
SC_GAMEPAD_BUTTON_RIGHT_STICK = SDL_CONTROLLER_BUTTON_RIGHTSTICK,
SC_GAMEPAD_BUTTON_LEFT_SHOULDER = SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
SC_GAMEPAD_BUTTON_RIGHT_SHOULDER = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
SC_GAMEPAD_BUTTON_DPAD_UP = SDL_CONTROLLER_BUTTON_DPAD_UP,
SC_GAMEPAD_BUTTON_DPAD_DOWN = SDL_CONTROLLER_BUTTON_DPAD_DOWN,
SC_GAMEPAD_BUTTON_DPAD_LEFT = SDL_CONTROLLER_BUTTON_DPAD_LEFT,
SC_GAMEPAD_BUTTON_DPAD_RIGHT = SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
SC_GAMEPAD_BUTTON_SOUTH = SDL_GAMEPAD_BUTTON_SOUTH,
SC_GAMEPAD_BUTTON_EAST = SDL_GAMEPAD_BUTTON_EAST,
SC_GAMEPAD_BUTTON_WEST = SDL_GAMEPAD_BUTTON_WEST,
SC_GAMEPAD_BUTTON_NORTH = SDL_GAMEPAD_BUTTON_NORTH,
SC_GAMEPAD_BUTTON_BACK = SDL_GAMEPAD_BUTTON_BACK,
SC_GAMEPAD_BUTTON_GUIDE = SDL_GAMEPAD_BUTTON_GUIDE,
SC_GAMEPAD_BUTTON_START = SDL_GAMEPAD_BUTTON_START,
SC_GAMEPAD_BUTTON_LEFT_STICK = SDL_GAMEPAD_BUTTON_LEFT_STICK,
SC_GAMEPAD_BUTTON_RIGHT_STICK = SDL_GAMEPAD_BUTTON_RIGHT_STICK,
SC_GAMEPAD_BUTTON_LEFT_SHOULDER = SDL_GAMEPAD_BUTTON_LEFT_SHOULDER,
SC_GAMEPAD_BUTTON_RIGHT_SHOULDER = SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER,
SC_GAMEPAD_BUTTON_DPAD_UP = SDL_GAMEPAD_BUTTON_DPAD_UP,
SC_GAMEPAD_BUTTON_DPAD_DOWN = SDL_GAMEPAD_BUTTON_DPAD_DOWN,
SC_GAMEPAD_BUTTON_DPAD_LEFT = SDL_GAMEPAD_BUTTON_DPAD_LEFT,
SC_GAMEPAD_BUTTON_DPAD_RIGHT = SDL_GAMEPAD_BUTTON_DPAD_RIGHT,
};
static_assert(sizeof(enum sc_mod) >= sizeof(SDL_Keymod),
@ -393,8 +393,6 @@ 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
};
@ -451,8 +449,8 @@ sc_scancode_from_sdl(SDL_Scancode scancode) {
static inline enum sc_action
sc_action_from_sdl_keyboard_type(uint32_t type) {
assert(type == SDL_KEYDOWN || type == SDL_KEYUP);
if (type == SDL_KEYDOWN) {
assert(type == SDL_EVENT_KEY_DOWN || type == SDL_EVENT_KEY_UP);
if (type == SDL_EVENT_KEY_DOWN) {
return SC_ACTION_DOWN;
}
return SC_ACTION_UP;
@ -460,8 +458,8 @@ sc_action_from_sdl_keyboard_type(uint32_t type) {
static inline enum sc_action
sc_action_from_sdl_mousebutton_type(uint32_t type) {
assert(type == SDL_MOUSEBUTTONDOWN || type == SDL_MOUSEBUTTONUP);
if (type == SDL_MOUSEBUTTONDOWN) {
assert(type == SDL_EVENT_MOUSE_BUTTON_DOWN || type == SDL_EVENT_MOUSE_BUTTON_UP);
if (type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
return SC_ACTION_DOWN;
}
return SC_ACTION_UP;
@ -469,12 +467,12 @@ sc_action_from_sdl_mousebutton_type(uint32_t type) {
static inline enum sc_touch_action
sc_touch_action_from_sdl(uint32_t type) {
assert(type == SDL_FINGERMOTION || type == SDL_FINGERDOWN ||
type == SDL_FINGERUP);
if (type == SDL_FINGERMOTION) {
assert(type == SDL_EVENT_FINGER_MOTION || type == SDL_EVENT_FINGER_DOWN ||
type == SDL_EVENT_FINGER_UP);
if (type == SDL_EVENT_FINGER_MOTION) {
return SC_TOUCH_ACTION_MOVE;
}
if (type == SDL_FINGERDOWN) {
if (type == SDL_EVENT_FINGER_DOWN) {
return SC_TOUCH_ACTION_DOWN;
}
return SC_TOUCH_ACTION_UP;
@ -484,7 +482,7 @@ static inline enum sc_mouse_button
sc_mouse_button_from_sdl(uint8_t button) {
if (button >= SDL_BUTTON_LEFT && button <= SDL_BUTTON_X2) {
// SC_MOUSE_BUTTON_* constants are initialized from SDL_BUTTON(index)
return SDL_BUTTON(button);
return SDL_BUTTON_MASK(button);
}
return SC_MOUSE_BUTTON_UNKNOWN;
@ -500,7 +498,7 @@ sc_mouse_buttons_state_from_sdl(uint32_t buttons_state) {
static inline enum sc_gamepad_axis
sc_gamepad_axis_from_sdl(uint8_t axis) {
if (axis <= SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {
if (axis <= SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {
// SC_GAMEPAD_AXIS_* constants are initialized from
// SDL_CONTROLLER_AXIS_*
return axis;
@ -510,7 +508,7 @@ sc_gamepad_axis_from_sdl(uint8_t axis) {
static inline enum sc_gamepad_button
sc_gamepad_button_from_sdl(uint8_t button) {
if (button <= SDL_CONTROLLER_BUTTON_DPAD_RIGHT) {
if (button <= SDL_GAMEPAD_BUTTON_DPAD_RIGHT) {
// SC_GAMEPAD_BUTTON_* constants are initialized from
// SDL_CONTROLLER_BUTTON_*
return button;
@ -520,8 +518,8 @@ sc_gamepad_button_from_sdl(uint8_t button) {
static inline enum sc_action
sc_action_from_sdl_controllerbutton_type(uint32_t type) {
assert(type == SDL_CONTROLLERBUTTONDOWN || type == SDL_CONTROLLERBUTTONUP);
if (type == SDL_CONTROLLERBUTTONDOWN) {
assert(type == SDL_EVENT_GAMEPAD_BUTTON_DOWN || type == SDL_EVENT_GAMEPAD_BUTTON_UP);
if (type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
return SC_ACTION_DOWN;
}
return SC_ACTION_UP;

View File

@ -3,7 +3,7 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <SDL2/SDL.h>
#include <SDL3/SDL.h>
#include "android/input.h"
#include "android/keycodes.h"
@ -374,11 +374,11 @@ sc_input_manager_process_key(struct sc_input_manager *im,
bool paused = im->screen->paused;
bool video = im->screen->video;
SDL_Keycode sdl_keycode = event->keysym.sym;
uint16_t mod = event->keysym.mod;
bool down = event->type == SDL_KEYDOWN;
bool ctrl = event->keysym.mod & KMOD_CTRL;
bool shift = event->keysym.mod & KMOD_SHIFT;
SDL_Keycode sdl_keycode = event->key;
uint16_t mod = event->mod;
bool down = event->type == SDL_EVENT_KEY_DOWN;
bool ctrl = event->mod & SDL_KMOD_CTRL;
bool shift = event->mod & SDL_KMOD_SHIFT;
bool repeat = event->repeat;
// Either the modifier includes a shortcut modifier, or the key
@ -402,39 +402,39 @@ sc_input_manager_process_key(struct sc_input_manager *im,
if (is_shortcut) {
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
switch (sdl_keycode) {
case SDLK_h:
case SDLK_H:
if (im->kp && !shift && !repeat && !paused) {
action_home(im, action);
}
return;
case SDLK_b: // fall-through
case SDLK_B: // fall-through
case SDLK_BACKSPACE:
if (im->kp && !shift && !repeat && !paused) {
action_back(im, action);
}
return;
case SDLK_s:
case SDLK_S:
if (im->kp && !shift && !repeat && !paused) {
action_app_switch(im, action);
}
return;
case SDLK_m:
case SDLK_M:
if (im->kp && !shift && !repeat && !paused) {
action_menu(im, action);
}
return;
case SDLK_p:
case SDLK_P:
if (im->kp && !shift && !repeat && !paused) {
action_power(im, action);
}
return;
case SDLK_o:
case SDLK_O:
if (control && !repeat && down && !paused) {
bool on = shift;
set_display_power(im, on);
}
return;
case SDLK_z:
case SDLK_Z:
if (video && down && !repeat) {
sc_screen_set_paused(im->screen, !shift);
}
@ -483,17 +483,17 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
}
return;
case SDLK_c:
case SDLK_C:
if (im->kp && !shift && !repeat && down && !paused) {
get_device_clipboard(im, SC_COPY_KEY_COPY);
}
return;
case SDLK_x:
case SDLK_X:
if (im->kp && !shift && !repeat && down && !paused) {
get_device_clipboard(im, SC_COPY_KEY_CUT);
}
return;
case SDLK_v:
case SDLK_V:
if (im->kp && !repeat && down && !paused) {
if (shift || im->legacy_paste) {
// inject the text as input events
@ -505,27 +505,27 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
}
return;
case SDLK_f:
case SDLK_F:
if (video && !shift && !repeat && down) {
sc_screen_toggle_fullscreen(im->screen);
}
return;
case SDLK_w:
case SDLK_W:
if (video && !shift && !repeat && down) {
sc_screen_resize_to_fit(im->screen);
}
return;
case SDLK_g:
case SDLK_G:
if (video && !shift && !repeat && down) {
sc_screen_resize_to_pixel_perfect(im->screen);
}
return;
case SDLK_i:
case SDLK_I:
if (video && !shift && !repeat && down) {
switch_fps_counter_state(im);
}
return;
case SDLK_n:
case SDLK_N:
if (control && !repeat && down && !paused) {
if (shift) {
collapse_panels(im);
@ -536,7 +536,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
}
return;
case SDLK_r:
case SDLK_R:
if (control && !repeat && down && !paused) {
if (shift) {
reset_video(im);
@ -545,7 +545,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
}
return;
case SDLK_k:
case SDLK_K:
if (control && !shift && !repeat && down && !paused
&& im->kp && im->kp->hid) {
// Only if the current keyboard is hid
@ -562,7 +562,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
uint64_t ack_to_wait = SC_SEQUENCE_INVALID;
bool is_ctrl_v = ctrl && !shift && sdl_keycode == SDLK_v && down && !repeat;
bool is_ctrl_v = ctrl && !shift && sdl_keycode == SDLK_V && down && !repeat;
if (im->clipboard_autosync && is_ctrl_v) {
if (im->legacy_paste) {
// inject the text as input events
@ -595,7 +595,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
return;
}
enum sc_scancode scancode = sc_scancode_from_sdl(event->keysym.scancode);
enum sc_scancode scancode = sc_scancode_from_sdl(event->scancode);
if (scancode == SC_SCANCODE_UNKNOWN) {
return;
}
@ -605,7 +605,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
.keycode = keycode,
.scancode = scancode,
.repeat = event->repeat,
.mods_state = sc_mods_state_from_sdl(event->keysym.mod),
.mods_state = sc_mods_state_from_sdl(event->mod),
};
assert(im->kp->ops->process_key);
@ -674,7 +674,7 @@ sc_input_manager_process_touch(struct sc_input_manager *im,
int dw;
int dh;
SDL_GL_GetDrawableSize(im->screen->window, &dw, &dh);
SDL_GetWindowSizeInPixels(im->screen->window, &dw, &dh);
// SDL touch event coordinates are normalized in the range [0; 1]
int32_t x = event->x * dw;
@ -687,7 +687,7 @@ sc_input_manager_process_touch(struct sc_input_manager *im,
sc_screen_convert_drawable_to_frame_coords(im->screen, x, y),
},
.action = sc_touch_action_from_sdl(event->type),
.pointer_id = event->fingerId,
.pointer_id = event->fingerID,
.pressure = event->pressure,
};
@ -723,7 +723,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
bool control = im->controller;
bool paused = im->screen->paused;
bool down = event->type == SDL_MOUSEBUTTONDOWN;
bool down = event->type == SDL_EVENT_MOUSE_BUTTON_DOWN;
enum sc_mouse_button button = sc_mouse_button_from_sdl(event->button);
if (button == SC_MOUSE_BUTTON_UNKNOWN) {
@ -736,8 +736,8 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
}
SDL_Keymod keymod = SDL_GetModState();
bool ctrl_pressed = keymod & KMOD_CTRL;
bool shift_pressed = keymod & KMOD_SHIFT;
bool ctrl_pressed = keymod & SDL_KMOD_CTRL;
bool shift_pressed = keymod & SDL_KMOD_SHIFT;
if (control && !paused) {
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
@ -889,22 +889,20 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
}
// mouse_x and mouse_y are expressed in pixels relative to the window
int mouse_x;
int mouse_y;
float mouse_x;
float mouse_y;
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
(void) buttons; // Actual buttons are tracked manually to ignore shortcuts
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 = event->preciseX,
.vscroll = event->preciseY,
.hscroll = event->x,
.vscroll = event->y,
#else
.hscroll = event->x,
.vscroll = event->y,
#endif
.hscroll_int = event->x,
.vscroll_int = event->y,
.buttons_state = im->mouse_buttons_state,
};
@ -913,31 +911,31 @@ 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) {
if (event->type == SDL_CONTROLLERDEVICEADDED) {
SDL_GameController *gc = SDL_GameControllerOpen(event->which);
const SDL_GamepadDeviceEvent *event) {
if (event->type == SDL_EVENT_GAMEPAD_ADDED) {
SDL_Gamepad *gc = SDL_OpenGamepad(event->which);
if (!gc) {
LOGW("Could not open game controller");
return;
}
SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gc);
SDL_Joystick *joystick = SDL_GetGamepadJoystick(gc);
if (!joystick) {
LOGW("Could not get controller joystick");
SDL_GameControllerClose(gc);
SDL_CloseGamepad(gc);
return;
}
struct sc_gamepad_device_event evt = {
.gamepad_id = SDL_JoystickInstanceID(joystick),
.gamepad_id = SDL_GetJoystickID(joystick),
};
im->gp->ops->process_gamepad_added(im->gp, &evt);
} else if (event->type == SDL_CONTROLLERDEVICEREMOVED) {
} else if (event->type == SDL_EVENT_GAMEPAD_REMOVED) {
SDL_JoystickID id = event->which;
SDL_GameController *gc = SDL_GameControllerFromInstanceID(id);
SDL_Gamepad *gc = SDL_GetGamepadFromID(id);
if (gc) {
SDL_GameControllerClose(gc);
SDL_CloseGamepad(gc);
} else {
LOGW("Unknown gamepad device removed");
}
@ -954,7 +952,7 @@ sc_input_manager_process_gamepad_device(struct sc_input_manager *im,
static void
sc_input_manager_process_gamepad_axis(struct sc_input_manager *im,
const SDL_ControllerAxisEvent *event) {
const SDL_GamepadAxisEvent *event) {
enum sc_gamepad_axis axis = sc_gamepad_axis_from_sdl(event->axis);
if (axis == SC_GAMEPAD_AXIS_UNKNOWN) {
return;
@ -970,7 +968,7 @@ sc_input_manager_process_gamepad_axis(struct sc_input_manager *im,
static void
sc_input_manager_process_gamepad_button(struct sc_input_manager *im,
const SDL_ControllerButtonEvent *event) {
const SDL_GamepadButtonEvent *event) {
enum sc_gamepad_button button = sc_gamepad_button_from_sdl(event->button);
if (button == SC_GAMEPAD_BUTTON_UNKNOWN) {
return;
@ -993,8 +991,8 @@ is_apk(const char *file) {
static void
sc_input_manager_process_file(struct sc_input_manager *im,
const SDL_DropEvent *event) {
char *file = strdup(event->file);
SDL_free(event->file);
assert(event->type == SDL_EVENT_DROP_FILE);
char *file = strdup(event->data);
if (!file) {
LOG_OOM();
return;
@ -1018,66 +1016,66 @@ sc_input_manager_handle_event(struct sc_input_manager *im,
bool control = im->controller;
bool paused = im->screen->paused;
switch (event->type) {
case SDL_TEXTINPUT:
case SDL_EVENT_TEXT_INPUT:
if (!im->kp || paused) {
break;
}
sc_input_manager_process_text_input(im, &event->text);
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
// some key events do not interact with the device, so process the
// event even if control is disabled
sc_input_manager_process_key(im, &event->key);
break;
case SDL_MOUSEMOTION:
case SDL_EVENT_MOUSE_MOTION:
if (!im->mp || paused) {
break;
}
sc_input_manager_process_mouse_motion(im, &event->motion);
break;
case SDL_MOUSEWHEEL:
case SDL_EVENT_MOUSE_WHEEL:
if (!im->mp || paused) {
break;
}
sc_input_manager_process_mouse_wheel(im, &event->wheel);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
// some mouse events do not interact with the device, so process
// the event even if control is disabled
sc_input_manager_process_mouse_button(im, &event->button);
break;
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP:
case SDL_EVENT_FINGER_MOTION:
case SDL_EVENT_FINGER_DOWN:
case SDL_EVENT_FINGER_UP:
if (!im->mp || paused) {
break;
}
sc_input_manager_process_touch(im, &event->tfinger);
break;
case SDL_CONTROLLERDEVICEADDED:
case SDL_CONTROLLERDEVICEREMOVED:
case SDL_EVENT_GAMEPAD_ADDED:
case SDL_EVENT_GAMEPAD_REMOVED:
// Handle device added or removed even if paused
if (!im->gp) {
break;
}
sc_input_manager_process_gamepad_device(im, &event->cdevice);
sc_input_manager_process_gamepad_device(im, &event->gdevice);
break;
case SDL_CONTROLLERAXISMOTION:
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
if (!im->gp || paused) {
break;
}
sc_input_manager_process_gamepad_axis(im, &event->caxis);
sc_input_manager_process_gamepad_axis(im, &event->gaxis);
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
if (!im->gp || paused) {
break;
}
sc_input_manager_process_gamepad_button(im, &event->cbutton);
sc_input_manager_process_gamepad_button(im, &event->gbutton);
break;
case SDL_DROPFILE: {
case SDL_EVENT_DROP_FILE: {
if (!control) {
break;
}

View File

@ -5,8 +5,8 @@
#include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_keycode.h>
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_keycode.h>
#include "controller.h"
#include "file_pusher.h"

View File

@ -1,11 +1,13 @@
#include "common.h"
#include <stdbool.h>
#include <stdio.h>
#ifdef HAVE_V4L2
# include <libavdevice/avdevice.h>
#endif
#define SDL_MAIN_HANDLED // avoid link error on Linux Windows Subsystem
#include <SDL2/SDL.h>
#define SDL_FUNCTION_POINTER_IS_VOID_POINTER
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL.h>
#include "cli.h"
#include "options.h"

View File

@ -20,14 +20,11 @@ bool
sc_mouse_capture_handle_event(struct sc_mouse_capture *mc,
const SDL_Event *event) {
switch (event->type) {
case SDL_WINDOWEVENT:
if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
sc_mouse_capture_set_active(mc, false);
return true;
}
break;
case SDL_KEYDOWN: {
SDL_Keycode key = event->key.keysym.sym;
case SDL_EVENT_WINDOW_FOCUS_LOST:
sc_mouse_capture_set_active(mc, false);
return true;
case SDL_EVENT_KEY_DOWN: {
SDL_Keycode key = event->key.key;
if (sc_mouse_capture_is_capture_key(mc, key)) {
if (!mc->mouse_capture_key_pressed) {
mc->mouse_capture_key_pressed = key;
@ -41,8 +38,8 @@ sc_mouse_capture_handle_event(struct sc_mouse_capture *mc,
}
break;
}
case SDL_KEYUP: {
SDL_Keycode key = event->key.keysym.sym;
case SDL_EVENT_KEY_UP: {
SDL_Keycode key = event->key.key;
SDL_Keycode cap = mc->mouse_capture_key_pressed;
mc->mouse_capture_key_pressed = 0;
if (sc_mouse_capture_is_capture_key(mc, key)) {
@ -56,24 +53,24 @@ sc_mouse_capture_handle_event(struct sc_mouse_capture *mc,
}
break;
}
case SDL_MOUSEWHEEL:
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
case SDL_EVENT_MOUSE_WHEEL:
case SDL_EVENT_MOUSE_MOTION:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
if (!sc_mouse_capture_is_active(mc)) {
// The mouse will be captured on SDL_MOUSEBUTTONUP, so consume
// the event
return true;
}
break;
case SDL_MOUSEBUTTONUP:
case SDL_EVENT_MOUSE_BUTTON_UP:
if (!sc_mouse_capture_is_active(mc)) {
sc_mouse_capture_set_active(mc, true);
return true;
}
break;
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP:
case SDL_EVENT_FINGER_MOTION:
case SDL_EVENT_FINGER_DOWN:
case SDL_EVENT_FINGER_UP:
// Touch events are not compatible with relative mode
// (coordinates are not relative), so consume the event
return true;
@ -101,10 +98,9 @@ sc_mouse_capture_set_active(struct sc_mouse_capture *mc, bool capture) {
SDL_WarpMouseInWindow(mc->window, w / 2, h / 2);
}
}
#else
(void) mc;
#endif
if (SDL_SetRelativeMouseMode(capture)) {
bool ok = SDL_SetWindowRelativeMouseMode(mc->window, capture);
if (!ok) {
LOGE("Could not set relative mouse mode to %s: %s",
capture ? "true" : "false", SDL_GetError());
}
@ -112,8 +108,7 @@ sc_mouse_capture_set_active(struct sc_mouse_capture *mc, bool capture) {
bool
sc_mouse_capture_is_active(struct sc_mouse_capture *mc) {
(void) mc;
return SDL_GetRelativeMouseMode();
return SDL_GetWindowRelativeMouseMode(mc->window);
}
void

View File

@ -5,7 +5,7 @@
#include <stdbool.h>
#include <SDL2/SDL.h>
#include <SDL3/SDL.h>
struct sc_mouse_capture {
SDL_Window *window;

View File

@ -3,21 +3,29 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <SDL2/SDL.h>
#include <SDL3/SDL.h>
void
sc_opengl_init(struct sc_opengl *gl) {
gl->GetString = SDL_GL_GetProcAddress("glGetString");
gl->GetString = (const GLubyte *(*)(GLenum))
SDL_GL_GetProcAddress("glGetString");
assert(gl->GetString);
gl->TexParameterf = SDL_GL_GetProcAddress("glTexParameterf");
gl->BindTexture = (void (*)(GLenum, GLuint))
SDL_GL_GetProcAddress("glBindTexture");
assert(gl->BindTexture);
gl->TexParameterf = (void (*)(GLenum, GLenum, GLfloat))
SDL_GL_GetProcAddress("glTexParameterf");
assert(gl->TexParameterf);
gl->TexParameteri = SDL_GL_GetProcAddress("glTexParameteri");
gl->TexParameteri = (void (*)(GLenum, GLenum, GLint))
SDL_GL_GetProcAddress("glTexParameteri");
assert(gl->TexParameteri);
// optional
gl->GenerateMipmap = SDL_GL_GetProcAddress("glGenerateMipmap");
gl->GenerateMipmap = (void (*)(GLenum))
SDL_GL_GetProcAddress("glGenerateMipmap");
const char *version = (const char *) gl->GetString(GL_VERSION);
assert(version);

View File

@ -4,7 +4,7 @@
#include "common.h"
#include <stdbool.h>
#include <SDL2/SDL_opengl.h>
#include <SDL3/SDL_opengl.h>
struct sc_opengl {
const char *version;
@ -15,6 +15,9 @@ struct sc_opengl {
const GLubyte *
(*GetString)(GLenum name);
void
(*BindTexture)(GLenum target, GLuint texture);
void
(*TexParameterf)(GLenum target, GLenum pname, GLfloat param);

View File

@ -2,7 +2,8 @@
#include <assert.h>
#include <inttypes.h>
#include <SDL2/SDL_clipboard.h>
#include <stdlib.h>
#include <SDL3/SDL_clipboard.h>
#include "device_msg.h"
#include "events.h"

View File

@ -6,7 +6,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL2/SDL.h>
#include <SDL3/SDL.h>
#ifdef _WIN32
// not needed here, but winsock2.h must never be included AFTER windows.h
@ -94,7 +94,7 @@ struct scrcpy {
#ifdef _WIN32
static BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
if (ctrl_type == CTRL_C_EVENT) {
sc_push_event(SDL_QUIT);
sc_push_event(SDL_EVENT_QUIT);
return TRUE;
}
return FALSE;
@ -108,41 +108,26 @@ sdl_set_hints(const char *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");
}
// Handle a click to gain focus as any other click
if (!SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1")) {
LOGW("Could not enable mouse focus clickthrough");
}
#ifdef SCRCPY_SDL_HAS_HINT_TOUCH_MOUSE_EVENTS
// Disable synthetic mouse events from touch events
// Touch events with id SDL_TOUCH_MOUSEID are ignored anyway, but it is
// better not to generate them in the first place.
if (!SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0")) {
LOGW("Could not disable synthetic mouse events");
}
#endif
#ifdef SCRCPY_SDL_HAS_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
// Disable compositor bypassing on X11
if (!SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0")) {
LOGW("Could not disable X11 compositor bypass");
}
#endif
// Do not minimize on focus loss
if (!SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0")) {
@ -198,7 +183,7 @@ event_loop(struct scrcpy *s, bool has_screen) {
case SC_EVENT_TIME_LIMIT_REACHED:
LOGI("Time limit reached");
return SCRCPY_EXIT_SUCCESS;
case SDL_QUIT:
case SDL_EVENT_QUIT:
LOGD("User requested to quit");
return SCRCPY_EXIT_SUCCESS;
case SC_EVENT_RUN_ON_MAIN_THREAD: {
@ -238,7 +223,7 @@ await_for_server(bool *connected) {
SDL_Event event;
while (SDL_WaitEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
case SDL_EVENT_QUIT:
if (connected) {
*connected = false;
}
@ -366,12 +351,19 @@ static void
init_sdl_gamepads(void) {
// Trigger a SDL_CONTROLLERDEVICEADDED event for all gamepads already
// connected
int num_joysticks = SDL_NumJoysticks();
for (int i = 0; i < num_joysticks; ++i) {
if (SDL_IsGameController(i)) {
int count;
SDL_JoystickID *joysticks = SDL_GetJoysticks(&count);
if (!joysticks) {
LOGE("Could not list joysticks: %s", SDL_GetError());
return;
}
for (int i = 0; i < count; ++i) {
SDL_JoystickID joystick = joysticks[i];
if (SDL_IsGamepad(joystick)) {
SDL_Event event;
event.cdevice.type = SDL_CONTROLLERDEVICEADDED;
event.cdevice.which = i;
event.gdevice.type = SDL_EVENT_GAMEPAD_ADDED;
event.gdevice.which = i;
SDL_PushEvent(&event);
}
}
@ -387,7 +379,7 @@ scrcpy(struct scrcpy_options *options) {
struct scrcpy *s = &scrcpy;
// Minimal SDL initialization
if (SDL_Init(SDL_INIT_EVENTS)) {
if (!SDL_Init(SDL_INIT_EVENTS)) {
LOGE("Could not initialize SDL: %s", SDL_GetError());
return SCRCPY_EXIT_FAILURE;
}
@ -513,7 +505,7 @@ scrcpy(struct scrcpy_options *options) {
// --no-video-playback is passed so that clipboard synchronization
// still works.
// <https://github.com/Genymobile/scrcpy/issues/4418>
if (SDL_Init(SDL_INIT_VIDEO)) {
if (!SDL_Init(SDL_INIT_VIDEO)) {
// If it fails, it is an error only if video playback is enabled
if (options->video_playback) {
LOGE("Could not initialize SDL video: %s", SDL_GetError());
@ -525,14 +517,14 @@ scrcpy(struct scrcpy_options *options) {
}
if (options->audio_playback) {
if (SDL_Init(SDL_INIT_AUDIO)) {
if (!SDL_Init(SDL_INIT_AUDIO)) {
LOGE("Could not initialize SDL audio: %s", SDL_GetError());
goto end;
}
}
if (options->gamepad_input_mode != SC_GAMEPAD_INPUT_MODE_DISABLED) {
if (SDL_Init(SDL_INIT_GAMECONTROLLER)) {
if (!SDL_Init(SDL_INIT_GAMEPAD)) {
LOGE("Could not initialize SDL gamepad: %s", SDL_GetError());
goto end;
}

View File

@ -2,12 +2,13 @@
#include <assert.h>
#include <string.h>
#include <SDL2/SDL.h>
#include <SDL3/SDL.h>
#include "events.h"
#include "icon.h"
#include "options.h"
#include "util/log.h"
#include "util/window.h"
#define DISPLAY_MARGINS 96
@ -64,7 +65,9 @@ set_window_size(struct sc_screen *screen, struct sc_size new_size) {
static bool
get_preferred_display_bounds(struct sc_size *bounds) {
SDL_Rect rect;
if (SDL_GetDisplayUsableBounds(0, &rect)) {
SDL_DisplayID display = SDL_GetPrimaryDisplay();
bool ok = SDL_GetDisplayUsableBounds(display, &rect);
if (!ok) {
LOGW("Could not get display usable bounds: %s", SDL_GetError());
return false;
}
@ -168,7 +171,7 @@ sc_screen_update_content_rect(struct sc_screen *screen) {
int dw;
int dh;
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
SDL_GetWindowSizeInPixels(screen->window, &dw, &dh);
struct sc_size content_size = screen->content_size;
// The drawable size is the window size * the HiDPI scale
@ -208,6 +211,7 @@ sc_screen_update_content_rect(struct sc_screen *screen) {
static void
sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
assert(screen->video);
assert(screen->has_video_window);
if (update_content_rect) {
sc_screen_update_content_rect(screen);
@ -225,7 +229,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
@ -235,18 +239,18 @@ sc_screen_render_novideo(struct sc_screen *screen) {
//
// <https://bugzilla.libsdl.org/show_bug.cgi?id=2077>
// <https://stackoverflow.com/a/40693139/1987178>
static int
static bool
event_watcher(void *data, SDL_Event *event) {
struct sc_screen *screen = data;
assert(screen->video);
if (event->type == SDL_WINDOWEVENT
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
if (event->type == SDL_EVENT_WINDOW_EXPOSED) {
// In practice, it seems to always be called from the same thread in
// that specific case. Anyway, it's just a workaround.
sc_screen_render(screen, true);
}
return 0;
return true;
}
#endif
@ -257,6 +261,7 @@ sc_screen_frame_sink_open(struct sc_frame_sink *sink,
(void) ctx;
struct sc_screen *screen = DOWNCAST(sink);
(void) screen;
if (ctx->width <= 0 || ctx->width > 0xFFFF
|| ctx->height <= 0 || ctx->height > 0xFFFF) {
@ -264,19 +269,6 @@ sc_screen_frame_sink_open(struct sc_frame_sink *sink,
return false;
}
assert(ctx->width > 0 && ctx->width <= 0xFFFF);
assert(ctx->height > 0 && ctx->height <= 0xFFFF);
// screen->frame_size is never used before the event is pushed, and the
// event acts as a memory barrier so it is safe without mutex
screen->frame_size.width = ctx->width;
screen->frame_size.height = ctx->height;
// Post the event on the UI thread (the texture must be created from there)
bool ok = sc_push_event(SC_EVENT_SCREEN_INIT_SIZE);
if (!ok) {
return false;
}
#ifndef NDEBUG
screen->open = true;
#endif
@ -327,6 +319,7 @@ sc_screen_init(struct sc_screen *screen,
const struct sc_screen_params *params) {
screen->resize_pending = false;
screen->has_frame = false;
screen->has_video_window = false;
screen->fullscreen = false;
screen->maximized = false;
screen->minimized = false;
@ -360,7 +353,7 @@ sc_screen_init(struct sc_screen *screen,
}
}
uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI;
uint32_t window_flags = SDL_WINDOW_HIGH_PIXEL_DENSITY;
if (params->always_on_top) {
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
}
@ -394,22 +387,31 @@ sc_screen_init(struct sc_screen *screen,
}
// The window will be positioned and sized on first video frame
screen->window = SDL_CreateWindow(title, x, y, width, height, window_flags);
screen->window =
sc_create_sdl_window(title, x, y, width, height, window_flags);
if (!screen->window) {
LOGE("Could not create window: %s", SDL_GetError());
goto error_destroy_fps_counter;
}
ok = SDL_StartTextInput(screen->window);
if (!ok) {
LOGE("Could not enable text input: %s", SDL_GetError());
goto error_destroy_window;
}
SDL_Surface *icon = scrcpy_icon_load();
if (icon) {
SDL_SetWindowIcon(screen->window, icon);
if (!SDL_SetWindowIcon(screen->window, icon)) {
LOGW("Could not set window icon: %s", SDL_GetError());
}
} else if (params->video) {
// just a warning
LOGW("Could not load icon");
} 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;
@ -603,44 +605,6 @@ sc_screen_set_orientation(struct sc_screen *screen,
sc_screen_render(screen, true);
}
static bool
sc_screen_init_size(struct sc_screen *screen) {
// Before first frame
assert(!screen->has_frame);
// The requested size is passed via screen->frame_size
struct sc_size content_size =
get_oriented_size(screen->frame_size, screen->orientation);
screen->content_size = content_size;
enum sc_display_result res =
sc_display_set_texture_size(&screen->display, screen->frame_size);
return res != SC_DISPLAY_RESULT_ERROR;
}
// recreate the texture and resize the window if the frame size has changed
static enum sc_display_result
prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
assert(screen->video);
if (screen->frame_size.width == new_frame_size.width
&& screen->frame_size.height == new_frame_size.height) {
return SC_DISPLAY_RESULT_OK;
}
// frame dimension changed
screen->frame_size = new_frame_size;
struct sc_size new_content_size =
get_oriented_size(new_frame_size, screen->orientation);
set_content_size(screen, new_content_size);
sc_screen_update_content_rect(screen);
return sc_display_set_texture_size(&screen->display, screen->frame_size);
}
static bool
sc_screen_apply_frame(struct sc_screen *screen) {
assert(screen->video);
@ -649,7 +613,38 @@ sc_screen_apply_frame(struct sc_screen *screen) {
AVFrame *frame = screen->frame;
struct sc_size new_frame_size = {frame->width, frame->height};
enum sc_display_result res = prepare_for_frame(screen, new_frame_size);
if (!screen->has_frame
|| screen->frame_size.width != new_frame_size.width
|| screen->frame_size.height != new_frame_size.height) {
// frame dimension changed
screen->frame_size = new_frame_size;
struct sc_size new_content_size =
get_oriented_size(new_frame_size, screen->orientation);
if (screen->has_frame) {
set_content_size(screen, new_content_size);
sc_screen_update_content_rect(screen);
} else {
// This is the first frame
screen->has_frame = true;
screen->content_size = new_content_size;
}
enum sc_display_result res =
sc_display_prepare_texture(&screen->display, screen->frame_size,
frame->colorspace, frame->color_range);
if (res == SC_DISPLAY_RESULT_ERROR) {
return false;
}
if (res == SC_DISPLAY_RESULT_PENDING) {
// Not an error, but do not continue
return true;
}
}
enum sc_display_result res =
sc_display_update_texture(&screen->display, frame);
if (res == SC_DISPLAY_RESULT_ERROR) {
return false;
}
@ -658,17 +653,9 @@ sc_screen_apply_frame(struct sc_screen *screen) {
return true;
}
res = sc_display_update_texture(&screen->display, frame);
if (res == SC_DISPLAY_RESULT_ERROR) {
return false;
}
if (res == SC_DISPLAY_RESULT_PENDING) {
// Not an error, but do not continue
return true;
}
if (!screen->has_frame) {
screen->has_frame = true;
assert(screen->has_frame);
if (!screen->has_video_window) {
screen->has_video_window = true;
// this is the very first frame, show the window
sc_screen_show_initial_window(screen);
@ -738,8 +725,8 @@ void
sc_screen_toggle_fullscreen(struct sc_screen *screen) {
assert(screen->video);
uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;
if (SDL_SetWindowFullscreen(screen->window, new_mode)) {
bool ok = SDL_SetWindowFullscreen(screen->window, !screen->fullscreen);
if (!ok) {
LOGW("Could not switch fullscreen mode: %s", SDL_GetError());
return;
}
@ -800,16 +787,9 @@ sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
bool
sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
// !video implies !has_video_window
assert(screen->video || !screen->has_video_window);
switch (event->type) {
case SC_EVENT_SCREEN_INIT_SIZE: {
// The initial size is passed via screen->frame_size
bool ok = sc_screen_init_size(screen);
if (!ok) {
LOGE("Could not initialize screen size");
return false;
}
return true;
}
case SC_EVENT_NEW_FRAME: {
bool ok = sc_screen_update_frame(screen);
if (!ok) {
@ -818,45 +798,38 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
}
return true;
}
case SDL_WINDOWEVENT:
if (!screen->video
&& event->window.event == SDL_WINDOWEVENT_EXPOSED) {
case SDL_EVENT_WINDOW_EXPOSED:
if (!screen->video) {
sc_screen_render_novideo(screen);
} else if (screen->has_video_window) {
sc_screen_render(screen, true);
}
// !video implies !has_frame
assert(screen->video || !screen->has_frame);
if (!screen->has_frame) {
// Do nothing
return true;
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
if (screen->has_video_window) {
sc_screen_render(screen, true);
}
return true;
case SDL_EVENT_WINDOW_MAXIMIZED:
screen->maximized = true;
return true;
case SDL_EVENT_WINDOW_MINIMIZED:
screen->minimized = true;
return true;
case SDL_EVENT_WINDOW_RESTORED:
if (screen->fullscreen) {
// On Windows, in maximized+fullscreen, disabling
// fullscreen mode unexpectedly triggers the "restored"
// then "maximized" events, leaving the window in a
// weird state (maximized according to the events, but
// not maximized visually).
return true;
}
switch (event->window.event) {
case SDL_WINDOWEVENT_EXPOSED:
sc_screen_render(screen, true);
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
sc_screen_render(screen, true);
break;
case SDL_WINDOWEVENT_MAXIMIZED:
screen->maximized = true;
break;
case SDL_WINDOWEVENT_MINIMIZED:
screen->minimized = true;
break;
case SDL_WINDOWEVENT_RESTORED:
if (screen->fullscreen) {
// On Windows, in maximized+fullscreen, disabling
// fullscreen mode unexpectedly triggers the "restored"
// then "maximized" events, leaving the window in a
// weird state (maximized according to the events, but
// not maximized visually).
break;
}
screen->maximized = false;
screen->minimized = false;
apply_pending_resize(screen);
sc_screen_render(screen, true);
break;
screen->maximized = false;
screen->minimized = false;
if (screen->has_video_window) {
apply_pending_resize(screen);
sc_screen_render(screen, true);
}
return true;
}
@ -939,7 +912,7 @@ sc_screen_hidpi_scale_coords(struct sc_screen *screen, int32_t *x, int32_t *y) {
// take the HiDPI scaling (dw/ww and dh/wh) into account
int ww, wh, dw, dh;
SDL_GetWindowSize(screen->window, &ww, &wh);
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
SDL_GetWindowSizeInPixels(screen->window, &dw, &dh);
// scale for HiDPI (64 bits for intermediate multiplications)
*x = (int64_t) *x * dw / ww;

View File

@ -5,7 +5,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL.h>
#include <SDL3/SDL.h>
#include <libavcodec/avcodec.h>
#include <libavutil/frame.h>
#include <libavutil/pixfmt.h>
@ -61,6 +61,7 @@ struct sc_screen {
// rectangle of the content (excluding black borders)
struct SDL_Rect rect;
bool has_frame;
bool has_video_window;
bool fullscreen;
bool maximized;
bool minimized;

View File

@ -6,11 +6,11 @@
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL_keycode.h>
#include <SDL3/SDL_keycode.h>
#include "options.h"
#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI)
#define SC_SDL_SHORTCUT_MODS_MASK (SDL_KMOD_CTRL | SDL_KMOD_ALT | SDL_KMOD_GUI)
// input: OR of enum sc_shortcut_mod
// output: OR of SDL_Keymod
@ -18,22 +18,22 @@ static inline uint16_t
sc_shortcut_mods_to_sdl(uint8_t shortcut_mods) {
uint16_t sdl_mod = 0;
if (shortcut_mods & SC_SHORTCUT_MOD_LCTRL) {
sdl_mod |= KMOD_LCTRL;
sdl_mod |= SDL_KMOD_LCTRL;
}
if (shortcut_mods & SC_SHORTCUT_MOD_RCTRL) {
sdl_mod |= KMOD_RCTRL;
sdl_mod |= SDL_KMOD_RCTRL;
}
if (shortcut_mods & SC_SHORTCUT_MOD_LALT) {
sdl_mod |= KMOD_LALT;
sdl_mod |= SDL_KMOD_LALT;
}
if (shortcut_mods & SC_SHORTCUT_MOD_RALT) {
sdl_mod |= KMOD_RALT;
sdl_mod |= SDL_KMOD_RALT;
}
if (shortcut_mods & SC_SHORTCUT_MOD_LSUPER) {
sdl_mod |= KMOD_LGUI;
sdl_mod |= SDL_KMOD_LGUI;
}
if (shortcut_mods & SC_SHORTCUT_MOD_RSUPER) {
sdl_mod |= KMOD_RGUI;
sdl_mod |= SDL_KMOD_RGUI;
}
return sdl_mod;
}
@ -50,12 +50,12 @@ sc_shortcut_mods_is_shortcut_mod(uint16_t sdl_shortcut_mods, uint16_t sdl_mod) {
static inline bool
sc_shortcut_mods_is_shortcut_key(uint16_t sdl_shortcut_mods,
SDL_Keycode keycode) {
return (sdl_shortcut_mods & KMOD_LCTRL && keycode == SDLK_LCTRL)
|| (sdl_shortcut_mods & KMOD_RCTRL && keycode == SDLK_RCTRL)
|| (sdl_shortcut_mods & KMOD_LALT && keycode == SDLK_LALT)
|| (sdl_shortcut_mods & KMOD_RALT && keycode == SDLK_RALT)
|| (sdl_shortcut_mods & KMOD_LGUI && keycode == SDLK_LGUI)
|| (sdl_shortcut_mods & KMOD_RGUI && keycode == SDLK_RGUI);
return (sdl_shortcut_mods & SDL_KMOD_LCTRL && keycode == SDLK_LCTRL)
|| (sdl_shortcut_mods & SDL_KMOD_RCTRL && keycode == SDLK_RCTRL)
|| (sdl_shortcut_mods & SDL_KMOD_LALT && keycode == SDLK_LALT)
|| (sdl_shortcut_mods & SDL_KMOD_RALT && keycode == SDLK_RALT)
|| (sdl_shortcut_mods & SDL_KMOD_LGUI && keycode == SDLK_LGUI)
|| (sdl_shortcut_mods & SDL_KMOD_RGUI && keycode == SDLK_RGUI);
}
#endif

View File

@ -3,7 +3,7 @@
#include <assert.h>
#include <inttypes.h>
#include <string.h>
#include <SDL2/SDL_gamecontroller.h>
#include <SDL3/SDL_gamepad.h>
#include "hid/hid_gamepad.h"
#include "input_events.h"
@ -74,10 +74,10 @@ sc_gamepad_processor_process_gamepad_added(struct sc_gamepad_processor *gp,
return;
}
SDL_GameController* game_controller =
SDL_GameControllerFromInstanceID(event->gamepad_id);
SDL_Gamepad * game_controller =
SDL_GetGamepadFromID(event->gamepad_id);
assert(game_controller);
const char *name = SDL_GameControllerName(game_controller);
const char *name = SDL_GetGamepadName(game_controller);
LOGI("Gamepad added: [%" PRIu32 "] %s", event->gamepad_id, name);
sc_gamepad_uhid_send_open(gamepad, &hid_open);

View File

@ -2,8 +2,8 @@
#include <assert.h>
#include <string.h>
#include <SDL2/SDL_keyboard.h>
#include <SDL2/SDL_keycode.h>
#include <SDL3/SDL_keyboard.h>
#include <SDL3/SDL_keycode.h>
#include "util/log.h"
#include "util/thread.h"

View File

@ -55,7 +55,8 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
struct sc_mouse_uhid *mouse = DOWNCAST(mp);
struct sc_hid_input hid_input;
if (!sc_hid_mouse_generate_input_from_scroll(&hid_input, event)) {
if (!sc_hid_mouse_generate_input_from_scroll(&mouse->hid, &hid_input,
event)) {
return;
}
@ -65,6 +66,8 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
bool
sc_mouse_uhid_init(struct sc_mouse_uhid *mouse,
struct sc_controller *controller) {
sc_hid_mouse_init(&mouse->hid);
mouse->controller = controller;
static const struct sc_mouse_processor_ops ops = {

View File

@ -4,11 +4,13 @@
#include <stdbool.h>
#include "controller.h"
#include "hid/hid_mouse.h"
#include "trait/mouse_processor.h"
struct sc_mouse_uhid {
struct sc_mouse_processor mouse_processor; // mouse processor trait
struct sc_hid_mouse hid;
struct sc_controller *controller;
};

View File

@ -42,7 +42,8 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp,
struct sc_mouse_aoa *mouse = DOWNCAST(mp);
struct sc_hid_input hid_input;
if (!sc_hid_mouse_generate_input_from_scroll(&hid_input, event)) {
if (!sc_hid_mouse_generate_input_from_scroll(&mouse->hid, &hid_input,
event)) {
return;
}
@ -64,6 +65,8 @@ sc_mouse_aoa_init(struct sc_mouse_aoa *mouse, struct sc_aoa *aoa) {
return false;
}
sc_hid_mouse_init(&mouse->hid);
static const struct sc_mouse_processor_ops ops = {
.process_mouse_motion = sc_mouse_processor_process_mouse_motion,
.process_mouse_click = sc_mouse_processor_process_mouse_click,

View File

@ -6,11 +6,13 @@
#include <stdbool.h>
#include "usb/aoa_hid.h"
#include "hid/hid_mouse.h"
#include "trait/mouse_processor.h"
struct sc_mouse_aoa {
struct sc_mouse_processor mouse_processor; // mouse processor trait
struct sc_hid_mouse hid;
struct sc_aoa *aoa;
};

View File

@ -3,7 +3,7 @@
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <SDL2/SDL.h>
#include <SDL3/SDL.h>
#ifdef _WIN32
# include "adb/adb.h"
@ -45,7 +45,7 @@ event_loop(struct scrcpy_otg *s) {
case SC_EVENT_AOA_OPEN_ERROR:
LOGE("AOA open error");
return SCRCPY_EXIT_FAILURE;
case SDL_QUIT:
case SDL_EVENT_QUIT:
LOGD("User requested to quit");
return SCRCPY_EXIT_SUCCESS;
default:
@ -63,22 +63,18 @@ scrcpy_otg(struct scrcpy_options *options) {
const char *serial = options->serial;
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) {
LOGW("Could not enable linear filtering");
}
if (!SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1")) {
LOGW("Could not allow joystick background events");
}
// Minimal SDL initialization
if (SDL_Init(SDL_INIT_EVENTS)) {
if (!SDL_Init(SDL_INIT_EVENTS)) {
LOGE("Could not initialize SDL: %s", SDL_GetError());
return SCRCPY_EXIT_FAILURE;
}
if (options->gamepad_input_mode != SC_GAMEPAD_INPUT_MODE_DISABLED) {
if (SDL_Init(SDL_INIT_GAMECONTROLLER)) {
if (!SDL_Init(SDL_INIT_GAMEPAD)) {
LOGE("Could not initialize SDL controller: %s", SDL_GetError());
// Not fatal, keyboard/mouse should still work
}

View File

@ -7,12 +7,13 @@
#include "options.h"
#include "util/acksync.h"
#include "util/log.h"
#include "util/window.h"
static void
sc_screen_otg_render(struct sc_screen_otg *screen) {
SDL_RenderClear(screen->renderer);
if (screen->texture) {
SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL);
SDL_RenderTexture(screen->renderer, screen->texture, NULL, NULL);
}
SDL_RenderPresent(screen->renderer);
}
@ -34,7 +35,7 @@ sc_screen_otg_init(struct sc_screen_otg *screen,
int width = params->window_width ? params->window_width : 256;
int height = params->window_height ? params->window_height : 256;
uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI;
uint32_t window_flags = SDL_WINDOW_HIGH_PIXEL_DENSITY;
if (params->always_on_top) {
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
}
@ -42,13 +43,14 @@ sc_screen_otg_init(struct sc_screen_otg *screen,
window_flags |= SDL_WINDOW_BORDERLESS;
}
screen->window = SDL_CreateWindow(title, x, y, width, height, window_flags);
screen->window =
sc_create_sdl_window(title, x, y, width, height, window_flags);
if (!screen->window) {
LOGE("Could not create window: %s", SDL_GetError());
return false;
}
screen->renderer = SDL_CreateRenderer(screen->window, -1, 0);
screen->renderer = SDL_CreateRenderer(screen->window, NULL);
if (!screen->renderer) {
LOGE("Could not create renderer: %s", SDL_GetError());
goto error_destroy_window;
@ -59,7 +61,10 @@ sc_screen_otg_init(struct sc_screen_otg *screen,
if (icon) {
SDL_SetWindowIcon(screen->window, icon);
if (SDL_RenderSetLogicalSize(screen->renderer, icon->w, icon->h)) {
bool ok =
SDL_SetRenderLogicalPresentation(screen->renderer, icon->w, icon->h,
SDL_LOGICAL_PRESENTATION_LETTERBOX);
if (!ok) {
LOGW("Could not set renderer logical size: %s", SDL_GetError());
// don't fail
}
@ -108,10 +113,10 @@ sc_screen_otg_process_key(struct sc_screen_otg *screen,
struct sc_key_event evt = {
.action = sc_action_from_sdl_keyboard_type(event->type),
.keycode = sc_keycode_from_sdl(event->keysym.sym),
.scancode = sc_scancode_from_sdl(event->keysym.scancode),
.keycode = sc_keycode_from_sdl(event->key),
.scancode = sc_scancode_from_sdl(event->scancode),
.repeat = event->repeat,
.mods_state = sc_mods_state_from_sdl(event->keysym.mod),
.mods_state = sc_mods_state_from_sdl(event->mod),
};
assert(kp->ops->process_key);
@ -165,14 +170,12 @@ sc_screen_otg_process_mouse_wheel(struct sc_screen_otg *screen,
struct sc_mouse_scroll_event evt = {
// .position not used for HID events
#if SDL_VERSION_ATLEAST(2, 0, 18)
.hscroll = event->preciseX,
.vscroll = event->preciseY,
.hscroll = event->x,
.vscroll = event->y,
#else
.hscroll = event->x,
.vscroll = event->y,
#endif
.hscroll_int = event->x,
.vscroll_int = event->y,
.buttons_state = sc_mouse_buttons_state_from_sdl(sdl_buttons_state),
};
@ -182,34 +185,34 @@ sc_screen_otg_process_mouse_wheel(struct sc_screen_otg *screen,
static void
sc_screen_otg_process_gamepad_device(struct sc_screen_otg *screen,
const SDL_ControllerDeviceEvent *event) {
const SDL_GamepadDeviceEvent *event) {
assert(screen->gamepad);
struct sc_gamepad_processor *gp = &screen->gamepad->gamepad_processor;
if (event->type == SDL_CONTROLLERDEVICEADDED) {
SDL_GameController *gc = SDL_GameControllerOpen(event->which);
if (event->type == SDL_EVENT_GAMEPAD_ADDED) {
SDL_Gamepad *gc = SDL_OpenGamepad(event->which);
if (!gc) {
LOGW("Could not open game controller");
return;
}
SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gc);
SDL_Joystick *joystick = SDL_GetGamepadJoystick(gc);
if (!joystick) {
LOGW("Could not get controller joystick");
SDL_GameControllerClose(gc);
SDL_CloseGamepad(gc);
return;
}
struct sc_gamepad_device_event evt = {
.gamepad_id = SDL_JoystickInstanceID(joystick),
.gamepad_id = SDL_GetJoystickID(joystick),
};
gp->ops->process_gamepad_added(gp, &evt);
} else if (event->type == SDL_CONTROLLERDEVICEREMOVED) {
} else if (event->type == SDL_EVENT_GAMEPAD_REMOVED) {
SDL_JoystickID id = event->which;
SDL_GameController *gc = SDL_GameControllerFromInstanceID(id);
SDL_Gamepad *gc = SDL_GetGamepadFromID(id);
if (gc) {
SDL_GameControllerClose(gc);
SDL_CloseGamepad(gc);
} else {
LOGW("Unknown gamepad device removed");
}
@ -223,7 +226,7 @@ sc_screen_otg_process_gamepad_device(struct sc_screen_otg *screen,
static void
sc_screen_otg_process_gamepad_axis(struct sc_screen_otg *screen,
const SDL_ControllerAxisEvent *event) {
const SDL_GamepadAxisEvent *event) {
assert(screen->gamepad);
struct sc_gamepad_processor *gp = &screen->gamepad->gamepad_processor;
@ -242,7 +245,7 @@ sc_screen_otg_process_gamepad_axis(struct sc_screen_otg *screen,
static void
sc_screen_otg_process_gamepad_button(struct sc_screen_otg *screen,
const SDL_ControllerButtonEvent *event) {
const SDL_GamepadButtonEvent *event) {
assert(screen->gamepad);
struct sc_gamepad_processor *gp = &screen->gamepad->gamepad_processor;
@ -267,59 +270,55 @@ sc_screen_otg_handle_event(struct sc_screen_otg *screen, SDL_Event *event) {
}
switch (event->type) {
case SDL_WINDOWEVENT:
switch (event->window.event) {
case SDL_WINDOWEVENT_EXPOSED:
sc_screen_otg_render(screen);
break;
}
return;
case SDL_KEYDOWN:
case SDL_EVENT_WINDOW_EXPOSED:
sc_screen_otg_render(screen);
break;
case SDL_EVENT_KEY_DOWN:
if (screen->keyboard) {
sc_screen_otg_process_key(screen, &event->key);
}
break;
case SDL_KEYUP:
case SDL_EVENT_KEY_UP:
if (screen->keyboard) {
sc_screen_otg_process_key(screen, &event->key);
}
break;
case SDL_MOUSEMOTION:
case SDL_EVENT_MOUSE_MOTION:
if (screen->mouse) {
sc_screen_otg_process_mouse_motion(screen, &event->motion);
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
if (screen->mouse) {
sc_screen_otg_process_mouse_button(screen, &event->button);
}
break;
case SDL_MOUSEBUTTONUP:
case SDL_EVENT_MOUSE_BUTTON_UP:
if (screen->mouse) {
sc_screen_otg_process_mouse_button(screen, &event->button);
}
break;
case SDL_MOUSEWHEEL:
case SDL_EVENT_MOUSE_WHEEL:
if (screen->mouse) {
sc_screen_otg_process_mouse_wheel(screen, &event->wheel);
}
break;
case SDL_CONTROLLERDEVICEADDED:
case SDL_CONTROLLERDEVICEREMOVED:
case SDL_EVENT_GAMEPAD_ADDED:
case SDL_EVENT_GAMEPAD_REMOVED:
// Handle device added or removed even if paused
if (screen->gamepad) {
sc_screen_otg_process_gamepad_device(screen, &event->cdevice);
sc_screen_otg_process_gamepad_device(screen, &event->gdevice);
}
break;
case SDL_CONTROLLERAXISMOTION:
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
if (screen->gamepad) {
sc_screen_otg_process_gamepad_axis(screen, &event->caxis);
sc_screen_otg_process_gamepad_axis(screen, &event->gaxis);
}
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
if (screen->gamepad) {
sc_screen_otg_process_gamepad_button(screen, &event->cbutton);
sc_screen_otg_process_gamepad_button(screen, &event->gbutton);
}
break;
}

View File

@ -5,7 +5,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <SDL2/SDL.h>
#include <SDL3/SDL.h>
#include "mouse_capture.h"
#include "usb/gamepad_aoa.h"

View File

@ -50,13 +50,13 @@ log_level_sdl_to_sc(SDL_LogPriority priority) {
void
sc_set_log_level(enum sc_log_level level) {
SDL_LogPriority sdl_log = log_level_sc_to_sdl(level);
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, sdl_log);
SDL_LogSetPriority(SDL_LOG_CATEGORY_CUSTOM, sdl_log);
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, sdl_log);
SDL_SetLogPriority(SDL_LOG_CATEGORY_CUSTOM, sdl_log);
}
enum sc_log_level
sc_get_log_level(void) {
SDL_LogPriority sdl_log = SDL_LogGetPriority(SDL_LOG_CATEGORY_APPLICATION);
SDL_LogPriority sdl_log = SDL_GetLogPriority(SDL_LOG_CATEGORY_APPLICATION);
return log_level_sdl_to_sc(sdl_log);
}
@ -128,7 +128,7 @@ sc_av_log_callback(void *avcl, int level, const char *fmt, va_list vl) {
free(local_fmt);
}
static const char *const sc_sdl_log_priority_names[SDL_NUM_LOG_PRIORITIES] = {
static const char *const sc_sdl_log_priority_names[SDL_LOG_PRIORITY_COUNT] = {
[SDL_LOG_PRIORITY_VERBOSE] = "VERBOSE",
[SDL_LOG_PRIORITY_DEBUG] = "DEBUG",
[SDL_LOG_PRIORITY_INFO] = "INFO",
@ -144,14 +144,14 @@ sc_sdl_log_print(void *userdata, int category, SDL_LogPriority priority,
(void) category;
FILE *out = priority < SDL_LOG_PRIORITY_WARN ? stdout : stderr;
assert(priority < SDL_NUM_LOG_PRIORITIES);
assert(priority < SDL_LOG_PRIORITY_COUNT);
const char *prio_name = sc_sdl_log_priority_names[priority];
fprintf(out, "%s: %s\n", prio_name, message);
}
void
sc_log_configure(void) {
SDL_LogSetOutputFunction(sc_sdl_log_print, NULL);
SDL_SetLogOutputFunction(sc_sdl_log_print, NULL);
// Redirect FFmpeg logs to SDL logs
av_log_set_callback(sc_av_log_callback);
}

View File

@ -3,7 +3,7 @@
#include "common.h"
#include <SDL2/SDL_log.h>
#include <SDL3/SDL_log.h>
#include "options.h"

View File

@ -2,6 +2,7 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
# include <ws2tcpip.h>

View File

@ -4,7 +4,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <SDL2/SDL_thread.h>
#include <SDL3/SDL_mutex.h>
#include "util/log.h"
@ -31,11 +31,7 @@ static SDL_ThreadPriority
to_sdl_thread_priority(enum sc_thread_priority priority) {
switch (priority) {
case SC_THREAD_PRIORITY_TIME_CRITICAL:
#ifdef SCRCPY_SDL_HAS_THREAD_PRIORITY_TIME_CRITICAL
return SDL_THREAD_PRIORITY_TIME_CRITICAL;
#else
// fall through
#endif
case SC_THREAD_PRIORITY_HIGH:
return SDL_THREAD_PRIORITY_HIGH;
case SC_THREAD_PRIORITY_NORMAL:
@ -51,8 +47,8 @@ to_sdl_thread_priority(enum sc_thread_priority priority) {
bool
sc_thread_set_priority(enum sc_thread_priority priority) {
SDL_ThreadPriority sdl_priority = to_sdl_thread_priority(priority);
int r = SDL_SetThreadPriority(sdl_priority);
if (r) {
bool ok = SDL_SetCurrentThreadPriority(sdl_priority);
if (!ok) {
LOGD("Could not set thread priority: %s", SDL_GetError());
return false;
}
@ -67,7 +63,7 @@ sc_thread_join(sc_thread *thread, int *status) {
bool
sc_mutex_init(sc_mutex *mutex) {
SDL_mutex *sdl_mutex = SDL_CreateMutex();
SDL_Mutex *sdl_mutex = SDL_CreateMutex();
if (!sdl_mutex) {
LOG_OOM();
return false;
@ -89,40 +85,25 @@ void
sc_mutex_lock(sc_mutex *mutex) {
// SDL mutexes are recursive, but we don't want to use recursive mutexes
assert(!sc_mutex_held(mutex));
int r = SDL_LockMutex(mutex->mutex);
SDL_LockMutex(mutex->mutex);
#ifndef NDEBUG
if (r) {
LOGE("Could not lock mutex: %s", SDL_GetError());
abort();
}
atomic_store_explicit(&mutex->locker, sc_thread_get_id(),
memory_order_relaxed);
#else
(void) r;
#endif
}
void
sc_mutex_unlock(sc_mutex *mutex) {
#ifndef NDEBUG
assert(sc_mutex_held(mutex));
#ifndef NDEBUG
atomic_store_explicit(&mutex->locker, 0, memory_order_relaxed);
#endif
int r = SDL_UnlockMutex(mutex->mutex);
#ifndef NDEBUG
if (r) {
LOGE("Could not lock mutex: %s", SDL_GetError());
abort();
}
#else
(void) r;
#endif
SDL_UnlockMutex(mutex->mutex);
}
sc_thread_id
sc_thread_get_id(void) {
return SDL_ThreadID();
return SDL_GetCurrentThreadID();
}
#ifndef NDEBUG
@ -136,7 +117,7 @@ sc_mutex_held(struct sc_mutex *mutex) {
bool
sc_cond_init(sc_cond *cond) {
SDL_cond *sdl_cond = SDL_CreateCond();
SDL_Condition *sdl_cond = SDL_CreateCondition();
if (!sdl_cond) {
LOG_OOM();
return false;
@ -148,22 +129,15 @@ sc_cond_init(sc_cond *cond) {
void
sc_cond_destroy(sc_cond *cond) {
SDL_DestroyCond(cond->cond);
SDL_DestroyCondition(cond->cond);
}
void
sc_cond_wait(sc_cond *cond, sc_mutex *mutex) {
int r = SDL_CondWait(cond->cond, mutex->mutex);
SDL_WaitCondition(cond->cond, mutex->mutex);
#ifndef NDEBUG
if (r) {
LOGE("Could not wait on condition: %s", SDL_GetError());
abort();
}
atomic_store_explicit(&mutex->locker, sc_thread_get_id(),
memory_order_relaxed);
#else
(void) r;
#endif
}
@ -177,44 +151,22 @@ sc_cond_timedwait(sc_cond *cond, sc_mutex *mutex, sc_tick deadline) {
// Round up to the next millisecond to guarantee that the deadline is
// reached when returning due to timeout
uint32_t ms = SC_TICK_TO_MS(deadline - now + SC_TICK_FROM_MS(1) - 1);
int r = SDL_CondWaitTimeout(cond->cond, mutex->mutex, ms);
bool signaled = SDL_WaitConditionTimeout(cond->cond, mutex->mutex, ms);
#ifndef NDEBUG
if (r < 0) {
LOGE("Could not wait on condition with timeout: %s", SDL_GetError());
abort();
}
atomic_store_explicit(&mutex->locker, sc_thread_get_id(),
memory_order_relaxed);
#endif
assert(r == 0 || r == SDL_MUTEX_TIMEDOUT);
// The deadline is reached on timeout
assert(r != SDL_MUTEX_TIMEDOUT || sc_tick_now() >= deadline);
return r == 0;
assert(signaled || sc_tick_now() >= deadline);
return signaled;
}
void
sc_cond_signal(sc_cond *cond) {
int r = SDL_CondSignal(cond->cond);
#ifndef NDEBUG
if (r) {
LOGE("Could not signal a condition: %s", SDL_GetError());
abort();
}
#else
(void) r;
#endif
SDL_SignalCondition(cond->cond);
}
void
sc_cond_broadcast(sc_cond *cond) {
int r = SDL_CondBroadcast(cond->cond);
#ifndef NDEBUG
if (r) {
LOGE("Could not broadcast a condition: %s", SDL_GetError());
abort();
}
#else
(void) r;
#endif
SDL_BroadcastCondition(cond->cond);
}

View File

@ -10,8 +10,8 @@
/* Forward declarations */
typedef struct SDL_Thread SDL_Thread;
typedef struct SDL_mutex SDL_mutex;
typedef struct SDL_cond SDL_cond;
typedef struct SDL_Mutex SDL_Mutex;
typedef struct SDL_Condition SDL_Condition;
typedef int sc_thread_fn(void *);
typedef unsigned sc_thread_id;
@ -29,14 +29,14 @@ enum sc_thread_priority {
};
typedef struct sc_mutex {
SDL_mutex *mutex;
SDL_Mutex *mutex;
#ifndef NDEBUG
sc_atomic_thread_id locker;
#endif
} sc_mutex;
typedef struct sc_cond {
SDL_cond *cond;
SDL_Condition *cond;
} sc_cond;
extern sc_thread_id SC_MAIN_THREAD_ID;

View File

@ -191,7 +191,8 @@ sc_vecdeque_reallocdata_(void *ptr, size_t newcap, size_t item_size,
size_t right_len = MIN(size, oldcap - oldorigin);
assert(right_len);
memcpy(newptr, (char *) ptr + (oldorigin * item_size), right_len * item_size);
memcpy(newptr, (char *) ptr + (oldorigin * item_size),
right_len * item_size);
if (size > right_len) {
memcpy((char *) newptr + (right_len * item_size), ptr,

33
app/src/util/window.c Normal file
View File

@ -0,0 +1,33 @@
#include "window.h"
SDL_Window *
sc_create_sdl_window(const char *title, int64_t x, int64_t y, int64_t width,
int64_t height, int64_t flags) {
SDL_Window *window = NULL;
SDL_PropertiesID props = SDL_CreateProperties();
if (!props) {
return NULL;
}
bool ok =
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING,
title);
ok &= SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, x);
ok &= SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y);
ok &= SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER,
width);
ok &= SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER,
height);
ok &= SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER,
flags);
if (!ok) {
SDL_DestroyProperties(props);
return NULL;
}
window = SDL_CreateWindowWithProperties(props);
SDL_DestroyProperties(props);
return window;
}

13
app/src/util/window.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef SC_WINDOW_H
#define SC_WINDOW_H
#include "common.h"
#include <stdint.h>
#include <SDL3/SDL_video.h>
SDL_Window *
sc_create_sdl_window(const char *title, int64_t x, int64_t y, int64_t width,
int64_t height, int64_t flags);
#endif

View File

@ -10,17 +10,20 @@
#ifdef HAVE_USB
# include <libusb-1.0/libusb.h>
#endif
#include <SDL2/SDL_version.h>
#include <SDL3/SDL_version.h>
void
scrcpy_print_version(void) {
printf("\nDependencies (compiled / linked):\n");
SDL_version sdl;
SDL_GetVersion(&sdl);
int sdl = SDL_GetVersion();
printf(" - SDL: %u.%u.%u / %u.%u.%u\n",
SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
(unsigned) sdl.major, (unsigned) sdl.minor, (unsigned) sdl.patch);
SDL_MAJOR_VERSION,
SDL_MINOR_VERSION,
SDL_MICRO_VERSION,
SDL_VERSIONNUM_MAJOR(sdl),
SDL_VERSIONNUM_MINOR(sdl),
SDL_VERSIONNUM_MICRO(sdl));
unsigned avcodec = avcodec_version();
printf(" - libavcodec: %u.%u.%u / %u.%u.%u\n",

View File

@ -1,13 +1,8 @@
package com.genymobile.scrcpy.util;
import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.wrappers.ContentProvider;
import com.genymobile.scrcpy.wrappers.ServiceManager;
import android.os.Build;
import java.io.IOException;
public final class Settings {
public static final String TABLE_SYSTEM = ContentProvider.TABLE_SYSTEM;
@ -18,66 +13,26 @@ public final class Settings {
/* not instantiable */
}
private static void execSettingsPut(String table, String key, String value) throws SettingsException {
try {
Command.exec("settings", "put", table, key, value);
} catch (IOException | InterruptedException e) {
throw new SettingsException("put", table, key, value, e);
}
}
private static String execSettingsGet(String table, String key) throws SettingsException {
try {
return Command.execReadLine("settings", "get", table, key);
} catch (IOException | InterruptedException e) {
throw new SettingsException("get", table, key, null, e);
}
}
public static String getValue(String table, String key) throws SettingsException {
if (Build.VERSION.SDK_INT <= AndroidVersions.API_30_ANDROID_11) {
// on Android >= 12, it always fails: <https://github.com/Genymobile/scrcpy/issues/2788>
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
return provider.getValue(table, key);
} catch (SettingsException e) {
Ln.w("Could not get settings value via ContentProvider, fallback to settings process", e);
}
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
return provider.getValue(table, key);
}
return execSettingsGet(table, key);
}
public static void putValue(String table, String key, String value) throws SettingsException {
if (Build.VERSION.SDK_INT <= AndroidVersions.API_30_ANDROID_11) {
// on Android >= 12, it always fails: <https://github.com/Genymobile/scrcpy/issues/2788>
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
provider.putValue(table, key, value);
} catch (SettingsException e) {
Ln.w("Could not put settings value via ContentProvider, fallback to settings process", e);
}
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
provider.putValue(table, key, value);
}
execSettingsPut(table, key, value);
}
public static String getAndPutValue(String table, String key, String value) throws SettingsException {
if (Build.VERSION.SDK_INT <= AndroidVersions.API_30_ANDROID_11) {
// on Android >= 12, it always fails: <https://github.com/Genymobile/scrcpy/issues/2788>
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
String oldValue = provider.getValue(table, key);
if (!value.equals(oldValue)) {
provider.putValue(table, key, value);
}
return oldValue;
} catch (SettingsException e) {
Ln.w("Could not get and put settings value via ContentProvider, fallback to settings process", e);
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
String oldValue = provider.getValue(table, key);
if (!value.equals(oldValue)) {
provider.putValue(table, key, value);
}
return oldValue;
}
String oldValue = getValue(table, key);
if (!value.equals(oldValue)) {
putValue(table, key, value);
}
return oldValue;
}
}