Updated SDL and fixed Emscripten build

This commit is contained in:
Yousef Khadadeh 2026-04-12 17:31:32 +03:00
parent 919e41db22
commit bd4d662dd9
36 changed files with 1156 additions and 125 deletions

View file

@ -56,6 +56,7 @@ set(sources "src/main.cpp"
"src/CustomEvents.cpp"
"src/Screens/Screen.cpp"
"src/Screens/DrawingProgramScreen.cpp"
"src/Screens/FileSelectScreen.cpp"
"src/Bookmarks/BookmarkListItem.cpp"
"src/Bookmarks/BookmarkManager.cpp"
"src/ResourceDisplay/ResourceDisplay.cpp"
@ -259,6 +260,8 @@ elseif(APPLE)
endif()
if(EMSCRIPTEN)
set(SDL_EMSCRIPTEN_PERSISTENT_PATH "/infinipaint" CACHE STRING "SDL emscripten persistent path")
if(CMAKE_BUILD_TYPE STREQUAL Debug)
target_compile_options(main PRIVATE -g3)
target_link_options(main PRIVATE -g3)
@ -377,7 +380,7 @@ else()
if(ANDROID)
target_link_libraries(main SDL3::SDL3 game-text-input::game-text-input)
else()
target_link_libraries(main sdl::sdl)
target_link_libraries(main sdl-infinipaint::sdl-infinipaint)
endif()
target_link_libraries(main icu-infinipaint::icu-infinipaint)
endif()

View file

@ -79,7 +79,7 @@
<activity android:name="com.erroratline0.infinipaint.InfiniPaint"
android:label="@string/app_name"
android:alwaysRetainTaskState="true"
android:launchMode="singleInstance"
android:launchMode="singleTop"
android:configChanges="layoutDirection|locale|grammaticalGender|fontScale|fontWeightAdjustment|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
android:preferMinimalPostProcessing="true"
android:exported="true"

View file

@ -256,6 +256,7 @@ public class HIDDeviceManager {
0x24c6, // PowerA
0x2c22, // Qanba
0x2dc8, // 8BitDo
0x37d7, // Flydigi
0x9886, // ASTRO Gaming
};

View file

@ -61,7 +61,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
private static final String TAG = "SDL";
private static final int SDL_MAJOR_VERSION = 3;
private static final int SDL_MINOR_VERSION = 4;
private static final int SDL_MICRO_VERSION = 2;
private static final int SDL_MICRO_VERSION = 4;
/*
// Display InputType.SOURCE/CLASS of events and devices
//

View file

@ -65,7 +65,6 @@ class SDLInputConnection extends BaseInputConnection
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if (Build.VERSION.SDK_INT <= 29 /* Android 10.0 (Q) */) {
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions>/14560344/android-backspace-in-webview-baseinputconnection
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
if (beforeLength > 0 && afterLength == 0) {
@ -75,7 +74,6 @@ class SDLInputConnection extends BaseInputConnection
}
return true;
}
}
if (!super.deleteSurroundingText(beforeLength, afterLength)) {
return false;

View file

@ -205,8 +205,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
WindowInsets.Type.systemGestures() |
WindowInsets.Type.mandatorySystemGestures() |
WindowInsets.Type.tappableElement() |
WindowInsets.Type.displayCutout() |
WindowInsets.Type.ime());
WindowInsets.Type.displayCutout());
SDLActivity.onNativeInsetsChanged(combined.left, combined.right, combined.top, combined.bottom);
}

View file

@ -1,2 +1,3 @@
conan export %0/../conan-skia/recipes/skia/all/conanfile.py --version=143.20251028.0
conan export %0/../icu/all/conanfile.py --version=77.1
conan export %0/../sdl/3.x/conanfile.py --version=3.4.4

View file

@ -4,5 +4,6 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
conan export "$(dirname "$0")/conan-skia/recipes/skia/all/conanfile.py" --version=143.20251028.0
conan export "$(dirname "$0")/icu/all/conanfile.py" --version=77.1
conan export "$(dirname "$0")/sdl/3.x/conanfile.py" --version=3.4.4
#conan create . -o use_system_expat=False -o use_system_freetype=False -o use_system_harfbuzz=False -o use_system_icu=False -o use_system_libjpeg_turbo=False -o use_system_libpng=False -o use_system_libwebp=False -o use_system_zlib=False --build=missing --version=134.20250327.0
#cd ../../../..

View file

@ -0,0 +1,4 @@
sources:
"3.4.4":
url: "https://github.com/libsdl-org/SDL/releases/download/release-3.4.4/SDL3-3.4.4.tar.gz"
sha256: "ee712dbe6a89bb140bbfc2ce72358fb5ee5cc2240abeabd54855012db30b3864"

475
conan/sdl/3.x/conanfile.py Normal file
View file

