diff --git a/CMakeLists.txt b/CMakeLists.txt index ab9c3dd..4c6325a 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/android-project/app/src/main/AndroidManifest.xml b/android-project/app/src/main/AndroidManifest.xml index 5233d83..0a5a9b0 100644 --- a/android-project/app/src/main/AndroidManifest.xml +++ b/android-project/app/src/main/AndroidManifest.xml @@ -79,7 +79,7 @@ /14560344/android-backspace-in-webview-baseinputconnection - // and https://bugzilla.libsdl.org/show_bug.cgi?id=2265 - if (beforeLength > 0 && afterLength == 0) { - // backspace(s) - while (beforeLength-- > 0) { - nativeGenerateScancodeForUnichar('\b'); - } - return true; - } - } + // 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) { + // backspace(s) + while (beforeLength-- > 0) { + nativeGenerateScancodeForUnichar('\b'); + } + return true; + } if (!super.deleteSurroundingText(beforeLength, afterLength)) { return false; diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java b/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java index 37781d2..1579b73 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java @@ -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); } diff --git a/conan/export_libs.bat b/conan/export_libs.bat index 380d14a..1800057 100755 --- a/conan/export_libs.bat +++ b/conan/export_libs.bat @@ -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 diff --git a/conan/export_libs.sh b/conan/export_libs.sh index 230d530..76511e6 100755 --- a/conan/export_libs.sh +++ b/conan/export_libs.sh @@ -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 ../../../.. diff --git a/conan/sdl/3.x/conandata.yml b/conan/sdl/3.x/conandata.yml new file mode 100644 index 0000000..78f86e8 --- /dev/null +++ b/conan/sdl/3.x/conandata.yml @@ -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" diff --git a/conan/sdl/3.x/conanfile.py b/conan/sdl/3.x/conanfile.py new file mode 100644 index 0000000..2ae2edb --- /dev/null +++ b/conan/sdl/3.x/conanfile.py @@ -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/") # 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", + ] + ) diff --git a/conan/sdl/3.x/test_package/CMakeLists.txt b/conan/sdl/3.x/test_package/CMakeLists.txt new file mode 100644 index 0000000..71ddca0 --- /dev/null +++ b/conan/sdl/3.x/test_package/CMakeLists.txt @@ -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) diff --git a/conan/sdl/3.x/test_package/conanfile.py b/conan/sdl/3.x/test_package/conanfile.py new file mode 100644 index 0000000..c21bd20 --- /dev/null +++ b/conan/sdl/3.x/test_package/conanfile.py @@ -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") diff --git a/conan/sdl/3.x/test_package/test_package.cpp b/conan/sdl/3.x/test_package/test_package.cpp new file mode 100644 index 0000000..b9d1f23 --- /dev/null +++ b/conan/sdl/3.x/test_package/test_package.cpp @@ -0,0 +1,8 @@ +#include +#include + +int main(int argc, char* args[]) { + auto version = SDL_GetVersion(); + std::cout << "SDL version " << version << std::endl; + return 0; +} diff --git a/conan/sdl/all/conandata.yml b/conan/sdl/all/conandata.yml new file mode 100644 index 0000000..9d28c0d --- /dev/null +++ b/conan/sdl/all/conandata.yml @@ -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" diff --git a/conan/sdl/all/conanfile.py b/conan/sdl/all/conanfile.py new file mode 100644 index 0000000..7cff02b --- /dev/null +++ b/conan/sdl/all/conanfile.py @@ -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"), + ) diff --git a/conan/sdl/all/patches/2.32.10-0001-alsa-fix-signature-of-snd_pcm_info_free.patch b/conan/sdl/all/patches/2.32.10-0001-alsa-fix-signature-of-snd_pcm_info_free.patch new file mode 100644 index 0000000..544c147 --- /dev/null +++ b/conan/sdl/all/patches/2.32.10-0001-alsa-fix-signature-of-snd_pcm_info_free.patch @@ -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 + diff --git a/conan/sdl/all/test_package/CMakeLists.txt b/conan/sdl/all/test_package/CMakeLists.txt new file mode 100644 index 0000000..4195ae0 --- /dev/null +++ b/conan/sdl/all/test_package/CMakeLists.txt @@ -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() + diff --git a/conan/sdl/all/test_package/conanfile.py b/conan/sdl/all/test_package/conanfile.py new file mode 100644 index 0000000..c21bd20 --- /dev/null +++ b/conan/sdl/all/test_package/conanfile.py @@ -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") diff --git a/conan/sdl/all/test_package/test_package.cpp b/conan/sdl/all/test_package/test_package.cpp new file mode 100644 index 0000000..69868e5 --- /dev/null +++ b/conan/sdl/all/test_package/test_package.cpp @@ -0,0 +1,9 @@ +#include +#include + +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; +} diff --git a/conan/sdl/config.yml b/conan/sdl/config.yml new file mode 100644 index 0000000..a789df0 --- /dev/null +++ b/conan/sdl/config.yml @@ -0,0 +1,5 @@ +versions: + "3.4.0": + folder: 3.x + "2.32.10": + folder: all diff --git a/conanfile.py b/conanfile.py index 73b6719..10424a8 100644 --- a/conanfile.py +++ b/conanfile.py @@ -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 = { diff --git a/deps/SDL b/deps/SDL index 683181b..5848e58 160000 --- a/deps/SDL +++ b/deps/SDL @@ -1 +1 @@ -Subproject commit 683181b47cfabd293e3ea409f838915b8297a4fd +Subproject commit 5848e584a1b606de26e3dbd1c7e4ecbc34f807a6 diff --git a/linuxinstall/com.infinipaint.infinipaint.yml b/linuxinstall/com.infinipaint.infinipaint.yml index 195eb7a..a72693d 100644 --- a/linuxinstall/com.infinipaint.infinipaint.yml +++ b/linuxinstall/com.infinipaint.infinipaint.yml @@ -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 diff --git a/src/ClientData.cpp b/src/ClientData.cpp index ba02aa2..d3928e6 100644 --- a/src/ClientData.cpp +++ b/src/ClientData.cpp @@ -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(); diff --git a/src/GUIStuff/ElementHelpers/ColorPickerItemsHelpers.hpp b/src/GUIStuff/ElementHelpers/ColorPickerItemsHelpers.hpp index 0894778..d4bf6fb 100644 --- a/src/GUIStuff/ElementHelpers/ColorPickerItemsHelpers.hpp +++ b/src/GUIStuff/ElementHelpers/ColorPickerItemsHelpers.hpp @@ -14,7 +14,7 @@ struct ColorPickerItemsOptions { template 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(); }; diff --git a/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp b/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp index 1c8ba27..3f1ee24 100644 --- a/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp +++ b/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp @@ -93,7 +93,7 @@ template 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()) { ss.precision(decimalPrecision); diff --git a/src/GUIStuff/Elements/ColorPicker.decl.hpp b/src/GUIStuff/Elements/ColorPicker.decl.hpp index 112f395..6f16eaa 100644 --- a/src/GUIStuff/Elements/ColorPicker.decl.hpp +++ b/src/GUIStuff/Elements/ColorPicker.decl.hpp @@ -69,7 +69,7 @@ template 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: diff --git a/src/GUIStuff/Elements/NumberSlider.hpp b/src/GUIStuff/Elements/NumberSlider.hpp index 1728713..fbeff8c 100644 --- a/src/GUIStuff/Elements/NumberSlider.hpp +++ b/src/GUIStuff/Elements/NumberSlider.hpp @@ -33,7 +33,7 @@ template 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) { diff --git a/src/InputManager.cpp b/src/InputManager.cpp index c778c7b..c728f5d 100644 --- a/src/InputManager.cpp +++ b/src/InputManager.cpp @@ -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,25 +925,35 @@ 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) { - emscripten_browser_clipboard::paste_async([](std::string&& pasteData, void* callbackData){ - InputManager* inMan = (InputManager*)callbackData; - std::string pData = pasteData; - inMan->process_text_paste(pData); - }, this); - } - else { - struct PasteData { - std::optional pos; - InputManager* t; - }; - static PasteData pasteData; - pasteData.pos = info.pastePosition; - pasteData.t = this; - emscripten_browser_clipboard::paste_async_image([](std::string_view pasteData, void* callbackData){ - PasteData* p = (PasteData*)callbackData; - p->t->process_image_paste(pasteData, p->pos); - }, &pasteData); + 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){ + PasteData* p = (PasteData*)callbackData; + p->t->process_text_paste(pasteData, p->allowRichText); + }, &pasteData); + break; + } + case CustomEvents::PasteEvent::DataType::IMAGE: { + struct PasteData { + std::optional pos; + InputManager* t; + }; + static PasteData pasteData; + pasteData.pos = info.pastePosition; + pasteData.t = this; + emscripten_browser_clipboard::paste_async_image([](std::string_view pasteData, void* callbackData){ + PasteData* p = (PasteData*)callbackData; + p->t->process_image_paste(pasteData, p->pos); + }, &pasteData); + break; + } } #else switch(type) { diff --git a/src/MainProgram.cpp b/src/MainProgram.cpp index 146e74a..88eb2f7 100755 --- a/src/MainProgram.cpp +++ b/src/MainProgram.cpp @@ -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); diff --git a/src/RichText/TextBox.cpp b/src/RichText/TextBox.cpp index 636f418..91732c5 100644 --- a/src/RichText/TextBox.cpp +++ b/src/RichText/TextBox.cpp @@ -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; diff --git a/src/Screens/FileSelectScreen.cpp b/src/Screens/FileSelectScreen.cpp new file mode 100644 index 0000000..0454f53 --- /dev/null +++ b/src/Screens/FileSelectScreen.cpp @@ -0,0 +1 @@ +#include "FileSelectScreen.hpp" diff --git a/src/Screens/FileSelectScreen.hpp b/src/Screens/FileSelectScreen.hpp new file mode 100644 index 0000000..e99cc2f --- /dev/null +++ b/src/Screens/FileSelectScreen.hpp @@ -0,0 +1,7 @@ +#pragma once + +class FileSelectScreen { + public: + FileSelectScreen(); + private: +}; diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 5633391..2fcaa69 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -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(callbackData); CustomEvents::emit_event({ .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({ .isClient = uD->iC, .filePathSource = std::filesystem::path(fileName), .netSource = uD->nS, .serverLocalID = uD->sLID, .fileDataBuffer = buffer - }, true); + }); } }, &uploadData); #else diff --git a/src/World.cpp b/src/World.cpp index 6f3b11c..0d567f3 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -21,9 +21,6 @@ #include "WorldGrid.hpp" #include #include -#include -#include "FileHelpers.hpp" -#include "FontData.hpp" #include #include #include @@ -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) { diff --git a/src/main.cpp b/src/main.cpp index 3ba3eca..a2ce416 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -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,17 +393,19 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { int initWidth = 1000; int initHeight = 900; -#ifdef __EMSCRIPTEN__ - Vector2d sizeD; - EMSCRIPTEN_RESULT result = emscripten_get_element_css_size("#canvas", &sizeD.x(), &sizeD.y()); - if(result != EMSCRIPTEN_RESULT_SUCCESS) - std::cout << "Failed to get canvas size" << std::endl; - else { - initWidth = sizeD.x(); - initHeight = sizeD.y(); - std::cout << "Initial window size: " << initWidth << " " << initHeight << std::endl; + #ifdef __EMSCRIPTEN__ + { + Vector2d sizeD; + EMSCRIPTEN_RESULT result = emscripten_get_element_css_size("#canvas", &sizeD.x(), &sizeD.y()); + if(result != EMSCRIPTEN_RESULT_SUCCESS) + std::cout << "Failed to get canvas size" << std::endl; + else { + initWidth = sizeD.x(); + initHeight = sizeD.y(); + std::cout << "Initial window size: " << initWidth << " " << initHeight << std::endl; + } } -#endif + #endif initialize_sdl(mS, initWidth, initHeight); @@ -443,25 +418,24 @@ 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__ - const char* documentsPathSDL = SDL_GetUserFolder(SDL_FOLDER_DOCUMENTS); - if(!documentsPathSDL) { - documentsPathSDL = SDL_GetUserFolder(SDL_FOLDER_DESKTOP); - if(!documentsPathSDL) - mS.m->documentsPath = mS.m->conf.configPath; + #ifndef __EMSCRIPTEN__ + const char* documentsPathSDL = SDL_GetUserFolder(SDL_FOLDER_DOCUMENTS); + if(!documentsPathSDL) { + documentsPathSDL = SDL_GetUserFolder(SDL_FOLDER_DESKTOP); + if(!documentsPathSDL) + mS.m->documentsPath = mS.m->conf.configPath; + else + mS.m->documentsPath = std::filesystem::path(documentsPathSDL); + } else mS.m->documentsPath = std::filesystem::path(documentsPathSDL); - } - 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 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)