build: bundle SFML 3.x sys/network in third_party

Remove the SFML external dependency, include the SFML 3.0.1 system and
network libraries in third_party and adjust the build code, tools and
documentation accordingly.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
This commit is contained in:
Rafael Kitover 2025-04-24 16:11:58 +00:00
parent 9e724ea4b8
commit 4cf6cccbaf
88 changed files with 15529 additions and 436 deletions

View File

@ -25,7 +25,6 @@ endif()
set(VCPKG_DEPS pkgconf zlib pthreads "sdl2[samplerate]" gettext wxwidgets)
set(VCPKG_DEPS_OPTIONAL
sfml ENABLE_LINK
ffmpeg ENABLE_FFMPEG
faudio ENABLE_FAUDIO
)

View File

@ -166,7 +166,6 @@ And the following development libraries:
- [ffmpeg](https://ffmpeg.org/) (optional, at least version `4.0.4`, for game recording)
- [gettext](https://www.gnu.org/software/gettext/) and gettext-tools
- [SDL2](https://www.libsdl.org/) (required)
- [SFML](https://www.sfml-dev.org/) (optional, for link)
- [openal-soft](https://kcat.strangesoft.net/openal.html) (required, a sound interface)
- [wxWidgets](https://wxwidgets.org/) (required for GUI, 2.8 and non-stl builds are no longer supported)
@ -211,7 +210,7 @@ cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_LINK=NO -G Ninja
| `ENABLE_ASM` | Enable the following two ASM options | ON for 32 bit builds |
| `ENABLE_ASM_SCALERS` | Enable x86 ASM graphic filters | ON for 32 bit builds |
| `ENABLE_MMX` | Enable MMX | ON for 32 bit builds |
| `ENABLE_LINK` | Enable GBA linking functionality (requires SFML) | AUTO |
| `ENABLE_LINK` | Enable GBA linking functionality | AUTO |
| `ENABLE_LIRC` | Enable LIRC support | OFF |
| `ENABLE_FFMPEG` | Enable ffmpeg A/V recording | AUTO |
| `ENABLE_ONLINEUPDATES` | Enable online update checks | ON |
@ -224,7 +223,6 @@ cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_LINK=NO -G Ninja
| `BUILD_TESTING` | Build the tests and enable ctest support. | ON |
| `VBAM_STATIC` | Try link all libs statically (the following are set to ON if ON) | OFF |
| `SDL2_STATIC` | Try to link static SDL2 libraries | OFF |
| `SFML_STATIC_LIBRARIES` | Try to link static SFML libraries | OFF |
| `FFMPEG_STATIC` | Try to link static ffmpeg libraries | OFF |
| `OPENAL_STATIC` | Try to link static OpenAL libraries | OFF |
| `TRANSLATIONS_ONLY` | Build only the translations.zip and nothing else | OFF |

View File

@ -120,3 +120,9 @@ if(ENABLE_LINK OR ENABLE_WX)
endif()
endif()
if(ENABLE_LINK)
add_subdirectory(${CMAKE_SOURCE_DIR}/third_party/sfml/src/SFML/System EXCLUDE_FROM_ALL)
add_subdirectory(${CMAKE_SOURCE_DIR}/third_party/sfml/src/SFML/Network EXCLUDE_FROM_ALL)
set(SFML_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/third_party/sfml/include)
set(SFML_LIBRARIES sfml-system sfml-network)
endif()

View File

@ -1,365 +0,0 @@
# This script locates the SFML library
# ------------------------------------
#
# Usage
# -----
#
# When you try to locate the SFML libraries, you must specify which modules you want to use (system, window, graphics, network, audio, main).
# If none is given, the SFML_LIBRARIES variable will be empty and you'll end up linking to nothing.
# example:
# find_package(SFML COMPONENTS graphics window system) # find the graphics, window and system modules
#
# You can enforce a specific version, either MAJOR.MINOR or only MAJOR.
# If nothing is specified, the version won't be checked (i.e. any version will be accepted).
# example:
# find_package(SFML COMPONENTS ...) # no specific version required
# find_package(SFML 2 COMPONENTS ...) # any 2.x version
# find_package(SFML 2.4 COMPONENTS ...) # version 2.4 or greater
#
# By default, the dynamic libraries of SFML will be found. To find the static ones instead,
# you must set the SFML_STATIC_LIBRARIES variable to TRUE before calling find_package(SFML ...).
# Since you have to link yourself all the SFML dependencies when you link it statically, the following
# additional variables are defined: SFML_XXX_DEPENDENCIES and SFML_DEPENDENCIES (see their detailed
# description below).
# In case of static linking, the SFML_STATIC macro will also be defined by this script.
# example:
# set(SFML_STATIC_LIBRARIES TRUE)
# find_package(SFML 2 COMPONENTS network system)
#
# On Mac OS X if SFML_STATIC_LIBRARIES is not set to TRUE then by default CMake will search for frameworks unless
# CMAKE_FIND_FRAMEWORK is set to "NEVER" for example. Please refer to CMake documentation for more details.
# Moreover, keep in mind that SFML frameworks are only available as release libraries unlike dylibs which
# are available for both release and debug modes.
#
# If SFML is not installed in a standard path, you can use the SFML_ROOT CMake (or environment) variable
# to tell CMake where SFML is.
#
# Output
# ------
#
# This script defines the following variables:
# - For each specified module XXX (system, window, graphics, network, audio, main):
# - SFML_XXX_LIBRARY_DEBUG: the name of the debug library of the xxx module (set to SFML_XXX_LIBRARY_RELEASE is no debug version is found)
# - SFML_XXX_LIBRARY_RELEASE: the name of the release library of the xxx module (set to SFML_XXX_LIBRARY_DEBUG is no release version is found)
# - SFML_XXX_LIBRARY: the name of the library to link to for the xxx module (includes both debug and optimized names if necessary)
# - SFML_XXX_FOUND: true if either the debug or release library of the xxx module is found
# - SFML_XXX_DEPENDENCIES: the list of libraries the module depends on, in case of static linking
# - SFML_LIBRARIES: the list of all libraries corresponding to the required modules
# - SFML_FOUND: true if all the required modules are found
# - SFML_INCLUDE_DIR: the path where SFML headers are located (the directory containing the SFML/Config.hpp file)
# - SFML_DEPENDENCIES: the list of libraries SFML depends on, in case of static linking
#
# example:
# find_package(SFML 2 COMPONENTS system window graphics audio REQUIRED)
# include_directories(${SFML_INCLUDE_DIR})
# add_executable(myapp ...)
# target_link_libraries(myapp ${SFML_LIBRARIES})
# define the SFML_STATIC macro if static build was chosen
if(SFML_STATIC_LIBRARIES)
add_compile_definitions(SFML_STATIC)
endif()
# define the list of search paths for headers and libraries
set(FIND_SFML_PATHS
${SFML_ROOT}
$ENV{SFML_ROOT}
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw
/opt/local
/opt/csw
/opt)
# find the SFML include directory
find_path(SFML_INCLUDE_DIR SFML/Config.hpp
PATH_SUFFIXES include
PATHS ${FIND_SFML_PATHS})
# check the version number
set(SFML_VERSION_OK TRUE)
if(SFML_FIND_VERSION AND SFML_INCLUDE_DIR)
# extract the major and minor version numbers from SFML/Config.hpp
# we have to handle framework a little bit differently:
if("${SFML_INCLUDE_DIR}" MATCHES "SFML.framework")
set(SFML_CONFIG_HPP_INPUT "${SFML_INCLUDE_DIR}/Headers/Config.hpp")
else()
set(SFML_CONFIG_HPP_INPUT "${SFML_INCLUDE_DIR}/SFML/Config.hpp")
endif()
FILE(READ "${SFML_CONFIG_HPP_INPUT}" SFML_CONFIG_HPP_CONTENTS)
STRING(REGEX REPLACE ".*#define *SFML_VERSION_MAJOR *([0-9]+).*" "\\1" SFML_VERSION_MAJOR "${SFML_CONFIG_HPP_CONTENTS}")
STRING(REGEX REPLACE ".*#define *SFML_VERSION_MINOR *([0-9]+).*" "\\1" SFML_VERSION_MINOR "${SFML_CONFIG_HPP_CONTENTS}")
STRING(REGEX REPLACE ".*#define *SFML_VERSION_PATCH *([0-9]+).*" "\\1" SFML_VERSION_PATCH "${SFML_CONFIG_HPP_CONTENTS}")
if (NOT "${SFML_VERSION_PATCH}" MATCHES "^[0-9]+$")
set(SFML_VERSION_PATCH 0)
endif()
math(EXPR SFML_REQUESTED_VERSION "${SFML_FIND_VERSION_MAJOR} * 10000 + ${SFML_FIND_VERSION_MINOR} * 100 + ${SFML_FIND_VERSION_PATCH}")
# if we could extract them, compare with the requested version number
if (SFML_VERSION_MAJOR)
# transform version numbers to an integer
math(EXPR SFML_VERSION "${SFML_VERSION_MAJOR} * 10000 + ${SFML_VERSION_MINOR} * 100 + ${SFML_VERSION_PATCH}")
# compare them
if(SFML_VERSION LESS SFML_REQUESTED_VERSION)
set(SFML_VERSION_OK FALSE)
endif()
else()
# SFML version is < 2.0
if (SFML_REQUESTED_VERSION GREATER 10900)
set(SFML_VERSION_OK FALSE)
set(SFML_VERSION_MAJOR 1)
set(SFML_VERSION_MINOR x)
set(SFML_VERSION_PATCH x)
endif()
endif()
endif()
# find the requested modules
set(SFML_FOUND TRUE) # will be set to false if one of the required modules is not found
foreach(FIND_SFML_COMPONENT ${SFML_FIND_COMPONENTS})
string(TOLOWER ${FIND_SFML_COMPONENT} FIND_SFML_COMPONENT_LOWER)
string(TOUPPER ${FIND_SFML_COMPONENT} FIND_SFML_COMPONENT_UPPER)
set(FIND_SFML_COMPONENT_NAME sfml-${FIND_SFML_COMPONENT_LOWER})
# no suffix for sfml-main, it is always a static library
if(FIND_SFML_COMPONENT_LOWER STREQUAL "main")
# release library
find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE
NAMES ${FIND_SFML_COMPONENT_NAME}
PATH_SUFFIXES lib64 lib
PATHS ${FIND_SFML_PATHS})
# debug library
find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG
NAMES ${FIND_SFML_COMPONENT_NAME}-d
PATH_SUFFIXES lib64 lib
PATHS ${FIND_SFML_PATHS})
else()
# static release library
find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE
NAMES ${FIND_SFML_COMPONENT_NAME}-s
PATH_SUFFIXES lib64 lib
PATHS ${FIND_SFML_PATHS})
# static debug library
find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG
NAMES ${FIND_SFML_COMPONENT_NAME}-s-d
PATH_SUFFIXES lib64 lib
PATHS ${FIND_SFML_PATHS})
# dynamic release library
find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE
NAMES ${FIND_SFML_COMPONENT_NAME}
PATH_SUFFIXES lib64 lib
PATHS ${FIND_SFML_PATHS})
# dynamic debug library
find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG
NAMES ${FIND_SFML_COMPONENT_NAME}-d
PATH_SUFFIXES lib64 lib
PATHS ${FIND_SFML_PATHS})
# choose the entries that fit the requested link type
if(SFML_STATIC_LIBRARIES)
if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE})
endif()
if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG})
endif()
else()
if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE})
endif()
if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG})
endif()
endif()
endif()
if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG OR SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE)
# library found
set(SFML_${FIND_SFML_COMPONENT_UPPER}_FOUND TRUE)
# if both are found, set SFML_XXX_LIBRARY to contain both
if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG AND SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY debug ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG}
optimized ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE})
endif()
# if only one debug/release variant is found, set the other to be equal to the found one
if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG AND NOT SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE)
# debug and not release
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG})
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG})
endif()
if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE AND NOT SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG)
# release and not debug
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE})
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE})
endif()
else()
# library not found
set(SFML_FOUND FALSE)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_FOUND FALSE)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY "")
set(FIND_SFML_MISSING "${FIND_SFML_MISSING} SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY")
endif()
# mark as advanced
MARK_AS_ADVANCED(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY
SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE
SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG
SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE
SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG
SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE
SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG)
# add to the global list of libraries
set(SFML_LIBRARIES ${SFML_LIBRARIES} "${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY}")
endforeach()
# in case of static linking, we must also define the list of all the dependencies of SFML libraries
if(SFML_STATIC_LIBRARIES)
# detect the OS
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(FIND_SFML_OS_WINDOWS 1)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(FIND_SFML_OS_LINUX 1)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
set(FIND_SFML_OS_FREEBSD 1)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(FIND_SFML_OS_MACOSX 1)
endif()
# start with an empty list
set(SFML_DEPENDENCIES)
set(FIND_SFML_DEPENDENCIES_NOTFOUND)
# macro that searches for a 3rd-party library
macro(find_sfml_dependency output friendlyname)
# No lookup in environment variables (PATH on Windows), as they may contain wrong library versions
find_library(${output} NAMES ${ARGN} PATHS ${FIND_SFML_PATHS} PATH_SUFFIXES lib NO_SYSTEM_ENVIRONMENT_PATH)
if(${${output}} STREQUAL "${output}-NOTFOUND")
unset(output)
set(FIND_SFML_DEPENDENCIES_NOTFOUND "${FIND_SFML_DEPENDENCIES_NOTFOUND} ${friendlyname}")
endif()
endmacro()
# sfml-system
list(FIND SFML_FIND_COMPONENTS "system" FIND_SFML_SYSTEM_COMPONENT)
if(NOT ${FIND_SFML_SYSTEM_COMPONENT} EQUAL -1)
# update the list -- these are only system libraries, no need to find them
if(FIND_SFML_OS_LINUX OR FIND_SFML_OS_FREEBSD OR FIND_SFML_OS_MACOSX)
set(SFML_SYSTEM_DEPENDENCIES "pthread")
endif()
if(FIND_SFML_OS_LINUX)
set(SFML_SYSTEM_DEPENDENCIES ${SFML_SYSTEM_DEPENDENCIES} "rt")
endif()
if(FIND_SFML_OS_WINDOWS)
set(SFML_SYSTEM_DEPENDENCIES "winmm")
endif()
set(SFML_DEPENDENCIES ${SFML_SYSTEM_DEPENDENCIES} ${SFML_DEPENDENCIES})
endif()
# sfml-network
list(FIND SFML_FIND_COMPONENTS "network" FIND_SFML_NETWORK_COMPONENT)
if(NOT ${FIND_SFML_NETWORK_COMPONENT} EQUAL -1)
# update the list -- these are only system libraries, no need to find them
if(FIND_SFML_OS_WINDOWS)
set(SFML_NETWORK_DEPENDENCIES "ws2_32")
endif()
set(SFML_DEPENDENCIES ${SFML_NETWORK_DEPENDENCIES} ${SFML_DEPENDENCIES})
endif()
# sfml-window
list(FIND SFML_FIND_COMPONENTS "window" FIND_SFML_WINDOW_COMPONENT)
if(NOT ${FIND_SFML_WINDOW_COMPONENT} EQUAL -1)
# find libraries
if(FIND_SFML_OS_LINUX OR FIND_SFML_OS_FREEBSD)
find_sfml_dependency(X11_LIBRARY "X11" X11)
find_sfml_dependency(XRANDR_LIBRARY "Xrandr" Xrandr)
endif()
if(FIND_SFML_OS_LINUX)
find_sfml_dependency(UDEV_LIBRARIES "UDev" udev libudev)
endif()
# update the list
if(FIND_SFML_OS_WINDOWS)
set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "opengl32" "winmm" "gdi32")
elseif(FIND_SFML_OS_LINUX)
set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "GL" ${X11_LIBRARY} ${XRANDR_LIBRARY} ${UDEV_LIBRARIES})
elseif(FIND_SFML_OS_FREEBSD)
set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "GL" ${X11_LIBRARY} ${XRANDR_LIBRARY} "usbhid")
elseif(FIND_SFML_OS_MACOSX)
set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "-framework OpenGL -framework Foundation -framework AppKit -framework IOKit -framework Carbon")
endif()
set(SFML_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} ${SFML_DEPENDENCIES})
endif()
# sfml-graphics
list(FIND SFML_FIND_COMPONENTS "graphics" FIND_SFML_GRAPHICS_COMPONENT)
if(NOT ${FIND_SFML_GRAPHICS_COMPONENT} EQUAL -1)
# find libraries
find_sfml_dependency(FREETYPE_LIBRARY "FreeType" freetype)
find_sfml_dependency(JPEG_LIBRARY "libjpeg" jpeg)
# update the list
set(SFML_GRAPHICS_DEPENDENCIES ${FREETYPE_LIBRARY} ${JPEG_LIBRARY})
set(SFML_DEPENDENCIES ${SFML_GRAPHICS_DEPENDENCIES} ${SFML_DEPENDENCIES})
endif()
# sfml-audio
list(FIND SFML_FIND_COMPONENTS "audio" FIND_SFML_AUDIO_COMPONENT)
if(NOT ${FIND_SFML_AUDIO_COMPONENT} EQUAL -1)
# find libraries
find_sfml_dependency(OPENAL_LIBRARY "OpenAL" openal openal32)
find_sfml_dependency(OGG_LIBRARY "Ogg" ogg)
find_sfml_dependency(VORBIS_LIBRARY "Vorbis" vorbis)
find_sfml_dependency(VORBISFILE_LIBRARY "VorbisFile" vorbisfile)
find_sfml_dependency(VORBISENC_LIBRARY "VorbisEnc" vorbisenc)
find_sfml_dependency(FLAC_LIBRARY "FLAC" FLAC)
# update the list
set(SFML_AUDIO_DEPENDENCIES ${OPENAL_LIBRARY} ${FLAC_LIBRARY} ${VORBISENC_LIBRARY} ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY})
set(SFML_DEPENDENCIES ${SFML_DEPENDENCIES} ${SFML_AUDIO_DEPENDENCIES})
endif()
endif()
# handle errors
if(NOT SFML_VERSION_OK)
# SFML version not ok
set(FIND_SFML_ERROR "SFML found but version too low (requested: ${SFML_FIND_VERSION}, found: ${SFML_VERSION_MAJOR}.${SFML_VERSION_MINOR}.${SFML_VERSION_PATCH})")
set(SFML_FOUND FALSE)
elseif(SFML_STATIC_LIBRARIES AND FIND_SFML_DEPENDENCIES_NOTFOUND)
set(FIND_SFML_ERROR "SFML found but some of its dependencies are missing (${FIND_SFML_DEPENDENCIES_NOTFOUND})")
set(SFML_FOUND FALSE)
elseif(NOT SFML_FOUND)
# include directory or library not found
set(FIND_SFML_ERROR "Could NOT find SFML (missing: ${FIND_SFML_MISSING})")
endif()
if (NOT SFML_FOUND)
if(SFML_FIND_REQUIRED)
# fatal error
message(FATAL_ERROR ${FIND_SFML_ERROR})
elseif(NOT SFML_FIND_QUIETLY)
# error but continue
message("${FIND_SFML_ERROR}")
endif()
endif()
# handle success
if(SFML_FOUND AND NOT SFML_FIND_QUIETLY)
message(STATUS "Found SFML ${SFML_VERSION_MAJOR}.${SFML_VERSION_MINOR}.${SFML_VERSION_PATCH} in ${SFML_INCLUDE_DIR}")
endif()

View File

@ -30,7 +30,6 @@ option(VBAM_STATIC "Try to link all libraries statically" ${VBAM_STATIC_DEFAULT}
if(VBAM_STATIC)
set(SDL2_STATIC ON)
set(SFML_STATIC_LIBRARIES ON)
set(FFMPEG_STATIC ON)
set(OPENAL_STATIC ON)
set_property(GLOBAL PROPERTY LINK_SEARCH_START_STATIC ON)
@ -68,14 +67,8 @@ endif()
find_package(PkgConfig)
# Link / SFML
if(TRANSLATIONS_ONLY)
set(ENABLE_LINK_DEFAULT OFF)
else()
find_package(SFML 3.0 COMPONENTS network system)
set(ENABLE_LINK_DEFAULT OFF)
if(SFML_FOUND)
set(ENABLE_LINK_DEFAULT ON)
endif()
if(NOT TRANSLATIONS_ONLY)
set(ENABLE_LINK_DEFAULT ON)
endif()
option(ENABLE_LINK "Enable GBA linking functionality" ${ENABLE_LINK_DEFAULT})

View File

@ -191,7 +191,7 @@ freebsd_installdeps() {
check sudo pkg update
pkgs="llvm-devel cmake ccache nasm ffmpeg gettext-tools gettext pkgconf sdl2 sfml wx31-gtk3 iconv zip ninja"
pkgs="llvm-devel cmake ccache nasm ffmpeg gettext-tools gettext pkgconf sdl2 wx31-gtk3 iconv zip ninja"
[ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs ffmpeg"
@ -318,12 +318,6 @@ debian_installdeps() {
if [ -z "$target" ]; then
sudo apt-get -qq -y update
sfml_libs=
for lib in graphics window network; do
sfml_libs="$sfml_libs $(apt-cache search "libsfml-$lib" | sed 's/ - .*//' | sort -r | head -1)"
done
glew_lib=$(apt-cache search libglew | grep '^libglew[0-9]' | sed 's/ - .*//' | sort -r | head -1)
sdl_lib=$(apt-cache search '^libsdl2-2.0' | sed 's/ - .*//' | sort -r | head -1)
@ -350,7 +344,7 @@ debian_installdeps() {
;;
esac
pkgs="build-essential g++ nasm cmake ccache gettext zlib1g-dev libgl1-mesa-dev libgettextpo-dev libsdl2-dev $sdl_lib libglu1-mesa-dev libglu1-mesa libgles2-mesa-dev libsfml-dev $sfml_libs $glew_lib $wx_libs libgtk2.0-dev libgtk-3-dev ccache zip ninja-build libopenal-dev"
pkgs="build-essential g++ nasm cmake ccache gettext zlib1g-dev libgl1-mesa-dev libgettextpo-dev libsdl2-dev $sdl_lib libglu1-mesa-dev libglu1-mesa libgles2-mesa-dev $glew_lib $wx_libs libgtk2.0-dev libgtk-3-dev ccache zip ninja-build libopenal-dev"
[ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs libavcodec-dev libavformat-dev libswscale-dev libavutil-dev $libswresample_dev"
@ -401,7 +395,7 @@ debian_installdeps() {
fi
fi
deps="gcc zlib ffmpeg gettext sdl2 sfml openal wxwidgets openal"
deps="gcc zlib ffmpeg gettext sdl2 openal wxwidgets openal"
[ -n "$ENABLE_FFMPEG" ] && deps="$deps ffmpeg"
set --
@ -498,7 +492,7 @@ fedora_installdeps() {
# this is sometimes necessary for rawhide
set -- --exclude='glibc32*'
fi
for pkg in zlib-devel mesa-libGL-devel ffmpeg-devel gettext-devel SDL2-devel SFML-devel openal-soft-devel wxGTK-devel gtk3-devel; do
for pkg in zlib-devel mesa-libGL-devel ffmpeg-devel gettext-devel SDL2-devel openal-soft-devel wxGTK-devel gtk3-devel; do
case $pkg in
*ffmpeg*)
[ -z "$ENABLE_FFMPEG" ] && continue
@ -597,8 +591,6 @@ fedora_installdeps() {
# get the necessary win32 headers
git submodule update --init --remote --recursive
warning='SFML is required for LINK support, Fedora does not currently have a MinGW SFML package, if you want LINK support you will need to install it manually'
fi
[ -z "$rpms_installed" ] && check sudo dnf -y --nogpgcheck --best --allowerasing install "$@"
@ -684,8 +676,6 @@ rhel_installdeps() {
set -- --exclude='glibc32*'
fi
warning='RHEL does not currently have SFML packages, LINK support will be disabled'
for pkg in zlib-devel mesa-libGL-devel ffmpeg-devel gettext-devel SDL2-devel openal-soft-devel wxGTK3-devel gtk3-devel; do
case $pkg in
*ffmpeg*)
@ -777,8 +767,6 @@ rhel_installdeps() {
# get the necessary win32 headers
git submodule update --init --remote --recursive
warning='SFML is required for LINK support, RHEL/EPEL does not currently have a MinGW SFML package, if you want LINK support you will need to install it manually'
fi
[ -z "$rpms_installed" ] && check sudo yum -y install "$@"
@ -797,7 +785,7 @@ suse_installdeps() {
check_cross
installing
tools="make cmake ccache nasm gettext-tools pkg-config ccache zip sfml2-devel ninja"
tools="make cmake ccache nasm gettext-tools pkg-config ccache zip ninja"
libs="gcc gcc-c++ libSDL2-devel wxGTK3-3_2-devel openal-soft-devel ffmpeg-7-libavcodec-devel ffmpeg-7-libavdevice-devel ffmpeg-7-libavfilter-devel ffmpeg-7-libavformat-devel ffmpeg-7-libavutil-devel ffmpeg-7-libpostproc-devel ffmpeg-7-libswresample-devel ffmpeg-7-libswscale-devel"
@ -866,7 +854,7 @@ archlinux_installdeps() {
$pacman -Q gtk3-classic >/dev/null 2>&1 && gtk=gtk3-classic
libs="zlib mesa gettext sdl2 wxgtk3 $gtk sfml openal"
libs="zlib mesa gettext sdl2 wxgtk3 $gtk openal"
[ -n "$ENABLE_FFMPEG" ] && libs="$libs ffmpeg"
@ -882,7 +870,7 @@ archlinux_installdeps() {
else
# try to build 32 bit binaries
# lib32-sfml and lib32-ffmpeg are in AUR
# lib32-ffmpeg is in AUR
archlinux_require_yaourt
# enable multilib repos if not enabled
@ -976,8 +964,6 @@ EOF
# get the necessary win32 headers
git submodule update --init --remote --recursive
warning 'SFML is required for LINK support, the SFML package in AUR is currently broken, if you want LINK support you will need to install it manually'
fi
build_instructions
@ -1040,13 +1026,12 @@ solus_installdeps() {
done
else
# no 32bit versions of these
set -- "$@" SFML-devel ffmpeg-devel
set -- "$@" ffmpeg-devel
fi
check sudo eopkg -y install "$@"
if [ -n "$amd64" -a "$target" = m32 ]; then
warning 'SFML is required for LINK support, there is no 32 bit SFML package in Solus currently, if you want LINK support you will need to install it manually'
warning 'ffmpeg is required for game recording, there is no 32 bit ffmpeg package in Solus currently, you may wish to install it manually'
fi
@ -1068,7 +1053,6 @@ gentoo_installdeps() {
dev-util/ccache \
sys-devel/binutils \
media-libs/libsdl2 \
media-libs/libsfml \
media-libs/openal \
x11-libs/wxGTK:$wx_slot \
sys-libs/zlib \
@ -1086,7 +1070,7 @@ gentoo_installdeps() {
alpine_installdeps() {
installing
check sudo apk add cmake ninja g++ ccache nasm gettext-dev zlib-dev mesa-dev sdl2-dev glu-dev sfml-dev wxwidgets-dev gtk+3.0-dev zip
check sudo apk add cmake ninja g++ ccache nasm gettext-dev zlib-dev mesa-dev sdl2-dev glu-dev wxwidgets-dev gtk+3.0-dev zip
build_instructions
}
@ -1121,7 +1105,7 @@ windows_installdeps() {
;;
esac
pkgs="$pkgs SDL2 sfml wxWidgets3.2 zlib binutils cmake crt-git headers-git make pkgconf tools-git windows-default-manifest libmangle-git ninja gdb ccache openal"
pkgs="$pkgs SDL2 wxWidgets3.2 zlib binutils cmake crt-git headers-git make pkgconf tools-git windows-default-manifest libmangle-git ninja gdb ccache openal"
case "$target" in
*x86_64)
@ -1197,11 +1181,6 @@ brew_installdeps() {
[ -n "$ENABLE_FFMPEG" ] && brews="$brews ffmpeg"
# sfml brew currently broken in the travis mac environment
# if [ -z "$TRAVIS" ]; then
brews="$brews sfml"
# fi
# This is necessary for the GitHub Actions CI:
brew -v install python
brew link --overwrite python
@ -1224,7 +1203,7 @@ macports_installdeps() {
check sudo port -v selfupdate
ports="cmake ccache nasm gettext pkgconfig libsdl2 sfml wxWidgets-3.0 libiconv ninja"
ports="cmake ccache nasm gettext pkgconfig libsdl2 wxWidgets-3.0 libiconv ninja"
[ -n "$ENABLE_FFMPEG" ] && ports="$ports ffmpeg"
@ -1240,7 +1219,7 @@ fink_installdeps() {
check sudo fink -vy selfupdate
pkgs="cmake ccache nasm libgettext8-dev gettext-tools pkgconfig sdl2 wxwidgets300-osxcocoa libiconv-dev sfml24-dev ccache ninja"
pkgs="cmake ccache nasm libgettext8-dev gettext-tools pkgconfig sdl2 wxwidgets300-osxcocoa libiconv-dev ccache ninja"
[ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs ffmpeg"

View File

@ -19,8 +19,6 @@ parts:
cmake-generator: Ninja
stage-packages:
- libsdl2-2.0-0
- libsfml-network2.5
- libsfml-system2.5
- libnotify4
- libsm6
- libopenal1
@ -46,7 +44,6 @@ parts:
- zip
- libnotify-dev
- libsdl2-dev
- libsfml-dev
- libgtk-3-dev
- libopenal-dev
- libwxgtk3.0-gtk3-dev

View File

@ -26,7 +26,7 @@
#include <cstring>
#include <string>
#include <SFML/Network.hpp>
#include "SFML/Network.hpp"
#include <libintl.h>
#define _(x) gettext(x)

View File

@ -7,7 +7,7 @@
#include <cstdint>
#include <SFML/Network.hpp>
#include "SFML/Network.hpp"
class GBASockClient {
public:

164
third_party/sfml/include/SFML/Config.hpp vendored Normal file
View File

@ -0,0 +1,164 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// SFML version
////////////////////////////////////////////////////////////
#define SFML_VERSION_MAJOR 3
#define SFML_VERSION_MINOR 1
#define SFML_VERSION_PATCH 0
#define SFML_VERSION_IS_RELEASE false
////////////////////////////////////////////////////////////
// Identify the operating system
// see https://sourceforge.net/p/predef/wiki/Home/
////////////////////////////////////////////////////////////
#if defined(_WIN32)
// Windows
#define SFML_SYSTEM_WINDOWS
#ifndef NOMINMAX
#define NOMINMAX
#endif
#elif defined(__APPLE__) && defined(__MACH__)
// Apple platform, see which one it is
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
// iOS
#define SFML_SYSTEM_IOS
#elif TARGET_OS_MAC
// macOS
#define SFML_SYSTEM_MACOS
#else
// Unsupported Apple system
#error This Apple operating system is not supported by SFML library
#endif
#elif defined(__unix__)
// UNIX system, see which one it is
#if defined(__ANDROID__)
// Android
#define SFML_SYSTEM_ANDROID
#elif defined(__linux__)
// Linux
#define SFML_SYSTEM_LINUX
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
// FreeBSD
#define SFML_SYSTEM_FREEBSD
#elif defined(__OpenBSD__)
// OpenBSD
#define SFML_SYSTEM_OPENBSD
#elif defined(__NetBSD__)
// NetBSD
#define SFML_SYSTEM_NETBSD
#else
// Unsupported UNIX system
#error This UNIX operating system is not supported by SFML library
#endif
#else
// Unsupported system
#error This operating system is not supported by SFML library
#endif
////////////////////////////////////////////////////////////
// Ensure minimum C++ language standard version is met
////////////////////////////////////////////////////////////
#if (defined(_MSVC_LANG) && _MSVC_LANG < 201703L) || (!defined(_MSVC_LANG) && __cplusplus < 201703L)
#error "Enable C++17 or newer for your compiler (e.g. -std=c++17 for GCC/Clang or /std:c++17 for MSVC)"
#endif
////////////////////////////////////////////////////////////
// Portable debug macro
////////////////////////////////////////////////////////////
#if !defined(NDEBUG)
#define SFML_DEBUG
#endif
////////////////////////////////////////////////////////////
// Helpers to create portable import / export macros for each module
////////////////////////////////////////////////////////////
#if !defined(SFML_STATIC)
#if defined(SFML_SYSTEM_WINDOWS)
// Windows compilers need specific (and different) keywords for export and import
#define SFML_API_EXPORT __declspec(dllexport)
#define SFML_API_IMPORT __declspec(dllimport)
// For Visual C++ compilers, we also need to turn off this annoying C4251 & C4275 warning
#ifdef _MSC_VER
#pragma warning(disable : 4251) // Using standard library types in our own exported types is okay
#pragma warning(disable : 4275) // Exporting types derived from the standard library is okay
#endif
#else // Linux, FreeBSD, macOS
#define SFML_API_EXPORT __attribute__((__visibility__("default")))
#define SFML_API_IMPORT __attribute__((__visibility__("default")))
#endif
#else
// Static build doesn't need import/export macros
#define SFML_API_EXPORT
#define SFML_API_IMPORT
#endif

View File

@ -0,0 +1,51 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Ftp.hpp>
#include <SFML/Network/Http.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Packet.hpp>
#include <SFML/Network/Socket.hpp>
#include <SFML/Network/SocketHandle.hpp>
#include <SFML/Network/SocketSelector.hpp>
#include <SFML/Network/TcpListener.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/Network/UdpSocket.hpp>
#include <SFML/System.hpp>
////////////////////////////////////////////////////////////
/// \defgroup network Network module
///
/// Socket-based communication, utilities and higher-level
/// network protocols (HTTP, FTP).
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,44 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
////////////////////////////////////////////////////////////
// Portable import / export macros
////////////////////////////////////////////////////////////
#if defined(SFML_NETWORK_EXPORTS)
#define SFML_NETWORK_API SFML_API_EXPORT
#else
#define SFML_NETWORK_API SFML_API_IMPORT
#endif

View File

@ -0,0 +1,629 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/System/Time.hpp>
#include <filesystem>
#include <string>
#include <vector>
namespace sf
{
class IpAddress;
////////////////////////////////////////////////////////////
/// \brief A FTP client
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Ftp
{
public:
////////////////////////////////////////////////////////////
/// \brief Enumeration of transfer modes
///
////////////////////////////////////////////////////////////
enum class TransferMode
{
Binary, //!< Binary mode (file is transferred as a sequence of bytes)
Ascii, //!< Text mode using ASCII encoding
Ebcdic //!< Text mode using EBCDIC encoding
};
////////////////////////////////////////////////////////////
/// \brief FTP response
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Response
{
public:
////////////////////////////////////////////////////////////
/// \brief Status codes possibly returned by a FTP response
///
////////////////////////////////////////////////////////////
enum class Status
{
// 1xx: the requested action is being initiated,
// expect another reply before proceeding with a new command
RestartMarkerReply = 110, //!< Restart marker reply
ServiceReadySoon = 120, //!< Service ready in N minutes
DataConnectionAlreadyOpened = 125, //!< Data connection already opened, transfer starting
OpeningDataConnection = 150, //!< File status ok, about to open data connection
// 2xx: the requested action has been successfully completed
Ok = 200, //!< Command ok
PointlessCommand = 202, //!< Command not implemented
SystemStatus = 211, //!< System status, or system help reply
DirectoryStatus = 212, //!< Directory status
FileStatus = 213, //!< File status
HelpMessage = 214, //!< Help message
SystemType = 215, //!< NAME system type, where NAME is an official system name from the list in the Assigned Numbers document
ServiceReady = 220, //!< Service ready for new user
ClosingConnection = 221, //!< Service closing control connection
DataConnectionOpened = 225, //!< Data connection open, no transfer in progress
ClosingDataConnection = 226, //!< Closing data connection, requested file action successful
EnteringPassiveMode = 227, //!< Entering passive mode
LoggedIn = 230, //!< User logged in, proceed. Logged out if appropriate
FileActionOk = 250, //!< Requested file action ok
DirectoryOk = 257, //!< PATHNAME created
// 3xx: the command has been accepted, but the requested action
// is dormant, pending receipt of further information
NeedPassword = 331, //!< User name ok, need password
NeedAccountToLogIn = 332, //!< Need account for login
NeedInformation = 350, //!< Requested file action pending further information
// 4xx: the command was not accepted and the requested action did not take place,
// but the error condition is temporary and the action may be requested again
ServiceUnavailable = 421, //!< Service not available, closing control connection
DataConnectionUnavailable = 425, //!< Can't open data connection
TransferAborted = 426, //!< Connection closed, transfer aborted
FileActionAborted = 450, //!< Requested file action not taken
LocalError = 451, //!< Requested action aborted, local error in processing
InsufficientStorageSpace = 452, //!< Requested action not taken; insufficient storage space in system, file unavailable
// 5xx: the command was not accepted and
// the requested action did not take place
CommandUnknown = 500, //!< Syntax error, command unrecognized
ParametersUnknown = 501, //!< Syntax error in parameters or arguments
CommandNotImplemented = 502, //!< Command not implemented
BadCommandSequence = 503, //!< Bad sequence of commands
ParameterNotImplemented = 504, //!< Command not implemented for that parameter
NotLoggedIn = 530, //!< Not logged in
NeedAccountToStore = 532, //!< Need account for storing files
FileUnavailable = 550, //!< Requested action not taken, file unavailable
PageTypeUnknown = 551, //!< Requested action aborted, page type unknown
NotEnoughMemory = 552, //!< Requested file action aborted, exceeded storage allocation
FilenameNotAllowed = 553, //!< Requested action not taken, file name not allowed
// 10xx: SFML custom codes
InvalidResponse = 1000, //!< Not part of the FTP standard, generated by SFML when a received response cannot be parsed
ConnectionFailed = 1001, //!< Not part of the FTP standard, generated by SFML when the low-level socket connection with the server fails
ConnectionClosed = 1002, //!< Not part of the FTP standard, generated by SFML when the low-level socket connection is unexpectedly closed
InvalidFile = 1003 //!< Not part of the FTP standard, generated by SFML when a local file cannot be read or written
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// This constructor is used by the FTP client to build
/// the response.
///
/// \param code Response status code
/// \param message Response message
///
////////////////////////////////////////////////////////////
explicit Response(Status code = Status::InvalidResponse, std::string message = "");
////////////////////////////////////////////////////////////
/// \brief Check if the status code means a success
///
/// This function is defined for convenience, it is
/// equivalent to testing if the status code is < 400.
///
/// \return `true` if the status is a success, `false` if it is a failure
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool isOk() const;
////////////////////////////////////////////////////////////
/// \brief Get the status code of the response
///
/// \return Status code
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status getStatus() const;
////////////////////////////////////////////////////////////
/// \brief Get the full message contained in the response
///
/// \return The response message
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::string& getMessage() const;
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
Status m_status; //!< Status code returned from the server
std::string m_message; //!< Last message received from the server
};
////////////////////////////////////////////////////////////
/// \brief Specialization of FTP response returning a directory
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API DirectoryResponse : public Response
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// \param response Source response
///
////////////////////////////////////////////////////////////
DirectoryResponse(const Response& response);
////////////////////////////////////////////////////////////
/// \brief Get the directory returned in the response
///
/// \return Directory name
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::filesystem::path& getDirectory() const;
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::filesystem::path m_directory; //!< Directory extracted from the response message
};
////////////////////////////////////////////////////////////
/// \brief Specialization of FTP response returning a
/// file name listing
////////////////////////////////////////////////////////////
class SFML_NETWORK_API ListingResponse : public Response
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// \param response Source response
/// \param data Data containing the raw listing
///
////////////////////////////////////////////////////////////
ListingResponse(const Response& response, const std::string& data);
////////////////////////////////////////////////////////////
/// \brief Return the array of directory/file names
///
/// \return Array containing the requested listing
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::vector<std::string>& getListing() const;
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::vector<std::string> m_listing; //!< Directory/file names extracted from the data
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
Ftp() = default;
////////////////////////////////////////////////////////////
/// \brief Destructor
///
/// Automatically closes the connection with the server if
/// it is still opened.
///
////////////////////////////////////////////////////////////
~Ftp();
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
Ftp(const Ftp&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
Ftp& operator=(const Ftp&) = delete;
////////////////////////////////////////////////////////////
/// \brief Connect to the specified FTP server
///
/// The port has a default value of 21, which is the standard
/// port used by the FTP protocol. You shouldn't use a different
/// value, unless you really know what you do.
/// This function tries to connect to the server so it may take
/// a while to complete, especially if the server is not
/// reachable. To avoid blocking your application for too long,
/// you can use a timeout. The default value, `Time::Zero`, means that the
/// system timeout will be used (which is usually pretty long).
///
/// \param server Name or address of the FTP server to connect to
/// \param port Port used for the connection
/// \param timeout Maximum time to wait
///
/// \return Server response to the request
///
/// \see `disconnect`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response connect(IpAddress server, unsigned short port = 21, Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
/// \brief Close the connection with the server
///
/// \return Server response to the request
///
/// \see `connect`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response disconnect();
////////////////////////////////////////////////////////////
/// \brief Log in using an anonymous account
///
/// Logging in is mandatory after connecting to the server.
/// Users that are not logged in cannot perform any operation.
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response login();
////////////////////////////////////////////////////////////
/// \brief Log in using a username and a password
///
/// Logging in is mandatory after connecting to the server.
/// Users that are not logged in cannot perform any operation.
///
/// \param name User name
/// \param password Password
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response login(const std::string& name, const std::string& password);
////////////////////////////////////////////////////////////
/// \brief Send a null command to keep the connection alive
///
/// This command is useful because the server may close the
/// connection automatically if no command is sent.
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response keepAlive();
////////////////////////////////////////////////////////////
/// \brief Get the current working directory
///
/// The working directory is the root path for subsequent
/// operations involving directories and/or filenames.
///
/// \return Server response to the request
///
/// \see `getDirectoryListing`, `changeDirectory`, `parentDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] DirectoryResponse getWorkingDirectory();
////////////////////////////////////////////////////////////
/// \brief Get the contents of the given directory
///
/// This function retrieves the sub-directories and files
/// contained in the given directory. It is not recursive.
/// The `directory` parameter is relative to the current
/// working directory.
///
/// \param directory Directory to list
///
/// \return Server response to the request
///
/// \see `getWorkingDirectory`, `changeDirectory`, `parentDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] ListingResponse getDirectoryListing(const std::string& directory = "");
////////////////////////////////////////////////////////////
/// \brief Change the current working directory
///
/// The new directory must be relative to the current one.
///
/// \param directory New working directory
///
/// \return Server response to the request
///
/// \see `getWorkingDirectory`, `getDirectoryListing`, `parentDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response changeDirectory(const std::string& directory);
////////////////////////////////////////////////////////////
/// \brief Go to the parent directory of the current one
///
/// \return Server response to the request
///
/// \see `getWorkingDirectory`, `getDirectoryListing`, `changeDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response parentDirectory();
////////////////////////////////////////////////////////////
/// \brief Create a new directory
///
/// The new directory is created as a child of the current
/// working directory.
///
/// \param name Name of the directory to create
///
/// \return Server response to the request
///
/// \see `deleteDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response createDirectory(const std::string& name);
////////////////////////////////////////////////////////////
/// \brief Remove an existing directory
///
/// The directory to remove must be relative to the
/// current working directory.
/// Use this function with caution, the directory will
/// be removed permanently!
///
/// \param name Name of the directory to remove
///
/// \return Server response to the request
///
/// \see `createDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response deleteDirectory(const std::string& name);
////////////////////////////////////////////////////////////
/// \brief Rename an existing file
///
/// The file names must be relative to the current working
/// directory.
///
/// \param file File to rename
/// \param newName New name of the file
///
/// \return Server response to the request
///
/// \see `deleteFile`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response renameFile(const std::filesystem::path& file, const std::filesystem::path& newName);
////////////////////////////////////////////////////////////
/// \brief Remove an existing file
///
/// The file name must be relative to the current working
/// directory.
/// Use this function with caution, the file will be
/// removed permanently!
///
/// \param name File to remove
///
/// \return Server response to the request
///
/// \see `renameFile`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response deleteFile(const std::filesystem::path& name);
////////////////////////////////////////////////////////////
/// \brief Download a file from the server
///
/// The file name of the distant file is relative to the
/// current working directory of the server, and the local
/// destination path is relative to the current directory
/// of your application.
/// If a file with the same file name as the distant file
/// already exists in the local destination path, it will
/// be overwritten.
///
/// \param remoteFile File name of the distant file to download
/// \param localPath The directory in which to put the file on the local computer
/// \param mode Transfer mode
///
/// \return Server response to the request
///
/// \see `upload`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response download(const std::filesystem::path& remoteFile,
const std::filesystem::path& localPath,
TransferMode mode = TransferMode::Binary);
////////////////////////////////////////////////////////////
/// \brief Upload a file to the server
///
/// The name of the local file is relative to the current
/// working directory of your application, and the
/// remote path is relative to the current directory of the
/// FTP server.
///
/// The append parameter controls whether the remote file is
/// appended to or overwritten if it already exists.
///
/// \param localFile Path of the local file to upload
/// \param remotePath The directory in which to put the file on the server
/// \param mode Transfer mode
/// \param append Pass `true` to append to or `false` to overwrite the remote file if it already exists
///
/// \return Server response to the request
///
/// \see `download`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response upload(const std::filesystem::path& localFile,
const std::filesystem::path& remotePath,
TransferMode mode = TransferMode::Binary,
bool append = false);
////////////////////////////////////////////////////////////
/// \brief Send a command to the FTP server
///
/// While the most often used commands are provided as member
/// functions in the `sf::Ftp` class, this method can be used
/// to send any FTP command to the server. If the command
/// requires one or more parameters, they can be specified
/// in `parameter`. If the server returns information, you
/// can extract it from the response using `Response::getMessage()`.
///
/// \param command Command to send
/// \param parameter Command parameter
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response sendCommand(const std::string& command, const std::string& parameter = "");
private:
////////////////////////////////////////////////////////////
/// \brief Receive a response from the server
///
/// This function must be called after each call to
/// `sendCommand` that expects a response.
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
Response getResponse();
////////////////////////////////////////////////////////////
/// \brief Utility class for exchanging data with the server
/// on the data channel
///
////////////////////////////////////////////////////////////
class DataChannel;
friend class DataChannel;
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
TcpSocket m_commandSocket; //!< Socket holding the control connection with the server
std::string m_receiveBuffer; //!< Received command data that is yet to be processed
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::Ftp
/// \ingroup network
///
/// `sf::Ftp` is a very simple FTP client that allows you
/// to communicate with a FTP server. The FTP protocol allows
/// you to manipulate a remote file system (list files,
/// upload, download, create, remove, ...).
///
/// Using the FTP client consists of 4 parts:
/// \li Connecting to the FTP server
/// \li Logging in (either as a registered user or anonymously)
/// \li Sending commands to the server
/// \li Disconnecting (this part can be done implicitly by the destructor)
///
/// Every command returns a FTP response, which contains the
/// status code as well as a message from the server. Some
/// commands such as `getWorkingDirectory()` and `getDirectoryListing()`
/// return additional data, and use a class derived from
/// `sf::Ftp::Response` to provide this data. The most often used
/// commands are directly provided as member functions, but it is
/// also possible to use specific commands with the `sendCommand()` function.
///
/// Note that response statuses >= 1000 are not part of the FTP standard,
/// they are generated by SFML when an internal error occurs.
///
/// All commands, especially upload and download, may take some
/// time to complete. This is important to know if you don't want
/// to block your application while the server is completing
/// the task.
///
/// Usage example:
/// \code
/// // Create a new FTP client
/// sf::Ftp ftp;
///
/// // Connect to the server
/// sf::Ftp::Response response = ftp.connect("ftp://ftp.myserver.com");
/// if (response.isOk())
/// std::cout << "Connected" << std::endl;
///
/// // Log in
/// response = ftp.login("laurent", "dF6Zm89D");
/// if (response.isOk())
/// std::cout << "Logged in" << std::endl;
///
/// // Print the working directory
/// sf::Ftp::DirectoryResponse directory = ftp.getWorkingDirectory();
/// if (directory.isOk())
/// std::cout << "Working directory: " << directory.getDirectory() << std::endl;
///
/// // Create a new directory
/// response = ftp.createDirectory("files");
/// if (response.isOk())
/// std::cout << "Created new directory" << std::endl;
///
/// // Upload a file to this new directory
/// response = ftp.upload("local-path/file.txt", "files", sf::Ftp::TransferMode::Ascii);
/// if (response.isOk())
/// std::cout << "File uploaded" << std::endl;
///
/// // Send specific commands (here: FEAT to list supported FTP features)
/// response = ftp.sendCommand("FEAT");
/// if (response.isOk())
/// std::cout << "Feature list:\n" << response.getMessage() << std::endl;
///
/// // Disconnect from the server (optional)
/// ftp.disconnect();
/// \endcode
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,480 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/System/Time.hpp>
#include <iosfwd>
#include <map>
#include <optional>
#include <string>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief A HTTP client
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Http
{
public:
////////////////////////////////////////////////////////////
/// \brief HTTP request
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Request
{
public:
////////////////////////////////////////////////////////////
/// \brief Enumerate the available HTTP methods for a request
///
////////////////////////////////////////////////////////////
enum class Method
{
Get, //!< Request in get mode, standard method to retrieve a page
Post, //!< Request in post mode, usually to send data to a page
Head, //!< Request a page's header only
Put, //!< Request in put mode, useful for a REST API
Delete //!< Request in delete mode, useful for a REST API
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// This constructor creates a GET request, with the root
/// URI ("/") and an empty body.
///
/// \param uri Target URI
/// \param method Method to use for the request
/// \param body Content of the request's body
///
////////////////////////////////////////////////////////////
Request(const std::string& uri = "/", Method method = Method::Get, const std::string& body = "");
////////////////////////////////////////////////////////////
/// \brief Set the value of a field
///
/// The field is created if it doesn't exist. The name of
/// the field is case-insensitive.
/// By default, a request doesn't contain any field (but the
/// mandatory fields are added later by the HTTP client when
/// sending the request).
///
/// \param field Name of the field to set
/// \param value Value of the field
///
////////////////////////////////////////////////////////////
void setField(const std::string& field, const std::string& value);
////////////////////////////////////////////////////////////
/// \brief Set the request method
///
/// See the Method enumeration for a complete list of all
/// the available methods.
/// The method is `Http::Request::Method::Get` by default.
///
/// \param method Method to use for the request
///
////////////////////////////////////////////////////////////
void setMethod(Method method);
////////////////////////////////////////////////////////////
/// \brief Set the requested URI
///
/// The URI is the resource (usually a web page or a file)
/// that you want to get or post.
/// The URI is "/" (the root page) by default.
///
/// \param uri URI to request, relative to the host
///
////////////////////////////////////////////////////////////
void setUri(const std::string& uri);
////////////////////////////////////////////////////////////
/// \brief Set the HTTP version for the request
///
/// The HTTP version is 1.0 by default.
///
/// \param major Major HTTP version number
/// \param minor Minor HTTP version number
///
////////////////////////////////////////////////////////////
void setHttpVersion(unsigned int major, unsigned int minor);
////////////////////////////////////////////////////////////
/// \brief Set the body of the request
///
/// The body of a request is optional and only makes sense
/// for POST requests. It is ignored for all other methods.
/// The body is empty by default.
///
/// \param body Content of the body
///
////////////////////////////////////////////////////////////
void setBody(const std::string& body);
private:
friend class Http;
////////////////////////////////////////////////////////////
/// \brief Prepare the final request to send to the server
///
/// This is used internally by Http before sending the
/// request to the web server.
///
/// \return String containing the request, ready to be sent
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::string prepare() const;
////////////////////////////////////////////////////////////
/// \brief Check if the request defines a field
///
/// This function uses case-insensitive comparisons.
///
/// \param field Name of the field to test
///
/// \return `true` if the field exists, `false` otherwise
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool hasField(const std::string& field) const;
////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////
using FieldTable = std::map<std::string, std::string>; // Use an ordered map for predictable payloads
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
FieldTable m_fields; //!< Fields of the header associated to their value
Method m_method; //!< Method to use for the request
std::string m_uri; //!< Target URI of the request
unsigned int m_majorVersion{1}; //!< Major HTTP version
unsigned int m_minorVersion{}; //!< Minor HTTP version
std::string m_body; //!< Body of the request
};
////////////////////////////////////////////////////////////
/// \brief HTTP response
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Response
{
public:
////////////////////////////////////////////////////////////
/// \brief Enumerate all the valid status codes for a response
///
////////////////////////////////////////////////////////////
enum class Status
{
// 2xx: success
Ok = 200, //!< Most common code returned when operation was successful
Created = 201, //!< The resource has successfully been created
Accepted = 202, //!< The request has been accepted, but will be processed later by the server
NoContent = 204, //!< The server didn't send any data in return
ResetContent = 205, //!< The server informs the client that it should clear the view (form) that caused the request to be sent
PartialContent = 206, //!< The server has sent a part of the resource, as a response to a partial GET request
// 3xx: redirection
MultipleChoices = 300, //!< The requested page can be accessed from several locations
MovedPermanently = 301, //!< The requested page has permanently moved to a new location
MovedTemporarily = 302, //!< The requested page has temporarily moved to a new location
NotModified = 304, //!< For conditional requests, means the requested page hasn't changed and doesn't need to be refreshed
// 4xx: client error
BadRequest = 400, //!< The server couldn't understand the request (syntax error)
Unauthorized = 401, //!< The requested page needs an authentication to be accessed
Forbidden = 403, //!< The requested page cannot be accessed at all, even with authentication
NotFound = 404, //!< The requested page doesn't exist
RangeNotSatisfiable = 407, //!< The server can't satisfy the partial GET request (with a "Range" header field)
// 5xx: server error
InternalServerError = 500, //!< The server encountered an unexpected error
NotImplemented = 501, //!< The server doesn't implement a requested feature
BadGateway = 502, //!< The gateway server has received an error from the source server
ServiceNotAvailable = 503, //!< The server is temporarily unavailable (overloaded, in maintenance, ...)
GatewayTimeout = 504, //!< The gateway server couldn't receive a response from the source server
VersionNotSupported = 505, //!< The server doesn't support the requested HTTP version
// 10xx: SFML custom codes
InvalidResponse = 1000, //!< Response is not a valid HTTP one
ConnectionFailed = 1001 //!< Connection with server failed
};
////////////////////////////////////////////////////////////
/// \brief Get the value of a field
///
/// If the field `field` is not found in the response header,
/// the empty string is returned. This function uses
/// case-insensitive comparisons.
///
/// \param field Name of the field to get
///
/// \return Value of the field, or empty string if not found
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::string& getField(const std::string& field) const;
////////////////////////////////////////////////////////////
/// \brief Get the response status code
///
/// The status code should be the first thing to be checked
/// after receiving a response, it defines whether it is a
/// success, a failure or anything else (see the Status
/// enumeration).
///
/// \return Status code of the response
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status getStatus() const;
////////////////////////////////////////////////////////////
/// \brief Get the major HTTP version number of the response
///
/// \return Major HTTP version number
///
/// \see `getMinorHttpVersion`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned int getMajorHttpVersion() const;
////////////////////////////////////////////////////////////
/// \brief Get the minor HTTP version number of the response
///
/// \return Minor HTTP version number
///
/// \see `getMajorHttpVersion`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned int getMinorHttpVersion() const;
////////////////////////////////////////////////////////////
/// \brief Get the body of the response
///
/// The body of a response may contain:
/// \li the requested page (for GET requests)
/// \li a response from the server (for POST requests)
/// \li nothing (for HEAD requests)
/// \li an error message (in case of an error)
///
/// \return The response body
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::string& getBody() const;
private:
friend class Http;
////////////////////////////////////////////////////////////
/// \brief Construct the header from a response string
///
/// This function is used by `Http` to build the response
/// of a request.
///
/// \param data Content of the response to parse
///
////////////////////////////////////////////////////////////
void parse(const std::string& data);
////////////////////////////////////////////////////////////
/// \brief Read values passed in the answer header
///
/// This function is used by `Http` to extract values passed
/// in the response.
///
/// \param in String stream containing the header values
///
////////////////////////////////////////////////////////////
void parseFields(std::istream& in);
////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////
using FieldTable = std::map<std::string, std::string>; // Use an ordered map for predictable payloads
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
FieldTable m_fields; //!< Fields of the header
Status m_status{Status::ConnectionFailed}; //!< Status code
unsigned int m_majorVersion{}; //!< Major HTTP version
unsigned int m_minorVersion{}; //!< Minor HTTP version
std::string m_body; //!< Body of the response
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
Http() = default;
////////////////////////////////////////////////////////////
/// \brief Construct the HTTP client with the target host
///
/// This is equivalent to calling `setHost(host, port)`.
/// The port has a default value of 0, which means that the
/// HTTP client will use the right port according to the
/// protocol used (80 for HTTP). You should leave it like
/// this unless you really need a port other than the
/// standard one, or use an unknown protocol.
///
/// \param host Web server to connect to
/// \param port Port to use for connection
///
////////////////////////////////////////////////////////////
Http(const std::string& host, unsigned short port = 0);
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
Http(const Http&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
Http& operator=(const Http&) = delete;
////////////////////////////////////////////////////////////
/// \brief Set the target host
///
/// This function just stores the host address and port, it
/// doesn't actually connect to it until you send a request.
/// The port has a default value of 0, which means that the
/// HTTP client will use the right port according to the
/// protocol used (80 for HTTP). You should leave it like
/// this unless you really need a port other than the
/// standard one, or use an unknown protocol.
///
/// \param host Web server to connect to
/// \param port Port to use for connection
///
////////////////////////////////////////////////////////////
void setHost(const std::string& host, unsigned short port = 0);
////////////////////////////////////////////////////////////
/// \brief Send a HTTP request and return the server's response.
///
/// You must have a valid host before sending a request (see `setHost`).
/// Any missing mandatory header field in the request will be added
/// with an appropriate value.
/// Warning: this function waits for the server's response and may
/// not return instantly; use a thread if you don't want to block your
/// application, or use a timeout to limit the time to wait. A value
/// of `Time::Zero` means that the client will use the system default timeout
/// (which is usually pretty long).
///
/// \param request Request to send
/// \param timeout Maximum time to wait
///
/// \return Server's response
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response sendRequest(const Request& request, Time timeout = Time::Zero);
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
TcpSocket m_connection; //!< Connection to the host
std::optional<IpAddress> m_host; //!< Web host address
std::string m_hostName; //!< Web host name
unsigned short m_port{}; //!< Port used for connection with host
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::Http
/// \ingroup network
///
/// `sf::Http` is a very simple HTTP client that allows you
/// to communicate with a web server. You can retrieve
/// web pages, send data to an interactive resource,
/// download a remote file, etc. The HTTPS protocol is
/// not supported.
///
/// The HTTP client is split into 3 classes:
/// \li `sf::Http::Request`
/// \li `sf::Http::Response`
/// \li `sf::Http`
///
/// `sf::Http::Request` builds the request that will be
/// sent to the server. A request is made of:
/// \li a method (what you want to do)
/// \li a target URI (usually the name of the web page or file)
/// \li one or more header fields (options that you can pass to the server)
/// \li an optional body (for POST requests)
///
/// `sf::Http::Response` parse the response from the web server
/// and provides getters to read them. The response contains:
/// \li a status code
/// \li header fields (that may be answers to the ones that you requested)
/// \li a body, which contains the contents of the requested resource
///
/// `sf::Http` provides a simple function, SendRequest, to send a
/// `sf::Http::Request` and return the corresponding `sf::Http::Response`
/// from the server.
///
/// Usage example:
/// \code
/// // Create a new HTTP client
/// sf::Http http;
///
/// // We'll work on http://www.sfml-dev.org
/// http.setHost("http://www.sfml-dev.org");
///
/// // Prepare a request to get the 'features.php' page
/// sf::Http::Request request("features.php");
///
/// // Send the request
/// sf::Http::Response response = http.sendRequest(request);
///
/// // Check the status code and display the result
/// sf::Http::Response::Status status = response.getStatus();
/// if (status == sf::Http::Response::Status::Ok)
/// {
/// std::cout << response.getBody() << std::endl;
/// }
/// else
/// {
/// std::cout << "Error " << status << std::endl;
/// }
/// \endcode
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,297 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/System/Time.hpp>
#include <iosfwd>
#include <optional>
#include <string>
#include <string_view>
#include <cstdint>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Encapsulate an IPv4 network address
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API IpAddress
{
public:
////////////////////////////////////////////////////////////
/// \brief Construct the address from a null-terminated string view
///
/// Here \a address can be either a decimal address
/// (ex: "192.168.1.56") or a network name (ex: "localhost").
///
/// \param address IP address or network name
///
/// \return Address if provided argument was valid, otherwise `std::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<IpAddress> resolve(std::string_view address);
////////////////////////////////////////////////////////////
/// \brief Construct the address from 4 bytes
///
/// Calling `IpAddress(a, b, c, d)` is equivalent to calling
/// `IpAddress::resolve("a.b.c.d")`, but safer as it doesn't
/// have to parse a string to get the address components.
///
/// \param byte0 First byte of the address
/// \param byte1 Second byte of the address
/// \param byte2 Third byte of the address
/// \param byte3 Fourth byte of the address
///
////////////////////////////////////////////////////////////
IpAddress(std::uint8_t byte0, std::uint8_t byte1, std::uint8_t byte2, std::uint8_t byte3);
////////////////////////////////////////////////////////////
/// \brief Construct the address from a 32-bits integer
///
/// This constructor uses the internal representation of
/// the address directly. It should be used for optimization
/// purposes, and only if you got that representation from
/// `IpAddress::toInteger()`.
///
/// \param address 4 bytes of the address packed into a 32-bits integer
///
/// \see `toInteger`
///
////////////////////////////////////////////////////////////
explicit IpAddress(std::uint32_t address);
////////////////////////////////////////////////////////////
/// \brief Get a string representation of the address
///
/// The returned string is the decimal representation of the
/// IP address (like "192.168.1.56"), even if it was constructed
/// from a host name.
///
/// \return String representation of the address
///
/// \see `toInteger`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::string toString() const;
////////////////////////////////////////////////////////////
/// \brief Get an integer representation of the address
///
/// The returned number is the internal representation of the
/// address, and should be used for optimization purposes only
/// (like sending the address through a socket).
/// The integer produced by this function can then be converted
/// back to a `sf::IpAddress` with the proper constructor.
///
/// \return 32-bits unsigned integer representation of the address
///
/// \see `toString`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::uint32_t toInteger() const;
////////////////////////////////////////////////////////////
/// \brief Get the computer's local address
///
/// The local address is the address of the computer from the
/// LAN point of view, i.e. something like 192.168.1.56. It is
/// meaningful only for communications over the local network.
/// Unlike getPublicAddress, this function is fast and may be
/// used safely anywhere.
///
/// \return Local IP address of the computer on success, `std::nullopt` otherwise
///
/// \see `getPublicAddress`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<IpAddress> getLocalAddress();
////////////////////////////////////////////////////////////
/// \brief Get the computer's public address
///
/// The public address is the address of the computer from the
/// internet point of view, i.e. something like 89.54.1.169.
/// It is necessary for communications over the world wide web.
/// The only way to get a public address is to ask it to a
/// distant website; as a consequence, this function depends on
/// both your network connection and the server, and may be
/// very slow. You should use it as few as possible. Because
/// this function depends on the network connection and on a distant
/// server, you may use a time limit if you don't want your program
/// to be possibly stuck waiting in case there is a problem; this
/// limit is deactivated by default.
///
/// \param timeout Maximum time to wait
///
/// \return Public IP address of the computer on success, `std::nullopt` otherwise
///
/// \see `getLocalAddress`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<IpAddress> getPublicAddress(Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
// Static member data
////////////////////////////////////////////////////////////
// NOLINTBEGIN(readability-identifier-naming)
static const IpAddress Any; //!< Value representing any address (0.0.0.0)
static const IpAddress LocalHost; //!< The "localhost" address (for connecting a computer to itself locally)
static const IpAddress Broadcast; //!< The "broadcast" address (for sending UDP messages to everyone on a local network)
// NOLINTEND(readability-identifier-naming)
private:
friend SFML_NETWORK_API bool operator<(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::uint32_t m_address; //!< Address stored as an unsigned 32 bit integer
};
////////////////////////////////////////////////////////////
/// \brief Overload of `operator==` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if both addresses are equal
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator==(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator!=` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if both addresses are different
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator!=(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator<` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if `left` is lesser than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator<(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator>` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if `left` is greater than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator>(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator<=` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if \a left is lesser or equal than \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator<=(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator>=` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if `left` is greater or equal than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator>=(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator>>` to extract an IP address from an input stream
///
/// \param stream Input stream
/// \param address IP address to extract
///
/// \return Reference to the input stream
///
////////////////////////////////////////////////////////////
SFML_NETWORK_API std::istream& operator>>(std::istream& stream, std::optional<IpAddress>& address);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator<<` to print an IP address to an output stream
///
/// \param stream Output stream
/// \param address IP address to print
///
/// \return Reference to the output stream
///
////////////////////////////////////////////////////////////
SFML_NETWORK_API std::ostream& operator<<(std::ostream& stream, IpAddress address);
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::IpAddress
/// \ingroup network
///
/// `sf::IpAddress` is a utility class for manipulating network
/// addresses. It provides a set a implicit constructors and
/// conversion functions to easily build or transform an IP
/// address from/to various representations.
///
/// Usage example:
/// \code
/// auto a2 = sf::IpAddress::resolve("127.0.0.1"); // the local host address
/// auto a3 = sf::IpAddress::Broadcast; // the broadcast address
/// sf::IpAddress a4(192, 168, 1, 56); // a local address
/// auto a5 = sf::IpAddress::resolve("my_computer"); // a local address created from a network name
/// auto a6 = sf::IpAddress::resolve("89.54.1.169"); // a distant address
/// auto a7 = sf::IpAddress::resolve("www.google.com"); // a distant address created from a network name
/// auto a8 = sf::IpAddress::getLocalAddress(); // my address on the local network
/// auto a9 = sf::IpAddress::getPublicAddress(); // my address on the internet
/// \endcode
///
/// Note that `sf::IpAddress` currently doesn't support IPv6
/// nor other types of network addresses.
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,552 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <string>
#include <vector>
#include <cstddef>
#include <cstdint>
namespace sf
{
class String;
////////////////////////////////////////////////////////////
/// \brief Utility class to build blocks of data to transfer
/// over the network
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Packet
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Creates an empty packet.
///
////////////////////////////////////////////////////////////
Packet() = default;
////////////////////////////////////////////////////////////
/// \brief Virtual destructor
///
////////////////////////////////////////////////////////////
virtual ~Packet() = default;
////////////////////////////////////////////////////////////
/// \brief Copy constructor
///
////////////////////////////////////////////////////////////
Packet(const Packet&) = default;
////////////////////////////////////////////////////////////
/// \brief Copy assignment
///
////////////////////////////////////////////////////////////
Packet& operator=(const Packet&) = default;
////////////////////////////////////////////////////////////
/// \brief Move constructor
///
////////////////////////////////////////////////////////////
Packet(Packet&&) noexcept = default;
////////////////////////////////////////////////////////////
/// \brief Move assignment
///
////////////////////////////////////////////////////////////
Packet& operator=(Packet&&) noexcept = default;
////////////////////////////////////////////////////////////
/// \brief Append data to the end of the packet
///
/// \param data Pointer to the sequence of bytes to append
/// \param sizeInBytes Number of bytes to append
///
/// \see `clear`
/// \see `getReadPosition`
///
////////////////////////////////////////////////////////////
void append(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Get the current reading position in the packet
///
/// The next read operation will read data from this position
///
/// \return The byte offset of the current read position
///
/// \see `append`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::size_t getReadPosition() const;
////////////////////////////////////////////////////////////
/// \brief Clear the packet
///
/// After calling Clear, the packet is empty.
///
/// \see `append`
///
////////////////////////////////////////////////////////////
void clear();
////////////////////////////////////////////////////////////
/// \brief Get a pointer to the data contained in the packet
///
/// Warning: the returned pointer may become invalid after
/// you append data to the packet, therefore it should never
/// be stored.
/// The return pointer is a `nullptr` if the packet is empty.
///
/// \return Pointer to the data
///
/// \see `getDataSize`
///
////////////////////////////////////////////////////////////
[[nodiscard]] const void* getData() const;
////////////////////////////////////////////////////////////
/// \brief Get the size of the data contained in the packet
///
/// This function returns the number of bytes pointed to by
/// what `getData` returns.
///
/// \return Data size, in bytes
///
/// \see `getData`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::size_t getDataSize() const;
////////////////////////////////////////////////////////////
/// \brief Tell if the reading position has reached the
/// end of the packet
///
/// This function is useful to know if there is some data
/// left to be read, without actually reading it.
///
/// \return `true` if all data was read, `false` otherwise
///
/// \see `operator` bool
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool endOfPacket() const;
////////////////////////////////////////////////////////////
/// \brief Test the validity of the packet, for reading
///
/// This operator allows to test the packet as a boolean
/// variable, to check if a reading operation was successful.
///
/// A packet will be in an invalid state if it has no more
/// data to read.
///
/// This behavior is the same as standard C++ streams.
///
/// Usage example:
/// \code
/// float x;
/// packet >> x;
/// if (packet)
/// {
/// // ok, x was extracted successfully
/// }
///
/// // -- or --
///
/// float x;
/// if (packet >> x)
/// {
/// // ok, x was extracted successfully
/// }
/// \endcode
///
/// Don't focus on the return type, it's equivalent to bool but
/// it disallows unwanted implicit conversions to integer or
/// pointer types.
///
/// \return `true` if last data extraction from packet was successful
///
/// \see `endOfPacket`
///
////////////////////////////////////////////////////////////
explicit operator bool() const;
////////////////////////////////////////////////////////////
/// Overload of `operator>>` to read data from the packet
///
////////////////////////////////////////////////////////////
Packet& operator>>(bool& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::int8_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::uint8_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::int16_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::uint16_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::int32_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::uint32_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::int64_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::uint64_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(float& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(double& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(char* data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::string& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(wchar_t* data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::wstring& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(String& data);
////////////////////////////////////////////////////////////
/// Overload of `operator<<` to write data into the packet
///
////////////////////////////////////////////////////////////
Packet& operator<<(bool data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::int8_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::uint8_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::int16_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::uint16_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::int32_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::uint32_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::int64_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::uint64_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(float data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(double data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const char* data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const std::string& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const wchar_t* data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const std::wstring& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const String& data);
protected:
friend class TcpSocket;
friend class UdpSocket;
////////////////////////////////////////////////////////////
/// \brief Called before the packet is sent over the network
///
/// This function can be defined by derived classes to
/// transform the data before it is sent; this can be
/// used for compression, encryption, etc.
/// The function must return a pointer to the modified data,
/// as well as the number of bytes pointed.
/// The default implementation provides the packet's data
/// without transforming it.
///
/// \param size Variable to fill with the size of data to send
///
/// \return Pointer to the array of bytes to send
///
/// \see `onReceive`
///
////////////////////////////////////////////////////////////
virtual const void* onSend(std::size_t& size);
////////////////////////////////////////////////////////////
/// \brief Called after the packet is received over the network
///
/// This function can be defined by derived classes to
/// transform the data after it is received; this can be
/// used for decompression, decryption, etc.
/// The function receives a pointer to the received data,
/// and must fill the packet with the transformed bytes.
/// The default implementation fills the packet directly
/// without transforming the data.
///
/// \param data Pointer to the received bytes
/// \param size Number of bytes
///
/// \see `onSend`
///
////////////////////////////////////////////////////////////
virtual void onReceive(const void* data, std::size_t size);
private:
////////////////////////////////////////////////////////////
/// \brief Check if the packet can extract a given number of bytes
///
/// This function updates accordingly the state of the packet.
///
/// \param size Size to check
///
/// \return `true` if \a size bytes can be read from the packet
///
////////////////////////////////////////////////////////////
bool checkSize(std::size_t size);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::vector<std::byte> m_data; //!< Data stored in the packet
std::size_t m_readPos{}; //!< Current reading position in the packet
std::size_t m_sendPos{}; //!< Current send position in the packet (for handling partial sends)
bool m_isValid{true}; //!< Reading state of the packet
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::Packet
/// \ingroup network
///
/// Packets provide a safe and easy way to serialize data,
/// in order to send it over the network using sockets
/// (`sf::TcpSocket`, `sf::UdpSocket`).
///
/// Packets solve 2 fundamental problems that arise when
/// transferring data over the network:
/// \li data is interpreted correctly according to the endianness
/// \li the bounds of the packet are preserved (one send == one receive)
///
/// The `sf::Packet` class provides both input and output modes.
/// It is designed to follow the behavior of standard C++ streams,
/// using operators >> and << to extract and insert data.
///
/// It is recommended to use only fixed-size types (like `std::int32_t`, etc.),
/// to avoid possible differences between the sender and the receiver.
/// Indeed, the native C++ types may have different sizes on two platforms
/// and your data may be corrupted if that happens.
///
/// Usage example:
/// \code
/// std::uint32_t x = 24;
/// std::string s = "hello";
/// double d = 5.89;
///
/// // Group the variables to send into a packet
/// sf::Packet packet;
/// packet << x << s << d;
///
/// // Send it over the network (socket is a valid sf::TcpSocket)
/// socket.send(packet);
///
/// -----------------------------------------------------------------
///
/// // Receive the packet at the other end
/// sf::Packet packet;
/// socket.receive(packet);
///
/// // Extract the variables contained in the packet
/// std::uint32_t x;
/// std::string s;
/// double d;
/// if (packet >> x >> s >> d)
/// {
/// // Data extracted successfully...
/// }
/// \endcode
///
/// Packets have built-in `operator>>` and << overloads for
/// standard types:
/// \li `bool`
/// \li fixed-size integer types (`int[8|16|32]_t`, `uint[8|16|32]_t`)
/// \li floating point numbers (`float`, `double`)
/// \li string types (`char*`, `wchar_t*`, `std::string`, `std::wstring`, `sf::String`)
///
/// Like standard streams, it is also possible to define your own
/// overloads of operators >> and << in order to handle your
/// custom types.
///
/// \code
/// struct MyStruct
/// {
/// float number{};
/// std::int8_t integer{};
/// std::string str;
/// };
///
/// sf::Packet& operator <<(sf::Packet& packet, const MyStruct& m)
/// {
/// return packet << m.number << m.integer << m.str;
/// }
///
/// sf::Packet& operator >>(sf::Packet& packet, MyStruct& m)
/// {
/// return packet >> m.number >> m.integer >> m.str;
/// }
/// \endcode
///
/// Packets also provide an extra feature that allows to apply
/// custom transformations to the data before it is sent,
/// and after it is received. This is typically used to
/// handle automatic compression or encryption of the data.
/// This is achieved by inheriting from `sf::Packet`, and overriding
/// the onSend and onReceive functions.
///
/// Here is an example:
/// \code
/// class ZipPacket : public sf::Packet
/// {
/// const void* onSend(std::size_t& size) override
/// {
/// const void* srcData = getData();
/// std::size_t srcSize = getDataSize();
///
/// return MySuperZipFunction(srcData, srcSize, &size);
/// }
///
/// void onReceive(const void* data, std::size_t size) override
/// {
/// std::size_t dstSize;
/// const void* dstData = MySuperUnzipFunction(data, size, &dstSize);
///
/// append(dstData, dstSize);
/// }
/// };
///
/// // Use like regular packets:
/// ZipPacket packet;
/// packet << x << s << d;
/// ...
/// \endcode
///
/// \see `sf::TcpSocket`, `sf::UdpSocket`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,229 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/SocketHandle.hpp>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Base class for all the socket types
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Socket
{
public:
////////////////////////////////////////////////////////////
/// \brief Status codes that may be returned by socket functions
///
////////////////////////////////////////////////////////////
enum class Status
{
Done, //!< The socket has sent / received the data
NotReady, //!< The socket is not ready to send / receive data yet
Partial, //!< The socket sent a part of the data
Disconnected, //!< The TCP socket has been disconnected
Error //!< An unexpected error happened
};
////////////////////////////////////////////////////////////
/// \brief Some special values used by sockets
///
////////////////////////////////////////////////////////////
// NOLINTNEXTLINE(readability-identifier-naming)
static constexpr unsigned short AnyPort{0}; //!< Special value that tells the system to pick any available port
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
virtual ~Socket();
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
Socket(const Socket&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
Socket& operator=(const Socket&) = delete;
////////////////////////////////////////////////////////////
/// \brief Move constructor
///
////////////////////////////////////////////////////////////
Socket(Socket&& socket) noexcept;
////////////////////////////////////////////////////////////
/// \brief Move assignment
///
////////////////////////////////////////////////////////////
Socket& operator=(Socket&& socket) noexcept;
////////////////////////////////////////////////////////////
/// \brief Set the blocking state of the socket
///
/// In blocking mode, calls will not return until they have
/// completed their task. For example, a call to Receive in
/// blocking mode won't return until some data was actually
/// received.
/// In non-blocking mode, calls will always return immediately,
/// using the return code to signal whether there was data
/// available or not.
/// By default, all sockets are blocking.
///
/// \param blocking `true` to set the socket as blocking, `false` for non-blocking
///
/// \see `isBlocking`
///
////////////////////////////////////////////////////////////
void setBlocking(bool blocking);
////////////////////////////////////////////////////////////
/// \brief Tell whether the socket is in blocking or non-blocking mode
///
/// \return `true` if the socket is blocking, `false` otherwise
///
/// \see `setBlocking`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool isBlocking() const;
protected:
////////////////////////////////////////////////////////////
/// \brief Types of protocols that the socket can use
///
////////////////////////////////////////////////////////////
enum class Type
{
Tcp, //!< TCP protocol
Udp //!< UDP protocol
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// This constructor can only be accessed by derived classes.
///
/// \param type Type of the socket (TCP or UDP)
///
////////////////////////////////////////////////////////////
explicit Socket(Type type);
////////////////////////////////////////////////////////////
/// \brief Return the internal handle of the socket
///
/// The returned handle may be invalid if the socket
/// was not created yet (or already destroyed).
/// This function can only be accessed by derived classes.
///
/// \return The internal (OS-specific) handle of the socket
///
////////////////////////////////////////////////////////////
[[nodiscard]] SocketHandle getNativeHandle() const;
////////////////////////////////////////////////////////////
/// \brief Create the internal representation of the socket
///
/// This function can only be accessed by derived classes.
///
////////////////////////////////////////////////////////////
void create();
////////////////////////////////////////////////////////////
/// \brief Create the internal representation of the socket
/// from a socket handle
///
/// This function can only be accessed by derived classes.
///
/// \param handle OS-specific handle of the socket to wrap
///
////////////////////////////////////////////////////////////
void create(SocketHandle handle);
////////////////////////////////////////////////////////////
/// \brief Close the socket gracefully
///
/// This function can only be accessed by derived classes.
///
////////////////////////////////////////////////////////////
void close();
private:
friend class SocketSelector;
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
Type m_type; //!< Type of the socket (TCP or UDP)
SocketHandle m_socket; //!< Socket descriptor
bool m_isBlocking{true}; //!< Current blocking mode of the socket
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::Socket
/// \ingroup network
///
/// This class mainly defines internal stuff to be used by
/// derived classes.
///
/// The only public features that it defines, and which
/// is therefore common to all the socket classes, is the
/// blocking state. All sockets can be set as blocking or
/// non-blocking.
///
/// In blocking mode, socket functions will hang until
/// the operation completes, which means that the entire
/// program (well, in fact the current thread if you use
/// multiple ones) will be stuck waiting for your socket
/// operation to complete.
///
/// In non-blocking mode, all the socket functions will
/// return immediately. If the socket is not ready to complete
/// the requested operation, the function simply returns
/// the proper status code (`Socket::Status::NotReady`).
///
/// The default mode, which is blocking, is the one that is
/// generally used, in combination with threads or selectors.
/// The non-blocking mode is rather used in real-time
/// applications that run an endless loop that can poll
/// the socket often enough, and cannot afford blocking
/// this loop.
///
/// \see `sf::TcpListener`, `sf::TcpSocket`, `sf::UdpSocket`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,52 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
#if defined(SFML_SYSTEM_WINDOWS)
#include <basetsd.h>
#endif
namespace sf
{
////////////////////////////////////////////////////////////
// Low-level socket handle type, specific to each platform
////////////////////////////////////////////////////////////
#if defined(SFML_SYSTEM_WINDOWS)
using SocketHandle = UINT_PTR;
#else
using SocketHandle = int;
#endif
} // namespace sf

View File

@ -0,0 +1,273 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/System/Time.hpp>
#include <memory>
namespace sf
{
class Socket;
////////////////////////////////////////////////////////////
/// \brief Multiplexer that allows to read from multiple sockets
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API SocketSelector
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
SocketSelector();
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
~SocketSelector();
////////////////////////////////////////////////////////////
/// \brief Copy constructor
///
/// \param copy Instance to copy
///
////////////////////////////////////////////////////////////
SocketSelector(const SocketSelector& copy);
////////////////////////////////////////////////////////////
/// \brief Overload of assignment operator
///
/// \param right Instance to assign
///
/// \return Reference to self
///
////////////////////////////////////////////////////////////
SocketSelector& operator=(const SocketSelector& right);
////////////////////////////////////////////////////////////
/// \brief Move constructor
///
////////////////////////////////////////////////////////////
SocketSelector(SocketSelector&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Move assignment
///
////////////////////////////////////////////////////////////
SocketSelector& operator=(SocketSelector&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Add a new socket to the selector
///
/// This function keeps a weak reference to the socket,
/// so you have to make sure that the socket is not destroyed
/// while it is stored in the selector.
/// This function does nothing if the socket is not valid.
///
/// \param socket Reference to the socket to add
///
/// \see `remove`, `clear`
///
////////////////////////////////////////////////////////////
void add(Socket& socket);
////////////////////////////////////////////////////////////
/// \brief Remove a socket from the selector
///
/// This function doesn't destroy the socket, it simply
/// removes the reference that the selector has to it.
///
/// \param socket Reference to the socket to remove
///
/// \see `add`, `clear`
///
////////////////////////////////////////////////////////////
void remove(Socket& socket);
////////////////////////////////////////////////////////////
/// \brief Remove all the sockets stored in the selector
///
/// This function doesn't destroy any instance, it simply
/// removes all the references that the selector has to
/// external sockets.
///
/// \see `add`, `remove`
///
////////////////////////////////////////////////////////////
void clear();
////////////////////////////////////////////////////////////
/// \brief Wait until one or more sockets are ready to receive
///
/// This function returns as soon as at least one socket has
/// some data available to be received. To know which sockets are
/// ready, use the `isReady` function.
/// If you use a timeout and no socket is ready before the timeout
/// is over, the function returns `false`.
///
/// \param timeout Maximum time to wait, (use Time::Zero for infinity)
///
/// \return `true` if there are sockets ready, `false` otherwise
///
/// \see `isReady`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool wait(Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
/// \brief Test a socket to know if it is ready to receive data
///
/// This function must be used after a call to Wait, to know
/// which sockets are ready to receive data. If a socket is
/// ready, a call to receive will never block because we know
/// that there is data available to read.
/// Note that if this function returns `true` for a TcpListener,
/// this means that it is ready to accept a new connection.
///
/// \param socket Socket to test
///
/// \return `true` if the socket is ready to read, `false` otherwise
///
/// \see `isReady`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool isReady(Socket& socket) const;
private:
struct SocketSelectorImpl;
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::unique_ptr<SocketSelectorImpl> m_impl; //!< Opaque pointer to the implementation (which requires OS-specific types)
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::SocketSelector
/// \ingroup network
///
/// Socket selectors provide a way to wait until some data is
/// available on a set of sockets, instead of just one. This
/// is convenient when you have multiple sockets that may
/// possibly receive data, but you don't know which one will
/// be ready first. In particular, it avoids to use a thread
/// for each socket; with selectors, a single thread can handle
/// all the sockets.
///
/// All types of sockets can be used in a selector:
/// \li `sf::TcpListener`
/// \li `sf::TcpSocket`
/// \li `sf::UdpSocket`
///
/// A selector doesn't store its own copies of the sockets
/// (socket classes are not copyable anyway), it simply keeps
/// a reference to the original sockets that you pass to the
/// "add" function. Therefore, you can't use the selector as a
/// socket container, you must store them outside and make sure
/// that they are alive as long as they are used in the selector.
///
/// Using a selector is simple:
/// \li populate the selector with all the sockets that you want to observe
/// \li make it wait until there is data available on any of the sockets
/// \li test each socket to find out which ones are ready
///
/// Usage example:
/// \code
/// // Create a socket to listen to new connections
/// sf::TcpListener listener;
/// if (listener.listen(55001) != sf::Socket::Status::Done)
/// {
/// // Handle error...
/// }
///
/// // Create a list to store the future clients
/// std::vector<sf::TcpSocket> clients;
///
/// // Create a selector
/// sf::SocketSelector selector;
///
/// // Add the listener to the selector
/// selector.add(listener);
///
/// // Endless loop that waits for new connections
/// while (running)
/// {
/// // Make the selector wait for data on any socket
/// if (selector.wait())
/// {
/// // Test the listener
/// if (selector.isReady(listener))
/// {
/// // The listener is ready: there is a pending connection
/// sf::TcpSocket client;
/// if (listener.accept(client) == sf::Socket::Status::Done)
/// {
/// // Add the new client to the selector so that we will
/// // be notified when they send something
/// selector.add(client);
///
/// // Add the new client to the clients list
/// clients.push_back(std::move(client));
/// }
/// else
/// {
/// // Handle error...
/// }
/// }
/// else
/// {
/// // The listener socket is not ready, test all other sockets (the clients)
/// for (sf::TcpSocket& client : clients)
/// {
/// if (selector.isReady(client))
/// {
/// // The client has sent some data, we can receive it
/// sf::Packet packet;
/// if (client.receive(packet) == sf::Socket::Status::Done)
/// {
/// ...
/// }
/// }
/// }
/// }
/// }
/// }
/// \endcode
///
/// \see `sf::Socket`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,166 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Socket.hpp>
namespace sf
{
class TcpSocket;
////////////////////////////////////////////////////////////
/// \brief Socket that listens to new TCP connections
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API TcpListener : public Socket
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
TcpListener();
////////////////////////////////////////////////////////////
/// \brief Get the port to which the socket is bound locally
///
/// If the socket is not listening to a port, this function
/// returns 0.
///
/// \return Port to which the socket is bound
///
/// \see `listen`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned short getLocalPort() const;
////////////////////////////////////////////////////////////
/// \brief Start listening for incoming connection attempts
///
/// This function makes the socket start listening on the
/// specified port, waiting for incoming connection attempts.
///
/// If the socket is already listening on a port when this
/// function is called, it will stop listening on the old
/// port before starting to listen on the new port.
///
/// When providing `sf::Socket::AnyPort` as port, the listener
/// will request an available port from the system.
/// The chosen port can be retrieved by calling `getLocalPort()`.
///
/// \param port Port to listen on for incoming connection attempts
/// \param address Address of the interface to listen on
///
/// \return Status code
///
/// \see `accept`, `close`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status listen(unsigned short port, IpAddress address = IpAddress::Any);
////////////////////////////////////////////////////////////
/// \brief Stop listening and close the socket
///
/// This function gracefully stops the listener. If the
/// socket is not listening, this function has no effect.
///
/// \see `listen`
///
////////////////////////////////////////////////////////////
void close();
////////////////////////////////////////////////////////////
/// \brief Accept a new connection
///
/// If the socket is in blocking mode, this function will
/// not return until a connection is actually received.
///
/// \param socket Socket that will hold the new connection
///
/// \return Status code
///
/// \see `listen`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status accept(TcpSocket& socket);
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::TcpListener
/// \ingroup network
///
/// A listener socket is a special type of socket that listens to
/// a given port and waits for connections on that port.
/// This is all it can do.
///
/// When a new connection is received, you must call accept and
/// the listener returns a new instance of `sf::TcpSocket` that
/// is properly initialized and can be used to communicate with
/// the new client.
///
/// Listener sockets are specific to the TCP protocol,
/// UDP sockets are connectionless and can therefore communicate
/// directly. As a consequence, a listener socket will always
/// return the new connections as `sf::TcpSocket` instances.
///
/// A listener is automatically closed on destruction, like all
/// other types of socket. However if you want to stop listening
/// before the socket is destroyed, you can call its `close()`
/// function.
///
/// Usage example:
/// \code
/// // Create a listener socket and make it wait for new
/// // connections on port 55001
/// sf::TcpListener listener;
/// listener.listen(55001);
///
/// // Endless loop that waits for new connections
/// while (running)
/// {
/// sf::TcpSocket client;
/// if (listener.accept(client) == sf::Socket::Done)
/// {
/// // A new client just connected!
/// std::cout << "New connection received from " << client.getRemoteAddress().value() << std::endl;
/// doSomethingWith(client);
/// }
/// }
/// \endcode
///
/// \see `sf::TcpSocket`, `sf::Socket`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,317 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/Socket.hpp>
#include <SFML/System/Time.hpp>
#include <optional>
#include <vector>
#include <cstddef>
#include <cstdint>
namespace sf
{
class TcpListener;
class IpAddress;
class Packet;
////////////////////////////////////////////////////////////
/// \brief Specialized socket using the TCP protocol
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API TcpSocket : public Socket
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
TcpSocket();
////////////////////////////////////////////////////////////
/// \brief Get the port to which the socket is bound locally
///
/// If the socket is not connected, this function returns 0.
///
/// \return Port to which the socket is bound
///
/// \see `connect`, `getRemotePort`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned short getLocalPort() const;
////////////////////////////////////////////////////////////
/// \brief Get the address of the connected peer
///
/// If the socket is not connected, this function returns
/// an unset optional.
///
/// \return Address of the remote peer
///
/// \see `getRemotePort`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::optional<IpAddress> getRemoteAddress() const;
////////////////////////////////////////////////////////////
/// \brief Get the port of the connected peer to which
/// the socket is connected
///
/// If the socket is not connected, this function returns 0.
///
/// \return Remote port to which the socket is connected
///
/// \see `getRemoteAddress`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned short getRemotePort() const;
////////////////////////////////////////////////////////////
/// \brief Connect the socket to a remote peer
///
/// In blocking mode, this function may take a while, especially
/// if the remote peer is not reachable. The last parameter allows
/// you to stop trying to connect after a given timeout.
/// If the socket is already connected, the connection is
/// forcibly disconnected before attempting to connect again.
///
/// \param remoteAddress Address of the remote peer
/// \param remotePort Port of the remote peer
/// \param timeout Optional maximum time to wait
///
/// \return Status code
///
/// \see `disconnect`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status connect(IpAddress remoteAddress, unsigned short remotePort, Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
/// \brief Disconnect the socket from its remote peer
///
/// This function gracefully closes the connection. If the
/// socket is not connected, this function has no effect.
///
/// \see `connect`
///
////////////////////////////////////////////////////////////
void disconnect();
////////////////////////////////////////////////////////////
/// \brief Send raw data to the remote peer
///
/// To be able to handle partial sends over non-blocking
/// sockets, use the `send(const void*, std::size_t, std::size_t&)`
/// overload instead.
/// This function will fail if the socket is not connected.
///
/// \param data Pointer to the sequence of bytes to send
/// \param size Number of bytes to send
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(const void* data, std::size_t size);
////////////////////////////////////////////////////////////
/// \brief Send raw data to the remote peer
///
/// This function will fail if the socket is not connected.
///
/// \param data Pointer to the sequence of bytes to send
/// \param size Number of bytes to send
/// \param sent The number of bytes sent will be written here
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(const void* data, std::size_t size, std::size_t& sent);
////////////////////////////////////////////////////////////
/// \brief Receive raw data from the remote peer
///
/// In blocking mode, this function will wait until some
/// bytes are actually received.
/// This function will fail if the socket is not connected.
///
/// \param data Pointer to the array to fill with the received bytes
/// \param size Maximum number of bytes that can be received
/// \param received This variable is filled with the actual number of bytes received
///
/// \return Status code
///
/// \see `send`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(void* data, std::size_t size, std::size_t& received);
////////////////////////////////////////////////////////////
/// \brief Send a formatted packet of data to the remote peer
///
/// In non-blocking mode, if this function returns `sf::Socket::Status::Partial`,
/// you \em must retry sending the same unmodified packet before sending
/// anything else in order to guarantee the packet arrives at the remote
/// peer uncorrupted.
/// This function will fail if the socket is not connected.
///
/// \param packet Packet to send
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(Packet& packet);
////////////////////////////////////////////////////////////
/// \brief Receive a formatted packet of data from the remote peer
///
/// In blocking mode, this function will wait until the whole packet
/// has been received.
/// This function will fail if the socket is not connected.
///
/// \param packet Packet to fill with the received data
///
/// \return Status code
///
/// \see `send`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(Packet& packet);
private:
friend class TcpListener;
////////////////////////////////////////////////////////////
/// \brief Structure holding the data of a pending packet
///
////////////////////////////////////////////////////////////
struct PendingPacket
{
std::uint32_t size{}; //!< Data of packet size
std::size_t sizeReceived{}; //!< Number of size bytes received so far
std::vector<std::byte> data; //!< Data of the packet
};
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
PendingPacket m_pendingPacket; //!< Temporary data of the packet currently being received
std::vector<std::byte> m_blockToSendBuffer; //!< Buffer used to prepare data being sent from the socket
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::TcpSocket
/// \ingroup network
///
/// TCP is a connected protocol, which means that a TCP
/// socket can only communicate with the host it is connected
/// to. It can't send or receive anything if it is not connected.
///
/// The TCP protocol is reliable but adds a slight overhead.
/// It ensures that your data will always be received in order
/// and without errors (no data corrupted, lost or duplicated).
///
/// When a socket is connected to a remote host, you can
/// retrieve information about this host with the
/// `getRemoteAddress` and `getRemotePort` functions. You can
/// also get the local port to which the socket is bound
/// (which is automatically chosen when the socket is connected),
/// with the getLocalPort function.
///
/// Sending and receiving data can use either the low-level
/// or the high-level functions. The low-level functions
/// process a raw sequence of bytes, and cannot ensure that
/// one call to Send will exactly match one call to Receive
/// at the other end of the socket.
///
/// The high-level interface uses packets (see `sf::Packet`),
/// which are easier to use and provide more safety regarding
/// the data that is exchanged. You can look at the `sf::Packet`
/// class to get more details about how they work.
///
/// The socket is automatically disconnected when it is destroyed,
/// but if you want to explicitly close the connection while
/// the socket instance is still alive, you can call disconnect.
///
/// Usage example:
/// \code
/// // ----- The client -----
///
/// // Create a socket and connect it to 192.168.1.50 on port 55001
/// sf::TcpSocket socket;
/// socket.connect("192.168.1.50", 55001);
///
/// // Send a message to the connected host
/// std::string message = "Hi, I am a client";
/// socket.send(message.c_str(), message.size() + 1);
///
/// // Receive an answer from the server
/// std::array<char, 1024> buffer;
/// std::size_t received = 0;
/// socket.receive(buffer.data(), buffer.size(), received);
/// std::cout << "The server said: " << buffer.data() << std::endl;
///
/// // ----- The server -----
///
/// // Create a listener to wait for incoming connections on port 55001
/// sf::TcpListener listener;
/// listener.listen(55001);
///
/// // Wait for a connection
/// sf::TcpSocket socket;
/// listener.accept(socket);
/// std::cout << "New client connected: " << socket.getRemoteAddress().value() << std::endl;
///
/// // Receive a message from the client
/// std::array<char, 1024> buffer;
/// std::size_t received = 0;
/// socket.receive(buffer.data(), buffer.size(), received);
/// std::cout << "The client said: " << buffer.data() << std::endl;
///
/// // Send an answer
/// std::string message = "Welcome, client";
/// socket.send(message.c_str(), message.size() + 1);
/// \endcode
///
/// \see `sf::Socket`, `sf::UdpSocket`, `sf::Packet`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,293 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Socket.hpp>
#include <optional>
#include <vector>
#include <cstddef>
namespace sf
{
class Packet;
////////////////////////////////////////////////////////////
/// \brief Specialized socket using the UDP protocol
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API UdpSocket : public Socket
{
public:
////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////
// NOLINTNEXTLINE(readability-identifier-naming)
static constexpr std::size_t MaxDatagramSize{65507}; //!< The maximum number of bytes that can be sent in a single UDP datagram
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
UdpSocket();
////////////////////////////////////////////////////////////
/// \brief Get the port to which the socket is bound locally
///
/// If the socket is not bound to a port, this function
/// returns 0.
///
/// \return Port to which the socket is bound
///
/// \see `bind`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned short getLocalPort() const;
////////////////////////////////////////////////////////////
/// \brief Bind the socket to a specific port
///
/// Binding the socket to a port is necessary for being
/// able to receive data on that port.
///
/// When providing `sf::Socket::AnyPort` as port, the listener
/// will request an available port from the system.
/// The chosen port can be retrieved by calling `getLocalPort()`.
///
/// Since the socket can only be bound to a single port at
/// any given moment, if it is already bound when this
/// function is called, it will be unbound from the previous
/// port before being bound to the new one.
///
/// \param port Port to bind the socket to
/// \param address Address of the interface to bind to
///
/// \return Status code
///
/// \see `unbind`, `getLocalPort`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status bind(unsigned short port, IpAddress address = IpAddress::Any);
////////////////////////////////////////////////////////////
/// \brief Unbind the socket from the local port to which it is bound
///
/// The port that the socket was previously bound to is immediately
/// made available to the operating system after this function is called.
/// This means that a subsequent call to `bind()` will be able to re-bind
/// the port if no other process has done so in the mean time.
/// If the socket is not bound to a port, this function has no effect.
///
/// \see `bind`
///
////////////////////////////////////////////////////////////
void unbind();
////////////////////////////////////////////////////////////
/// \brief Send raw data to a remote peer
///
/// Make sure that `size` is not greater than
/// `UdpSocket::MaxDatagramSize`, otherwise this function will
/// fail and no data will be sent.
///
/// \param data Pointer to the sequence of bytes to send
/// \param size Number of bytes to send
/// \param remoteAddress Address of the receiver
/// \param remotePort Port of the receiver to send the data to
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(const void* data, std::size_t size, IpAddress remoteAddress, unsigned short remotePort);
////////////////////////////////////////////////////////////
/// \brief Receive raw data from a remote peer
///
/// In blocking mode, this function will wait until some
/// bytes are actually received.
/// Be careful to use a buffer which is large enough for
/// the data that you intend to receive, if it is too small
/// then an error will be returned and *all* the data will
/// be lost.
///
/// \param data Pointer to the array to fill with the received bytes
/// \param size Maximum number of bytes that can be received
/// \param received This variable is filled with the actual number of bytes received
/// \param remoteAddress Address of the peer that sent the data
/// \param remotePort Port of the peer that sent the data
///
/// \return Status code
///
/// \see `send`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(void* data,
std::size_t size,
std::size_t& received,
std::optional<IpAddress>& remoteAddress,
unsigned short& remotePort);
////////////////////////////////////////////////////////////
/// \brief Send a formatted packet of data to a remote peer
///
/// Make sure that the packet size is not greater than
/// `UdpSocket::MaxDatagramSize`, otherwise this function will
/// fail and no data will be sent.
///
/// \param packet Packet to send
/// \param remoteAddress Address of the receiver
/// \param remotePort Port of the receiver to send the data to
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(Packet& packet, IpAddress remoteAddress, unsigned short remotePort);
////////////////////////////////////////////////////////////
/// \brief Receive a formatted packet of data from a remote peer
///
/// In blocking mode, this function will wait until the whole packet
/// has been received.
///
/// \param packet Packet to fill with the received data
/// \param remoteAddress Address of the peer that sent the data
/// \param remotePort Port of the peer that sent the data
///
/// \return Status code
///
/// \see `send`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(Packet& packet, std::optional<IpAddress>& remoteAddress, unsigned short& remotePort);
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::vector<std::byte> m_buffer{MaxDatagramSize}; //!< Temporary buffer holding the received data in Receive(Packet)
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::UdpSocket
/// \ingroup network
///
/// A UDP socket is a connectionless socket. Instead of
/// connecting once to a remote host, like TCP sockets,
/// it can send to and receive from any host at any time.
///
/// It is a datagram protocol: bounded blocks of data (datagrams)
/// are transferred over the network rather than a continuous
/// stream of data (TCP). Therefore, one call to send will always
/// match one call to receive (if the datagram is not lost),
/// with the same data that was sent.
///
/// The UDP protocol is lightweight but unreliable. Unreliable
/// means that datagrams may be duplicated, be lost or
/// arrive reordered. However, if a datagram arrives, its
/// data is guaranteed to be valid.
///
/// UDP is generally used for real-time communication
/// (audio or video streaming, real-time games, etc.) where
/// speed is crucial and lost data doesn't matter much.
///
/// Sending and receiving data can use either the low-level
/// or the high-level functions. The low-level functions
/// process a raw sequence of bytes, whereas the high-level
/// interface uses packets (see `sf::Packet`), which are easier
/// to use and provide more safety regarding the data that is
/// exchanged. You can look at the `sf::Packet` class to get
/// more details about how they work.
///
/// It is important to note that `UdpSocket` is unable to send
/// datagrams bigger than `MaxDatagramSize`. In this case, it
/// returns an error and doesn't send anything. This applies
/// to both raw data and packets. Indeed, even packets are
/// unable to split and recompose data, due to the unreliability
/// of the protocol (dropped, mixed or duplicated datagrams may
/// lead to a big mess when trying to recompose a packet).
///
/// If the socket is bound to a port, it is automatically
/// unbound from it when the socket is destroyed. However,
/// you can unbind the socket explicitly with the Unbind
/// function if necessary, to stop receiving messages or
/// make the port available for other sockets.
///
/// Usage example:
/// \code
/// // ----- The client -----
///
/// // Create a socket and bind it to the port 55001
/// sf::UdpSocket socket;
/// socket.bind(55001);
///
/// // Send a message to 192.168.1.50 on port 55002
/// std::string message = "Hi, I am " + sf::IpAddress::getLocalAddress().toString();
/// socket.send(message.c_str(), message.size() + 1, "192.168.1.50", 55002);
///
/// // Receive an answer (most likely from 192.168.1.50, but could be anyone else)
/// std::array<char, 1024> buffer;
/// std::size_t received = 0;
/// std::optional<sf::IpAddress> sender;
/// unsigned short port;
/// if (socket.receive(buffer.data(), buffer.size(), received, sender, port) == sf::Socket::Status::Done)
/// std::cout << sender->toString() << " said: " << buffer.data() << std::endl;
///
/// // ----- The server -----
///
/// // Create a socket and bind it to the port 55002
/// sf::UdpSocket socket;
/// socket.bind(55002);
///
/// // Receive a message from anyone
/// std::array<char, 1024> buffer;
/// std::size_t received = 0;
/// std::optional<sf::IpAddress> sender;
/// unsigned short port;
/// if (socket.receive(buffer.data(), buffer.size(), received, sender, port) == sf::Socket::Status::Done)
/// std::cout << sender->toString() << " said: " << buffer.data() << std::endl;
///
/// // Send an answer
/// std::string message = "Welcome " + sender.toString();
/// socket.send(message.c_str(), message.size() + 1, sender, port);
/// \endcode
///
/// \see `sf::Socket`, `sf::TcpSocket`, `sf::Packet`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,55 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
#include <SFML/System/Angle.hpp>
#include <SFML/System/Clock.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/Exception.hpp>
#include <SFML/System/FileInputStream.hpp>
#include <SFML/System/InputStream.hpp>
#include <SFML/System/MemoryInputStream.hpp>
#include <SFML/System/Sleep.hpp>
#include <SFML/System/String.hpp>
#include <SFML/System/Time.hpp>
#include <SFML/System/Utf.hpp>
#include <SFML/System/Vector2.hpp>
#include <SFML/System/Vector3.hpp>
////////////////////////////////////////////////////////////
/// \defgroup system System module
///
/// Base module of SFML, defining various utilities. It provides
/// vector classes, Unicode strings and conversion functions,
/// threads and mutexes, timing classes.
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,511 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Represents an angle value.
///
////////////////////////////////////////////////////////////
class Angle
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Sets the angle value to zero.
///
////////////////////////////////////////////////////////////
constexpr Angle() = default;
////////////////////////////////////////////////////////////
/// \brief Return the angle's value in degrees
///
/// \return Angle in degrees
///
/// \see `asRadians`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr float asDegrees() const;
////////////////////////////////////////////////////////////
/// \brief Return the angle's value in radians
///
/// \return Angle in radians
///
/// \see `asDegrees`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr float asRadians() const;
////////////////////////////////////////////////////////////
/// \brief Wrap to a range such that -180° <= angle < 180°
///
/// Similar to a modulo operation, this returns a copy of the angle
/// constrained to the range [-180°, 180°) == [-Pi, Pi).
/// The resulting angle represents a rotation which is equivalent to `*this`.
///
/// The name "signed" originates from the similarity to signed integers:
/// <table>
/// <tr>
/// <th></th>
/// <th>signed</th>
/// <th>unsigned</th>
/// </tr>
/// <tr>
/// <td>char</td>
/// <td>[-128, 128)</td>
/// <td>[0, 256)</td>
/// </tr>
/// <tr>
/// <td>Angle</td>
/// <td>[-180°, 180°)</td>
/// <td>[0°, 360°)</td>
/// </tr>
/// </table>
///
/// \return Signed angle, wrapped to [-180°, 180°)
///
/// \see `wrapUnsigned`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle wrapSigned() const;
////////////////////////////////////////////////////////////
/// \brief Wrap to a range such that 0° <= angle < 360°
///
/// Similar to a modulo operation, this returns a copy of the angle
/// constrained to the range [0°, 360°) == [0, Tau) == [0, 2*Pi).
/// The resulting angle represents a rotation which is equivalent to `*this`.
///
/// The name "unsigned" originates from the similarity to unsigned integers:
/// <table>
/// <tr>
/// <th></th>
/// <th>signed</th>
/// <th>unsigned</th>
/// </tr>
/// <tr>
/// <td>char</td>
/// <td>[-128, 128)</td>
/// <td>[0, 256)</td>
/// </tr>
/// <tr>
/// <td>Angle</td>
/// <td>[-180°, 180°)</td>
/// <td>[0°, 360°)</td>
/// </tr>
/// </table>
///
/// \return Unsigned angle, wrapped to [0°, 360°)
///
/// \see `wrapSigned`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle wrapUnsigned() const;
////////////////////////////////////////////////////////////
// Static member data
////////////////////////////////////////////////////////////
// NOLINTNEXTLINE(readability-identifier-naming)
static const Angle Zero; //!< Predefined 0 degree angle value
private:
friend constexpr Angle degrees(float angle);
friend constexpr Angle radians(float angle);
////////////////////////////////////////////////////////////
/// \brief Construct from a number of radians
///
/// This function is internal. To construct angle values,
/// use `sf::radians` or `sf::degrees` instead.
///
/// \param radians Angle in radians
///
////////////////////////////////////////////////////////////
constexpr explicit Angle(float radians);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
float m_radians{}; //!< Angle value stored as radians
};
////////////////////////////////////////////////////////////
/// \brief Construct an angle value from a number of degrees
///
/// \param angle Number of degrees
///
/// \return Angle value constructed from the number of degrees
///
/// \see `radians`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle degrees(float angle);
////////////////////////////////////////////////////////////
/// \brief Construct an angle value from a number of radians
///
/// \param angle Number of radians
///
/// \return Angle value constructed from the number of radians
///
/// \see `degrees`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle radians(float angle);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of `operator==` to compare two angle values
/// \note Does not automatically wrap the angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return `true` if both angle values are equal
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator==(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of `operator!=` to compare two angle values
/// \note Does not automatically wrap the angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return `true` if both angle values are different
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator!=(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of `operator<` to compare two angle values
/// \note Does not automatically wrap the angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return `true` if \a left is less than \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator<(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of `operator>` to compare two angle values
/// \note Does not automatically wrap the angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return `true` if \a left is greater than \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator>(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of `operator<=` to compare two angle values
/// \note Does not automatically wrap the angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return `true` if \a left is less than or equal to \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator<=(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of `operator>=` to compare two angle values
/// \note Does not automatically wrap the angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return `true` if \a left is greater than or equal to \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator>=(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of unary `operator-` to negate an angle value.
///
/// Represents a rotation in the opposite direction.
///
/// \param right Right operand (an angle)
///
/// \return Negative of the angle value
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator-(Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary `operator+` to add two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return Sum of the two angle values
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator+(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary `operator+=` to add/assign two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return Sum of the two angle values
///
////////////////////////////////////////////////////////////
constexpr Angle& operator+=(Angle& left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary `operator-` to subtract two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return Difference of the two angle values
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator-(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary `operator-=` to subtract/assign two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return Difference of the two angle values
///
////////////////////////////////////////////////////////////
constexpr Angle& operator-=(Angle& left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary `operator*` to scale an angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (a number)
///
/// \return `left` multiplied by `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator*(Angle left, float right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary `operator*` to scale an angle value
///
/// \param left Left operand (a number)
/// \param right Right operand (an angle)
///
/// \return `left` multiplied by `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator*(float left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary `operator*=` to scale/assign an angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (a number)
///
/// \return `left` multiplied by `right`
///
////////////////////////////////////////////////////////////
constexpr Angle& operator*=(Angle& left, float right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary `operator/` to scale an angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (a number)
///
/// \return `left` divided by `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator/(Angle left, float right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary `operator/=` to scale/assign an angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (a number)
///
/// \return `left` divided by `right`
///
////////////////////////////////////////////////////////////
constexpr Angle& operator/=(Angle& left, float right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary `operator/` to compute the ratio of two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return `left` divided by `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr float operator/(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary `operator%` to compute modulo of an angle value.
///
/// Right hand angle must be greater than zero.
///
/// Examples:
/// \code
/// sf::degrees(90) % sf::degrees(40) // 10 degrees
/// sf::degrees(-90) % sf::degrees(40) // 30 degrees (not -10)
/// \endcode
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return `left` modulo `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator%(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary `operator%=` to compute/assign remainder of an angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return `left` modulo `right`
///
////////////////////////////////////////////////////////////
constexpr Angle& operator%=(Angle& left, Angle right);
namespace Literals
{
////////////////////////////////////////////////////////////
/// \relates sf::Angle
/// \brief User defined literal for angles in degrees, e.g.\ `10.5_deg`
///
/// \param angle Angle in degrees
///
/// \return Angle
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator""_deg(long double angle);
////////////////////////////////////////////////////////////
/// \relates sf::Angle
/// \brief User defined literal for angles in degrees, e.g.\ `90_deg`
///
/// \param angle Angle in degrees
///
/// \return Angle
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator""_deg(unsigned long long int angle);
////////////////////////////////////////////////////////////
/// \relates sf::Angle
/// \brief User defined literal for angles in radians, e.g.\ `0.1_rad`
///
/// \param angle Angle in radians
///
/// \return Angle
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator""_rad(long double angle);
////////////////////////////////////////////////////////////
/// \relates sf::Angle
/// \brief User defined literal for angles in radians, e.g.\ `2_rad`
///
/// \param angle Angle in radians
///
/// \return Angle
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator""_rad(unsigned long long int angle);
} // namespace Literals
} // namespace sf
#include <SFML/System/Angle.inl>
////////////////////////////////////////////////////////////
/// \class sf::Angle
/// \ingroup system
///
/// `sf::Angle` encapsulates an angle value in a flexible way.
/// It allows for defining an angle value either as a number
/// of degrees or radians. It also works the other way
/// around. You can read an angle value as either a number
/// of degrees or radians.
///
/// By using such a flexible interface, the API doesn't
/// impose any fixed type or unit for angle values and lets
/// the user choose their own preferred representation.
///
/// Angle values support the usual mathematical operations.
/// You can add or subtract two angles, multiply or divide
/// an angle by a number, compare two angles, etc.
///
/// Usage example:
/// \code
/// sf::Angle a1 = sf::degrees(90);
/// float radians = a1.asRadians(); // 1.5708f
///
/// sf::Angle a2 = sf::radians(3.141592654f);
/// float degrees = a2.asDegrees(); // 180.0f
///
/// using namespace sf::Literals;
/// sf::Angle a3 = 10_deg; // 10 degrees
/// sf::Angle a4 = 1.5_deg; // 1.5 degrees
/// sf::Angle a5 = 1_rad; // 1 radians
/// sf::Angle a6 = 3.14_rad; // 3.14 radians
/// \endcode
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,274 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Angle.hpp> // NOLINT(misc-header-include-cycle)
#include <cassert>
namespace sf
{
namespace priv
{
inline constexpr float pi = 3.141592654f;
inline constexpr float tau = pi * 2.f;
constexpr float positiveRemainder(float a, float b)
{
assert(b > 0.f && "Cannot calculate remainder with non-positive divisor");
const float val = a - static_cast<float>(static_cast<int>(a / b)) * b;
return val >= 0.f ? val : val + b;
}
} // namespace priv
////////////////////////////////////////////////////////////
constexpr float Angle::asDegrees() const
{
return m_radians * (180.f / priv::pi);
}
////////////////////////////////////////////////////////////
constexpr float Angle::asRadians() const
{
return m_radians;
}
////////////////////////////////////////////////////////////
constexpr Angle Angle::wrapSigned() const
{
return radians(priv::positiveRemainder(m_radians + priv::pi, priv::tau) - priv::pi);
}
////////////////////////////////////////////////////////////
constexpr Angle Angle::wrapUnsigned() const
{
return radians(priv::positiveRemainder(m_radians, priv::tau));
}
////////////////////////////////////////////////////////////
constexpr Angle::Angle(float radians) : m_radians(radians)
{
}
////////////////////////////////////////////////////////////
constexpr Angle degrees(float angle)
{
return Angle(angle * (priv::pi / 180.f));
}
////////////////////////////////////////////////////////////
constexpr Angle radians(float angle)
{
return Angle(angle);
}
////////////////////////////////////////////////////////////
constexpr bool operator==(Angle left, Angle right)
{
return left.asRadians() == right.asRadians();
}
////////////////////////////////////////////////////////////
constexpr bool operator!=(Angle left, Angle right)
{
return left.asRadians() != right.asRadians();
}
////////////////////////////////////////////////////////////
constexpr bool operator<(Angle left, Angle right)
{
return left.asRadians() < right.asRadians();
}
////////////////////////////////////////////////////////////
constexpr bool operator>(Angle left, Angle right)
{
return left.asRadians() > right.asRadians();
}
////////////////////////////////////////////////////////////
constexpr bool operator<=(Angle left, Angle right)
{
return left.asRadians() <= right.asRadians();
}
////////////////////////////////////////////////////////////
constexpr bool operator>=(Angle left, Angle right)
{
return left.asRadians() >= right.asRadians();
}
////////////////////////////////////////////////////////////
constexpr Angle operator-(Angle right)
{
return radians(-right.asRadians());
}
////////////////////////////////////////////////////////////
constexpr Angle operator+(Angle left, Angle right)
{
return radians(left.asRadians() + right.asRadians());
}
////////////////////////////////////////////////////////////
constexpr Angle& operator+=(Angle& left, Angle right)
{
return left = left + right;
}
////////////////////////////////////////////////////////////
constexpr Angle operator-(Angle left, Angle right)
{
return radians(left.asRadians() - right.asRadians());
}
////////////////////////////////////////////////////////////
constexpr Angle& operator-=(Angle& left, Angle right)
{
return left = left - right;
}
////////////////////////////////////////////////////////////
constexpr Angle operator*(Angle left, float right)
{
return radians(left.asRadians() * right);
}
////////////////////////////////////////////////////////////
constexpr Angle operator*(float left, Angle right)
{
return right * left;
}
////////////////////////////////////////////////////////////
constexpr Angle& operator*=(Angle& left, float right)
{
return left = left * right;
}
////////////////////////////////////////////////////////////
constexpr Angle operator/(Angle left, float right)
{
assert(right != 0.f && "Angle::operator/ cannot divide by 0");
return radians(left.asRadians() / right);
}
////////////////////////////////////////////////////////////
constexpr Angle& operator/=(Angle& left, float right)
{
assert(right != 0.f && "Angle::operator/= cannot divide by 0");
return left = left / right;
}
////////////////////////////////////////////////////////////
constexpr float operator/(Angle left, Angle right)
{
assert(right.asRadians() != 0.f && "Angle::operator/ cannot divide by 0");
return left.asRadians() / right.asRadians();
}
////////////////////////////////////////////////////////////
constexpr Angle operator%(Angle left, Angle right)
{
assert(right.asRadians() != 0.f && "Angle::operator% cannot modulus by 0");
return radians(priv::positiveRemainder(left.asRadians(), right.asRadians()));
}
////////////////////////////////////////////////////////////
constexpr Angle& operator%=(Angle& left, Angle right)
{
assert(right.asRadians() != 0.f && "Angle::operator%= cannot modulus by 0");
return left = left % right;
}
namespace Literals
{
////////////////////////////////////////////////////////////
constexpr Angle operator""_deg(long double angle)
{
return degrees(static_cast<float>(angle));
}
////////////////////////////////////////////////////////////
constexpr Angle operator""_deg(unsigned long long angle)
{
return degrees(static_cast<float>(angle));
}
////////////////////////////////////////////////////////////
constexpr Angle operator""_rad(long double angle)
{
return radians(static_cast<float>(angle));
}
////////////////////////////////////////////////////////////
constexpr Angle operator""_rad(unsigned long long angle)
{
return radians(static_cast<float>(angle));
}
} // namespace Literals
////////////////////////////////////////////////////////////
// Static member data
////////////////////////////////////////////////////////////
// Note: the 'inline' keyword here is technically not required, but VS2019 fails
// to compile with a bogus "multiple definition" error if not explicitly used.
inline constexpr Angle Angle::Zero;
} // namespace sf

View File

@ -0,0 +1,196 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Export.hpp>
#include <chrono>
#include <ratio>
#include <type_traits>
#ifdef SFML_SYSTEM_ANDROID
#include <SFML/System/SuspendAwareClock.hpp>
#endif
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief Chooses a monotonic clock of highest resolution
///
/// The `high_resolution_clock` is usually an alias for other
/// clocks: `steady_clock` or `system_clock`, whichever has a
/// higher precision.
///
/// `sf::Clock`, however, is aimed towards monotonic time
/// measurements and so `system_clock` could never be a choice
/// as its subject to discontinuous jumps in the system time
/// (e.g., if the system administrator manually changes
/// the clock), and by the incremental adjustments performed
/// by `adjtime` and Network Time Protocol. On the other
/// hand, monotonic clocks are unaffected by this behavior.
///
/// Note: Linux implementation of a monotonic clock that
/// takes sleep time into account is represented by
/// `CLOCK_BOOTTIME`. Android devices can define the macro:
/// `SFML_ANDROID_USE_SUSPEND_AWARE_CLOCK` to use a separate
/// implementation of that clock, instead.
///
/// For more information on Linux clocks visit:
/// https://linux.die.net/man/2/clock_gettime
///
////////////////////////////////////////////////////////////
#if defined(SFML_SYSTEM_ANDROID) && defined(SFML_ANDROID_USE_SUSPEND_AWARE_CLOCK)
using ClockImpl = SuspendAwareClock;
#else
using ClockImpl = std::conditional_t<std::chrono::high_resolution_clock::is_steady, std::chrono::high_resolution_clock, std::chrono::steady_clock>;
#endif
static_assert(ClockImpl::is_steady, "Provided implementation is not a monotonic clock");
static_assert(std::ratio_less_equal_v<ClockImpl::period, std::micro>,
"Clock resolution is too low. Expecting at least a microsecond precision");
} // namespace priv
class Time;
////////////////////////////////////////////////////////////
/// \brief Utility class that measures the elapsed time
///
/// The clock starts automatically after being constructed.
///
////////////////////////////////////////////////////////////
class SFML_SYSTEM_API Clock
{
public:
////////////////////////////////////////////////////////////
/// \brief Get the elapsed time
///
/// This function returns the time elapsed since the last call
/// to `restart()` (or the construction of the instance if `restart()`
/// has not been called).
///
/// \return Time elapsed
///
////////////////////////////////////////////////////////////
[[nodiscard]] Time getElapsedTime() const;
////////////////////////////////////////////////////////////
/// \brief Check whether the clock is running
///
/// \return `true` if the clock is running, `false` otherwise
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool isRunning() const;
////////////////////////////////////////////////////////////
/// \brief Start the clock
///
/// \see `stop`
///
////////////////////////////////////////////////////////////
void start();
////////////////////////////////////////////////////////////
/// \brief Stop the clock
///
/// \see `start`
///
////////////////////////////////////////////////////////////
void stop();
////////////////////////////////////////////////////////////
/// \brief Restart the clock
///
/// This function puts the time counter back to zero, returns
/// the elapsed time, and leaves the clock in a running state.
///
/// \return Time elapsed
///
/// \see `reset`
///
////////////////////////////////////////////////////////////
Time restart();
////////////////////////////////////////////////////////////
/// \brief Reset the clock
///
/// This function puts the time counter back to zero, returns
/// the elapsed time, and leaves the clock in a paused state.
///
/// \return Time elapsed
///
/// \see `restart`
///
////////////////////////////////////////////////////////////
Time reset();
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
priv::ClockImpl::time_point m_refPoint{priv::ClockImpl::now()}; //!< Time of last reset
priv::ClockImpl::time_point m_stopPoint; //!< Time of last stop
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::Clock
/// \ingroup system
///
/// `sf::Clock` is a lightweight class for measuring time.
///
/// It provides the most precise time that the underlying
/// OS can achieve (generally microseconds or nanoseconds).
/// It also ensures monotonicity, which means that the returned
/// time can never go backward, even if the system time is
/// changed.
///
/// Usage example:
/// \code
/// sf::Clock clock;
/// ...
/// Time time1 = clock.getElapsedTime();
/// ...
/// Time time2 = clock.restart();
/// ...
/// Time time3 = clock.reset();
/// \endcode
///
/// The `sf::Time` value returned by the clock can then be
/// converted to a number of seconds, milliseconds or even
/// microseconds.
///
/// \see `sf::Time`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,77 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Export.hpp>
#include <iosfwd>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Standard stream used by SFML to output warnings and errors
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API std::ostream& err();
} // namespace sf
////////////////////////////////////////////////////////////
/// \fn sf::err
/// \ingroup system
///
/// By default, `sf::err()` outputs to the same location as `std::cerr`,
/// (-> the stderr descriptor) which is the console if there's
/// one available.
///
/// It is a standard `std::ostream` instance, so it supports all the
/// insertion operations defined by the STL
/// (`operator<<`, manipulators, etc.).
///
/// `sf::err()` can be redirected to write to another output, independently
/// of `std::cerr`, by using the `rdbuf()` function provided by the
/// `std::ostream` class.
///
/// Example:
/// \code
/// // Redirect to a file
/// std::ofstream file("sfml-log.txt");
/// std::streambuf* previous = sf::err().rdbuf(file.rdbuf());
///
/// // Redirect to nothing
/// sf::err().rdbuf(nullptr);
///
/// // Restore the original output
/// sf::err().rdbuf(previous);
/// \endcode
///
/// \return Reference to `std::ostream` representing the SFML error stream
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,46 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Export.hpp>
#include <stdexcept>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Library-specific exception type
///
////////////////////////////////////////////////////////////
class SFML_SYSTEM_API Exception : public std::runtime_error
{
public:
using std::runtime_error::runtime_error;
};
} // namespace sf

View File

@ -0,0 +1,44 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
////////////////////////////////////////////////////////////
// Portable import / export macros
////////////////////////////////////////////////////////////
#if defined(SFML_SYSTEM_EXPORTS)
#define SFML_SYSTEM_API SFML_API_EXPORT
#else
#define SFML_SYSTEM_API SFML_API_IMPORT
#endif

View File

@ -0,0 +1,212 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
#include <SFML/System/Export.hpp>
#include <SFML/System/InputStream.hpp>
#include <filesystem>
#include <memory>
#include <cstdint>
#include <cstdio>
#ifdef SFML_SYSTEM_ANDROID
namespace sf::priv
{
class SFML_SYSTEM_API ResourceStream;
}
#endif
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Implementation of input stream based on a file
///
////////////////////////////////////////////////////////////
class SFML_SYSTEM_API FileInputStream : public InputStream
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Construct a file input stream that is not associated
/// with a file to read.
///
////////////////////////////////////////////////////////////
FileInputStream();
////////////////////////////////////////////////////////////
/// \brief Default destructor
///
////////////////////////////////////////////////////////////
~FileInputStream() override;
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
FileInputStream(const FileInputStream&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
FileInputStream& operator=(const FileInputStream&) = delete;
////////////////////////////////////////////////////////////
/// \brief Move constructor
///
////////////////////////////////////////////////////////////
FileInputStream(FileInputStream&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Move assignment
///
////////////////////////////////////////////////////////////
FileInputStream& operator=(FileInputStream&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Construct the stream from a file path
///
/// \param filename Name of the file to open
///
/// \throws sf::Exception on error
///
////////////////////////////////////////////////////////////
explicit FileInputStream(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Open the stream from a file path
///
/// \param filename Name of the file to open
///
/// \return `true` on success, `false` on error
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool open(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Read data from the stream
///
/// After reading, the stream's reading position must be
/// advanced by the amount of bytes read.
///
/// \param data Buffer where to copy the read data
/// \param size Desired number of bytes to read
///
/// \return The number of bytes actually read, or `std::nullopt` on error
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::optional<std::size_t> read(void* data, std::size_t size) override;
////////////////////////////////////////////////////////////
/// \brief Change the current reading position
///
/// \param position The position to seek to, from the beginning
///
/// \return The position actually sought to, or `std::nullopt` on error
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::optional<std::size_t> seek(std::size_t position) override;
////////////////////////////////////////////////////////////
/// \brief Get the current reading position in the stream
///
/// \return The current position, or `std::nullopt` on error.
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::optional<std::size_t> tell() override;
////////////////////////////////////////////////////////////
/// \brief Return the size of the stream
///
/// \return The total number of bytes available in the stream, or `std::nullopt` on error
///
////////////////////////////////////////////////////////////
std::optional<std::size_t> getSize() override;
private:
////////////////////////////////////////////////////////////
/// \brief Deleter for stdio file stream that closes the file stream
///
////////////////////////////////////////////////////////////
struct FileCloser
{
void operator()(std::FILE* file);
};
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
#ifdef SFML_SYSTEM_ANDROID
std::unique_ptr<priv::ResourceStream> m_androidFile;
#endif
std::unique_ptr<std::FILE, FileCloser> m_file; //!< stdio file stream
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::FileInputStream
/// \ingroup system
///
/// This class is a specialization of `InputStream` that
/// reads from a file on disk.
///
/// It wraps a file in the common `InputStream` interface
/// and therefore allows to use generic classes or functions
/// that accept such a stream, with a file on disk as the data
/// source.
///
/// In addition to the virtual functions inherited from
/// `InputStream`, `FileInputStream` adds a function to
/// specify the file to open.
///
/// SFML resource classes can usually be loaded directly from
/// a filename, so this class shouldn't be useful to you unless
/// you create your own algorithms that operate on an InputStream.
///
/// Usage example:
/// \code
/// void process(InputStream& stream);
///
/// std::optional stream = sf::FileInputStream::open("some_file.dat");
/// if (stream)
/// process(*stream);
/// \endcode
///
/// \see `InputStream`, `MemoryInputStream`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,166 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
#include <SFML/System/Export.hpp>
#include <optional>
#include <cstdint>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Abstract class for custom file input streams
///
////////////////////////////////////////////////////////////
class SFML_SYSTEM_API InputStream
{
public:
////////////////////////////////////////////////////////////
/// \brief Virtual destructor
///
////////////////////////////////////////////////////////////
virtual ~InputStream() = default;
////////////////////////////////////////////////////////////
/// \brief Read data from the stream
///
/// After reading, the stream's reading position must be
/// advanced by the amount of bytes read.
///
/// \param data Buffer where to copy the read data
/// \param size Desired number of bytes to read
///
/// \return The number of bytes actually read, or `std::nullopt` on error
///
////////////////////////////////////////////////////////////
[[nodiscard]] virtual std::optional<std::size_t> read(void* data, std::size_t size) = 0;
////////////////////////////////////////////////////////////
/// \brief Change the current reading position
///
/// \param position The position to seek to, from the beginning
///
/// \return The position actually sought to, or `std::nullopt` on error
///
////////////////////////////////////////////////////////////
[[nodiscard]] virtual std::optional<std::size_t> seek(std::size_t position) = 0;
////////////////////////////////////////////////////////////
/// \brief Get the current reading position in the stream
///
/// \return The current position, or `std::nullopt` on error.
///
////////////////////////////////////////////////////////////
[[nodiscard]] virtual std::optional<std::size_t> tell() = 0;
////////////////////////////////////////////////////////////
/// \brief Return the size of the stream
///
/// \return The total number of bytes available in the stream, or `std::nullopt` on error
///
////////////////////////////////////////////////////////////
virtual std::optional<std::size_t> getSize() = 0;
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::InputStream
/// \ingroup system
///
/// This class allows users to define their own file input sources
/// from which SFML can load resources.
///
/// SFML resource classes like `sf::Texture` and
/// `sf::SoundBuffer` provide `loadFromFile` and `loadFromMemory` functions,
/// which read data from conventional sources. However, if you
/// have data coming from a different source (over a network,
/// embedded, encrypted, compressed, etc) you can derive your
/// own class from `sf::InputStream` and load SFML resources with
/// their `loadFromStream` function.
///
/// Usage example:
/// \code
/// // custom stream class that reads from inside a zip file
/// class ZipStream : public sf::InputStream
/// {
/// public:
///
/// ZipStream(const std::string& archive);
///
/// [[nodiscard]] bool open(const std::filesystem::path& filename);
///
/// [[nodiscard]] std::optional<std::size_t> read(void* data, std::size_t size);
///
/// [[nodiscard]] std::optional<std::size_t> seek(std::size_t position);
///
/// [[nodiscard]] std::optional<std::size_t> tell();
///
/// std::optional<std::size_t> getSize();
///
/// private:
///
/// ...
/// };
///
/// // now you can load textures...
/// ZipStream stream("resources.zip");
///
/// if (!stream.open("images/img.png"))
/// {
/// // Handle error...
/// }
///
/// const sf::Texture texture(stream);
///
/// // musics...
/// sf::Music music;
/// ZipStream stream("resources.zip");
///
/// if (!stream.open("musics/msc.ogg"))
/// {
/// // Handle error...
/// }
///
/// if (!music.openFromStream(stream))
/// {
/// // Handle error...
/// }
///
/// // etc.
/// \endcode
///
/// \see `FileInputStream`, `MemoryInputStream`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,139 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
#include <SFML/System/Export.hpp>
#include <SFML/System/InputStream.hpp>
#include <cstddef>
#include <cstdint>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Implementation of input stream based on a memory chunk
///
////////////////////////////////////////////////////////////
class SFML_SYSTEM_API MemoryInputStream : public InputStream
{
public:
////////////////////////////////////////////////////////////
/// \brief Construct the stream from its data
///
/// \param data Pointer to the data in memory
/// \param sizeInBytes Size of the data, in bytes
///
////////////////////////////////////////////////////////////
MemoryInputStream(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Read data from the stream
///
/// After reading, the stream's reading position must be
/// advanced by the amount of bytes read.
///
/// \param data Buffer where to copy the read data
/// \param size Desired number of bytes to read
///
/// \return The number of bytes actually read, or `std::nullopt` on error
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::optional<std::size_t> read(void* data, std::size_t size) override;
////////////////////////////////////////////////////////////
/// \brief Change the current reading position
///
/// \param position The position to seek to, from the beginning
///
/// \return The position actually sought to, or `std::nullopt` on error
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::optional<std::size_t> seek(std::size_t position) override;
////////////////////////////////////////////////////////////
/// \brief Get the current reading position in the stream
///
/// \return The current position, or `std::nullopt` on error.
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::optional<std::size_t> tell() override;
////////////////////////////////////////////////////////////
/// \brief Return the size of the stream
///
/// \return The total number of bytes available in the stream, or `std::nullopt` on error
///
////////////////////////////////////////////////////////////
std::optional<std::size_t> getSize() override;
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
const std::byte* m_data{}; //!< Pointer to the data in memory
std::size_t m_size{}; //!< Total size of the data
std::size_t m_offset{}; //!< Current reading position
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::MemoryInputStream
/// \ingroup system
///
/// This class is a specialization of `InputStream` that
/// reads from data in memory.
///
/// It wraps a memory chunk in the common `InputStream` interface
/// and therefore allows to use generic classes or functions
/// that accept such a stream, with content already loaded in memory.
///
/// In addition to the virtual functions inherited from
/// `InputStream`, `MemoryInputStream` adds a function to
/// specify the pointer and size of the data in memory.
///
/// SFML resource classes can usually be loaded directly from
/// memory, so this class shouldn't be useful to you unless
/// you create your own algorithms that operate on an InputStream.
///
/// Usage example:
/// \code
/// void process(InputStream& stream);
///
/// MemoryInputStream stream(thePtr, theSize);
/// process(stream);
/// \endcode
///
/// \see `InputStream`, `FileInputStream`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,58 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Export.hpp>
#if !defined(SFML_SYSTEM_ANDROID)
#error NativeActivity.hpp: This header is Android only.
#endif
struct ANativeActivity;
namespace sf
{
////////////////////////////////////////////////////////////
/// \ingroup system
/// \brief Return a pointer to the Android native activity
///
/// You shouldn't have to use this function, unless you want
/// to implement very specific details, that SFML doesn't
/// support, or to use a workaround for a known issue.
///
/// \return Pointer to Android native activity structure
///
/// \sfplatform{Android,SFML/System/NativeActivity.hpp}
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API ANativeActivity* getNativeActivity();
} // namespace sf

View File

@ -0,0 +1,57 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Export.hpp>
namespace sf
{
class Time;
////////////////////////////////////////////////////////////
/// \ingroup system
/// \brief Make the current thread sleep for a given duration
///
/// `sf::sleep` is the best way to block a program or one of its
/// threads, as it doesn't consume any CPU power. Compared to
/// the standard `std::this_thread::sleep_for` function, this
/// one provides more accurate sleeping time thanks to some
/// platform-specific tweaks.
///
/// `sf::sleep` only guarantees millisecond precision. Sleeping
/// for a duration less than 1 millisecond is prone to result
/// in the actual sleep duration being less than what is
/// requested.
///
/// \param duration Time to sleep
///
////////////////////////////////////////////////////////////
void SFML_SYSTEM_API sleep(Time duration);
} // namespace sf

View File

@ -0,0 +1,700 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Export.hpp>
#include <SFML/System/Utf.hpp>
#include <locale>
#include <string>
#include <cstddef>
#include <cstdint>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Character traits for `std::uint8_t`
///
////////////////////////////////////////////////////////////
struct SFML_SYSTEM_API U8StringCharTraits
{
// NOLINTBEGIN(readability-identifier-naming)
using char_type = std::uint8_t;
using int_type = std::char_traits<char>::int_type;
using off_type = std::char_traits<char>::off_type;
using pos_type = std::char_traits<char>::pos_type;
using state_type = std::char_traits<char>::state_type;
static void assign(char_type& c1, char_type c2) noexcept;
static char_type* assign(char_type* s, std::size_t n, char_type c);
static bool eq(char_type c1, char_type c2) noexcept;
static bool lt(char_type c1, char_type c2) noexcept;
static char_type* move(char_type* s1, const char_type* s2, std::size_t n);
static char_type* copy(char_type* s1, const char_type* s2, std::size_t n);
static int compare(const char_type* s1, const char_type* s2, std::size_t n);
static std::size_t length(const char_type* s);
static const char_type* find(const char_type* s, std::size_t n, const char_type& c);
static char_type to_char_type(int_type i) noexcept;
static int_type to_int_type(char_type c) noexcept;
static bool eq_int_type(int_type i1, int_type i2) noexcept;
static int_type eof() noexcept;
static int_type not_eof(int_type i) noexcept;
// NOLINTEND(readability-identifier-naming)
};
////////////////////////////////////////////////////////////
/// \brief Portable replacement for `std::basic_string<std::uint8_t>`
///
/// While all major C++ implementations happen to define this
/// as of early 2024, this specialization is not strictly speaking
/// standard C++. Thus we can't depend on its continued existence.
///
////////////////////////////////////////////////////////////
using U8String = std::basic_string<std::uint8_t, U8StringCharTraits>;
////////////////////////////////////////////////////////////
/// \brief Utility string class that automatically handles
/// conversions between types and encodings
///
////////////////////////////////////////////////////////////
class SFML_SYSTEM_API String
{
public:
////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////
using Iterator = std::u32string::iterator; //!< Iterator type
using ConstIterator = std::u32string::const_iterator; //!< Read-only iterator type
////////////////////////////////////////////////////////////
// Static member data
////////////////////////////////////////////////////////////
// NOLINTBEGIN(readability-identifier-naming)
/// Represents an invalid position in the string
static inline const std::size_t InvalidPos{std::u32string::npos};
// NOLINTEND(readability-identifier-naming)
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// This constructor creates an empty string.
///
////////////////////////////////////////////////////////////
String() = default;
////////////////////////////////////////////////////////////
/// \brief Deleted `std::nullptr_t` constructor
///
/// Disallow construction from `nullptr` literal
///
////////////////////////////////////////////////////////////
String(std::nullptr_t, const std::locale& = {}) = delete;
////////////////////////////////////////////////////////////
/// \brief Construct from a single ANSI character and a locale
///
/// The source character is converted to UTF-32 according
/// to the given locale.
///
/// \param ansiChar ANSI character to convert
/// \param locale Locale to use for conversion
///
////////////////////////////////////////////////////////////
String(char ansiChar, const std::locale& locale = {});
////////////////////////////////////////////////////////////
/// \brief Construct from single wide character
///
/// \param wideChar Wide character to convert
///
////////////////////////////////////////////////////////////
String(wchar_t wideChar);
////////////////////////////////////////////////////////////
/// \brief Construct from single UTF-32 character
///
/// \param utf32Char UTF-32 character to convert
///
////////////////////////////////////////////////////////////
String(char32_t utf32Char);
////////////////////////////////////////////////////////////
/// \brief Construct from a null-terminated C-style ANSI string and a locale
///
/// The source string is converted to UTF-32 according
/// to the given locale.
///
/// \param ansiString ANSI string to convert
/// \param locale Locale to use for conversion
///
////////////////////////////////////////////////////////////
String(const char* ansiString, const std::locale& locale = {});
////////////////////////////////////////////////////////////
/// \brief Construct from an ANSI string and a locale
///
/// The source string is converted to UTF-32 according
/// to the given locale.
///
/// \param ansiString ANSI string to convert
/// \param locale Locale to use for conversion
///
////////////////////////////////////////////////////////////
String(const std::string& ansiString, const std::locale& locale = {});
////////////////////////////////////////////////////////////
/// \brief Construct from null-terminated C-style wide string
///
/// \param wideString Wide string to convert
///
////////////////////////////////////////////////////////////
String(const wchar_t* wideString);
////////////////////////////////////////////////////////////
/// \brief Construct from a wide string
///
/// \param wideString Wide string to convert
///
////////////////////////////////////////////////////////////
String(const std::wstring& wideString);
////////////////////////////////////////////////////////////
/// \brief Construct from a null-terminated C-style UTF-32 string
///
/// \param utf32String UTF-32 string to assign
///
////////////////////////////////////////////////////////////
String(const char32_t* utf32String);
////////////////////////////////////////////////////////////
/// \brief Construct from an UTF-32 string
///
/// \param utf32String UTF-32 string to assign
///
////////////////////////////////////////////////////////////
String(std::u32string utf32String);
////////////////////////////////////////////////////////////
/// \brief Create a new `sf::String` from a UTF-8 encoded string
///
/// \param begin Forward iterator to the beginning of the UTF-8 sequence
/// \param end Forward iterator to the end of the UTF-8 sequence
///
/// \return A `sf::String` containing the source string
///
/// \see `fromUtf16`, `fromUtf32`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] static String fromUtf8(T begin, T end);
////////////////////////////////////////////////////////////
/// \brief Create a new `sf::String` from a UTF-16 encoded string
///
/// \param begin Forward iterator to the beginning of the UTF-16 sequence
/// \param end Forward iterator to the end of the UTF-16 sequence
///
/// \return A `sf::String` containing the source string
///
/// \see `fromUtf8`, `fromUtf32`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] static String fromUtf16(T begin, T end);
////////////////////////////////////////////////////////////
/// \brief Create a new `sf::String` from a UTF-32 encoded string
///
/// This function is provided for consistency, it is equivalent to
/// using the constructors that takes a `const char32_t*` or
/// a `std::u32string`.
///
/// \param begin Forward iterator to the beginning of the UTF-32 sequence
/// \param end Forward iterator to the end of the UTF-32 sequence
///
/// \return A `sf::String` containing the source string
///
/// \see `fromUtf8`, `fromUtf16`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] static String fromUtf32(T begin, T end);
////////////////////////////////////////////////////////////
/// \brief Implicit conversion operator to `std::string` (ANSI string)
///
/// The current global locale is used for conversion. If you
/// want to explicitly specify a locale, see toAnsiString.
/// Characters that do not fit in the target encoding are
/// discarded from the returned string.
/// This operator is defined for convenience, and is equivalent
/// to calling `toAnsiString()`.
///
/// \return Converted ANSI string
///
/// \see `toAnsiString`, `operator std::wstring`
///
////////////////////////////////////////////////////////////
operator std::string() const;
////////////////////////////////////////////////////////////
/// \brief Implicit conversion operator to `std::wstring` (wide string)
///
/// Characters that do not fit in the target encoding are
/// discarded from the returned string.
/// This operator is defined for convenience, and is equivalent
/// to calling `toWideString()`.
///
/// \return Converted wide string
///
/// \see `toWideString`, `operator std::string`
///
////////////////////////////////////////////////////////////
operator std::wstring() const;
////////////////////////////////////////////////////////////
/// \brief Convert the Unicode string to an ANSI string
///
/// The UTF-32 string is converted to an ANSI string in
/// the encoding defined by `locale`.
/// Characters that do not fit in the target encoding are
/// discarded from the returned string.
///
/// \param locale Locale to use for conversion
///
/// \return Converted ANSI string
///
/// \see `toWideString`, `operator std::string`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::string toAnsiString(const std::locale& locale = {}) const;
////////////////////////////////////////////////////////////
/// \brief Convert the Unicode string to a wide string
///
/// Characters that do not fit in the target encoding are
/// discarded from the returned string.
///
/// \return Converted wide string
///
/// \see `toAnsiString`, `operator std::wstring`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::wstring toWideString() const;
////////////////////////////////////////////////////////////
/// \brief Convert the Unicode string to a UTF-8 string
///
/// \return Converted UTF-8 string
///
/// \see `toUtf16`, `toUtf32`
///
////////////////////////////////////////////////////////////
[[nodiscard]] sf::U8String toUtf8() const;
////////////////////////////////////////////////////////////
/// \brief Convert the Unicode string to a UTF-16 string
///
/// \return Converted UTF-16 string
///
/// \see `toUtf8`, `toUtf32`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::u16string toUtf16() const;
////////////////////////////////////////////////////////////
/// \brief Convert the Unicode string to a UTF-32 string
///
/// This function doesn't perform any conversion, since the
/// string is already stored as UTF-32 internally.
///
/// \return Converted UTF-32 string
///
/// \see `toUtf8`, `toUtf16`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::u32string toUtf32() const;
////////////////////////////////////////////////////////////
/// \brief Overload of `operator+=` to append an UTF-32 string
///
/// \param right String to append
///
/// \return Reference to self
///
////////////////////////////////////////////////////////////
String& operator+=(const String& right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator[]` to access a character by its position
///
/// This function provides read-only access to characters.
/// Note: the behavior is undefined if `index` is out of range.
///
/// \param index Index of the character to get
///
/// \return Character at position `index`
///
////////////////////////////////////////////////////////////
[[nodiscard]] char32_t operator[](std::size_t index) const;
////////////////////////////////////////////////////////////
/// \brief Overload of `operator[]` to access a character by its position
///
/// This function provides read and write access to characters.
/// Note: the behavior is undefined if `index` is out of range.
///
/// \param index Index of the character to get
///
/// \return Reference to the character at position `index`
///
////////////////////////////////////////////////////////////
[[nodiscard]] char32_t& operator[](std::size_t index);
////////////////////////////////////////////////////////////
/// \brief Clear the string
///
/// This function removes all the characters from the string.
///
/// \see `isEmpty`, `erase`
///
////////////////////////////////////////////////////////////
void clear();
////////////////////////////////////////////////////////////
/// \brief Get the size of the string
///
/// \return Number of characters in the string
///
/// \see `isEmpty`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::size_t getSize() const;
////////////////////////////////////////////////////////////
/// \brief Check whether the string is empty or not
///
/// \return `true` if the string is empty (i.e. contains no character)
///
/// \see `clear`, `getSize`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool isEmpty() const;
////////////////////////////////////////////////////////////
/// \brief Erase one or more characters from the string
///
/// This function removes a sequence of `count` characters
/// starting from `position`.
///
/// \param position Position of the first character to erase
/// \param count Number of characters to erase
///
////////////////////////////////////////////////////////////
void erase(std::size_t position, std::size_t count = 1);
////////////////////////////////////////////////////////////
/// \brief Insert one or more characters into the string
///
/// This function inserts the characters of `str`
/// into the string, starting from `position`.
///
/// \param position Position of insertion
/// \param str Characters to insert
///
////////////////////////////////////////////////////////////
void insert(std::size_t position, const String& str);
////////////////////////////////////////////////////////////
/// \brief Find a sequence of one or more characters in the string
///
/// This function searches for the characters of `str`
/// in the string, starting from `start`.
///
/// \param str Characters to find
/// \param start Where to begin searching
///
/// \return Position of `str` in the string, or `String::InvalidPos` if not found
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::size_t find(const String& str, std::size_t start = 0) const;
////////////////////////////////////////////////////////////
/// \brief Replace a substring with another string
///
/// This function replaces the substring that starts at index `position`
/// and spans `length` characters with the string `replaceWith`.
///
/// \param position Index of the first character to be replaced
/// \param length Number of characters to replace. You can pass InvalidPos to
/// replace all characters until the end of the string.
/// \param replaceWith String that replaces the given substring.
///
////////////////////////////////////////////////////////////
void replace(std::size_t position, std::size_t length, const String& replaceWith);
////////////////////////////////////////////////////////////
/// \brief Replace all occurrences of a substring with a replacement string
///
/// This function replaces all occurrences of `searchFor` in this string
/// with the string `replaceWith`.
///
/// \param searchFor The value being searched for
/// \param replaceWith The value that replaces found `searchFor` values
///
////////////////////////////////////////////////////////////
void replace(const String& searchFor, const String& replaceWith);
////////////////////////////////////////////////////////////
/// \brief Return a part of the string
///
/// This function returns the substring that starts at index `position`
/// and spans `length` characters.
///
/// \param position Index of the first character
/// \param length Number of characters to include in the substring (if
/// the string is shorter, as many characters as possible
/// are included). `InvalidPos` can be used to include all
/// characters until the end of the string.
///
/// \return String object containing a substring of this object
///
////////////////////////////////////////////////////////////
[[nodiscard]] String substring(std::size_t position, std::size_t length = InvalidPos) const;
////////////////////////////////////////////////////////////
/// \brief Get a pointer to the C-style array of characters
///
/// This functions provides a read-only access to a
/// null-terminated C-style representation of the string.
/// The returned pointer is temporary and is meant only for
/// immediate use, thus it is not recommended to store it.
///
/// \return Read-only pointer to the array of characters
///
////////////////////////////////////////////////////////////
[[nodiscard]] const char32_t* getData() const;
////////////////////////////////////////////////////////////
/// \brief Return an iterator to the beginning of the string
///
/// \return Read-write iterator to the beginning of the string characters
///
/// \see `end`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Iterator begin();
////////////////////////////////////////////////////////////
/// \brief Return an iterator to the beginning of the string
///
/// \return Read-only iterator to the beginning of the string characters
///
/// \see `end`
///
////////////////////////////////////////////////////////////
[[nodiscard]] ConstIterator begin() const;
////////////////////////////////////////////////////////////
/// \brief Return an iterator to the end of the string
///
/// The end iterator refers to 1 position past the last character;
/// thus it represents an invalid character and should never be
/// accessed.
///
/// \return Read-write iterator to the end of the string characters
///
/// \see `begin`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Iterator end();
////////////////////////////////////////////////////////////
/// \brief Return an iterator to the end of the string
///
/// The end iterator refers to 1 position past the last character;
/// thus it represents an invalid character and should never be
/// accessed.
///
/// \return Read-only iterator to the end of the string characters
///
/// \see `begin`
///
////////////////////////////////////////////////////////////
[[nodiscard]] ConstIterator end() const;
private:
friend SFML_SYSTEM_API bool operator==(const String& left, const String& right);
friend SFML_SYSTEM_API bool operator<(const String& left, const String& right);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::u32string m_string; //!< Internal string of UTF-32 characters
};
////////////////////////////////////////////////////////////
/// \relates String
/// \brief Overload of `operator==` to compare two UTF-32 strings
///
/// \param left Left operand (a string)
/// \param right Right operand (a string)
///
/// \return `true` if both strings are equal
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API bool operator==(const String& left, const String& right);
////////////////////////////////////////////////////////////
/// \relates String
/// \brief Overload of `operator!=` to compare two UTF-32 strings
///
/// \param left Left operand (a string)
/// \param right Right operand (a string)
///
/// \return `true` if both strings are different
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API bool operator!=(const String& left, const String& right);
////////////////////////////////////////////////////////////
/// \relates String
/// \brief Overload of `operator<` to compare two UTF-32 strings
///
/// \param left Left operand (a string)
/// \param right Right operand (a string)
///
/// \return `true` if `left` is lexicographically before `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API bool operator<(const String& left, const String& right);
////////////////////////////////////////////////////////////
/// \relates String
/// \brief Overload of `operator>` to compare two UTF-32 strings
///
/// \param left Left operand (a string)
/// \param right Right operand (a string)
///
/// \return `true` if `left` is lexicographically after `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API bool operator>(const String& left, const String& right);
////////////////////////////////////////////////////////////
/// \relates String
/// \brief Overload of `operator<=` to compare two UTF-32 strings
///
/// \param left Left operand (a string)
/// \param right Right operand (a string)
///
/// \return `true` if `left` is lexicographically before or equivalent to `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API bool operator<=(const String& left, const String& right);
////////////////////////////////////////////////////////////
/// \relates String
/// \brief Overload of `operator>=` to compare two UTF-32 strings
///
/// \param left Left operand (a string)
/// \param right Right operand (a string)
///
/// \return `true` if `left` is lexicographically after or equivalent to `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API bool operator>=(const String& left, const String& right);
////////////////////////////////////////////////////////////
/// \relates String
/// \brief Overload of binary `operator+` to concatenate two strings
///
/// \param left Left operand (a string)
/// \param right Right operand (a string)
///
/// \return Concatenated string
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API String operator+(const String& left, const String& right);
} // namespace sf
#include <SFML/System/String.inl>
////////////////////////////////////////////////////////////
/// \class sf::String
/// \ingroup system
///
/// `sf::String` is a utility string class defined mainly for
/// convenience. It is a Unicode string (implemented using
/// UTF-32), thus it can store any character in the world
/// (European, Chinese, Arabic, Hebrew, etc.).
///
/// It automatically handles conversions from/to ANSI and
/// wide strings, so that you can work with standard string
/// classes and still be compatible with functions taking a
/// `sf::String`.
///
/// \code
/// sf::String s;
///
/// std::string s1 = s; // automatically converted to ANSI string
/// std::wstring s2 = s; // automatically converted to wide string
/// s = "hello"; // automatically converted from ANSI string
/// s = L"hello"; // automatically converted from wide string
/// s += 'a'; // automatically converted from ANSI string
/// s += L'a'; // automatically converted from wide string
/// \endcode
///
/// Conversions involving ANSI strings use the default user locale. However
/// it is possible to use a custom locale if necessary:
/// \code
/// std::locale locale;
/// sf::String s;
/// ...
/// std::string s1 = s.toAnsiString(locale);
/// s = sf::String("hello", locale);
/// \endcode
///
/// `sf::String` defines the most important functions of the
/// standard `std::string` class: removing, random access, iterating,
/// appending, comparing, etc. However it is a simple class
/// provided for convenience, and you may have to consider using
/// a more optimized class if your program requires complex string
/// handling. The automatic conversion functions will then take
/// care of converting your string to `sf::String` whenever SFML
/// requires it.
///
/// Please note that SFML also defines a low-level, generic
/// interface for Unicode handling, see the `sf::Utf` classes.
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,64 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/String.hpp> // NOLINT(misc-header-include-cycle)
#include <iterator>
namespace sf
{
////////////////////////////////////////////////////////////
template <typename T>
String String::fromUtf8(T begin, T end)
{
String string;
Utf8::toUtf32(begin, end, std::back_inserter(string.m_string));
return string;
}
////////////////////////////////////////////////////////////
template <typename T>
String String::fromUtf16(T begin, T end)
{
String string;
Utf16::toUtf32(begin, end, std::back_inserter(string.m_string));
return string;
}
////////////////////////////////////////////////////////////
template <typename T>
String String::fromUtf32(T begin, T end)
{
String string;
string.m_string.assign(begin, end);
return string;
}
} // namespace sf

View File

@ -0,0 +1,76 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Export.hpp>
#include <chrono>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Android, chrono-compatible, suspend-aware clock
///
/// Linux steady clock is represented by `CLOCK_MONOTONIC`.
/// However, this implementation does not work properly for
/// long-running clocks that work in the background when the
/// system is suspended.
///
/// `SuspendAwareClock` uses `CLOCK_BOOTTIME` which is identical
/// to `CLOCK_MONOTONIC`, except that it also includes any time
/// that the system is suspended.
///
/// Note: In most cases, `CLOCK_MONOTONIC` is a better choice.
/// Make sure this implementation is required for your use case.
///
////////////////////////////////////////////////////////////
struct SFML_SYSTEM_API SuspendAwareClock
{
////////////////////////////////////////////////////////////
/// \brief Type traits and static members
///
/// These type traits and static members meet the requirements
/// of a Clock concept in the C++ Standard. More specifically,
/// TrivialClock requirements are met. Thus, naming convention
/// has been kept consistent to allow for extended use e.g.
/// https://en.cppreference.com/w/cpp/chrono/is_clock
///
////////////////////////////////////////////////////////////
using duration = std::chrono::nanoseconds;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<SuspendAwareClock, duration>;
static constexpr bool is_steady = true; // NOLINT(readability-identifier-naming)
static time_point now() noexcept;
};
} // namespace sf

View File

@ -0,0 +1,500 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <chrono>
#include <cstdint>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Represents a time value
///
////////////////////////////////////////////////////////////
class Time
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Sets the time value to zero.
///
////////////////////////////////////////////////////////////
constexpr Time() = default;
////////////////////////////////////////////////////////////
/// \brief Construct from `std::chrono::duration`
///
////////////////////////////////////////////////////////////
template <typename Rep, typename Period>
constexpr Time(const std::chrono::duration<Rep, Period>& duration);
////////////////////////////////////////////////////////////
/// \brief Return the time value as a number of seconds
///
/// \return Time in seconds
///
/// \see `asMilliseconds`, `asMicroseconds`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr float asSeconds() const;
////////////////////////////////////////////////////////////
/// \brief Return the time value as a number of milliseconds
///
/// \return Time in milliseconds
///
/// \see `asSeconds`, `asMicroseconds`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr std::int32_t asMilliseconds() const;
////////////////////////////////////////////////////////////
/// \brief Return the time value as a number of microseconds
///
/// \return Time in microseconds
///
/// \see `asSeconds`, `asMilliseconds`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr std::int64_t asMicroseconds() const;
////////////////////////////////////////////////////////////
/// \brief Return the time value as a `std::chrono::duration`
///
/// \return Time in microseconds
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr std::chrono::microseconds toDuration() const;
////////////////////////////////////////////////////////////
/// \brief Implicit conversion to `std::chrono::duration`
///
/// \return Duration in microseconds
///
////////////////////////////////////////////////////////////
template <typename Rep, typename Period>
constexpr operator std::chrono::duration<Rep, Period>() const;
////////////////////////////////////////////////////////////
// Static member data
////////////////////////////////////////////////////////////
// NOLINTNEXTLINE(readability-identifier-naming)
static const Time Zero; //!< Predefined "zero" time value
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::chrono::microseconds m_microseconds{}; //!< Time value stored as microseconds
};
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Construct a time value from a number of seconds
///
/// \param amount Number of seconds
///
/// \return Time value constructed from the amount of seconds
///
/// \see `milliseconds`, `microseconds`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time seconds(float amount);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Construct a time value from a number of milliseconds
///
/// \param amount Number of milliseconds
///
/// \return Time value constructed from the amount of milliseconds
///
/// \see `seconds`, `microseconds`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time milliseconds(std::int32_t amount);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Construct a time value from a number of microseconds
///
/// \param amount Number of microseconds
///
/// \return Time value constructed from the amount of microseconds
///
/// \see `seconds`, `milliseconds`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time microseconds(std::int64_t amount);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of `operator==` to compare two time values
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return `true` if both time values are equal
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator==(Time left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of `operator!=` to compare two time values
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return `true` if both time values are different
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator!=(Time left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of `operator<` to compare two time values
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return `true` if `left` is lesser than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator<(Time left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of `operator>` to compare two time values
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return `true` if `left` is greater than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator>(Time left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of `operator<=` to compare two time values
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return `true` if `left` is lesser or equal than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator<=(Time left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of `operator>=` to compare two time values
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return `true` if `left` is greater or equal than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator>=(Time left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of unary `operator-` to negate a time value
///
/// \param right Right operand (a time)
///
/// \return Opposite of the time value
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time operator-(Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator+` to add two time values
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return Sum of the two times values
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time operator+(Time left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator+=` to add/assign two time values
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return Sum of the two times values
///
////////////////////////////////////////////////////////////
constexpr Time& operator+=(Time& left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator-` to subtract two time values
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return Difference of the two times values
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time operator-(Time left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator-=` to subtract/assign two time values
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return Difference of the two times values
///
////////////////////////////////////////////////////////////
constexpr Time& operator-=(Time& left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator*` to scale a time value
///
/// \param left Left operand (a time)
/// \param right Right operand (a number)
///
/// \return `left` multiplied by `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time operator*(Time left, float right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator*` to scale a time value
///
/// \param left Left operand (a time)
/// \param right Right operand (a number)
///
/// \return `left` multiplied by `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time operator*(Time left, std::int64_t right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator*` to scale a time value
///
/// \param left Left operand (a number)
/// \param right Right operand (a time)
///
/// \return `left` multiplied by `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time operator*(float left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator*` to scale a time value
///
/// \param left Left operand (a number)
/// \param right Right operand (a time)
///
/// \return `left` multiplied by `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time operator*(std::int64_t left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator*=` to scale/assign a time value
///
/// \param left Left operand (a time)
/// \param right Right operand (a number)
///
/// \return `left` multiplied by `right`
///
////////////////////////////////////////////////////////////
constexpr Time& operator*=(Time& left, float right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator*=` to scale/assign a time value
///
/// \param left Left operand (a time)
/// \param right Right operand (a number)
///
/// \return `left` multiplied by `right`
///
////////////////////////////////////////////////////////////
constexpr Time& operator*=(Time& left, std::int64_t right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator/` to scale a time value
///
/// \param left Left operand (a time)
/// \param right Right operand (a number)
///
/// \return `left` divided by `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time operator/(Time left, float right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator/` to scale a time value
///
/// \param left Left operand (a time)
/// \param right Right operand (a number)
///
/// \return `left` divided by `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time operator/(Time left, std::int64_t right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator/=` to scale/assign a time value
///
/// \param left Left operand (a time)
/// \param right Right operand (a number)
///
/// \return `left` divided by `right`
///
////////////////////////////////////////////////////////////
constexpr Time& operator/=(Time& left, float right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator/=` to scale/assign a time value
///
/// \param left Left operand (a time)
/// \param right Right operand (a number)
///
/// \return `left` divided by `right`
///
////////////////////////////////////////////////////////////
constexpr Time& operator/=(Time& left, std::int64_t right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator/` to compute the ratio of two time values
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return `left` divided by `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr float operator/(Time left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator%` to compute remainder of a time value
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return `left` modulo `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Time operator%(Time left, Time right);
////////////////////////////////////////////////////////////
/// \relates Time
/// \brief Overload of binary `operator%=` to compute/assign remainder of a time value
///
/// \param left Left operand (a time)
/// \param right Right operand (a time)
///
/// \return `left` modulo `right`
///
////////////////////////////////////////////////////////////
constexpr Time& operator%=(Time& left, Time right);
} // namespace sf
#include <SFML/System/Time.inl>
////////////////////////////////////////////////////////////
/// \class sf::Time
/// \ingroup system
///
/// `sf::Time` encapsulates a time value in a flexible way.
/// It allows to define a time value either as a number of
/// seconds, milliseconds or microseconds. It also works the
/// other way round: you can read a time value as either
/// a number of seconds, milliseconds or microseconds. It
/// even interoperates with the `<chrono>` header. You can
/// construct an `sf::Time` from a `chrono::duration` and read
/// any `sf::Time` as a chrono::duration.
///
/// By using such a flexible interface, the API doesn't
/// impose any fixed type or resolution for time values,
/// and let the user choose its own favorite representation.
///
/// Time values support the usual mathematical operations:
/// you can add or subtract two times, multiply or divide
/// a time by a number, compare two times, etc.
///
/// Since they represent a time span and not an absolute time
/// value, times can also be negative.
///
/// Usage example:
/// \code
/// sf::Time t1 = sf::seconds(0.1f);
/// std::int32_t milli = t1.asMilliseconds(); // 100
///
/// sf::Time t2 = sf::milliseconds(30);
/// std::int64_t micro = t2.asMicroseconds(); // 30'000
///
/// sf::Time t3 = sf::microseconds(-800'000);
/// float sec = t3.asSeconds(); // -0.8
///
/// sf::Time t4 = std::chrono::milliseconds(250);
/// std::chrono::microseconds micro2 = t4.toDuration(); // 250'000us
/// \endcode
///
/// \code
/// void update(sf::Time elapsed)
/// {
/// position += speed * elapsed.asSeconds();
/// }
///
/// update(sf::milliseconds(100));
/// \endcode
///
/// \see `sf::Clock`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,284 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Time.hpp> // NOLINT(misc-header-include-cycle)
#include <ratio>
#include <cassert>
namespace sf
{
////////////////////////////////////////////////////////////
template <typename Rep, typename Period>
constexpr Time::Time(const std::chrono::duration<Rep, Period>& duration) : m_microseconds(duration)
{
}
////////////////////////////////////////////////////////////
constexpr float Time::asSeconds() const
{
return std::chrono::duration<float>(m_microseconds).count();
}
////////////////////////////////////////////////////////////
constexpr std::int32_t Time::asMilliseconds() const
{
return std::chrono::duration_cast<std::chrono::duration<std::int32_t, std::milli>>(m_microseconds).count();
}
////////////////////////////////////////////////////////////
constexpr std::int64_t Time::asMicroseconds() const
{
return m_microseconds.count();
}
////////////////////////////////////////////////////////////
constexpr std::chrono::microseconds Time::toDuration() const
{
return m_microseconds;
}
////////////////////////////////////////////////////////////
template <typename Rep, typename Period>
constexpr Time::operator std::chrono::duration<Rep, Period>() const
{
return m_microseconds;
}
////////////////////////////////////////////////////////////
constexpr Time seconds(float amount)
{
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::duration<float>(amount));
}
////////////////////////////////////////////////////////////
constexpr Time milliseconds(std::int32_t amount)
{
return std::chrono::milliseconds(amount);
}
////////////////////////////////////////////////////////////
constexpr Time microseconds(std::int64_t amount)
{
return std::chrono::microseconds(amount);
}
////////////////////////////////////////////////////////////
constexpr bool operator==(Time left, Time right)
{
return left.asMicroseconds() == right.asMicroseconds();
}
////////////////////////////////////////////////////////////
constexpr bool operator!=(Time left, Time right)
{
return left.asMicroseconds() != right.asMicroseconds();
}
////////////////////////////////////////////////////////////
constexpr bool operator<(Time left, Time right)
{
return left.asMicroseconds() < right.asMicroseconds();
}
////////////////////////////////////////////////////////////
constexpr bool operator>(Time left, Time right)
{
return left.asMicroseconds() > right.asMicroseconds();
}
////////////////////////////////////////////////////////////
constexpr bool operator<=(Time left, Time right)
{
return left.asMicroseconds() <= right.asMicroseconds();
}
////////////////////////////////////////////////////////////
constexpr bool operator>=(Time left, Time right)
{
return left.asMicroseconds() >= right.asMicroseconds();
}
////////////////////////////////////////////////////////////
constexpr Time operator-(Time right)
{
return microseconds(-right.asMicroseconds());
}
////////////////////////////////////////////////////////////
constexpr Time operator+(Time left, Time right)
{
return microseconds(left.asMicroseconds() + right.asMicroseconds());
}
////////////////////////////////////////////////////////////
constexpr Time& operator+=(Time& left, Time right)
{
return left = left + right;
}
////////////////////////////////////////////////////////////
constexpr Time operator-(Time left, Time right)
{
return microseconds(left.asMicroseconds() - right.asMicroseconds());
}
////////////////////////////////////////////////////////////
constexpr Time& operator-=(Time& left, Time right)
{
return left = left - right;
}
////////////////////////////////////////////////////////////
constexpr Time operator*(Time left, float right)
{
return seconds(left.asSeconds() * right);
}
////////////////////////////////////////////////////////////
constexpr Time operator*(Time left, std::int64_t right)
{
return microseconds(left.asMicroseconds() * right);
}
////////////////////////////////////////////////////////////
constexpr Time operator*(float left, Time right)
{
return right * left;
}
////////////////////////////////////////////////////////////
constexpr Time operator*(std::int64_t left, Time right)
{
return right * left;
}
////////////////////////////////////////////////////////////
constexpr Time& operator*=(Time& left, float right)
{
return left = left * right;
}
////////////////////////////////////////////////////////////
constexpr Time& operator*=(Time& left, std::int64_t right)
{
return left = left * right;
}
////////////////////////////////////////////////////////////
constexpr Time operator/(Time left, float right)
{
assert(right != 0 && "Time::operator/ cannot divide by 0");
return seconds(left.asSeconds() / right);
}
////////////////////////////////////////////////////////////
constexpr Time operator/(Time left, std::int64_t right)
{
assert(right != 0 && "Time::operator/ cannot divide by 0");
return microseconds(left.asMicroseconds() / right);
}
////////////////////////////////////////////////////////////
constexpr Time& operator/=(Time& left, float right)
{
assert(right != 0 && "Time::operator/= cannot divide by 0");
return left = left / right;
}
////////////////////////////////////////////////////////////
constexpr Time& operator/=(Time& left, std::int64_t right)
{
assert(right != 0 && "Time::operator/= cannot divide by 0");
return left = left / right;
}
////////////////////////////////////////////////////////////
constexpr float operator/(Time left, Time right)
{
assert(right.asMicroseconds() != 0 && "Time::operator/ cannot divide by 0");
return left.asSeconds() / right.asSeconds();
}
////////////////////////////////////////////////////////////
constexpr Time operator%(Time left, Time right)
{
assert(right.asMicroseconds() != 0 && "Time::operator% cannot modulus by 0");
return microseconds(left.asMicroseconds() % right.asMicroseconds());
}
////////////////////////////////////////////////////////////
constexpr Time& operator%=(Time& left, Time right)
{
assert(right.asMicroseconds() != 0 && "Time::operator%= cannot modulus by 0");
return left = left % right;
}
////////////////////////////////////////////////////////////
// Static member data
////////////////////////////////////////////////////////////
// Note: the 'inline' keyword here is technically not required, but VS2019 fails
// to compile with a bogus "multiple definition" error if not explicitly used.
inline constexpr Time Time::Zero;
} // namespace sf

View File

@ -0,0 +1,758 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
#include <array>
#include <locale>
#include <cstdint>
#include <cstdlib>
namespace sf
{
template <unsigned int N>
class Utf;
////////////////////////////////////////////////////////////
/// \brief Specialization of the Utf template for UTF-8
///
////////////////////////////////////////////////////////////
template <>
class Utf<8>
{
public:
////////////////////////////////////////////////////////////
/// \brief Decode a single UTF-8 character
///
/// Decoding a character means finding its unique 32-bits
/// code (called the codepoint) in the Unicode standard.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Codepoint of the decoded UTF-8 character
/// \param replacement Replacement character to use in case the UTF-8 sequence is invalid
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In>
static In decode(In begin, In end, char32_t& output, char32_t replacement = 0);
////////////////////////////////////////////////////////////
/// \brief Encode a single UTF-8 character
///
/// Encoding a character means converting a unique 32-bits
/// code (called the codepoint) in the target encoding, UTF-8.
///
/// \param input Codepoint to encode as UTF-8
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to UTF-8 (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename Out>
static Out encode(char32_t input, Out output, std::uint8_t replacement = 0);
////////////////////////////////////////////////////////////
/// \brief Advance to the next UTF-8 character
///
/// This function is necessary for multi-elements encodings, as
/// a single character may use more than 1 storage element.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In>
static In next(In begin, In end);
////////////////////////////////////////////////////////////
/// \brief Count the number of characters of a UTF-8 sequence
///
/// This function is necessary for multi-elements encodings, as
/// a single character may use more than 1 storage element, thus the
/// total size can be different from (begin - end).
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In>
static std::size_t count(In begin, In end);
////////////////////////////////////////////////////////////
/// \brief Convert an ANSI characters range to UTF-8
///
/// The current global locale will be used by default, unless you
/// pass a custom one in the `locale` parameter.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out fromAnsi(In begin, In end, Out output, const std::locale& locale = {});
////////////////////////////////////////////////////////////
/// \brief Convert a wide characters range to UTF-8
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out fromWide(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-8
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out fromLatin1(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-8 characters range to ANSI characters
///
/// The current global locale will be used by default, unless you
/// pass a custom one in the `locale` parameter.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them)
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toAnsi(In begin, In end, Out output, char replacement = 0, const std::locale& locale = {});
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-8 characters range to wide characters
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toWide(In begin, In end, Out output, wchar_t replacement = 0);
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-8 characters range to latin-1 (ISO-5589-1) characters
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toLatin1(In begin, In end, Out output, char replacement = 0);
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-8 characters range to UTF-8
///
/// This functions does nothing more than a direct copy;
/// it is defined only to provide the same interface as other
/// specializations of the `sf::Utf<>` template, and allow
/// generic code to be written on top of it.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toUtf8(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-8 characters range to UTF-16
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toUtf16(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-8 characters range to UTF-32
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toUtf32(In begin, In end, Out output);
};
////////////////////////////////////////////////////////////
/// \brief Specialization of the Utf template for UTF-16
///
////////////////////////////////////////////////////////////
template <>
class Utf<16>
{
public:
////////////////////////////////////////////////////////////
/// \brief Decode a single UTF-16 character
///
/// Decoding a character means finding its unique 32-bits
/// code (called the codepoint) in the Unicode standard.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Codepoint of the decoded UTF-16 character
/// \param replacement Replacement character to use in case the UTF-8 sequence is invalid
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In>
static In decode(In begin, In end, char32_t& output, char32_t replacement = 0);
////////////////////////////////////////////////////////////
/// \brief Encode a single UTF-16 character
///
/// Encoding a character means converting a unique 32-bits
/// code (called the codepoint) in the target encoding, UTF-16.
///
/// \param input Codepoint to encode as UTF-16
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to UTF-16 (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename Out>
static Out encode(char32_t input, Out output, char16_t replacement = 0);
////////////////////////////////////////////////////////////
/// \brief Advance to the next UTF-16 character
///
/// This function is necessary for multi-elements encodings, as
/// a single character may use more than 1 storage element.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In>
static In next(In begin, In end);
////////////////////////////////////////////////////////////
/// \brief Count the number of characters of a UTF-16 sequence
///
/// This function is necessary for multi-elements encodings, as
/// a single character may use more than 1 storage element, thus the
/// total size can be different from (begin - end).
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In>
static std::size_t count(In begin, In end);
////////////////////////////////////////////////////////////
/// \brief Convert an ANSI characters range to UTF-16
///
/// The current global locale will be used by default, unless you
/// pass a custom one in the `locale` parameter.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out fromAnsi(In begin, In end, Out output, const std::locale& locale = {});
////////////////////////////////////////////////////////////
/// \brief Convert a wide characters range to UTF-16
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out fromWide(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-16
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out fromLatin1(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-16 characters range to ANSI characters
///
/// The current global locale will be used by default, unless you
/// pass a custom one in the `locale` parameter.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them)
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toAnsi(In begin, In end, Out output, char replacement = 0, const std::locale& locale = {});
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-16 characters range to wide characters
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toWide(In begin, In end, Out output, wchar_t replacement = 0);
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toLatin1(In begin, In end, Out output, char replacement = 0);
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-16 characters range to UTF-8
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toUtf8(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-16 characters range to UTF-16
///
/// This functions does nothing more than a direct copy;
/// it is defined only to provide the same interface as other
/// specializations of the `sf::Utf<>` template, and allow
/// generic code to be written on top of it.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toUtf16(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-16 characters range to UTF-32
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toUtf32(In begin, In end, Out output);
};
////////////////////////////////////////////////////////////
/// \brief Specialization of the Utf template for UTF-32
///
////////////////////////////////////////////////////////////
template <>
class Utf<32>
{
public:
////////////////////////////////////////////////////////////
/// \brief Decode a single UTF-32 character
///
/// Decoding a character means finding its unique 32-bits
/// code (called the codepoint) in the Unicode standard.
/// For UTF-32, the character value is the same as the codepoint.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Codepoint of the decoded UTF-32 character
/// \param replacement Replacement character to use in case the UTF-8 sequence is invalid
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In>
static In decode(In begin, In end, char32_t& output, char32_t replacement = 0);
////////////////////////////////////////////////////////////
/// \brief Encode a single UTF-32 character
///
/// Encoding a character means converting a unique 32-bits
/// code (called the codepoint) in the target encoding, UTF-32.
/// For UTF-32, the codepoint is the same as the character value.
///
/// \param input Codepoint to encode as UTF-32
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to UTF-32 (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename Out>
static Out encode(char32_t input, Out output, char32_t replacement = 0);
////////////////////////////////////////////////////////////
/// \brief Advance to the next UTF-32 character
///
/// This function is trivial for UTF-32, which can store
/// every character in a single storage element.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In>
static In next(In begin, In end);
////////////////////////////////////////////////////////////
/// \brief Count the number of characters of a UTF-32 sequence
///
/// This function is trivial for UTF-32, which can store
/// every character in a single storage element.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
///
/// \return Iterator pointing to one past the last read element of the input sequence
///
////////////////////////////////////////////////////////////
template <typename In>
static std::size_t count(In begin, In end);
////////////////////////////////////////////////////////////
/// \brief Convert an ANSI characters range to UTF-32
///
/// The current global locale will be used by default, unless you
/// pass a custom one in the `locale` parameter.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out fromAnsi(In begin, In end, Out output, const std::locale& locale = {});
////////////////////////////////////////////////////////////
/// \brief Convert a wide characters range to UTF-32
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out fromWide(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-32
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out fromLatin1(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-32 characters range to ANSI characters
///
/// The current global locale will be used by default, unless you
/// pass a custom one in the `locale` parameter.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them)
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toAnsi(In begin, In end, Out output, char replacement = 0, const std::locale& locale = {});
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-32 characters range to wide characters
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toWide(In begin, In end, Out output, wchar_t replacement = 0);
////////////////////////////////////////////////////////////
/// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toLatin1(In begin, In end, Out output, char replacement = 0);
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-32 characters range to UTF-8
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toUtf8(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-32 characters range to UTF-16
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toUtf16(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Convert a UTF-32 characters range to UTF-32
///
/// This functions does nothing more than a direct copy;
/// it is defined only to provide the same interface as other
/// specializations of the `sf::Utf<>` template, and allow
/// generic code to be written on top of it.
///
/// \param begin Iterator pointing to the beginning of the input sequence
/// \param end Iterator pointing to the end of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename In, typename Out>
static Out toUtf32(In begin, In end, Out output);
////////////////////////////////////////////////////////////
/// \brief Decode a single ANSI character to UTF-32
///
/// This function does not exist in other specializations
/// of `sf::Utf<>`, it is defined for convenience (it is used by
/// several other conversion functions).
///
/// \param input Input ANSI character
/// \param locale Locale to use for conversion
///
/// \return Converted character
///
////////////////////////////////////////////////////////////
template <typename In>
static char32_t decodeAnsi(In input, const std::locale& locale = {});
////////////////////////////////////////////////////////////
/// \brief Decode a single wide character to UTF-32
///
/// This function does not exist in other specializations
/// of `sf::Utf<>`, it is defined for convenience (it is used by
/// several other conversion functions).
///
/// \param input Input wide character
///
/// \return Converted character
///
////////////////////////////////////////////////////////////
template <typename In>
static char32_t decodeWide(In input);
////////////////////////////////////////////////////////////
/// \brief Encode a single UTF-32 character to ANSI
///
/// This function does not exist in other specializations
/// of `sf::Utf<>`, it is defined for convenience (it is used by
/// several other conversion functions).
///
/// \param codepoint Iterator pointing to the beginning of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement if the input character is not convertible to ANSI (use 0 to skip it)
/// \param locale Locale to use for conversion
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename Out>
static Out encodeAnsi(char32_t codepoint, Out output, char replacement = 0, const std::locale& locale = {});
////////////////////////////////////////////////////////////
/// \brief Encode a single UTF-32 character to wide
///
/// This function does not exist in other specializations
/// of `sf::Utf<>`, it is defined for convenience (it is used by
/// several other conversion functions).
///
/// \param codepoint Iterator pointing to the beginning of the input sequence
/// \param output Iterator pointing to the beginning of the output sequence
/// \param replacement Replacement if the input character is not convertible to wide (use 0 to skip it)
///
/// \return Iterator to the end of the output sequence which has been written
///
////////////////////////////////////////////////////////////
template <typename Out>
static Out encodeWide(char32_t codepoint, Out output, wchar_t replacement = 0);
};
// Make type aliases to get rid of the template syntax
using Utf8 = Utf<8>;
using Utf16 = Utf<16>;
using Utf32 = Utf<32>;
} // namespace sf
#include <SFML/System/Utf.inl>
////////////////////////////////////////////////////////////
/// \class sf::Utf
/// \ingroup system
///
/// Utility class providing generic functions for UTF conversions.
///
/// `sf::Utf` is a low-level, generic interface for counting, iterating,
/// encoding and decoding Unicode characters and strings. It is able
/// to handle ANSI, wide, latin-1, UTF-8, UTF-16 and UTF-32 encodings.
///
/// `sf::Utf<X>` functions are all static, these classes are not meant to
/// be instantiated. All the functions are template, so that you
/// can use any character / string type for a given encoding.
///
/// It has 3 specializations:
/// \li `sf::Utf<8>` (with `sf::Utf8` type alias)
/// \li `sf::Utf<16>` (with `sf::Utf16` type alias)
/// \li `sf::Utf<32>` (with `sf::Utf32` type alias)
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,829 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Utf.hpp> // NOLINT(misc-header-include-cycle)
#include <iterator>
////////////////////////////////////////////////////////////
// References:
//
// https://www.unicode.org/
// https://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.c
// https://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.h
// https://people.w3.org/rishida/scripts/uniview/conversion
//
////////////////////////////////////////////////////////////
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out copyBits(In begin, In end, Out output)
{
using InputType = typename std::iterator_traits<In>::value_type;
using OutputType = typename Out::container_type::value_type;
static_assert(sizeof(OutputType) >= sizeof(InputType));
static_assert(std::is_integral_v<InputType>);
static_assert(std::is_integral_v<OutputType>);
// The goal is to copy the byte representation of the input into the output type.
// A single static_cast will try to preserve the value as opposed to the byte representation
// which leads to issues when the input is signed and has a negative value. That will get
// wrapped to a very large unsigned value which is incorrect. To address this, we first
// cast the input to its unsigned equivalent then cast that to the destination type which has
// the property of preserving the byte representation of the input. A simple memcpy seems
// like a viable solution but copying the bytes of a type into a larger type yields different
// results on big versus little endian machines so it's not a possibility.
//
// Why do this? For example take the Latin1 character é. It has a byte representation of 0xE9
// and a signed integer value of -23. If you cast -23 to a char32_t, you get a value of
// 4294967273 which is not a valid Unicode codepoint. What we actually wanted was a char32_t
// with the byte representation 0x000000E9.
while (begin != end)
*output++ = static_cast<OutputType>(static_cast<std::make_unsigned_t<InputType>>(*begin++));
return output;
}
} // namespace priv
template <typename In>
In Utf<8>::decode(In begin, In end, char32_t& output, char32_t replacement)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
// clang-format off
// Some useful precomputed data
static constexpr std::array<std::uint8_t, 256> trailing =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5
};
static constexpr std::array<std::uint32_t, 6> offsets =
{
0x00000000, 0x00003080, 0x000E2080, 0x03C82080, 0xFA082080, 0x82082080
};
// clang-format on
// decode the character
const auto trailingBytes = trailing[static_cast<std::uint8_t>(*begin)];
if (trailingBytes < std::distance(begin, end))
{
output = 0;
// clang-format off
switch (trailingBytes)
{
case 5: output += static_cast<std::uint8_t>(*begin++); output <<= 6; [[fallthrough]];
case 4: output += static_cast<std::uint8_t>(*begin++); output <<= 6; [[fallthrough]];
case 3: output += static_cast<std::uint8_t>(*begin++); output <<= 6; [[fallthrough]];
case 2: output += static_cast<std::uint8_t>(*begin++); output <<= 6; [[fallthrough]];
case 1: output += static_cast<std::uint8_t>(*begin++); output <<= 6; [[fallthrough]];
case 0: output += static_cast<std::uint8_t>(*begin++);
}
// clang-format on
output -= offsets[trailingBytes];
}
else
{
// Incomplete character
begin = end;
output = replacement;
}
return begin;
}
////////////////////////////////////////////////////////////
template <typename Out>
Out Utf<8>::encode(char32_t input, Out output, std::uint8_t replacement)
{
// Some useful precomputed data
static constexpr std::array<std::uint8_t, 7> firstBytes = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
// encode the character
if ((input > 0x0010FFFF) || ((input >= 0xD800) && (input <= 0xDBFF)))
{
// Invalid character
if (replacement)
*output++ = static_cast<typename Out::container_type::value_type>(replacement);
}
else
{
// Valid character
// Get the number of bytes to write
std::size_t bytestoWrite = 1;
// clang-format off
if (input < 0x80) bytestoWrite = 1;
else if (input < 0x800) bytestoWrite = 2;
else if (input < 0x10000) bytestoWrite = 3;
else if (input <= 0x0010FFFF) bytestoWrite = 4;
// clang-format on
// Extract the bytes to write
std::array<std::uint8_t, 4> bytes{};
// clang-format off
switch (bytestoWrite)
{
case 4: bytes[3] = static_cast<std::uint8_t>((input | 0x80) & 0xBF); input >>= 6; [[fallthrough]];
case 3: bytes[2] = static_cast<std::uint8_t>((input | 0x80) & 0xBF); input >>= 6; [[fallthrough]];
case 2: bytes[1] = static_cast<std::uint8_t>((input | 0x80) & 0xBF); input >>= 6; [[fallthrough]];
case 1: bytes[0] = static_cast<std::uint8_t> (input | firstBytes[bytestoWrite]);
}
// clang-format on
// Add them to the output
output = priv::copyBits(bytes.data(), bytes.data() + bytestoWrite, output);
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In>
In Utf<8>::next(In begin, In end)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
char32_t codepoint = 0;
return decode(begin, end, codepoint);
}
////////////////////////////////////////////////////////////
template <typename In>
std::size_t Utf<8>::count(In begin, In end)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
std::size_t length = 0;
while (begin != end)
{
begin = next(begin, end);
++length;
}
return length;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<8>::fromAnsi(In begin, In end, Out output, const std::locale& locale)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
while (begin != end)
{
const char32_t codepoint = Utf<32>::decodeAnsi(*begin++, locale);
output = encode(codepoint, output);
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<8>::fromWide(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(wchar_t));
while (begin != end)
{
const char32_t codepoint = Utf<32>::decodeWide(*begin++);
output = encode(codepoint, output);
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<8>::fromLatin1(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
// Latin-1 is directly compatible with Unicode encodings,
// and can thus be treated as (a sub-range of) UTF-32
while (begin != end)
output = encode(static_cast<std::uint8_t>(*begin++), output);
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<8>::toAnsi(In begin, In end, Out output, char replacement, const std::locale& locale)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
while (begin != end)
{
char32_t codepoint = 0;
begin = decode(begin, end, codepoint);
output = Utf<32>::encodeAnsi(codepoint, output, replacement, locale);
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<8>::toWide(In begin, In end, Out output, wchar_t replacement)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
while (begin != end)
{
char32_t codepoint = 0;
begin = decode(begin, end, codepoint);
output = Utf<32>::encodeWide(codepoint, output, replacement);
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<8>::toLatin1(In begin, In end, Out output, char replacement)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
// Latin-1 is directly compatible with Unicode encodings,
// and can thus be treated as (a sub-range of) UTF-32
while (begin != end)
{
char32_t codepoint = 0;
begin = decode(begin, end, codepoint);
*output++ = codepoint < 256 ? static_cast<char>(codepoint) : replacement;
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<8>::toUtf8(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
return priv::copyBits(begin, end, output);
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<8>::toUtf16(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
while (begin != end)
{
char32_t codepoint = 0;
begin = decode(begin, end, codepoint);
output = Utf<16>::encode(codepoint, output);
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<8>::toUtf32(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
while (begin != end)
{
char32_t codepoint = 0;
begin = decode(begin, end, codepoint);
*output++ = codepoint;
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In>
In Utf<16>::decode(In begin, In end, char32_t& output, char32_t replacement)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char16_t));
const char16_t first = *begin++;
// If it's a surrogate pair, first convert to a single UTF-32 character
if ((first >= 0xD800) && (first <= 0xDBFF))
{
if (begin != end)
{
const std::uint32_t second = *begin++;
if ((second >= 0xDC00) && (second <= 0xDFFF))
{
// The second element is valid: convert the two elements to a UTF-32 character
output = ((first - 0xD800u) << 10) + (second - 0xDC00) + 0x0010000;
}
else
{
// Invalid character
output = replacement;
}
}
else
{
// Invalid character
begin = end;
output = replacement;
}
}
else
{
// We can make a direct copy
output = first;
}
return begin;
}
////////////////////////////////////////////////////////////
template <typename Out>
Out Utf<16>::encode(char32_t input, Out output, char16_t replacement)
{
if (input <= 0xFFFF)
{
// The character can be copied directly, we just need to check if it's in the valid range
if ((input >= 0xD800) && (input <= 0xDFFF))
{
// Invalid character (this range is reserved)
if (replacement)
*output++ = replacement;
}
else
{
// Valid character directly convertible to a single UTF-16 character
*output++ = static_cast<char16_t>(input);
}
}
else if (input > 0x0010FFFF)
{
// Invalid character (greater than the maximum Unicode value)
if (replacement)
*output++ = replacement;
}
else
{
// The input character will be converted to two UTF-16 elements
input -= 0x0010000;
*output++ = static_cast<char16_t>((input >> 10) + 0xD800);
*output++ = static_cast<char16_t>((input & 0x3FFUL) + 0xDC00);
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In>
In Utf<16>::next(In begin, In end)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char16_t));
char32_t codepoint = 0;
return decode(begin, end, codepoint);
}
////////////////////////////////////////////////////////////
template <typename In>
std::size_t Utf<16>::count(In begin, In end)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char16_t));
std::size_t length = 0;
while (begin != end)
{
begin = next(begin, end);
++length;
}
return length;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<16>::fromAnsi(In begin, In end, Out output, const std::locale& locale)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
while (begin != end)
{
const char32_t codepoint = Utf<32>::decodeAnsi(*begin++, locale);
output = encode(codepoint, output);
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<16>::fromWide(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(wchar_t));
while (begin != end)
{
const char32_t codepoint = Utf<32>::decodeWide(*begin++);
output = encode(codepoint, output);
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<16>::fromLatin1(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
// Latin-1 is directly compatible with Unicode encodings,
// and can thus be treated as (a sub-range of) UTF-32
return priv::copyBits(begin, end, output);
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<16>::toAnsi(In begin, In end, Out output, char replacement, const std::locale& locale)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char16_t));
while (begin != end)
{
char32_t codepoint = 0;
begin = decode(begin, end, codepoint);
output = Utf<32>::encodeAnsi(codepoint, output, replacement, locale);
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<16>::toWide(In begin, In end, Out output, wchar_t replacement)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char16_t));
while (begin != end)
{
char32_t codepoint = 0;
begin = decode(begin, end, codepoint);
output = Utf<32>::encodeWide(codepoint, output, replacement);
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<16>::toLatin1(In begin, In end, Out output, char replacement)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char16_t));
// Latin-1 is directly compatible with Unicode encodings,
// and can thus be treated as (a sub-range of) UTF-32
while (begin != end)
{
*output++ = *begin < 256 ? static_cast<char>(*begin) : replacement;
++begin;
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<16>::toUtf8(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char16_t));
while (begin != end)
{
char32_t codepoint = 0;
begin = decode(begin, end, codepoint);
output = Utf<8>::encode(codepoint, output);
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<16>::toUtf16(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char16_t));
return priv::copyBits(begin, end, output);
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<16>::toUtf32(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char16_t));
while (begin != end)
{
char32_t codepoint = 0;
begin = decode(begin, end, codepoint);
*output++ = codepoint;
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In>
In Utf<32>::decode(In begin, In /*end*/, char32_t& output, char32_t /*replacement*/)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char32_t));
output = *begin++;
return begin;
}
////////////////////////////////////////////////////////////
template <typename Out>
Out Utf<32>::encode(char32_t input, Out output, char32_t /*replacement*/)
{
*output++ = input;
return output;
}
////////////////////////////////////////////////////////////
template <typename In>
In Utf<32>::next(In begin, In /*end*/)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char32_t));
return ++begin;
}
////////////////////////////////////////////////////////////
template <typename In>
std::size_t Utf<32>::count(In begin, In end)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char32_t));
return static_cast<std::size_t>(end - begin);
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<32>::fromAnsi(In begin, In end, Out output, const std::locale& locale)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
while (begin != end)
*output++ = decodeAnsi(*begin++, locale);
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<32>::fromWide(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(wchar_t));
while (begin != end)
*output++ = decodeWide(*begin++);
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<32>::fromLatin1(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char));
// Latin-1 is directly compatible with Unicode encodings,
// and can thus be treated as (a sub-range of) UTF-32
return priv::copyBits(begin, end, output);
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<32>::toAnsi(In begin, In end, Out output, char replacement, const std::locale& locale)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char32_t));
while (begin != end)
output = encodeAnsi(*begin++, output, replacement, locale);
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<32>::toWide(In begin, In end, Out output, wchar_t replacement)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char32_t));
while (begin != end)
output = encodeWide(*begin++, output, replacement);
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<32>::toLatin1(In begin, In end, Out output, char replacement)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char32_t));
// Latin-1 is directly compatible with Unicode encodings,
// and can thus be treated as (a sub-range of) UTF-32
while (begin != end)
{
*output++ = *begin < 256 ? static_cast<char>(*begin) : replacement;
++begin;
}
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<32>::toUtf8(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char32_t));
while (begin != end)
output = Utf<8>::encode(*begin++, output);
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<32>::toUtf16(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char32_t));
while (begin != end)
output = Utf<16>::encode(*begin++, output);
return output;
}
////////////////////////////////////////////////////////////
template <typename In, typename Out>
Out Utf<32>::toUtf32(In begin, In end, Out output)
{
static_assert(sizeof(decltype(*begin)) == sizeof(char32_t));
return priv::copyBits(begin, end, output);
}
////////////////////////////////////////////////////////////
template <typename In>
char32_t Utf<32>::decodeAnsi(In input, const std::locale& locale)
{
// Get the facet of the locale which deals with character conversion
const auto& facet = std::use_facet<std::ctype<wchar_t>>(locale);
// Use the facet to convert each character of the input string
return static_cast<char32_t>(facet.widen(input));
}
////////////////////////////////////////////////////////////
template <typename In>
char32_t Utf<32>::decodeWide(In input)
{
// The encoding of wide characters is not well defined and is left to the system;
// however we can safely assume that it is UCS-2 on Windows and
// UCS-4 on Unix systems.
// In both cases, a simple copy is enough (UCS-2 is a subset of UCS-4,
// and UCS-4 *is* UTF-32).
return static_cast<char32_t>(input);
}
////////////////////////////////////////////////////////////
template <typename Out>
Out Utf<32>::encodeAnsi(char32_t codepoint, Out output, char replacement, const std::locale& locale)
{
// Get the facet of the locale which deals with character conversion
const auto& facet = std::use_facet<std::ctype<wchar_t>>(locale);
// Use the facet to convert each character of the input string
*output++ = facet.narrow(static_cast<wchar_t>(codepoint), replacement);
return output;
}
////////////////////////////////////////////////////////////
template <typename Out>
Out Utf<32>::encodeWide(char32_t codepoint, Out output, wchar_t replacement)
{
// The encoding of wide characters is not well defined and is left to the system;
// however we can safely assume that it is UCS-2 on Windows and
// UCS-4 on Unix systems.
// For UCS-2 we need to check if the source characters fits in (UCS-2 is a subset of UCS-4).
// For UCS-4 we can do a direct copy (UCS-4 *is* UTF-32).
if constexpr (sizeof(wchar_t) == 4)
{
*output++ = static_cast<wchar_t>(codepoint);
}
else
{
if ((codepoint <= 0xFFFF) && ((codepoint < 0xD800) || (codepoint > 0xDFFF)))
{
*output++ = static_cast<wchar_t>(codepoint);
}
else if (replacement)
{
*output++ = replacement;
}
}
return output;
}
} // namespace sf

View File

@ -0,0 +1,435 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
#include <SFML/System/Export.hpp>
#include <SFML/System/Angle.hpp>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Class template for manipulating
/// 2-dimensional vectors
///
////////////////////////////////////////////////////////////
template <typename T>
class Vector2
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Creates a `Vector2(0, 0)`.
///
////////////////////////////////////////////////////////////
constexpr Vector2() = default;
////////////////////////////////////////////////////////////
/// \brief Construct the vector from cartesian coordinates
///
/// \param x X coordinate
/// \param y Y coordinate
///
////////////////////////////////////////////////////////////
constexpr Vector2(T x, T y);
////////////////////////////////////////////////////////////
/// \brief Converts the vector to another type of vector
///
////////////////////////////////////////////////////////////
template <typename U>
constexpr explicit operator Vector2<U>() const;
////////////////////////////////////////////////////////////
/// \brief Construct the vector from polar coordinates <i><b>(floating-point)</b></i>
///
/// \param r Length of vector (can be negative)
/// \param phi Angle from X axis
///
/// Note that this constructor is lossy: calling `length()` and `angle()`
/// may return values different to those provided in this constructor.
///
/// In particular, these transforms can be applied:
/// * `Vector2(r, phi) == Vector2(-r, phi + 180_deg)`
/// * `Vector2(r, phi) == Vector2(r, phi + n * 360_deg)`
///
////////////////////////////////////////////////////////////
SFML_SYSTEM_API Vector2(T r, Angle phi);
////////////////////////////////////////////////////////////
/// \brief Length of the vector <i><b>(floating-point)</b></i>.
///
/// If you are not interested in the actual length, but only in comparisons, consider using `lengthSquared()`.
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API T length() const;
////////////////////////////////////////////////////////////
/// \brief Square of vector's length.
///
/// Suitable for comparisons, more efficient than `length()`.
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr T lengthSquared() const;
////////////////////////////////////////////////////////////
/// \brief Vector with same direction but length 1 <i><b>(floating-point)</b></i>.
///
/// \pre `*this` is no zero vector.
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API Vector2 normalized() const;
////////////////////////////////////////////////////////////
/// \brief Signed angle from `*this` to `rhs` <i><b>(floating-point)</b></i>.
///
/// \return The smallest angle which rotates `*this` in positive
/// or negative direction, until it has the same direction as `rhs`.
/// The result has a sign and lies in the range [-180, 180) degrees.
/// \pre Neither `*this` nor `rhs` is a zero vector.
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API Angle angleTo(Vector2 rhs) const;
////////////////////////////////////////////////////////////
/// \brief Signed angle from +X or (1,0) vector <i><b>(floating-point)</b></i>.
///
/// For example, the vector (1,0) corresponds to 0 degrees, (0,1) corresponds to 90 degrees.
///
/// \return Angle in the range [-180, 180) degrees.
/// \pre This vector is no zero vector.
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API Angle angle() const;
////////////////////////////////////////////////////////////
/// \brief Rotate by angle \c phi <i><b>(floating-point)</b></i>.
///
/// Returns a vector with same length but different direction.
///
/// In SFML's default coordinate system with +X right and +Y down,
/// this amounts to a clockwise rotation by `phi`.
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API Vector2 rotatedBy(Angle phi) const;
////////////////////////////////////////////////////////////
/// \brief Projection of this vector onto `axis` <i><b>(floating-point)</b></i>.
///
/// \param axis Vector being projected onto. Need not be normalized.
/// \pre `axis` must not have length zero.
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API Vector2 projectedOnto(Vector2 axis) const;
////////////////////////////////////////////////////////////
/// \brief Returns a perpendicular vector.
///
/// Returns `*this` rotated by +90 degrees; (x,y) becomes (-y,x).
/// For example, the vector (1,0) is transformed to (0,1).
///
/// In SFML's default coordinate system with +X right and +Y down,
/// this amounts to a clockwise rotation.
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Vector2 perpendicular() const;
////////////////////////////////////////////////////////////
/// \brief Dot product of two 2D vectors.
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr T dot(Vector2 rhs) const;
////////////////////////////////////////////////////////////
/// \brief Z component of the cross product of two 2D vectors.
///
/// Treats the operands as 3D vectors, computes their cross product
/// and returns the result's Z component (X and Y components are always zero).
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr T cross(Vector2 rhs) const;
////////////////////////////////////////////////////////////
/// \brief Component-wise multiplication of `*this` and `rhs`.
///
/// Computes `(lhs.x*rhs.x, lhs.y*rhs.y)`.
///
/// Scaling is the most common use case for component-wise multiplication/division.
/// This operation is also known as the Hadamard or Schur product.
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Vector2 componentWiseMul(Vector2 rhs) const;
////////////////////////////////////////////////////////////
/// \brief Component-wise division of `*this` and `rhs`.
///
/// Computes `(lhs.x/rhs.x, lhs.y/rhs.y)`.
///
/// Scaling is the most common use case for component-wise multiplication/division.
///
/// \pre Neither component of `rhs` is zero.
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Vector2 componentWiseDiv(Vector2 rhs) const;
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
T x{}; //!< X coordinate of the vector
T y{}; //!< Y coordinate of the vector
};
// Define the most common types
using Vector2i = Vector2<int>;
using Vector2u = Vector2<unsigned int>;
using Vector2f = Vector2<float>;
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of unary `operator-`
///
/// \param right Vector to negate
///
/// \return Member-wise opposite of the vector
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr Vector2<T> operator-(Vector2<T> right);
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary `operator+=`
///
/// This operator performs a member-wise addition of both vectors,
/// and assigns the result to `left`.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return Reference to `left`
///
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T>& operator+=(Vector2<T>& left, Vector2<T> right);
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary `operator-=`
///
/// This operator performs a member-wise subtraction of both vectors,
/// and assigns the result to `left`.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return Reference to \c left
///
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T>& operator-=(Vector2<T>& left, Vector2<T> right);
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary `operator+`
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return Member-wise addition of both vectors
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr Vector2<T> operator+(Vector2<T> left, Vector2<T> right);
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary `operator-`
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return Member-wise subtraction of both vectors
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr Vector2<T> operator-(Vector2<T> left, Vector2<T> right);
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary `operator*`
///
/// \param left Left operand (a vector)
/// \param right Right operand (a scalar value)
///
/// \return Member-wise multiplication by `right`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr Vector2<T> operator*(Vector2<T> left, T right);
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary `operator*`
///
/// \param left Left operand (a scalar value)
/// \param right Right operand (a vector)
///
/// \return Member-wise multiplication by `left`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr Vector2<T> operator*(T left, Vector2<T> right);
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary `operator*=`
///
/// This operator performs a member-wise multiplication by `right`,
/// and assigns the result to `left`.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a scalar value)
///
/// \return Reference to `left`
///
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T>& operator*=(Vector2<T>& left, T right);
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary `operator/`
///
/// \param left Left operand (a vector)
/// \param right Right operand (a scalar value)
///
/// \return Member-wise division by `right`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr Vector2<T> operator/(Vector2<T> left, T right);
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary `operator/=`
///
/// This operator performs a member-wise division by `right`,
/// and assigns the result to `left`.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a scalar value)
///
/// \return Reference to `left`
///
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T>& operator/=(Vector2<T>& left, T right);
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary `operator==`
///
/// This operator compares strict equality between two vectors.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return `true` if `left` is equal to `right`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr bool operator==(Vector2<T> left, Vector2<T> right);
////////////////////////////////////////////////////////////
/// \relates Vector2
/// \brief Overload of binary `operator!=`
///
/// This operator compares strict difference between two vectors.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return `true` if `left` is not equal to `right`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr bool operator!=(Vector2<T> left, Vector2<T> right);
} // namespace sf
#include <SFML/System/Vector2.inl>
////////////////////////////////////////////////////////////
/// \class sf::Vector2
/// \ingroup system
///
/// `sf::Vector2` is a simple class that defines a mathematical
/// vector with two coordinates (x and y). It can be used to
/// represent anything that has two dimensions: a size, a point,
/// a velocity, a scale, etc.
///
/// The API provides basic arithmetic (addition, subtraction, scale), as
/// well as more advanced geometric operations, such as dot/cross products,
/// length and angle computations, projections, rotations, etc.
///
/// The template parameter T is the type of the coordinates. It
/// can be any type that supports arithmetic operations (+, -, /, *)
/// and comparisons (==, !=), for example int or float.
/// Note that some operations are only meaningful for vectors where T is
/// a floating point type (e.g. float or double), often because
/// results cannot be represented accurately with integers.
/// The method documentation mentions "(floating-point)" in those cases.
///
/// You generally don't have to care about the templated form (`sf::Vector2<T>`),
/// the most common specializations have special type aliases:
/// \li `sf::Vector2<float>` is `sf::Vector2f`
/// \li `sf::Vector2<int>` is `sf::Vector2i`
/// \li `sf::Vector2<unsigned int>` is `sf::Vector2u`
///
/// The `sf::Vector2` class has a simple interface, its x and y members
/// can be accessed directly (there are no accessors like setX(), getX()).
///
/// Usage example:
/// \code
/// sf::Vector2f v(16.5f, 24.f);
/// v.x = 18.2f;
/// float y = v.y;
///
/// sf::Vector2f w = v * 5.f;
/// sf::Vector2f u;
/// u = v + w;
///
/// float s = v.dot(w);
///
/// bool different = (v != u);
/// \endcode
///
/// Note: for 3-dimensional vectors, see `sf::Vector3`.
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,217 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Vector2.hpp> // NOLINT(misc-header-include-cycle)
#include <cassert>
namespace sf
{
////////////////////////////////////////////////////////////
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
template <typename T>
constexpr Vector2<T>::Vector2(T x, T y) : x(x), y(y)
{
}
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
////////////////////////////////////////////////////////////
template <typename T>
template <typename U>
constexpr Vector2<T>::operator Vector2<U>() const
{
return Vector2<U>(static_cast<U>(x), static_cast<U>(y));
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr T Vector2<T>::lengthSquared() const
{
return dot(*this);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T> Vector2<T>::perpendicular() const
{
return Vector2<T>(-y, x);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr T Vector2<T>::dot(Vector2<T> rhs) const
{
return x * rhs.x + y * rhs.y;
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr T Vector2<T>::cross(Vector2<T> rhs) const
{
return x * rhs.y - y * rhs.x;
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T> Vector2<T>::componentWiseMul(Vector2<T> rhs) const
{
return Vector2<T>(x * rhs.x, y * rhs.y);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T> Vector2<T>::componentWiseDiv(Vector2<T> rhs) const
{
assert(rhs.x != 0 && "Vector2::componentWiseDiv() cannot divide by 0");
assert(rhs.y != 0 && "Vector2::componentWiseDiv() cannot divide by 0");
return Vector2<T>(x / rhs.x, y / rhs.y);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T> operator-(Vector2<T> right)
{
return Vector2<T>(-right.x, -right.y);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T>& operator+=(Vector2<T>& left, Vector2<T> right)
{
left.x += right.x;
left.y += right.y;
return left;
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T>& operator-=(Vector2<T>& left, Vector2<T> right)
{
left.x -= right.x;
left.y -= right.y;
return left;
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T> operator+(Vector2<T> left, Vector2<T> right)
{
return Vector2<T>(left.x + right.x, left.y + right.y);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T> operator-(Vector2<T> left, Vector2<T> right)
{
return Vector2<T>(left.x - right.x, left.y - right.y);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T> operator*(Vector2<T> left, T right)
{
return Vector2<T>(left.x * right, left.y * right);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T> operator*(T left, Vector2<T> right)
{
return Vector2<T>(right.x * left, right.y * left);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T>& operator*=(Vector2<T>& left, T right)
{
left.x *= right;
left.y *= right;
return left;
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T> operator/(Vector2<T> left, T right)
{
assert(right != 0 && "Vector2::operator/ cannot divide by 0");
return Vector2<T>(left.x / right, left.y / right);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector2<T>& operator/=(Vector2<T>& left, T right)
{
assert(right != 0 && "Vector2::operator/= cannot divide by 0");
left.x /= right;
left.y /= right;
return left;
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr bool operator==(Vector2<T> left, Vector2<T> right)
{
return (left.x == right.x) && (left.y == right.y);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr bool operator!=(Vector2<T> left, Vector2<T> right)
{
return !(left == right);
}
} // namespace sf

View File

@ -0,0 +1,356 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
#include <SFML/System/Export.hpp>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Utility template class for manipulating
/// 3-dimensional vectors
///
////////////////////////////////////////////////////////////
template <typename T>
class Vector3
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Creates a `Vector3(0, 0, 0)`.
///
////////////////////////////////////////////////////////////
constexpr Vector3() = default;
////////////////////////////////////////////////////////////
/// \brief Construct the vector from its coordinates
///
/// \param x X coordinate
/// \param y Y coordinate
/// \param z Z coordinate
///
////////////////////////////////////////////////////////////
constexpr Vector3(T x, T y, T z);
////////////////////////////////////////////////////////////
/// \brief Converts the vector to another type of vector
///
////////////////////////////////////////////////////////////
template <typename U>
constexpr explicit operator Vector3<U>() const;
////////////////////////////////////////////////////////////
/// \brief Length of the vector <i><b>(floating-point)</b></i>.
///
/// If you are not interested in the actual length, but only in comparisons, consider using `lengthSquared()`.
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API T length() const;
////////////////////////////////////////////////////////////
/// \brief Square of vector's length.
///
/// Suitable for comparisons, more efficient than `length()`.
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr T lengthSquared() const;
////////////////////////////////////////////////////////////
/// \brief Vector with same direction but length 1 <i><b>(floating-point)</b></i>.
///
/// \pre `*this` is no zero vector.
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_SYSTEM_API Vector3 normalized() const;
////////////////////////////////////////////////////////////
/// \brief Dot product of two 3D vectors.
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr T dot(const Vector3& rhs) const;
////////////////////////////////////////////////////////////
/// \brief Cross product of two 3D vectors.
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Vector3 cross(const Vector3& rhs) const;
////////////////////////////////////////////////////////////
/// \brief Component-wise multiplication of `*this` and `rhs`.
///
/// Computes `(lhs.x*rhs.x, lhs.y*rhs.y, lhs.z*rhs.z)`.
///
/// Scaling is the most common use case for component-wise multiplication/division.
/// This operation is also known as the Hadamard or Schur product.
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Vector3 componentWiseMul(const Vector3& rhs) const;
////////////////////////////////////////////////////////////
/// \brief Component-wise division of `*this` and `rhs`.
///
/// Computes `(lhs.x/rhs.x, lhs.y/rhs.y, lhs.z/rhs.z)`.
///
/// Scaling is the most common use case for component-wise multiplication/division.
///
/// \pre Neither component of `rhs` is zero.
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Vector3 componentWiseDiv(const Vector3& rhs) const;
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
T x{}; //!< X coordinate of the vector
T y{}; //!< Y coordinate of the vector
T z{}; //!< Z coordinate of the vector
};
////////////////////////////////////////////////////////////
/// \relates Vector3
/// \brief Overload of unary `operator-`
///
/// \param left Vector to negate
///
/// \return Member-wise opposite of the vector
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr Vector3<T> operator-(const Vector3<T>& left);
////////////////////////////////////////////////////////////
/// \relates Vector3
/// \brief Overload of binary `operator+=`
///
/// This operator performs a member-wise addition of both vectors,
/// and assigns the result to `left`.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return Reference to `left`
///
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T>& operator+=(Vector3<T>& left, const Vector3<T>& right);
////////////////////////////////////////////////////////////
/// \relates Vector3
/// \brief Overload of binary `operator-=`
///
/// This operator performs a member-wise subtraction of both vectors,
/// and assigns the result to `left`.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return Reference to `left`
///
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T>& operator-=(Vector3<T>& left, const Vector3<T>& right);
////////////////////////////////////////////////////////////
/// \relates Vector3
/// \brief Overload of binary `operator+`
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return Member-wise addition of both vectors
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr Vector3<T> operator+(const Vector3<T>& left, const Vector3<T>& right);
////////////////////////////////////////////////////////////
/// \relates Vector3
/// \brief Overload of binary `operator-`
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return Member-wise subtraction of both vectors
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr Vector3<T> operator-(const Vector3<T>& left, const Vector3<T>& right);
////////////////////////////////////////////////////////////
/// \relates Vector3
/// \brief Overload of binary `operator*`
///
/// \param left Left operand (a vector)
/// \param right Right operand (a scalar value)
///
/// \return Member-wise multiplication by `right`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr Vector3<T> operator*(const Vector3<T>& left, T right);
////////////////////////////////////////////////////////////
/// \relates Vector3
/// \brief Overload of binary `operator*`
///
/// \param left Left operand (a scalar value)
/// \param right Right operand (a vector)
///
/// \return Member-wise multiplication by `left`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr Vector3<T> operator*(T left, const Vector3<T>& right);
////////////////////////////////////////////////////////////
/// \relates Vector3
/// \brief Overload of binary `operator*=`
///
/// This operator performs a member-wise multiplication by `right`,
/// and assigns the result to `left`.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a scalar value)
///
/// \return Reference to `left`
///
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T>& operator*=(Vector3<T>& left, T right);
////////////////////////////////////////////////////////////
/// \relates Vector3
/// \brief Overload of binary `operator/`
///
/// \param left Left operand (a vector)
/// \param right Right operand (a scalar value)
///
/// \return Member-wise division by `right`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr Vector3<T> operator/(const Vector3<T>& left, T right);
////////////////////////////////////////////////////////////
/// \relates Vector3
/// \brief Overload of binary `operator/=`
///
/// This operator performs a member-wise division by `right`,
/// and assigns the result to `left`.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a scalar value)
///
/// \return Reference to `left`
///
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T>& operator/=(Vector3<T>& left, T right);
////////////////////////////////////////////////////////////
/// \relates Vector3
/// \brief Overload of binary `operator==`
///
/// This operator compares strict equality between two vectors.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return `true` if `left` is equal to `right`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr bool operator==(const Vector3<T>& left, const Vector3<T>& right);
////////////////////////////////////////////////////////////
/// \relates Vector3
/// \brief Overload of binary `operator!=`
///
/// This operator compares strict difference between two vectors.
///
/// \param left Left operand (a vector)
/// \param right Right operand (a vector)
///
/// \return `true` if `left` is not equal to `right`
///
////////////////////////////////////////////////////////////
template <typename T>
[[nodiscard]] constexpr bool operator!=(const Vector3<T>& left, const Vector3<T>& right);
// Aliases for the most common types
using Vector3i = Vector3<int>;
using Vector3f = Vector3<float>;
} // namespace sf
#include <SFML/System/Vector3.inl>
////////////////////////////////////////////////////////////
/// \class sf::Vector3
/// \ingroup system
///
/// `sf::Vector3` is a simple class that defines a mathematical
/// vector with three coordinates (x, y and z). It can be used to
/// represent anything that has three dimensions: a size, a point,
/// a velocity, etc.
///
/// The template parameter T is the type of the coordinates. It
/// can be any type that supports arithmetic operations (+, -, /, *)
/// and comparisons (==, !=), for example int or float.
/// Note that some operations are only meaningful for vectors where T is
/// a floating point type (e.g. float or double), often because
/// results cannot be represented accurately with integers.
/// The method documentation mentions "(floating-point)" in those cases.
///
/// You generally don't have to care about the templated form (`sf::Vector3<T>`),
/// the most common specializations have special type aliases:
/// \li `sf::Vector3<float>` is `sf::Vector3f`
/// \li `sf::Vector3<int>` is `sf::Vector3i`
///
/// The `sf::Vector3` class has a small and simple interface, its x, y and z members
/// can be accessed directly (there are no accessors like `setX()`, `getX()`).
///
/// Usage example:
/// \code
/// sf::Vector3f v(16.5f, 24.f, -3.2f);
/// v.x = 18.2f;
/// float y = v.y;
///
/// sf::Vector3f w = v * 5.f;
/// sf::Vector3f u;
/// u = v + w;
///
/// float s = v.dot(w);
/// sf::Vector3f t = v.cross(w);
///
/// bool different = (v != u);
/// \endcode
///
/// Note: for 2-dimensional vectors, see `sf::Vector2`.
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,214 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Vector3.hpp> // NOLINT(misc-header-include-cycle)
#include <cassert>
namespace sf
{
////////////////////////////////////////////////////////////
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
template <typename T>
constexpr Vector3<T>::Vector3(T x, T y, T z) : x(x), y(y), z(z)
{
}
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
////////////////////////////////////////////////////////////
template <typename T>
template <typename U>
constexpr Vector3<T>::operator Vector3<U>() const
{
return Vector3<U>(static_cast<U>(x), static_cast<U>(y), static_cast<U>(z));
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr T Vector3<T>::lengthSquared() const
{
return dot(*this);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr T Vector3<T>::dot(const Vector3<T>& rhs) const
{
return x * rhs.x + y * rhs.y + z * rhs.z;
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T> Vector3<T>::cross(const Vector3<T>& rhs) const
{
return Vector3<T>((y * rhs.z) - (z * rhs.y), (z * rhs.x) - (x * rhs.z), (x * rhs.y) - (y * rhs.x));
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T> Vector3<T>::componentWiseMul(const Vector3<T>& rhs) const
{
return Vector3<T>(x * rhs.x, y * rhs.y, z * rhs.z);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T> Vector3<T>::componentWiseDiv(const Vector3<T>& rhs) const
{
assert(rhs.x != 0 && "Vector3::componentWiseDiv() cannot divide by 0");
assert(rhs.y != 0 && "Vector3::componentWiseDiv() cannot divide by 0");
assert(rhs.z != 0 && "Vector3::componentWiseDiv() cannot divide by 0");
return Vector3<T>(x / rhs.x, y / rhs.y, z / rhs.z);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T> operator-(const Vector3<T>& left)
{
return Vector3<T>(-left.x, -left.y, -left.z);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T>& operator+=(Vector3<T>& left, const Vector3<T>& right)
{
left.x += right.x;
left.y += right.y;
left.z += right.z;
return left;
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T>& operator-=(Vector3<T>& left, const Vector3<T>& right)
{
left.x -= right.x;
left.y -= right.y;
left.z -= right.z;
return left;
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T> operator+(const Vector3<T>& left, const Vector3<T>& right)
{
return Vector3<T>(left.x + right.x, left.y + right.y, left.z + right.z);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T> operator-(const Vector3<T>& left, const Vector3<T>& right)
{
return Vector3<T>(left.x - right.x, left.y - right.y, left.z - right.z);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T> operator*(const Vector3<T>& left, T right)
{
return Vector3<T>(left.x * right, left.y * right, left.z * right);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T> operator*(T left, const Vector3<T>& right)
{
return Vector3<T>(right.x * left, right.y * left, right.z * left);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T>& operator*=(Vector3<T>& left, T right)
{
left.x *= right;
left.y *= right;
left.z *= right;
return left;
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T> operator/(const Vector3<T>& left, T right)
{
assert(right != 0 && "Vector3::operator/ cannot divide by 0");
return Vector3<T>(left.x / right, left.y / right, left.z / right);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr Vector3<T>& operator/=(Vector3<T>& left, T right)
{
assert(right != 0 && "Vector3::operator/= cannot divide by 0");
left.x /= right;
left.y /= right;
left.z /= right;
return left;
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr bool operator==(const Vector3<T>& left, const Vector3<T>& right)
{
return (left.x == right.x) && (left.y == right.y) && (left.z == right.z);
}
////////////////////////////////////////////////////////////
template <typename T>
constexpr bool operator!=(const Vector3<T>& left, const Vector3<T>& right)
{
return !(left == right);
}
} // namespace sf

9
third_party/sfml/license.md vendored Normal file
View File

@ -0,0 +1,9 @@
Copyright (C) 2007-2025 Laurent Gomila - laurent@sfml-dev.org
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@ -0,0 +1,59 @@
set(PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
set(INCROOT ${PROJECT_SOURCE_DIR}/include/SFML/Network)
set(SRCROOT ${PROJECT_SOURCE_DIR}/src/SFML/Network)
# all source files
set(SRC
${INCROOT}/../Network.hpp
${INCROOT}/Export.hpp
${INCROOT}/Ftp.hpp
${INCROOT}/Http.hpp
${INCROOT}/IpAddress.hpp
${INCROOT}/Packet.hpp
${INCROOT}/SocketHandle.hpp
${INCROOT}/Socket.hpp
${INCROOT}/SocketSelector.hpp
${INCROOT}/TcpListener.hpp
${INCROOT}/TcpSocket.hpp
${INCROOT}/UdpSocket.hpp
${SRCROOT}/Ftp.cpp
${SRCROOT}/Http.cpp
${SRCROOT}/IpAddress.cpp
${SRCROOT}/Packet.cpp
${SRCROOT}/Socket.cpp
${SRCROOT}/SocketImpl.hpp
${SRCROOT}/SocketSelector.cpp
${SRCROOT}/TcpListener.cpp
${SRCROOT}/TcpSocket.cpp
${SRCROOT}/UdpSocket.cpp
)
# add platform specific sources
if(WIN32)
set(SRC
${SRC}
${SRCROOT}/Win32/SocketImpl.cpp
)
else()
set(SRC
${SRC}
${SRCROOT}/Unix/SocketImpl.cpp
)
endif()
source_group("" FILES ${SRC})
# define the sfml-network target
add_library(sfml-network ${SRC})
target_include_directories(sfml-network PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_SOURCE_DIR}/../../../include)
target_compile_definitions(sfml-network PRIVATE SFML_STATIC)
target_compile_definitions(sfml-network INTERFACE SFML_STATIC)
# setup dependencies
target_link_libraries(sfml-network PUBLIC sfml-system)
if(WIN32)
target_link_libraries(sfml-network PRIVATE ws2_32)
endif()

View File

@ -0,0 +1,643 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Ftp.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
#include <array>
#include <fstream>
#include <ostream>
#include <sstream>
#include <utility>
#include <cctype>
#include <cstddef>
#include <cstdint>
namespace sf
{
////////////////////////////////////////////////////////////
class Ftp::DataChannel
{
public:
////////////////////////////////////////////////////////////
explicit DataChannel(Ftp& owner);
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
DataChannel(const DataChannel&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
DataChannel& operator=(const DataChannel&) = delete;
////////////////////////////////////////////////////////////
Ftp::Response open(Ftp::TransferMode mode);
////////////////////////////////////////////////////////////
void send(std::istream& stream);
////////////////////////////////////////////////////////////
void receive(std::ostream& stream);
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
Ftp& m_ftp; //!< Reference to the owner Ftp instance
TcpSocket m_dataSocket; //!< Socket used for data transfers
};
////////////////////////////////////////////////////////////
Ftp::Response::Response(Status code, std::string message) : m_status(code), m_message(std::move(message))
{
}
////////////////////////////////////////////////////////////
bool Ftp::Response::isOk() const
{
return static_cast<int>(m_status) < 400;
}
////////////////////////////////////////////////////////////
Ftp::Response::Status Ftp::Response::getStatus() const
{
return m_status;
}
////////////////////////////////////////////////////////////
const std::string& Ftp::Response::getMessage() const
{
return m_message;
}
////////////////////////////////////////////////////////////
Ftp::DirectoryResponse::DirectoryResponse(const Ftp::Response& response) : Ftp::Response(response)
{
if (isOk())
{
// Extract the directory from the server response
const std::string::size_type begin = getMessage().find('"', 0);
const std::string::size_type end = getMessage().find('"', begin + 1);
m_directory = getMessage().substr(begin + 1, end - begin - 1);
}
}
////////////////////////////////////////////////////////////
const std::filesystem::path& Ftp::DirectoryResponse::getDirectory() const
{
return m_directory;
}
////////////////////////////////////////////////////////////
Ftp::ListingResponse::ListingResponse(const Ftp::Response& response, const std::string& data) : Ftp::Response(response)
{
if (isOk())
{
// Fill the array of strings
std::string::size_type lastPos = 0;
for (std::string::size_type pos = data.find("\r\n"); pos != std::string::npos; pos = data.find("\r\n", lastPos))
{
m_listing.push_back(data.substr(lastPos, pos - lastPos));
lastPos = pos + 2;
}
}
}
////////////////////////////////////////////////////////////
const std::vector<std::string>& Ftp::ListingResponse::getListing() const
{
return m_listing;
}
////////////////////////////////////////////////////////////
Ftp::~Ftp()
{
(void)disconnect();
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::connect(IpAddress server, unsigned short port, Time timeout)
{
// Connect to the server
if (m_commandSocket.connect(server, port, timeout) != Socket::Status::Done)
return Response(Response::Status::ConnectionFailed);
// Get the response to the connection
return getResponse();
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::login()
{
return login("anonymous", "user@sfml-dev.org");
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::login(const std::string& name, const std::string& password)
{
Response response = sendCommand("USER", name);
if (response.isOk())
response = sendCommand("PASS", password);
return response;
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::disconnect()
{
// Send the exit command
Response response = sendCommand("QUIT");
if (response.isOk())
m_commandSocket.disconnect();
return response;
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::keepAlive()
{
return sendCommand("NOOP");
}
////////////////////////////////////////////////////////////
Ftp::DirectoryResponse Ftp::getWorkingDirectory()
{
return {sendCommand("PWD")};
}
////////////////////////////////////////////////////////////
Ftp::ListingResponse Ftp::getDirectoryListing(const std::string& directory)
{
// Open a data channel on default port (20) using ASCII transfer mode
std::ostringstream directoryData;
DataChannel data(*this);
Response response = data.open(TransferMode::Ascii);
if (response.isOk())
{
// Tell the server to send us the listing
response = sendCommand("NLST", directory);
if (response.isOk())
{
// Receive the listing
data.receive(directoryData);
// Get the response from the server
response = getResponse();
}
}
return {response, directoryData.str()};
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::changeDirectory(const std::string& directory)
{
return sendCommand("CWD", directory);
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::parentDirectory()
{
return sendCommand("CDUP");
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::createDirectory(const std::string& name)
{
return sendCommand("MKD", name);
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::deleteDirectory(const std::string& name)
{
return sendCommand("RMD", name);
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::renameFile(const std::filesystem::path& file, const std::filesystem::path& newName)
{
Response response = sendCommand("RNFR", file.string());
if (response.isOk())
response = sendCommand("RNTO", newName.string());
return response;
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::deleteFile(const std::filesystem::path& name)
{
return sendCommand("DELE", name.string());
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::download(const std::filesystem::path& remoteFile, const std::filesystem::path& localPath, TransferMode mode)
{
// Open a data channel using the given transfer mode
DataChannel data(*this);
Response response = data.open(mode);
if (response.isOk())
{
// Tell the server to start the transfer
response = sendCommand("RETR", remoteFile.string());
if (response.isOk())
{
// Create the file and truncate it if necessary
const std::filesystem::path filepath = localPath / remoteFile.filename();
std::ofstream file(filepath, std::ios_base::binary | std::ios_base::trunc);
if (!file)
return Response(Response::Status::InvalidFile);
// Receive the file data
data.receive(file);
// Close the file
file.close();
// Get the response from the server
response = getResponse();
// If the download was unsuccessful, delete the partial file
if (!response.isOk())
std::filesystem::remove(filepath);
}
}
return response;
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::upload(const std::filesystem::path& localFile,
const std::filesystem::path& remotePath,
TransferMode mode,
bool append)
{
// Get the contents of the file to send
std::ifstream file(localFile, std::ios_base::binary);
if (!file)
return Response(Response::Status::InvalidFile);
// Open a data channel using the given transfer mode
DataChannel data(*this);
Response response = data.open(mode);
if (response.isOk())
{
// Tell the server to start the transfer
response = sendCommand(append ? "APPE" : "STOR", (remotePath / localFile.filename()).string());
if (response.isOk())
{
// Send the file data
data.send(file);
// Get the response from the server
response = getResponse();
}
}
return response;
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::sendCommand(const std::string& command, const std::string& parameter)
{
// Build the command string
const std::string commandStr = parameter.empty() ? command + "\r\n" : command + " " + parameter + "\r\n";
// Send it to the server
if (m_commandSocket.send(commandStr.c_str(), commandStr.length()) != Socket::Status::Done)
return Response(Response::Status::ConnectionClosed);
// Get the response
return getResponse();
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::getResponse()
{
// We'll use a variable to keep track of the last valid code.
// It is useful in case of multi-lines responses, because the end of such a response
// will start by the same code
unsigned int lastCode = 0;
bool isInsideMultiline = false;
std::string message;
for (;;)
{
// Receive the response from the server
std::array<char, 1024> buffer{};
std::size_t length = 0;
if (m_receiveBuffer.empty())
{
if (m_commandSocket.receive(buffer.data(), buffer.size(), length) != Socket::Status::Done)
return Response(Response::Status::ConnectionClosed);
}
else
{
std::copy(m_receiveBuffer.begin(), m_receiveBuffer.end(), buffer.data());
length = m_receiveBuffer.size();
m_receiveBuffer.clear();
}
// There can be several lines inside the received buffer, extract them all
std::istringstream in(std::string(buffer.data(), length), std::ios_base::binary);
while (in)
{
// Try to extract the code
unsigned int code = 0;
if (in >> code)
{
// Extract the separator
char separator = 0;
in.get(separator);
// The '-' character means a multiline response
if ((separator == '-') && !isInsideMultiline)
{
// Set the multiline flag
isInsideMultiline = true;
// Keep track of the code
if (lastCode == 0)
lastCode = code;
// Extract the line
std::getline(in, message);
// Remove the ending '\r' (all lines are terminated by "\r\n")
message.erase(message.length() - 1);
message = separator + message + "\n";
}
else
{
// We must make sure that the code is the same, otherwise it means
// we haven't reached the end of the multiline response
if ((separator != '-') && ((code == lastCode) || (lastCode == 0)))
{
// Extract the line
std::string line;
std::getline(in, line);
// Remove the ending '\r' (all lines are terminated by "\r\n")
line.erase(line.length() - 1);
// Append it to the message
if (code == lastCode)
{
std::ostringstream out;
out << code << separator << line;
message += out.str();
}
else
{
message = separator + line;
}
// Save the remaining data for the next time getResponse() is called
m_receiveBuffer.assign(buffer.data() + static_cast<std::size_t>(in.tellg()),
length - static_cast<std::size_t>(in.tellg()));
// Return the response code and message
return Response(static_cast<Response::Status>(code), message);
}
// The line we just read was actually not a response,
// only a new part of the current multiline response
// Extract the line
std::string line;
std::getline(in, line);
if (!line.empty())
{
// Remove the ending '\r' (all lines are terminated by "\r\n")
line.erase(line.length() - 1);
// Append it to the current message
std::ostringstream out;
out << code << separator << line << '\n';
message += out.str();
}
}
}
else if (lastCode != 0)
{
// It seems we are in the middle of a multiline response
// Clear the error bits of the stream
in.clear();
// Extract the line
std::string line;
std::getline(in, line);
if (!line.empty())
{
// Remove the ending '\r' (all lines are terminated by "\r\n")
line.erase(line.length() - 1);
// Append it to the current message
message += line + "\n";
}
}
else
{
// Error: cannot extract the code, and we are not in a multiline response
return Response(Response::Status::InvalidResponse);
}
}
}
// We never reach there
}
////////////////////////////////////////////////////////////
Ftp::DataChannel::DataChannel(Ftp& owner) : m_ftp(owner)
{
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::DataChannel::open(Ftp::TransferMode mode)
{
// Open a data connection in active mode (we connect to the server)
Ftp::Response response = m_ftp.sendCommand("PASV");
if (response.isOk())
{
// Extract the connection address and port from the response
const std::string::size_type begin = response.getMessage().find_first_of("0123456789");
if (begin != std::string::npos)
{
std::array<std::uint8_t, 6> data{};
std::string str = response.getMessage().substr(begin);
std::size_t index = 0;
for (unsigned char& datum : data)
{
// Extract the current number
while (std::isdigit(str[index]))
{
datum = static_cast<std::uint8_t>(
static_cast<std::uint8_t>(datum * 10) + static_cast<std::uint8_t>(str[index] - '0'));
++index;
}
// Skip separator
++index;
}
// Reconstruct connection port and address
const auto port = static_cast<std::uint16_t>(data[4] * 256 + data[5]);
const IpAddress address(data[0], data[1], data[2], data[3]);
// Connect the data channel to the server
if (m_dataSocket.connect(address, port) == Socket::Status::Done)
{
// Translate the transfer mode to the corresponding FTP parameter
std::string modeStr;
switch (mode)
{
case Ftp::TransferMode::Binary:
modeStr = "I";
break;
case Ftp::TransferMode::Ascii:
modeStr = "A";
break;
case Ftp::TransferMode::Ebcdic:
modeStr = "E";
break;
}
// Set the transfer mode
response = m_ftp.sendCommand("TYPE", modeStr);
}
else
{
// Failed to connect to the server
response = Ftp::Response(Ftp::Response::Status::ConnectionFailed);
}
}
}
return response;
}
////////////////////////////////////////////////////////////
void Ftp::DataChannel::receive(std::ostream& stream)
{
// Receive data
std::array<char, 1024> buffer{};
std::size_t received = 0;
while (m_dataSocket.receive(buffer.data(), buffer.size(), received) == Socket::Status::Done)
{
stream.write(buffer.data(), static_cast<std::streamsize>(received));
if (!stream.good())
{
err() << "FTP Error: Writing to the file has failed" << std::endl;
break;
}
}
// Close the data socket
m_dataSocket.disconnect();
}
////////////////////////////////////////////////////////////
void Ftp::DataChannel::send(std::istream& stream)
{
// Send data
std::array<char, 1024> buffer{};
std::size_t count = 0;
for (;;)
{
// read some data from the stream
stream.read(buffer.data(), buffer.size());
if (!stream.good() && !stream.eof())
{
err() << "FTP Error: Reading from the file has failed" << std::endl;
break;
}
count = static_cast<std::size_t>(stream.gcount());
if (count > 0)
{
// we could read more data from the stream: send them
if (m_dataSocket.send(buffer.data(), count) != Socket::Status::Done)
break;
}
else
{
// no more data: exit the loop
break;
}
}
// Close the data socket
m_dataSocket.disconnect();
}
} // namespace sf

View File

@ -0,0 +1,399 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Http.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/Utils.hpp>
#include <algorithm>
#include <array>
#include <iterator>
#include <limits>
#include <ostream>
#include <sstream>
#include <utility>
#include <cctype>
#include <cstddef>
namespace sf
{
////////////////////////////////////////////////////////////
Http::Request::Request(const std::string& uri, Method method, const std::string& body) : m_method(method)
{
setUri(uri);
setBody(body);
}
////////////////////////////////////////////////////////////
void Http::Request::setField(const std::string& field, const std::string& value)
{
m_fields[toLower(field)] = value;
}
////////////////////////////////////////////////////////////
void Http::Request::setMethod(Http::Request::Method method)
{
m_method = method;
}
////////////////////////////////////////////////////////////
void Http::Request::setUri(const std::string& uri)
{
m_uri = uri;
// Make sure it starts with a '/'
if (m_uri.empty() || (m_uri[0] != '/'))
m_uri.insert(m_uri.begin(), '/');
}
////////////////////////////////////////////////////////////
void Http::Request::setHttpVersion(unsigned int major, unsigned int minor)
{
m_majorVersion = major;
m_minorVersion = minor;
}
////////////////////////////////////////////////////////////
void Http::Request::setBody(const std::string& body)
{
m_body = body;
}
////////////////////////////////////////////////////////////
std::string Http::Request::prepare() const
{
std::ostringstream out;
// Convert the method to its string representation
std::string method;
switch (m_method)
{
case Method::Get:
method = "GET";
break;
case Method::Post:
method = "POST";
break;
case Method::Head:
method = "HEAD";
break;
case Method::Put:
method = "PUT";
break;
case Method::Delete:
method = "DELETE";
break;
}
// Write the first line containing the request type
out << method << " " << m_uri << " ";
out << "HTTP/" << m_majorVersion << "." << m_minorVersion << "\r\n";
// Write fields
for (const auto& [fieldKey, fieldValue] : m_fields)
{
out << fieldKey << ": " << fieldValue << "\r\n";
}
// Use an extra \r\n to separate the header from the body
out << "\r\n";
// Add the body
out << m_body;
return out.str();
}
////////////////////////////////////////////////////////////
bool Http::Request::hasField(const std::string& field) const
{
return m_fields.find(toLower(field)) != m_fields.end();
}
////////////////////////////////////////////////////////////
const std::string& Http::Response::getField(const std::string& field) const
{
if (const auto it = m_fields.find(toLower(field)); it != m_fields.end())
{
return it->second;
}
static const std::string empty;
return empty;
}
////////////////////////////////////////////////////////////
Http::Response::Status Http::Response::getStatus() const
{
return m_status;
}
////////////////////////////////////////////////////////////
unsigned int Http::Response::getMajorHttpVersion() const
{
return m_majorVersion;
}
////////////////////////////////////////////////////////////
unsigned int Http::Response::getMinorHttpVersion() const
{
return m_minorVersion;
}
////////////////////////////////////////////////////////////
const std::string& Http::Response::getBody() const
{
return m_body;
}
////////////////////////////////////////////////////////////
void Http::Response::parse(const std::string& data)
{
std::istringstream in(data);
// Extract the HTTP version from the first line
std::string version;
if (in >> version)
{
if ((version.size() >= 8) && (version[6] == '.') && (toLower(version.substr(0, 5)) == "http/") &&
std::isdigit(version[5]) && std::isdigit(version[7]))
{
m_majorVersion = static_cast<unsigned int>(version[5] - '0');
m_minorVersion = static_cast<unsigned int>(version[7] - '0');
}
else
{
// Invalid HTTP version
m_status = Status::InvalidResponse;
return;
}
}
// Extract the status code from the first line
int status = 0;
if (in >> status)
{
m_status = static_cast<Status>(status);
}
else
{
// Invalid status code
m_status = Status::InvalidResponse;
return;
}
// Ignore the end of the first line
in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Parse the other lines, which contain fields, one by one
parseFields(in);
m_body.clear();
// Determine whether the transfer is chunked
if (toLower(getField("transfer-encoding")) != "chunked")
{
// Not chunked - just read everything at once
std::copy(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>(), std::back_inserter(m_body));
}
else
{
// Chunked - have to read chunk by chunk
std::size_t length = 0;
// Read all chunks, identified by a chunk-size not being 0
while (in >> std::hex >> length)
{
// Drop the rest of the line (chunk-extension)
in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Copy the actual content data
std::istreambuf_iterator<char> it(in);
const std::istreambuf_iterator<char> itEnd;
for (std::size_t i = 0; ((i < length) && (it != itEnd)); ++i)
{
m_body.push_back(*it);
++it; // Iterate in separate expression to work around false positive -Wnull-dereference warning in GCC 12.1.0
}
}
// Drop the rest of the line (chunk-extension)
in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Read all trailers (if present)
parseFields(in);
}
}
////////////////////////////////////////////////////////////
void Http::Response::parseFields(std::istream& in)
{
std::string line;
while (std::getline(in, line) && (line.size() > 2))
{
const std::string::size_type pos = line.find(": ");
if (pos != std::string::npos)
{
// Extract the field name and its value
const std::string field = line.substr(0, pos);
std::string value = line.substr(pos + 2);
// Remove any trailing \r
if (!value.empty() && (*value.rbegin() == '\r'))
value.erase(value.size() - 1);
// Add the field
m_fields[toLower(field)] = value;
}
}
}
////////////////////////////////////////////////////////////
Http::Http(const std::string& host, unsigned short port)
{
setHost(host, port);
}
////////////////////////////////////////////////////////////
void Http::setHost(const std::string& host, unsigned short port)
{
// Check the protocol
if (toLower(host.substr(0, 7)) == "http://")
{
// HTTP protocol
m_hostName = host.substr(7);
m_port = (port != 0 ? port : 80);
}
else if (toLower(host.substr(0, 8)) == "https://")
{
// HTTPS protocol -- unsupported (requires encryption and certificates and stuff...)
err() << "HTTPS protocol is not supported by sf::Http" << std::endl;
m_hostName.clear();
m_port = 0;
}
else
{
// Undefined protocol - use HTTP
m_hostName = host;
m_port = (port != 0 ? port : 80);
}
// Remove any trailing '/' from the host name
if (!m_hostName.empty() && (*m_hostName.rbegin() == '/'))
m_hostName.erase(m_hostName.size() - 1);
m_host = IpAddress::resolve(m_hostName);
}
////////////////////////////////////////////////////////////
Http::Response Http::sendRequest(const Http::Request& request, Time timeout)
{
// First make sure that the request is valid -- add missing mandatory fields
Request toSend(request);
if (!toSend.hasField("From"))
{
toSend.setField("From", "user@sfml-dev.org");
}
if (!toSend.hasField("User-Agent"))
{
toSend.setField("User-Agent", "libsfml-network/3.x");
}
if (!toSend.hasField("Host"))
{
toSend.setField("Host", m_hostName);
}
if (!toSend.hasField("Content-Length"))
{
std::ostringstream out;
out << toSend.m_body.size();
toSend.setField("Content-Length", out.str());
}
if ((toSend.m_method == Request::Method::Post) && !toSend.hasField("Content-Type"))
{
toSend.setField("Content-Type", "application/x-www-form-urlencoded");
}
if ((toSend.m_majorVersion * 10 + toSend.m_minorVersion >= 11) && !toSend.hasField("Connection"))
{
toSend.setField("Connection", "close");
}
// Prepare the response
Response received;
// Connect the socket to the host
if (m_connection.connect(m_host.value(), m_port, timeout) == Socket::Status::Done)
{
// Convert the request to string and send it through the connected socket
const std::string requestStr = toSend.prepare();
if (!requestStr.empty())
{
// Send it through the socket
if (m_connection.send(requestStr.c_str(), requestStr.size()) == Socket::Status::Done)
{
// Wait for the server's response
std::string receivedStr;
std::size_t size = 0;
std::array<char, 1024> buffer{};
while (m_connection.receive(buffer.data(), buffer.size(), size) == Socket::Status::Done)
{
receivedStr.append(buffer.data(), buffer.data() + size);
}
// Build the Response object from the received data
received.parse(receivedStr);
}
}
// Close the connection
m_connection.disconnect();
}
return received;
}
} // namespace sf

View File

@ -0,0 +1,253 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Http.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/System/Err.hpp>
#include <istream>
#include <ostream>
#include <cstring>
namespace sf
{
////////////////////////////////////////////////////////////
const IpAddress IpAddress::Any(0, 0, 0, 0);
const IpAddress IpAddress::LocalHost(127, 0, 0, 1);
const IpAddress IpAddress::Broadcast(255, 255, 255, 255);
////////////////////////////////////////////////////////////
std::optional<IpAddress> IpAddress::resolve(std::string_view address)
{
using namespace std::string_view_literals;
if (address.empty())
{
// Not generating en error message here as resolution failure is a valid outcome.
return std::nullopt;
}
if (address == "255.255.255.255"sv)
{
// The broadcast address needs to be handled explicitly,
// because it is also the value returned by inet_addr on error
return Broadcast;
}
if (address == "0.0.0.0"sv)
return Any;
// Try to convert the address as a byte representation ("xxx.xxx.xxx.xxx")
if (const std::uint32_t ip = inet_addr(address.data()); ip != INADDR_NONE)
return IpAddress(ntohl(ip));
// Not a valid address, try to convert it as a host name
addrinfo hints{}; // Zero-initialize
hints.ai_family = AF_INET;
addrinfo* result = nullptr;
if (getaddrinfo(address.data(), nullptr, &hints, &result) == 0 && result != nullptr)
{
sockaddr_in sin{};
std::memcpy(&sin, result->ai_addr, sizeof(*result->ai_addr));
const std::uint32_t ip = sin.sin_addr.s_addr;
freeaddrinfo(result);
return IpAddress(ntohl(ip));
}
// Not generating en error message here as resolution failure is a valid outcome.
return std::nullopt;
}
////////////////////////////////////////////////////////////
IpAddress::IpAddress(std::uint8_t byte0, std::uint8_t byte1, std::uint8_t byte2, std::uint8_t byte3) :
m_address(static_cast<std::uint32_t>((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3))
{
}
////////////////////////////////////////////////////////////
IpAddress::IpAddress(std::uint32_t address) : m_address(address)
{
}
////////////////////////////////////////////////////////////
std::string IpAddress::toString() const
{
in_addr address{};
address.s_addr = htonl(m_address);
return inet_ntoa(address);
}
////////////////////////////////////////////////////////////
std::uint32_t IpAddress::toInteger() const
{
return m_address;
}
////////////////////////////////////////////////////////////
std::optional<IpAddress> IpAddress::getLocalAddress()
{
// The method here is to connect a UDP socket to a public ip,
// and get the local socket address with the getsockname function.
// UDP connection will not send anything to the network, so this function won't cause any overhead.
// Create the socket
const SocketHandle sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == priv::SocketImpl::invalidSocket())
{
err() << "Failed to retrieve local address (invalid socket)" << std::endl;
return std::nullopt;
}
// Connect the socket to a public ip (here 1.1.1.1) on any
// port. This will give the local address of the network interface
// used for default routing which is usually what we want.
sockaddr_in address = priv::SocketImpl::createAddress(0x01010101, 9);
if (connect(sock, reinterpret_cast<sockaddr*>(&address), sizeof(address)) == -1)
{
priv::SocketImpl::close(sock);
err() << "Failed to retrieve local address (socket connection failure)" << std::endl;
return std::nullopt;
}
// Get the local address of the socket connection
priv::SocketImpl::AddrLength size = sizeof(address);
if (getsockname(sock, reinterpret_cast<sockaddr*>(&address), &size) == -1)
{
priv::SocketImpl::close(sock);
err() << "Failed to retrieve local address (socket local address retrieval failure)" << std::endl;
return std::nullopt;
}
// Close the socket
priv::SocketImpl::close(sock);
// Finally build the IP address
return IpAddress(ntohl(address.sin_addr.s_addr));
}
////////////////////////////////////////////////////////////
std::optional<IpAddress> IpAddress::getPublicAddress(Time timeout)
{
// The trick here is more complicated, because the only way
// to get our public IP address is to get it from a distant computer.
// Here we get the web page from http://www.sfml-dev.org/ip-provider.php
// and parse the result to extract our IP address
// (not very hard: the web page contains only our IP address).
Http server("www.sfml-dev.org");
const Http::Request request("/ip-provider.php", Http::Request::Method::Get);
const Http::Response page = server.sendRequest(request, timeout);
const Http::Response::Status status = page.getStatus();
if (status == Http::Response::Status::Ok)
return IpAddress::resolve(page.getBody());
err() << "Failed to retrieve public address from external IP resolution server (HTTP response status "
<< static_cast<int>(status) << ")" << std::endl;
return std::nullopt;
}
////////////////////////////////////////////////////////////
bool operator==(IpAddress left, IpAddress right)
{
return !(left < right) && !(right < left);
}
////////////////////////////////////////////////////////////
bool operator!=(IpAddress left, IpAddress right)
{
return !(left == right);
}
////////////////////////////////////////////////////////////
bool operator<(IpAddress left, IpAddress right)
{
return left.m_address < right.m_address;
}
////////////////////////////////////////////////////////////
bool operator>(IpAddress left, IpAddress right)
{
return right < left;
}
////////////////////////////////////////////////////////////
bool operator<=(IpAddress left, IpAddress right)
{
return !(right < left);
}
////////////////////////////////////////////////////////////
bool operator>=(IpAddress left, IpAddress right)
{
return !(left < right);
}
////////////////////////////////////////////////////////////
std::istream& operator>>(std::istream& stream, std::optional<IpAddress>& address)
{
std::string str;
stream >> str;
address = IpAddress::resolve(str);
return stream;
}
////////////////////////////////////////////////////////////
std::ostream& operator<<(std::ostream& stream, IpAddress address)
{
return stream << address.toString();
}
} // namespace sf

View File

@ -0,0 +1,596 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Packet.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/System/String.hpp>
#include <SFML/System/Utils.hpp>
#include <array>
#include <cassert>
#include <cstring>
#include <cwchar>
namespace sf
{
////////////////////////////////////////////////////////////
void Packet::append(const void* data, std::size_t sizeInBytes)
{
if (data && (sizeInBytes > 0))
{
const auto* begin = reinterpret_cast<const std::byte*>(data);
const auto* end = begin + sizeInBytes;
m_data.insert(m_data.end(), begin, end);
}
}
////////////////////////////////////////////////////////////
std::size_t Packet::getReadPosition() const
{
return m_readPos;
}
////////////////////////////////////////////////////////////
void Packet::clear()
{
m_data.clear();
m_readPos = 0;
m_isValid = true;
}
////////////////////////////////////////////////////////////
const void* Packet::getData() const
{
return !m_data.empty() ? m_data.data() : nullptr;
}
////////////////////////////////////////////////////////////
std::size_t Packet::getDataSize() const
{
return m_data.size();
}
////////////////////////////////////////////////////////////
bool Packet::endOfPacket() const
{
return m_readPos >= m_data.size();
}
////////////////////////////////////////////////////////////
Packet::operator bool() const
{
return m_isValid;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(bool& data)
{
std::uint8_t value = 0;
if (*this >> value)
data = (value != 0);
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::int8_t& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::uint8_t& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::int16_t& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
data = static_cast<std::int16_t>(ntohs(static_cast<std::uint16_t>(data)));
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::uint16_t& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
data = ntohs(data);
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::int32_t& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
data = static_cast<std::int32_t>(ntohl(static_cast<std::uint32_t>(data)));
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::uint32_t& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
data = ntohl(data);
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::int64_t& data)
{
if (checkSize(sizeof(data)))
{
// Since ntohll is not available everywhere, we have to convert
// to network byte order (big endian) manually
std::array<std::byte, sizeof(data)> bytes{};
std::memcpy(bytes.data(), &m_data[m_readPos], bytes.size());
data = toInteger<std::int64_t>(bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]);
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::uint64_t& data)
{
if (checkSize(sizeof(data)))
{
// Since ntohll is not available everywhere, we have to convert
// to network byte order (big endian) manually
std::array<std::byte, sizeof(data)> bytes{};
std::memcpy(bytes.data(), &m_data[m_readPos], sizeof(data));
data = toInteger<std::uint64_t>(bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]);
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(float& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(double& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(char* data)
{
assert(data && "Packet::operator>> Data must not be null");
// First extract string length
std::uint32_t length = 0;
*this >> length;
if ((length > 0) && checkSize(length))
{
// Then extract characters
std::memcpy(data, &m_data[m_readPos], length);
data[length] = '\0';
// Update reading position
m_readPos += length;
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::string& data)
{
// First extract string length
std::uint32_t length = 0;
*this >> length;
data.clear();
if ((length > 0) && checkSize(length))
{
// Then extract characters
data.assign(reinterpret_cast<char*>(&m_data[m_readPos]), length);
// Update reading position
m_readPos += length;
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(wchar_t* data)
{
assert(data && "Packet::operator>> Data must not be null");
// First extract string length
std::uint32_t length = 0;
*this >> length;
if ((length > 0) && checkSize(length * sizeof(std::uint32_t)))
{
// Then extract characters
for (std::uint32_t i = 0; i < length; ++i)
{
std::uint32_t character = 0;
*this >> character;
data[i] = static_cast<wchar_t>(character);
}
data[length] = L'\0';
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::wstring& data)
{
// First extract string length
std::uint32_t length = 0;
*this >> length;
data.clear();
if ((length > 0) && checkSize(length * sizeof(std::uint32_t)))
{
// Then extract characters
for (std::uint32_t i = 0; i < length; ++i)
{
std::uint32_t character = 0;
*this >> character;
data += static_cast<wchar_t>(character);
}
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(String& data)
{
// First extract the string length
std::uint32_t length = 0;
*this >> length;
data.clear();
if ((length > 0) && checkSize(length * sizeof(std::uint32_t)))
{
// Then extract characters
for (std::uint32_t i = 0; i < length; ++i)
{
std::uint32_t character = 0;
*this >> character;
data += static_cast<char32_t>(character);
}
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(bool data)
{
*this << static_cast<std::uint8_t>(data);
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::int8_t data)
{
append(&data, sizeof(data));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::uint8_t data)
{
append(&data, sizeof(data));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::int16_t data)
{
const auto toWrite = static_cast<std::int16_t>(htons(static_cast<std::uint16_t>(data)));
append(&toWrite, sizeof(toWrite));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::uint16_t data)
{
const std::uint16_t toWrite = htons(data);
append(&toWrite, sizeof(toWrite));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::int32_t data)
{
const auto toWrite = static_cast<std::int32_t>(htonl(static_cast<std::uint32_t>(data)));
append(&toWrite, sizeof(toWrite));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::uint32_t data)
{
const std::uint32_t toWrite = htonl(data);
append(&toWrite, sizeof(toWrite));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::int64_t data)
{
// Since htonll is not available everywhere, we have to convert
// to network byte order (big endian) manually
const std::array toWrite = {static_cast<std::uint8_t>((data >> 56) & 0xFF),
static_cast<std::uint8_t>((data >> 48) & 0xFF),
static_cast<std::uint8_t>((data >> 40) & 0xFF),
static_cast<std::uint8_t>((data >> 32) & 0xFF),
static_cast<std::uint8_t>((data >> 24) & 0xFF),
static_cast<std::uint8_t>((data >> 16) & 0xFF),
static_cast<std::uint8_t>((data >> 8) & 0xFF),
static_cast<std::uint8_t>((data) & 0xFF)};
append(toWrite.data(), toWrite.size());
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::uint64_t data)
{
// Since htonll is not available everywhere, we have to convert
// to network byte order (big endian) manually
const std::array toWrite = {static_cast<std::uint8_t>((data >> 56) & 0xFF),
static_cast<std::uint8_t>((data >> 48) & 0xFF),
static_cast<std::uint8_t>((data >> 40) & 0xFF),
static_cast<std::uint8_t>((data >> 32) & 0xFF),
static_cast<std::uint8_t>((data >> 24) & 0xFF),
static_cast<std::uint8_t>((data >> 16) & 0xFF),
static_cast<std::uint8_t>((data >> 8) & 0xFF),
static_cast<std::uint8_t>((data) & 0xFF)};
append(toWrite.data(), toWrite.size());
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(float data)
{
append(&data, sizeof(data));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(double data)
{
append(&data, sizeof(data));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(const char* data)
{
assert(data && "Packet::operator<< Data must not be null");
// First insert string length
const auto length = static_cast<std::uint32_t>(std::strlen(data));
*this << length;
// Then insert characters
append(data, length * sizeof(char));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(const std::string& data)
{
// First insert string length
const auto length = static_cast<std::uint32_t>(data.size());
*this << length;
// Then insert characters
if (length > 0)
append(data.c_str(), length * sizeof(std::string::value_type));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(const wchar_t* data)
{
assert(data && "Packet::operator<< Data must not be null");
// First insert string length
const auto length = static_cast<std::uint32_t>(std::wcslen(data));
*this << length;
// Then insert characters
for (const wchar_t* c = data; *c != L'\0'; ++c)
*this << static_cast<std::uint32_t>(*c);
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(const std::wstring& data)
{
// First insert string length
const auto length = static_cast<std::uint32_t>(data.size());
*this << length;
// Then insert characters
if (length > 0)
{
for (const wchar_t c : data)
*this << static_cast<std::uint32_t>(c);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(const String& data)
{
// First insert the string length
const auto length = static_cast<std::uint32_t>(data.getSize());
*this << length;
// Then insert characters
if (length > 0)
{
for (const unsigned int datum : data)
*this << datum;
}
return *this;
}
////////////////////////////////////////////////////////////
bool Packet::checkSize(std::size_t size)
{
// Determine if size is big enough to trigger an overflow
const bool overflowDetected = m_readPos + size < m_readPos;
m_isValid = m_isValid && (m_readPos + size <= m_data.size()) && !overflowDetected;
return m_isValid;
}
////////////////////////////////////////////////////////////
const void* Packet::onSend(std::size_t& size)
{
size = getDataSize();
return getData();
}
////////////////////////////////////////////////////////////
void Packet::onReceive(const void* data, std::size_t size)
{
append(data, size);
}
} // namespace sf

View File

@ -0,0 +1,175 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Socket.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/System/Err.hpp>
#include <ostream>
#include <utility>
namespace sf
{
////////////////////////////////////////////////////////////
Socket::Socket(Type type) : m_type(type), m_socket(priv::SocketImpl::invalidSocket())
{
}
////////////////////////////////////////////////////////////
Socket::~Socket()
{
// Close the socket before it gets destructed
close();
}
////////////////////////////////////////////////////////////
Socket::Socket(Socket&& socket) noexcept :
m_type(socket.m_type),
m_socket(std::exchange(socket.m_socket, priv::SocketImpl::invalidSocket())),
m_isBlocking(socket.m_isBlocking)
{
}
////////////////////////////////////////////////////////////
Socket& Socket::operator=(Socket&& socket) noexcept
{
if (&socket == this)
return *this;
close();
m_type = socket.m_type;
m_socket = std::exchange(socket.m_socket, priv::SocketImpl::invalidSocket());
m_isBlocking = socket.m_isBlocking;
return *this;
}
////////////////////////////////////////////////////////////
void Socket::setBlocking(bool blocking)
{
// Apply if the socket is already created
if (m_socket != priv::SocketImpl::invalidSocket())
priv::SocketImpl::setBlocking(m_socket, blocking);
m_isBlocking = blocking;
}
////////////////////////////////////////////////////////////
bool Socket::isBlocking() const
{
return m_isBlocking;
}
////////////////////////////////////////////////////////////
SocketHandle Socket::getNativeHandle() const
{
return m_socket;
}
////////////////////////////////////////////////////////////
void Socket::create()
{
// Don't create the socket if it already exists
if (m_socket == priv::SocketImpl::invalidSocket())
{
const SocketHandle handle = socket(PF_INET, m_type == Type::Tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
if (handle == priv::SocketImpl::invalidSocket())
{
err() << "Failed to create socket" << std::endl;
return;
}
create(handle);
}
}
////////////////////////////////////////////////////////////
void Socket::create(SocketHandle handle)
{
// Don't create the socket if it already exists
if (m_socket == priv::SocketImpl::invalidSocket())
{
// Assign the new handle
m_socket = handle;
// Set the current blocking state
setBlocking(m_isBlocking);
if (m_type == Type::Tcp)
{
// Disable the Nagle algorithm (i.e. removes buffering of TCP packets)
int yes = 1;
if (setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&yes), sizeof(yes)) == -1)
{
err() << "Failed to set socket option \"TCP_NODELAY\" ; "
<< "all your TCP packets will be buffered" << std::endl;
}
// On macOS, disable the SIGPIPE signal on disconnection
#ifdef SFML_SYSTEM_MACOS
if (setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char*>(&yes), sizeof(yes)) == -1)
{
err() << "Failed to set socket option \"SO_NOSIGPIPE\"" << std::endl;
}
#endif
}
else
{
// Enable broadcast by default for UDP sockets
int yes = 1;
if (setsockopt(m_socket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char*>(&yes), sizeof(yes)) == -1)
{
err() << "Failed to enable broadcast on UDP socket" << std::endl;
}
}
}
}
////////////////////////////////////////////////////////////
void Socket::close()
{
// Close the socket
if (m_socket != priv::SocketImpl::invalidSocket())
{
priv::SocketImpl::close(m_socket);
m_socket = priv::SocketImpl::invalidSocket();
}
}
} // namespace sf

View File

@ -0,0 +1,123 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Socket.hpp>
#include <SFML/Network/SocketHandle.hpp>
#if defined(SFML_SYSTEM_WINDOWS)
#include <SFML/System/Win32/WindowsHeader.hpp>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstddef>
#endif
#include <cstdint>
namespace sf::priv
{
////////////////////////////////////////////////////////////
/// \brief Helper class implementing all the non-portable
/// socket stuff
///
////////////////////////////////////////////////////////////
class SocketImpl
{
public:
////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////
#if defined(SFML_SYSTEM_WINDOWS)
using AddrLength = int;
using Size = int;
#else
using AddrLength = socklen_t;
using Size = std::size_t;
#endif
////////////////////////////////////////////////////////////
/// \brief Create an internal sockaddr_in address
///
/// \param address Target address
/// \param port Target port
///
/// \return sockaddr_in ready to be used by socket functions
///
////////////////////////////////////////////////////////////
static sockaddr_in createAddress(std::uint32_t address, unsigned short port);
////////////////////////////////////////////////////////////
/// \brief Return the value of the invalid socket
///
/// \return Special value of the invalid socket
///
////////////////////////////////////////////////////////////
static SocketHandle invalidSocket();
////////////////////////////////////////////////////////////
/// \brief Close and destroy a socket
///
/// \param sock Handle of the socket to close
///
////////////////////////////////////////////////////////////
static void close(SocketHandle sock);
////////////////////////////////////////////////////////////
/// \brief Set a socket as blocking or non-blocking
///
/// \param sock Handle of the socket
/// \param block New blocking state of the socket
///
////////////////////////////////////////////////////////////
static void setBlocking(SocketHandle sock, bool block);
////////////////////////////////////////////////////////////
/// Get the last socket error status
///
/// \return Status corresponding to the last socket error
///
////////////////////////////////////////////////////////////
static Socket::Status getErrorStatus();
};
} // namespace sf::priv

View File

@ -0,0 +1,209 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Socket.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/Network/SocketSelector.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
#include <memory>
#include <ostream>
#include <utility>
#ifdef _MSC_VER
#pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro
#endif
namespace sf
{
////////////////////////////////////////////////////////////
struct SocketSelector::SocketSelectorImpl
{
fd_set allSockets{}; //!< Set containing all the sockets handles
fd_set socketsReady{}; //!< Set containing handles of the sockets that are ready
int maxSocket{}; //!< Maximum socket handle
int socketCount{}; //!< Number of socket handles
};
////////////////////////////////////////////////////////////
SocketSelector::SocketSelector() : m_impl(std::make_unique<SocketSelectorImpl>())
{
clear();
}
////////////////////////////////////////////////////////////
SocketSelector::~SocketSelector() = default;
////////////////////////////////////////////////////////////
SocketSelector::SocketSelector(const SocketSelector& copy) : m_impl(std::make_unique<SocketSelectorImpl>(*copy.m_impl))
{
}
////////////////////////////////////////////////////////////
SocketSelector& SocketSelector::operator=(const SocketSelector& right)
{
SocketSelector temp(right);
std::swap(m_impl, temp.m_impl);
return *this;
}
////////////////////////////////////////////////////////////
SocketSelector::SocketSelector(SocketSelector&&) noexcept = default;
////////////////////////////////////////////////////////////]
SocketSelector& SocketSelector::operator=(SocketSelector&&) noexcept = default;
////////////////////////////////////////////////////////////
void SocketSelector::add(Socket& socket)
{
const SocketHandle handle = socket.getNativeHandle();
if (handle != priv::SocketImpl::invalidSocket())
{
#if defined(SFML_SYSTEM_WINDOWS)
if (m_impl->socketCount >= FD_SETSIZE)
{
err() << "The socket can't be added to the selector because the "
<< "selector is full. This is a limitation of your operating "
<< "system's FD_SETSIZE setting.";
return;
}
if (FD_ISSET(handle, &m_impl->allSockets))
return;
++m_impl->socketCount;
#else
if (handle >= FD_SETSIZE)
{
err() << "The socket can't be added to the selector because its "
<< "ID is too high. This is a limitation of your operating "
<< "system's FD_SETSIZE setting.";
return;
}
// SocketHandle is an int in POSIX
m_impl->maxSocket = std::max(m_impl->maxSocket, handle);
#endif
FD_SET(handle, &m_impl->allSockets);
}
}
////////////////////////////////////////////////////////////
void SocketSelector::remove(Socket& socket)
{
const SocketHandle handle = socket.getNativeHandle();
if (handle != priv::SocketImpl::invalidSocket())
{
#if defined(SFML_SYSTEM_WINDOWS)
if (!FD_ISSET(handle, &m_impl->allSockets))
return;
--m_impl->socketCount;
#else
if (handle >= FD_SETSIZE)
return;
#endif
FD_CLR(handle, &m_impl->allSockets);
FD_CLR(handle, &m_impl->socketsReady);
}
}
////////////////////////////////////////////////////////////
void SocketSelector::clear()
{
FD_ZERO(&m_impl->allSockets);
FD_ZERO(&m_impl->socketsReady);
m_impl->maxSocket = 0;
m_impl->socketCount = 0;
}
////////////////////////////////////////////////////////////
bool SocketSelector::wait(Time timeout)
{
// Setup the timeout
timeval time{};
time.tv_sec = static_cast<long>(timeout.asMicroseconds() / 1'000'000);
time.tv_usec = static_cast<int>(timeout.asMicroseconds() % 1'000'000);
// Initialize the set that will contain the sockets that are ready
m_impl->socketsReady = m_impl->allSockets;
// Wait until one of the sockets is ready for reading, or timeout is reached
// The first parameter is ignored on Windows
const int count = select(m_impl->maxSocket + 1, &m_impl->socketsReady, nullptr, nullptr, timeout != Time::Zero ? &time : nullptr);
return count > 0;
}
////////////////////////////////////////////////////////////
bool SocketSelector::isReady(Socket& socket) const
{
const SocketHandle handle = socket.getNativeHandle();
if (handle != priv::SocketImpl::invalidSocket())
{
#if !defined(SFML_SYSTEM_WINDOWS)
if (handle >= FD_SETSIZE)
return false;
#endif
return FD_ISSET(handle, &m_impl->socketsReady) != 0;
}
return false;
}
} // namespace sf

View File

@ -0,0 +1,132 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/Network/TcpListener.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/System/Err.hpp>
#include <ostream>
namespace sf
{
////////////////////////////////////////////////////////////
TcpListener::TcpListener() : Socket(Type::Tcp)
{
}
////////////////////////////////////////////////////////////
unsigned short TcpListener::getLocalPort() const
{
if (getNativeHandle() != priv::SocketImpl::invalidSocket())
{
// Retrieve information about the local end of the socket
sockaddr_in address{};
priv::SocketImpl::AddrLength size = sizeof(address);
if (getsockname(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return ntohs(address.sin_port);
}
}
// We failed to retrieve the port
return 0;
}
////////////////////////////////////////////////////////////
Socket::Status TcpListener::listen(unsigned short port, IpAddress address)
{
// Close the socket if it is already bound
close();
// Create the internal socket if it doesn't exist
create();
// Check if the address is valid
if (address == IpAddress::Broadcast)
return Status::Error;
// Bind the socket to the specified port
sockaddr_in addr = priv::SocketImpl::createAddress(address.toInteger(), port);
if (bind(getNativeHandle(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1)
{
// Not likely to happen, but...
err() << "Failed to bind listener socket to port " << port << std::endl;
return Status::Error;
}
// Listen to the bound port
if (::listen(getNativeHandle(), SOMAXCONN) == -1)
{
// Oops, socket is deaf
err() << "Failed to listen to port " << port << std::endl;
return Status::Error;
}
return Status::Done;
}
////////////////////////////////////////////////////////////
void TcpListener::close()
{
// Simply close the socket
Socket::close();
}
////////////////////////////////////////////////////////////
Socket::Status TcpListener::accept(TcpSocket& socket)
{
// Make sure that we're listening
if (getNativeHandle() == priv::SocketImpl::invalidSocket())
{
err() << "Failed to accept a new connection, the socket is not listening" << std::endl;
return Status::Error;
}
// Accept a new connection
sockaddr_in address{};
priv::SocketImpl::AddrLength length = sizeof(address);
const SocketHandle remote = ::accept(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), &length);
// Check for errors
if (remote == priv::SocketImpl::invalidSocket())
return priv::SocketImpl::getErrorStatus();
// Initialize the new connected socket
socket.close();
socket.create(remote);
return Status::Done;
}
} // namespace sf

View File

@ -0,0 +1,429 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Packet.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
#include <array>
#include <ostream>
#include <cstring>
#ifdef _MSC_VER
#pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro
#endif
namespace
{
// Low-level send/receive flags (OS-dependent)
#ifdef SFML_SYSTEM_LINUX
constexpr int flags = MSG_NOSIGNAL;
#else
constexpr int flags = 0;
#endif
} // namespace
namespace sf
{
////////////////////////////////////////////////////////////
TcpSocket::TcpSocket() : Socket(Type::Tcp)
{
}
////////////////////////////////////////////////////////////
unsigned short TcpSocket::getLocalPort() const
{
if (getNativeHandle() != priv::SocketImpl::invalidSocket())
{
// Retrieve information about the local end of the socket
sockaddr_in address{};
priv::SocketImpl::AddrLength size = sizeof(address);
if (getsockname(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return ntohs(address.sin_port);
}
}
// We failed to retrieve the port
return 0;
}
////////////////////////////////////////////////////////////
std::optional<IpAddress> TcpSocket::getRemoteAddress() const
{
if (getNativeHandle() != priv::SocketImpl::invalidSocket())
{
// Retrieve information about the remote end of the socket
sockaddr_in address{};
priv::SocketImpl::AddrLength size = sizeof(address);
if (getpeername(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return IpAddress(ntohl(address.sin_addr.s_addr));
}
}
// We failed to retrieve the address
return std::nullopt;
}
////////////////////////////////////////////////////////////
unsigned short TcpSocket::getRemotePort() const
{
if (getNativeHandle() != priv::SocketImpl::invalidSocket())
{
// Retrieve information about the remote end of the socket
sockaddr_in address{};
priv::SocketImpl::AddrLength size = sizeof(address);
if (getpeername(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return ntohs(address.sin_port);
}
}
// We failed to retrieve the port
return 0;
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::connect(IpAddress remoteAddress, unsigned short remotePort, Time timeout)
{
// Disconnect the socket if it is already connected
disconnect();
// Create the internal socket if it doesn't exist
create();
// Create the remote address
sockaddr_in address = priv::SocketImpl::createAddress(remoteAddress.toInteger(), remotePort);
if (timeout <= Time::Zero)
{
// ----- We're not using a timeout: just try to connect -----
// Connect the socket
if (::connect(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) == -1)
return priv::SocketImpl::getErrorStatus();
// Connection succeeded
return Status::Done;
}
// ----- We're using a timeout: we'll need a few tricks to make it work -----
// Save the previous blocking state
const bool blocking = isBlocking();
// Switch to non-blocking to enable our connection timeout
if (blocking)
setBlocking(false);
// Try to connect to the remote address
if (::connect(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) >= 0)
{
// We got instantly connected! (it may no happen a lot...)
setBlocking(blocking);
return Status::Done;
}
// Get the error status
Status status = priv::SocketImpl::getErrorStatus();
// If we were in non-blocking mode, return immediately
if (!blocking)
return status;
// Otherwise, wait until something happens to our socket (success, timeout or error)
if (status == Socket::Status::NotReady)
{
// Setup the selector
fd_set selector;
FD_ZERO(&selector);
FD_SET(getNativeHandle(), &selector);
// Setup the timeout
timeval time{};
time.tv_sec = static_cast<long>(timeout.asMicroseconds() / 1'000'000);
time.tv_usec = static_cast<int>(timeout.asMicroseconds() % 1'000'000);
// Wait for something to write on our socket (which means that the connection request has returned)
if (select(static_cast<int>(getNativeHandle() + 1), nullptr, &selector, nullptr, &time) > 0)
{
// At this point the connection may have been either accepted or refused.
// To know whether it's a success or a failure, we must check the address of the connected peer
if (getRemoteAddress().has_value())
{
// Connection accepted
status = Status::Done;
}
else
{
// Connection refused
status = priv::SocketImpl::getErrorStatus();
}
}
else
{
// Failed to connect before timeout is over
status = priv::SocketImpl::getErrorStatus();
}
}
// Switch back to blocking mode
setBlocking(true);
return status;
}
////////////////////////////////////////////////////////////
void TcpSocket::disconnect()
{
// Close the socket
close();
// Reset the pending packet data
m_pendingPacket = PendingPacket();
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::send(const void* data, std::size_t size)
{
if (!isBlocking())
err() << "Warning: Partial sends might not be handled properly." << std::endl;
std::size_t sent = 0;
return send(data, size, sent);
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::send(const void* data, std::size_t size, std::size_t& sent)
{
// Check the parameters
if (!data || (size == 0))
{
err() << "Cannot send data over the network (no data to send)" << std::endl;
return Status::Error;
}
// Loop until every byte has been sent
int result = 0;
for (sent = 0; sent < size; sent += static_cast<std::size_t>(result))
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuseless-cast"
// Send a chunk of data
result = static_cast<int>(::send(getNativeHandle(),
static_cast<const char*>(data) + sent,
static_cast<priv::SocketImpl::Size>(size - sent),
flags));
#pragma GCC diagnostic pop
// Check for errors
if (result < 0)
{
const Status status = priv::SocketImpl::getErrorStatus();
if ((status == Status::NotReady) && sent)
return Status::Partial;
return status;
}
}
return Status::Done;
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::receive(void* data, std::size_t size, std::size_t& received)
{
// First clear the variables to fill
received = 0;
// Check the destination buffer
if (!data)
{
err() << "Cannot receive data from the network (the destination buffer is invalid)" << std::endl;
return Status::Error;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuseless-cast"
// Receive a chunk of bytes
const int sizeReceived = static_cast<int>(
recv(getNativeHandle(), static_cast<char*>(data), static_cast<priv::SocketImpl::Size>(size), flags));
#pragma GCC diagnostic pop
// Check the number of bytes received
if (sizeReceived > 0)
{
received = static_cast<std::size_t>(sizeReceived);
return Status::Done;
}
if (sizeReceived == 0)
{
return Socket::Status::Disconnected;
}
return priv::SocketImpl::getErrorStatus();
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::send(Packet& packet)
{
// TCP is a stream protocol, it doesn't preserve messages boundaries.
// This means that we have to send the packet size first, so that the
// receiver knows the actual end of the packet in the data stream.
// We allocate an extra memory block so that the size can be sent
// together with the data in a single call. This may seem inefficient,
// but it is actually required to avoid partial send, which could cause
// data corruption on the receiving end.
// Get the data to send from the packet
std::size_t size = 0;
const void* data = packet.onSend(size);
// First convert the packet size to network byte order
std::uint32_t packetSize = htonl(static_cast<std::uint32_t>(size));
// Allocate memory for the data block to send
m_blockToSendBuffer.resize(sizeof(packetSize) + size);
// Copy the packet size and data into the block to send
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnull-dereference" // False positive.
std::memcpy(m_blockToSendBuffer.data(), &packetSize, sizeof(packetSize));
#pragma GCC diagnostic pop
if (size > 0)
std::memcpy(m_blockToSendBuffer.data() + sizeof(packetSize), data, size);
// These warnings are ignored here for portability, as even on Windows the
// signature of `send` might change depending on whether Win32 or MinGW is
// being used.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuseless-cast"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
// Send the data block
std::size_t sent = 0;
const Status status = send(m_blockToSendBuffer.data() + packet.m_sendPos,
static_cast<priv::SocketImpl::Size>(m_blockToSendBuffer.size() - packet.m_sendPos),
sent);
#pragma GCC diagnostic pop
#pragma GCC diagnostic pop
// In the case of a partial send, record the location to resume from
if (status == Status::Partial)
{
packet.m_sendPos += sent;
}
else if (status == Status::Done)
{
packet.m_sendPos = 0;
}
return status;
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::receive(Packet& packet)
{
// First clear the variables to fill
packet.clear();
// We start by getting the size of the incoming packet
std::uint32_t packetSize = 0;
std::size_t received = 0;
if (m_pendingPacket.sizeReceived < sizeof(m_pendingPacket.size))
{
// Loop until we've received the entire size of the packet
// (even a 4 byte variable may be received in more than one call)
while (m_pendingPacket.sizeReceived < sizeof(m_pendingPacket.size))
{
char* data = reinterpret_cast<char*>(&m_pendingPacket.size) + m_pendingPacket.sizeReceived;
const Status status = receive(data, sizeof(m_pendingPacket.size) - m_pendingPacket.sizeReceived, received);
m_pendingPacket.sizeReceived += received;
if (status != Status::Done)
return status;
}
// The packet size has been fully received
packetSize = ntohl(m_pendingPacket.size);
}
else
{
// The packet size has already been received in a previous call
packetSize = ntohl(m_pendingPacket.size);
}
// Loop until we receive all the packet data
std::array<char, 1024> buffer{};
while (m_pendingPacket.data.size() < packetSize)
{
// Receive a chunk of data
const std::size_t sizeToGet = std::min(packetSize - m_pendingPacket.data.size(), buffer.size());
const Status status = receive(buffer.data(), sizeToGet, received);
if (status != Status::Done)
return status;
// Append it into the packet
if (received > 0)
{
m_pendingPacket.data.resize(m_pendingPacket.data.size() + received);
std::byte* begin = m_pendingPacket.data.data() + m_pendingPacket.data.size() - received;
std::memcpy(begin, buffer.data(), received);
}
}
// We have received all the packet data: we can copy it to the user packet
if (!m_pendingPacket.data.empty())
packet.onReceive(m_pendingPacket.data.data(), m_pendingPacket.data.size());
// Clear the pending packet data
m_pendingPacket = PendingPacket();
return Status::Done;
}
} // namespace sf

View File

@ -0,0 +1,223 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Packet.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/Network/UdpSocket.hpp>
#include <SFML/System/Err.hpp>
#include <ostream>
#include <cstddef>
namespace sf
{
////////////////////////////////////////////////////////////
UdpSocket::UdpSocket() : Socket(Type::Udp)
{
}
////////////////////////////////////////////////////////////
unsigned short UdpSocket::getLocalPort() const
{
if (getNativeHandle() != priv::SocketImpl::invalidSocket())
{
// Retrieve information about the local end of the socket
sockaddr_in address{};
priv::SocketImpl::AddrLength size = sizeof(address);
if (getsockname(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return ntohs(address.sin_port);
}
}
// We failed to retrieve the port
return 0;
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::bind(unsigned short port, IpAddress address)
{
// Close the socket if it is already bound
close();
// Create the internal socket if it doesn't exist
create();
// Check if the address is valid
if (address == IpAddress::Broadcast)
return Status::Error;
// Bind the socket
sockaddr_in addr = priv::SocketImpl::createAddress(address.toInteger(), port);
if (::bind(getNativeHandle(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1)
{
err() << "Failed to bind socket to port " << port << std::endl;
return Status::Error;
}
return Status::Done;
}
////////////////////////////////////////////////////////////
void UdpSocket::unbind()
{
// Simply close the socket
close();
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::send(const void* data, std::size_t size, IpAddress remoteAddress, unsigned short remotePort)
{
// Create the internal socket if it doesn't exist
create();
// Make sure that all the data will fit in one datagram
if (size > MaxDatagramSize)
{
err() << "Cannot send data over the network "
<< "(the number of bytes to send is greater than sf::UdpSocket::MaxDatagramSize)" << std::endl;
return Status::Error;
}
// Build the target address
sockaddr_in address = priv::SocketImpl::createAddress(remoteAddress.toInteger(), remotePort);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuseless-cast"
// Send the data (unlike TCP, all the data is always sent in one call)
const int sent = static_cast<int>(
sendto(getNativeHandle(),
static_cast<const char*>(data),
static_cast<priv::SocketImpl::Size>(size),
0,
reinterpret_cast<sockaddr*>(&address),
sizeof(address)));
#pragma GCC diagnostic pop
// Check for errors
if (sent < 0)
return priv::SocketImpl::getErrorStatus();
return Status::Done;
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::receive(void* data,
std::size_t size,
std::size_t& received,
std::optional<IpAddress>& remoteAddress,
unsigned short& remotePort)
{
// First clear the variables to fill
received = 0;
remoteAddress = std::nullopt;
remotePort = 0;
// Check the destination buffer
if (!data)
{
err() << "Cannot receive data from the network (the destination buffer is invalid)" << std::endl;
return Status::Error;
}
// Data that will be filled with the other computer's address
sockaddr_in address = priv::SocketImpl::createAddress(INADDR_ANY, 0);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuseless-cast"
// Receive a chunk of bytes
priv::SocketImpl::AddrLength addressSize = sizeof(address);
const int sizeReceived = static_cast<int>(
recvfrom(getNativeHandle(),
static_cast<char*>(data),
static_cast<priv::SocketImpl::Size>(size),
0,
reinterpret_cast<sockaddr*>(&address),
&addressSize));
#pragma GCC diagnostic pop
// Check for errors
if (sizeReceived < 0)
return priv::SocketImpl::getErrorStatus();
// Fill the sender information
received = static_cast<std::size_t>(sizeReceived);
remoteAddress = IpAddress(ntohl(address.sin_addr.s_addr));
remotePort = ntohs(address.sin_port);
return Status::Done;
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::send(Packet& packet, IpAddress remoteAddress, unsigned short remotePort)
{
// UDP is a datagram-oriented protocol (as opposed to TCP which is a stream protocol).
// Sending one datagram is almost safe: it may be lost but if it's received, then its data
// is guaranteed to be ok. However, splitting a packet into multiple datagrams would be highly
// unreliable, since datagrams may be reordered, dropped or mixed between different sources.
// That's why SFML imposes a limit on packet size so that they can be sent in a single datagram.
// This also removes the overhead associated to packets -- there's no size to send in addition
// to the packet's data.
// Get the data to send from the packet
std::size_t size = 0;
const void* data = packet.onSend(size);
// Send it
return send(data, size, remoteAddress, remotePort);
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::receive(Packet& packet, std::optional<IpAddress>& remoteAddress, unsigned short& remotePort)
{
// See the detailed comment in send(Packet) above.
// Receive the datagram
std::size_t received = 0;
const Status status = receive(m_buffer.data(), m_buffer.size(), received, remoteAddress, remotePort);
// If we received valid data, we can copy it to the user packet
packet.clear();
if ((status == Status::Done) && (received > 0))
packet.onReceive(m_buffer.data(), received);
return status;
}
} // namespace sf

View File

@ -0,0 +1,111 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/System/Err.hpp>
#include <fcntl.h>
#include <ostream>
#include <cerrno>
namespace sf::priv
{
////////////////////////////////////////////////////////////
sockaddr_in SocketImpl::createAddress(std::uint32_t address, unsigned short port)
{
auto addr = sockaddr_in();
addr.sin_addr.s_addr = htonl(address);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
#if defined(SFML_SYSTEM_MACOS)
addr.sin_len = sizeof(addr);
#endif
return addr;
}
////////////////////////////////////////////////////////////
SocketHandle SocketImpl::invalidSocket()
{
return -1;
}
////////////////////////////////////////////////////////////
void SocketImpl::close(SocketHandle sock)
{
::close(sock);
}
////////////////////////////////////////////////////////////
void SocketImpl::setBlocking(SocketHandle sock, bool block)
{
const int status = fcntl(sock, F_GETFL);
if (block)
{
if (fcntl(sock, F_SETFL, status & ~O_NONBLOCK) == -1)
err() << "Failed to set file status flags: " << errno << std::endl;
}
else
{
if (fcntl(sock, F_SETFL, status | O_NONBLOCK) == -1)
err() << "Failed to set file status flags: " << errno << std::endl;
}
}
////////////////////////////////////////////////////////////
Socket::Status SocketImpl::getErrorStatus()
{
// The following are sometimes equal to EWOULDBLOCK,
// so we have to make a special case for them in order
// to avoid having double values in the switch case
if ((errno == EAGAIN) || (errno == EINPROGRESS))
return Socket::Status::NotReady;
// clang-format off
switch (errno)
{
case EWOULDBLOCK: return Socket::Status::NotReady;
case ECONNABORTED: return Socket::Status::Disconnected;
case ECONNRESET: return Socket::Status::Disconnected;
case ETIMEDOUT: return Socket::Status::Disconnected;
case ENETRESET: return Socket::Status::Disconnected;
case ENOTCONN: return Socket::Status::Disconnected;
case EPIPE: return Socket::Status::Disconnected;
default: return Socket::Status::Error;
}
// clang-format on
}
} // namespace sf::priv

View File

@ -0,0 +1,110 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/SocketImpl.hpp>
#include <cstdint>
namespace
{
////////////////////////////////////////////////////////////
// Windows needs some initialization and cleanup to get
// sockets working properly... so let's create a class that will
// do it automatically
////////////////////////////////////////////////////////////
struct SocketInitializer
{
SocketInitializer()
{
WSADATA init;
WSAStartup(MAKEWORD(2, 2), &init);
}
~SocketInitializer()
{
WSACleanup();
}
} globalInitializer;
} // namespace
namespace sf::priv
{
////////////////////////////////////////////////////////////
sockaddr_in SocketImpl::createAddress(std::uint32_t address, unsigned short port)
{
auto addr = sockaddr_in();
addr.sin_addr.s_addr = htonl(address);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
return addr;
}
////////////////////////////////////////////////////////////
SocketHandle SocketImpl::invalidSocket()
{
return INVALID_SOCKET;
}
////////////////////////////////////////////////////////////
void SocketImpl::close(SocketHandle sock)
{
closesocket(sock);
}
////////////////////////////////////////////////////////////
void SocketImpl::setBlocking(SocketHandle sock, bool block)
{
u_long blocking = block ? 0 : 1;
ioctlsocket(sock, static_cast<long>(FIONBIO), &blocking);
}
////////////////////////////////////////////////////////////
Socket::Status SocketImpl::getErrorStatus()
{
// clang-format off
switch (WSAGetLastError())
{
case WSAEWOULDBLOCK: return Socket::Status::NotReady;
case WSAEALREADY: return Socket::Status::NotReady;
case WSAECONNABORTED: return Socket::Status::Disconnected;
case WSAECONNRESET: return Socket::Status::Disconnected;
case WSAETIMEDOUT: return Socket::Status::Disconnected;
case WSAENETRESET: return Socket::Status::Disconnected;
case WSAENOTCONN: return Socket::Status::Disconnected;
case WSAEISCONN: return Socket::Status::Done; // when connecting a non-blocking socket
default: return Socket::Status::Error;
}
// clang-format on
}
} // namespace sf::priv

View File

@ -0,0 +1,74 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
// Copyright (C) 2013 Jonathan De Wachter (dewachter.jonathan@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Android/Activity.hpp>
#include <android/log.h>
#include <cassert>
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_INFO, "sfml-error", __VA_ARGS__))
std::streambuf::int_type LogcatStream::overflow(std::streambuf::int_type c)
{
if (c == '\n')
{
m_message.push_back(static_cast<char>(c));
LOGE("%s", m_message.c_str());
m_message.clear();
}
m_message.push_back(static_cast<char>(c));
return traits_type::not_eof(c);
}
namespace sf::priv
{
ActivityStates*& getActivityStatesPtr()
{
static ActivityStates* states = nullptr;
return states;
}
void resetActivity(ActivityStates* initializedStates)
{
getActivityStatesPtr() = initializedStates;
}
ActivityStates& getActivity()
{
ActivityStates* const states = getActivityStatesPtr();
assert(states != nullptr &&
"Cannot dereference null activity states pointer. Call priv::resetActivity() to initialize it.");
return *states;
}
} // namespace sf::priv

View File

@ -0,0 +1,103 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2013 Jonathan De Wachter (dewachter.jonathan@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/EglContext.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/System/EnumArray.hpp>
#include <android/configuration.h>
#include <android/native_activity.h>
#include <fstream>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
#include <cstddef>
class SFML_SYSTEM_API LogcatStream : public std::streambuf
{
public:
LogcatStream() = default;
std::streambuf::int_type overflow(std::streambuf::int_type c) override;
private:
std::string m_message;
};
namespace sf::priv
{
struct ActivityStates
{
ANativeActivity* activity{};
ANativeWindow* window{};
ALooper* looper{};
AInputQueue* inputQueue{};
AConfiguration* config{};
EGLDisplay display{};
EglContext* context{};
std::vector<std::byte> savedState;
std::recursive_mutex mutex;
void (*forwardEvent)(const Event& event){};
int (*processEvent)(int fd, int events, void* data){};
std::unordered_map<int, Vector2i> touchEvents;
Vector2i mousePosition;
EnumArray<Mouse::Button, bool, Mouse::ButtonCount> isButtonPressed{};
bool mainOver{};
Vector2i screenSize;
Vector2i fullScreenSize;
bool initialized{};
bool terminated{};
bool fullscreen{};
bool updated{};
LogcatStream logcat;
};
SFML_SYSTEM_API ActivityStates*& getActivityStatesPtr();
SFML_SYSTEM_API void resetActivity(ActivityStates* initializedStates);
SFML_SYSTEM_API ActivityStates& getActivity();
} // namespace sf::priv

View File

@ -0,0 +1,39 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Android/Activity.hpp>
#include <SFML/System/NativeActivity.hpp>
namespace sf
{
////////////////////////////////////////////////////////////
ANativeActivity* getNativeActivity()
{
return priv::getActivity().activity;
}
} // namespace sf

View File

@ -0,0 +1,91 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2013 Jonathan De Wachter (dewachter.jonathan@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Android/Activity.hpp>
#include <SFML/System/Android/ResourceStream.hpp>
#include <mutex>
namespace sf::priv
{
////////////////////////////////////////////////////////////
bool ResourceStream::open(const std::filesystem::path& filename)
{
ActivityStates& states = getActivity();
const std::lock_guard lock(states.mutex);
m_file.reset(AAssetManager_open(states.activity->assetManager, filename.c_str(), AASSET_MODE_UNKNOWN));
return m_file != nullptr;
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> ResourceStream::read(void* data, std::size_t size)
{
assert(m_file && "ResourceStream::read() cannot be called when file is not initialized");
const auto numBytesRead = AAsset_read(m_file.get(), data, size);
if (numBytesRead < 0)
return std::nullopt;
return numBytesRead;
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> ResourceStream::seek(std::size_t position)
{
assert(m_file && "ResourceStream::seek() cannot be called when file is not initialized");
const auto newPosition = AAsset_seek(m_file.get(), static_cast<off_t>(position), SEEK_SET);
if (newPosition < 0)
return std::nullopt;
return newPosition;
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> ResourceStream::tell()
{
assert(m_file && "ResourceStream::tell() cannot be called when file is not initialized");
return getSize().value() - static_cast<std::size_t>(AAsset_getRemainingLength(m_file.get()));
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> ResourceStream::getSize()
{
assert(m_file && "ResourceStream::getSize() cannot be called when file is not initialized");
return AAsset_getLength(m_file.get());
}
////////////////////////////////////////////////////////////
void ResourceStream::AAssetDeleter::operator()(AAsset* file)
{
AAsset_close(file);
}
} // namespace sf::priv

View File

@ -0,0 +1,119 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2013 Jonathan De Wachter (dewachter.jonathan@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Export.hpp>
#include <SFML/System/InputStream.hpp>
#include <android/asset_manager.h>
#include <filesystem>
#include <string>
namespace sf::priv
{
////////////////////////////////////////////////////////////
/// \brief Read from Android asset files
///
////////////////////////////////////////////////////////////
class SFML_SYSTEM_API ResourceStream : public InputStream
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// \param filename Filename of the asset
///
////////////////////////////////////////////////////////////
ResourceStream() = default;
////////////////////////////////////////////////////////////
/// \brief Open the stream from a file path
///
/// \param filename Name of the file to open
///
/// \return `true` on success, `false` on error
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool open(const std::filesystem::path& filename);
////////////////////////////////////////////////////////////
/// \brief Read data from the asset
///
/// \param data Buffer where the asset data is copied
/// \param size Number of bytes read
///
/// \return The number of bytes actually read, or `std::nullopt` on error
///
////////////////////////////////////////////////////////////
std::optional<std::size_t> read(void* data, std::size_t size) override;
////////////////////////////////////////////////////////////
/// \brief Change the current reading position in the asset file
///
/// \param position The position to seek to, from the beginning
///
/// \return The position actually sought to, or `std::nullopt` on error
///
////////////////////////////////////////////////////////////
std::optional<std::size_t> seek(std::size_t position) override;
////////////////////////////////////////////////////////////
/// \brief Get the current reading position in the asset file
///
/// \return The current position, or `std::nullopt` on error.
///
////////////////////////////////////////////////////////////
std::optional<std::size_t> tell() override;
////////////////////////////////////////////////////////////
/// \brief Return the size of the asset file
///
/// \return The total number of bytes available in the asset, or `std::nullopt` on error
///
////////////////////////////////////////////////////////////
std::optional<std::size_t> getSize() override;
private:
////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////
struct AAssetDeleter
{
void operator()(AAsset*);
};
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::unique_ptr<AAsset, AAssetDeleter> m_file; ///< The asset file to read
};
} // namespace sf::priv

View File

@ -0,0 +1,46 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/SuspendAwareClock.hpp>
#include <ctime>
namespace sf
{
SuspendAwareClock::time_point SuspendAwareClock::now() noexcept
{
::timespec ts{};
#ifdef CLOCK_BOOTTIME
clock_gettime(CLOCK_BOOTTIME, &ts);
#else
#error "CLOCK_BOOTTIME is essential for SuspendAwareClock to work"
#endif // CLOCK_BOOTTIME
return time_point(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec));
}
} // namespace sf

View File

@ -0,0 +1,96 @@
set(PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
set(INCROOT ${PROJECT_SOURCE_DIR}/include/SFML/System)
set(SRCROOT ${PROJECT_SOURCE_DIR}/src/SFML/System)
# all source files
set(SRC
${INCROOT}/../System.hpp
${INCROOT}/Angle.hpp
${INCROOT}/Angle.inl
${INCROOT}/Clock.hpp
${INCROOT}/Err.hpp
${INCROOT}/Exception.hpp
${INCROOT}/Export.hpp
${INCROOT}/FileInputStream.hpp
${INCROOT}/InputStream.hpp
${INCROOT}/MemoryInputStream.hpp
${INCROOT}/NativeActivity.hpp
${INCROOT}/Sleep.hpp
${INCROOT}/String.hpp
${INCROOT}/String.inl
${INCROOT}/SuspendAwareClock.hpp
${INCROOT}/Time.hpp
${INCROOT}/Time.inl
${INCROOT}/Utf.hpp
${INCROOT}/Utf.inl
${INCROOT}/Vector2.hpp
${INCROOT}/Vector2.inl
${INCROOT}/Vector3.hpp
${INCROOT}/Vector3.inl
${SRCROOT}/Clock.cpp
${SRCROOT}/EnumArray.hpp
${SRCROOT}/Err.cpp
${SRCROOT}/FileInputStream.cpp
${SRCROOT}/MemoryInputStream.cpp
${SRCROOT}/Sleep.cpp
${SRCROOT}/String.cpp
${SRCROOT}/Utils.cpp
${SRCROOT}/Utils.hpp
${SRCROOT}/Vector2.cpp
${SRCROOT}/Vector3.cpp
)
source_group("" FILES ${SRC})
# add platform specific sources
if(WIN32)
set(PLATFORM_SRC
${SRCROOT}/Win32/SleepImpl.cpp
${SRCROOT}/Win32/SleepImpl.hpp
${SRCROOT}/Win32/WindowsHeader.hpp
)
source_group("windows" FILES ${PLATFORM_SRC})
else()
set(PLATFORM_SRC
${SRCROOT}/Unix/SleepImpl.cpp
${SRCROOT}/Unix/SleepImpl.hpp
)
if(ANDROID)
set(PLATFORM_SRC ${PLATFORM_SRC}
${SRCROOT}/Android/Activity.cpp
${SRCROOT}/Android/Activity.hpp
${SRCROOT}/Android/NativeActivity.cpp
${SRCROOT}/Android/ResourceStream.cpp
${SRCROOT}/Android/ResourceStream.hpp
${SRCROOT}/Android/SuspendAwareClock.cpp
)
endif()
source_group("unix" FILES ${PLATFORM_SRC})
endif()
# define the sfml-system target
add_library(sfml-system STATIC ${SRC} ${PLATFORM_SRC})
target_include_directories(sfml-system PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_SOURCE_DIR}/../../../include)
target_compile_definitions(sfml-system PRIVATE SFML_STATIC)
target_compile_definitions(sfml-system INTERFACE SFML_STATIC)
if(ANDROID)
# glad sources
target_include_directories(sfml-system PRIVATE "${PROJECT_SOURCE_DIR}/extlibs/headers/glad/include")
endif()
# setup dependencies
if(UNIX OR APPLE)
target_link_libraries(sfml-system PRIVATE pthread)
endif()
if(UNIX AND NOT APPLE)
target_link_libraries(sfml-system PRIVATE rt)
elseif(WIN32)
target_link_libraries(sfml-system PRIVATE winmm)
elseif(ANDROID)
target_link_libraries(sfml-system PRIVATE android log)
endif()

View File

@ -0,0 +1,88 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Clock.hpp>
#include <SFML/System/Time.hpp>
namespace sf
{
////////////////////////////////////////////////////////////
Time Clock::getElapsedTime() const
{
if (isRunning())
return std::chrono::duration_cast<std::chrono::microseconds>(priv::ClockImpl::now() - m_refPoint);
return std::chrono::duration_cast<std::chrono::microseconds>(m_stopPoint - m_refPoint);
}
////////////////////////////////////////////////////////////
bool Clock::isRunning() const
{
return m_stopPoint == priv::ClockImpl::time_point();
}
////////////////////////////////////////////////////////////
void Clock::start()
{
if (!isRunning())
{
m_refPoint += priv::ClockImpl::now() - m_stopPoint;
m_stopPoint = {};
}
}
////////////////////////////////////////////////////////////
void Clock::stop()
{
if (isRunning())
m_stopPoint = priv::ClockImpl::now();
}
////////////////////////////////////////////////////////////
Time Clock::restart()
{
const Time elapsed = getElapsedTime();
m_refPoint = priv::ClockImpl::now();
m_stopPoint = {};
return elapsed;
}
////////////////////////////////////////////////////////////
Time Clock::reset()
{
const Time elapsed = getElapsedTime();
m_refPoint = priv::ClockImpl::now();
m_stopPoint = m_refPoint;
return elapsed;
}
} // namespace sf

View File

@ -0,0 +1,13 @@
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
include(CMakeFindDependencyMacro)
# start with an empty list
set(FIND_SFML_DEPENDENCIES_NOTFOUND)
find_dependency(Threads)
if(FIND_SFML_DEPENDENCIES_NOTFOUND)
set(FIND_SFML_ERROR "SFML found but some of its dependencies are missing (${FIND_SFML_DEPENDENCIES_NOTFOUND})")
set(SFML_FOUND OFF)
endif()

View File

@ -0,0 +1,75 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <array>
#include <type_traits>
#include <cassert>
#include <cstddef>
namespace sf::priv
{
////////////////////////////////////////////////////////////
/// \brief Fixed-size array container indexed by an enumeration
///
////////////////////////////////////////////////////////////
template <typename Enum, typename Value, std::size_t Count>
struct EnumArray : public std::array<Value, Count>
{
static_assert(std::is_enum_v<Enum>, "Enum type parameter must be an enumeration");
////////////////////////////////////////////////////////////
/// \brief Returns a reference to the element associated to specified \a key
///
/// No bounds checking is performed in release builds.
///
////////////////////////////////////////////////////////////
constexpr Value& operator[](Enum key)
{
const auto index = static_cast<std::size_t>(key);
assert(index < Count && "Index is out of bounds");
return std::array<Value, Count>::operator[](index);
}
////////////////////////////////////////////////////////////
/// \brief Returns a reference to the element associated to specified \a key
///
/// No bounds checking is performed in release builds.
///
////////////////////////////////////////////////////////////
constexpr const Value& operator[](Enum key) const
{
const auto index = static_cast<std::size_t>(key);
assert(index < Count && "Index is out of bounds");
return std::array<Value, Count>::operator[](index);
}
};
} // namespace sf::priv

109
third_party/sfml/src/SFML/System/Err.cpp vendored Normal file
View File

@ -0,0 +1,109 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Err.hpp>
#include <iostream>
#include <streambuf>
#include <cstdio>
namespace
{
// This class will be used as the default streambuf of sf::Err,
// it outputs to stderr by default (to keep the default behavior)
class DefaultErrStreamBuf : public std::streambuf
{
public:
DefaultErrStreamBuf()
{
// Allocate the write buffer
constexpr int size = 64;
char* buffer = new char[size];
setp(buffer, buffer + size);
}
~DefaultErrStreamBuf() override
{
// Synchronize
sync();
// Delete the write buffer
delete[] pbase();
}
private:
int overflow(int character) override
{
if ((character != EOF) && (pptr() != epptr()))
{
// Valid character
return sputc(static_cast<char>(character));
}
if (character != EOF)
{
// Not enough space in the buffer: synchronize output and try again
sync();
return overflow(character);
}
// Invalid character: synchronize output
return sync();
}
int sync() override
{
// Check if there is something into the write buffer
if (pbase() != pptr())
{
// Print the contents of the write buffer into the standard error output
const auto size = static_cast<std::size_t>(pptr() - pbase());
std::fwrite(pbase(), 1, size, stderr);
// Reset the pointer position to the beginning of the write buffer
setp(pbase(), epptr());
}
return 0;
}
};
} // namespace
namespace sf
{
////////////////////////////////////////////////////////////
std::ostream& err()
{
static DefaultErrStreamBuf buffer;
static std::ostream stream(&buffer);
return stream;
}
} // namespace sf

View File

@ -0,0 +1,167 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Exception.hpp>
#include <SFML/System/FileInputStream.hpp>
#include <SFML/System/Utils.hpp>
#ifdef SFML_SYSTEM_ANDROID
#include <SFML/System/Android/Activity.hpp>
#include <SFML/System/Android/ResourceStream.hpp>
#endif
#include <memory>
#include <cstddef>
namespace sf
{
////////////////////////////////////////////////////////////
void FileInputStream::FileCloser::operator()(std::FILE* file)
{
std::fclose(file);
}
////////////////////////////////////////////////////////////
FileInputStream::FileInputStream() = default;
////////////////////////////////////////////////////////////
FileInputStream::FileInputStream(const std::filesystem::path& filename)
{
if (!open(filename))
throw Exception("Failed to open file input stream");
}
////////////////////////////////////////////////////////////
FileInputStream::~FileInputStream() = default;
////////////////////////////////////////////////////////////
FileInputStream::FileInputStream(FileInputStream&&) noexcept = default;
////////////////////////////////////////////////////////////
FileInputStream& FileInputStream::operator=(FileInputStream&&) noexcept = default;
////////////////////////////////////////////////////////////
bool FileInputStream::open(const std::filesystem::path& filename)
{
#ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr)
{
m_androidFile = std::make_unique<priv::ResourceStream>();
if (!m_androidFile->open(filename))
return false;
return m_androidFile->tell().has_value();
}
#endif
m_file.reset(openFile(filename, "rb"));
return m_file != nullptr;
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> FileInputStream::read(void* data, std::size_t size)
{
#ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr)
{
if (!m_androidFile)
return std::nullopt;
return m_androidFile->read(data, size);
}
#endif
if (!m_file)
return std::nullopt;
return std::fread(data, 1, size, m_file.get());
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> FileInputStream::seek(std::size_t position)
{
#ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr)
{
if (!m_androidFile)
return std::nullopt;
return m_androidFile->seek(position);
}
#endif
if (!m_file)
return std::nullopt;
if (std::fseek(m_file.get(), static_cast<long>(position), SEEK_SET))
return std::nullopt;
return tell();
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> FileInputStream::tell()
{
#ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr)
{
if (!m_androidFile)
return std::nullopt;
return m_androidFile->tell();
}
#endif
if (!m_file)
return std::nullopt;
const auto position = std::ftell(m_file.get());
return position < 0 ? std::nullopt : std::optional<std::size_t>(position);
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> FileInputStream::getSize()
{
#ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr)
{
if (!m_androidFile)
return std::nullopt;
return m_androidFile->getSize();
}
#endif
if (!m_file)
return std::nullopt;
const auto position = tell().value();
std::fseek(m_file.get(), 0, SEEK_END);
const std::optional size = tell();
if (!seek(position).has_value())
return std::nullopt;
return size;
}
} // namespace sf

View File

@ -0,0 +1,92 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/MemoryInputStream.hpp>
#include <algorithm>
#include <cstring>
namespace sf
{
////////////////////////////////////////////////////////////
MemoryInputStream::MemoryInputStream(const void* data, std::size_t sizeInBytes) :
m_data(static_cast<const std::byte*>(data)),
m_size(sizeInBytes)
{
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> MemoryInputStream::read(void* data, std::size_t size)
{
if (!m_data)
return std::nullopt;
const std::size_t count = std::min(size, m_size - m_offset);
if (count > 0)
{
std::memcpy(data, m_data + m_offset, static_cast<std::size_t>(count));
m_offset += count;
}
return count;
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> MemoryInputStream::seek(std::size_t position)
{
if (!m_data)
return std::nullopt;
m_offset = position < m_size ? position : m_size;
return m_offset;
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> MemoryInputStream::tell()
{
if (!m_data)
return std::nullopt;
return m_offset;
}
////////////////////////////////////////////////////////////
std::optional<std::size_t> MemoryInputStream::getSize()
{
if (!m_data)
return std::nullopt;
return m_size;
}
} // namespace sf

View File

@ -0,0 +1,50 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Sleep.hpp>
#include <SFML/System/Time.hpp>
#if defined(SFML_SYSTEM_WINDOWS)
#include <SFML/System/Win32/SleepImpl.hpp>
#else
#include <SFML/System/Unix/SleepImpl.hpp>
#endif
namespace sf
{
////////////////////////////////////////////////////////////
void sleep(Time duration)
{
// Note that 'std::this_thread::sleep_for' is intentionally not used here
// as it results in inconsistent sleeping times under MinGW-w64.
if (duration >= Time::Zero)
priv::sleepImpl(duration);
}
} // namespace sf

View File

@ -0,0 +1,477 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/String.hpp>
#include <SFML/System/Utf.hpp>
#include <iterator>
#include <utility>
#include <cassert>
#include <cstring>
#include <cwchar>
namespace sf
{
////////////////////////////////////////////////////////////
void U8StringCharTraits::assign(char_type& c1, char_type c2) noexcept
{
c1 = c2;
}
////////////////////////////////////////////////////////////
U8StringCharTraits::char_type* U8StringCharTraits::assign(char_type* s, std::size_t n, char_type c)
{
return reinterpret_cast<char_type*>(std::char_traits<char>::assign(reinterpret_cast<char*>(s), n, static_cast<char>(c)));
}
////////////////////////////////////////////////////////////
bool U8StringCharTraits::eq(char_type c1, char_type c2) noexcept
{
return c1 == c2;
}
////////////////////////////////////////////////////////////
bool U8StringCharTraits::lt(char_type c1, char_type c2) noexcept
{
return c1 < c2;
}
////////////////////////////////////////////////////////////
U8StringCharTraits::char_type* U8StringCharTraits::move(char_type* s1, const char_type* s2, std::size_t n)
{
std::memmove(s1, s2, n);
return s1;
}
////////////////////////////////////////////////////////////
U8StringCharTraits::char_type* U8StringCharTraits::copy(char_type* s1, const char_type* s2, std::size_t n)
{
std::memcpy(s1, s2, n);
return s1;
}
////////////////////////////////////////////////////////////
int U8StringCharTraits::compare(const char_type* s1, const char_type* s2, std::size_t n)
{
return std::memcmp(s1, s2, n);
}
////////////////////////////////////////////////////////////
std::size_t U8StringCharTraits::length(const char_type* s)
{
return std::strlen(reinterpret_cast<const char*>(s));
}
////////////////////////////////////////////////////////////
const U8StringCharTraits::char_type* U8StringCharTraits::find(const char_type* s, std::size_t n, const char_type& c)
{
return reinterpret_cast<const char_type*>(
std::char_traits<char>::find(reinterpret_cast<const char*>(s), n, static_cast<char>(c)));
}
////////////////////////////////////////////////////////////
U8StringCharTraits::char_type U8StringCharTraits::to_char_type(int_type i) noexcept
{
return static_cast<U8StringCharTraits::char_type>(std::char_traits<char>::to_char_type(i));
}
////////////////////////////////////////////////////////////
U8StringCharTraits::int_type U8StringCharTraits::to_int_type(char_type c) noexcept
{
return std::char_traits<char>::to_int_type(static_cast<char>(c));
}
////////////////////////////////////////////////////////////
bool U8StringCharTraits::eq_int_type(int_type i1, int_type i2) noexcept
{
return i1 == i2;
}
////////////////////////////////////////////////////////////
U8StringCharTraits::int_type U8StringCharTraits::eof() noexcept
{
return std::char_traits<char>::eof();
}
////////////////////////////////////////////////////////////
U8StringCharTraits::int_type U8StringCharTraits::not_eof(int_type i) noexcept
{
return std::char_traits<char>::not_eof(i);
}
////////////////////////////////////////////////////////////
String::String(char ansiChar, const std::locale& locale) : m_string(1, Utf32::decodeAnsi(ansiChar, locale))
{
}
////////////////////////////////////////////////////////////
String::String(wchar_t wideChar) : m_string(1, Utf32::decodeWide(wideChar))
{
}
////////////////////////////////////////////////////////////
String::String(char32_t utf32Char) : m_string(1, utf32Char)
{
}
////////////////////////////////////////////////////////////
String::String(const char* ansiString, const std::locale& locale)
{
if (ansiString)
{
const std::size_t length = std::strlen(ansiString);
if (length > 0)
{
m_string.reserve(length + 1);
Utf32::fromAnsi(ansiString, ansiString + length, std::back_inserter(m_string), locale);
}
}
}
////////////////////////////////////////////////////////////
String::String(const std::string& ansiString, const std::locale& locale)
{
m_string.reserve(ansiString.length() + 1);
Utf32::fromAnsi(ansiString.begin(), ansiString.end(), std::back_inserter(m_string), locale);
}
////////////////////////////////////////////////////////////
String::String(const wchar_t* wideString)
{
if (wideString)
{
const std::size_t length = std::wcslen(wideString);
if (length > 0)
{
m_string.reserve(length + 1);
Utf32::fromWide(wideString, wideString + length, std::back_inserter(m_string));
}
}
}
////////////////////////////////////////////////////////////
String::String(const std::wstring& wideString)
{
m_string.reserve(wideString.length() + 1);
Utf32::fromWide(wideString.begin(), wideString.end(), std::back_inserter(m_string));
}
////////////////////////////////////////////////////////////
String::String(const char32_t* utf32String) : m_string(utf32String ? utf32String : U"")
{
}
////////////////////////////////////////////////////////////
String::String(std::u32string utf32String) : m_string(std::move(utf32String))
{
}
////////////////////////////////////////////////////////////
String::operator std::string() const
{
return toAnsiString();
}
////////////////////////////////////////////////////////////
String::operator std::wstring() const
{
return toWideString();
}
////////////////////////////////////////////////////////////
std::string String::toAnsiString(const std::locale& locale) const
{
// Prepare the output string
std::string output;
output.reserve(m_string.length() + 1);
// Convert
Utf32::toAnsi(m_string.begin(), m_string.end(), std::back_inserter(output), 0, locale);
return output;
}
////////////////////////////////////////////////////////////
std::wstring String::toWideString() const
{
// Prepare the output string
std::wstring output;
output.reserve(m_string.length() + 1);
// Convert
Utf32::toWide(m_string.begin(), m_string.end(), std::back_inserter(output), 0);
return output;
}
////////////////////////////////////////////////////////////
U8String String::toUtf8() const
{
// Prepare the output string
U8String output;
output.reserve(m_string.length());
// Convert
Utf32::toUtf8(m_string.begin(), m_string.end(), std::back_inserter(output));
return output;
}
////////////////////////////////////////////////////////////
std::u16string String::toUtf16() const
{
// Prepare the output string
std::u16string output;
output.reserve(m_string.length());
// Convert
Utf32::toUtf16(m_string.begin(), m_string.end(), std::back_inserter(output));
return output;
}
////////////////////////////////////////////////////////////
std::u32string String::toUtf32() const
{
return m_string;
}
////////////////////////////////////////////////////////////
String& String::operator+=(const String& right)
{
m_string += right.m_string;
return *this;
}
////////////////////////////////////////////////////////////
char32_t String::operator[](std::size_t index) const
{
assert(index < m_string.size() && "Index is out of bounds");
return m_string[index];
}
////////////////////////////////////////////////////////////
char32_t& String::operator[](std::size_t index)
{
assert(index < m_string.size() && "Index is out of bounds");
return m_string[index];
}
////////////////////////////////////////////////////////////
void String::clear()
{
m_string.clear();
}
////////////////////////////////////////////////////////////
std::size_t String::getSize() const
{
return m_string.size();
}
////////////////////////////////////////////////////////////
bool String::isEmpty() const
{
return m_string.empty();
}
////////////////////////////////////////////////////////////
void String::erase(std::size_t position, std::size_t count)
{
m_string.erase(position, count);
}
////////////////////////////////////////////////////////////
void String::insert(std::size_t position, const String& str)
{
m_string.insert(position, str.m_string);
}
////////////////////////////////////////////////////////////
std::size_t String::find(const String& str, std::size_t start) const
{
return m_string.find(str.m_string, start);
}
////////////////////////////////////////////////////////////
void String::replace(std::size_t position, std::size_t length, const String& replaceWith)
{
m_string.replace(position, length, replaceWith.m_string);
}
////////////////////////////////////////////////////////////
void String::replace(const String& searchFor, const String& replaceWith)
{
const std::size_t step = replaceWith.getSize();
const std::size_t len = searchFor.getSize();
std::size_t pos = find(searchFor);
// Replace each occurrence of search
while (pos != InvalidPos)
{
replace(pos, len, replaceWith);
pos = find(searchFor, pos + step);
}
}
////////////////////////////////////////////////////////////
String String::substring(std::size_t position, std::size_t length) const
{
return m_string.substr(position, length);
}
////////////////////////////////////////////////////////////
const char32_t* String::getData() const
{
return m_string.c_str();
}
////////////////////////////////////////////////////////////
String::Iterator String::begin()
{
return m_string.begin();
}
////////////////////////////////////////////////////////////
String::ConstIterator String::begin() const
{
return m_string.begin();
}
////////////////////////////////////////////////////////////
String::Iterator String::end()
{
return m_string.end();
}
////////////////////////////////////////////////////////////
String::ConstIterator String::end() const
{
return m_string.end();
}
////////////////////////////////////////////////////////////
bool operator==(const String& left, const String& right)
{
return left.m_string == right.m_string;
}
////////////////////////////////////////////////////////////
bool operator!=(const String& left, const String& right)
{
return !(left == right);
}
////////////////////////////////////////////////////////////
bool operator<(const String& left, const String& right)
{
return left.m_string < right.m_string;
}
////////////////////////////////////////////////////////////
bool operator>(const String& left, const String& right)
{
return right < left;
}
////////////////////////////////////////////////////////////
bool operator<=(const String& left, const String& right)
{
return !(right < left);
}
////////////////////////////////////////////////////////////
bool operator>=(const String& left, const String& right)
{
return !(left < right);
}
////////////////////////////////////////////////////////////
String operator+(const String& left, const String& right)
{
String string = left;
string += right;
return string;
}
} // namespace sf

View File

@ -0,0 +1,57 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Time.hpp>
#include <SFML/System/Unix/SleepImpl.hpp>
#include <cerrno>
#include <ctime>
namespace sf::priv
{
////////////////////////////////////////////////////////////
void sleepImpl(Time time)
{
const std::int64_t usecs = time.asMicroseconds();
// Construct the time to wait
timespec ti{};
ti.tv_sec = static_cast<time_t>(usecs / 1'000'000);
ti.tv_nsec = static_cast<long>((usecs % 1'000'000) * 1'000);
// Wait...
// If nanosleep returns -1, we check errno. If it is EINTR
// nanosleep was interrupted and has set ti to the remaining
// duration. We continue sleeping until the complete duration
// has passed. We stop sleeping if it was due to an error.
while ((nanosleep(&ti, &ti) == -1) && (errno == EINTR))
{
}
}
} // namespace sf::priv

View File

@ -0,0 +1,48 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
namespace sf
{
class Time;
}
namespace sf::priv
{
////////////////////////////////////////////////////////////
/// \brief Unix implementation of sf::Sleep
///
/// \param time Time to sleep
///
////////////////////////////////////////////////////////////
void sleepImpl(Time time);
} // namespace sf::priv

View File

@ -0,0 +1,64 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Utils.hpp>
#include <sstream>
#include <cctype>
namespace sf
{
std::string toLower(std::string str)
{
for (char& c : str)
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
return str;
}
std::string formatDebugPathInfo(const std::filesystem::path& path)
{
std::ostringstream oss;
// convert to UTF-8 to handle non-ascii/non-latin1 filenames on windows
// cast is required to work in C++20 where u8string is char8_t which can't be printed to char stream
oss << " Provided path: " << reinterpret_cast<const char*>(path.u8string().c_str()) << '\n' //
<< " Absolute path: " << reinterpret_cast<const char*>(std::filesystem::absolute(path).u8string().c_str());
return oss.str();
}
std::FILE* openFile(const std::filesystem::path& filename, std::string_view mode)
{
#ifdef SFML_SYSTEM_WINDOWS
const std::wstring wmode(mode.begin(), mode.end());
return _wfopen(filename.c_str(), wmode.data());
#else
return std::fopen(filename.c_str(), mode.data());
#endif
}
} // namespace sf

View File

@ -0,0 +1,58 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Export.hpp>
#include <filesystem>
#include <string>
#include <string_view>
#include <cstddef>
#include <cstdio>
namespace sf
{
[[nodiscard]] SFML_SYSTEM_API std::string toLower(std::string str);
[[nodiscard]] SFML_SYSTEM_API std::string formatDebugPathInfo(const std::filesystem::path& path);
// Convert byte sequence into integer
// toInteger<int>(0x12, 0x34, 0x56) == 0x563412
template <typename IntegerType, typename... Bytes>
[[nodiscard]] constexpr IntegerType toInteger(Bytes... byte)
{
static_assert(sizeof(IntegerType) >= sizeof...(Bytes), "IntegerType not large enough to contain bytes");
IntegerType integer = 0;
std::size_t index = 0;
return ((integer |= static_cast<IntegerType>(static_cast<IntegerType>(byte) << 8 * index++)), ...);
}
[[nodiscard]] SFML_SYSTEM_API std::FILE* openFile(const std::filesystem::path& filename, std::string_view mode);
} // namespace sf

View File

@ -0,0 +1,124 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#include <SFML/System/Vector2.hpp>
#include <type_traits>
#include <cassert>
#include <cmath>
namespace sf
{
////////////////////////////////////////////////////////////
template <typename T>
Vector2<T> Vector2<T>::normalized() const
{
static_assert(std::is_floating_point_v<T>, "Vector2::normalized() is only supported for floating point types");
assert(*this != Vector2<T>() && "Vector2::normalized() cannot normalize a zero vector");
return (*this) / length();
}
////////////////////////////////////////////////////////////
template <typename T>
Angle Vector2<T>::angleTo(Vector2<T> rhs) const
{
static_assert(std::is_floating_point_v<T>, "Vector2::angleTo() is only supported for floating point types");
assert(*this != Vector2<T>() && "Vector2::angleTo() cannot calculate angle from a zero vector");
assert(rhs != Vector2<T>() && "Vector2::angleTo() cannot calculate angle to a zero vector");
return radians(static_cast<float>(std::atan2(cross(rhs), dot(rhs))));
}
////////////////////////////////////////////////////////////
template <typename T>
Angle Vector2<T>::angle() const
{
static_assert(std::is_floating_point_v<T>, "Vector2::angle() is only supported for floating point types");
assert(*this != Vector2<T>() && "Vector2::angle() cannot calculate angle from a zero vector");
return radians(static_cast<float>(std::atan2(y, x)));
}
////////////////////////////////////////////////////////////
template <typename T>
Vector2<T> Vector2<T>::rotatedBy(Angle phi) const
{
static_assert(std::is_floating_point_v<T>, "Vector2::rotatedBy() is only supported for floating point types");
// No zero vector assert, because rotating a zero vector is well-defined (yields always itself)
T cos = std::cos(static_cast<T>(phi.asRadians()));
T sin = std::sin(static_cast<T>(phi.asRadians()));
// Don't manipulate x and y separately, otherwise they're overwritten too early
return Vector2<T>(cos * x - sin * y, sin * x + cos * y);
}
////////////////////////////////////////////////////////////
template <typename T>
Vector2<T> Vector2<T>::projectedOnto(Vector2<T> axis) const
{
static_assert(std::is_floating_point_v<T>, "Vector2::projectedOnto() is only supported for floating point types");
assert(axis != Vector2<T>() && "Vector2::projectedOnto() cannot project onto a zero vector");
return dot(axis) / axis.lengthSquared() * axis;
}
////////////////////////////////////////////////////////////
template <typename T>
Vector2<T>::Vector2(T r, Angle phi) :
x(r * static_cast<T>(std::cos(phi.asRadians()))),
y(r * static_cast<T>(std::sin(phi.asRadians())))
{
static_assert(std::is_floating_point_v<T>, "Vector2::Vector2(T, Angle) is only supported for floating point types");
}
////////////////////////////////////////////////////////////
template <typename T>
T Vector2<T>::length() const
{
static_assert(std::is_floating_point_v<T>, "Vector2::length() is only supported for floating point types");
// don't use std::hypot because of slow performance
return std::sqrt(x * x + y * y);
}
} // namespace sf
////////////////////////////////////////////////////////////
// Explicit template instantiations
////////////////////////////////////////////////////////////
template class sf::Vector2<float>;
template class sf::Vector2<double>;
template class sf::Vector2<long double>;

View File

@ -0,0 +1,65 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#include <SFML/System/Vector3.hpp>
#include <type_traits>
#include <cassert>
#include <cmath>
namespace sf
{
////////////////////////////////////////////////////////////
template <typename T>
Vector3<T> Vector3<T>::normalized() const
{
static_assert(std::is_floating_point_v<T>, "Vector3::normalized() is only supported for floating point types");
assert(*this != Vector3<T>() && "Vector3::normalized() cannot normalize a zero vector");
return (*this) / length();
}
////////////////////////////////////////////////////////////
template <typename T>
T Vector3<T>::length() const
{
static_assert(std::is_floating_point_v<T>, "Vector3::length() is only supported for floating point types");
// don't use std::hypot because of slow performance
return std::sqrt(x * x + y * y + z * z);
}
} // namespace sf
////////////////////////////////////////////////////////////
// Explicit template instantiations
////////////////////////////////////////////////////////////
template class sf::Vector3<float>;
template class sf::Vector3<double>;
template class sf::Vector3<long double>;

View File

@ -0,0 +1,57 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Time.hpp>
#include <SFML/System/Win32/SleepImpl.hpp>
#include <SFML/System/Win32/WindowsHeader.hpp>
#include <mmsystem.h>
namespace sf::priv
{
////////////////////////////////////////////////////////////
void sleepImpl(Time time)
{
// Get the minimum supported timer resolution on this system
static const UINT periodMin = []
{
TIMECAPS tc;
timeGetDevCaps(&tc, sizeof(TIMECAPS));
return tc.wPeriodMin;
}();
// Set the timer resolution to the minimum for the Sleep call
timeBeginPeriod(periodMin);
// Wait...
::Sleep(static_cast<DWORD>(time.asMilliseconds()));
// Reset the timer resolution back to the system default
timeEndPeriod(periodMin);
}
} // namespace sf::priv

View File

@ -0,0 +1,48 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
namespace sf
{
class Time;
}
namespace sf::priv
{
////////////////////////////////////////////////////////////
/// \brief Windows implementation of sf::Sleep
///
/// \param time Time to sleep
///
////////////////////////////////////////////////////////////
void sleepImpl(Time time);
} // namespace sf::priv

View File

@ -0,0 +1,55 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef _WIN32_WINDOWS
#define _WIN32_WINDOWS 0x0501 // NOLINT(bugprone-reserved-identifier)
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#ifndef WINVER
#define WINVER 0x0501
#endif
#ifndef UNICODE
#define UNICODE 1
#endif
#ifndef _UNICODE
#define _UNICODE 1 // NOLINT(bugprone-reserved-identifier)
#endif
#include <windows.h>

View File

@ -107,7 +107,6 @@ export BUILD_ENV
DIST_TAR_ARGS="
help2man --exclude ChangeLog
sfml --exclude libs-osx
expat --exclude README.md
libsoxr --exclude inst-check-soxr-lsr
graphviz --exclude COPYING --exclude graphviz.spec

View File

@ -321,7 +321,7 @@ table_line_append DIST_PATCHES fontconfig-target " \
table_line_append DIST_PATCHES libgd 'https://gist.githubusercontent.com/rkitover/c64ea5b83ddea94ace58c40c7de42879/raw/fbaf4885fbefb302116b56626c0e191df514e8c6/libgd-2.2.4-mingw-static.patch'
table_insert_before DISTS sfml '
table_insert_before DISTS wxwidgets '
openal https://github.com/kcat/openal-soft/archive/openal-soft-1.19.1.tar.gz lib/libOpenAL32.a
'

View File

@ -14,7 +14,7 @@ export BUILD_ENV
. "$(dirname "$0")/../builder/core.sh"
# on mac openal is part of the system, on most unixes we need openal-soft
table_insert_before DISTS sfml '
table_insert_before DISTS wxwidgets '
openal http://kcat.strangesoft.net/openal-releases/openal-soft-1.18.2.tar.bz2 lib/libopenal.a
'
@ -27,7 +27,7 @@ libXrender libXrandr libXfixes libXdamage libxshmfence libXi libXtst
libXxf86vm"
# have to build a large chunk of X11 on *nix
table_insert_before DISTS sfml '
table_insert_before DISTS wxwidgets '
xproto https://www.x.org/archive/individual/proto/xproto-7.0.31.tar.bz2 include/X11/X.h
xcb-proto https://www.x.org/archive/individual/xcb/xcb-proto-1.13.tar.bz2 lib/pkgconfig/xcb-proto.pc
inputproto https://www.x.org/archive/individual/proto/inputproto-2.3.2.tar.bz2 include/X11/extensions/XI.h
@ -65,7 +65,7 @@ for dist in $XORG_DISTS; do
done
# and Wayland now that that's a thing
table_insert_before DISTS sfml '
table_insert_before DISTS wxwidgets '
wayland https://wayland.freedesktop.org/releases/wayland-1.16.0.tar.xz lib/libwayland-client.so
wayland-protocols https://wayland.freedesktop.org/releases/wayland-protocols-1.15.tar.xz share/pkgconfig/wayland-protocols.pc
'
@ -76,7 +76,7 @@ for dist in wayland; do
done
# and mesa OpenGL (the Gallium drivers in mesa require llvm)
table_insert_before DISTS sfml '
table_insert_before DISTS wxwidgets '
libpciaccess https://www.x.org/archive//individual/lib/libpciaccess-0.14.tar.bz2 lib/libpciaccess.a
libdrm https://dri.freedesktop.org/libdrm/libdrm-2.4.88.tar.bz2 lib/libdrm.a
# llvm http://releases.llvm.org/5.0.0/llvm-5.0.0.src.tar.xz lib/libLLVMCore.a

View File

@ -189,7 +189,6 @@ DISTS=$DISTS'
faudio https://github.com/FNA-XNA/FAudio/archive/refs/tags/24.09.tar.gz lib/libFAudio.a
flac https://ftp.osuosl.org/pub/xiph/releases/flac/flac-1.3.4.tar.xz lib/libFLAC.a
harfbuzz https://github.com/harfbuzz/harfbuzz/releases/download/10.0.1/harfbuzz-10.0.1.tar.xz lib/libharfbuzz.a
sfml https://github.com/SFML/SFML/releases/download/3.0.0/SFML-3.0.0-sources.zip lib/libsfml-system-s.a
shared-mime-info https://gitlab.freedesktop.org/xdg/shared-mime-info/-/archive/2.2/shared-mime-info-2.2.tar.bz2 bin/update-mime-database
wxwidgets https://github.com/wxWidgets/wxWidgets/releases/download/v3.2.7/wxWidgets-3.2.7.tar.bz2 lib/libwx_baseu-3.*.a
ffmpeg http://ffmpeg.org/releases/ffmpeg-7.0.2.tar.xz lib/libavformat.a
@ -360,7 +359,6 @@ DIST_ARGS="$DIST_ARGS
python3 --with-ensurepip=install --with-system-expat
XML-Parser EXPATINCPATH=\"\$BUILD_ROOT/root/include\" EXPATLIBPATH=\"\$BUILD_ROOT/root/lib\"
doxygen -DICONV_ACCEPTS_NONCONST_INPUT:BOOL=FALSE -DICONV_ACCEPTS_CONST_INPUT:BOOL=TRUE
sfml -DSFML_USE_SYSTEM_DEPS=TRUE -DSFML_BUILD_AUDIO=FALSE
libcroco --disable-Bsymbolic
snappy -DSNAPPY_BUILD_TESTS=OFF -DSNAPPY_BUILD_BENCHMARKS=OFF
libjpeg-turbo -DWITH_JPEG8=ON -DWITH_SIMD=OFF
@ -413,7 +411,6 @@ DIST_EXTRA_LDFLAGS="$DIST_EXTRA_LDFLAGS
DIST_EXTRA_CXXFLAGS="$DIST_EXTRA_CXXFLAGS
gperf -std=gnu++11
doxygen -std=gnu++11
sfml -std=gnu++11
wxwidgets -std=gnu++11
libmodplug -std=gnu++11
libopencore-amrnb -std=gnu++11

View File

@ -195,12 +195,6 @@ if [ "$target_cpu" = i386 ]; then
table_line_append DIST_ARGS python2 '--host= --build='
table_line_append DIST_ARGS python3 '--host= --build='
table_line_append DIST_ARGS sfml '-DCMAKE_OSX_ARCHITECTURES=i386'
table_line_append DIST_PRE_BUILD sfml " \
sed -i.bak '/FATAL_ERROR \"Only 64-bit architecture is supported/d' CMakeLists.txt; \
"
table_line_append DIST_ARGS libicu '--host= --build='
fi
@ -237,11 +231,6 @@ table_line_append DIST_CONFIGURE_OVERRIDES ffmpeg "--disable-videotoolbox --extr
table_line_remove DISTS python2
table_line_append DIST_PRE_BUILD sfml " \
sed -E -i.bak '/OSX_DEPLOYMENT_TARGET/d' CMakeLists.txt; \
"
if [ -n "$APPLE_SILICON" ] && [ "$target_cpu" != ARM64 ]; then
table_line_remove DISTS m4
table_line_remove DISTS XML-Parser