@ -0,0 +1,475 @@
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.apple import is_apple_os
from conan.tools.files import get, copy, replace_in_file, rmdir
from conan.tools.gnu import PkgConfigDeps
from conan.tools.microsoft import is_msvc
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout, CMakeDeps
import os
required_conan_version = ">=2"
_subsystems = [
("audio", []),
("video", []),
("gpu", ["video"]),
("render", ["video"]),
("camera", ["video"]),
("joystick", []),
("haptic", ["joystick"]),
("hidapi", []),
("power", []),
("sensor", []),
("dialog", []),
("tray", []),
]
class SDLConan(ConanFile):
name = "sdl-infinipaint"
description = "A cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware"
license = "Zlib"
url = "https://github.com/conan-io/conan-center-index"
homepage = "https://www.libsdl.org"
topics = ("sdl3", "audio", "keyboard", "graphics", "opengl")
package_type = "library"
settings = "os", "arch", "compiler", "build_type"
options = {
**{
"shared": [True, False],
"fPIC": [True, False],
},
**{subsystem: [True, False] for subsystem, _ in _subsystems},
**{
"alsa": [True, False],
"pulseaudio": [True, False],
"sndio": [True, False],
"opengl": [True, False],
"opengles": [True, False],
"x11": [True, False],
"xcursor": [True, False],
"xdbe": [True, False],
"xinput": [True, False],
"xfixes": [True, False],
"xrandr": [True, False],
"xscrnsaver": [True, False],
"xshape": [True, False],
"xsync": [True, False],
"wayland": [True, False],
"vulkan": [True, False],
"metal": [True, False],
"directx": [True, False],
"libudev": [True, False],
"dbus": [True, False],
"libusb": [True, False],
"libiconv": [True, False],
"emscriptenPersistentPath": ["ANY"]
}
}
default_options = {
**{
"shared": False,
"fPIC": True,
},
**{subsystem: True for subsystem, _ in _subsystems},
**{
## Audio
# Linux only
"alsa": True,
"pulseaudio": True,
"sndio": True,
## Video
"opengl": True,
"opengles": True,
"x11": True,
"xcursor": True,
"xdbe": True,
"xinput": True,
"xfixes": True,
"xrandr": True,
"xscrnsaver": True,
"xshape": True,
"xsync": True,
"wayland": True,
"vulkan": True,
"metal": True,
"directx": True,
## Hidapi
"libusb": False,
## Other
"libudev": True,
"dbus": True,
"libiconv": False,
"emscriptenPersistentPath": ""
}
}
def config_options(self):
if self.settings.os == "Windows":
del self.options.fPIC
if not self._is_unix_sys:
del self.options.pulseaudio
del self.options.alsa
del self.options.sndio
del self.options.libudev
del self.options.dbus
del self.options.x11
del self.options.xcursor
del self.options.xdbe
del self.options.xinput
del self.options.xfixes
del self.options.xrandr
del self.options.xscrnsaver
del self.options.xshape
del self.options.xsync
del self.options.wayland
if not is_apple_os(self):
del self.options.metal
if self.settings.os != "Windows":
del self.options.directx
if self.settings.os != "Emscripten":
del self.options.emscriptenPersistentPath
def configure(self):
if self.options.shared:
self.options.rm_safe("fPIC")
if not (self.settings.os == "Android" and self.options.get_safe("hidapi")):
self.settings.rm_safe("compiler.libcxx")
self.settings.rm_safe("compiler.cppstd")
if not self.options.get_safe("audio"):
self.options.rm_safe("alsa")
self.options.rm_safe("pulseaudio")
self.options.rm_safe("sndio")
if not self.options.get_safe("video"):
self.options.rm_safe("opengl")
self.options.rm_safe("opengles")
self.options.rm_safe("x11")
self.options.rm_safe("wayland")
self.options.rm_safe("vulkan")
self.options.rm_safe("metal")
if not self.options.get_safe("x11"):
self.options.rm_safe("xcursor")
self.options.rm_safe("xdbe")
self.options.rm_safe("xinput")
self.options.rm_safe("xfixes")
self.options.rm_safe("xrandr")
self.options.rm_safe("xscrnsaver")
self.options.rm_safe("xshape")
self.options.rm_safe("xsync")
if not self.options.get_safe("hidapi") or self.settings.os in ["Android", "iOS", "tvOS", "visionOS", "watchOS"]:
# libusb is only an option if hidapi is enabled: https://github.com/libsdl-org/SDL/blob/db3a35e9bc3aa245dfbe67585b3f67d7b4b62845/cmake/sdlchecks.cmake#L1111
# libusb is not available on Android/iOS/tvOS/visionOS/watchOS https://github.com/libsdl-org/SDL/blob/db3a35e9bc3aa245dfbe67585b3f67d7b4b62845/CMakeLists.txt#L159-L160
self.options.rm_safe("libusb")
def validate(self):
# If any of the subsystems is enabled, then the corresponding dependencies must be enabled
for subsystem, dependencies in _subsystems:
if self.options.get_safe(subsystem):
for dependency in dependencies:
if not self.options.get_safe(dependency):
raise ConanInvalidConfiguration(f'-o="&:{subsystem}=True" subsystem requires -o="&:{dependency}=True"')
def validate_build(self):
if self.settings.os == "Android" and not self.conf.get("user.sdl:android", False):
raise ConanInvalidConfiguration("SDL builds on android require extra configuration on the user's side. "
"Set -c user.sdl:android=True if you understand it and want to build it")
def layout(self):
cmake_layout(self, src_folder="src")
def source(self):
get(self, **self.conan_data["sources"][self.version], strip_root=True)
# Prevent inspecting target properties for libusb to derive the name of the .so/.dll
# instead, just link the library normally, see
# https://github.com/libsdl-org/SDL/blob/96292a5b464258a2b926e0a3d72f8b98c2a81aa6/cmake/sdlchecks.cmake#L1107-L1113
replace_in_file(self, os.path.join(self.source_folder, "cmake", "sdlchecks.cmake"),
"target_get_dynamic_library(dynamic_libusb", "#target_get_dynamic_library(dynamic_libusb")
@property
def _is_unix_sys(self):
""" True for UNIX but not Macos/Android"""
return self.settings.os in ("Linux", "FreeBSD")
@property
def _supports_opengl(self):
return (self.options.get_safe("opengl")
and self.settings.os not in ("iOS", "visionOS", "tvOS", "watchOS"))
@property
def _supports_opengles(self):
return (self.options.get_safe("opengles")
and self.settings.os in ("Android", "iOS", "visionOS", "tvOS", "watchOS"))
@property
def _supports_dbus(self):
return self.options.get_safe("dbus") and self._is_unix_sys
def requirements(self):
if self.options.get_safe("libiconv"):
self.requires("libiconv/1.17")
if self.options.get_safe("libusb"):
self.requires("libusb/1.0.26")
if self._supports_opengl:
self.requires("opengl/system")
if self.options.get_safe("libudev"):
self.requires("libudev/system")
if self._supports_dbus:
self.requires("dbus/1.15.8")
if self.options.get_safe("pulseaudio"):
self.requires("pulseaudio/17.0")
if self.options.get_safe("alsa"):
self.requires("libalsa/[>=1.2 <1.3]")
if self.options.get_safe("sndio"):
self.requires("libsndio/1.9.0")
if self.options.get_safe("wayland"):
self.requires("xkbcommon/1.6.0")
# Version comes from xkbcommon
self.requires("wayland/[^1.22]")
self.requires("egl/system")
if self.options.get_safe("x11"):
self.requires("xorg/system")
def build_requirements(self):
self.tool_requires("cmake/[>=3.24 <4]")
if self._is_unix_sys and not self.conf.get("tools.gnu:pkg_config", check_type=str):
self.tool_requires("pkgconf/[>=2.2 <3]")
if self.options.get_safe("wayland"):
self.tool_requires("wayland/<host_version>") # Provides wayland-scanner
def generate(self):
tc = CMakeToolchain(self)
tc.cache_variables["SDL_SHARED"] = self.options.shared
tc.cache_variables["SDL_STATIC"] = not self.options.shared
tc.cache_variables["SDL_TEST_LIBRARY"] = False
tc.cache_variables["SDL_TESTS"] = False
tc.cache_variables["SDL_EXAMPLES"] = False
tc.cache_variables["SDL_INSTALL_EXAMPLES"] = False
tc.cache_variables["CMAKE_TRY_COMPILE_CONFIGURATION"] = str(self.settings.build_type)
tc.cache_variables["SDL_SYSTEM_ICONV_DEFAULT"] = True
tc.cache_variables["SDL_LIBICONV"] = self.options.libiconv
tc.cache_variables["SDL_JACK"] = False # Jack is not available in CCI
for subsystem in _subsystems:
tc.cache_variables[f"SDL_{subsystem[0].upper()}"] = self.options.get_safe(subsystem[0])
tc.cache_variables["SDL_OPENGL"] = bool(self._supports_opengl)
tc.cache_variables["SDL_OPENGLES"] = bool(self._supports_opengles)
if self.options.get_safe("emscriptenPersistentPath") and self.options.get_safe("emscriptenPersistentPath") != "":
tc.cache_variables["SDL_EMSCRIPTEN_PERSISTENT_PATH"] = self.options.get_safe("emscriptenPersistentPath")
if self.options.hidapi:
tc.cache_variables["SDL_HIDAPI_LIBUSB"] = self.options.get_safe("libusb")
# Prevent loading shared libusb during runtime
# This just means it will be linked traditionally, even when libusb is shared
# See https://github.com/libsdl-org/SDL/blob/96292a5b464258a2b926e0a3d72f8b98c2a81aa6/cmake/sdlchecks.cmake#L1107-L1113
tc.cache_variables["SDL_HIDAPI_LIBUSB_SHARED"] = False
tc.variables["SDL_VULKAN"] = self.options.get_safe("vulkan")
tc.variables["SDL_METAL"] = self.options.get_safe("metal")
tc.variables["SDL_DIRECTX"] = self.options.get_safe("directx")
if self.options.get_safe("pulseaudio"):
tc.cache_variables["SDL_PULSEAUDIO"] = True
tc.cache_variables["SDL_PULSEAUDIO_SHARED"] = self.dependencies["pulseaudio"].options.get_safe("shared", True)
if self.options.get_safe("alsa"):
tc.cache_variables["SDL_ALSA"] = True
tc.cache_variables["SDL_ALSA_SHARED"] = self.dependencies["libalsa"].options.shared
if self.options.get_safe("sndio"):
tc.cache_variables["SDL_SNDIO"] = True
tc.cache_variables["SDL_SNDIO_SHARED"] = True # sndio is always shared
tc.cache_variables["SDL_LIBUDEV"] = self.options.get_safe("libudev", False)
# X11 and wayland configuration
with_x11 = self.options.get_safe("x11", False)
tc.cache_variables["SDL_X11"] = with_x11
tc.cache_variables["SDL_X11_SHARED"] = True
if with_x11:
# See https://github.com/bincrafters/community/issues/696
tc.cache_variables["SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS"] = 1
tc.cache_variables["SDL_X11_XCURSOR"] = self.options.xcursor
tc.cache_variables["SDL_X11_XDBE"] = self.options.xdbe
tc.cache_variables["SDL_X11_XINPUT"] = self.options.xinput
tc.cache_variables["SDL_X11_XFIXES"] = self.options.xfixes
tc.cache_variables["SDL_X11_XRANDR"] = self.options.xrandr
tc.cache_variables["SDL_X11_XSCRNSAVER"] = self.options.xscrnsaver
tc.cache_variables["SDL_X11_XSHAPE"] = self.options.xshape
tc.cache_variables["SDL_X11_XSYNC"] = self.options.xsync
with_wayland = self.options.get_safe("wayland", False)
tc.cache_variables["SDL_WAYLAND"] = with_wayland
if with_wayland:
tc.cache_variables["SDL_WAYLAND_SHARED"] = self.dependencies["wayland"].options.shared
if not with_x11 and not with_wayland:
# Disable windowing support:
# https://github.com/libsdl-org/SDL/blob/main/docs/README-cmake.md#cmake-fails-to-build-without-x11-or-wayland-support
tc.cache_variables["SDL_UNIX_CONSOLE_BUILD"] = True
tc.generate()
deps = CMakeDeps(self)
deps.set_property("libusb", "cmake_target_name", "LibUSB::LibUSB")
deps.set_property("libusb", "cmake_file_name", "LibUSB")
deps.generate()
pcdeps = PkgConfigDeps(self)
pcdeps.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
copy(self, "LICENSE.txt", self.source_folder, os.path.join(self.package_folder, "licenses"))
cmake = CMake(self)
cmake.install()
rmdir(self, os.path.join(self.package_folder, "cmake"))
rmdir(self, os.path.join(self.package_folder, "lib", "cmake"))
rmdir(self, os.path.join(self.package_folder, "lib", "pkgconfig"))
@property
def _is_clang_cl(self):
return self.settings.os == "Windows" and self.settings.compiler == "clang" and \
self.settings.compiler.get_safe("runtime")
def package_info(self):
self.cpp_info.set_property("cmake_file_name", "SDL3")
sdl_lib_name = "SDL3"
if (is_msvc(self) or self._is_clang_cl) and not self.options.shared:
sdl_lib_name = f"{sdl_lib_name}-static"
self.cpp_info.components["sdl3"].libs = [sdl_lib_name]
self.cpp_info.components["sdl3"].set_property("cmake_target_name", "SDL3::SDL3")
self.cpp_info.components["sdl3"].set_property("cmake_target_aliases", [
"SDL3::SDL3-shared" if self.options.shared else "SDL3::SDL3-static",
])
# Target that only contains the include directories
self.cpp_info.components["headers"].set_property("cmake_target_name", "SDL3::Headers")
self.cpp_info.components["headers"].libdirs = []
self.cpp_info.components["sdl3"].requires.append("headers")
if self.settings.os in ("Linux", "FreeBSD", "Macos"):
self.cpp_info.components["sdl3"].system_libs.append("pthread")
if self.options.get_safe("libiconv"):
self.cpp_info.components["sdl3"].requires.append("libiconv::libiconv")
if self.options.get_safe("libudev"):
self.cpp_info.components["sdl3"].requires.append("libudev::libudev")
if self.options.get_safe("libusb"):
self.cpp_info.components["sdl3"].requires.append("libusb::libusb")
if self.options.get_safe("dbus"):
self.cpp_info.components["sdl3"].requires.append("dbus::dbus")
if self.options.get_safe("opengl"):
self.cpp_info.components["sdl3"].requires.append("opengl::opengl")
if self.options.get_safe("wayland"):
self.cpp_info.components["sdl3"].requires.extend(["wayland::wayland", "xkbcommon::xkbcommon", "egl::egl"])
if self.options.get_safe("x11"):
self.cpp_info.components["sdl3"].requires.extend(["xorg::x11", "xorg::xext"])
# xdbe, xshape and xsync are covered by x11 and xext
if self.options.xcursor:
self.cpp_info.components["sdl3"].requires.append("xorg::xcursor")
if self.options.xinput:
self.cpp_info.components["sdl3"].requires.append("xorg::xi")
if self.options.xfixes:
self.cpp_info.components["sdl3"].requires.append("xorg::xfixes")
if self.options.xrandr:
self.cpp_info.components["sdl3"].requires.append("xorg::xrandr")
if self.options.xscrnsaver:
self.cpp_info.components["sdl3"].requires.append("xorg::xscrnsaver")
if self.options.get_safe("audio"):
if self.options.get_safe("alsa"):
self.cpp_info.components["sdl3"].requires.append("libalsa::libalsa")
if self.options.get_safe("pulseaudio"):
self.cpp_info.components["sdl3"].requires.append("pulseaudio::pulseaudio")
if self.options.get_safe("sndio"):
self.cpp_info.components["sdl3"].requires.append("libsndio::libsndio")
if self.settings.os == "Android":
if self.options.get_safe("video"):
self.cpp_info.components["sdl3"].system_libs.extend(["dl", "log", "android"])
if self.options.get_safe("opengles"):
self.cpp_info.components["sdl3"].system_libs.extend(["GLESv1_CM", "GLESv2"])
if self.options.get_safe("audio"):
self.cpp_info.components["sdl3"].system_libs.append("OpenSLES")
# TODO(conan client): when shared, SDL do not need to link against its dependencies but conan will complain about it
if is_apple_os(self) and not self.options.shared:
self.cpp_info.components["sdl3"].frameworks = ["CoreVideo", "Foundation"]
if self.settings.os == "Macos":
self.cpp_info.components["sdl3"].frameworks.extend(["Cocoa", "Carbon"])
if self.options.get_safe("audio"):
self.cpp_info.components["sdl3"].frameworks.extend(["CoreAudio", "AudioToolbox", "AVFoundation"])
if self.options.get_safe("video"):
if self.settings.os in ("iOS", "tvOS", "visionOS", "watchOS"):
self.cpp_info.components["sdl3"].frameworks.extend(["CoreGraphics", "QuartzCore", "UIKit"])
else:
self.cpp_info.components["sdl3"].frameworks.append("UniformTypeIdentifiers")
if self.options.get_safe("camera"):
if self.settings.os in ("Macos", "iOS"):
self.cpp_info.components["sdl3"].frameworks.append("CoreMedia")
self.cpp_info.components["sdl3"].frameworks.append("AVFoundation")
if self.options.get_safe("joystick"):
self.cpp_info.components["sdl3"].frameworks.append("GameController")
self.cpp_info.components["sdl3"].sharedlinkflags.append("-Wl,-weak_framework,CoreHaptics")
self.cpp_info.components["sdl3"].exelinkflags.append("-Wl,-weak_framework,CoreHaptics")
if self.settings.os == "Macos":
# Mind that ForceFeedback is also added in haptic system, but haptic depends on joystick
self.cpp_info.components["sdl3"].frameworks.extend(["ForceFeedback", "IOKit"])
elif self.settings.os in ("iOS", "visionOS", "watchOS"):
self.cpp_info.components["sdl3"].frameworks.append("CoreMotion")
if self.options.get_safe("hidapi") and self.settings.os in ("iOS", "tvOS"):
self.cpp_info.components["sdl3"].frameworks.append("CoreBluetooth")
if self.options.get_safe("power") and self.settings.os == "Macos":
self.cpp_info.components["sdl3"].frameworks.append("IOKit")
if self.options.get_safe("opengles") and self.settings.os in ("iOS", "tvOS", "visionOS", "watchOS"):
self.cpp_info.components["sdl3"].frameworks.append("OpenGLES")
if self.options.get_safe("metal"):
self.cpp_info.components["sdl3"].frameworks.extend(["Metal", "QuartzCore"])
# Windows links with all libs by default
if self.settings.os == "Windows":
self.cpp_info.components["sdl3"].system_libs.extend(
[
"kernel32",
"user32",
"gdi32",
"winmm",
"imm32",
"ole32",
"oleaut32",
"version",
"uuid",
"advapi32",
"setupapi",
"shell32",
]
)

