diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 55340c30c7..23bd0b2562 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -73,7 +73,7 @@ jobs: run: | set -x sudo apt-get update - sudo apt-get -y -q --no-install-recommends install zlib1g-dev libncursesw5-dev libgeoip-dev nettle-dev libgmp-dev libcurl4-gnutls-dev libsdl2-dev libogg-dev libvorbis-dev libopusfile-dev libwebp-dev libjpeg8-dev libpng-dev libfreetype6-dev libglew-dev libopenal-dev ninja-build + sudo apt-get -y -q --no-install-recommends install zlib1g-dev libncursesw5-dev libgeoip-dev nettle-dev libgmp-dev libcurl4-gnutls-dev libogg-dev libvorbis-dev libopusfile-dev libwebp-dev libjpeg8-dev libpng-dev libfreetype6-dev libglew-dev libopenal-dev ninja-build git submodule update --init --recursive curl -sS https://gitlab.com/illwieckz/git-checkout-modules/raw/master/git-checkout-modules -o ~/git-checkout-modules diff --git a/CMakeLists.txt b/CMakeLists.txt index 93ff21b2b6..29dde26819 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,7 +214,7 @@ else() endif() # Dependencies version, this must match the number in external_deps/build.sh -set(DEPS_VERSION 10) +set(DEPS_VERSION 11) option(USE_EXTERNAL_DEPS "Download or reuse dependencies from EXTERNAL_DEPS_DIR (mandatory for building and running NaCl .nexe binaries)." ON) @@ -454,22 +454,38 @@ if (DEPS_DIR) if (NOT EXISTS ${DEPS_DIR}) file(MAKE_DIRECTORY ${EXTERNAL_DEPS_DIR}) get_filename_component(BASENAME ${DEPS_DIR} NAME) - set(REMOTE "https://dl.unvanquished.net/deps/${BASENAME}${DEPS_EXT}") - message(STATUS "Downloading dependencies from '${REMOTE}'") - file(DOWNLOAD ${REMOTE} ${OBJ_DIR}/${BASENAME}${DEPS_EXT} - SHOW_PROGRESS - STATUS DOWNLOAD_RESULT - LOG DOWNLOAD_LOG - ) - list(GET DOWNLOAD_RESULT 0 DOWNLOAD_STATUS) - list(GET DOWNLOAD_RESULT 1 DOWNLOAD_STRING) + set(DEPS_FILENAME "${BASENAME}${DEPS_EXT}") + + # Download test DEPS archive if DEPS archive isn't available yet. + # This makes possible to get the CI working before releasing the DEPS archive. + foreach(REMOTE_BASEURL https://dl.unvanquished.net/deps;https://dl.unvanquished.net/test/deps) + set(REMOTE "${REMOTE_BASEURL}/${DEPS_FILENAME}") + message(STATUS "Downloading dependencies from '${REMOTE}'") + + file(DOWNLOAD "${REMOTE}" "${OBJ_DIR}/${DEPS_FILENAME}" + SHOW_PROGRESS + STATUS DOWNLOAD_RESULT + LOG DOWNLOAD_LOG + ) + + list(GET DOWNLOAD_RESULT 0 DOWNLOAD_STATUS) + list(GET DOWNLOAD_RESULT 1 DOWNLOAD_STRING) + + if (DOWNLOAD_STATUS EQUAL 0) + break() + else () + message(WARNING "Error downloading '${REMOTE}': + Status code: ${DOWNLOAD_STATUS} + Error string: ${DOWNLOAD_STRING} + Download log: ${DOWNLOAD_LOG}" + ) + endif() + endforeach() + if (NOT DOWNLOAD_STATUS EQUAL 0) - message(FATAL_ERROR "Error downloading '${REMOTE}': - Status code: ${DOWNLOAD_STATUS} - Error string: ${DOWNLOAD_STRING} - Download log: ${DOWNLOAD_LOG}" - ) + message(FATAL_ERROR "Failed to download '${DEPS_FILENAME}'") endif() + message(STATUS "Download completed successfully") # Extract the downloaded archive @@ -486,7 +502,7 @@ if (DEPS_DIR) # Add to paths set(CMAKE_FIND_ROOT_PATH ${DEPS_DIR} ${CMAKE_FIND_ROOT_PATH}) set(CMAKE_INCLUDE_PATH ${DEPS_DIR} ${DEPS_DIR}/include ${CMAKE_INCLUDE_PATH}) - set(CMAKE_FRAMEWORK_PATH ${DEPS_DIR} ${CMAKE_FRAMEWORK_PATH}) + set(CMAKE_FRAMEWORK_PATH ${DEPS_DIR}/lib ${CMAKE_FRAMEWORK_PATH}) set(CMAKE_PREFIX_PATH ${DEPS_DIR} ${CMAKE_PREFIX_PATH}) if (DAEMON_PARENT_SCOPE_DIR) # Also set parent scope so the top level CMakeLists can find precompiled deps @@ -709,30 +725,12 @@ endif() # SDL, required for all targets on win32 because of iconv and SDL_SetHint(SDL_TIMER_RESOLUTION, 0) if (BUILD_CLIENT OR WIN32) - find_package(SDL2 CONFIG) - - if (SDL2_SDL2main_FOUND) - mark_as_advanced(SDL2_DIR) - else() - # We cannot use REQUIRED because it would look - # for OldSDL2_FOUND instead of SDL2_FOUND. - find_package(OldSDL2) - - if (NOT SDL2_FOUND) - message(FATAL_ERROR "Could NOT find SDL2") - endif() - - include_directories(${SDL2_INCLUDE_DIR}) - - mark_as_advanced(SDL2MAIN_LIBRARY SDL2_LIBRARY SDL2_INCLUDE_DIR) - endif () + find_package(SDL3 REQUIRED CONFIG) if (WIN32) - set(LIBS_ENGINE_BASE ${LIBS_ENGINE_BASE} - $<$:SDL2::SDL2main> SDL2::SDL2) + set(LIBS_ENGINE_BASE ${LIBS_ENGINE_BASE} SDL3::SDL3) else() - set(LIBS_CLIENT ${LIBS_CLIENT} - $<$:SDL2::SDL2main> SDL2::SDL2) + set(LIBS_CLIENT ${LIBS_CLIENT} SDL3::SDL3) endif() endif() @@ -1062,11 +1060,16 @@ if (DEPS_DIR AND (BUILD_CLIENT OR BUILD_TTY_CLIENT OR BUILD_SERVER OR BUILD_DUMM # Mac requires some libraries from external_deps if (APPLE) - add_custom_command(TARGET runtime_deps PRE_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${DEPS_DIR}/SDL2.framework - ${FULL_OUTPUT_DIR}/SDL2.framework - ) + file(GLOB RUNTIME_FRAMEWORKS ${DEPS_DIR}/lib/*.framework) + foreach(RUNTIME_FRAMEWORK ${RUNTIME_FRAMEWORKS}) + get_filename_component(RUNTIME_FRAMEWORK_NAME ${RUNTIME_FRAMEWORK} NAME) + add_custom_command(TARGET runtime_deps PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${RUNTIME_FRAMEWORK} + ${FULL_OUTPUT_DIR}/${RUNTIME_FRAMEWORK_NAME} + ) + endforeach() + file(GLOB RUNTIME_LIBS ${DEPS_DIR}/lib/*.dylib) foreach(RUNTIME_LIB ${RUNTIME_LIBS}) add_custom_command(TARGET runtime_deps PRE_BUILD @@ -1079,7 +1082,7 @@ if (DEPS_DIR AND (BUILD_CLIENT OR BUILD_TTY_CLIENT OR BUILD_SERVER OR BUILD_DUMM # Windows requires some libraries from external_deps if (WIN32) - file(GLOB RUNTIME_LIBS ${DEPS_DIR}/bin/*.dll ${DEPS_DIR}/SDL2/lib/*/SDL2.dll) + file(GLOB RUNTIME_LIBS ${DEPS_DIR}/bin/*.dll ${DEPS_DIR}/SDL3/lib/*/SDL3.dll) foreach(RUNTIME_LIB ${RUNTIME_LIBS}) add_custom_command(TARGET runtime_deps PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9152a3adf8..5db00f050f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -120,7 +120,7 @@ jobs: - bash: | set -e sudo apt-get update - sudo apt-get -y -q --no-install-recommends install zlib1g-dev libncursesw5-dev libgeoip-dev nettle-dev libgmp-dev libcurl4-gnutls-dev libsdl2-dev libogg-dev libvorbis-dev libopusfile-dev libwebp-dev libjpeg8-dev libpng-dev libfreetype6-dev libglew-dev libopenal-dev liblua5.2-dev ninja-build $(EXTRA_PACKAGES) + sudo apt-get -y -q --no-install-recommends install zlib1g-dev libncursesw5-dev libgeoip-dev nettle-dev libgmp-dev libcurl4-gnutls-dev libogg-dev libvorbis-dev libopusfile-dev libwebp-dev libjpeg8-dev libpng-dev libfreetype6-dev libglew-dev libopenal-dev liblua5.2-dev ninja-build $(EXTRA_PACKAGES) $(EXTRA_INSTALLS) displayName: 'Install deps' - bash: | diff --git a/cmake/FindOldSDL2.cmake b/cmake/FindOldSDL2.cmake deleted file mode 100644 index 7c7a665ba9..0000000000 --- a/cmake/FindOldSDL2.cmake +++ /dev/null @@ -1,186 +0,0 @@ -# Locate SDL2 library -# This module defines -# SDL2_LIBRARY, the name of the library to link against -# SDL2_FOUND, if false, do not try to link to SDL2 -# SDL2_INCLUDE_DIR, where to find SDL.h -# -# This module responds to the the flag: -# SDL2_BUILDING_LIBRARY -# If this is defined, then no SDL2_main will be linked in because -# only applications need main(). -# Otherwise, it is assumed you are building an application and this -# module will attempt to locate and set the the proper link flags -# as part of the returned SDL2_LIBRARY variable. -# -# Don't forget to include SDL2main.h and SDL2main.m your project for the -# OS X framework based version. (Other versions link to -lSDL2main which -# this module will try to find on your behalf.) Also for OS X, this -# module will automatically add the -framework Cocoa on your behalf. -# -# -# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration -# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library -# (SDL2.dll, libsdl2.so, SDL2.framework, etc). -# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. -# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value -# as appropriate. These values are used to generate the final SDL2_LIBRARY -# variable, but when these values are unset, SDL2_LIBRARY does not get created. -# -# -# $SDL2DIR is an environment variable that would -# correspond to the ./configure --prefix=$SDL2DIR -# used in building SDL2. -# l.e.galup 9-20-02 -# -# Modified by Eric Wing. -# Added code to assist with automated building by using environmental variables -# and providing a more controlled/consistent search behavior. -# Added new modifications to recognize OS X frameworks and -# additional Unix paths (FreeBSD, etc). -# Also corrected the header search path to follow "proper" SDL2 guidelines. -# Added a search for SDL2main which is needed by some platforms. -# Added a search for threads which is needed by some platforms. -# Added needed compile switches for MinGW. -# -# On OSX, this will prefer the Framework version (if found) over others. -# People will have to manually change the cache values of -# SDL2_LIBRARY to override this selection or set the CMake environment -# CMAKE_INCLUDE_PATH to modify the search paths. -# -# Note that the header path has changed from SDL2/SDL.h to just SDL.h -# This needed to change because "proper" SDL2 convention -# is #include "SDL.h", not . This is done for portability -# reasons because not all systems place things in SDL2/ (see FreeBSD). -# -# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake -# module with the minor edit of changing "SDL" to "SDL2" where necessary. This -# was not created for redistribution, and exists temporarily pending official -# SDL2 CMake modules. - -#============================================================================= -# Copyright 2003-2009 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -FIND_PATH(SDL2_INCLUDE_DIR SDL.h - HINTS - $ENV{SDL2DIR} - PATH_SUFFIXES include/SDL2 include - PATHS - ~/Library/Frameworks - /Library/Frameworks - /usr/local/include/SDL2 - /usr/include/SDL2 - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt -) -#MESSAGE("SDL2_INCLUDE_DIR is ${SDL2_INCLUDE_DIR}") - -FIND_LIBRARY(SDL2_LIBRARY_TEMP - NAMES SDL2 - HINTS - $ENV{SDL2DIR} - PATH_SUFFIXES lib64 lib - PATHS - /sw - /opt/local - /opt/csw - /opt -) - -#MESSAGE("SDL2_LIBRARY_TEMP is ${SDL2_LIBRARY_TEMP}") - -IF(NOT SDL2_BUILDING_LIBRARY) - IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") - # Non-OS X framework versions expect you to also dynamically link to - # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms - # seem to provide SDL2main for compatibility even though they don't - # necessarily need it. - FIND_LIBRARY(SDL2MAIN_LIBRARY - NAMES SDL2main - HINTS - $ENV{SDL2DIR} - PATH_SUFFIXES lib64 lib - PATHS - /sw - /opt/local - /opt/csw - /opt - ) - ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") -ENDIF(NOT SDL2_BUILDING_LIBRARY) - -# SDL2 may require threads on your system. -# The Apple build may not need an explicit flag because one of the -# frameworks may already provide it. -# But for non-OSX systems, I will use the CMake Threads package. -IF(NOT APPLE) - FIND_PACKAGE(Threads) -ENDIF(NOT APPLE) - -# MinGW needs an additional library, mwindows -# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows -# (Actually on second look, I think it only needs one of the m* libraries.) -IF(MINGW) - SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") -ENDIF(MINGW) - -SET(SDL2_FOUND "NO") -IF(SDL2_LIBRARY_TEMP) - # For SDL2main - IF(NOT SDL2_BUILDING_LIBRARY) - IF(SDL2MAIN_LIBRARY) - SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) - ENDIF(SDL2MAIN_LIBRARY) - ENDIF(NOT SDL2_BUILDING_LIBRARY) - - # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. - # CMake doesn't display the -framework Cocoa string in the UI even - # though it actually is there if I modify a pre-used variable. - # I think it has something to do with the CACHE STRING. - # So I use a temporary variable until the end so I can set the - # "real" variable in one-shot. - IF(APPLE) - SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") - ENDIF(APPLE) - - # For threads, as mentioned Apple doesn't need this. - # In fact, there seems to be a problem if I used the Threads package - # and try using this line, so I'm just skipping it entirely for OS X. - IF(NOT APPLE) - SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) - ENDIF(NOT APPLE) - - # For MinGW library - IF(MINGW) - SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) - ENDIF(MINGW) - - # Set the final string here so the GUI reflects the final state. - SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") - # Set the temp variable to INTERNAL so it is not seen in the CMake GUI - SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") - - SET(SDL2_FOUND "YES") -ENDIF(SDL2_LIBRARY_TEMP) - -INCLUDE(FindPackageHandleStandardArgs) - -FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 - REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) - -if(SDL2_FOUND AND NOT TARGET SDL2::SDL2) - add_library(SDL2::SDL2 INTERFACE IMPORTED) - set_property(TARGET SDL2::SDL2 PROPERTY INTERFACE_LINK_LIBRARIES "${SDL2_LIBRARIES}") - set_property(TARGET SDL2::SDL2 PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIRS}") -endif() diff --git a/cmake/cross-toolchain-mingw32.cmake b/cmake/cross-toolchain-mingw32.cmake index 3c97d7a0e6..c33340276b 100644 --- a/cmake/cross-toolchain-mingw32.cmake +++ b/cmake/cross-toolchain-mingw32.cmake @@ -7,10 +7,8 @@ set( CMAKE_C_COMPILER i686-w64-mingw32-gcc ) set( CMAKE_CXX_COMPILER i686-w64-mingw32-g++ ) set( CMAKE_RC_COMPILER i686-w64-mingw32-windres ) -# Target prefix -set( CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32 ) - # Find programs using host paths and headers/libraries using target paths +# FIXME: not respected when cmake invokes pkg-config set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) diff --git a/cmake/cross-toolchain-mingw64.cmake b/cmake/cross-toolchain-mingw64.cmake index fb308a4556..24636132bf 100644 --- a/cmake/cross-toolchain-mingw64.cmake +++ b/cmake/cross-toolchain-mingw64.cmake @@ -7,10 +7,8 @@ set( CMAKE_C_COMPILER x86_64-w64-mingw32-gcc ) set( CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++ ) set( CMAKE_RC_COMPILER x86_64-w64-mingw32-windres ) -# Target prefix -set( CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32 ) - # Find programs using host paths and headers/libraries using target paths +# FIXME: not respected when cmake invokes pkg-config set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) diff --git a/external_deps/build.sh b/external_deps/build.sh index 3b2f5ebbb0..dc0560f8bc 100755 --- a/external_deps/build.sh +++ b/external_deps/build.sh @@ -9,7 +9,7 @@ WORK_DIR="${PWD}" # This should match the DEPS_VERSION in CMakeLists.txt. # This is mostly to ensure the path the files end up at if you build deps yourself # are the same as the ones when extracting from the downloaded packages. -DEPS_VERSION=10 +DEPS_VERSION=11 # Package download pages PKGCONFIG_BASEURL='https://pkg-config.freedesktop.org/releases' @@ -18,15 +18,15 @@ ZLIB_BASEURL='https://zlib.net/fossils' GMP_BASEURL='https://gmplib.org/download/gmp' NETTLE_BASEURL='https://mirror.cyberbits.eu/gnu/nettle' CURL_BASEURL='https://curl.se/download' -SDL2_BASEURL='https://www.libsdl.org/release' +SDL3_BASEURL='https://www.libsdl.org/release' GLEW_BASEURL='https://github.com/nigels-com/glew/releases' # Index: https://download.sourceforge.net/libpng/files/libpng16 PNG_BASEURL='https://sourceforge.net/projects/libpng/files/libpng16' -# Index: https://downloads.sourceforge.net/project/libjpeg-turbo -JPEG_BASEURL='https://sourceforge.net/projects/libjpeg-turbo/files' +JPEG_BASEURL='https://github.com/libjpeg-turbo/libjpeg-turbo/releases' # Index: https://storage.googleapis.com/downloads.webmproject.org/releases/webp/index.html WEBP_BASEURL='https://storage.googleapis.com/downloads.webmproject.org/releases/webp' -OPENAL_BASEURL='https://openal-soft.org/openal-releases' +# Index: https://github.com/kcat/openal-soft/releases +OPENAL_BASEURL='https://github.com/kcat/openal-soft' OGG_BASEURL='https://downloads.xiph.org/releases/ogg' VORBIS_BASEURL='https://downloads.xiph.org/releases/vorbis' OPUS_BASEURL='https://downloads.xiph.org/releases/opus' @@ -41,24 +41,26 @@ WASMTIME_BASEURL='https://github.com/bytecodealliance/wasmtime/releases' # Package versions PKGCONFIG_VERSION=0.29.2 -NASM_VERSION=2.16.01 -ZLIB_VERSION=1.2.13 -GMP_VERSION=6.2.1 -NETTLE_VERSION=3.8.1 -CURL_VERSION=7.83.1 -SDL2_VERSION=2.26.5 +NASM_VERSION=2.16.03 +ZLIB_VERSION=1.3.1 +GMP_VERSION=6.3.0 +NETTLE_VERSION=3.10.2 +CURL_VERSION=8.15.0 +SDL3_VERSION=3.2.22 GLEW_VERSION=2.2.0 -PNG_VERSION=1.6.39 -JPEG_VERSION=2.1.5.1 -WEBP_VERSION=1.3.2 -OPENAL_VERSION=1.23.1 -OGG_VERSION=1.3.5 +PNG_VERSION=1.6.50 +JPEG_VERSION=3.1.1 +# WebP 1.6.0 introduced AVX2 intrinsics that are not available on +# the GCC 10 compiler provided by Debian Bullseye. +WEBP_VERSION=1.5.0 +OPENAL_VERSION=1.24.3 +OGG_VERSION=1.3.6 VORBIS_VERSION=1.3.7 -OPUS_VERSION=1.4 +OPUS_VERSION=1.5.2 OPUSFILE_VERSION=0.12 NACLSDK_VERSION=44.0.2403.155 NACLRUNTIME_REVISION=2aea5fcfce504862a825920fcaea1a8426afbd6f -NCURSES_VERSION=6.2 +NCURSES_VERSION=6.5 WASISDK_VERSION=16.0 WASMTIME_VERSION=2.0.2 @@ -70,13 +72,17 @@ CXX='false' LD='ld' AR='ar' RANLIB='ranlib' -CONFIGURE_SHARED=(--disable-shared --enable-static) +PKG_CONFIG='pkg-config' +CROSS_PKG_CONFIG_PATH='' +LIBS_SHARED='OFF' +LIBS_STATIC='ON' +CMAKE_TOOLCHAIN='' # Always reset flags, we heavily cross-compile and must not inherit any stray flag # from environment. -CFLAGS='' -CXXFLAGS='' CPPFLAGS='' -LDFLAGS='' +CFLAGS='-O3 -fPIC' +CXXFLAGS='-O3 -fPIC' +LDFLAGS='-O3 -fPIC' log() { level="${1}"; shift @@ -108,11 +114,11 @@ extract() { "${SCRIPT_DIR}/cygtar.py" -xjf "${1}" -C "${2}" ;; *.dmg) - mkdir -p "${2}-dmg" - hdiutil attach -mountpoint "${2}-dmg" "${1}" - cp -R "${2}-dmg/"* "${2}/" - hdiutil detach "${2}-dmg" - rmdir "${2}-dmg" + local dmg_temp_dir="$(mktemp -d)" + hdiutil attach -mountpoint "${dmg_temp_dir}" "${1}" + cp -R "${dmg_temp_dir}/"* "${2}/" + hdiutil detach "${dmg_temp_dir}" + rmdir "${dmg_temp_dir}" ;; *) log ERROR "Unknown archive type for ${1}" @@ -161,8 +167,84 @@ download_extract() { extract "${tarball_file}" "${extract_dir}" } -# Build pkg-config -# Still needed, at least on macos, for opusfile +configure_build() { + local configure_args=() + + if [ "${LIBS_SHARED}" = 'ON' ] + then + configure_args+=(--enable-shared) + else + configure_args+=(--disable-shared) + fi + + if [ "${LIBS_STATIC}" = 'ON' ] + then + configure_args+=(--enable-static) + else + configure_args+=(--disable-static) + fi + + # Workaround macOS bash limitation. + if [ -n "${1:-}" ] + then + configure_args+=("${@}") + fi + + ./configure \ + --host="${HOST}" \ + --prefix="${PREFIX}" \ + --libdir="${PREFIX}/lib" \ + "${configure_args[@]}" + + make + make install +} + +get_compiler_name() { + echo "${1}" +} + +get_compiler_arg1() { + shift + + # Check for ${@} not being empty to workaround a macOS bash limitation. + if [ -n "${1:-}" ] + then + echo "${@}" + fi +} + +cmake_build() { + local cmake_args=() + + cmake_args+=(-DCMAKE_C_COMPILER="$(get_compiler_name ${CC})") + cmake_args+=(-DCMAKE_CXX_COMPILER="$(get_compiler_name ${CXX})") + cmake_args+=(-DCMAKE_C_COMPILER_ARG1="$(get_compiler_arg1 ${CC})") + cmake_args+=(-DCMAKE_CXX_COMPILER_ARG1="$(get_compiler_arg1 ${CXX})") + cmake_args+=(-DCMAKE_C_FLAGS="${CFLAGS}") + cmake_args+=(-DCMAKE_CXX_FLAGS="${CXXFLAGS}") + cmake_args+=(-DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}") + + # Check for ${@} not being empty to workaround a macOS bash limitation. + if [ -n "${1:-}" ] + then + cmake_args+=("${@}") + fi + + cmake -S . -B build \ + -DCMAKE_TOOLCHAIN_FILE="${CMAKE_TOOLCHAIN}" \ + -DCMAKE_BUILD_TYPE='Release' \ + -DCMAKE_PREFIX_PATH="${PREFIX}" \ + -DCMAKE_INSTALL_PREFIX="${PREFIX}" \ + -DBUILD_SHARED_LIBS="${LIBS_SHARED}" \ + "${cmake_args[@]}" + + cmake --build build + cmake --install build --strip +} + +# Build pkg-config, needed for opusfile and SDL3. +# As a host-mode dependency it must be provided by the system when cross-compiling. build_pkgconfig() { local dir_name="pkg-config-${PKGCONFIG_VERSION}" local archive_name="${dir_name}.tar.gz" @@ -173,10 +255,10 @@ build_pkgconfig() { "${download_only}" && return cd "${dir_name}" - # The default -O2 is dropped when there's user-provided CFLAGS. - CFLAGS="${CFLAGS} -O2 -Wno-error=int-conversion" ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" --with-internal-glib - make - make install + + CFLAGS="${CFLAGS} -Wno-error=int-conversion" \ + configure_build \ + --with-internal-glib } # Build NASM @@ -213,16 +295,16 @@ build_zlib() { "${download_only}" && return cd "${dir_name}" + case "${PLATFORM}" in windows-*-*) LOC="${CFLAGS}" make -f win32/Makefile.gcc PREFIX="${HOST}-" make -f win32/Makefile.gcc install BINARY_PATH="${PREFIX}/bin" LIBRARY_PATH="${PREFIX}/lib" INCLUDE_PATH="${PREFIX}/include" SHARED_MODE=1 ;; *) - # The default -O3 is dropped when there's user-provided CFLAGS. - CFLAGS="${CFLAGS} -O3" ./configure --prefix="${PREFIX}" --libdir="${PREFIX}/lib" --static --const - make - make install + CFLAGS="${CFLAGS} -DZLIB_CONST" \ + cmake_build \ + -DZLIB_BUILD_EXAMPLES=OFF ;; esac } @@ -239,7 +321,6 @@ build_gmp() { "${download_only}" && return - cd "${dir_name}" case "${PLATFORM}" in windows-*-msvc) # Configure script gets confused if we override the compiler. Shouldn't @@ -251,19 +332,22 @@ build_gmp() { ;; esac - # The default -O2 is dropped when there's user-provided CFLAGS. + local gmp_configure_args=() + case "${PLATFORM}" in macos-*-*) # The assembler objects are incompatible with PIE - CFLAGS="${CFLAGS} -O2" ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" "${CONFIGURE_SHARED[@]}" --disable-assembly + gmp_configure_args+=(--disable-assembly) ;; *) - CFLAGS="${CFLAGS} -O2" ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" "${CONFIGURE_SHARED[@]}" ;; esac - make - make install + cd "${dir_name}" + + configure_build \ + "${gmp_configure_args[@]}" + case "${PLATFORM}" in windows-*-msvc) export CC="${CC_BACKUP}" @@ -284,10 +368,10 @@ build_nettle() { "${download_only}" && return cd "${dir_name}" - # The default -O2 is dropped when there's user-provided CFLAGS. - CFLAGS="${CFLAGS} -O2" ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" "${CONFIGURE_SHARED[@]}" - make - make install + + configure_build \ + --disable-fat \ + --disable-pic } # Build cURL @@ -302,34 +386,58 @@ build_curl() { "${download_only}" && return cd "${dir_name}" - # The user-provided CFLAGS doesn't drop the default -O2 - ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" --without-ssl --without-libssh2 --without-librtmp --without-libidn2 --without-brotli --without-zstd --disable-file --disable-ldap --disable-crypto-auth --disable-gopher --disable-ftp --disable-tftp --disable-dict --disable-imap --disable-mqtt --disable-smtp --disable-pop3 --disable-telnet --disable-rtsp --disable-threaded-resolver --disable-alt-svc "${CONFIGURE_SHARED[@]}" - make - make install + + cmake_build \ + -DBUILD_CURL_EXE=OFF \ + -DBUILD_TESTING=OFF \ + -DENABLE_CURL_MANUAL=OFF \ + -DENABLE_THREADED_RESOLVER=OFF \ + -DENABLE_UNIX_SOCKETS=OFF \ + -DUSE_HTTPSRR=OFF \ + -DUSE_LIBIDN2=OFF \ + -DUSE_LIBRTMP=OFF \ + -DUSE_MSH3=OFF \ + -DUSE_NGHTTP2=OFF \ + -DUSE_NGTCP2=OFF \ + -DUSE_OPENSSL_QUIC=OFF \ + -DUSE_QUICHE=OFF \ + -DUSE_WIN32_IDN=OFF \ + -DCURL_BROTLI=OFF \ + -DCURL_ZLIB=OFF \ + -DCURL_ZSTD=OFF \ + -DCURL_ENABLE_SSL=OFF \ + -DCURL_USE_GSSAPI=OFF \ + -DCURL_USE_LIBPSL=OFF \ + -DCURL_USE_LIBSSH=OFF \ + -DCURL_USE_LIBSSH2=OFF \ + -DCURL_USE_MBEDTLS=OFF \ + -DCURL_USE_OPENSSL=OFF \ + -DCURL_USE_WOLFSSL=OFF \ + -DHTTP_ONLY=ON # Implies all CURL_DISABLE_xxx options except HTTP } -# Build SDL2 -build_sdl2() { - local dir_name="SDL2-${SDL2_VERSION}" +# Build SDL3 +build_sdl3() { + local dir_name="SDL3-${SDL3_VERSION}" case "${PLATFORM}" in windows-*-mingw) - local archive_name="SDL2-devel-${SDL2_VERSION}-mingw.tar.gz" + local archive_name="SDL3-devel-${SDL3_VERSION}-mingw.tar.gz" ;; windows-*-msvc) - local archive_name="SDL2-devel-${SDL2_VERSION}-VC.zip" + local archive_name="SDL3-devel-${SDL3_VERSION}-VC.zip" ;; macos-*-*) - local archive_name="SDL2-${SDL2_VERSION}.dmg" + local archive_name="SDL3-${SDL3_VERSION}.dmg" ;; *) - local archive_name="SDL2-${SDL2_VERSION}.tar.gz" + local archive_name="SDL3-${SDL3_VERSION}.tar.gz" ;; esac - download_extract sdl2 "${archive_name}" \ - "${SDL2_BASEURL}/${archive_name}" \ - "https://github.com/libsdl-org/SDL/releases/download/release-${SDL2_VERSION}/${archive_name}" + download_extract sdl3 "${archive_name}" \ + "${SDL3_BASEURL}/${archive_name}" \ + "https://github.com/libsdl-org/SDL/releases/download/release-${SDL3_VERSION}/${archive_name}" "${download_only}" && return @@ -337,45 +445,41 @@ build_sdl2() { windows-*-mingw) cd "${dir_name}" cp -rv "${HOST}"/* "${PREFIX}/" + rm "${PREFIX}/lib/libSDL3_test.a" + rm "${PREFIX}/lib/cmake/SDL3/SDL3testTargets"*.cmake ;; windows-*-msvc) cd "${dir_name}" - mkdir -p "${PREFIX}/SDL2/cmake" - cp "cmake/"* "${PREFIX}/SDL2/cmake" - mkdir -p "${PREFIX}/SDL2/include" - cp "include/"* "${PREFIX}/SDL2/include" + mkdir -p "${PREFIX}/SDL3/cmake" + cp "cmake/"* "${PREFIX}/SDL3/cmake" + mkdir -p "${PREFIX}/SDL3/include/SDL3" + cp "include/SDL3/"* "${PREFIX}/SDL3/include/SDL3" case "${PLATFORM}" in *-i686-*) - local sdl2_lib_dir='lib/x86' + local sdl3_lib_dir='lib/x86' ;; *-amd64-*) - local sdl2_lib_dir='lib/x64' + local sdl3_lib_dir='lib/x64' ;; *) - log ERROR 'Unsupported platform for SDL2' + log ERROR 'Unsupported platform for SDL3' ;; esac - mkdir -p "${PREFIX}/SDL2/${sdl2_lib_dir}" - cp "${sdl2_lib_dir}/"{SDL2.lib,SDL2main.lib} "${PREFIX}/SDL2/${sdl2_lib_dir}" - cp "${sdl2_lib_dir}/"*.dll "${PREFIX}/SDL2/${sdl2_lib_dir}" + mkdir -p "${PREFIX}/SDL3/${sdl3_lib_dir}" + cp "${sdl3_lib_dir}/SDL3.lib" "${PREFIX}/SDL3/${sdl3_lib_dir}" + cp "${sdl3_lib_dir}/"*.dll "${PREFIX}/SDL3/${sdl3_lib_dir}" ;; macos-*-*) - rm -rf "${PREFIX}/SDL2.framework" - cp -R "SDL2.framework" "${PREFIX}" + cp -R "SDL3.xcframework/macos-arm64_x86_64/SDL3.framework" "${PREFIX}/lib" ;; *) cd "${dir_name}" - # The default -O3 is dropped when there's user-provided CFLAGS. - CFLAGS="${CFLAGS} -O3" ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" "${CONFIGURE_SHARED[@]}" - make - make install - # Workaround for an SDL2 CMake bug, we need to provide - # a bin/ directory even when nothing is used from it. - mkdir -p "${PREFIX}/bin" - # We don't keep empty folders. - touch "${PREFIX}/bin/.keep" + + cmake_build \ + -DSDL_TEST_LIBRARY=OFF \ + -DSDL_AUDIO=OFF ;; esac } @@ -392,33 +496,54 @@ build_glew() { "${download_only}" && return cd "${dir_name}" + + local glew_env=(LDFLAGS.EXTRA="${LDFLAGS}") + local glew_options=(GLEW_DEST="${PREFIX}" CC="${CC}" AR="${AR}" STRIP="${STRIP}") + + case "${PLATFORM}" in + windows-*-*) + glew_env+=(CFLAGS.EXTRA="${CFLAGS}") + glew_options+=(SYSTEM="linux-mingw${BITNESS}" LD="${LD}" AR="${AR}" RANLIB="${RANLIB}") + ;; + macos-*-*) + glew_env+=(CFLAGS.EXTRA="${CFLAGS} -dynamic -fno-common") + glew_options+=(SYSTEM=darwin LD="${CC}") + ;; + linux-*-*) + glew_env+=(CFLAGS.EXTRA="${CFLAGS}") + glew_options+=(LIBDIR="${PREFIX}/lib" LD="${CC}") + ;; + *) + log ERROR 'Unsupported platform for GLEW' + ;; + esac + # env hack: CFLAGS.EXTRA is populated with some flags, which are sometimess necessary for # compilation, in the makefile with +=. If CFLAGS.EXTRA is set on the command line, those # += will be ignored. But if it is set via the environment, the two sources are actually # concatenated how we would like. Bash doesn't allow variables with a dot so use env. # The hack doesn't work on Mac's ancient Make (the env var has no effect), so we have to # manually re-add the required flags there. + case "${PLATFORM}" in + macos-*-*) + make "${glew_env[@]}" "${glew_options[@]}" + make install "${glew_env[@]}" "${glew_options[@]}" + ;; + *) + env "${glew_env[@]}" make "${glew_options[@]}" + env "${glew_env[@]}" make install "${glew_options[@]}" + ;; + esac + case "${PLATFORM}" in windows-*-*) - env CFLAGS.EXTRA="${CFLAGS}" LDFLAGS.EXTRA="${LDFLAGS}" make SYSTEM="linux-mingw${BITNESS}" GLEW_DEST="${PREFIX}" CC="${CC}" AR="${AR}" RANLIB="${RANLIB}" STRIP="${HOST}-strip" LD="${LD}" - env CFLAGS.EXTRA="${CFLAGS}" LDFLAGS.EXTRA="${LDFLAGS}" make install SYSTEM="linux-mingw${BITNESS}" GLEW_DEST="${PREFIX}" CC="${CC}" AR="${AR}" RANLIB="${RANLIB}" STRIP="${HOST}-strip" LD="${LD}" mv "${PREFIX}/lib/glew32.dll" "${PREFIX}/bin/" rm "${PREFIX}/lib/libglew32.a" cp lib/libglew32.dll.a "${PREFIX}/lib/" ;; macos-*-*) - make SYSTEM=darwin GLEW_DEST="${PREFIX}" CC="${CC}" LD="${CC}" CFLAGS.EXTRA="${CFLAGS} -dynamic -fno-common" LDFLAGS.EXTRA="${LDFLAGS}" - make install SYSTEM=darwin GLEW_DEST="${PREFIX}" CC="${CC}" LD="${CC}" CFLAGS.EXTRA="${CFLAGS} -dynamic -fno-common" LDFLAGS.EXTRA="${LDFLAGS}" install_name_tool -id "@rpath/libGLEW.${GLEW_VERSION}.dylib" "${PREFIX}/lib/libGLEW.${GLEW_VERSION}.dylib" ;; - linux-*-*) - local strip="${HOST/-unknown-/-}-strip" - env CFLAGS.EXTRA="${CFLAGS}" LDFLAGS.EXTRA="${LDFLAGS}" make GLEW_DEST="${PREFIX}" CC="${CC}" LD="${CC}" STRIP="${strip}" - env CFLAGS.EXTRA="${CFLAGS}" LDFLAGS.EXTRA="${LDFLAGS}" make install GLEW_DEST="${PREFIX}" CC="${CC}" LD="${CC}" LIBDIR="${PREFIX}/lib" - ;; - *) - log ERROR 'Unsupported platform for GLEW' - ;; esac } @@ -433,10 +558,10 @@ build_png() { "${download_only}" && return cd "${dir_name}" - # The default -O2 is dropped when there's user-provided CFLAGS. - CFLAGS="${CFLAGS} -O2" ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" "${CONFIGURE_SHARED[@]}" - make - make install + + configure_build \ + --disable-tests \ + --disable-tools } # Build JPEG @@ -445,7 +570,7 @@ build_jpeg() { local archive_name="${dir_name}.tar.gz" download_extract jpeg "${archive_name}" \ - "${JPEG_BASEURL}/${JPEG_VERSION}/${archive_name}" + "${JPEG_BASEURL}/download/${JPEG_VERSION}/${archive_name}" "${download_only}" && return @@ -460,13 +585,15 @@ build_jpeg() { local SYSTEM_NAME='Linux' ;; *) - # Other platforms can build but we need need to explicitly + # Other platforms can build but we need to explicitly # set CMAKE_SYSTEM_NAME for CMAKE_CROSSCOMPILING to be set # and CMAKE_SYSTEM_PROCESSOR to not be ignored by cmake. log ERROR 'Unsupported platform for JPEG' ;; esac + local jpeg_cmake_args=(-DREQUIRE_SIMD=ON) + case "${PLATFORM}" in *-amd64-*) local SYSTEM_PROCESSOR='x86_64' @@ -480,34 +607,43 @@ build_jpeg() { ;; *-arm64-*) local SYSTEM_PROCESSOR='aarch64' + jpeg_cmake_args+=(-DNEON_INTRINSICS=ON) ;; *-armhf-*) local SYSTEM_PROCESSOR='arm' + jpeg_cmake_args+=(-DNEON_INTRINSICS=ON) ;; *) log ERROR 'Unsupported platform for JPEG' ;; esac - local jpeg_cmake_call=(cmake -S . -B build -DCMAKE_INSTALL_PREFIX="${PREFIX}" \ - -DCMAKE_C_FLAGS="${CFLAGS}" -DCMAKE_CXX_FLAGS="${CXXFLAGS}" \ - -DCMAKE_SYSTEM_NAME="${SYSTEM_NAME}" -DCMAKE_SYSTEM_PROCESSOR="${SYSTEM_PROCESSOR}" \ - -DWITH_JPEG8=1) - - cd "${dir_name}" case "${PLATFORM}" in - windows-*-mingw) - "${jpeg_cmake_call[@]}" -DCMAKE_TOOLCHAIN_FILE="${SCRIPT_DIR}/../cmake/cross-toolchain-mingw${BITNESS}.cmake" -DENABLE_SHARED=0 - ;; - windows-*-msvc) - "${jpeg_cmake_call[@]}" -DCMAKE_TOOLCHAIN_FILE="${SCRIPT_DIR}/../cmake/cross-toolchain-mingw${BITNESS}.cmake" -DENABLE_SHARED=1 + windows-*-*) ;; *) - "${jpeg_cmake_call[@]}" -DENABLE_SHARED=0 + # Workaround for: undefined reference to `log10' + # The CMakeLists.txt file only does -lm if UNIX, + # but UNIX may not be true on Linux. + jpeg_cmake_args+=(-DUNIX=True) ;; esac - make -C build - make -C build install + + cd "${dir_name}" + + # -DHAVE_THREAD_LOCAL=0 overrides the compiler test to avoid the silly thread_local variable, + # which causes a libwinpthread dependency on Windows. + cmake_build \ + -DHAVE_THREAD_LOCAL=0 \ + -DENABLE_SHARED="${LIBS_SHARED}" \ + -DENABLE_STATIC="${LIBS_STATIC}" \ + -DCMAKE_SYSTEM_NAME="${SYSTEM_NAME}" \ + -DCMAKE_SYSTEM_PROCESSOR="${SYSTEM_PROCESSOR}" \ + -DWITH_JPEG8=1 \ + -DWITH_TURBOJPEG=0 \ + "${jpeg_cmake_args[@]}" + + rm -r "${PREFIX}/lib/cmake/libjpeg-turbo" } # Build WebP @@ -521,64 +657,115 @@ build_webp() { "${download_only}" && return cd "${dir_name}" - # The default -O2 is dropped when there's user-provided CFLAGS. - CFLAGS="${CFLAGS} -O2" ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" --disable-libwebpdemux "${CONFIGURE_SHARED[@]}" - make - make install + + # WEBP_LINK_STATIC is ON by default + + cmake_build \ + -DWEBP_BUILD_ANIM_UTILS=OFF \ + -DWEBP_BUILD_CWEBP=OFF \ + -DWEBP_BUILD_DWEBP=OFF \ + -DWEBP_BUILD_EXTRAS=OFF \ + -DWEBP_BUILD_GIF2WEBP=OFF \ + -DWEBP_BUILD_IMG2WEBP=OFF \ + -DWEBP_BUILD_LIBWEBPMUX=OFF \ + -DWEBP_BUILD_VWEBP=OFF \ + -DWEBP_BUILD_WEBPINFO=OFF \ + -DWEBP_BUILD_WEBPMUX=OFF \ + -DWEBP_ENABLE_SIMD=ON \ + -DWEBP_USE_THREAD=OFF } # Build OpenAL build_openal() { + # On OpenAL website, Windows binaries are on: + # https://openal-soft.org/openal-binaries/openal-soft-1.24.3-bin.zip + # and sources are on: + # https://openal-soft.org/openal-releases/openal-soft-1.24.3.tar.bz2 + + # But on GitHub Windows binaries are on: + # https://github.com/kcat/openal-soft/releases/download/1.24.3/openal-soft-1.24.3-bin.zip + # and sources are on: + # https://github.com/kcat/openal-soft/archive/refs/tags/1.24.3.tar.gz + + # They contain the same content, but GitHub is more reliable so we use the tar.gz archive. + # We mirror it as openal-soft-1.24.3.tar.gz for convenience. + + # There is no tar.bz2 uploaded to GitHub anymore, so we cannot use GitHub as a mirror + # for the OpenAL website. + case "${PLATFORM}" in windows-*-*) local dir_name="openal-soft-${OPENAL_VERSION}-bin" local archive_name="${dir_name}.zip" - ;; - macos-*-*|linux-*-*) - local dir_name="openal-soft-${OPENAL_VERSION}" - local archive_name="${dir_name}.tar.bz2" - local openal_cmake_call=(cmake -S . -B . -DCMAKE_INSTALL_PREFIX="${PREFIX}" \ - -DCMAKE_C_FLAGS="${CFLAGS}" -DCMAKE_CXX_FLAGS="${CXXFLAGS}" \ - -DCMAKE_BUILD_TYPE=Release -DALSOFT_EXAMPLES=OFF) + local github_archive_name="${archive_name}" + local github_subdir='releases/download' ;; *) - log ERROR 'Unsupported platform for OpenAL' + local dir_name="openal-soft-${OPENAL_VERSION}" + local archive_name="${dir_name}.tar.gz" + local github_archive_name="${OPENAL_VERSION}.tar.gz" + local github_subdir='archive/refs/tags' ;; esac download_extract openal "${archive_name}" \ - "${OPENAL_BASEURL}/${archive_name}" \ - "https://github.com/kcat/openal-soft/releases/download/${OPENAL_VERSION}/${archive_name}" \ + "${OPENAL_BASEURL}/${github_subdir}/${OPENAL_VERSION}/${github_archive_name}" "${download_only}" && return + local openal_cmake_args=(-DALSOFT_EXAMPLES=OFF -DALSOFT_BACKEND_SNDIO=OFF) + + case "${PLATFORM}" in + *-i686-*|*-amd64-*) + openal_cmake_args+=(-DALSOFT_CPUEXT_SSE=ON -DALSOFT_REQUIRE_SSE=ON) + openal_cmake_args+=(-DALSOFT_CPUEXT_SSE2=ON -DALSOFT_REQUIRE_SSE2=ON) + openal_cmake_args+=(-DALSOFT_CPUEXT_SSE3=ON -DALSOFT_REQUIRE_SSE3=OFF) + openal_cmake_args+=(-DALSOFT_CPUEXT_SSE4_1=ON -DALSOFT_REQUIRE_SSE4_1=OFF) + ;; + *-armhf-*|*-arm64-*) + openal_cmake_args+=(-DALSOFT_CPUEXT_NEON=ON -DALSOFT_REQUIRE_NEON=ON) + ;; + esac + + case "${PLATFORM}" in + windows-i686-*) + local openal_win_dir='Win32' + ;; + windows-amd64-*) + local openal_win_dir='Win64' + ;; + macos-*-*) + openal_cmake_args+=(-DLIBTYPE=SHARED) + ;; + *) + if [ "${LIBS_SHARED}" = 'ON' ] + then + openal_cmake_args+=(-DLIBTYPE=SHARED) + elif [ "${LIBS_STATIC}" = 'ON' ] + then + openal_cmake_args+=(-DLIBTYPE=STATIC) + fi + ;; + esac + case "${PLATFORM}" in windows-*-*) cd "${dir_name}" cp -r "include/AL" "${PREFIX}/include" - case "${PLATFORM}" in - *-i686-*) - cp "libs/Win32/libOpenAL32.dll.a" "${PREFIX}/lib" - cp "bin/Win32/soft_oal.dll" "${PREFIX}/bin/OpenAL32.dll" - ;; - *-amd64-*) - cp "libs/Win64/libOpenAL32.dll.a" "${PREFIX}/lib" - cp "bin/Win64/soft_oal.dll" "${PREFIX}/bin/OpenAL32.dll" - ;; - esac + cp "libs/${openal_win_dir}/libOpenAL32.dll.a" "${PREFIX}/lib" + cp "bin/${openal_win_dir}/soft_oal.dll" "${PREFIX}/bin/OpenAL32.dll" ;; - macos-*-*) + *) cd "${dir_name}" - "${openal_cmake_call[@]}" - make - make install - install_name_tool -id "@rpath/libopenal.${OPENAL_VERSION}.dylib" "${PREFIX}/lib/libopenal.${OPENAL_VERSION}.dylib" + + cmake_build \ + "${openal_cmake_args[@]}" ;; - linux-*-*) - cd "${dir_name}" - "${openal_cmake_call[@]}" -DLIBTYPE=STATIC - make - make install + esac + + case "${PLATFORM}" in + macos-*-*) + install_name_tool -id "@rpath/libopenal.${OPENAL_VERSION}.dylib" "${PREFIX}/lib/libopenal.${OPENAL_VERSION}.dylib" ;; esac } @@ -594,13 +781,12 @@ build_ogg() { "${download_only}" && return cd "${dir_name}" + # This header breaks the vorbis and opusfile Mac builds cat <(echo '#include ') include/ogg/os_types.h > os_types.tmp mv os_types.tmp include/ogg/os_types.h - # The user-provided CFLAGS doesn't drop the default -O2 - ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" "${CONFIGURE_SHARED[@]}" - make - make install + + configure_build } # Build Vorbis @@ -614,10 +800,18 @@ build_vorbis() { "${download_only}" && return cd "${dir_name}" - # The user-provided CFLAGS doesn't drop the default -O3 - ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" "${CONFIGURE_SHARED[@]}" --disable-examples - make - make install + + case "${PLATFORM}" in + windows-*-msvc) + # Workaround a build issue on MinGW: + # See: https://github.com/microsoft/vcpkg/issues/22990 + # and: https://github.com/microsoft/vcpkg/pull/23761 + ls win32/vorbis.def win32/vorbisenc.def win32/vorbisfile.def \ + | xargs -I{} -P3 sed -e 's/LIBRARY//' -i {} + ;; + esac + + cmake_build } # Build Opus @@ -630,19 +824,35 @@ build_opus() { "${download_only}" && return - cd "${dir_name}" - # The default -O2 is dropped when there's user-provided CFLAGS. + local opus_cmake_args=() + case "${PLATFORM}" in windows-*-*) - # With MinGW _FORTIFY_SOURCE (added by configure) can only by used with -fstack-protector enabled. - CFLAGS="${CFLAGS} -O2 -D_FORTIFY_SOURCE=0" ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" "${CONFIGURE_SHARED[@]}" + # With MinGW, we would get this error: + # undefined reference to `__stack_chk_guard' + opus_cmake_args+=(-DOPUS_FORTIFY_SOURCE=OFF -DOPUS_STACK_PROTECTOR=OFF) ;; - *) - CFLAGS="${CFLAGS} -O2" ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" "${CONFIGURE_SHARED[@]}" + esac + + case "${PLATFORM}" in + *-i686-*|*-amd64-*) + opus_cmake_args+=(-DOPUS_X86_MAY_HAVE_SSE=OFF -DOPUS_X86_PRESUME_SSE=ON) + opus_cmake_args+=(-DOPUS_X86_MAY_HAVE_SSE2=OFF -DOPUS_X86_PRESUME_SSE2=ON) + opus_cmake_args+=(-DOPUS_X86_MAY_HAVE_SSE4_1=OFF -DOPUS_X86_PRESUME_SSE4_1=OFF) + opus_cmake_args+=(-DOPUS_X86_MAY_HAVE_AVX2=OFF -DOPUS_X86_PRESUME_AVX2=OFF) + ;; + *-armhf-*|*-arm64-*) + opus_cmake_args+=(-DOPUS_MAY_HAVE_NEON=OFF -DOPUS_PRESUME_NEON=ON) ;; esac - make - make install + + cd "${dir_name}" + + cmake_build \ + -DOPUS_BUILD_PROGRAMS=OFF \ + -DOPUS_BUILD_TESTING=OFF \ + -DOPUS_FLOAT_APPROX=ON \ + "${opus_cmake_args[@]}" } # Build OpusFile @@ -656,10 +866,9 @@ build_opusfile() { "${download_only}" && return cd "${dir_name}" - # The default -O2 is dropped when there's user-provided CFLAGS. - CFLAGS="${CFLAGS} -O2" ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" "${CONFIGURE_SHARED[@]}" --disable-http - make - make install + + configure_build \ + --disable-http } # Build ncurses @@ -674,11 +883,16 @@ build_ncurses() { "${download_only}" && return cd "${dir_name}" - # The default -O2 is dropped when there's user-provided CFLAGS. + + # Brutally disable writing to database + cp /dev/null misc/run_tic.in # Configure terminfo search dirs based on the ones used in Debian. By default it will only look in (only) the install directory. - CFLAGS="${CFLAGS} -O2" ./configure --host="${HOST}" --prefix="${PREFIX}" --libdir="${PREFIX}/lib" --enable-widec "${CONFIGURE_SHARED[@]}" --with-terminfo-dirs=/etc/terminfo:/lib/terminfo --with-default-terminfo-dir=/usr/share/terminfo - make - make install + configure_build \ + --with-strip-program="${STRIP}" \ + --without-progs \ + --enable-widec \ + --with-terminfo-dirs=/etc/terminfo:/lib/terminfo \ + --with-default-terminfo-dir=/usr/share/terminfo } # "Builds" (downloads) the WASI SDK @@ -984,7 +1198,7 @@ build_install() { rm -rf "${PKG_PREFIX}/def" rm -rf "${PKG_PREFIX}/share" rm -rf "${PKG_PREFIX}/lib/pkgconfig" - find "${PKG_PREFIX}/bin" -not -type d -not -name '*.dll' -not -name '.keep' -execdir rm -f -- {} \; + find "${PKG_PREFIX}/bin" -not -type d -not -name '*.dll' -not -execdir rm -f -- {} \; find "${PKG_PREFIX}/lib" -name '*.la' -execdir rm -f -- {} \; find "${PKG_PREFIX}/lib" -name '*.dll.a' -execdir bash -c 'rm -f -- "$(basename "{}" .dll.a).a"' \; find "${PKG_PREFIX}/lib" -name '*.dylib' -execdir bash -c 'rm -f -- "$(basename "{}" .dylib).a"' \; @@ -992,21 +1206,52 @@ build_install() { # Strip libraries case "${PLATFORM}" in windows-*-mingw) - find "${PKG_PREFIX}/bin" -name '*.dll' -execdir "${HOST}-strip" --strip-unneeded -- {} \; - find "${PKG_PREFIX}/lib" -name '*.a' -execdir "${HOST}-strip" --strip-unneeded -- {} \; + find "${PKG_PREFIX}/bin" -name '*.dll' -execdir "${STRIP}" --strip-unneeded -- {} \; + find "${PKG_PREFIX}/lib" -name '*.a' -execdir "${STRIP}" --strip-unneeded -- {} \; ;; windows-*-msvc) - find "${PKG_PREFIX}/bin" -name '*.dll' -execdir "${HOST}-strip" --strip-unneeded -- {} \; + find "${PKG_PREFIX}/bin" -name '*.dll' -execdir "${STRIP}" --strip-unneeded -- {} \; find "${PKG_PREFIX}/lib" -name '*.a' -execdir rm -f -- {} \; find "${PKG_PREFIX}/lib" -name '*.exp' -execdir rm -f -- {} \; # Fix import lib paths to use MSVC-style instead of MinGW ones (see 'genlib' target) find "${PKG_PREFIX}/lib/cmake" -name '*.cmake' -execdir sed -i -E 's@[.]dll[.]a\b@.lib@g' {} \; ;; + linux-*-*) + find "${PKG_PREFIX}/lib" -name '*.so' -execdir rm -f -- {} \; + find "${PKG_PREFIX}/lib" -name '*.so.*' -execdir rm -f -- {} \; + find "${PKG_PREFIX}/lib" -name '*_g.a' -execdir rm -f -- {} \; + + find "${PKG_PREFIX}/lib" -name '*.a' -execdir "${STRIP}" --strip-unneeded -- {} \; + ;; + macos-*-*) + find "${PKG_PREFIX}/lib" -name '*.a' -execdir "${STRIP}" -u {} \; + esac + + case "${PLATFORM}" in + windows-*-*) + # CMake looks for libSDL3.a and aborts if missing if this file exists: + rm -rf "${PKG_PREFIX}/lib/cmake/SDL3/SDL3staticTargets.cmake" + ;; esac # Remove empty directories find "${PKG_PREFIX}/" -mindepth 1 -type d -empty -delete + + # Check for unwanted embedded paths with a couple of exemptions: + # - Opus's release mode assertions - https://github.com/xiph/opus/issues/399 + # - SDL3 has a small number of renderer backend filenames embedded for some reason + local good=true + for f in $(find "${PKG_PREFIX}" -type f -not -name '*libopus*' -not -name 'libSDL3.a'); do + if grep --with-filename external_deps "${f}" + then + good=false + elif [[ $? != 1 ]] + then + exit $? # grep errored + fi + done + "${good}" || log ERROR 'Absolute paths found embedded in install. This may be due to builds with debug symbols or config files that need to be fixed.' } # Create a redistributable package for the dependencies @@ -1031,23 +1276,26 @@ build_wipe() { # Common setup code common_setup() { HOST="${2}" + "common_setup_${1}" common_setup_arch + DOWNLOAD_DIR="${WORK_DIR}/download_cache" PKG_BASEDIR="${PLATFORM}_${DEPS_VERSION}" BUILD_BASEDIR="build-${PKG_BASEDIR}" BUILD_DIR="${WORK_DIR}/${BUILD_BASEDIR}" PREFIX="${BUILD_DIR}/prefix" PATH="${PREFIX}/bin:${PATH}" - PKG_CONFIG="pkg-config" - PKG_CONFIG_PATH="${PREFIX}/lib/pkgconfig" + PKG_CONFIG_PATH="${PREFIX}/lib/pkgconfig:${CROSS_PKG_CONFIG_PATH}" CPPFLAGS+=" -I${PREFIX}/include" LDFLAGS+=" -L${PREFIX}/lib" + mkdir -p "${DOWNLOAD_DIR}" mkdir -p "${PREFIX}/bin" mkdir -p "${PREFIX}/include" mkdir -p "${PREFIX}/lib" - export CC CXX LD AR RANLIB PKG_CONFIG PKG_CONFIG_PATH PATH CFLAGS CXXFLAGS CPPFLAGS LDFLAGS + + export CC CXX LD AR RANLIB STRIP PKG_CONFIG PKG_CONFIG_PATH PATH CFLAGS CXXFLAGS CPPFLAGS LDFLAGS } common_setup_arch() { @@ -1082,29 +1330,34 @@ common_setup_arch() { # Lua does use this one, which results in compiler warnings. But this is OK because # the Windows build of Lua is only used in developer gamelogic builds, and Microsoft # supports %lld since Visual Studio 2013. Also we don't build Lua anymore. -common_setup_msvc() { - CONFIGURE_SHARED=(--enable-shared --disable-static) - # Libtool bug prevents -static-libgcc from being set in LDFLAGS - CC="${HOST}-gcc -static-libgcc" - CXX="${HOST}-g++ -static-libgcc" +common_setup_windows() { + STRIP="${HOST}-strip" LD="${HOST}-ld" AR="${HOST}-ar" RANLIB="${HOST}-ranlib" CFLAGS+=' -D__USE_MINGW_ANSI_STDIO=0' + CMAKE_TOOLCHAIN="${SCRIPT_DIR}/../cmake/cross-toolchain-mingw${BITNESS}.cmake" +} + +common_setup_msvc() { + LIBS_SHARED='ON' + LIBS_STATIC='OFF' + # Libtool bug prevents -static-libgcc from being set in LDFLAGS + CC="${HOST}-gcc -static-libgcc" + CXX="${HOST}-g++ -static-libgcc" + common_setup_windows } common_setup_mingw() { CC="${HOST}-gcc" CXX="${HOST}-g++" - LD="${HOST}-ld" - AR="${HOST}-ar" - RANLIB="${HOST}-ranlib" - CFLAGS+=' -D__USE_MINGW_ANSI_STDIO=0' + common_setup_windows } common_setup_macos() { CC='clang' CXX='clang++' + STRIP='strip' CFLAGS+=" -arch ${MACOS_ARCH}" CXXFLAGS+=" -arch ${MACOS_ARCH}" LDFLAGS+=" -arch ${MACOS_ARCH}" @@ -1114,6 +1367,8 @@ common_setup_macos() { common_setup_linux() { CC="${HOST/-unknown-/-}-gcc" CXX="${HOST/-unknown-/-}-g++" + STRIP="${HOST/-unknown-/-}-strip" + CROSS_PKG_CONFIG_PATH="/usr/lib/${HOST/-unknown-/-}/pkgconfig" CFLAGS+=' -fPIC' CXXFLAGS+=' -fPIC' } @@ -1147,7 +1402,8 @@ setup_windows-amd64-mingw() { # Set up environment for 64-bit amd64 macOS setup_macos-amd64-default() { MACOS_ARCH=x86_64 - export MACOSX_DEPLOYMENT_TARGET=10.12 # works with CMake + # OpenAL requires 10.14. + export MACOSX_DEPLOYMENT_TARGET=10.14 # works with CMake common_setup macos x86_64-apple-darwin11 } @@ -1171,29 +1427,29 @@ setup_linux-arm64-default() { common_setup linux aarch64-unknown-linux-gnu } -base_windows_amd64_msvc_packages='zlib gmp nettle curl sdl2 glew png jpeg webp openal ogg vorbis opus opusfile naclsdk depcheck genlib' +base_windows_amd64_msvc_packages='zlib gmp nettle curl sdl3 glew png jpeg webp openal ogg vorbis opus opusfile naclsdk depcheck genlib' all_windows_amd64_msvc_packages="${base_windows_amd64_msvc_packages}" base_windows_i686_msvc_packages="${base_windows_amd64_msvc_packages}" all_windows_i686_msvc_packages="${base_windows_amd64_msvc_packages}" -base_windows_amd64_mingw_packages='zlib gmp nettle curl sdl2 glew png jpeg webp openal ogg vorbis opus opusfile naclsdk depcheck' +base_windows_amd64_mingw_packages='zlib gmp nettle curl sdl3 glew png jpeg webp openal ogg vorbis opus opusfile naclsdk depcheck' all_windows_amd64_mingw_packages="${base_windows_amd64_mingw_packages}" base_windows_i686_mingw_packages="${base_windows_amd64_mingw_packages}" all_windows_i686_mingw_packages="${base_windows_amd64_mingw_packages}" -base_macos_amd64_default_packages='pkgconfig nasm gmp nettle sdl2 glew png jpeg webp openal ogg vorbis opus opusfile naclsdk' +base_macos_amd64_default_packages='pkgconfig nasm gmp nettle sdl3 glew png jpeg webp openal ogg vorbis opus opusfile naclsdk' all_macos_amd64_default_packages="${base_macos_amd64_default_packages}" -base_linux_amd64_default_packages='naclsdk naclruntime' -all_linux_amd64_default_packages='zlib gmp nettle curl sdl2 glew png jpeg webp openal ogg vorbis opus opusfile naclsdk naclruntime' +base_linux_i686_default_packages='sdl3 naclsdk' +all_linux_i686_default_packages='zlib gmp nettle curl sdl3 glew png jpeg webp openal ogg vorbis opus opusfile ncurses naclsdk' -base_linux_i686_default_packages="${base_linux_amd64_default_packages}" -all_linux_i686_default_packages="${all_linux_amd64_default_packages}" +base_linux_amd64_default_packages="${base_linux_i686_default_packages} naclruntime" +all_linux_amd64_default_packages="${all_linux_i686_default_packages} naclruntime" -base_linux_arm64_default_packages='naclsdk' -all_linux_arm64_default_packages='zlib gmp nettle curl sdl2 glew png jpeg webp openal ogg vorbis opus opusfile naclsdk' +base_linux_arm64_default_packages='sdl3 naclsdk' +all_linux_arm64_default_packages='zlib gmp nettle curl sdl3 glew png jpeg webp openal ogg vorbis opus opusfile ncurses naclsdk' base_linux_armhf_default_packages="${base_linux_arm64_default_packages}" all_linux_armhf_default_packages="${all_linux_arm64_default_packages}" @@ -1221,7 +1477,7 @@ errorHelp() { \tbuild-macos — platforms buildable on macos: ${macos_build_platforms} Packages: - \tpkgconfig nasm zlib gmp nettle curl sdl2 glew png jpeg webp openal ogg vorbis opus opusfile naclsdk wasisdk wasmtime + \tpkgconfig nasm zlib gmp nettle curl sdl3 glew png jpeg webp openal ogg vorbis opus opusfile naclsdk wasisdk wasmtime Virtual packages: \tbase — build packages for pre-built binaries to be downloaded when building the game @@ -1247,10 +1503,13 @@ errorHelp() { \tall: same linux-amd64-default: - linux-i686-default: \tbase: ${base_linux_amd64_default_packages} \tall: ${all_linux_amd64_default_packages} + linux-i686-default: + \tbase: ${base_linux_i686_default_packages} + \tall: ${all_linux_i686_default_packages} + linux-arm64-default: linux-armhf-default: \tbase: ${base_linux_arm64_default_packages} @@ -1299,6 +1558,7 @@ CURL="$(command -v curl)" || log ERROR "Command 'curl' not found" # Enable parallel build export MAKEFLAGS="-j`nproc 2> /dev/null || sysctl -n hw.ncpu 2> /dev/null || echo 1`" export SCONSFLAGS="${MAKEFLAGS}" +export CMAKE_BUILD_PARALLEL_LEVEL="$(nproc 2> /dev/null || sysctl -n hw.ncpu 2> /dev/null || echo 1)" # Setup platform platform="${1}"; shift diff --git a/src.cmake b/src.cmake index df5b642eca..bfd5f352f6 100644 --- a/src.cmake +++ b/src.cmake @@ -356,6 +356,10 @@ set(CLIENTLIST ${RENDERERLIST} ) +if (APPLE) + set(CLIENTLIST ${CLIENTLIST} ${ENGINE_DIR}/sys/DisableAccentMenu.m) +endif() + set(CLIENTTESTLIST ${ENGINETESTLIST} ${ENGINE_DIR}/renderer/gl_shader_test.cpp ) diff --git a/src/engine/client/ClientApplication.cpp b/src/engine/client/ClientApplication.cpp index eef3888473..1e50ba4a5e 100644 --- a/src/engine/client/ClientApplication.cpp +++ b/src/engine/client/ClientApplication.cpp @@ -34,7 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "client.h" #if defined(_WIN32) || defined(BUILD_GRAPHICAL_CLIENT) -#include +#include #ifdef BUILD_GRAPHICAL_CLIENT extern SDL_Window *window; #else @@ -80,14 +80,6 @@ class ClientApplication : public Application { } void Initialize() override { -#if defined(_WIN32) && defined(BUILD_GRAPHICAL_CLIENT) - // If not set, the size of the screen may be determined incorrectly for r_mode -2. - // E.g. with 125% scaling on a 1920x1080 screen, the size will be determined to be 1536x864 - // and there will be blank space on the top and right. - // Don't set this for TTY applications as they really aren't DPI aware. Let them scale. - SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "system"); -#endif - #if defined(__linux__) && defined(BUILD_GRAPHICAL_CLIENT) // identify the game by its name in certain // volume control / power control applets, diff --git a/src/engine/client/cl_main.cpp b/src/engine/client/cl_main.cpp index d781aee3b3..c2ca2235ce 100644 --- a/src/engine/client/cl_main.cpp +++ b/src/engine/client/cl_main.cpp @@ -56,7 +56,7 @@ Maryland 20850 USA. #include #endif #ifdef BUILD_GRAPHICAL_CLIENT -#include +#include #endif #if defined(USE_MUMBLE) diff --git a/src/engine/client/dl_main.cpp b/src/engine/client/dl_main.cpp index d7775dea87..0044c64794 100644 --- a/src/engine/client/dl_main.cpp +++ b/src/engine/client/dl_main.cpp @@ -39,9 +39,6 @@ Maryland 20850 USA. #include "common/Common.h" -#ifdef __MINGW32__ -#define CURL_STATICLIB -#endif #include #include "common/FileSystem.h" diff --git a/src/engine/client/key_identification.cpp b/src/engine/client/key_identification.cpp index a1b152ffc6..66c499d571 100644 --- a/src/engine/client/key_identification.cpp +++ b/src/engine/client/key_identification.cpp @@ -36,8 +36,8 @@ Maryland 20850 USA. #include "key_identification.h" -#include -#include +#include +#include namespace Keyboard { @@ -129,7 +129,7 @@ static int ParseCharacter(Str::StringRef s) // for the key on a QWERTY layout, if possible. static Key KeyFromUnprefixedCharacter(int ch) { - SDL_Scancode sc = SDL_GetScancodeFromKey(static_cast(ch)); + SDL_Scancode sc = SDL_GetScancodeFromKey(static_cast(ch), nullptr); if (sc != SDL_SCANCODE_UNKNOWN) { return Key::FromScancode(sc); } @@ -215,7 +215,7 @@ std::string KeyToString(Key key) } int GetCharForScancode(int scancode) { - int keycode = static_cast(SDL_GetKeyFromScancode(Util::enum_cast(scancode))); + int keycode = static_cast(SDL_GetKeyFromScancode(Util::enum_cast(scancode), 0, false)); // The keycode is a "large" number for keys such as Shift if (MIN_PRINTABLE_ASCII <= keycode && keycode <= UNICODE_MAX_CODE_POINT) { return keycode; diff --git a/src/engine/framework/System.cpp b/src/engine/framework/System.cpp index 3b58d98d9c..2d66f2a791 100644 --- a/src/engine/framework/System.cpp +++ b/src/engine/framework/System.cpp @@ -41,7 +41,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #ifdef _WIN32 #include -#include +#include +#include #else #include #include diff --git a/src/engine/sys/DisableAccentMenu.m b/src/engine/sys/DisableAccentMenu.m new file mode 100644 index 0000000000..a8776cf3ce --- /dev/null +++ b/src/engine/sys/DisableAccentMenu.m @@ -0,0 +1,38 @@ +/* +=========================================================================== +Daemon BSD Source Code +Copyright (c) 2025, Daemon Developers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Daemon developers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +=========================================================================== +*/ + +#import + +void DisableAccentMenu() { + // Don't do this: https://forums.factorio.com/download/file.php?id=46540 + NSDictionary *appDefaults = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSNumber numberWithBool:NO], @"ApplePressAndHoldEnabled", nil]; + [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; +} diff --git a/src/engine/sys/sdl_glimp.cpp b/src/engine/sys/sdl_glimp.cpp index 46ed518d52..d76fa68028 100644 --- a/src/engine/sys/sdl_glimp.cpp +++ b/src/engine/sys/sdl_glimp.cpp @@ -21,10 +21,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "qcommon/q_shared.h" // Include before SDL.h due to M_PI issue... -#include +#include #ifdef USE_SMP -#include +#include #endif #include "renderer/tr_local.h" @@ -36,7 +36,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "sdl_icon.h" #pragma warning(pop) -#include "SDL_syswm.h" #include "framework/CommandSystem.h" #include "framework/CvarSystem.h" @@ -186,6 +185,7 @@ static Cvar::Cvar workaround_glHardware_intel_useFirstProvokinVertex( Cvar::NONE, true ); SDL_Window *window = nullptr; +static SDL_PropertiesID windowProperties; static SDL_GLContext glContext = nullptr; #ifdef USE_SMP @@ -214,9 +214,9 @@ SMP acceleration * thread-safe OpenGL libraries. */ -static SDL_mutex *smpMutex = nullptr; -static SDL_cond *renderCommandsEvent = nullptr; -static SDL_cond *renderCompletedEvent = nullptr; +static SDL_Mutex *smpMutex = nullptr; +static SDL_Condition *renderCommandsEvent = nullptr; +static SDL_Condition *renderCompletedEvent = nullptr; static void ( *renderThreadFunction )() = nullptr; static SDL_Thread *renderThread = nullptr; @@ -269,7 +269,7 @@ bool GLimp_SpawnRenderThread( void ( *function )() ) return false; } - renderCommandsEvent = SDL_CreateCond(); + renderCommandsEvent = SDL_CreateCondition(); if ( renderCommandsEvent == nullptr ) { @@ -278,7 +278,7 @@ bool GLimp_SpawnRenderThread( void ( *function )() ) return false; } - renderCompletedEvent = SDL_CreateCond(); + renderCompletedEvent = SDL_CreateCondition(); if ( renderCompletedEvent == nullptr ) { @@ -323,13 +323,13 @@ void GLimp_ShutdownRenderThread() if ( renderCommandsEvent != nullptr ) { - SDL_DestroyCond( renderCommandsEvent ); + SDL_DestroyCondition( renderCommandsEvent ); renderCommandsEvent = nullptr; } if ( renderCompletedEvent != nullptr ) { - SDL_DestroyCond( renderCompletedEvent ); + SDL_DestroyCondition( renderCompletedEvent ); renderCompletedEvent = nullptr; } @@ -356,11 +356,11 @@ void *GLimp_RendererSleep() smpDataReady = false; // after this, the front end can exit GLimp_FrontEndSleep - SDL_CondSignal( renderCompletedEvent ); + SDL_SignalCondition( renderCompletedEvent ); while ( !smpDataReady ) { - SDL_CondWait( renderCommandsEvent, smpMutex ); + SDL_WaitCondition( renderCommandsEvent, smpMutex ); } data = ( void * ) smpData; @@ -383,7 +383,7 @@ void GLimp_FrontEndSleep() { while ( smpData ) { - SDL_CondWait( renderCompletedEvent, smpMutex ); + SDL_WaitCondition( renderCompletedEvent, smpMutex ); } } SDL_UnlockMutex( smpMutex ); @@ -417,7 +417,7 @@ void GLimp_WakeRenderer( void *data ) smpDataReady = true; // after this, the renderer can continue through GLimp_RendererSleep - SDL_CondSignal( renderCommandsEvent ); + SDL_SignalCondition( renderCommandsEvent ); } SDL_UnlockMutex( smpMutex ); } @@ -472,7 +472,6 @@ enum class rserr_t }; cvar_t *r_allowResize; // make window resizable -cvar_t *r_centerWindow; cvar_t *r_displayIndex; cvar_t *r_sdlDriver; @@ -564,10 +563,8 @@ static void SetSwapInterval( int swapInterval ) About how to deal with errors: - > If an application requests adaptive vsync and the system - > does not support it, this function will fail and return -1. - > In such a case, you should probably retry the call with 1 - > for the interval. + > Returns true on success or false on failure; + > call SDL_GetError() for more information. > -- https://wiki.libsdl.org/SDL_GL_SetSwapInterval Given what's written in Swap Interval Khronos page, setting r_finish @@ -588,7 +585,7 @@ static void SetSwapInterval( int swapInterval ) int sign = swapInterval < 0 ? -1 : 1; int interval = std::abs( swapInterval ); - while ( SDL_GL_SetSwapInterval( sign * interval ) == -1 ) + while ( !SDL_GL_SetSwapInterval( sign * interval ) ) { if ( sign == -1 ) { @@ -655,55 +652,68 @@ GLimp_DetectAvailableModes */ static bool GLimp_DetectAvailableModes() { - char buf[ MAX_STRING_CHARS ] = { 0 }; - SDL_Rect modes[ 128 ]; - int numModes = 0; - int i; - SDL_DisplayMode windowMode; - int display; + constexpr int maxModes = 128; + SDL_Rect modes[ maxModes ]; - display = SDL_GetWindowDisplayIndex( window ); + SDL_DisplayID display = SDL_GetDisplayForWindow( window ); - if ( SDL_GetWindowDisplayMode( window, &windowMode ) < 0 ) + int allModes; + SDL_DisplayMode **displayModes = SDL_GetFullscreenDisplayModes( display, &allModes ); + + if ( !displayModes ) { - logger.Warn("Couldn't get window display mode: %s", SDL_GetError() ); - /* FIXME: returning true means the engine will crash if the window size is - larger than what the GPU can support, but we need to not fail to open a window - with a size the GPU can handle even if not using native screen resolutions. */ - return true; + Sys::Error( "Couldn't get display modes: %s", SDL_GetError() ); } - for ( i = 0; i < SDL_GetNumDisplayModes( display ); i++ ) + int numModes = 0; + for ( int i = 0; i < allModes; i++ ) { - SDL_DisplayMode mode; - - if ( SDL_GetDisplayMode( display, i, &mode ) < 0 ) - { - continue; - } + SDL_DisplayMode *mode = displayModes[ i ]; - if ( !mode.w || !mode.h ) + if ( !mode->w || !mode->h ) { + // FIXME is this really a thing? I don't see it in SDL2 or SDL3 documentation logger.Notice("Display supports any resolution" ); + SDL_free( displayModes ); return true; } - if ( windowMode.format != mode.format || windowMode.refresh_rate != mode.refresh_rate ) + if ( numModes == 0 ) + { + modes[ numModes ].w = mode->w; + modes[ numModes ].h = mode->h; + } + else { - continue; + if ( modes[ numModes - 1 ].w == mode->w + && modes[ numModes - 1 ].h == mode->h ) + { + continue; + } + + modes[ numModes ].w = mode->w; + modes[ numModes ].h = mode->h; } - modes[ numModes ].w = mode.w; - modes[ numModes ].h = mode.h; numModes++; + + if ( numModes == maxModes ) + { + logger.Warn( "More than %d modes", maxModes ); + break; + } } + SDL_free( displayModes ); + if ( numModes > 1 ) { qsort( modes, numModes, sizeof( SDL_Rect ), GLimp_CompareModes ); } - for ( i = 0; i < numModes; i++ ) + char buf[ MAX_STRING_CHARS ] = { 0 }; + + for ( int i = 0; i < numModes; i++ ) { const char *newModeString = va( "%ux%u ", modes[ i ].w, modes[ i ].h ); @@ -798,6 +808,15 @@ static void GLimp_SetAttributes( const glConfiguration &configuration ) } } +// Copied from https://github.com/libsdl-org/SDL/blob/main/docs/README-migration.md +static SDL_Surface *SDL_CreateRGBSurfaceFrom( + void *pixels, int width, int height, int depth, int pitch, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask ) +{ + return SDL_CreateSurfaceFrom( width, height, + SDL_GetPixelFormatForMasks( depth, Rmask, Gmask, Bmask, Amask ), + pixels, pitch); +} + static bool GLimp_CreateWindow( bool fullscreen, bool bordered, const glConfiguration &configuration ) { /* The requested attributes should be set before creating @@ -851,20 +870,36 @@ static bool GLimp_CreateWindow( bool fullscreen, bool bordered, const glConfigur } } - int x, y; - if ( r_centerWindow->integer ) + int numDisplays; + SDL_DisplayID *displayIDs = SDL_GetDisplays( &numDisplays ); + + if ( !displayIDs ) { - // center window on specified display - x = SDL_WINDOWPOS_CENTERED_DISPLAY( r_displayIndex->integer ); - y = SDL_WINDOWPOS_CENTERED_DISPLAY( r_displayIndex->integer ); + Sys::Error( "SDL_GetDisplays failed: %s\n", SDL_GetError() ); } - else + + SDL_DisplayID displayID = + r_displayIndex->integer < numDisplays || r_displayIndex->integer > numDisplays + ? displayIDs[ r_displayIndex->integer ] + : 0; // 0 implies primary display + + SDL_free( displayIDs ); + + int x = SDL_WINDOWPOS_CENTERED_DISPLAY( displayID ); + int y = SDL_WINDOWPOS_CENTERED_DISPLAY( displayID ); + + windowProperties = SDL_CreateProperties(); + if ( !windowProperties ) { - x = SDL_WINDOWPOS_UNDEFINED_DISPLAY( r_displayIndex->integer ); - y = SDL_WINDOWPOS_UNDEFINED_DISPLAY( r_displayIndex->integer ); + Sys::Error( "SDL_CreateProperties failed" ); } - - window = SDL_CreateWindow( CLIENT_WINDOW_TITLE, x, y, windowConfig.vidWidth, windowConfig.vidHeight, flags ); + SDL_SetStringProperty( windowProperties, SDL_PROP_WINDOW_CREATE_TITLE_STRING, CLIENT_WINDOW_TITLE ); + SDL_SetNumberProperty( windowProperties, SDL_PROP_WINDOW_CREATE_X_NUMBER, x ); + SDL_SetNumberProperty( windowProperties, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y ); + SDL_SetNumberProperty( windowProperties, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, windowConfig.vidWidth ); + SDL_SetNumberProperty( windowProperties, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, windowConfig.vidHeight ); + SDL_SetNumberProperty( windowProperties, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags ); + window = SDL_CreateWindowWithProperties( windowProperties ); if ( window ) { @@ -883,12 +918,14 @@ static bool GLimp_CreateWindow( bool fullscreen, bool bordered, const glConfigur windowType ? windowType : "", windowType ? " ": "" ); logger.Warn("SDL_CreateWindow failed: %s", SDL_GetError() ); + SDL_DestroyProperties( windowProperties ); + windowProperties = 0; return false; } SDL_SetWindowIcon( window, icon ); - SDL_FreeSurface( icon ); + SDL_DestroySurface( icon ); return true; } @@ -897,7 +934,7 @@ static void GLimp_DestroyContextIfExists() { if ( glContext != nullptr ) { - SDL_GL_DeleteContext( glContext ); + SDL_GL_DestroyContext( glContext ); glContext = nullptr; } } @@ -915,6 +952,8 @@ static void GLimp_DestroyWindowIfExists() logger.Debug("Destroying %d×%d SDL window at %d,%d", w, h, x, y ); SDL_DestroyWindow( window ); window = nullptr; + SDL_DestroyProperties( windowProperties ); + windowProperties = 0; } } @@ -1008,9 +1047,7 @@ static bool GLimp_RecreateWindowWhenChange( const bool fullscreen, const bool bo if ( bordered != currentBordered ) { - SDL_bool sdlBordered = bordered ? SDL_TRUE : SDL_FALSE; - - SDL_SetWindowBordered( window, sdlBordered ); + SDL_SetWindowBordered( window, bordered ); const char* windowType = bordered ? "bordered" : "borderless"; logger.Debug( "SDL window set as %s.", windowType ); @@ -1024,30 +1061,46 @@ static bool GLimp_RecreateWindowWhenChange( const bool fullscreen, const bool bo static rserr_t GLimp_SetModeAndResolution( const int mode ) { - SDL_DisplayMode desktopMode; + int numDisplays; + SDL_DisplayID *displayIDs = SDL_GetDisplays( &numDisplays ); + + if ( !displayIDs ) + { + Sys::Error( "SDL_GetDisplays failed: %s\n", SDL_GetError() ); + } + + if ( numDisplays <= 0 ) + { + Sys::Error( "SDL_GetDisplays returned 0 displays" ); + } + + SDL_DisplayID displayID = displayIDs[ Math::Clamp( r_displayIndex->integer, 0, numDisplays - 1 ) ]; + + SDL_free( displayIDs ); + + const SDL_DisplayMode *desktopMode = SDL_GetDesktopDisplayMode( displayID ); - if ( SDL_GetDesktopDisplayMode( r_displayIndex->integer, &desktopMode ) == 0 ) + if ( desktopMode ) { - windowConfig.displayAspect = ( float ) desktopMode.w / ( float ) desktopMode.h; + windowConfig.displayAspect = ( float ) desktopMode->w / ( float ) desktopMode->h; logger.Notice( "Display aspect: %.3f", windowConfig.displayAspect ); } else { - memset( &desktopMode, 0, sizeof( SDL_DisplayMode ) ); windowConfig.displayAspect = 1.333f; logger.Warn("Cannot determine display aspect, assuming %.3f: %s", windowConfig.displayAspect, SDL_GetError() ); } - windowConfig.displayWidth = desktopMode.w; - windowConfig.displayHeight = desktopMode.h; + windowConfig.displayWidth = desktopMode->w; + windowConfig.displayHeight = desktopMode->h; if ( mode == -2 ) { // use desktop video resolution - if ( desktopMode.h > 0 ) + if ( desktopMode->h > 0 ) { - windowConfig.vidWidth = desktopMode.w; - windowConfig.vidHeight = desktopMode.h; + windowConfig.vidWidth = desktopMode->w; + windowConfig.vidHeight = desktopMode->h; } else { @@ -1704,8 +1757,6 @@ GLimp_StartDriverAndSetMode */ static rserr_t GLimp_StartDriverAndSetMode( int mode, bool fullscreen, bool bordered ) { - int numDisplays; - #if !defined(_WIN32) && !defined(__APPLE__) /* Let X11 and Wayland desktops (Linux, FreeBSD…) associate the game window with the XDG .desktop file, with the proper name and icon. @@ -1723,11 +1774,18 @@ static rserr_t GLimp_StartDriverAndSetMode( int mode, bool fullscreen, bool bord if ( !SDL_WasInit( SDL_INIT_VIDEO ) ) { const char *driverName; - SDL_version v; - SDL_GetVersion( &v ); + + const int linked = SDL_GetVersion(); + const int compiled = SDL_VERSION; logger.Notice("SDL_Init( SDL_INIT_VIDEO )... " ); - logger.Notice("Using SDL version %u.%u.%u", v.major, v.minor, v.patch ); + logger.Notice("Using SDL version %d.%d.%d (compiled against SDL version %d.%d.%d)", + SDL_VERSIONNUM_MAJOR(linked), + SDL_VERSIONNUM_MINOR(linked), + SDL_VERSIONNUM_MICRO(linked), + SDL_VERSIONNUM_MAJOR(compiled), + SDL_VERSIONNUM_MINOR(compiled), + SDL_VERSIONNUM_MICRO(compiled)); /* It is recommended to test for negative value and not just -1. @@ -1739,7 +1797,7 @@ static rserr_t GLimp_StartDriverAndSetMode( int mode, bool fullscreen, bool bord > if (SDL_Init(SDL_INIT_EVERYTHING) < 0) > -- https://wiki.libsdl.org/SDL_GetError */ - if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) + if ( !SDL_Init( SDL_INIT_VIDEO ) ) { Sys::Error("SDL_Init( SDL_INIT_VIDEO ) failed: %s", SDL_GetError() ); } @@ -1755,11 +1813,12 @@ static rserr_t GLimp_StartDriverAndSetMode( int mode, bool fullscreen, bool bord Cvar_Set( "r_sdlDriver", driverName ); } - numDisplays = SDL_GetNumVideoDisplays(); + int numDisplays; + SDL_DisplayID *displayIDs = SDL_GetDisplays( &numDisplays ); - if ( numDisplays <= 0 ) + if ( !displayIDs ) { - Sys::Error( "SDL_GetNumVideoDisplays failed: %s\n", SDL_GetError() ); + Sys::Error( "SDL_GetDisplays failed: %s\n", SDL_GetError() ); } #if defined(DAEMON_OPENGL_ABI) @@ -1822,7 +1881,7 @@ static GLenum debugTypes[] = }; #ifdef _WIN32 -#define DEBUG_CALLBACK_CALL APIENTRY +#define DEBUG_CALLBACK_CALL __stdcall //APIENTRY #else #define DEBUG_CALLBACK_CALL #endif @@ -2638,7 +2697,6 @@ bool GLimp_Init() r_sdlDriver = Cvar_Get( "r_sdlDriver", "", CVAR_ROM ); r_allowResize = Cvar_Get( "r_allowResize", "0", CVAR_LATCH ); - r_centerWindow = Cvar_Get( "r_centerWindow", "0", 0 ); r_displayIndex = Cvar_Get( "r_displayIndex", "0", 0 ); Cvar::Latch( workaround_glDriver_amd_adrenalin_disableBindlessTexture ); @@ -2918,7 +2976,7 @@ void GLimp_HandleCvars() if ( Util::optional noBorder = r_noBorder.GetModifiedValue() ) { - SDL_bool bordered = *noBorder ? SDL_FALSE : SDL_TRUE; + bool bordered = !*noBorder; SDL_SetWindowBordered( window, bordered ); } diff --git a/src/engine/sys/sdl_input.cpp b/src/engine/sys/sdl_input.cpp index d04fd96f2d..c673794757 100644 --- a/src/engine/sys/sdl_input.cpp +++ b/src/engine/sys/sdl_input.cpp @@ -34,7 +34,7 @@ Maryland 20850 USA. #include "common/Common.h" -#include +#include #include "client/client.h" #include "client/key_identification.h" #include "qcommon/q_unicode.h" @@ -48,7 +48,7 @@ static Log::Logger mouseLog("client.mouse", ""); static cvar_t *in_keyboardDebug = nullptr; static SDL_Joystick *stick = nullptr; -static SDL_GameController *gamepad = nullptr; +static SDL_Gamepad *gamepad = nullptr; static cvar_t *in_nograb; @@ -68,38 +68,39 @@ static SDL_Window *window = nullptr; IN_PrintKey =============== */ -static void IN_PrintKey( const SDL_Keysym *keysym, Keyboard::Key keycodeKey, bool down ) +static void IN_PrintKey( const SDL_Keymod mod, const SDL_Scancode scancode, const SDL_Keycode eventKey, + Keyboard::Key keycodeKey, bool down ) { std::string kmods; - if ( keysym->mod & KMOD_LSHIFT ) { kmods += "KMOD_LSHIFT "; } + if ( mod & SDL_KMOD_LSHIFT ) { kmods += "KMOD_LSHIFT "; } - if ( keysym->mod & KMOD_RSHIFT ) { kmods += "KMOD_RSHIFT "; } + if ( mod & SDL_KMOD_RSHIFT ) { kmods += "KMOD_RSHIFT "; } - if ( keysym->mod & KMOD_LCTRL ) { kmods += "KMOD_LCTRL "; } + if ( mod & SDL_KMOD_LCTRL ) { kmods += "KMOD_LCTRL "; } - if ( keysym->mod & KMOD_RCTRL ) { kmods += "KMOD_RCTRL "; } + if ( mod & SDL_KMOD_RCTRL ) { kmods += "KMOD_RCTRL "; } - if ( keysym->mod & KMOD_LALT ) { kmods += "KMOD_LALT "; } + if ( mod & SDL_KMOD_LALT ) { kmods += "KMOD_LALT "; } - if ( keysym->mod & KMOD_RALT ) { kmods += "KMOD_RALT "; } + if ( mod & SDL_KMOD_RALT ) { kmods += "KMOD_RALT "; } - if ( keysym->mod & KMOD_LGUI ) { kmods += "KMOD_LGUI "; } + if ( mod & SDL_KMOD_LGUI ) { kmods += "KMOD_LGUI "; } - if ( keysym->mod & KMOD_RGUI ) { kmods += "KMOD_RGUI "; } + if ( mod & SDL_KMOD_RGUI ) { kmods += "KMOD_RGUI "; } - if ( keysym->mod & KMOD_NUM ) { kmods += "KMOD_NUM" ; } + if ( mod & SDL_KMOD_NUM ) { kmods += "KMOD_NUM" ; } - if ( keysym->mod & KMOD_CAPS ) { kmods += "KMOD_CAPS "; } + if ( mod & SDL_KMOD_CAPS ) { kmods += "KMOD_CAPS "; } - if ( keysym->mod & KMOD_MODE ) { kmods += "KMOD_MODE "; } + if ( mod & SDL_KMOD_MODE ) { kmods += "KMOD_MODE "; } - if ( keysym->mod & KMOD_RESERVED ) { kmods += "KMOD_RESERVED "; } + // if ( mod & SDL_KMOD_RESERVED ) { kmods += "KMOD_RESERVED "; } Log::defaultLogger.WithoutSuppression().Notice( "%s%c scancode = 0x%03x | SDL name = \"%s\" | keycode bind = %s | scancode bind = %s", - kmods, down ? '+' : '-', keysym->scancode, SDL_GetKeyName( keysym->sym ), - KeyToString( keycodeKey ), KeyToString( Keyboard::Key::FromScancode( keysym->scancode ))); + kmods, down ? '+' : '-', scancode, SDL_GetKeyName( eventKey ), + KeyToString( keycodeKey ), KeyToString( Keyboard::Key::FromScancode( scancode ))); } /* @@ -121,22 +122,22 @@ static bool IN_IsConsoleKey( Keyboard::Key key ) } // Translates based on keycode, not scancode. -static Keyboard::Key IN_TranslateSDLToQ3Key( SDL_Keysym *keysym, bool down ) +static Keyboard::Key IN_TranslateSDLToQ3Key( SDL_KeyboardEvent *event, bool down ) { using Keyboard::Key; Key key; - if ( keysym->sym == SDLK_DELETE ) // SDLK_DELETE is anomalously located in the Unicode range. + if ( event->key == SDLK_DELETE ) // SDLK_DELETE is anomalously located in the Unicode range. { key = Key(K_DEL); } - else if ( keysym->sym >= SDLK_SPACE && keysym->sym < UNICODE_MAX_CODE_POINT ) + else if ( event->key >= SDLK_SPACE && event->key < UNICODE_MAX_CODE_POINT ) { - key = Key::FromCharacter(keysym->sym); + key = Key::FromCharacter( event->key ); } else { - switch ( keysym->sym ) + switch ( event->key ) { case SDLK_PAGEUP: key = Key(K_PGUP); @@ -393,7 +394,7 @@ static Keyboard::Key IN_TranslateSDLToQ3Key( SDL_Keysym *keysym, bool down ) if ( in_keyboardDebug->integer ) { - IN_PrintKey( keysym, key, down ); + IN_PrintKey( event->mod, event->scancode, event->key, key, down ); } return key; @@ -454,21 +455,21 @@ void IN_SetMouseMode(MouseMode newMode) switch ( newMode ) { case MouseMode::SystemCursor: - SDL_ShowCursor( SDL_ENABLE ); - SDL_SetWindowGrab( window, SDL_FALSE ); - SDL_SetRelativeMouseMode( SDL_FALSE ); + SDL_ShowCursor(); + SDL_SetWindowMouseGrab( window, false ); + SDL_SetWindowRelativeMouseMode( window, false ); break; case MouseMode::CustomCursor: - SDL_ShowCursor( SDL_DISABLE ); - SDL_SetWindowGrab( window, SDL_FALSE ); - SDL_SetRelativeMouseMode( SDL_FALSE ); + SDL_HideCursor(); + SDL_SetWindowMouseGrab( window, false ); + SDL_SetWindowRelativeMouseMode( window, false ); break; case MouseMode::Deltas: - SDL_ShowCursor( SDL_DISABLE ); - SDL_SetWindowGrab( window, SDL_TRUE ); - SDL_SetRelativeMouseMode( SDL_TRUE ); + SDL_HideCursor(); + SDL_SetWindowMouseGrab( window, true ); + SDL_SetWindowRelativeMouseMode( window, true ); break; } @@ -552,9 +553,9 @@ struct unsigned int oldhats; } stick_state; -static const char* JoystickNameForIndex(int index) +static const char* JoystickNameForID( SDL_JoystickID id ) { - const char* name = SDL_JoystickNameForIndex(index); + const char* name = SDL_GetJoystickNameForID( id ); return name ? name : ""; } @@ -565,12 +566,9 @@ IN_InitJoystick */ static void IN_InitJoystick() { - int i = 0; - int total = 0; - if ( stick != nullptr ) { - SDL_JoystickClose( stick ); + SDL_CloseJoystick( stick ); } stick = nullptr; @@ -592,7 +590,7 @@ static void IN_InitJoystick() { Log::Debug( "Calling SDL_Init(SDL_INIT_JOYSTICK)..." ); - if ( SDL_Init( SDL_INIT_JOYSTICK ) < 0 ) + if ( !SDL_Init( SDL_INIT_JOYSTICK ) ) { Log::Warn( "SDL_Init(SDL_INIT_JOYSTICK) failed: %s", SDL_GetError() ); return; @@ -601,24 +599,33 @@ static void IN_InitJoystick() Log::Debug( "SDL_Init(SDL_INIT_JOYSTICK) passed." ); } - total = SDL_NumJoysticks(); + int total = 0; + SDL_JoystickID* ids = SDL_GetJoysticks( &total ); Log::Debug( "%d possible joysticks", total ); - for ( i = 0; i < total; i++ ) + for ( int i = 0; i < total; i++ ) { - Log::Debug( "[%d] %s", i, JoystickNameForIndex( i ) ); + Log::Debug( "[%d] %s", i, JoystickNameForID( ids[i] ) ); } in_joystickNo = Cvar_Get( "in_joystickNo", "0", 0 ); + in_joystickUseAnalog = Cvar_Get( "in_joystickUseAnalog", "0", 0 ); + + if ( total <= 0 ) + { + SDL_free( ids ); + return; + } if ( in_joystickNo->integer < 0 || in_joystickNo->integer >= total ) { Cvar_Set( "in_joystickNo", "0" ); } - in_joystickUseAnalog = Cvar_Get( "in_joystickUseAnalog", "0", 0 ); + SDL_JoystickID id = ids[ in_joystickNo->integer ]; + SDL_free( ids ); - stick = SDL_JoystickOpen( in_joystickNo->integer ); + stick = SDL_OpenJoystick( id ); if ( stick == nullptr ) { @@ -626,26 +633,26 @@ static void IN_InitJoystick() return; } - if ( SDL_IsGameController( in_joystickNo->integer ) ) + if ( SDL_IsGamepad( id ) ) { - gamepad = SDL_GameControllerOpen( in_joystickNo->integer ); + gamepad = SDL_OpenGamepad( id ); if ( gamepad ) { Cvar_Set( "in_gameControllerAvailable", "1" ); - SDL_GameControllerEventState( SDL_QUERY ); + SDL_GamepadEventsEnabled(); } } Log::Debug( "Joystick %d opened", in_joystickNo->integer ); - Log::Debug( "Name: %s", JoystickNameForIndex( in_joystickNo->integer ) ); - Log::Debug( "Axes: %d", SDL_JoystickNumAxes( stick ) ); - Log::Debug( "Hats: %d", SDL_JoystickNumHats( stick ) ); - Log::Debug( "Buttons: %d", SDL_JoystickNumButtons( stick ) ); - Log::Debug( "Balls: %d", SDL_JoystickNumBalls( stick ) ); + Log::Debug( "Name: %s", JoystickNameForID( id ) ); + Log::Debug( "Axes: %d", SDL_GetNumJoystickAxes( stick ) ); + Log::Debug( "Hats: %d", SDL_GetNumJoystickHats( stick ) ); + Log::Debug( "Buttons: %d", SDL_GetNumJoystickButtons( stick ) ); + Log::Debug( "Balls: %d", SDL_GetNumJoystickBalls( stick ) ); Log::Debug( "Use Analog: %s", in_joystickUseAnalog->integer ? "Yes" : "No" ); Log::Debug( "Use SDL2 GameController mappings: %s", gamepad ? "Yes" : "No" ); - SDL_JoystickEventState( SDL_QUERY ); + SDL_GamepadEventsEnabled(); } /* @@ -657,12 +664,12 @@ static void IN_ShutdownJoystick() { if ( gamepad ) { - SDL_GameControllerClose( gamepad ); + SDL_CloseGamepad( gamepad ); gamepad = nullptr; } if ( stick ) { - SDL_JoystickClose( stick ); + SDL_CloseJoystick( stick ); stick = nullptr; } @@ -709,10 +716,10 @@ static void IN_JoyMove() return; } - SDL_JoystickUpdate(); + SDL_UpdateJoysticks(); // update the ball state. - total = SDL_JoystickNumBalls( stick ); + total = SDL_GetNumJoystickBalls( stick ); if ( total > 0 ) { @@ -723,7 +730,7 @@ static void IN_JoyMove() { int dx = 0; int dy = 0; - SDL_JoystickGetBall( stick, i, &dx, &dy ); + SDL_GetJoystickBall( stick, i, &dx, &dy ); balldx += dx; balldy += dy; } @@ -750,7 +757,7 @@ static void IN_JoyMove() } // now query the stick buttons... - total = SDL_JoystickNumButtons( stick ); + total = SDL_GetNumJoystickButtons( stick ); if ( total > 0 ) { @@ -761,7 +768,7 @@ static void IN_JoyMove() for ( i = 0; i < total; i++ ) { - bool pressed = ( SDL_JoystickGetButton( stick, i ) != 0 ); + bool pressed = ( SDL_GetJoystickButton( stick, i ) != 0 ); if ( pressed != stick_state.buttons[ i ] ) { @@ -773,7 +780,7 @@ static void IN_JoyMove() } // look at the hats... - total = SDL_JoystickNumHats( stick ); + total = SDL_GetNumJoystickHats( stick ); if ( total > 0 ) { @@ -781,7 +788,7 @@ static void IN_JoyMove() for ( i = 0; i < total; i++ ) { - ( ( Uint8 * ) &hats ) [ i ] = SDL_JoystickGetHat( stick, i ); + ( ( Uint8 * ) &hats ) [ i ] = SDL_GetJoystickHat( stick, i ); } } @@ -885,7 +892,7 @@ static void IN_JoyMove() stick_state.oldhats = hats; // finally, look at the axes... - total = SDL_JoystickNumAxes( stick ); + total = SDL_GetNumJoystickAxes( stick ); if ( total > 0 ) { @@ -893,7 +900,7 @@ static void IN_JoyMove() for ( i = 0; i < total; i++ ) { - Sint16 axis = SDL_JoystickGetAxis( stick, i ); + Sint16 axis = SDL_GetJoystickAxis( stick, i ); if ( !in_joystickUseAnalog->integer ) { @@ -945,9 +952,9 @@ static void IN_JoyMove() stick_state.oldaxes = axes; } -static void IN_GameControllerAxis( SDL_GameControllerAxis controllerAxis, joystickAxis_t gameAxis, float scale ) +static void IN_GameControllerAxis( SDL_GamepadAxis controllerAxis, joystickAxis_t gameAxis, float scale ) { - Sint16 axis = SDL_GameControllerGetAxis( gamepad, controllerAxis ); + Sint16 axis = SDL_GetGamepadAxis( gamepad, controllerAxis ); float f = ( ( float ) axis ) / 32767.0f; if ( f > -in_joystickThreshold->value && f < in_joystickThreshold->value ) @@ -965,12 +972,12 @@ static void IN_GameControllerAxis( SDL_GameControllerAxis controllerAxis, joysti } } -static int IN_GameControllerAxisToButton( SDL_GameControllerAxis controllerAxis, keyNum_t key ) +static int IN_GameControllerAxisToButton( SDL_GamepadAxis controllerAxis, keyNum_t key ) { using Keyboard::Key; unsigned int axes = 0; - Sint16 axis = SDL_GameControllerGetAxis( gamepad, controllerAxis ); + Sint16 axis = SDL_GetGamepadAxis( gamepad, controllerAxis ); float f = ( ( float ) axis ) / 32767.0f; if ( f > in_gameControllerTriggerDeadzone->value ) @@ -984,7 +991,7 @@ static int IN_GameControllerAxisToButton( SDL_GameControllerAxis controllerAxis, if ( in_gameControllerDebug->integer ) { Log::Notice( "GameController axis = %s to key = Q:0x%02x(%s), value = %f", - SDL_GameControllerGetStringForAxis( controllerAxis ), key, + SDL_GetGamepadStringForAxis( controllerAxis ), key, Keyboard::KeyToString( Key(key) ), f ); } } @@ -995,7 +1002,7 @@ static int IN_GameControllerAxisToButton( SDL_GameControllerAxis controllerAxis, if ( in_gameControllerDebug->integer ) { Log::Notice( "GameController axis = %s to key = Q:0x%02x(%s), value = %f", - SDL_GameControllerGetStringForAxis( controllerAxis ), key, + SDL_GetGamepadStringForAxis( controllerAxis ), key, Keyboard::KeyToString( Key(key) ), f ); } } @@ -1019,11 +1026,11 @@ static void IN_GameControllerMove() return; } - SDL_GameControllerUpdate(); + SDL_UpdateGamepads(); for ( i = 0; i < (K_CONTROLLER_MAX - K_CONTROLLER_A); i++ ) { - bool pressed = SDL_GameControllerGetButton( gamepad, Util::enum_cast(i) ); + bool pressed = SDL_GetGamepadButton( gamepad, Util::enum_cast(i) ); if ( pressed != stick_state.buttons[ i ] ) { @@ -1032,7 +1039,7 @@ static void IN_GameControllerMove() if ( in_gameControllerDebug->integer ) { Log::Notice( "GameController button %s = %s", - SDL_GameControllerGetStringForButton( Util::enum_cast(i) ), + SDL_GetGamepadStringForButton( Util::enum_cast< SDL_GamepadButton >(i) ), pressed ? "Pressed" : "Released" ); } @@ -1041,15 +1048,15 @@ static void IN_GameControllerMove() } // use left stick for strafing - IN_GameControllerAxis( SDL_CONTROLLER_AXIS_LEFTX, joystickAxis_t::AXIS_SIDE, 127 ); - IN_GameControllerAxis( SDL_CONTROLLER_AXIS_LEFTY, joystickAxis_t::AXIS_FORWARD, -127 ); + IN_GameControllerAxis( SDL_GAMEPAD_AXIS_LEFTX, joystickAxis_t::AXIS_SIDE, 127 ); + IN_GameControllerAxis( SDL_GAMEPAD_AXIS_LEFTY, joystickAxis_t::AXIS_FORWARD, -127 ); // use right stick for viewing - IN_GameControllerAxis( SDL_CONTROLLER_AXIS_RIGHTX, joystickAxis_t::AXIS_YAW, -127 ); - IN_GameControllerAxis( SDL_CONTROLLER_AXIS_RIGHTY, joystickAxis_t::AXIS_PITCH, 127 ); + IN_GameControllerAxis( SDL_GAMEPAD_AXIS_RIGHTX, joystickAxis_t::AXIS_YAW, -127 ); + IN_GameControllerAxis( SDL_GAMEPAD_AXIS_RIGHTY, joystickAxis_t::AXIS_PITCH, 127 ); - axes |= IN_GameControllerAxisToButton( SDL_CONTROLLER_AXIS_TRIGGERLEFT, K_CONTROLLER_LT ); - axes |= IN_GameControllerAxisToButton( SDL_CONTROLLER_AXIS_TRIGGERRIGHT, K_CONTROLLER_RT ); + axes |= IN_GameControllerAxisToButton( SDL_GAMEPAD_AXIS_LEFT_TRIGGER, K_CONTROLLER_LT ); + axes |= IN_GameControllerAxisToButton( SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, K_CONTROLLER_RT ); /* Save for future generations. */ stick_state.oldaxes = axes; @@ -1080,12 +1087,12 @@ static void IN_ProcessEvents( bool dropInput ) { switch ( e.type ) { - case SDL_KEYDOWN: + case SDL_EVENT_KEY_DOWN: if ( !dropInput ) { // Send events for both scancode- and keycode-based Keys - Key kScan = Keyboard::Key::FromScancode( e.key.keysym.scancode ); - Key kKeycode = IN_TranslateSDLToQ3Key( &e.key.keysym, true ); + Key kScan = Keyboard::Key::FromScancode( e.key.scancode ); + Key kKeycode = IN_TranslateSDLToQ3Key( &e.key, true ); bool consoleFound = false; for (Key k: {kScan, kKeycode} ) { if ( IN_IsConsoleKey( k ) && !keys[ Key(K_ALT) ].down) { @@ -1104,30 +1111,30 @@ static void IN_ProcessEvents( bool dropInput ) } break; - case SDL_KEYUP: + case SDL_EVENT_KEY_UP: if ( !dropInput ) { QueueKeyEvent( - Keyboard::Key::FromScancode( e.key.keysym.scancode ), - IN_TranslateSDLToQ3Key( &e.key.keysym, false ), + Keyboard::Key::FromScancode( e.key.scancode ), + IN_TranslateSDLToQ3Key( &e.key, false ), false, false ); } break; - case SDL_TEXTINPUT: + case SDL_EVENT_TEXT_INPUT: if ( !lastEventWasConsoleKeyDown ) { - char *c = e.text.text; + std::string text = e.text.text; - while ( *c ) - { + const char* c = text.c_str(); + while ( *c ) { int width = Q_UTF8_Width( c ); Com_QueueEvent( Util::make_unique( Q_UTF8_CodePoint( c ) ) ); c += width; } } break; - case SDL_MOUSEMOTION: + case SDL_EVENT_MOUSE_MOTION: if ( !dropInput ) { if ( mouse_mode != MouseMode::Deltas ) @@ -1141,8 +1148,8 @@ static void IN_ProcessEvents( bool dropInput ) } break; - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: if ( !dropInput ) { keyNum_t b; @@ -1172,10 +1179,10 @@ static void IN_ProcessEvents( bool dropInput ) b = Util::enum_cast(K_AUX1 + ( e.button.button - ( SDL_BUTTON_X2 + 1 ) ) % 16); break; } - QueueKeyEvent( b, e.type == SDL_MOUSEBUTTONDOWN ); + QueueKeyEvent( b, e.type == SDL_EVENT_MOUSE_BUTTON_DOWN ); } break; - case SDL_MOUSEWHEEL: + case SDL_EVENT_MOUSE_WHEEL: // FIXME: mouse wheel support shouldn't use keys! if ( e.wheel.y > 0 ) { @@ -1189,43 +1196,38 @@ static void IN_ProcessEvents( bool dropInput ) } break; - case SDL_WINDOWEVENT: - switch( e.window.event ) + case SDL_EVENT_WINDOW_RESIZED: + extern cvar_t* r_allowResize; + // Toggling r_fullscreen does not work well when r_allowResize is enabled - + // it generates spurious resize events. + if ( r_allowResize->integer ) { - case SDL_WINDOWEVENT_RESIZED: - extern cvar_t* r_allowResize; - // Toggling r_fullscreen does not work well when r_allowResize is enabled - - // it generates spurious resize events. - if ( r_allowResize->integer ) - { - char width[32], height[32]; - Com_sprintf( width, sizeof( width ), "%d", e.window.data1 ); - Com_sprintf( height, sizeof( height ), "%d", e.window.data2 ); - Cvar_Set( "r_customwidth", width ); - Cvar_Set( "r_customheight", height ); - Cvar_Set( "r_mode", "-1" ); - } - break; + char width[32], height[32]; + Com_sprintf( width, sizeof( width ), "%d", e.window.data1 ); + Com_sprintf( height, sizeof( height ), "%d", e.window.data2 ); + Cvar_Set( "r_customwidth", width ); + Cvar_Set( "r_customheight", height ); + Cvar_Set( "r_mode", "-1" ); + } + break; - case SDL_WINDOWEVENT_MINIMIZED: Cvar_SetValue( "com_minimized", 1 ); break; - case SDL_WINDOWEVENT_RESTORED: - case SDL_WINDOWEVENT_MAXIMIZED: Cvar_SetValue( "com_minimized", 0 ); break; - case SDL_WINDOWEVENT_FOCUS_LOST: Cvar_SetValue( "com_unfocused", 1 ); break; - case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_EVENT_WINDOW_MINIMIZED: Cvar_SetValue( "com_minimized", 1 ); break; + case SDL_EVENT_WINDOW_RESTORED: + case SDL_EVENT_WINDOW_MAXIMIZED: Cvar_SetValue( "com_minimized", 0 ); break; + case SDL_EVENT_WINDOW_FOCUS_LOST: Cvar_SetValue( "com_unfocused", 1 ); break; + case SDL_EVENT_WINDOW_FOCUS_GAINED: - Cvar_SetValue( "com_unfocused", 0 ); + Cvar_SetValue( "com_unfocused", 0 ); - // HACK: if the window is focused, it can't be minimized. - // fixes - // * https://github.com/DaemonEngine/Daemon/issues/408 - // * https://github.com/Unvanquished/Unvanquished/issues/1136 - // and maybe others - Cvar_SetValue( "com_minimized", 0 ); + // HACK: if the window is focused, it can't be minimized. + // fixes + // * https://github.com/DaemonEngine/Daemon/issues/408 + // * https://github.com/Unvanquished/Unvanquished/issues/1136 + // and maybe others + Cvar_SetValue( "com_minimized", 0 ); - break; - } break; - case SDL_QUIT: + case SDL_EVENT_QUIT: Cmd::ExecuteCommand("quit Closed window"); break; default: @@ -1236,7 +1238,7 @@ static void IN_ProcessEvents( bool dropInput ) } bool IN_IsNumLockOn() { - return SDL_GetModState() & KMOD_NUM; + return SDL_GetModState() & SDL_KMOD_NUM; } /* @@ -1296,6 +1298,9 @@ void IN_FrameEnd() IN_Init =============== */ +#ifdef __APPLE__ +extern "C" void DisableAccentMenu(); +#endif void IN_Init( void *windowData ) { int appState; @@ -1319,13 +1324,20 @@ void IN_Init( void *windowData ) in_gameControllerTriggerDeadzone = Cvar_Get( "in_gameControllerTriggerDeadzone", "0.5", 0); in_gameControllerDebug = Cvar_Get( "in_gameControllerDebug", "0", CVAR_TEMP ); - SDL_StartTextInput(); + SDL_StartTextInput( window ); IN_SetMouseMode( MouseMode::SystemCursor ); appState = SDL_GetWindowFlags( window ); Cvar_SetValue( "com_unfocused", !( appState & SDL_WINDOW_INPUT_FOCUS ) ); Cvar_SetValue( "com_minimized", ( appState & SDL_WINDOW_MINIMIZED ) ); IN_InitJoystick(); + +#ifdef __APPLE__ + // We have to act after SDL does. Whoever has the last word wins! + // https://github.com/libsdl-org/SDL/commit/caf0348b26d02bc22ba9a0a908c83c43f8e4e6ad + DisableAccentMenu(); +#endif + Log::Debug( "------------------------------------" ); } @@ -1342,7 +1354,7 @@ IN_Shutdown */ void IN_Shutdown() { - SDL_StopTextInput(); + SDL_StopTextInput( window ); IN_SetMouseMode(MouseMode::SystemCursor); mouse_mode_unset = true;