View file

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.15)
project(test_package)
find_package(SDL3 REQUIRED CONFIG)
add_executable(${PROJECT_NAME} test_package.cpp)
target_link_libraries(${PROJECT_NAME} SDL3::SDL3)

View file

@ -0,0 +1,31 @@
from conan import ConanFile
from conan.tools.build import can_run
from conan.tools.cmake import cmake_layout, CMake, CMakeToolchain
import os
class TestPackageConan(ConanFile):
settings = "os", "arch", "compiler", "build_type"
generators = "CMakeDeps", "VirtualRunEnv"
test_type = "explicit"
def requirements(self):
self.requires(self.tested_reference_str)
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def test(self):
if can_run(self):
bin_path = os.path.join(self.cpp.build.bindir, "test_package")
self.run(bin_path, env="conanrun")

View file

@ -0,0 +1,8 @@
#include <SDL3/SDL.h>
#include <iostream>
int main(int argc, char* args[]) {
auto version = SDL_GetVersion();
std::cout << "SDL version " << version << std::endl;
return 0;
}

View file

@ -0,0 +1,7 @@
sources:
"2.32.10":
url: "https://www.libsdl.org/release/SDL2-2.32.10.tar.gz"
sha256: "5f5993c530f084535c65a6879e9b26ad441169b3e25d789d83287040a9ca5165"
patches:
"2.32.10":
- patch_file: "patches/2.32.10-0001-alsa-fix-signature-of-snd_pcm_info_free.patch"

438
conan/sdl/all/conanfile.py Normal file
View file

@ -0,0 +1,438 @@
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.apple import is_apple_os
from conan.tools.files import (
get, replace_in_file, rm, rmdir, copy,
apply_conandata_patches, export_conandata_patches
)
from conan.tools.microsoft import is_msvc
from conan.tools.scm import Version
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
from conan.tools.env import Environment
import os
required_conan_version = ">=2.1"
class SDLConan(ConanFile):
name = "sdl"
description = "Access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL, Direct3D and Vulkan"
license = "Zlib"
url = "https://github.com/conan-io/conan-center-index"
homepage = "https://www.libsdl.org"
topics = ("sdl2", "audio", "keyboard", "graphics", "opengl")
package_type = "library"
settings = "os", "arch", "compiler", "build_type"
options = {
"shared": [True, False],
"fPIC": [True, False],
"directx": [True, False],
"alsa": [True, False],
"jack": [True, False],
"pulse": [True, False],
"sndio": [True, False],
"nas": [True, False],
"esd": [True, False],
"arts": [True, False],
"x11": [True, False],
"xcursor": [True, False],
"xinerama": [True, False],
"xinput": [True, False],
"xrandr": [True, False],
"xscrnsaver": [True, False],
"xshape": [True, False],
"xvm": [True, False],
"wayland": [True, False],
"directfb": [True, False],
"iconv": [True, False],
"video_rpi": [True, False],
"sdl2main": [True, False],
"opengl": [True, False],
"opengles": [True, False],
"vulkan": [True, False],
"libunwind": [True, False],
"hidapi": [True, False],
}
default_options = {
"shared": False,
"fPIC": True,
"directx": True,
"alsa": True,
"jack": False,
"pulse": True,
"sndio": False,
"nas": False,
"esd": False,
"arts": False,
"x11": True,
"xcursor": True,
"xinerama": True,
"xinput": True,
"xrandr": True,
"xscrnsaver": True,
"xshape": True,
"xvm": True,
"wayland": True,
"directfb": False,
"video_rpi": False,
"sdl2main": True,
"opengl": True,
"opengles": True,
"vulkan": True,
"libunwind": True,
"hidapi": True,
}
generators = "CMakeDeps", "PkgConfigDeps", "VirtualBuildEnv"
@property
def _is_clang_cl(self):
return self.settings.os == "Windows" and self.settings.compiler == "clang" and \
self.settings.compiler.get_safe("runtime")
def layout(self):
cmake_layout(self, src_folder="src")
def export_sources(self):
export_conandata_patches(self)
def generate(self):
self.define_toolchain()
lib_paths = [lib for _, dep in self.dependencies.items() for lib in dep.cpp_info.libdirs]
env = Environment()
env.define_path("LIBRARY_PATH", os.pathsep.join(lib_paths))
env = env.vars(self, scope="build")
env.save_script("sdl_env")
def config_options(self):
# Don't depend on iconv on Apple by default
# SDL2 depends on many system freamworks,
# which depend on the system-provided iconv
# and can conflict with the Conan provided one
self.options.iconv = not is_apple_os(self)
if self.settings.os == "Windows":
del self.options.fPIC
if (is_msvc(self) or self._is_clang_cl):
del self.options.iconv
if self.settings.os != "Linux":
del self.options.alsa
del self.options.jack
del self.options.pulse
del self.options.sndio
del self.options.nas
del self.options.esd
del self.options.arts
del self.options.x11
del self.options.xcursor
del self.options.xinerama
del self.options.xinput
del self.options.xrandr
del self.options.xscrnsaver
del self.options.xshape
del self.options.xvm
del self.options.wayland
del self.options.directfb
del self.options.video_rpi
del self.options.libunwind
if self.settings.os != "Windows":
del self.options.directx
def configure(self):
if self.options.shared:
self.options.rm_safe("fPIC")
# TODO: C++ is also required for WinRT and Haiku
if not (self.settings.os == "Android" and self.options.hidapi):
self.settings.rm_safe("compiler.libcxx")
self.settings.rm_safe("compiler.cppstd")
def requirements(self):
if self.options.get_safe("iconv", False):
self.requires("libiconv/1.17")
if self.settings.os == "Linux":
if self.options.alsa:
self.requires("libalsa/1.2.10")
if self.options.pulse:
self.requires("pulseaudio/14.2")
if self.options.opengl:
self.requires("opengl/system")
if self.options.nas:
self.requires("nas/1.9.5")
if self.options.wayland:
self.requires("wayland/1.22.0")
self.requires("xkbcommon/1.6.0")
self.requires("egl/system")
if self.options.libunwind:
self.requires("libunwind/1.8.0")
def validate(self):
# SDL>=2.0.18 requires xcode 12 or higher because it uses CoreHaptics.
if is_apple_os(self) and Version(self.settings.compiler.version) < "12":
raise ConanInvalidConfiguration("{}/{} requires xcode 12 or higher".format(self.name, self.version))
if self.settings.os == "Linux":
if self.options.sndio:
raise ConanInvalidConfiguration("Package for 'sndio' is not available (yet)")
if self.options.jack:
raise ConanInvalidConfiguration("Package for 'jack' is not available (yet)")
if self.options.esd:
raise ConanInvalidConfiguration("Package for 'esd' is not available (yet)")
if self.options.directfb:
raise ConanInvalidConfiguration("Package for 'directfb' is not available (yet)")
def build_requirements(self):
self.tool_requires("cmake/[>3.27 <4]")
if self.settings.os == "Linux" and not self.conf.get("tools.gnu:pkg_config", check_type=str):
self.tool_requires("pkgconf/2.1.0")
if hasattr(self, "settings_build") and self.options.get_safe("wayland"):
self.build_requires("wayland/1.22.0") # Provides wayland-scanner
def source(self):
get(self, **self.conan_data["sources"][self.version], strip_root=True)
apply_conandata_patches(self)
def _patch_sources(self):
# Ensure to find wayland-scanner from wayland recipe in build requirements (or requirements if 1 profile)
if self.options.get_safe("wayland"):
replace_in_file(self,
os.path.join(self.source_folder, "cmake", "sdlchecks.cmake"),
"find_program(WAYLAND_SCANNER NAMES wayland-scanner REQUIRED)",
'find_program(WAYLAND_SCANNER NAMES wayland-scanner REQUIRED PATHS "${WAYLAND_BIN_DIR}" NO_DEFAULT_PATH)',
)
if not self.options.shared:
replace_in_file(self, os.path.join(self.source_folder, "CMakeLists.txt"), "list(APPEND targets SDL2-static)", "set(targets SDL2-static)")
def define_toolchain(self):
tc = CMakeToolchain(self)
if self.settings.os == "Linux" and self.settings.compiler == "gcc" and Version(self.settings.compiler.version) < 5.0:
tc.preprocessor_definitions["GBM_BO_USE_CURSOR"] = 2
tc.variables["SDL2_DISABLE_INSTALL"] = False # SDL2_* options will get renamed to SDL_ options in the next SDL release
if is_apple_os(self):
tc.variables["CMAKE_OSX_ARCHITECTURES"] = {
"armv8": "arm64",
}.get(str(self.settings.arch), str(self.settings.arch))
cmake_required_includes = [] # List of directories used by CheckIncludeFile (https://cmake.org/cmake/help/latest/module/CheckIncludeFile.html)
cmake_extra_ldflags = []
cmake_extra_libs = []
if self.settings.os != "Windows" and not self.options.shared:
tc.variables["SDL_STATIC_PIC"] = self.options.fPIC
if (is_msvc(self) or self._is_clang_cl) and not self.options.shared:
tc.variables["HAVE_LIBC"] = True
tc.variables["SDL_SHARED"] = self.options.shared
tc.variables["SDL_STATIC"] = not self.options.shared
tc.variables["SDL_TEST"] = False
tc.variables["SDL_OPENGL"] = self.options.opengl
tc.variables["SDL_OPENGLES"] = self.options.opengles
tc.variables["SDL_VULKAN"] = self.options.vulkan
tc.variables["SDL_HIDAPI"] = self.options.hidapi
if self.settings.os == "Linux":
# See https://github.com/bincrafters/community/issues/696
tc.variables["SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS"] = 1
tc.variables["SDL_ALSA"] = self.options.alsa
if self.options.alsa:
tc.variables["SDL_ALSA_SHARED"] = self.dependencies["libalsa"].options.shared
tc.variables["HAVE_ASOUNDLIB_H"] = True
tc.variables["HAVE_LIBASOUND"] = True
tc.variables["SDL_JACK"] = self.options.jack
if self.options.jack:
tc.variables["SDL_JACK_SHARED"] = self.options["jack"].shared
tc.variables["SDL_ESD"] = self.options.esd
if self.options.esd:
tc.variables["SDL_ESD_SHARED"] = self.options["esd"].shared
tc.variables["SDL_PULSEAUDIO"] = self.options.pulse
if self.options.pulse:
tc.variables["SDL_PULSEAUDIO_SHARED"] = self.dependencies["pulseaudio"].options.shared
for component in self.dependencies["pulseaudio"].cpp_info.components:
if self.dependencies["pulseaudio"].cpp_info.components[component].libs:
cmake_extra_libs += self.dependencies["pulseaudio"].cpp_info.components[component].libs
cmake_extra_ldflags += ["-L{}".format(it) for it in self.dependencies["pulseaudio"].cpp_info.components[component].libdirs]
cmake_extra_ldflags += ["-lxcb", "-lrt"] # FIXME: SDL sources doesn't take into account transitive dependencies
tc.variables["SDL_SNDIO"] = self.options.sndio
if self.options.sndio:
tc.variables["SDL_SNDIO_SHARED"] = self.options["sndio"].shared
tc.variables["SDL_NAS"] = self.options.nas
if self.options.nas:
cmake_extra_ldflags += ["-lXau"] # FIXME: SDL sources doesn't take into account transitive dependencies
cmake_required_includes += self.dependencies["nas"].cpp_info.includedirs
tc.variables["SDL_NAS_SHARED"] = self.dependencies["nas"].options.shared
tc.variables["SDL_X11"] = self.options.x11
if self.options.x11:
tc.variables["HAVE_XEXT_H"] = True
tc.variables["SDL_X11_XCURSOR"] = self.options.xcursor
if self.options.xcursor:
tc.variables["HAVE_XCURSOR_H"] = True
tc.variables["SDL_X11_XINERAMA"] = self.options.xinerama
if self.options.xinerama:
tc.variables["HAVE_XINERAMA_H"] = True
tc.variables["SDL_X11_XINPUT"] = self.options.xinput
if self.options.xinput:
tc.variables["HAVE_XINPUT_H"] = True
tc.variables["SDL_X11_XRANDR"] = self.options.xrandr
if self.options.xrandr:
tc.variables["HAVE_XRANDR_H"] = True
tc.variables["SDL_X11_XSCRNSAVER"] = self.options.xscrnsaver
if self.options.xscrnsaver:
tc.variables["HAVE_XSS_H"] = True
tc.variables["SDL_X11_XSHAPE"] = self.options.xshape
if self.options.xshape:
tc.variables["HAVE_XSHAPE_H"] = True
tc.variables["SDL_X11_XVM"] = self.options.xvm
if self.options.xvm:
tc.variables["HAVE_XF86VM_H"] = True
tc.variables["SDL_WAYLAND"] = self.options.wayland
if self.options.wayland:
# FIXME: Otherwise 2.0.16 links with system wayland (from egl/system requirement)
cmake_extra_ldflags += ["-L{}".format(it) for it in self.dependencies["wayland"].cpp_info.libdirs]
tc.variables["SDL_WAYLAND_SHARED"] = self.dependencies["wayland"].options.shared
wayland = self.dependencies["wayland"] if not hasattr(self, "settings_build") else self.dependencies.build["wayland"]
wayland_bin_dir = wayland.cpp_info.bindirs[0] # for wayland scanner
tc.variables["WAYLAND_BIN_DIR"] = wayland_bin_dir
tc.variables["SDL_DIRECTFB"] = self.options.directfb
tc.variables["SDL_RPI"] = self.options.video_rpi
tc.variables["HAVE_LIBUNWIND_H"] = self.options.libunwind
elif self.settings.os == "Windows":
tc.variables["SDL_DIRECTX"] = self.options.directx
tc.variables["SDL2_DISABLE_SDL2MAIN"] = not self.options.sdl2main
tc.variables["SDL_LIBICONV"] = self.options.get_safe("iconv", False)
tc.variables["SDL_SYSTEM_ICONV"] = False
# Add extra information collected from the deps
tc.variables["EXTRA_LDFLAGS"] = ";".join(cmake_extra_ldflags)
tc.variables["CMAKE_REQUIRED_INCLUDES"] = ";".join(cmake_required_includes)
cmake_extra_cflags = ["-I{}".format(path) for _, dep in self.dependencies.items() for path in dep.cpp_info.includedirs]
tc.variables["EXTRA_CFLAGS"] = ";".join(cmake_extra_cflags).replace(os.sep, '/')
tc.variables["EXTRA_LIBS"] = ";".join(cmake_extra_libs)
tc.cache_variables["CMAKE_POLICY_DEFAULT_CMP0077"] = "NEW"
tc.generate()
def build(self):
self._patch_sources()
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()
copy(self, pattern="LICENSE.txt", src=os.path.join(self.source_folder), dst=os.path.join(self.package_folder, "licenses"))
rm(self, "sdl2-config", os.path.join(self.package_folder, "bin"))
rm(self, "*.pdb", os.path.join(self.package_folder, "lib"))
rm(self, "*.pdb", os.path.join(self.package_folder, "bin"))
rmdir(self, os.path.join(self.package_folder, "cmake"))
rmdir(self, os.path.join(self.package_folder, "lib", "cmake"))
rmdir(self, os.path.join(self.package_folder, "lib", "pkgconfig"))
rmdir(self, os.path.join(self.package_folder, "libdata"))
rmdir(self, os.path.join(self.package_folder, "share"))
if self.settings.os == "Android":
copy(self, pattern="*",
src=os.path.join(self.source_folder, "android-project", "app", "src", "main", "java"),
dst=os.path.join(self.package_folder, "share", "java", "SDL2"))
def package_info(self):
self.cpp_info.set_property("cmake_file_name", "SDL2")
postfix = "d" if self.settings.os != "Android" and self.settings.build_type == "Debug" else ""
# SDL2
lib_postfix = postfix
if (is_msvc(self) or self._is_clang_cl) and not self.options.shared:
lib_postfix = "-static" + postfix
self.cpp_info.components["libsdl2"].set_property("cmake_target_name", "SDL2::SDL2")
if not self.options.shared:
self.cpp_info.components["libsdl2"].set_property("cmake_target_aliases", ["SDL2::SDL2-static"])
self.cpp_info.components["libsdl2"].set_property("pkg_config_name", "sdl2")
self.cpp_info.components["libsdl2"].includedirs.append(os.path.join("include", "SDL2"))
self.cpp_info.components["libsdl2"].libs = ["SDL2" + lib_postfix]
if self.options.get_safe("iconv", False):
self.cpp_info.components["libsdl2"].requires.append("libiconv::libiconv")
if self.settings.os == "Linux":
self.cpp_info.components["libsdl2"].system_libs = ["dl", "rt", "pthread"]
if self.options.alsa:
self.cpp_info.components["libsdl2"].requires.append("libalsa::libalsa")
if self.options.pulse:
self.cpp_info.components["libsdl2"].requires.append("pulseaudio::pulseaudio")
if self.options.opengl:
self.cpp_info.components["libsdl2"].requires.append("opengl::opengl")
if self.options.jack:
self.cpp_info.components["libsdl2"].requires.append("jack::jack")
if self.options.sndio:
self.cpp_info.components["libsdl2"].requires.append("sndio::sndio")
if self.options.nas:
self.cpp_info.components["libsdl2"].requires.append("nas::nas")
if self.options.esd:
self.cpp_info.components["libsdl2"].requires.append("esd::esd")
if self.options.directfb:
self.cpp_info.components["libsdl2"].requires.append("directfb::directfb")
if self.options.video_rpi:
self.cpp_info.components["libsdl2"].libs.append("bcm_host")
self.cpp_info.components["libsdl2"].includedirs.extend([
"/opt/vc/include",
"/opt/vc/include/interface/vcos/pthreads",
"/opt/vc/include/interface/vmcs_host/linux"
])
self.cpp_info.components["libsdl2"].libdirs.append("/opt/vc/lib")
self.cpp_info.components["libsdl2"].sharedlinkflags.append("-Wl,-rpath,/opt/vc/lib")
self.cpp_info.components["libsdl2"].exelinkflags.append("-Wl,-rpath,/opt/vc/lib")
if self.options.wayland:
self.cpp_info.components["libsdl2"].requires.append("wayland::wayland")
self.cpp_info.components["libsdl2"].requires.append("xkbcommon::xkbcommon")
self.cpp_info.components["libsdl2"].requires.append("egl::egl")
if self.options.libunwind:
self.cpp_info.components["libsdl2"].requires.append("libunwind::libunwind")
elif is_apple_os(self) and not self.options.shared:
self.cpp_info.components["libsdl2"].frameworks = [
"CoreVideo", "CoreAudio", "AudioToolbox",
"AVFoundation", "Foundation", "QuartzCore",
]
if self.settings.os == "Macos":
self.cpp_info.components["libsdl2"].frameworks.extend([
"Cocoa",
"Carbon",
"IOKit",
"ForceFeedback",
"CoreFoundation",
"CoreServices",
"AppKit"
])
self.cpp_info.components["libsdl2"].frameworks.append("GameController")
elif self.settings.os in ["iOS", "tvOS", "watchOS"]:
self.cpp_info.components["libsdl2"].frameworks.extend([
"UIKit", "OpenGLES", "GameController", "CoreMotion",
"CoreGraphics", "CoreBluetooth",
])
self.cpp_info.components["libsdl2"].frameworks.append("Metal")
self.cpp_info.components["libsdl2"].sharedlinkflags.append("-Wl,-weak_framework,CoreHaptics")
self.cpp_info.components["libsdl2"].exelinkflags.append("-Wl,-weak_framework,CoreHaptics")
elif self.settings.os == "Windows":
self.cpp_info.components["libsdl2"].system_libs = ["user32", "gdi32", "winmm", "imm32", "ole32", "oleaut32", "version", "uuid", "advapi32", "setupapi", "shell32"]
if self.settings.compiler == "gcc":
self.cpp_info.components["libsdl2"].system_libs.append("mingw32")
elif self.settings.os == "Android" and not self.options.shared:
self.cpp_info.components["libsdl2"].system_libs.extend(["android", "dl", "log"])
if self.options.opengles:
self.cpp_info.components["libsdl2"].system_libs.extend(["GLESv1_CM", "GLESv2"])
self.cpp_info.components["libsdl2"].system_libs.append("OpenSLES")
# SDL2main
if self.options.sdl2main:
self.cpp_info.components["sdl2main"].set_property("cmake_target_name", "SDL2::SDL2main")
self.cpp_info.components["sdl2main"].libs = ["SDL2main" + postfix]
self.cpp_info.components["sdl2main"].requires = ["libsdl2"]
# Workaround to avoid unwanted sdl::sdl target in CMakeDeps generator
self.cpp_info.set_property(
"cmake_target_name",
"SDL2::{}".format("SDL2main" if self.options.sdl2main else "SDL2"),
)

View file

@ -0,0 +1,25 @@
From 29c3e9ebe636256c2409c196b184ea900967c27a Mon Sep 17 00:00:00 2001
Date: Tue, 2 Sep 2025 17:02:28 +0300
Subject: [PATCH] alsa: fix signature of snd_pcm_info_free
Reference issue: https://github.com/libsdl-org/SDL/issues/13845.
---
src/audio/alsa/SDL_alsa_audio.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c
index 8f3a49c81..7af711411 100644
--- a/src/audio/alsa/SDL_alsa_audio.c
+++ b/src/audio/alsa/SDL_alsa_audio.c
@@ -88,7 +88,7 @@ static const char *(*ALSA_snd_pcm_info_get_name)(const snd_pcm_info_t *);
static int (*ALSA_snd_pcm_info_get_card)(const snd_pcm_info_t *);
static int (*ALSA_snd_card_get_name)(int, char **);
static int (*ALSA_snd_pcm_info_malloc)(snd_pcm_info_t **);
-static int (*ALSA_snd_pcm_info_free)(snd_pcm_info_t *);
+static void (*ALSA_snd_pcm_info_free)(snd_pcm_info_t *);
#ifdef SND_CHMAP_API_VERSION
static snd_pcm_chmap_t *(*ALSA_snd_pcm_get_chmap)(snd_pcm_t *);
static int (*ALSA_snd_pcm_chmap_print)(const snd_pcm_chmap_t *map, size_t maxlen, char *buf);
--
2.39.5

View file

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.15)
project(test_package)
find_package(SDL2 REQUIRED CONFIG)
add_executable(${PROJECT_NAME} test_package.cpp)
target_link_libraries(${PROJECT_NAME} SDL2::SDL2main)
if(TARGET SDL2::SDL2-static)
target_link_libraries(${PROJECT_NAME} SDL2::SDL2-static)
else()
target_link_libraries(${PROJECT_NAME} SDL2::SDL2)
endif()

View file

@ -0,0 +1,31 @@
from conan import ConanFile
from conan.tools.build import can_run
from conan.tools.cmake import cmake_layout, CMake, CMakeToolchain
import os
class TestPackageConan(ConanFile):
settings = "os", "arch", "compiler", "build_type"
generators = "CMakeDeps", "VirtualRunEnv"
test_type = "explicit"
def requirements(self):
self.requires(self.tested_reference_str)
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def test(self):
if can_run(self):
bin_path = os.path.join(self.cpp.build.bindir, "test_package")
self.run(bin_path, env="conanrun")

View file

@ -0,0 +1,9 @@
#include <SDL.h>
#include <iostream>
int main(int argc, char* args[]) {
SDL_version v;
SDL_GetVersion(&v);
std::cout << "SDL version " << int(v.major) << "." << int(v.minor) << "." << int(v.patch) << std::endl;
return 0;
}

5
conan/sdl/config.yml Normal file
View file

@ -0,0 +1,5 @@
versions:
"3.4.0":
folder: 3.x
"2.32.10":
folder: all

View file

@ -92,7 +92,7 @@ class CompressorRecipe(ConanFile):
if self.settings.os == "Linux":
self.requires("fontconfig/2.17.1")
self.requires("egl/system")
self.requires("sdl/3.4.0", options = {
self.requires("sdl-infinipaint/3.4.4", options = {
"wayland": False,
"x11": True,
"pulseaudio": False,
@ -101,8 +101,12 @@ class CompressorRecipe(ConanFile):
"vulkan": False,
"opengles": False
})
elif self.settings.os == "Emscripten":
self.requires("sdl-infinipaint/3.4.4", options = {
"emscriptenPersistentPath": "/infinipaint"
})
elif self.settings.os != "Android":
self.requires("sdl/3.4.0")
self.requires("sdl-infinipaint/3.4.4")
if self.settings.os != "Emscripten" and self.settings.os != "Macos" and self.settings.os != "Android":
self.requires("hwloc/2.12.2", options = {

2
deps/SDL vendored

@ -1 +1 @@
Subproject commit 683181b47cfabd293e3ea409f838915b8297a4fd
Subproject commit 5848e584a1b606de26e3dbd1c7e4ecbc34f807a6

View file

@ -57,7 +57,7 @@ modules:
- -DSDL_X11=ON
sources:
- type: git
tag: release-3.4.0
tag: release-3.4.4
url: https://github.com/libsdl-org/SDL.git
- name: Skia
buildsystem: simple

View file

@ -193,6 +193,7 @@ void ClientData::draw_cursor(SkCanvas* canvas, const DrawData& drawData) const {
canvas->translate(cursorPos.x(), cursorPos.y());
SkPaint p(SkColor4f{cursorColor.x(), cursorColor.y(), cursorColor.z(), 0.2f});
p.setAntiAlias(drawData.skiaAA);
canvas->drawCircle(0.0f, 0.0f, 4.5f, p);
p.setStroke(true);
p.setStrokeWidth(1.0f);
@ -207,9 +208,11 @@ void ClientData::draw_cursor(SkCanvas* canvas, const DrawData& drawData) const {
Vector2f bounds{nextText, - metrics.fAscent + metrics.fDescent};
SkPaint p2(SkColor4f{cursorColor.x(), cursorColor.y(), cursorColor.z(), 0.5f});
p2.setAntiAlias(drawData.skiaAA);
canvas->drawSimpleText(displayName.c_str(), displayName.length(), SkTextEncoding::kUTF8, 6.0f, -metrics.fDescent, f, p2);
SkPaint p3(color_mul_alpha(drawData.main->g.gui.io.theme->backColor1, 0.5f));
p3.setAntiAlias(drawData.skiaAA);
canvas->drawRoundRect(SkRect::MakeXYWH(6.0f, -bounds.y(), bounds.x(), bounds.y()), 3.0f, 3.0f, p3);
canvas->restore();

View file

@ -14,7 +14,7 @@ struct ColorPickerItemsOptions {
template <typename T> void color_picker_items(GUIManager& gui, const char* id, T* val, const ColorPickerItemsOptions& options = {}) {
gui.new_id(id, [&] {
auto fullOnEdit = [&gui, oE = options.onEdit, val]() {
auto fullOnEdit = [&gui, oE = options.onEdit]() {
if(oE) oE();
gui.set_to_layout();
};

View file

@ -93,7 +93,7 @@ template <typename T> void input_scalar(GUIManager& gui, const char* id, T* val,
return minVal;
return std::clamp(toRet, minVal, maxVal);
};
d.toStr = [decimalPrecision = options.decimalPrecision, minVal, maxVal](const T& a) {
d.toStr = [decimalPrecision = options.decimalPrecision](const T& a) {
std::stringstream ss;
if(std::is_floating_point<T>()) {
ss.precision(decimalPrecision);

View file

@ -69,7 +69,7 @@ template <typename T> class ColorPicker : public Element {
ColorPicker(GUIManager& gui);
void layout(const Clay_ElementId& id, T* data, bool selectAlpha, const ColorPickerData& config);
virtual void update() override;
virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA);
virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) override;
void input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) override;
void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) override;
private:

View file

@ -33,7 +33,7 @@ template <typename T> class NumberSlider : public Element {
}
}
virtual void update() {
virtual void update() override {
smooth_two_way_animation_time(dd.holdAnimation, gui.io.deltaTime, dd.isHeld, HOLD_ANIMATION_TIME);
smooth_two_way_animation_time(dd.hoverAnimation, gui.io.deltaTime, mouseHovering, gui.io.theme->hoverExpandTime);
if(oldDD != dd) {

View file

@ -81,9 +81,7 @@ InputManager::InputManager(MainProgram& initMain):
});
emscripten_browser_clipboard::paste_event([](std::string&& pasteData, void* callbackData){
InputManager* inMan = (InputManager*)callbackData;
inMan->text.isNextPasteRich = true;
std::string pData = pasteData;
inMan->process_text_paste(pData);
inMan->process_text_paste(pasteData, true);
}, this);
#endif
@ -927,14 +925,22 @@ void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) {
void InputManager::call_paste(CustomEvents::PasteEvent::DataType type, const InputManagerCallPasteInfo& info) {
// Workaround for not being able to copy richtext to system clipboard, this should at least work within the application itself
#ifdef __EMSCRIPTEN__
if(type == PasteDataType::TEXT) {
switch(type) {
case CustomEvents::PasteEvent::DataType::TEXT: {
struct PasteData {
bool allowRichText;
InputManager* t;
};
static PasteData pasteData;
pasteData.allowRichText = info.allowRichText;
pasteData.t = this;
emscripten_browser_clipboard::paste_async([](std::string&& pasteData, void* callbackData){
InputManager* inMan = (InputManager*)callbackData;
std::string pData = pasteData;
inMan->process_text_paste(pData);
}, this);
PasteData* p = (PasteData*)callbackData;
p->t->process_text_paste(pasteData, p->allowRichText);
}, &pasteData);
break;
}
else {
case CustomEvents::PasteEvent::DataType::IMAGE: {
struct PasteData {
std::optional<Vector2f> pos;
InputManager* t;
@ -946,6 +952,8 @@ void InputManager::call_paste(CustomEvents::PasteEvent::DataType type, const Inp
PasteData* p = (PasteData*)callbackData;
p->t->process_image_paste(pasteData, p->pos);
}, &pasteData);
break;
}
}
#else
switch(type) {

View file

@ -111,17 +111,6 @@ void MainProgram::save_config() {
f.close();
}
conf.save_palettes();
#ifdef __EMSCRIPTEN__
EM_ASM(
FS.syncfs(false, (err) => {
if(err)
console.log("Error syncing IDBFS: ", err);
else
console.log("Synced to IDBFS");
});
);
#endif
}
void MainProgram::load_config() {
@ -174,7 +163,7 @@ void MainProgram::load_config() {
}
#ifdef __EMSCRIPTEN__
else
toolbar.viewWebVersionWelcome = true;
conf.viewWebVersionWelcome = true;
#endif
conf.load_palettes();
g.load_theme(conf.configPath, conf.themeCurrentlyLoaded);

View file

@ -183,8 +183,6 @@ void TextBox::process_mouse_left_button(Cursor& cur, const Vector2f& pos, int cl
if(clickCount || held) {
rebuild();
Cursor oldCursor = cur;
cur.pos = get_text_pos_closest_to_point(pos);
if(lastClicksCursorPos != cur.pos && clickCount) {
lastClicksAtCursorPos = 1;

View file

@ -0,0 +1 @@
#include "FileSelectScreen.hpp"

View file

@ -0,0 +1,7 @@
#pragma once
class FileSelectScreen {
public:
FileSelectScreen();
private:
};

View file

@ -551,7 +551,6 @@ void Toolbar::top_toolbar() {
#ifdef __EMSCRIPTEN__
emscripten_browser_file::upload("*", [](std::string const& fileName, std::string const& mimeType, std::string_view buffer, void* callbackData) {
if(!buffer.empty()) {
auto& main = *static_cast<MainProgram*>(callbackData);
CustomEvents::emit_event<CustomEvents::AddFileToCanvasEvent>({
.type = CustomEvents::AddFileToCanvasEvent::Type::BUFFER,
.name = fileName,
@ -1469,13 +1468,13 @@ void Toolbar::open_world_file(bool isClient, const std::string& netSource, const
emscripten_browser_file::upload("." + World::FILE_EXTENSION, [](std::string const& fileName, std::string const& mimeType, std::string_view buffer, void* callbackData) {
if(!buffer.empty()) {
UploadData* uD = (UploadData*)callbackData;
uD->main->new_tab({
CustomEvents::emit_event<CustomEvents::OpenInfiniPaintFileEvent>({
.isClient = uD->iC,
.filePathSource = std::filesystem::path(fileName),
.netSource = uD->nS,
.serverLocalID = uD->sLID,
.fileDataBuffer = buffer
}, true);
});
}
}, &uploadData);
#else

View file

@ -21,9 +21,6 @@
#include "WorldGrid.hpp"
#include <cereal/archives/portable_binary.hpp>
#include <chrono>
#include <fstream>
#include "FileHelpers.hpp"
#include "FontData.hpp"
#include <include/core/SkFontMetrics.h>
#include <Helpers/Networking/NetLibrary.hpp>
#include <Helpers/Logger.hpp>
@ -245,6 +242,8 @@ void World::update() {
void World::on_tab_out() {
rMan.clear_display_cache();
if(!clientStillConnecting)
drawProg.on_tab_out();
}
void World::input_add_file_to_canvas_callback(const CustomEvents::AddFileToCanvasEvent& addFile) {

View file

@ -151,11 +151,6 @@ struct MainStruct {
std::chrono::steady_clock::duration frameRefreshDuration = std::chrono::steady_clock::duration::zero();
} MainData;
#ifdef __EMSCRIPTEN__
bool emscriptenFilesystemReadReady = false;
bool emscriptenFilesystemLoadConfigDone = false;
#endif
bool load_file_to_string(std::string& toRet, std::string_view fileName) {
// https://nullptr.org/cpp-read-file-into-string/
std::ifstream file(fileName.data(), std::ios::in | std::ios::binary | std::ios::ate);
@ -334,12 +329,6 @@ void resize_window(MainStruct& mS) {
}
#ifdef __EMSCRIPTEN__
extern "C" {
EMSCRIPTEN_KEEPALIVE inline void emscripten_filesystem_loaded() {
emscriptenFilesystemReadReady = true;
}
}
const char* emscripten_before_unload(int eventType, const void *reserved, void *userData) {
MainStruct& mS = *((MainStruct*)userData);
mS.m->save_config();
@ -351,23 +340,7 @@ void init_logs(MainStruct& mS) {
char* homePathSDL = SDL_GetCurrentDirectory();
mS.homePath = std::filesystem::path(homePathSDL);
SDL_free(homePathSDL);
#ifdef __EMSCRIPTEN__
EM_ASM(
FS.mkdir('/infinipaint');
FS.mount(FS.filesystems.IDBFS, {}, '/infinipaint');
FS.syncfs(true, (err) => {
if(err)
console.log("Error syncing IDBFS: ", err);
else {
console.log("Synced to IDBFS. Read Ready");
Module["ccall"]('emscripten_filesystem_loaded', null, [], []);
}
});
);
mS.homePath = std::filesystem::path("/");
mS.configPath = std::filesystem::path("/infinipaint");
#elif CONFIG_NEXT_TO_EXECUTABLE
#ifdef CONFIG_NEXT_TO_EXECUTABLE
std::string CONFIG_FOLDER_NAME = "config";
std::filesystem::create_directory(CONFIG_FOLDER_NAME);
mS.configPath = mS.homePath / CONFIG_FOLDER_NAME;
@ -420,7 +393,8 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
int initWidth = 1000;
int initHeight = 900;
#ifdef __EMSCRIPTEN__
#ifdef __EMSCRIPTEN__
{
Vector2d sizeD;
EMSCRIPTEN_RESULT result = emscripten_get_element_css_size("#canvas", &sizeD.x(), &sizeD.y());
if(result != EMSCRIPTEN_RESULT_SUCCESS)
@ -430,7 +404,8 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
initHeight = sizeD.y();
std::cout << "Initial window size: " << initWidth << " " << initHeight << std::endl;
}
#endif
}
#endif
initialize_sdl(mS, initWidth, initHeight);
@ -443,7 +418,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
mS.m->logFile = &mS.logFile;
mS.m->conf.configPath = mS.configPath;
mS.m->homePath = mS.homePath;
#ifndef __EMSCRIPTEN__
#ifndef __EMSCRIPTEN__
const char* documentsPathSDL = SDL_GetUserFolder(SDL_FOLDER_DOCUMENTS);
if(!documentsPathSDL) {
documentsPathSDL = SDL_GetUserFolder(SDL_FOLDER_DESKTOP);
@ -454,14 +429,13 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
}
else
mS.m->documentsPath = std::filesystem::path(documentsPathSDL);
#endif
#endif
mS.m->window.sdlWindow = mS.window;
mS.m->update_scale_and_density();
#ifdef __EMSCRIPTEN__
emscripten_set_beforeunload_callback((void*)mSPtr, emscripten_before_unload);
#else
mS.m->load_config();
#endif
#ifdef __EMSCRIPTEN__
emscripten_set_beforeunload_callback((void*)mSPtr, emscripten_before_unload);
#endif
#ifdef USE_BACKEND_VULKAN
#ifdef USE_SKIA_BACKEND_GRAPHITE
std::unique_ptr<const skwindow::DisplayParams> displayParams = skwindow::DisplayParamsBuilder().build();
@ -614,13 +588,6 @@ SDL_AppResult SDL_AppIterate(void *appstate) {
#ifdef NDEBUG
try {
#endif
#ifdef __EMSCRIPTEN__
if(emscriptenFilesystemReadReady && !emscriptenFilesystemLoadConfigDone) {
mS.m->load_config();
emscriptenFilesystemLoadConfigDone = true;
}
#endif
mS.m->update();
if(mS.m->setToQuit)