feat: make custom version of glslang to improve shader compatibility + random improvements (#291)

* feat: added glslang and spirv-core for handling shaders, should provide better results than current systems

* fix: brought back proper include placement, should fix some shaders not working

* chore: dial down required version to 330

* fix: crash when taking screenshot

* fix: use glReadnPixels for X11 copying to be more memory safe

* chore: reverted part of last commit

* chore: delay initialization of browser until it's used once

* chore: do not initialize web browser unless explicitly needed

* chore: cleanup filesystem functions to use containers directly

* chore: memory cleanup fixes

* chore: fix glReadPixels for older opengl versions

* chore: remove shader patches as they shouldn't be needed anymore

* chore: initialise variables

* chore: update deps, actions and readme

* chore: make use of custon glslang and SPIRV-Cross

* Revert "chore: update deps, actions and readme"

This reverts commit c3fbc9340b.

* chore: update actions to include submodules

* chore: do not depend on SPIRV-Tools

* fix: added log10 macro

* feat: update to latest glslang (swizzle and vec2/vec3/vec4 implicit casting to float)

* revert: delayed initialization of cef was causing issues, reverted

* chore: re-organized web wallpaper support to use custom scheme and better handle multiprocessing

* chore: make use of external repos for all deps instead of copying things manually and more cleanup work

* chore: wrong include file used in CGLSLContext.cpp

* chore: fix wayland generation folder not being present

* feat: somewhat support TEXB0004

* chore: improve function call matching and fallback to more lax method if no function is found

* chore: changed shader compilation slightly so they're passed onto glsl just once

* feat: swap android's fft implementation (which wasn't right) with kissfft's and fix update frequency issues

* chore: added missing dependency

* chore: added missing dep to PKGBUILD

* feat: add testing tools to run over all backgrounds and getting output data

* chore: jail CDirectory to the basepath and prevent accessing data outside of the main directory

* chore: process script now scales the previews so the html file is not too big

* chore: add showcase gallery to the README.md

* chore: update README

* chore: some readability improvements on code

* chore: fix segfault after code cleanup

* chore: make use of std::filesystem::canonical instead of basepath and slight typing changes on texture objects

* chore: fix path detection being wrong, make use of std::filesystem::path on CContainers

* chore: cleanup of the core part of the project

* chore: bring back std::move and make some methods const where it makes sense

* feat: added a pretty printer for easier debug and comparison between different versions of linux-wallpaperengine

* chore: refactored shader compilation code once more to be easier to follow and fixed the longstanding bug of #include not being added in the right place

* chore: more debug info for the pretty printer

* fix: some textures applied were not the right ones

* chore: properly set combos based on textures

* feat: take into account project properties for shader values
feat: proper parsing of combo values in shaders
fix: shader units weren't linked as they should
chore: more support for detecting shader things automatically

* fix: blending mode for passes using the wrong value
fix: shader uniforms from project properties should now be taken into account

* chore: use ubuntu 22 and ubuntu 24 as builders, ubuntu 20 is retired

* chore: use ubuntu 22 and ubuntu 24 as builders, ubuntu 20 is retired

* chore: hopefully fix github actions build

* refactor: simplified working with properties, constants and shader variables

* chore: remove a couple of todos that aren't needed anymore

* chore: simplify the texture detection a little bit, still work left to do

* fix: regression on texture setup not working properly

* fix: filenames with dots were not being handled properly

* chore: remove some uselesss messages

* chore: fixed std::string json values not casting anything to it as it was assumed

* fix: null user value for constants means it cannot be modified by the user

* chore: remove exception when a shader constant uses a non-existant property

* fix: angles can be an user setting too, also added detection for animation frames to show a warning

* fix: ensure variable information is not commented out by a line comment

* fix: shader includes weren't being processed properly

* chore: update to latest glslang and SPIRV-Cross to support non-integer array indices

* chore: make use of auto where it made sense

* feat: make use of in/out promotion on glslang-WallpaperEngine
feat: use glslang-WallpaperEngine linkin process as an extra validation

* chore: improve scripts for running the app

* chore: hide background structure dump behind a command-line switch

* chore: rewritten bloom effect as a json object inside C++ so it's easier to follow

* chore: removed deprecated parameters and switched to argparse instead of getopt
fix: clamping mode wasn't applied by background properly, only globally

* chore: removed help prompt from the output unless actually required

* fix: web subprocesses weren't launching due to parameter's parsing, temporal fix

* feat: added material command copy support

* feat: do not initialize some subsystems if they're disabled or not used by backgrounds

* chore: ignore type in combos as only seems to be used in the editor

* chore: update to latest glslang-WallpaperEngine changes

* chore: delete uniforms if they already exist before setting

* chore: more cleanup and fixes

* chore: more cleanup and fixes

* chore: more cleanup and fixes

* chore: update file functions to make use of shared_ptr/unique_ptr instead of copying things around

* chore: more changes to unique_ptr and shared_ptr

* chore: more changes to unique_ptr and shared_ptr

* chore: more changes to unique_ptr and shared_ptr
feat: improved render initialization process to make it easier and simpler to add new drivers (no more #ifdef in CWallpaperApplication.cpp)

* chore: change all is/as castings to use typeid so no string comparison takes place

* chore: more cleanup, default initialization of values wherever possible

* chore: moved more things to std::unique_ptr and std::shared_ptr

* chore: moved more things to std::unique_ptr and std::shared_ptr

* fix: browser context usage crashed the app

* chore: the setting controls fullscreen detection creation the same way audio works

* fix: ensure that at least one frame is rendered before detecting fullscreen windows

* chore: slight changes to output and documentation to properly reflect current build configuration

* chore: fix mipmap texture creation

* chore: fix pass uniforms not taking into account fragment shader's uniforms
chore: keep processed code in the shader sent to opengl so it appears on RenderDoc

* chore: formating issues by codefactor

* chore: do not use new to allocate the pretty printer

* fix: strchr wasn't properly done for window geometry

* chore: add recording mode for status page generation

* chore: update .gitignore

* chore: update script to make use of video generation instead of the old python scripts

* chore: also copy project.json so it can be used on the site too

* fix: regression on invisible images not being rendered

* feat: add option to disable camera parallax

* chore: add the reversing tools I have locally

* chore: mention some of the common issues in the README.md

* chore: take submodules into account for archlinux

* chore: missed cd "$pkgname" in arch's prepare step
This commit is contained in:
Alexis Maiquez 2025-05-10 19:34:59 +02:00 committed by GitHub
parent 4a063d0b84
commit 7bffcded09
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
293 changed files with 8553 additions and 34718 deletions

View File

@ -14,8 +14,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Publish parallel-disk-usage to the AUR
- name: Publish linux-wallpaperengine-git to the AUR
uses: KSXGitHub/github-actions-deploy-aur@v2.7.2
with:
pkgname: linux-wallpaperengine-git

View File

@ -14,7 +14,7 @@ jobs:
build-x11:
strategy:
matrix:
os: [ubuntu-22.04, ubuntu-20.04]
os: [ubuntu-22.04, ubuntu-24.04]
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
# You can convert this to a matrix build if you need cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
@ -22,9 +22,15 @@ jobs:
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Install dependencies
run: sudo apt-get update && sudo apt-get -y install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxxf86vm-dev libglm-dev libglfw3-dev libmpv-dev mpv libmpv1 libpulse-dev libpulse0
if: matrix.os == 'ubuntu-22.04'
run: sudo apt-get update && sudo apt-get -y install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxxf86vm-dev libglm-dev libglfw3-dev libmpv-dev mpv libmpv1 libpulse-dev libpulse0 libfftw3-dev
- name: Install dependencies
if: matrix.os == 'ubuntu-24.04'
run: sudo apt-get update && sudo apt-get -y install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxxf86vm-dev libglm-dev libglfw3-dev libmpv-dev mpv libmpv2 libpulse-dev libpulse0 libfftw3-dev
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
@ -38,7 +44,7 @@ jobs:
build-x11-wayland:
strategy:
matrix:
os: [ubuntu-22.04]
os: [ubuntu-22.04, ubuntu-24.04]
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
# You can convert this to a matrix build if you need cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
@ -46,9 +52,16 @@ jobs:
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Install dependencies
run: sudo apt-get update && sudo apt-get -y install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxxf86vm-dev libglm-dev libglfw3-dev libmpv-dev mpv libmpv1 libpulse-dev libpulse0 wayland-scanner++ wayland-protocols libwayland-dev
if: matrix.os == 'ubuntu-22.04'
run: sudo apt-get update && sudo apt-get -y install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxxf86vm-dev libglm-dev libglfw3-dev libmpv-dev mpv libmpv1 libpulse-dev libpulse0 libfftw3-dev wayland-scanner++ wayland-protocols libwayland-dev
- name: Install dependencies
if: matrix.os == 'ubuntu-24.04'
run: sudo apt-get update && sudo apt-get -y install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxxf86vm-dev libglm-dev libglfw3-dev libmpv-dev mpv libmpv2 libpulse-dev libpulse0 libfftw3-dev wayland-scanner++ wayland-protocols libwayland-dev
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
@ -62,7 +75,7 @@ jobs:
build-wayland:
strategy:
matrix:
os: [ubuntu-22.04]
os: [ubuntu-22.04, ubuntu-24.04]
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
# You can convert this to a matrix build if you need cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
@ -70,9 +83,16 @@ jobs:
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Install dependencies
run: sudo apt-get update && sudo apt-get -y install wayland-scanner++ wayland-protocols libwayland-dev libwayland-egl-backend-dev libglfw3-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libglm-dev libmpv-dev mpv libmpv1 libpulse-dev libpulse0
if: matrix.os == 'ubuntu-22.04'
run: sudo apt-get update && sudo apt-get -y install wayland-scanner++ wayland-protocols libwayland-dev libwayland-egl-backend-dev libglfw3-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libglm-dev libmpv-dev mpv libmpv1 libpulse-dev libpulse0 libfftw3-dev
- name: Install dependencies
if: matrix.os == 'ubuntu-24.04'
run: sudo apt-get update && sudo apt-get -y install wayland-scanner++ wayland-protocols libwayland-dev libwayland-egl-backend-dev libglfw3-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libglm-dev libmpv-dev mpv libmpv2 libpulse-dev libpulse0 libfftw3-dev
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
cmake-build-debug*
cmake-build-*
.idea
build/
.vscode/

28
.gitmodules vendored Normal file
View File

@ -0,0 +1,28 @@
[submodule "src/External/glslang-WallpaperEngine"]
path = src/External/glslang-WallpaperEngine
url = https://github.com/Almamu/glslang-WallpaperEngine.git
branch = main
[submodule "src/External/SPIRV-Cross-WallpaperEngine"]
path = src/External/SPIRV-Cross-WallpaperEngine
url = https://github.com/Almamu/SPIRV-Cross-WallpaperEngine.git
branch = main
[submodule "src/External/stb"]
path = src/External/stb
url = https://github.com/nothings/stb.git
branch = master
[submodule "src/External/json"]
path = src/External/json
url = https://github.com/nlohmann/json.git
branch = develop
[submodule "src/External/MimeTypes"]
path = src/External/MimeTypes
url = https://github.com/lasselukkari/MimeTypes.git
branch = master
[submodule "src/External/kissfft"]
path = src/External/kissfft
url = https://github.com/mborgerding/kissfft.git
branch = master
[submodule "src/External/argparse"]
path = src/External/argparse
url = https://github.com/p-ranav/argparse.git
branch = master

View File

@ -10,10 +10,12 @@ set_property(GLOBAL PROPERTY OS_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
set(OpenGL_GL_PREFERENCE "LEGACY")
set(DATADIR ${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME})
set(PATCHESDIR ${DATADIR}/patches/)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if(NOT DEMOMODE)
set(DEMOMODE 0)
endif()
if(NOT ERRORONLY)
set(ERRORONLY 0)
endif()
@ -22,7 +24,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing")
# if you're developing you might find this debug option useful for shader output, although RenderDoc is encouraged
add_compile_definitions(ERRORONLY=${ERRORONLY})
add_compile_definitions(DATADIR="${DATADIR}")
add_compile_definitions(DEMOMODE=${DEMOMODE})
find_package(X11)
find_package(OpenGL REQUIRED)
@ -35,9 +37,14 @@ find_package(LZ4 REQUIRED)
find_package(FFMPEG REQUIRED)
find_package(PulseAudio REQUIRED)
set(ENABLE_OPT OFF)
set(SPIRV_CROSS_FORCE_PIC ON)
set(KISSFFT_TEST OFF)
set(KISSFFT_TOOLS OFF)
# Download CEF of specified version for current platform
# Specify the CEF distribution version.
set(CEF_VERSION "120.1.10+g3ce3184+chromium-120.0.6099.129")
set(CEF_VERSION "135.0.17+gcbc1c5b+chromium-135.0.7049.52")
# Determine the platform.
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
if("${PROJECT_ARCH}" STREQUAL "arm64")
@ -87,47 +94,28 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
endif()
message(STATUS "Using CEF for ${CMAKE_SYSTEM_NAME} - ${CEF_PLATFORM} (${CEF_ARCH_DETECTION})")
include(DownloadCEF)
DownloadCEF("${CEF_PLATFORM}" "${CEF_VERSION}" "${CMAKE_SOURCE_DIR}/third_party/cef")
DownloadCEF("${CEF_PLATFORM}" "${CEF_VERSION}" "${CMAKE_CURRENT_BINARY_DIR}/cef")
# add cef's cmake files to the lookup so we can use those directly
list(APPEND CMAKE_MODULE_PATH "${CEF_ROOT}/cmake")
find_package(CEF REQUIRED)
set(
CMAKE_RUNTIME_OUTPUT_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}/build
)
# remove some switches that forbid us from using things that are commonplace in our codebase
list(REMOVE_ITEM CEF_CXX_COMPILER_FLAGS -fno-exceptions)
list(REMOVE_ITEM CEF_CXX_COMPILER_FLAGS -fno-rtti)
# remove the vulkan library from the files to copy so it doesn't fail after removing it
list(REMOVE_ITEM CEF_BINARY_FILES libvulkan.so.1)
set(
CMAKE_LIBRARY_OUTPUT_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}/lib
)
set(
TARGET_OUTPUT_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}/build
)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/output)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
set(TARGET_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/output)
add_subdirectory(${CEF_LIBCEF_DLL_WRAPPER_PATH} libcef_dll_wrapper)
include_directories(
${MPV_INCLUDE_DIR}
${GLEW_INCLUDE_DIR}
${LZ4_INCLUDE_DIR}
${SDL2_INCLUDE_DIRS}
${FFMPEG_INCLUDE_DIR}
${PULSEAUDIO_INCLUDE_DIR}
src
${CEF_INCLUDE_PATH}
${CMAKE_SOURCE_DIR}
include)
add_library(ceflib SHARED IMPORTED)
set_target_properties(ceflib
PROPERTIES IMPORTED_LOCATION ${TARGET_OUTPUT_DIRECTORY}/libcef.so)
ADD_LOGICAL_TARGET("libcef_lib" "${CEF_LIB_DEBUG}" "${CEF_LIB_RELEASE}")
# SET_CEF_TARGET_OUT_DIR()
include_directories(${_CEF_ROOT})
add_subdirectory(src/External/glslang-WallpaperEngine glslang)
add_subdirectory(src/External/SPIRV-Cross-WallpaperEngine spirv-cross)
add_subdirectory(src/External/kissfft kissfft)
add_subdirectory(src/External/argparse argparse)
# try to enable wayland builds when possible
pkg_check_modules(WAYLAND_SUPPORT wayland-cursor wayland-protocols egl wayland-egl)
@ -141,54 +129,61 @@ if(WAYLAND_SUPPORT_FOUND)
OUTPUT_VARIABLE WAYLAND_PROTOCOLS_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")
set(WAYLAND_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/wayland)
file(MAKE_DIRECTORY ${WAYLAND_OUTPUT_DIR})
message(STATUS "Building protocols...")
execute_process(
COMMAND ${WaylandScanner} client-header ${CMAKE_SOURCE_DIR}/protocols/wlr-foreign-toplevel-management-unstable-v1.xml wlr-foreign-toplevel-management-unstable-v1-protocol.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
execute_process(
COMMAND ${WaylandScanner} private-code ${CMAKE_SOURCE_DIR}/protocols/wlr-foreign-toplevel-management-unstable-v1.xml wlr-foreign-toplevel-management-unstable-v1-protocol.c
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
execute_process(
COMMAND ${WaylandScanner} client-header ${CMAKE_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml wlr-layer-shell-unstable-v1-protocol.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
execute_process(
COMMAND ${WaylandScanner} private-code ${CMAKE_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml wlr-layer-shell-unstable-v1-protocol.c
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
execute_process(
COMMAND ${WaylandScanner} client-header ${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml xdg-shell-protocol.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
execute_process(
COMMAND ${WaylandScanner} private-code ${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml xdg-shell-protocol.c
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
add_custom_command(OUTPUT ${WAYLAND_OUTPUT_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.h
COMMAND ${WaylandScanner} client-header ${CMAKE_SOURCE_DIR}/protocols/wlr-foreign-toplevel-management-unstable-v1.xml ${WAYLAND_OUTPUT_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.h)
add_custom_command(OUTPUT ${WAYLAND_OUTPUT_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.c
COMMAND ${WaylandScanner} private-code ${CMAKE_SOURCE_DIR}/protocols/wlr-foreign-toplevel-management-unstable-v1.xml ${WAYLAND_OUTPUT_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.c)
add_custom_command(OUTPUT ${WAYLAND_OUTPUT_DIR}/wlr-layer-shell-unstable-v1-protocol.h
COMMAND ${WaylandScanner} client-header ${CMAKE_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml ${WAYLAND_OUTPUT_DIR}/wlr-layer-shell-unstable-v1-protocol.h)
add_custom_command(OUTPUT ${WAYLAND_OUTPUT_DIR}/wlr-layer-shell-unstable-v1-protocol.c
COMMAND ${WaylandScanner} private-code ${CMAKE_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml ${WAYLAND_OUTPUT_DIR}/wlr-layer-shell-unstable-v1-protocol.c)
add_custom_command(OUTPUT ${WAYLAND_OUTPUT_DIR}/xdg-shell-protocol.h
COMMAND ${WaylandScanner} client-header ${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml ${WAYLAND_OUTPUT_DIR}/xdg-shell-protocol.h)
add_custom_command(OUTPUT ${WAYLAND_OUTPUT_DIR}/xdg-shell-protocol.c
COMMAND ${WaylandScanner} private-code ${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml ${WAYLAND_OUTPUT_DIR}/xdg-shell-protocol.c)
add_compile_definitions(ENABLE_WAYLAND)
include_directories(${WAYLAND_OUTPUT_DIR})
set(WAYLAND_LIBRARIES
pthread
wayland-cursor
wayland-client
wayland-egl
${OPENGL_egl_LIBRARY})
set(WAYLAND_SOURCES
"src/WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.h"
"src/WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.cpp"
"src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.cpp"
"src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h"
"src/WallpaperEngine/Render/Drivers/Output/CWaylandOutput.cpp"
"src/WallpaperEngine/Render/Drivers/Output/CWaylandOutput.h"
"src/WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.cpp"
"src/WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.h"
"src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.cpp"
"src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.h"
"xdg-shell-protocol.c"
"wlr-foreign-toplevel-management-unstable-v1-protocol.c"
"wlr-layer-shell-unstable-v1-protocol.c")
src/WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.h
src/WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.cpp
src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.cpp
src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h
src/WallpaperEngine/Render/Drivers/Output/CWaylandOutput.cpp
src/WallpaperEngine/Render/Drivers/Output/CWaylandOutput.h
src/WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.cpp
src/WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.h
src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.cpp
src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.h
${WAYLAND_OUTPUT_DIR}/xdg-shell-protocol.c
${WAYLAND_OUTPUT_DIR}/xdg-shell-protocol.h
${WAYLAND_OUTPUT_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.c
${WAYLAND_OUTPUT_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.h
${WAYLAND_OUTPUT_DIR}/wlr-layer-shell-unstable-v1-protocol.c
${WAYLAND_OUTPUT_DIR}/wlr-layer-shell-unstable-v1-protocol.h)
endif()
if(X11_FOUND)
set(X11_SUPPORT_FOUND TRUE)
if(NOT X11_Xrandr_FOUND)
message(WARNING "X11 support disabled. Xrandr package is missing")
set(X11_SUPPORT_FOUND FALSE)
endif()
if(NOT X11_xf86vmode_FOUND)
elseif(NOT X11_xf86vmode_FOUND)
message(WARNING "X11 support disabled. Xxf86vm package is missing")
set(X11_SUPPORT_FOUND FALSE)
else()
message("X11 support enabled")
set(X11_SUPPORT_FOUND TRUE)
endif()
endif()
@ -196,26 +191,78 @@ if(X11_SUPPORT_FOUND)
include_directories(${X11_INCLUDE_DIR} ${XRANDR_INCLUDE_DIR})
add_compile_definitions(ENABLE_X11)
set(X11_LIBRARIES
${X11_LIBRARIES}
${X11_Xrandr_LIB})
set(X11_SOURCES
"src/WallpaperEngine/Render/Drivers/Output/CX11Output.cpp"
"src/WallpaperEngine/Render/Drivers/Output/CX11Output.h"
"src/WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.cpp"
"src/WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h")
src/WallpaperEngine/Render/Drivers/Output/CX11Output.cpp
src/WallpaperEngine/Render/Drivers/Output/CX11Output.h
src/WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.cpp
src/WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h)
SET(X11_INCLUDES
${X11_INCLUDE_DIR}
${XRANDR_INCLUDE_DIR})
set(CMAKE_REQUIRED_LIBRARIES ${X11_LIBRARIES})
# make sure some of the X11 functions we'll use are available
check_function_exists(XSetIOErrorExitHandler HAVE_XSETIOERROREXITHANDLER)
if(HAVE_XSETIOERROREXITHANDLER)
add_compile_definitions(HAVE_XSETIOERROREXITHANDLER=1)
endif()
endif()
if(DEMOMODE)
set(DEMOMODE_SOURCES
src/recording.cpp
src/recording.h)
message(WARNING "Enabling demo mode will automatically record 5 seconds and stop the software. This is used internally to produce the video seen on the website as a sort of status report")
endif()
if(NOT WAYLAND_SUPPORT_FOUND AND NOT X11_SUPPORT_FOUND)
message(WARNING "No window server detected at build time. You will only be able to preview backgrounds")
endif()
add_library(ceflib SHARED IMPORTED)
set_target_properties(ceflib
PROPERTIES IMPORTED_LOCATION ${TARGET_OUTPUT_DIRECTORY}/libcef.so)
ADD_LOGICAL_TARGET(libcef_lib "${CEF_LIB_DEBUG}" "${CEF_LIB_RELEASE}")
PRINT_CEF_CONFIG()
include_directories(
src
src/External/glslang-WallpaperEngine
src/External/SPIRV-Cross-WallpaperEngine
src/External/MimeTypes
src/External/json/include
src/External/stb
src/External/kissfft
${MPV_INCLUDE_DIR}
${GLEW_INCLUDE_DIR}
${LZ4_INCLUDE_DIR}
${SDL2_INCLUDE_DIRS}
${FFMPEG_INCLUDE_DIR}
${PULSEAUDIO_INCLUDE_DIR}
${CEF_INCLUDE_PATH}
${CMAKE_SOURCE_DIR}
${X11_INCLUDES})
add_executable(
linux-wallpaperengine
main.cpp
src/main.cpp
src/External/Android/fft.cpp
src/External/Android/fft.h
src/External/MimeTypes/MimeTypes.cpp
src/External/MimeTypes/MimeTypes.h
src/Steam/FileSystem/FileSystem.h
src/Steam/FileSystem/FileSystem.cpp
src/Steam/FileSystem/FileSystem.h
src/WallpaperEngine/Logging/CLog.cpp
src/WallpaperEngine/Logging/CLog.h
src/WallpaperEngine/PrettyPrinter/CPrettyPrinter.cpp
src/WallpaperEngine/PrettyPrinter/CPrettyPrinter.h
src/WallpaperEngine/Application/CApplicationContext.cpp
src/WallpaperEngine/Application/CApplicationContext.h
src/WallpaperEngine/Application/CWallpaperApplication.cpp
@ -281,12 +328,19 @@ add_executable(
src/WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector4.h
src/WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector4.cpp
src/WallpaperEngine/Render/Shaders/Compiler.h
src/WallpaperEngine/Render/Shaders/Compiler.cpp
src/WallpaperEngine/Render/Shaders/CShader.h
src/WallpaperEngine/Render/Shaders/CShader.cpp
src/WallpaperEngine/Render/Shaders/CShaderUnit.cpp
src/WallpaperEngine/Render/Shaders/CShaderUnit.h
src/WallpaperEngine/Render/Shaders/CGLSLContext.cpp
src/WallpaperEngine/Render/Shaders/CGLSLContext.h
src/WallpaperEngine/Render/Helpers/CContextAware.cpp
src/WallpaperEngine/Render/Helpers/CContextAware.h
src/WallpaperEngine/Render/Drivers/CVideoFactories.cpp
src/WallpaperEngine/Render/Drivers/CVideoFactories.h
src/WallpaperEngine/Render/Drivers/Detectors/CFullScreenDetector.cpp
src/WallpaperEngine/Render/Drivers/Detectors/CFullScreenDetector.h
@ -339,11 +393,23 @@ add_executable(
src/WallpaperEngine/Render/Objects/Effects/CMaterial.h
src/WallpaperEngine/Render/Objects/Effects/CMaterial.cpp
src/WallpaperEngine/WebBrowser/CEF/CRenderHandler.cpp
src/WallpaperEngine/WebBrowser/CEF/CRenderHandler.h
src/WallpaperEngine/WebBrowser/CEF/CBrowserClient.cpp
src/WallpaperEngine/WebBrowser/CEF/CBrowserClient.h
src/WallpaperEngine/WebBrowser/CEF/CBrowserApp.cpp
src/WallpaperEngine/WebBrowser/CEF/CBrowserApp.h
src/WallpaperEngine/WebBrowser/CEF/CSubprocessApp.cpp
src/WallpaperEngine/WebBrowser/CEF/CSubprocessApp.h
src/WallpaperEngine/WebBrowser/CEF/CWPSchemeHandlerFactory.cpp
src/WallpaperEngine/WebBrowser/CEF/CWPSchemeHandlerFactory.h
src/WallpaperEngine/WebBrowser/CEF/CWPSchemeHandler.cpp
src/WallpaperEngine/WebBrowser/CEF/CWPSchemeHandler.h
src/WallpaperEngine/WebBrowser/CWebBrowserContext.cpp
src/WallpaperEngine/WebBrowser/CWebBrowserContext.h
src/WallpaperEngine/FileSystem/FileSystem.cpp
src/WallpaperEngine/FileSystem/FileSystem.h
src/WallpaperEngine/Core/DynamicValues/CDynamicValue.cpp
src/WallpaperEngine/Core/DynamicValues/CDynamicValue.h
src/WallpaperEngine/Core/UserSettings/CUserSettingValue.cpp
src/WallpaperEngine/Core/UserSettings/CUserSettingValue.h
@ -403,12 +469,16 @@ add_executable(
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstant.cpp
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantFloat.h
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantFloat.cpp
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector2.h
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector2.cpp
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector3.h
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector3.cpp
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector4.h
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector4.cpp
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantInteger.h
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantInteger.cpp
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantProperty.h
src/WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantProperty.cpp
src/WallpaperEngine/Core/Objects/Particles/CControlPoint.cpp
src/WallpaperEngine/Core/Objects/Particles/CControlPoint.h
@ -442,17 +512,8 @@ add_executable(
${WAYLAND_SOURCES}
${X11_SOURCES}
)
${DEMOMODE_SOURCES})
COPY_FILES(linux-wallpaperengine "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${TARGET_OUTPUT_DIRECTORY}")
COPY_FILES(linux-wallpaperengine "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${TARGET_OUTPUT_DIRECTORY}")
SET_EXECUTABLE_TARGET_PROPERTIES(linux-wallpaperengine)
add_dependencies(linux-wallpaperengine libcef_dll_wrapper)
# Need to remove libvulkan, otherwise will get error on linking:
# /usr/bin/ld: /usr/lib/libmpv.so: undefined reference to `vkCreateXlibSurfaceKHR'
file(REMOVE "${CEF_BINARY_DIR_DEBUG}/libvulkan.so.1" "${CEF_BINARY_DIR_RELEASE}/libvulkan.so.1")
target_link_libraries (linux-wallpaperengine PUBLIC
${OPENGL_LIBRARIES}
${GLEW_LIBRARIES}
@ -463,39 +524,26 @@ target_link_libraries (linux-wallpaperengine PUBLIC
${FFMPEG_LIBRARIES}
${MPV_LIBRARY}
${PULSEAUDIO_LIBRARY}
${WAYLAND_LIBRARIES}
${X11_LIBRARIES}
kissfft
glslang
spirv-cross-core
spirv-cross-glsl
glfw
libcef_lib libcef_dll_wrapper)
libcef_lib
libcef_dll_wrapper
argparse)
COPY_FILES(linux-wallpaperengine "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${TARGET_OUTPUT_DIRECTORY}")
COPY_FILES(linux-wallpaperengine "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${TARGET_OUTPUT_DIRECTORY}")
# remove the vulkan lib as chromium includes a broken libvulkan.so.1 with it
file(REMOVE "${CEF_BINARY_DIR}/libvulkan.so.1")
if (WAYLAND_SUPPORT_FOUND)
target_link_libraries(linux-wallpaperengine PUBLIC
pthread
wayland-cursor
wayland-client
wayland-egl
${OPENGL_egl_LIBRARY})
endif()
if(X11_SUPPORT_FOUND)
target_link_libraries (linux-wallpaperengine PUBLIC
${X11_LIBRARIES} ${X11_Xrandr_LIB})
set(CMAKE_REQUIRED_LIBRARIES ${X11_LIBRARIES})
# make sure some of the X11 functions we'll use are available
check_function_exists(XSetIOErrorExitHandler HAVE_XSETIOERROREXITHANDLER)
if(HAVE_XSETIOERROREXITHANDLER)
add_compile_definitions(HAVE_XSETIOERROREXITHANDLER=1)
endif()
endif()
if(NOT WAYLAND_SUPPORT_FOUND AND NOT X11_SUPPORT_FOUND)
message(WARNING "No window server detected at build time. You will only be able to preview backgrounds")
endif()
add_dependencies(linux-wallpaperengine libcef_dll_wrapper glslang spirv-cross-core spirv-cross-glsl)
# custom version of cef's SET_EXECUTABLE_TARGET_PROPERTIES that fixes C files being compiled with the wrong parameters
REPLACED_SET_EXECUTABLE_TARGET_PROPERTIES(linux-wallpaperengine)
# set some install parameters if not in debug mode
install(DIRECTORY ${TARGET_OUTPUT_DIRECTORY}/ DESTINATION .)
install(FILES ${TARGET_OUTPUT_DIRECTORY}/${PROJECT_NAME} PERMISSIONS OWNER_READ OWNER_WRITE WORLD_EXECUTE WORLD_READ GROUP_READ DESTINATION .)
install(DIRECTORY share/ DESTINATION ./share)

View File

@ -46,3 +46,83 @@ function(DownloadCEF platform version download_dir)
)
endif()
endfunction()
# Set common target properties. Use SET_LIBRARY_TARGET_PROPERTIES() or
# SET_EXECUTABLE_TARGET_PROPERTIES() instead of calling this macro directly.
macro(REPLACED_SET_COMMON_TARGET_PROPERTIES target)
# Compile flags. (MODIFIED FOR C/C++ SEPARATION)
target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:C>:${CEF_COMPILER_FLAGS}>)
target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${CEF_CXX_COMPILER_FLAGS}>)
target_compile_options(${target} PRIVATE $<$<CONFIG:Debug>:${CEF_COMPILER_FLAGS_DEBUG} ${CEF_CXX_COMPILER_FLAGS_DEBUG}>)
target_compile_options(${target} PRIVATE $<$<CONFIG:Release>:${CEF_COMPILER_FLAGS_RELEASE} ${CEF_CXX_COMPILER_FLAGS_RELEASE}>)
# Compile definitions.
target_compile_definitions(${target} PRIVATE ${CEF_COMPILER_DEFINES})
target_compile_definitions(${target} PRIVATE $<$<CONFIG:Debug>:${CEF_COMPILER_DEFINES_DEBUG}>)
target_compile_definitions(${target} PRIVATE $<$<CONFIG:Release>:${CEF_COMPILER_DEFINES_RELEASE}>)
# Include directories.
target_include_directories(${target} PRIVATE ${CEF_INCLUDE_PATH})
# Linker flags.
if(CEF_LINKER_FLAGS)
string(REPLACE ";" " " _flags_str "${CEF_LINKER_FLAGS}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS ${_flags_str})
endif()
if(CEF_LINKER_FLAGS_DEBUG)
string(REPLACE ";" " " _flags_str "${CEF_LINKER_FLAGS_DEBUG}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS_DEBUG ${_flags_str})
endif()
if(CEF_LINKER_FLAGS_RELEASE)
string(REPLACE ";" " " _flags_str "${CEF_LINKER_FLAGS_RELEASE}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS_RELEASE ${_flags_str})
endif()
if(OS_MAC)
# Set Xcode target properties.
set_target_properties(${target} PROPERTIES
XCODE_ATTRIBUTE_ALWAYS_SEARCH_USER_PATHS NO
XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "gnu++11" # -std=gnu++11
XCODE_ATTRIBUTE_CLANG_LINK_OBJC_RUNTIME NO # -fno-objc-link-runtime
XCODE_ATTRIBUTE_CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS YES # -Wobjc-missing-property-synthesis
XCODE_ATTRIBUTE_COPY_PHASE_STRIP NO
XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING[variant=Release] YES # -Wl,-dead_strip
XCODE_ATTRIBUTE_GCC_C_LANGUAGE_STANDARD "c99" # -std=c99
XCODE_ATTRIBUTE_GCC_CW_ASM_SYNTAX NO # No -fasm-blocks
XCODE_ATTRIBUTE_GCC_DYNAMIC_NO_PIC NO
XCODE_ATTRIBUTE_GCC_ENABLE_CPP_EXCEPTIONS NO # -fno-exceptions
XCODE_ATTRIBUTE_GCC_ENABLE_CPP_RTTI NO # -fno-rtti
XCODE_ATTRIBUTE_GCC_ENABLE_PASCAL_STRINGS NO # No -mpascal-strings
XCODE_ATTRIBUTE_GCC_INLINES_ARE_PRIVATE_EXTERN YES # -fvisibility-inlines-hidden
XCODE_ATTRIBUTE_GCC_OBJC_CALL_CXX_CDTORS YES # -fobjc-call-cxx-cdtors
XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN YES # -fvisibility=hidden
XCODE_ATTRIBUTE_GCC_THREADSAFE_STATICS NO # -fno-threadsafe-statics
XCODE_ATTRIBUTE_GCC_TREAT_WARNINGS_AS_ERRORS YES # -Werror
XCODE_ATTRIBUTE_GCC_VERSION "com.apple.compilers.llvm.clang.1_0"
XCODE_ATTRIBUTE_GCC_WARN_ABOUT_MISSING_NEWLINE YES # -Wnewline-eof
XCODE_ATTRIBUTE_USE_HEADERMAP NO
OSX_ARCHITECTURES_DEBUG "${CMAKE_OSX_ARCHITECTURES}"
OSX_ARCHITECTURES_RELEASE "${CMAKE_OSX_ARCHITECTURES}"
)
endif()
endmacro()
# Set executable-specific properties.
macro(REPLACED_SET_EXECUTABLE_TARGET_PROPERTIES target)
REPLACED_SET_COMMON_TARGET_PROPERTIES(${target})
# Executable linker flags.
if(CEF_EXE_LINKER_FLAGS)
string(REPLACE ";" " " _flags_str "${CEF_EXE_LINKER_FLAGS}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS ${_flags_str})
endif()
if(CEF_EXE_LINKER_FLAGS_DEBUG)
string(REPLACE ";" " " _flags_str "${CEF_EXE_LINKER_FLAGS_DEBUG}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS_DEBUG ${_flags_str})
endif()
if(CEF_EXE_LINKER_FLAGS_RELEASE)
string(REPLACE ";" " " _flags_str "${CEF_EXE_LINKER_FLAGS_RELEASE}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS_RELEASE ${_flags_str})
endif()
endmacro()

View File

@ -1,39 +0,0 @@
# Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
# reserved. Use of this source code is governed by a BSD-style license that
# can be found in the LICENSE file.
#
# This file is the CEF CMake configuration entry point and should be loaded
# using `find_package(CEF REQUIRED)`. See the top-level CMakeLists.txt file
# included with the CEF binary distribution for usage information.
#
# Find the CEF binary distribution root directory.
set(_CEF_ROOT "")
if(CEF_ROOT AND IS_DIRECTORY "${CEF_ROOT}")
set(_CEF_ROOT "${CEF_ROOT}")
set(_CEF_ROOT_EXPLICIT 1)
else()
set(_ENV_CEF_ROOT "")
if(DEFINED ENV{CEF_ROOT})
file(TO_CMAKE_PATH "$ENV{CEF_ROOT}" _ENV_CEF_ROOT)
endif()
if(_ENV_CEF_ROOT AND IS_DIRECTORY "${_ENV_CEF_ROOT}")
set(_CEF_ROOT "${_ENV_CEF_ROOT}")
set(_CEF_ROOT_EXPLICIT 1)
endif()
unset(_ENV_CEF_ROOT)
endif()
if(NOT DEFINED _CEF_ROOT_EXPLICIT)
message(FATAL_ERROR "Must specify a CEF_ROOT value via CMake or environment variable.")
endif()
if(NOT IS_DIRECTORY "${_CEF_ROOT}/cmake")
message(FATAL_ERROR "No CMake bootstrap found for CEF binary distribution at: ${CEF_ROOT}.")
endif()
# Execute additional cmake files from the CEF binary distribution.
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${_CEF_ROOT}/cmake")
include("cef_variables")
include("cef_macros")

View File

@ -1,387 +0,0 @@
# Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
# reserved. Use of this source code is governed by a BSD-style license that
# can be found in the LICENSE file.
# Must be loaded via FindCEF.cmake.
if(NOT DEFINED _CEF_ROOT_EXPLICIT)
message(FATAL_ERROR "Use find_package(CEF) to load this file.")
endif()
#
# Shared macros.
#
# Print the current CEF configuration.
macro(PRINT_CEF_CONFIG)
message(STATUS "*** CEF CONFIGURATION SETTINGS ***")
message(STATUS "Generator: ${CMAKE_GENERATOR}")
message(STATUS "Platform: ${CMAKE_SYSTEM_NAME}")
message(STATUS "Project architecture: ${PROJECT_ARCH}")
if(GEN_NINJA OR GEN_MAKEFILES)
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
endif()
message(STATUS "Binary distribution root: ${_CEF_ROOT}")
if(OS_MAC)
message(STATUS "Base SDK: ${CMAKE_OSX_SYSROOT}")
message(STATUS "Target SDK: ${CEF_TARGET_SDK}")
endif()
if(OS_WINDOWS)
message(STATUS "Visual Studio ATL support: ${USE_ATL}")
endif()
message(STATUS "CEF sandbox: ${USE_SANDBOX}")
set(_libraries ${CEF_STANDARD_LIBS})
if(OS_WINDOWS AND USE_SANDBOX)
list(APPEND _libraries ${CEF_SANDBOX_STANDARD_LIBS})
endif()
message(STATUS "Standard libraries: ${_libraries}")
message(STATUS "Compile defines: ${CEF_COMPILER_DEFINES}")
message(STATUS "Compile defines (Debug): ${CEF_COMPILER_DEFINES_DEBUG}")
message(STATUS "Compile defines (Release): ${CEF_COMPILER_DEFINES_RELEASE}")
message(STATUS "C compile flags: ${CEF_COMPILER_FLAGS} ${CEF_C_COMPILER_FLAGS}")
message(STATUS "C compile flags (Debug): ${CEF_COMPILER_FLAGS_DEBUG} ${CEF_C_COMPILER_FLAGS_DEBUG}")
message(STATUS "C compile flags (Release): ${CEF_COMPILER_FLAGS_RELEASE} ${CEF_C_COMPILER_FLAGS_RELEASE}")
message(STATUS "C++ compile flags: ${CEF_COMPILER_FLAGS} ${CEF_CXX_COMPILER_FLAGS}")
message(STATUS "C++ compile flags (Debug): ${CEF_COMPILER_FLAGS_DEBUG} ${CEF_CXX_COMPILER_FLAGS_DEBUG}")
message(STATUS "C++ compile flags (Release): ${CEF_COMPILER_FLAGS_RELEASE} ${CEF_CXX_COMPILER_FLAGS_RELEASE}")
message(STATUS "Exe link flags: ${CEF_LINKER_FLAGS} ${CEF_EXE_LINKER_FLAGS}")
message(STATUS "Exe link flags (Debug): ${CEF_LINKER_FLAGS_DEBUG} ${CEF_EXE_LINKER_FLAGS_DEBUG}")
message(STATUS "Exe link flags (Release): ${CEF_LINKER_FLAGS_RELEASE} ${CEF_EXE_LINKER_FLAGS_RELEASE}")
message(STATUS "Shared link flags: ${CEF_LINKER_FLAGS} ${CEF_SHARED_LINKER_FLAGS}")
message(STATUS "Shared link flags (Debug): ${CEF_LINKER_FLAGS_DEBUG} ${CEF_SHARED_LINKER_FLAGS_DEBUG}")
message(STATUS "Shared link flags (Release): ${CEF_LINKER_FLAGS_RELEASE} ${CEF_SHARED_LINKER_FLAGS_RELEASE}")
if(OS_LINUX OR OS_WINDOWS)
message(STATUS "CEF Binary files: ${CEF_BINARY_FILES}")
message(STATUS "CEF Resource files: ${CEF_RESOURCE_FILES}")
endif()
endmacro()
# Append platform specific sources to a list of sources.
macro(APPEND_PLATFORM_SOURCES name_of_list)
if(OS_LINUX AND ${name_of_list}_LINUX)
list(APPEND ${name_of_list} ${${name_of_list}_LINUX})
endif()
if(OS_POSIX AND ${name_of_list}_POSIX)
list(APPEND ${name_of_list} ${${name_of_list}_POSIX})
endif()
if(OS_WINDOWS AND ${name_of_list}_WINDOWS)
list(APPEND ${name_of_list} ${${name_of_list}_WINDOWS})
endif()
if(OS_MAC AND ${name_of_list}_MAC)
list(APPEND ${name_of_list} ${${name_of_list}_MAC})
endif()
endmacro()
# Determine the target output directory based on platform and generator.
macro(SET_CEF_TARGET_OUT_DIR)
if(GEN_NINJA OR GEN_MAKEFILES)
# By default Ninja and Make builds don't create a subdirectory named after
# the configuration.
set(CEF_TARGET_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}")
# Output binaries (executables, libraries) to the correct directory.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CEF_TARGET_OUT_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CEF_TARGET_OUT_DIR})
else()
set(CEF_TARGET_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>")
endif()
endmacro()
# Copy a list of files from one directory to another. Relative file paths are maintained.
macro(COPY_FILES target file_list source_dir target_dir)
foreach(FILENAME ${file_list})
set(source_file ${source_dir}/${FILENAME})
# Remove the target file path component.
get_filename_component(target_name ${FILENAME} NAME)
set(target_file ${target_dir}/${target_name})
COPY_SINGLE_FILE(${target} ${source_file} ${target_file})
endforeach()
endmacro()
# Copy a list of files from one directory to another. Relative file paths are maintained.
macro(COPY_RESOURCES target file_list prefix_list source_dir target_dir)
foreach(FILENAME ${file_list})
set(source_file ${source_dir}/${FILENAME})
# Remove one or more prefixes from the source paths.
set(TARGET_FILENAME "${FILENAME}")
foreach(PREFIX ${prefix_list})
string(REGEX REPLACE "^.*${PREFIX}" "" TARGET_FILENAME ${TARGET_FILENAME})
endforeach()
set(target_file ${target_dir}/${TARGET_FILENAME})
COPY_SINGLE_FILE(${target} ${source_file} ${target_file})
endforeach()
endmacro()
macro(COPY_SINGLE_FILE target source_file target_file)
string(FIND ${source_file} "$<CONFIGURATION>" _pos)
if(NOT ${_pos} EQUAL -1)
# Must test with an actual configuration directory.
string(REPLACE "$<CONFIGURATION>" "Release" existing_source_file ${source_file})
if(NOT EXISTS ${existing_source_file})
string(REPLACE "$<CONFIGURATION>" "Debug" existing_source_file ${source_file})
endif()
else()
set(existing_source_file ${source_file})
endif()
if(IS_DIRECTORY ${existing_source_file})
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory "${source_file}" "${target_file}"
VERBATIM
)
else()
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${source_file}" "${target_file}"
VERBATIM
)
endif()
endmacro()
#
# Linux macros.
#
if(OS_LINUX)
# Use pkg-config to find Linux libraries and update compiler/linker variables.
macro(FIND_LINUX_LIBRARIES libraries)
# Read pkg-config info into variables.
execute_process(COMMAND pkg-config --cflags ${libraries} OUTPUT_VARIABLE FLL_CFLAGS)
execute_process(COMMAND pkg-config --libs-only-L --libs-only-other ${libraries} OUTPUT_VARIABLE FLL_LDFLAGS)
execute_process(COMMAND pkg-config --libs-only-l ${libraries} OUTPUT_VARIABLE FLL_LIBS)
# Strip leading and trailing whitepspace.
STRING(STRIP "${FLL_CFLAGS}" FLL_CFLAGS)
STRING(STRIP "${FLL_LDFLAGS}" FLL_LDFLAGS)
STRING(STRIP "${FLL_LIBS}" FLL_LIBS)
# Convert to a list.
separate_arguments(FLL_CFLAGS)
separate_arguments(FLL_LDFLAGS)
separate_arguments(FLL_LIBS)
# Update build variables.
list(APPEND CEF_C_COMPILER_FLAGS ${FLL_CFLAGS})
list(APPEND CEF_CXX_COMPILER_FLAGS ${FLL_CFLAGS})
list(APPEND CEF_EXE_LINKER_FLAGS ${FLL_LDFLAGS})
list(APPEND CEF_SHARED_LINKER_FLAGS ${FLL_LDFLAGS})
list(APPEND CEF_STANDARD_LIBS ${FLL_LIBS})
endmacro()
# Set SUID permissions on the specified executable.
macro(SET_LINUX_SUID_PERMISSIONS target executable)
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo ""
COMMAND ${CMAKE_COMMAND} -E echo "*** Run the following command manually to set SUID permissions ***"
COMMAND ${CMAKE_COMMAND} -E echo "EXE=\"${executable}\" && sudo -- chown root:root $EXE && sudo -- chmod 4755 $EXE"
COMMAND ${CMAKE_COMMAND} -E echo ""
VERBATIM
)
endmacro()
endif(OS_LINUX)
#
# Mac OS X macros.
#
if(OS_MAC)
# Manually process and copy over resource files.
macro(COPY_MAC_RESOURCES resource_list prefix_list target source_dir app_path)
foreach(FILENAME ${resource_list})
# Remove one or more prefixes from the source paths.
set(TARGET_FILENAME "${FILENAME}")
foreach(PREFIX ${prefix_list})
string(REGEX REPLACE "^.*${PREFIX}" "" TARGET_FILENAME ${TARGET_FILENAME})
endforeach()
# Determine the absolute source and target paths.
set(TARGET_PATH "${app_path}/Contents/Resources/${TARGET_FILENAME}")
if(IS_ABSOLUTE ${FILENAME})
set(SOURCE_PATH ${FILENAME})
else()
set(SOURCE_PATH "${source_dir}/${FILENAME}")
endif()
if(${FILENAME} MATCHES ".xib$")
# Change the target file extension.
string(REGEX REPLACE ".xib$" ".nib" TARGET_PATH ${TARGET_PATH})
get_filename_component(TARGET_DIRECTORY ${TARGET_PATH} PATH)
add_custom_command(
TARGET ${target}
POST_BUILD
# Create the target directory.
COMMAND ${CMAKE_COMMAND} -E make_directory "${TARGET_DIRECTORY}"
# Compile the XIB file to a NIB.
COMMAND /usr/bin/ibtool --output-format binary1 --compile "${TARGET_PATH}" "${SOURCE_PATH}"
VERBATIM
)
elseif(NOT ${TARGET_FILENAME} STREQUAL "Info.plist")
# Copy the file as-is.
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_PATH}" "${TARGET_PATH}"
VERBATIM
)
endif()
endforeach()
endmacro()
endif(OS_MAC)
#
# Windows macros.
#
if(OS_WINDOWS)
# Add custom manifest files to an executable target.
macro(ADD_WINDOWS_MANIFEST manifest_path target extension)
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND "mt.exe" -nologo
-manifest \"${manifest_path}/${target}.${extension}.manifest\" \"${manifest_path}/compatibility.manifest\"
-outputresource:"${CEF_TARGET_OUT_DIR}/${target}.${extension}"\;\#1
COMMENT "Adding manifest..."
)
endmacro()
endif(OS_WINDOWS)
#
# Target configuration macros.
#
# Add a logical target that can be used to link the specified libraries into an
# executable target.
macro(ADD_LOGICAL_TARGET target debug_lib release_lib)
add_library(${target} ${CEF_LIBTYPE} IMPORTED)
set_target_properties(${target} PROPERTIES
IMPORTED_LOCATION "${release_lib}"
IMPORTED_LOCATION_DEBUG "${debug_lib}"
IMPORTED_LOCATION_RELEASE "${release_lib}"
)
endmacro()
# Set common target properties. Use SET_LIBRARY_TARGET_PROPERTIES() or
# SET_EXECUTABLE_TARGET_PROPERTIES() instead of calling this macro directly.
macro(SET_COMMON_TARGET_PROPERTIES target)
# Compile flags.
target_compile_options(${target} PRIVATE ${CEF_COMPILER_FLAGS} ${CEF_CXX_COMPILER_FLAGS})
target_compile_options(${target} PRIVATE $<$<CONFIG:Debug>:${CEF_COMPILER_FLAGS_DEBUG} ${CEF_CXX_COMPILER_FLAGS_DEBUG}>)
target_compile_options(${target} PRIVATE $<$<CONFIG:Release>:${CEF_COMPILER_FLAGS_RELEASE} ${CEF_CXX_COMPILER_FLAGS_RELEASE}>)
# Compile definitions.
target_compile_definitions(${target} PRIVATE ${CEF_COMPILER_DEFINES})
target_compile_definitions(${target} PRIVATE $<$<CONFIG:Debug>:${CEF_COMPILER_DEFINES_DEBUG}>)
target_compile_definitions(${target} PRIVATE $<$<CONFIG:Release>:${CEF_COMPILER_DEFINES_RELEASE}>)
# Include directories.
target_include_directories(${target} PRIVATE ${CEF_INCLUDE_PATH})
# Linker flags.
if(CEF_LINKER_FLAGS)
string(REPLACE ";" " " _flags_str "${CEF_LINKER_FLAGS}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS ${_flags_str})
endif()
if(CEF_LINKER_FLAGS_DEBUG)
string(REPLACE ";" " " _flags_str "${CEF_LINKER_FLAGS_DEBUG}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS_DEBUG ${_flags_str})
endif()
if(CEF_LINKER_FLAGS_RELEASE)
string(REPLACE ";" " " _flags_str "${CEF_LINKER_FLAGS_RELEASE}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS_RELEASE ${_flags_str})
endif()
if(OS_MAC)
# Set Xcode target properties.
set_target_properties(${target} PROPERTIES
XCODE_ATTRIBUTE_ALWAYS_SEARCH_USER_PATHS NO
XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "gnu++11" # -std=gnu++11
XCODE_ATTRIBUTE_CLANG_LINK_OBJC_RUNTIME NO # -fno-objc-link-runtime
XCODE_ATTRIBUTE_CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS YES # -Wobjc-missing-property-synthesis
XCODE_ATTRIBUTE_COPY_PHASE_STRIP NO
XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING[variant=Release] YES # -Wl,-dead_strip
XCODE_ATTRIBUTE_GCC_C_LANGUAGE_STANDARD "c99" # -std=c99
XCODE_ATTRIBUTE_GCC_CW_ASM_SYNTAX NO # No -fasm-blocks
XCODE_ATTRIBUTE_GCC_DYNAMIC_NO_PIC NO
XCODE_ATTRIBUTE_GCC_ENABLE_CPP_EXCEPTIONS NO # -fno-exceptions
XCODE_ATTRIBUTE_GCC_ENABLE_CPP_RTTI NO # -fno-rtti
XCODE_ATTRIBUTE_GCC_ENABLE_PASCAL_STRINGS NO # No -mpascal-strings
XCODE_ATTRIBUTE_GCC_INLINES_ARE_PRIVATE_EXTERN YES # -fvisibility-inlines-hidden
XCODE_ATTRIBUTE_GCC_OBJC_CALL_CXX_CDTORS YES # -fobjc-call-cxx-cdtors
XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN YES # -fvisibility=hidden
XCODE_ATTRIBUTE_GCC_THREADSAFE_STATICS NO # -fno-threadsafe-statics
XCODE_ATTRIBUTE_GCC_TREAT_WARNINGS_AS_ERRORS YES # -Werror
XCODE_ATTRIBUTE_GCC_VERSION "com.apple.compilers.llvm.clang.1_0"
XCODE_ATTRIBUTE_GCC_WARN_ABOUT_MISSING_NEWLINE YES # -Wnewline-eof
XCODE_ATTRIBUTE_USE_HEADERMAP NO
OSX_ARCHITECTURES_DEBUG "${CMAKE_OSX_ARCHITECTURES}"
OSX_ARCHITECTURES_RELEASE "${CMAKE_OSX_ARCHITECTURES}"
)
endif()
endmacro()
# Set library-specific properties.
macro(SET_LIBRARY_TARGET_PROPERTIES target)
SET_COMMON_TARGET_PROPERTIES(${target})
# Shared library linker flags.
if(CEF_SHARED_LINKER_FLAGS)
string(REPLACE ";" " " _flags_str "${CEF_SHARED_LINKER_FLAGS}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS ${_flags_str})
endif()
if(CEF_SHARED_LINKER_FLAGS_DEBUG)
string(REPLACE ";" " " _flags_str "${CEF_SHARED_LINKER_FLAGS_DEBUG}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS_DEBUG ${_flags_str})
endif()
if(CEF_SHARED_LINKER_FLAGS_RELEASE)
string(REPLACE ";" " " _flags_str "${CEF_SHARED_LINKER_FLAGS_RELEASE}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS_RELEASE ${_flags_str})
endif()
endmacro()
# Set executable-specific properties.
macro(SET_EXECUTABLE_TARGET_PROPERTIES target)
SET_COMMON_TARGET_PROPERTIES(${target})
# Executable linker flags.
if(CEF_EXE_LINKER_FLAGS)
string(REPLACE ";" " " _flags_str "${CEF_EXE_LINKER_FLAGS}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS ${_flags_str})
endif()
if(CEF_EXE_LINKER_FLAGS_DEBUG)
string(REPLACE ";" " " _flags_str "${CEF_EXE_LINKER_FLAGS_DEBUG}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS_DEBUG ${_flags_str})
endif()
if(CEF_EXE_LINKER_FLAGS_RELEASE)
string(REPLACE ";" " " _flags_str "${CEF_EXE_LINKER_FLAGS_RELEASE}")
set_property(TARGET ${target} PROPERTY LINK_FLAGS_RELEASE ${_flags_str})
endif()
endmacro()

View File

@ -1,582 +0,0 @@
# Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
# reserved. Use of this source code is governed by a BSD-style license that
# can be found in the LICENSE file.
# Must be loaded via FindCEF.cmake.
if(NOT DEFINED _CEF_ROOT_EXPLICIT)
message(FATAL_ERROR "Use find_package(CEF) to load this file.")
endif()
#
# Shared configuration.
#
# Determine the platform.
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
set(OS_MAC 1)
set(OS_MACOSX 1) # For backwards compatibility.
set(OS_POSIX 1)
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
set(OS_LINUX 1)
set(OS_POSIX 1)
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
set(OS_WINDOWS 1)
endif()
# Determine the project architecture.
if(NOT DEFINED PROJECT_ARCH)
if(("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64") OR
("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM64"))
set(PROJECT_ARCH "arm64")
elseif(CMAKE_SIZEOF_VOID_P MATCHES 8)
set(PROJECT_ARCH "x86_64")
else()
set(PROJECT_ARCH "x86")
endif()
endif()
if(${CMAKE_GENERATOR} STREQUAL "Ninja")
set(GEN_NINJA 1)
elseif(${CMAKE_GENERATOR} STREQUAL "Unix Makefiles")
set(GEN_MAKEFILES 1)
endif()
# Determine the build type.
if(NOT CMAKE_BUILD_TYPE AND (GEN_NINJA OR GEN_MAKEFILES))
# CMAKE_BUILD_TYPE should be specified when using Ninja or Unix Makefiles.
set(CMAKE_BUILD_TYPE Release)
message(WARNING "No CMAKE_BUILD_TYPE value selected, using ${CMAKE_BUILD_TYPE}")
endif()
# Path to the include directory.
set(CEF_INCLUDE_PATH "${_CEF_ROOT}")
# Path to the libcef_dll_wrapper target.
set(CEF_LIBCEF_DLL_WRAPPER_PATH "${_CEF_ROOT}/libcef_dll")
# Shared compiler/linker flags.
list(APPEND CEF_COMPILER_DEFINES
# Allow C++ programs to use stdint.h macros specified in the C99 standard that aren't
# in the C++ standard (e.g. UINT8_MAX, INT64_MIN, etc)
__STDC_CONSTANT_MACROS __STDC_FORMAT_MACROS
)
# Configure use of the sandbox.
option(USE_SANDBOX "Enable or disable use of the sandbox." ON)
#
# Linux configuration.
#
if(OS_LINUX)
# Platform-specific compiler/linker flags.
set(CEF_LIBTYPE SHARED)
list(APPEND CEF_COMPILER_FLAGS
-fno-strict-aliasing # Avoid assumptions regarding non-aliasing of objects of different types
-fPIC # Generate position-independent code for shared libraries
-fstack-protector # Protect some vulnerable functions from stack-smashing (security feature)
-funwind-tables # Support stack unwinding for backtrace()
-fvisibility=hidden # Give hidden visibility to declarations that are not explicitly marked as visible
--param=ssp-buffer-size=4 # Set the minimum buffer size protected by SSP (security feature, related to stack-protector)
-pipe # Use pipes rather than temporary files for communication between build stages
-pthread # Use the pthread library
# -Wall # Enable all warnings
# -Werror # Treat warnings as errors
# -Wno-missing-field-initializers # Don't warn about missing field initializers
# -Wno-unused-parameter # Don't warn about unused parameters
# -Wno-error=comment # Don't warn about code in comments
# -Wno-comment # Don't warn about code in comments
# -Wno-deprecated-declarations # Don't warn about using deprecated methods
)
list(APPEND CEF_C_COMPILER_FLAGS
-std=c99 # Use the C99 language standard
)
list(APPEND CEF_CXX_COMPILER_FLAGS
# -fno-exceptions # Disable exceptions
# -fno-rtti # Disable real-time type information
-fno-threadsafe-statics # Don't generate thread-safe statics
-fvisibility-inlines-hidden # Give hidden visibility to inlined class member functions
-std=c++17 # Use the C++17 language standard
-Wsign-compare # Warn about mixed signed/unsigned type comparisons
)
list(APPEND CEF_COMPILER_FLAGS_DEBUG
-O0 # Disable optimizations
-g # Generate debug information
)
list(APPEND CEF_COMPILER_FLAGS_RELEASE
-O2 # Optimize for maximum speed
-fdata-sections # Enable linker optimizations to improve locality of reference for data sections
-ffunction-sections # Enable linker optimizations to improve locality of reference for function sections
-fno-ident # Ignore the #ident directive
-U_FORTIFY_SOURCE # Undefine _FORTIFY_SOURCE in case it was previously defined
-D_FORTIFY_SOURCE=2 # Add memory and string function protection (security feature, related to stack-protector)
)
list(APPEND CEF_LINKER_FLAGS
-fPIC # Generate position-independent code for shared libraries
-pthread # Use the pthread library
-Wl,--disable-new-dtags # Don't generate new-style dynamic tags in ELF
-Wl,--fatal-warnings # Treat warnings as errors
-Wl,-rpath,. # Set rpath so that libraries can be placed next to the executable
-Wl,-z,noexecstack # Mark the stack as non-executable (security feature)
-Wl,-z,now # Resolve symbols on program start instead of on first use (security feature)
-Wl,-z,relro # Mark relocation sections as read-only (security feature)
)
list(APPEND CEF_LINKER_FLAGS_RELEASE
-Wl,-O1 # Enable linker optimizations
-Wl,--as-needed # Only link libraries that export symbols used by the binary
-Wl,--gc-sections # Remove unused code resulting from -fdata-sections and -function-sections
)
list(APPEND CEF_COMPILER_DEFINES
_FILE_OFFSET_BITS=64 # Allow the Large File Support (LFS) interface to replace the old interface
)
list(APPEND CEF_COMPILER_DEFINES_RELEASE
NDEBUG # Not a debug build
)
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG(-Wno-undefined-var-template COMPILER_SUPPORTS_NO_UNDEFINED_VAR_TEMPLATE)
if(COMPILER_SUPPORTS_NO_UNDEFINED_VAR_TEMPLATE)
list(APPEND CEF_CXX_COMPILER_FLAGS
-Wno-undefined-var-template # Don't warn about potentially uninstantiated static members
)
endif()
CHECK_C_COMPILER_FLAG(-Wno-unused-local-typedefs COMPILER_SUPPORTS_NO_UNUSED_LOCAL_TYPEDEFS)
if(COMPILER_SUPPORTS_NO_UNUSED_LOCAL_TYPEDEFS)
list(APPEND CEF_C_COMPILER_FLAGS
-Wno-unused-local-typedefs # Don't warn about unused local typedefs
)
endif()
CHECK_CXX_COMPILER_FLAG(-Wno-literal-suffix COMPILER_SUPPORTS_NO_LITERAL_SUFFIX)
if(COMPILER_SUPPORTS_NO_LITERAL_SUFFIX)
list(APPEND CEF_CXX_COMPILER_FLAGS
-Wno-literal-suffix # Don't warn about invalid suffixes on literals
)
endif()
CHECK_CXX_COMPILER_FLAG(-Wno-narrowing COMPILER_SUPPORTS_NO_NARROWING)
if(COMPILER_SUPPORTS_NO_NARROWING)
list(APPEND CEF_CXX_COMPILER_FLAGS
-Wno-narrowing # Don't warn about type narrowing
)
endif()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
list(APPEND CEF_CXX_COMPILER_FLAGS
-Wno-attributes # The cfi-icall attribute is not supported by the GNU C++ compiler
)
endif()
if(PROJECT_ARCH STREQUAL "x86_64")
# 64-bit architecture.
list(APPEND CEF_COMPILER_FLAGS
-m64
-march=x86-64
)
list(APPEND CEF_LINKER_FLAGS
-m64
)
elseif(PROJECT_ARCH STREQUAL "x86")
# 32-bit architecture.
list(APPEND CEF_COMPILER_FLAGS
-msse2
-mfpmath=sse
-mmmx
-m32
)
list(APPEND CEF_LINKER_FLAGS
-m32
)
endif()
# Standard libraries.
set(CEF_STANDARD_LIBS
X11
)
# CEF directory paths.
set(CEF_RESOURCE_DIR "${_CEF_ROOT}/Resources")
set(CEF_BINARY_DIR "${_CEF_ROOT}/${CMAKE_BUILD_TYPE}")
set(CEF_BINARY_DIR_DEBUG "${_CEF_ROOT}/Debug")
set(CEF_BINARY_DIR_RELEASE "${_CEF_ROOT}/Release")
# CEF library paths.
set(CEF_LIB_DEBUG "${CEF_BINARY_DIR_DEBUG}/libcef.so")
set(CEF_LIB_RELEASE "${CEF_BINARY_DIR_RELEASE}/libcef.so")
# List of CEF binary files.
set(CEF_BINARY_FILES
chrome-sandbox
libcef.so
libEGL.so
libGLESv2.so
libvk_swiftshader.so
# libvulkan.so.1
snapshot_blob.bin
v8_context_snapshot.bin
vk_swiftshader_icd.json
)
# List of CEF resource files.
set(CEF_RESOURCE_FILES
chrome_100_percent.pak
chrome_200_percent.pak
resources.pak
icudtl.dat
locales
)
if(USE_SANDBOX)
list(APPEND CEF_COMPILER_DEFINES
CEF_USE_SANDBOX # Used by apps to test if the sandbox is enabled
)
endif()
endif()
#
# Mac OS X configuration.
#
if(OS_MAC)
# Platform-specific compiler/linker flags.
# See also Xcode target properties in cef_macros.cmake.
set(CEF_LIBTYPE SHARED)
list(APPEND CEF_COMPILER_FLAGS
-fno-strict-aliasing # Avoid assumptions regarding non-aliasing of objects of different types
-fstack-protector # Protect some vulnerable functions from stack-smashing (security feature)
-funwind-tables # Support stack unwinding for backtrace()
-fvisibility=hidden # Give hidden visibility to declarations that are not explicitly marked as visible
-Wall # Enable all warnings
-Werror # Treat warnings as errors
-Wextra # Enable additional warnings
-Wendif-labels # Warn whenever an #else or an #endif is followed by text
-Wnewline-eof # Warn about no newline at end of file
-Wno-missing-field-initializers # Don't warn about missing field initializers
-Wno-unused-parameter # Don't warn about unused parameters
)
list(APPEND CEF_C_COMPILER_FLAGS
-std=c99 # Use the C99 language standard
)
list(APPEND CEF_CXX_COMPILER_FLAGS
# -fno-exceptions # Disable exceptions
# -fno-rtti # Disable real-time type information
-fno-threadsafe-statics # Don't generate thread-safe statics
-fobjc-call-cxx-cdtors # Call the constructor/destructor of C++ instance variables in ObjC objects
-fvisibility-inlines-hidden # Give hidden visibility to inlined class member functions
-std=c++17 # Use the C++17 language standard
-Wno-narrowing # Don't warn about type narrowing
-Wsign-compare # Warn about mixed signed/unsigned type comparisons
)
list(APPEND CEF_COMPILER_FLAGS_DEBUG
-O0 # Disable optimizations
-g # Generate debug information
)
list(APPEND CEF_COMPILER_FLAGS_RELEASE
-O3 # Optimize for maximum speed plus a few extras
)
list(APPEND CEF_LINKER_FLAGS
-Wl,-search_paths_first # Search for static or shared library versions in the same pass
-Wl,-ObjC # Support creation of ObjC static libraries
-Wl,-pie # Generate position-independent code suitable for executables only
)
list(APPEND CEF_LINKER_FLAGS_RELEASE
-Wl,-dead_strip # Strip dead code
)
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG(-Wno-undefined-var-template COMPILER_SUPPORTS_NO_UNDEFINED_VAR_TEMPLATE)
if(COMPILER_SUPPORTS_NO_UNDEFINED_VAR_TEMPLATE)
list(APPEND CEF_CXX_COMPILER_FLAGS
-Wno-undefined-var-template # Don't warn about potentially uninstantiated static members
)
endif()
# Standard libraries.
set(CEF_STANDARD_LIBS
-lpthread
"-framework Cocoa"
"-framework AppKit"
)
# Find the newest available base SDK.
execute_process(COMMAND xcode-select --print-path OUTPUT_VARIABLE XCODE_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)
foreach(OS_VERSION 10.15 10.14 10.13)
set(SDK "${XCODE_PATH}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${OS_VERSION}.sdk")
if(NOT "${CMAKE_OSX_SYSROOT}" AND EXISTS "${SDK}" AND IS_DIRECTORY "${SDK}")
set(CMAKE_OSX_SYSROOT ${SDK})
endif()
endforeach()
# Target SDK.
set(CEF_TARGET_SDK "10.13")
list(APPEND CEF_COMPILER_FLAGS
-mmacosx-version-min=${CEF_TARGET_SDK}
)
set(CMAKE_OSX_DEPLOYMENT_TARGET ${CEF_TARGET_SDK})
# Target architecture.
if(PROJECT_ARCH STREQUAL "x86_64")
set(CMAKE_OSX_ARCHITECTURES "x86_64")
elseif(PROJECT_ARCH STREQUAL "arm64")
set(CMAKE_OSX_ARCHITECTURES "arm64")
else()
set(CMAKE_OSX_ARCHITECTURES "i386")
endif()
# Prevent Xcode 11 from doing automatic codesigning.
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
# CEF directory paths.
set(CEF_BINARY_DIR "${_CEF_ROOT}/$<CONFIGURATION>")
set(CEF_BINARY_DIR_DEBUG "${_CEF_ROOT}/Debug")
set(CEF_BINARY_DIR_RELEASE "${_CEF_ROOT}/Release")
if(USE_SANDBOX)
list(APPEND CEF_COMPILER_DEFINES
CEF_USE_SANDBOX # Used by apps to test if the sandbox is enabled
)
list(APPEND CEF_STANDARD_LIBS
-lsandbox
)
# CEF sandbox library paths.
set(CEF_SANDBOX_LIB_DEBUG "${CEF_BINARY_DIR_DEBUG}/cef_sandbox.a")
set(CEF_SANDBOX_LIB_RELEASE "${CEF_BINARY_DIR_RELEASE}/cef_sandbox.a")
endif()
# CEF Helper app suffixes.
# Format is "<name suffix>:<target suffix>:<plist suffix>".
set(CEF_HELPER_APP_SUFFIXES
"::"
" (Alerts):_alerts:.alerts"
" (GPU):_gpu:.gpu"
" (Plugin):_plugin:.plugin"
" (Renderer):_renderer:.renderer"
)
endif()
#
# Windows configuration.
#
if(OS_WINDOWS)
if (GEN_NINJA)
# When using the Ninja generator clear the CMake defaults to avoid excessive
# console warnings (see issue #2120).
set(CMAKE_CXX_FLAGS "")
set(CMAKE_CXX_FLAGS_DEBUG "")
set(CMAKE_CXX_FLAGS_RELEASE "")
endif()
if(USE_SANDBOX)
# Check if the current MSVC version is compatible with the cef_sandbox.lib
# static library. We require VS2015 or newer.
if(MSVC_VERSION LESS 1900)
message(WARNING "CEF sandbox is not compatible with the current MSVC version (${MSVC_VERSION})")
set(USE_SANDBOX OFF)
endif()
endif()
# Consumers who run into LNK4099 warnings can pass /Z7 instead (see issue #385).
set(CEF_DEBUG_INFO_FLAG "/Zi" CACHE STRING "Optional flag specifying specific /Z flag to use")
# Consumers using different runtime types may want to pass different flags
set(CEF_RUNTIME_LIBRARY_FLAG "/MT" CACHE STRING "Optional flag specifying which runtime to use")
if (CEF_RUNTIME_LIBRARY_FLAG)
list(APPEND CEF_COMPILER_FLAGS_DEBUG ${CEF_RUNTIME_LIBRARY_FLAG}d)
list(APPEND CEF_COMPILER_FLAGS_RELEASE ${CEF_RUNTIME_LIBRARY_FLAG})
endif()
# Platform-specific compiler/linker flags.
set(CEF_LIBTYPE STATIC)
list(APPEND CEF_COMPILER_FLAGS
/MP # Multiprocess compilation
/Gy # Enable function-level linking
/GR- # Disable run-time type information
/W4 # Warning level 4
/WX # Treat warnings as errors
/wd4100 # Ignore "unreferenced formal parameter" warning
/wd4127 # Ignore "conditional expression is constant" warning
/wd4244 # Ignore "conversion possible loss of data" warning
/wd4324 # Ignore "structure was padded due to alignment specifier" warning
/wd4481 # Ignore "nonstandard extension used: override" warning
/wd4512 # Ignore "assignment operator could not be generated" warning
/wd4701 # Ignore "potentially uninitialized local variable" warning
/wd4702 # Ignore "unreachable code" warning
/wd4996 # Ignore "function or variable may be unsafe" warning
${CEF_DEBUG_INFO_FLAG}
)
list(APPEND CEF_COMPILER_FLAGS_DEBUG
/RTC1 # Disable optimizations
/Od # Enable basic run-time checks
)
list(APPEND CEF_COMPILER_FLAGS_RELEASE
/O2 # Optimize for maximum speed
/Ob2 # Inline any suitable function
/GF # Enable string pooling
)
list(APPEND CEF_CXX_COMPILER_FLAGS
/std:c++17 # Use the C++17 language standard
)
list(APPEND CEF_LINKER_FLAGS_DEBUG
/DEBUG # Generate debug information
)
list(APPEND CEF_EXE_LINKER_FLAGS
/MANIFEST:NO # No default manifest (see ADD_WINDOWS_MANIFEST macro usage)
/LARGEADDRESSAWARE # Allow 32-bit processes to access 3GB of RAM
)
list(APPEND CEF_COMPILER_DEFINES
WIN32 _WIN32 _WINDOWS # Windows platform
UNICODE _UNICODE # Unicode build
# Targeting Windows 10. We can't say `=_WIN32_WINNT_WIN10` here because
# some files do `#if WINVER < 0x0600` without including windows.h before,
# and then _WIN32_WINNT_WIN10 isn't yet known to be 0x0A00.
WINVER=0x0A00
_WIN32_WINNT=0x0A00
NTDDI_VERSION=NTDDI_WIN10_FE
NOMINMAX # Use the standard's templated min/max
WIN32_LEAN_AND_MEAN # Exclude less common API declarations
_HAS_EXCEPTIONS=0 # Disable exceptions
)
list(APPEND CEF_COMPILER_DEFINES_RELEASE
NDEBUG _NDEBUG # Not a debug build
)
if(PROJECT_ARCH STREQUAL "x86")
# Set the initial stack size to 0.5MiB, instead of the 1.5MiB minimum
# needed by CEF's main thread. This saves significant memory on threads
# (like those in the Windows thread pool, and others) whose stack size we
# can only control through this setting. The main thread (in 32-bit builds
# only) uses fibers to switch to a 4MiB stack at runtime via
# CefRunWinMainWithPreferredStackSize().
list(APPEND CEF_EXE_LINKER_FLAGS
/STACK:0x80000
)
else()
# Increase the initial stack size to 8MiB from the default 1MiB.
list(APPEND CEF_EXE_LINKER_FLAGS
/STACK:0x800000
)
endif()
# Standard libraries.
set(CEF_STANDARD_LIBS
comctl32.lib
gdi32.lib
rpcrt4.lib
shlwapi.lib
ws2_32.lib
)
# CEF directory paths.
set(CEF_RESOURCE_DIR "${_CEF_ROOT}/Resources")
set(CEF_BINARY_DIR "${_CEF_ROOT}/$<CONFIGURATION>")
set(CEF_BINARY_DIR_DEBUG "${_CEF_ROOT}/Debug")
set(CEF_BINARY_DIR_RELEASE "${_CEF_ROOT}/Release")
# CEF library paths.
set(CEF_LIB_DEBUG "${CEF_BINARY_DIR_DEBUG}/libcef.lib")
set(CEF_LIB_RELEASE "${CEF_BINARY_DIR_RELEASE}/libcef.lib")
# List of CEF binary files.
set(CEF_BINARY_FILES
chrome_elf.dll
d3dcompiler_47.dll
libcef.dll
libEGL.dll
libGLESv2.dll
snapshot_blob.bin
v8_context_snapshot.bin
vk_swiftshader.dll
vk_swiftshader_icd.json
vulkan-1.dll
)
# List of CEF resource files.
set(CEF_RESOURCE_FILES
chrome_100_percent.pak
chrome_200_percent.pak
resources.pak
icudtl.dat
locales
)
if(USE_SANDBOX)
list(APPEND CEF_COMPILER_DEFINES
PSAPI_VERSION=1 # Required by cef_sandbox.lib
CEF_USE_SANDBOX # Used by apps to test if the sandbox is enabled
)
list(APPEND CEF_COMPILER_DEFINES_DEBUG
_HAS_ITERATOR_DEBUGGING=0 # Disable iterator debugging
)
# Libraries required by cef_sandbox.lib.
set(CEF_SANDBOX_STANDARD_LIBS
Advapi32.lib
dbghelp.lib
Delayimp.lib
ntdll.lib
OleAut32.lib
PowrProf.lib
Propsys.lib
psapi.lib
SetupAPI.lib
Shell32.lib
Shcore.lib
Userenv.lib
version.lib
wbemuuid.lib
WindowsApp.lib
winmm.lib
)
# CEF sandbox library paths.
set(CEF_SANDBOX_LIB_DEBUG "${CEF_BINARY_DIR_DEBUG}/cef_sandbox.lib")
set(CEF_SANDBOX_LIB_RELEASE "${CEF_BINARY_DIR_RELEASE}/cef_sandbox.lib")
endif()
# Configure use of ATL.
option(USE_ATL "Enable or disable use of ATL." ON)
if(USE_ATL)
# Locate the atlmfc directory if it exists. It may be at any depth inside
# the VC directory. The cl.exe path returned by CMAKE_CXX_COMPILER may also
# be at different depths depending on the toolchain version
# (e.g. "VC/bin/cl.exe", "VC/bin/amd64_x86/cl.exe",
# "VC/Tools/MSVC/14.10.25017/bin/HostX86/x86/cl.exe", etc).
set(HAS_ATLMFC 0)
get_filename_component(VC_DIR ${CMAKE_CXX_COMPILER} DIRECTORY)
get_filename_component(VC_DIR_NAME ${VC_DIR} NAME)
while(NOT ${VC_DIR_NAME} STREQUAL "VC")
get_filename_component(VC_DIR ${VC_DIR} DIRECTORY)
if(IS_DIRECTORY "${VC_DIR}/atlmfc")
set(HAS_ATLMFC 1)
break()
endif()
get_filename_component(VC_DIR_NAME ${VC_DIR} NAME)
endwhile()
# Determine if the Visual Studio install supports ATL.
if(NOT HAS_ATLMFC)
message(WARNING "ATL is not supported by your VC installation.")
set(USE_ATL OFF)
endif()
endif()
if(USE_ATL)
list(APPEND CEF_COMPILER_DEFINES
CEF_USE_ATL # Used by apps to test if ATL support is enabled
)
endif()
endif()

290
README.md
View File

@ -7,160 +7,193 @@
<a href="https://github.com/Almamu/linux-wallpaperengine/graphs/contributors"><img src="https://img.shields.io/github/contributors/Almamu/linux-wallpaperengine" /></a>
<a href="https://github.com/Almamu/linux-wallpaperengine/issues"><img src="https://img.shields.io/github/issues-raw/Almamu/linux-wallpaperengine" /></a>
<a href="https://github.com/Almamu/linux-wallpaperengine/issues?q=is%3Aissue+is%3Aopen+label%3A%22help%20wanted%22"><img src="https://img.shields.io/github/issues/Almamu/linux-wallpaperengine/help%20wanted?color=green" alt="help wanted"></a>
<a href="https://wpengine.alma.mu/"><img src="https://img.shields.io/badge/showcase_gallery-blue" alt="Compatibility gallery" /></a>
</p>
# 1. Disclaimer
**This is an educational project**. Although the project started as a learning exercise on the Irrlicht Engine, it has kind of turned into an OpenGL one instead due to limitations and issues with Irrlicht (most likely caused by my limited experience with graphics programming). As it turns out, working directly with OpenGL is not as hard as I thought. For more information on the project's license, check [LICENSE](LICENSE).
# 🖼️ Linux Wallpaper Engine
# 2. What is this project all about?
This project aims to reproduce the background functionality of Wallpaper Engine on Linux systems. Simple as that.
Bring **Wallpaper Engine**-style live wallpapers to Linux! This project allows you to run animated wallpapers from Steams Wallpaper Engine right on your desktop.
# 3. What is Wallpaper Engine?
Wallpaper Engine is a software designed by [Kristjan Skutta](https://store.steampowered.com/search/?developer=Kristjan%20Skutta&snr=1_5_9__400) that provides live wallpaper functionality to Windows Systems, allowing its users to animate their own backgrounds and share their own creations. You can find more about it on their [Steam page](https://store.steampowered.com/app/431960/Wallpaper_Engine/).
> ⚠️ This is an educational project that evolved into a functional OpenGL-based wallpaper engine for Linux. Expect some limitations and quirks!
---
## 📦 System Requirements
To compile and run this, you'll need:
# 4. Compilation requirements
## linux-wallpaperengine
- OpenGL 3.3 support
- CMake
- LZ4
- ZLIB
- LZ4, Zlib
- SDL2
- FFmpeg
- X11 (with libxxf86vm) or Wayland
- Xrandr (for X11 support)
- GLFW3
- GLM
- GLEW
- GLUT
- X11 or Wayland
- Xrandr (for X11)
- GLFW3, GLEW, GLUT, GLM
- MPV
- PulseAudio
- FFTW3
## Commands
```
Install the required dependencies on Ubuntu/Debian-based systems:
### Ubuntu 22.04
```bash
sudo apt-get update
sudo apt-get install build-essential cmake libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxxf86vm-dev libglm-dev libglfw3-dev libmpv-dev mpv libmpv1 libpulse-dev libpulse0
sudo apt-get install build-essential cmake libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxxf86vm-dev libglm-dev libglfw3-dev libmpv-dev mpv libmpv1 libpulse-dev libpulse0 libfftw3-dev
```
# 5. How to use
## 5.1. Pre-requirements
In order to properly use this software you'll need to own an actual copy of the Windows version of Wallpaper Engine (which you can buy on the [Steam Page](https://store.steampowered.com/app/431960/Wallpaper_Engine/)), as it contains some basic assets on which most of the backgrounds are based on.
The only way to get those assets is to install the Windows version through Steam. Luckily you don't really need a Windows installation for that. Using the Linux Steam client is enough to download the files we need. Note you may need to check "Enable Steam Play for all other titles" in the Steam Play section of Steam's settings if you haven't already. Also note that the software cannot actually be launched through Steam Play (Proton), but the setting is required for Steam to download the software.
## 5.2. Extracting the assets
The automatic way doesn't require anything extra, as long as Wallpaper Engine is installed in Steam the software should automatically detect where the assets are.
### 5.2.1. Extracting the assets manually
In the off-case where the software doesn't automatically detect the correct path, the assets can be extracted manually. Once Wallpaper Engine is downloaded, open the installation folder (Right-Click the application in Steam -> Manage -> Browse local files). Here you'll see the main folders of Wallpaper Engine. The folder we're interested in is the one named "assets".
![folder](docs/images/screenshot_folder.png)
The assets folder itself **must** be copied to the same folder where the binary lives.
## 5.3. Getting the sources
You can download a zipped version of the repository here: https://github.com/Almamu/linux-wallpaperengine/archive/refs/heads/main.zip
You can also clone the repository using git like this:
```
git clone git@github.com:Almamu/linux-wallpaperengine.git
### Ubuntu 24.04
```bash
sudo apt-get update
sudo apt-get install build-essential cmake libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxxf86vm-dev libglm-dev libglfw3-dev libmpv-dev mpv libmpv2 libpulse-dev libpulse0 libfftw3-dev
```
Or using the HTTPS method if you haven't set up SSH:
```
https://github.com/Almamu/linux-wallpaperengine.git
---
## 🐧 Arch Linux Users
You can install this directly from the AUR using your favorite AUR helper:
```bash
yay -S linux-wallpaperengine-git
```
## 5.4. Compilation steps
The project is built on CMake as the build engine. First we need to create the directory where the build will be stored and get into it:
> This installs the latest development version.
**Note:** Youll still need assets from the official Wallpaper Engine (via Steam). See below for details.
---
## 🚀 Getting Started
### 1. Get Wallpaper Engine Assets
You **must own and install Wallpaper Engine** via Steam. This provides the required assets used by many backgrounds.
Good news: **you usually dont need to copy anything manually.** The app will automatically look in these common install paths:
```
mkdir build
cd build
~/.steam/steam/steamapps/common
~/.local/share/Steam/steamapps/common
~/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common
~/snap/steam/common/.local/share/Steam/steamapps/common
```
Once the folder is created and we're in it, CMake has to generate the actual Makefiles. This can be done this way:
> ✅ If Wallpaper Engine is installed in one of these paths, the assets will be detected automatically!
---
#### ❗ If Assets Arent Found Automatically
You can copy the `assets` folder manually:
1. In Steam, right-click **Wallpaper Engine****Manage** → **Browse local files**
2. Copy the `assets` folder
3. Paste it into the same folder where the `linux-wallpaperengine` binary is located
---
### 2. Build from Source
Clone the repo:
```bash
git clone --recurse-submodules https://github.com/Almamu/linux-wallpaperengine.git
cd linux-wallpaperengine
```
Build it:
```bash
mkdir build && cd build
cmake ..
```
Take a closer look at the CMake output, if you miss any library, CMake will report the missing libraries so you can install them either trough your package manager or manually in your system.
Finally we can compile the project to generate the actual executable.
```
make
```
**REMEMBER: The assets folder has to be in the same folder as the executable**
Once the build process is finished, this should create a new `output` folder containing the app and all the required
support files to run.
## 5.5. Running a background
### 5.5.1. Running a background from Steam
Just like with the assets, the software can automatically detect where the subscribed backgrounds are stored. To get started, search in the workshop for whatever background you want to use and click the "+Subscribe" button. This should download the background in the steam directory.
> ✅ Remember: Place the `assets` folder next to the built binary if it isnt detected automatically.
To actually use the background you'll need to know the workshop's ID. This can be obtained right-clicking anywhere in the background's page -> "Copy Page URL". You can paste this URL anywhere, it will look something like this:
---
```
https://steamcommunity.com/sharedfiles/filedetails/?id=1845706469&searchtext=portal+3
## 🧪 Usage
Basic syntax:
```bash
./linux-wallpaperengine [options] <background_id or path>
```
Where 1845706469 is the wallpaper's ID. You can use this ID to run wallpaperengine:
```
You can use either:
- A Steam Workshop ID (e.g. `1845706469`)
- A path to a background folder
---
### 🔧 Common Options
| Option | Description |
|--------|-------------|
| `--silent` | Mute background audio |
| `--volume <val>` | Set audio volume |
| `--noautomute` | Don't mute when other apps play audio |
| `--no-audio-processing` | Disable audio reactive features |
| `--fps <val>` | Limit frame rate |
| `--window <XxYxWxH>` | Run in windowed mode with custom size/position |
| `--screen-root <screen>` | Set as background for specific screen |
| `--bg <id/path>` | Assign a background to a specific screen (use after `--screen-root`) |
| `--scaling <mode>` | Wallpaper scaling: `stretch`, `fit`, `fill`, or `default` |
| `--clamping <mode>` | Set texture clamping: `clamp`, `border`, `repeat` |
| `--assets-dir <path>` | Set custom path for assets |
| `--screenshot <file>` | Save screenshot (PNG, JPEG, BMP) |
| `--list-properties` | Show customizable properties of a wallpaper |
| `--set-property name=value` | Override a specific property |
| `--disable-mouse` | Disable mouse interaction |
| `--disable-parallax` | Disable parallax effect on backgrounds that support it |
| `--no-fullscreen-pause` | Prevent pausing while fullscreen apps are running |
---
### 💡 Examples
#### Run a background by ID
```bash
./linux-wallpaperengine 1845706469
```
### 5.5.2. Running a background in a different folder
For the situations where the software cannot detect where the backgrounds are stored, you can specify a full path to it, like so:
```
./linux-wallpaperengine /home/almamu/Development/backgrounds/1845706469/
#### Run a background from a folder
```bash
./linux-wallpaperengine ~/backgrounds/1845706469/
```
### 5.5.2. Running in a window (Default)
By default the app will load the backgrounds in a window so you can preview them:
```
./linux-wallpaperengine /home/almamu/Development/backgrounds/1845706469/
#### Assign backgrounds to screens with scaling
```bash
./linux-wallpaperengine \
--scaling stretch --screen-root eDP-1 --bg 2667198601 \
--scaling fill --screen-root HDMI-1 --bg 2667198602
```
Where `/home/almamu/Development/backgrounds/1845706469/` is the background's path.
### 5.5.3. Running as a screen's background
The app supports running as background in X11 and Wayland. Use the --screen-root switch and the screen name, like so:
```
./linux-wallpaperengine --screen-root HDMI-1 --screen-root DVI-D-1 1845706469
#### Run in a window
```bash
./linux-wallpaperengine --window 0x0x1280x720 1845706469
```
#### Wayland
Has only been tested under wlroots but should work on any flavour as long as wlr-layer-shell-unstable is supported.
#### X11
Only screens configured with the XRandr extension are supported. To specify the screen names (as reported from xrandr tool) just use the ```--screen-root``` switch. You can specify multiple screens at the same time, for example:
**IMPORTANT: Right now this doesn't work if there is anything drawing to the background (like a compositor, gnome, kde, nautilus, etc)**
### 5.5.4. Limiting FPS
To reduce the performance hit to your system you can reduce (or increase) the FPS limit with the switch ```--fps```, especially useful for laptops:
```
./linux-wallpaperengine --fps 30
#### Limit FPS to save power
```bash
./linux-wallpaperengine --fps 30 1845706469
```
## 5.6. Audio
### 5.6.1. Disable audio
It's possible to disable the audio of the background with the silent argument
```
./linux-wallpaperengine --silent
#### Take a screenshot
```bash
./linux-wallpaperengine --screenshot ~/wallpaper.png 1845706469
```
## 5.7. Taking a screenshot
It is possible to take a screenshot of the screen's content at the moment a background is loaded up and rendered. Useful for tools like pywal to further customize your environment:
```
./linux-wallpaperengine --screenshot /path/to/screenshot/name.png
```
This can be useful as output for pywal or other color systems that use images as basis to generate a set of colors
to apply to your system.
PNG, BMP and JPEG are supported.
## 5.8. Properties
Some backgrounds have a list of properties that the user can customize. These properties modify how parts of the background behave or look like. Support for these is present.
First, list all the available properties in a background, you can do that with the --list-properties switch:
```
#### View and change properties
```bash
./linux-wallpaperengine --list-properties 2370927443
```
@ -219,18 +252,43 @@ Any of these values can be modified with the --set-property switch. Say you want
./linux-wallpaperengine --set-property bloom=1 2370927443
```
If you keep --list-properties in the commandline you can see how the values change to confirm that it applied properly.
---
## 6. Example background
This was the first background to even be compatible with the software. And it's not 100% compatible yet. Both textures and shaders are properly loaded, but there are still particles missing.
## 🧪 Wayland & X11 Support
![example](docs/images/example.gif)
- **Wayland**: Works with compositors that support `wlr-layer-shell-unstable`.
- **X11**: Requires XRandr. Use `--screen-root <screen_name>` (as shown in `xrandr`).
###### 1845706469
In similar fashion to the example background, this one represents the progress of the program. It leverages FBOs (targets), and multiple-effects over objects.
> ⚠ For X11 users: Currently doesn't work if a compositor or desktop environment (e.g. GNOME, KDE, Nautilus) is drawing the background.
---
## 🌈 Example Backgrounds
![example1](docs/images/example.gif)
![example2](docs/images/example2.gif)
# 7. Special thanks
- [RePKG](https://github.com/notscuffed/repkg) for the information on texture flags
- [RenderDoc](https://github.com/baldurk/renderdoc) for the so helpful OpenGL debugging tool that simplified finding issues on the new OpenGL code. Seriously this tool ROCKS
## 🪲 Common issues
### Black screen when setting as screen's background
This can be caused by a few different things depending on your environment and setup.
### X11
Common symptom of a compositor drawing to the background which prevents Wallpaper Engine from being properly visible.
The only solution currently is disabling the compositor so Wallpaper Engine can properly draw on the screen
### NVIDIA
Some users have had issues with GLFW initialization and other OpenGL errors. These are generally something that's
worth reporting in the issues. Sometimes adding this variable when running Wallpaper Engine helps and/or solves
the issue:
```bash
__GL_THREADED_OPTIMIZATIONS=0 linux-wallpaperengine
```
We'll be looking at improving this in the future, but for now it can be a useful workaround.
---
## 🙏 Special Thanks
- [RePKG](https://github.com/notscuffed/repkg) for texture flag insights
- [RenderDoc](https://github.com/baldurk/renderdoc) the best OpenGL debugger out there!

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

View File

@ -1,45 +0,0 @@
#pragma once
#include <utility>
#include <nlohmann/detail/conversions/from_json.hpp>
#include <nlohmann/detail/conversions/to_json.hpp>
namespace nlohmann
{
template<typename, typename>
struct adl_serializer
{
/*!
@brief convert a JSON value to any value type
This function is usually called by the `get()` function of the
@ref basic_json class (either explicit or via conversion operators).
@param[in] j JSON value to read from
@param[in,out] val value to write to
*/
template<typename BasicJsonType, typename ValueType>
static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
{
::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
}
/*!
@brief convert any value type to a JSON value
This function is usually called by the constructors of the @ref basic_json
class.
@param[in,out] j JSON value to write to
@param[in] val value to read from
*/
template<typename BasicJsonType, typename ValueType>
static void to_json(BasicJsonType& j, ValueType&& val) noexcept(
noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
{
::nlohmann::to_json(j, std::forward<ValueType>(val));
}
};
}

View File

@ -1,378 +0,0 @@
#pragma once
#include <algorithm> // transform
#include <array> // array
#include <ciso646> // and, not
#include <forward_list> // forward_list
#include <iterator> // inserter, front_inserter, end
#include <map> // map
#include <string> // string
#include <tuple> // tuple, make_tuple
#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
#include <unordered_map> // unordered_map
#include <utility> // pair, declval
#include <valarray> // valarray
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
// overloads for basic_json template parameters
template<typename BasicJsonType, typename ArithmeticType,
enable_if_t<std::is_arithmetic<ArithmeticType>::value and
not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
int> = 0>
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
{
switch (static_cast<value_t>(j))
{
case value_t::number_unsigned:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
break;
}
case value_t::number_integer:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
break;
}
case value_t::number_float:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
break;
}
default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
}
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
{
if (JSON_UNLIKELY(not j.is_boolean()))
{
JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name())));
}
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
{
if (JSON_UNLIKELY(not j.is_string()))
{
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
}
template <
typename BasicJsonType, typename CompatibleStringType,
enable_if_t <
is_compatible_string_type<BasicJsonType, CompatibleStringType>::value and
not std::is_same<typename BasicJsonType::string_t,
CompatibleStringType>::value,
int > = 0 >
void from_json(const BasicJsonType& j, CompatibleStringType& s)
{
if (JSON_UNLIKELY(not j.is_string()))
{
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType, typename EnumType,
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
void from_json(const BasicJsonType& j, EnumType& e)
{
typename std::underlying_type<EnumType>::type val;
get_arithmetic_value(j, val);
e = static_cast<EnumType>(val);
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr)
{
if (JSON_UNLIKELY(not j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
}
// forward_list doesn't have an insert method
template<typename BasicJsonType, typename T, typename Allocator,
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{
if (JSON_UNLIKELY(not j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
std::transform(j.rbegin(), j.rend(),
std::front_inserter(l), [](const BasicJsonType & i)
{
return i.template get<T>();
});
}
// valarray doesn't have an insert method
template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::valarray<T>& l)
{
if (JSON_UNLIKELY(not j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
l.resize(j.size());
std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l));
}
template<typename BasicJsonType, typename CompatibleArrayType>
void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0> /*unused*/)
{
using std::end;
std::transform(j.begin(), j.end(),
std::inserter(arr, end(arr)), [](const BasicJsonType & i)
{
// get<BasicJsonType>() returns *this, this won't call a from_json
// method when value_type is BasicJsonType
return i.template get<typename CompatibleArrayType::value_type>();
});
}
template<typename BasicJsonType, typename CompatibleArrayType>
auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/)
-> decltype(
arr.reserve(std::declval<typename CompatibleArrayType::size_type>()),
void())
{
using std::end;
arr.reserve(j.size());
std::transform(j.begin(), j.end(),
std::inserter(arr, end(arr)), [](const BasicJsonType & i)
{
// get<BasicJsonType>() returns *this, this won't call a from_json
// method when value_type is BasicJsonType
return i.template get<typename CompatibleArrayType::value_type>();
});
}
template<typename BasicJsonType, typename T, std::size_t N>
void from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, priority_tag<2> /*unused*/)
{
for (std::size_t i = 0; i < N; ++i)
{
arr[i] = j.at(i).template get<T>();
}
}
template <
typename BasicJsonType, typename CompatibleArrayType,
enable_if_t <
is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and
not std::is_same<typename BasicJsonType::array_t,
CompatibleArrayType>::value and
std::is_constructible <
BasicJsonType, typename CompatibleArrayType::value_type >::value,
int > = 0 >
void from_json(const BasicJsonType& j, CompatibleArrayType& arr)
{
if (JSON_UNLIKELY(not j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " +
std::string(j.type_name())));
}
from_json_array_impl(j, arr, priority_tag<2> {});
}
template<typename BasicJsonType, typename CompatibleObjectType,
enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
{
if (JSON_UNLIKELY(not j.is_object()))
{
JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name())));
}
auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
using value_type = typename CompatibleObjectType::value_type;
std::transform(
inner_object->begin(), inner_object->end(),
std::inserter(obj, obj.begin()),
[](typename BasicJsonType::object_t::value_type const & p)
{
return value_type(p.first, p.second.template get<typename CompatibleObjectType::mapped_type>());
});
}
// overload for arithmetic types, not chosen for basic_json template arguments
// (BooleanType, etc..); note: Is it really necessary to provide explicit
// overloads for boolean_t etc. in case of a custom BooleanType which is not
// an arithmetic type?
template<typename BasicJsonType, typename ArithmeticType,
enable_if_t <
std::is_arithmetic<ArithmeticType>::value and
not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and
not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and
not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and
not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
int> = 0>
void from_json(const BasicJsonType& j, ArithmeticType& val)
{
switch (static_cast<value_t>(j))
{
case value_t::number_unsigned:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
break;
}
case value_t::number_integer:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
break;
}
case value_t::number_float:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
break;
}
case value_t::boolean:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
break;
}
default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
}
}
template<typename BasicJsonType, typename A1, typename A2>
void from_json(const BasicJsonType& j, std::pair<A1, A2>& p)
{
p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};
}
template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...>)
{
t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);
}
template<typename BasicJsonType, typename... Args>
void from_json(const BasicJsonType& j, std::tuple<Args...>& t)
{
from_json_tuple_impl(j, t, index_sequence_for<Args...> {});
}
template <typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
typename = enable_if_t<not std::is_constructible<
typename BasicJsonType::string_t, Key>::value>>
void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
{
if (JSON_UNLIKELY(not j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
for (const auto& p : j)
{
if (JSON_UNLIKELY(not p.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name())));
}
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
}
}
template <typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
typename = enable_if_t<not std::is_constructible<
typename BasicJsonType::string_t, Key>::value>>
void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
{
if (JSON_UNLIKELY(not j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
for (const auto& p : j)
{
if (JSON_UNLIKELY(not p.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name())));
}
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
}
}
struct from_json_fn
{
private:
template<typename BasicJsonType, typename T>
auto call(const BasicJsonType& j, T& val, priority_tag<1> /*unused*/) const
noexcept(noexcept(from_json(j, val)))
-> decltype(from_json(j, val), void())
{
return from_json(j, val);
}
template<typename BasicJsonType, typename T>
void call(const BasicJsonType& /*unused*/, T& /*unused*/, priority_tag<0> /*unused*/) const noexcept
{
static_assert(sizeof(BasicJsonType) == 0,
"could not find from_json() method in T's namespace");
#ifdef _MSC_VER
// MSVC does not show a stacktrace for the above assert
using decayed = uncvref_t<T>;
static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0,
"forcing MSVC stacktrace to show which T we're talking about.");
#endif
}
public:
template<typename BasicJsonType, typename T>
void operator()(const BasicJsonType& j, T& val) const
noexcept(noexcept(std::declval<from_json_fn>().call(j, val, priority_tag<1> {})))
{
return call(j, val, priority_tag<1> {});
}
};
}
/// namespace to hold default `from_json` function
/// to see why this is required:
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
namespace
{
constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,357 +0,0 @@
#pragma once
#include <ciso646> // or, and, not
#include <iterator> // begin, end
#include <tuple> // tuple, get
#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
#include <utility> // move, forward, declval, pair
#include <valarray> // valarray
#include <vector> // vector
#include <nlohmann/detail/meta.hpp>
#include <nlohmann/detail/value_t.hpp>
#include <nlohmann/detail/iterators/iteration_proxy.hpp>
namespace nlohmann
{
namespace detail
{
//////////////////
// constructors //
//////////////////
template<value_t> struct external_constructor;
template<>
struct external_constructor<value_t::boolean>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
{
j.m_type = value_t::boolean;
j.m_value = b;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::string>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
{
j.m_type = value_t::string;
j.m_value = s;
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
{
j.m_type = value_t::string;
j.m_value = std::move(s);
j.assert_invariant();
}
template<typename BasicJsonType, typename CompatibleStringType,
enable_if_t<not std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
int> = 0>
static void construct(BasicJsonType& j, const CompatibleStringType& str)
{
j.m_type = value_t::string;
j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::number_float>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
{
j.m_type = value_t::number_float;
j.m_value = val;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::number_unsigned>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
{
j.m_type = value_t::number_unsigned;
j.m_value = val;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::number_integer>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
{
j.m_type = value_t::number_integer;
j.m_value = val;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::array>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
{
j.m_type = value_t::array;
j.m_value = arr;
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
{
j.m_type = value_t::array;
j.m_value = std::move(arr);
j.assert_invariant();
}
template<typename BasicJsonType, typename CompatibleArrayType,
enable_if_t<not std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
int> = 0>
static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
{
using std::begin;
using std::end;
j.m_type = value_t::array;
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
{
j.m_type = value_t::array;
j.m_value = value_t::array;
j.m_value.array->reserve(arr.size());
for (const bool x : arr)
{
j.m_value.array->push_back(x);
}
j.assert_invariant();
}
template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
{
j.m_type = value_t::array;
j.m_value = value_t::array;
j.m_value.array->resize(arr.size());
std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::object>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
{
j.m_type = value_t::object;
j.m_value = obj;
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
{
j.m_type = value_t::object;
j.m_value = std::move(obj);
j.assert_invariant();
}
template<typename BasicJsonType, typename CompatibleObjectType,
enable_if_t<not std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int> = 0>
static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
{
using std::begin;
using std::end;
j.m_type = value_t::object;
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
j.assert_invariant();
}
};
/////////////
// to_json //
/////////////
template<typename BasicJsonType, typename T,
enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
void to_json(BasicJsonType& j, T b) noexcept
{
external_constructor<value_t::boolean>::construct(j, b);
}
template<typename BasicJsonType, typename CompatibleString,
enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
void to_json(BasicJsonType& j, const CompatibleString& s)
{
external_constructor<value_t::string>::construct(j, s);
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
{
external_constructor<value_t::string>::construct(j, std::move(s));
}
template<typename BasicJsonType, typename FloatType,
enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
void to_json(BasicJsonType& j, FloatType val) noexcept
{
external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
}
template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
{
external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
}
template<typename BasicJsonType, typename CompatibleNumberIntegerType,
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
{
external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
}
template<typename BasicJsonType, typename EnumType,
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
void to_json(BasicJsonType& j, EnumType e) noexcept
{
using underlying_type = typename std::underlying_type<EnumType>::type;
external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, const std::vector<bool>& e)
{
external_constructor<value_t::array>::construct(j, e);
}
template<typename BasicJsonType, typename CompatibleArrayType,
enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value or
std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value,
int> = 0>
void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
{
external_constructor<value_t::array>::construct(j, arr);
}
template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
void to_json(BasicJsonType& j, const std::valarray<T>& arr)
{
external_constructor<value_t::array>::construct(j, std::move(arr));
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
{
external_constructor<value_t::array>::construct(j, std::move(arr));
}
template<typename BasicJsonType, typename CompatibleObjectType,
enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
{
external_constructor<value_t::object>::construct(j, obj);
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
{
external_constructor<value_t::object>::construct(j, std::move(obj));
}
template<typename BasicJsonType, typename T, std::size_t N,
enable_if_t<not std::is_constructible<typename BasicJsonType::string_t, T (&)[N]>::value, int> = 0>
void to_json(BasicJsonType& j, T (&arr)[N])
{
external_constructor<value_t::array>::construct(j, arr);
}
template<typename BasicJsonType, typename... Args>
void to_json(BasicJsonType& j, const std::pair<Args...>& p)
{
j = {p.first, p.second};
}
// for https://github.com/nlohmann/json/pull/1134
template<typename BasicJsonType, typename T,
enable_if_t<std::is_same<T, typename iteration_proxy<typename BasicJsonType::iterator>::iteration_proxy_internal>::value, int> = 0>
void to_json(BasicJsonType& j, T b) noexcept
{
j = {{b.key(), b.value()}};
}
template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...>)
{
j = {std::get<Idx>(t)...};
}
template<typename BasicJsonType, typename... Args>
void to_json(BasicJsonType& j, const std::tuple<Args...>& t)
{
to_json_tuple_impl(j, t, index_sequence_for<Args...> {});
}
struct to_json_fn
{
private:
template<typename BasicJsonType, typename T>
auto call(BasicJsonType& j, T&& val, priority_tag<1> /*unused*/) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
-> decltype(to_json(j, std::forward<T>(val)), void())
{
return to_json(j, std::forward<T>(val));
}
template<typename BasicJsonType, typename T>
void call(BasicJsonType& /*unused*/, T&& /*unused*/, priority_tag<0> /*unused*/) const noexcept
{
static_assert(sizeof(BasicJsonType) == 0,
"could not find to_json() method in T's namespace");
#ifdef _MSC_VER
// MSVC does not show a stacktrace for the above assert
using decayed = uncvref_t<T>;
static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0,
"forcing MSVC stacktrace to show which T we're talking about.");
#endif
}
public:
template<typename BasicJsonType, typename T>
void operator()(BasicJsonType& j, T&& val) const
noexcept(noexcept(std::declval<to_json_fn>().call(j, std::forward<T>(val), priority_tag<1> {})))
{
return call(j, std::forward<T>(val), priority_tag<1> {});
}
};
}
/// namespace to hold default `to_json` function
namespace
{
constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;
}
}

View File

@ -1,330 +0,0 @@
#pragma once
#include <exception> // exception
#include <stdexcept> // runtime_error
#include <string> // to_string
namespace nlohmann
{
namespace detail
{
////////////////
// exceptions //
////////////////
/*!
@brief general exception of the @ref basic_json class
This class is an extension of `std::exception` objects with a member @a id for
exception ids. It is used as the base class for all exceptions thrown by the
@ref basic_json class. This class can hence be used as "wildcard" to catch
exceptions.
Subclasses:
- @ref parse_error for exceptions indicating a parse error
- @ref invalid_iterator for exceptions indicating errors with iterators
- @ref type_error for exceptions indicating executing a member function with
a wrong type
- @ref out_of_range for exceptions indicating access out of the defined range
- @ref other_error for exceptions indicating other library errors
@internal
@note To have nothrow-copy-constructible exceptions, we internally use
`std::runtime_error` which can cope with arbitrary-length error messages.
Intermediate strings are built with static functions and then passed to
the actual constructor.
@endinternal
@liveexample{The following code shows how arbitrary library exceptions can be
caught.,exception}
@since version 3.0.0
*/
class exception : public std::exception
{
public:
/// returns the explanatory string
const char* what() const noexcept override
{
return m.what();
}
/// the id of the exception
const int id;
protected:
exception(int id_, const char* what_arg) : id(id_), m(what_arg) {}
static std::string name(const std::string& ename, int id_)
{
return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
}
private:
/// an exception object as storage for error messages
std::runtime_error m;
};
/*!
@brief exception indicating a parse error
This exception is thrown by the library when a parse error occurs. Parse errors
can occur during the deserialization of JSON text, CBOR, MessagePack, as well
as when using JSON Patch.
Member @a byte holds the byte index of the last read character in the input
file.
Exceptions have ids 1xx.
name / id | example message | description
------------------------------ | --------------- | -------------------------
json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.
json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.
json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.
json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.
json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors.
json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.
json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.
json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.
json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.
json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.
json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.
json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.
@note For an input with n bytes, 1 is the index of the first character and n+1
is the index of the terminating null byte or the end of file. This also
holds true when reading a byte vector (CBOR or MessagePack).
@liveexample{The following code shows how a `parse_error` exception can be
caught.,parse_error}
@sa @ref exception for the base class of the library exceptions
@sa @ref invalid_iterator for exceptions indicating errors with iterators
@sa @ref type_error for exceptions indicating executing a member function with
a wrong type
@sa @ref out_of_range for exceptions indicating access out of the defined range
@sa @ref other_error for exceptions indicating other library errors
@since version 3.0.0
*/
class parse_error : public exception
{
public:
/*!
@brief create a parse error exception
@param[in] id_ the id of the exception
@param[in] byte_ the byte index where the error occurred (or 0 if the
position cannot be determined)
@param[in] what_arg the explanatory string
@return parse_error object
*/
static parse_error create(int id_, std::size_t byte_, const std::string& what_arg)
{
std::string w = exception::name("parse_error", id_) + "parse error" +
(byte_ != 0 ? (" at " + std::to_string(byte_)) : "") +
": " + what_arg;
return parse_error(id_, byte_, w.c_str());
}
/*!
@brief byte index of the parse error
The byte index of the last read character in the input file.
@note For an input with n bytes, 1 is the index of the first character and
n+1 is the index of the terminating null byte or the end of file.
This also holds true when reading a byte vector (CBOR or MessagePack).
*/
const std::size_t byte;
private:
parse_error(int id_, std::size_t byte_, const char* what_arg)
: exception(id_, what_arg), byte(byte_) {}
};
/*!
@brief exception indicating errors with iterators
This exception is thrown if iterators passed to a library function do not match
the expected semantics.
Exceptions have ids 2xx.
name / id | example message | description
----------------------------------- | --------------- | -------------------------
json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.
json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.
json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.
json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.
json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.
json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.
json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.
json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.
json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.
json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().
@liveexample{The following code shows how an `invalid_iterator` exception can be
caught.,invalid_iterator}
@sa @ref exception for the base class of the library exceptions
@sa @ref parse_error for exceptions indicating a parse error
@sa @ref type_error for exceptions indicating executing a member function with
a wrong type
@sa @ref out_of_range for exceptions indicating access out of the defined range
@sa @ref other_error for exceptions indicating other library errors
@since version 3.0.0
*/
class invalid_iterator : public exception
{
public:
static invalid_iterator create(int id_, const std::string& what_arg)
{
std::string w = exception::name("invalid_iterator", id_) + what_arg;
return invalid_iterator(id_, w.c_str());
}
private:
invalid_iterator(int id_, const char* what_arg)
: exception(id_, what_arg) {}
};
/*!
@brief exception indicating executing a member function with a wrong type
This exception is thrown in case of a type error; that is, a library function is
executed on a JSON value whose type does not match the expected semantics.
Exceptions have ids 3xx.
name / id | example message | description
----------------------------- | --------------- | -------------------------
json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.
json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.
json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&.
json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.
json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.
json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.
json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.
json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.
json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.
json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.
json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.
json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.
json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.
json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.
json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.
json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |
@liveexample{The following code shows how a `type_error` exception can be
caught.,type_error}
@sa @ref exception for the base class of the library exceptions
@sa @ref parse_error for exceptions indicating a parse error
@sa @ref invalid_iterator for exceptions indicating errors with iterators
@sa @ref out_of_range for exceptions indicating access out of the defined range
@sa @ref other_error for exceptions indicating other library errors
@since version 3.0.0
*/
class type_error : public exception
{
public:
static type_error create(int id_, const std::string& what_arg)
{
std::string w = exception::name("type_error", id_) + what_arg;
return type_error(id_, w.c_str());
}
private:
type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
};
/*!
@brief exception indicating access out of the defined range
This exception is thrown in case a library function is called on an input
parameter that exceeds the expected range, for instance in case of array
indices or nonexisting object keys.
Exceptions have ids 4xx.
name / id | example message | description
------------------------------- | --------------- | -------------------------
json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.
json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.
json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.
json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON only supports integers numbers up to 9223372036854775807. |
json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |
@liveexample{The following code shows how an `out_of_range` exception can be
caught.,out_of_range}
@sa @ref exception for the base class of the library exceptions
@sa @ref parse_error for exceptions indicating a parse error
@sa @ref invalid_iterator for exceptions indicating errors with iterators
@sa @ref type_error for exceptions indicating executing a member function with
a wrong type
@sa @ref other_error for exceptions indicating other library errors
@since version 3.0.0
*/
class out_of_range : public exception
{
public:
static out_of_range create(int id_, const std::string& what_arg)
{
std::string w = exception::name("out_of_range", id_) + what_arg;
return out_of_range(id_, w.c_str());
}
private:
out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
};
/*!
@brief exception indicating other library errors
This exception is thrown in case of errors that cannot be classified with the
other exception types.
Exceptions have ids 5xx.
name / id | example message | description
------------------------------ | --------------- | -------------------------
json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.
@sa @ref exception for the base class of the library exceptions
@sa @ref parse_error for exceptions indicating a parse error
@sa @ref invalid_iterator for exceptions indicating errors with iterators
@sa @ref type_error for exceptions indicating executing a member function with
a wrong type
@sa @ref out_of_range for exceptions indicating access out of the defined range
@liveexample{The following code shows how an `other_error` exception can be
caught.,other_error}
@since version 3.0.0
*/
class other_error : public exception
{
public:
static other_error create(int id_, const std::string& what_arg)
{
std::string w = exception::name("other_error", id_) + what_arg;
return other_error(id_, w.c_str());
}
private:
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
};
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,374 +0,0 @@
#pragma once
#include <cassert> // assert
#include <cstddef> // size_t
#include <cstring> // strlen
#include <istream> // istream
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
#include <memory> // shared_ptr, make_shared, addressof
#include <numeric> // accumulate
#include <string> // string, char_traits
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
#include <utility> // pair, declval
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
namespace detail
{
/// the supported input formats
enum class input_format_t { json, cbor, msgpack, ubjson };
////////////////////
// input adapters //
////////////////////
/*!
@brief abstract input adapter interface
Produces a stream of std::char_traits<char>::int_type characters from a
std::istream, a buffer, or some other input type. Accepts the return of
exactly one non-EOF character for future input. The int_type characters
returned consist of all valid char values as positive values (typically
unsigned char), plus an EOF value outside that range, specified by the value
of the function std::char_traits<char>::eof(). This value is typically -1, but
could be any arbitrary value which is not a valid char value.
*/
struct input_adapter_protocol
{
/// get a character [0,255] or std::char_traits<char>::eof().
virtual std::char_traits<char>::int_type get_character() = 0;
virtual ~input_adapter_protocol() = default;
};
/// a type to simplify interfaces
using input_adapter_t = std::shared_ptr<input_adapter_protocol>;
/*!
Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
beginning of input. Does not support changing the underlying std::streambuf
in mid-input. Maintains underlying std::istream and std::streambuf to support
subsequent use of standard std::istream operations to process any input
characters following those used in parsing the JSON input. Clears the
std::istream flags; any input errors (e.g., EOF) will be detected by the first
subsequent call for input from the std::istream.
*/
class input_stream_adapter : public input_adapter_protocol
{
public:
~input_stream_adapter() override
{
// clear stream flags; we use underlying streambuf I/O, do not
// maintain ifstream flags
is.clear();
}
explicit input_stream_adapter(std::istream& i)
: is(i), sb(*i.rdbuf())
{}
// delete because of pointer members
input_stream_adapter(const input_stream_adapter&) = delete;
input_stream_adapter& operator=(input_stream_adapter&) = delete;
// std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
// ensure that std::char_traits<char>::eof() and the character 0xFF do not
// end up as the same value, eg. 0xFFFFFFFF.
std::char_traits<char>::int_type get_character() override
{
return sb.sbumpc();
}
private:
/// the associated input stream
std::istream& is;
std::streambuf& sb;
};
/// input adapter for buffer input
class input_buffer_adapter : public input_adapter_protocol
{
public:
input_buffer_adapter(const char* b, const std::size_t l)
: cursor(b), limit(b + l)
{}
// delete because of pointer members
input_buffer_adapter(const input_buffer_adapter&) = delete;
input_buffer_adapter& operator=(input_buffer_adapter&) = delete;
std::char_traits<char>::int_type get_character() noexcept override
{
if (JSON_LIKELY(cursor < limit))
{
return std::char_traits<char>::to_int_type(*(cursor++));
}
return std::char_traits<char>::eof();
}
private:
/// pointer to the current character
const char* cursor;
/// pointer past the last character
const char* const limit;
};
template<typename WideStringType>
class wide_string_input_adapter : public input_adapter_protocol
{
public:
explicit wide_string_input_adapter(const WideStringType& w) : str(w) {}
std::char_traits<char>::int_type get_character() noexcept override
{
// check if buffer needs to be filled
if (utf8_bytes_index == utf8_bytes_filled)
{
if (sizeof(typename WideStringType::value_type) == 2)
{
fill_buffer_utf16();
}
else
{
fill_buffer_utf32();
}
assert(utf8_bytes_filled > 0);
assert(utf8_bytes_index == 0);
}
// use buffer
assert(utf8_bytes_filled > 0);
assert(utf8_bytes_index < utf8_bytes_filled);
return utf8_bytes[utf8_bytes_index++];
}
private:
void fill_buffer_utf16()
{
utf8_bytes_index = 0;
if (current_wchar == str.size())
{
utf8_bytes[0] = std::char_traits<char>::eof();
utf8_bytes_filled = 1;
}
else
{
// get the current character
const int wc = static_cast<int>(str[current_wchar++]);
// UTF-16 to UTF-8 encoding
if (wc < 0x80)
{
utf8_bytes[0] = wc;
utf8_bytes_filled = 1;
}
else if (wc <= 0x7FF)
{
utf8_bytes[0] = 0xC0 | ((wc >> 6));
utf8_bytes[1] = 0x80 | (wc & 0x3F);
utf8_bytes_filled = 2;
}
else if (0xD800 > wc or wc >= 0xE000)
{
utf8_bytes[0] = 0xE0 | ((wc >> 12));
utf8_bytes[1] = 0x80 | ((wc >> 6) & 0x3F);
utf8_bytes[2] = 0x80 | (wc & 0x3F);
utf8_bytes_filled = 3;
}
else
{
if (current_wchar < str.size())
{
const int wc2 = static_cast<int>(str[current_wchar++]);
const int charcode = 0x10000 + (((wc & 0x3FF) << 10) | (wc2 & 0x3FF));
utf8_bytes[0] = 0xf0 | (charcode >> 18);
utf8_bytes[1] = 0x80 | ((charcode >> 12) & 0x3F);
utf8_bytes[2] = 0x80 | ((charcode >> 6) & 0x3F);
utf8_bytes[3] = 0x80 | (charcode & 0x3F);
utf8_bytes_filled = 4;
}
else
{
// unknown character
++current_wchar;
utf8_bytes[0] = wc;
utf8_bytes_filled = 1;
}
}
}
}
void fill_buffer_utf32()
{
utf8_bytes_index = 0;
if (current_wchar == str.size())
{
utf8_bytes[0] = std::char_traits<char>::eof();
utf8_bytes_filled = 1;
}
else
{
// get the current character
const int wc = static_cast<int>(str[current_wchar++]);
// UTF-32 to UTF-8 encoding
if (wc < 0x80)
{
utf8_bytes[0] = wc;
utf8_bytes_filled = 1;
}
else if (wc <= 0x7FF)
{
utf8_bytes[0] = 0xC0 | ((wc >> 6) & 0x1F);
utf8_bytes[1] = 0x80 | (wc & 0x3F);
utf8_bytes_filled = 2;
}
else if (wc <= 0xFFFF)
{
utf8_bytes[0] = 0xE0 | ((wc >> 12) & 0x0F);
utf8_bytes[1] = 0x80 | ((wc >> 6) & 0x3F);
utf8_bytes[2] = 0x80 | (wc & 0x3F);
utf8_bytes_filled = 3;
}
else if (wc <= 0x10FFFF)
{
utf8_bytes[0] = 0xF0 | ((wc >> 18 ) & 0x07);
utf8_bytes[1] = 0x80 | ((wc >> 12) & 0x3F);
utf8_bytes[2] = 0x80 | ((wc >> 6) & 0x3F);
utf8_bytes[3] = 0x80 | (wc & 0x3F);
utf8_bytes_filled = 4;
}
else
{
// unknown character
utf8_bytes[0] = wc;
utf8_bytes_filled = 1;
}
}
}
private:
/// the wstring to process
const WideStringType& str;
/// index of the current wchar in str
std::size_t current_wchar = 0;
/// a buffer for UTF-8 bytes
std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
/// index to the utf8_codes array for the next valid byte
std::size_t utf8_bytes_index = 0;
/// number of valid bytes in the utf8_codes array
std::size_t utf8_bytes_filled = 0;
};
class input_adapter
{
public:
// native support
/// input adapter for input stream
input_adapter(std::istream& i)
: ia(std::make_shared<input_stream_adapter>(i)) {}
/// input adapter for input stream
input_adapter(std::istream&& i)
: ia(std::make_shared<input_stream_adapter>(i)) {}
input_adapter(const std::wstring& ws)
: ia(std::make_shared<wide_string_input_adapter<std::wstring>>(ws)) {}
input_adapter(const std::u16string& ws)
: ia(std::make_shared<wide_string_input_adapter<std::u16string>>(ws)) {}
input_adapter(const std::u32string& ws)
: ia(std::make_shared<wide_string_input_adapter<std::u32string>>(ws)) {}
/// input adapter for buffer
template<typename CharT,
typename std::enable_if<
std::is_pointer<CharT>::value and
std::is_integral<typename std::remove_pointer<CharT>::type>::value and
sizeof(typename std::remove_pointer<CharT>::type) == 1,
int>::type = 0>
input_adapter(CharT b, std::size_t l)
: ia(std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(b), l)) {}
// derived support
/// input adapter for string literal
template<typename CharT,
typename std::enable_if<
std::is_pointer<CharT>::value and
std::is_integral<typename std::remove_pointer<CharT>::type>::value and
sizeof(typename std::remove_pointer<CharT>::type) == 1,
int>::type = 0>
input_adapter(CharT b)
: input_adapter(reinterpret_cast<const char*>(b),
std::strlen(reinterpret_cast<const char*>(b))) {}
/// input adapter for iterator range with contiguous storage
template<class IteratorType,
typename std::enable_if<
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
int>::type = 0>
input_adapter(IteratorType first, IteratorType last)
{
// assertion to check that the iterator range is indeed contiguous,
// see http://stackoverflow.com/a/35008842/266378 for more discussion
assert(std::accumulate(
first, last, std::pair<bool, int>(true, 0),
[&first](std::pair<bool, int> res, decltype(*first) val)
{
res.first &= (val == *(std::next(std::addressof(*first), res.second++)));
return res;
}).first);
// assertion to check that each element is 1 byte long
static_assert(
sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1,
"each element in the iterator range must have the size of 1 byte");
const auto len = static_cast<size_t>(std::distance(first, last));
if (JSON_LIKELY(len > 0))
{
// there is at least one element: use the address of first
ia = std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(&(*first)), len);
}
else
{
// the address of first cannot be used: use nullptr
ia = std::make_shared<input_buffer_adapter>(nullptr, len);
}
}
/// input adapter for array
template<class T, std::size_t N>
input_adapter(T (&array)[N])
: input_adapter(std::begin(array), std::end(array)) {}
/// input adapter for contiguous container
template<class ContiguousContainer, typename
std::enable_if<not std::is_pointer<ContiguousContainer>::value and
std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,
int>::type = 0>
input_adapter(const ContiguousContainer& c)
: input_adapter(std::begin(c), std::end(c)) {}
operator input_adapter_t()
{
return ia;
}
private:
/// the actual adapter
input_adapter_t ia = nullptr;
};
}
}

View File

@ -1,687 +0,0 @@
#pragma once
#include <cstddef>
#include <string>
#include <vector>
#include <nlohmann/detail/input/parser.hpp>
#include <nlohmann/detail/exceptions.hpp>
namespace nlohmann
{
/*!
@brief SAX interface
*/
template<typename BasicJsonType>
struct json_sax
{
/// type for (signed) integers
using number_integer_t = typename BasicJsonType::number_integer_t;
/// type for unsigned integers
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
/// type for floating-point numbers
using number_float_t = typename BasicJsonType::number_float_t;
/// type for strings
using string_t = typename BasicJsonType::string_t;
/// constant to indicate that no size limit is given for array or object
static constexpr auto no_limit = std::size_t(-1);
/*!
@brief a null value was read
@return whether parsing should proceed
*/
virtual bool null() = 0;
/*!
@brief a boolean value was read
@param[in] val boolean value
@return whether parsing should proceed
*/
virtual bool boolean(bool val) = 0;
/*!
@brief an integer number was read
@param[in] val integer value
@return whether parsing should proceed
*/
virtual bool number_integer(number_integer_t val) = 0;
/*!
@brief an unsigned integer number was read
@param[in] val unsigned integer value
@return whether parsing should proceed
*/
virtual bool number_unsigned(number_unsigned_t val) = 0;
/*!
@brief an floating-point number was read
@param[in] val floating-point value
@param[in] s raw token value
@return whether parsing should proceed
*/
virtual bool number_float(number_float_t val, const string_t& s) = 0;
/*!
@brief a string was read
@param[in] val string value
@return whether parsing should proceed
*/
virtual bool string(string_t& val) = 0;
/*!
@brief the beginning of an object was read
@param[in] elements number of object elements or no_limit if unknown
@return whether parsing should proceed
@note binary formats may report the number of elements
*/
virtual bool start_object(std::size_t elements = no_limit) = 0;
/*!
@brief an object key was read
@param[in] val object key
@return whether parsing should proceed
*/
virtual bool key(string_t& val) = 0;
/*!
@brief the end of an object was read
@return whether parsing should proceed
*/
virtual bool end_object() = 0;
/*!
@brief the beginning of an array was read
@param[in] elements number of array elements or no_limit if unknown
@return whether parsing should proceed
@note binary formats may report the number of elements
*/
virtual bool start_array(std::size_t elements = no_limit) = 0;
/*!
@brief the end of an array was read
@return whether parsing should proceed
*/
virtual bool end_array() = 0;
/*!
@brief a parse error occurred
@param[in] position the position in the input where the error occurs
@param[in] last_token the last read token
@param[in] error_msg a detailed error message
@return whether parsing should proceed (must return false)
*/
virtual bool parse_error(std::size_t position,
const std::string& last_token,
const detail::exception& ex) = 0;
virtual ~json_sax() = default;
};
namespace detail
{
/*!
@brief SAX implementation to create a JSON value from SAX events
This class implements the @ref json_sax interface and processes the SAX events
to create a JSON value which makes it basically a DOM parser. The structure or
hierarchy of the JSON value is managed by the stack `ref_stack` which contains
a pointer to the respective array or object for each recursion depth.
After successful parsing, the value that is passed by reference to the
constructor contains the parsed value.
@tparam BasicJsonType the JSON type
*/
template<typename BasicJsonType>
class json_sax_dom_parser : public json_sax<BasicJsonType>
{
public:
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
/*!
@param[in, out] r reference to a JSON value that is manipulated while
parsing
@param[in] allow_exceptions_ whether parse errors yield exceptions
*/
json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)
: root(r), allow_exceptions(allow_exceptions_)
{}
bool null() override
{
handle_value(nullptr);
return true;
}
bool boolean(bool val) override
{
handle_value(val);
return true;
}
bool number_integer(number_integer_t val) override
{
handle_value(val);
return true;
}
bool number_unsigned(number_unsigned_t val) override
{
handle_value(val);
return true;
}
bool number_float(number_float_t val, const string_t&) override
{
handle_value(val);
return true;
}
bool string(string_t& val) override
{
handle_value(val);
return true;
}
bool start_object(std::size_t len) override
{
ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
if (JSON_UNLIKELY(len != json_sax<BasicJsonType>::no_limit and len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408,
"excessive object size: " + std::to_string(len)));
}
return true;
}
bool key(string_t& val) override
{
// add null at given key and store the reference for later
object_element = &(ref_stack.back()->m_value.object->operator[](val));
return true;
}
bool end_object() override
{
ref_stack.pop_back();
return true;
}
bool start_array(std::size_t len) override
{
ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
if (JSON_UNLIKELY(len != json_sax<BasicJsonType>::no_limit and len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408,
"excessive array size: " + std::to_string(len)));
}
return true;
}
bool end_array() override
{
ref_stack.pop_back();
return true;
}
bool parse_error(std::size_t, const std::string&,
const detail::exception& ex) override
{
errored = true;
if (allow_exceptions)
{
// determine the proper exception type from the id
switch ((ex.id / 100) % 100)
{
case 1:
JSON_THROW(*reinterpret_cast<const detail::parse_error*>(&ex));
case 2:
JSON_THROW(*reinterpret_cast<const detail::invalid_iterator*>(&ex)); // LCOV_EXCL_LINE
case 3:
JSON_THROW(*reinterpret_cast<const detail::type_error*>(&ex)); // LCOV_EXCL_LINE
case 4:
JSON_THROW(*reinterpret_cast<const detail::out_of_range*>(&ex));
case 5:
JSON_THROW(*reinterpret_cast<const detail::other_error*>(&ex)); // LCOV_EXCL_LINE
default:
assert(false); // LCOV_EXCL_LINE
}
}
return false;
}
constexpr bool is_errored() const
{
return errored;
}
private:
/*!
@invariant If the ref stack is empty, then the passed value will be the new
root.
@invariant If the ref stack contains a value, then it is an array or an
object to which we can add elements
*/
template<typename Value>
BasicJsonType* handle_value(Value&& v)
{
if (ref_stack.empty())
{
root = BasicJsonType(std::forward<Value>(v));
return &root;
}
else
{
assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
if (ref_stack.back()->is_array())
{
ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));
return &(ref_stack.back()->m_value.array->back());
}
else
{
assert(object_element);
*object_element = BasicJsonType(std::forward<Value>(v));
return object_element;
}
}
}
/// the parsed JSON value
BasicJsonType& root;
/// stack to model hierarchy of values
std::vector<BasicJsonType*> ref_stack;
/// helper to hold the reference for the next object element
BasicJsonType* object_element = nullptr;
/// whether a syntax error occurred
bool errored = false;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
};
template<typename BasicJsonType>
class json_sax_dom_callback_parser : public json_sax<BasicJsonType>
{
public:
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using parser_callback_t = typename BasicJsonType::parser_callback_t;
using parse_event_t = typename BasicJsonType::parse_event_t;
json_sax_dom_callback_parser(BasicJsonType& r,
const parser_callback_t cb,
const bool allow_exceptions_ = true)
: root(r), callback(cb), allow_exceptions(allow_exceptions_)
{
keep_stack.push_back(true);
}
bool null() override
{
handle_value(nullptr);
return true;
}
bool boolean(bool val) override
{
handle_value(val);
return true;
}
bool number_integer(number_integer_t val) override
{
handle_value(val);
return true;
}
bool number_unsigned(number_unsigned_t val) override
{
handle_value(val);
return true;
}
bool number_float(number_float_t val, const string_t&) override
{
handle_value(val);
return true;
}
bool string(string_t& val) override
{
handle_value(val);
return true;
}
bool start_object(std::size_t len) override
{
// check callback for object start
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
keep_stack.push_back(keep);
auto val = handle_value(BasicJsonType::value_t::object, true);
ref_stack.push_back(val.second);
// check object limit
if (ref_stack.back())
{
if (JSON_UNLIKELY(len != json_sax<BasicJsonType>::no_limit and len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408,
"excessive object size: " + std::to_string(len)));
}
}
return true;
}
bool key(string_t& val) override
{
BasicJsonType k = BasicJsonType(val);
// check callback for key
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
key_keep_stack.push_back(keep);
// add discarded value at given key and store the reference for later
if (keep and ref_stack.back())
{
object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);
}
return true;
}
bool end_object() override
{
if (ref_stack.back())
{
if (not callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
{
// discard object
*ref_stack.back() = discarded;
}
}
assert(not ref_stack.empty());
assert(not keep_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
if (not ref_stack.empty() and ref_stack.back())
{
// remove discarded value
if (ref_stack.back()->is_object())
{
for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
{
if (it->is_discarded())
{
ref_stack.back()->erase(it);
break;
}
}
}
}
return true;
}
bool start_array(std::size_t len) override
{
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
keep_stack.push_back(keep);
auto val = handle_value(BasicJsonType::value_t::array, true);
ref_stack.push_back(val.second);
// check array limit
if (ref_stack.back())
{
if (JSON_UNLIKELY(len != json_sax<BasicJsonType>::no_limit and len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408,
"excessive array size: " + std::to_string(len)));
}
}
return true;
}
bool end_array() override
{
bool keep = true;
if (ref_stack.back())
{
keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
if (not keep)
{
// discard array
*ref_stack.back() = discarded;
}
}
assert(not ref_stack.empty());
assert(not keep_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
// remove discarded value
if (not keep and not ref_stack.empty())
{
if (ref_stack.back()->is_array())
{
ref_stack.back()->m_value.array->pop_back();
}
}
return true;
}
bool parse_error(std::size_t, const std::string&,
const detail::exception& ex) override
{
errored = true;
if (allow_exceptions)
{
// determine the proper exception type from the id
switch ((ex.id / 100) % 100)
{
case 1:
JSON_THROW(*reinterpret_cast<const detail::parse_error*>(&ex));
case 2:
JSON_THROW(*reinterpret_cast<const detail::invalid_iterator*>(&ex)); // LCOV_EXCL_LINE
case 3:
JSON_THROW(*reinterpret_cast<const detail::type_error*>(&ex)); // LCOV_EXCL_LINE
case 4:
JSON_THROW(*reinterpret_cast<const detail::out_of_range*>(&ex));
case 5:
JSON_THROW(*reinterpret_cast<const detail::other_error*>(&ex)); // LCOV_EXCL_LINE
default:
assert(false); // LCOV_EXCL_LINE
}
}
return false;
}
constexpr bool is_errored() const
{
return errored;
}
private:
/*!
@param[in] v value to add to the JSON value we build during parsing
@param[in] skip_callback whether we should skip calling the callback
function; this is required after start_array() and
start_object() SAX events, because otherwise we would call the
callback function with an empty array or object, respectively.
@invariant If the ref stack is empty, then the passed value will be the new
root.
@invariant If the ref stack contains a value, then it is an array or an
object to which we can add elements
@return pair of boolean (whether value should be kept) and pointer (to the
passed value in the ref_stack hierarchy; nullptr if not kept)
*/
template<typename Value>
std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
{
assert(not keep_stack.empty());
// do not handle this value if we know it would be added to a discarded
// container
if (not keep_stack.back())
{
return {false, nullptr};
}
// create value
auto value = BasicJsonType(std::forward<Value>(v));
// check callback
const bool keep = skip_callback or callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
// do not handle this value if we just learnt it shall be discarded
if (not keep)
{
return {false, nullptr};
}
if (ref_stack.empty())
{
root = std::move(value);
return {true, &root};
}
else
{
assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
if (ref_stack.back()->is_array())
{
ref_stack.back()->m_value.array->push_back(std::move(value));
return {true, &(ref_stack.back()->m_value.array->back())};
}
else
{
// check if we should store an element for the current key
assert(not key_keep_stack.empty());
const bool store_element = key_keep_stack.back();
key_keep_stack.pop_back();
if (not store_element)
{
return {false, nullptr};
}
assert(object_element);
*object_element = std::move(value);
return {true, object_element};
}
}
}
/// the parsed JSON value
BasicJsonType& root;
/// stack to model hierarchy of values
std::vector<BasicJsonType*> ref_stack;
/// stack to manage which values to keep
std::vector<bool> keep_stack;
/// stack to manage which object keys to keep
std::vector<bool> key_keep_stack;
/// helper to hold the reference for the next object element
BasicJsonType* object_element = nullptr;
/// whether a syntax error occurred
bool errored = false;
/// callback function
const parser_callback_t callback = nullptr;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
/// a discarded value for the callback
BasicJsonType discarded = BasicJsonType::value_t::discarded;
};
template<typename BasicJsonType>
class json_sax_acceptor : public json_sax<BasicJsonType>
{
public:
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
bool null() override
{
return true;
}
bool boolean(bool) override
{
return true;
}
bool number_integer(number_integer_t) override
{
return true;
}
bool number_unsigned(number_unsigned_t) override
{
return true;
}
bool number_float(number_float_t, const string_t&) override
{
return true;
}
bool string(string_t&) override
{
return true;
}
bool start_object(std::size_t) override
{
return true;
}
bool key(string_t&) override
{
return true;
}
bool end_object() override
{
return true;
}
bool start_array(std::size_t) override
{
return true;
}
bool end_array() override
{
return true;
}
bool parse_error(std::size_t, const std::string&, const detail::exception&) override
{
return false;
}
};
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,486 +0,0 @@
#pragma once
#include <cassert> // assert
#include <cmath> // isfinite
#include <cstdint> // uint8_t
#include <functional> // function
#include <string> // string
#include <utility> // move
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/input/input_adapters.hpp>
#include <nlohmann/detail/input/json_sax.hpp>
#include <nlohmann/detail/input/lexer.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
////////////
// parser //
////////////
/*!
@brief syntax analysis
This class implements a recursive decent parser.
*/
template<typename BasicJsonType>
class parser
{
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using lexer_t = lexer<BasicJsonType>;
using token_type = typename lexer_t::token_type;
public:
enum class parse_event_t : uint8_t
{
/// the parser read `{` and started to process a JSON object
object_start,
/// the parser read `}` and finished processing a JSON object
object_end,
/// the parser read `[` and started to process a JSON array
array_start,
/// the parser read `]` and finished processing a JSON array
array_end,
/// the parser read a key of a value in an object
key,
/// the parser finished reading a JSON value
value
};
using json_sax_t = json_sax<BasicJsonType>;
using parser_callback_t =
std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;
/// a parser reading from an input adapter
explicit parser(detail::input_adapter_t&& adapter,
const parser_callback_t cb = nullptr,
const bool allow_exceptions_ = true)
: callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_)
{
// read first token
get_token();
}
/*!
@brief public parser interface
@param[in] strict whether to expect the last token to be EOF
@param[in,out] result parsed JSON value
@throw parse_error.101 in case of an unexpected token
@throw parse_error.102 if to_unicode fails or surrogate error
@throw parse_error.103 if to_unicode fails
*/
void parse(const bool strict, BasicJsonType& result)
{
if (callback)
{
json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
sax_parse_internal(&sdp);
result.assert_invariant();
// in strict mode, input must be completely read
if (strict and (get_token() != token_type::end_of_input))
{
sdp.parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input)));
}
// in case of an error, return discarded value
if (sdp.is_errored())
{
result = value_t::discarded;
return;
}
// set top-level value to null if it was discarded by the callback
// function
if (result.is_discarded())
{
result = nullptr;
}
}
else
{
json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
sax_parse_internal(&sdp);
result.assert_invariant();
// in strict mode, input must be completely read
if (strict and (get_token() != token_type::end_of_input))
{
sdp.parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input)));
}
// in case of an error, return discarded value
if (sdp.is_errored())
{
result = value_t::discarded;
return;
}
}
}
/*!
@brief public accept interface
@param[in] strict whether to expect the last token to be EOF
@return whether the input is a proper JSON text
*/
bool accept(const bool strict = true)
{
json_sax_acceptor<BasicJsonType> sax_acceptor;
return sax_parse(&sax_acceptor, strict);
}
bool sax_parse(json_sax_t* sax, const bool strict = true)
{
const bool result = sax_parse_internal(sax);
// strict mode: next byte must be EOF
if (result and strict and (get_token() != token_type::end_of_input))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input)));
}
return result;
}
private:
bool sax_parse_internal(json_sax_t* sax)
{
// stack to remember the hieararchy of structured values we are parsing
// true = array; false = object
std::vector<bool> states;
// value to avoid a goto (see comment where set to true)
bool skip_to_state_evaluation = false;
while (true)
{
if (not skip_to_state_evaluation)
{
// invariant: get_token() was called before each iteration
switch (last_token)
{
case token_type::begin_object:
{
if (JSON_UNLIKELY(not sax->start_object()))
{
return false;
}
// closing } -> we are done
if (get_token() == token_type::end_object)
{
if (JSON_UNLIKELY(not sax->end_object()))
{
return false;
}
break;
}
// parse key
if (JSON_UNLIKELY(last_token != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string)));
}
else
{
if (JSON_UNLIKELY(not sax->key(m_lexer.get_string())))
{
return false;
}
}
// parse separator (:)
if (JSON_UNLIKELY(get_token() != token_type::name_separator))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator)));
}
// remember we are now inside an object
states.push_back(false);
// parse values
get_token();
continue;
}
case token_type::begin_array:
{
if (JSON_UNLIKELY(not sax->start_array()))
{
return false;
}
// closing ] -> we are done
if (get_token() == token_type::end_array)
{
if (JSON_UNLIKELY(not sax->end_array()))
{
return false;
}
break;
}
// remember we are now inside an array
states.push_back(true);
// parse values (no need to call get_token)
continue;
}
case token_type::value_float:
{
const auto res = m_lexer.get_number_float();
if (JSON_UNLIKELY(not std::isfinite(res)))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'"));
}
else
{
if (JSON_UNLIKELY(not sax->number_float(res, m_lexer.get_string())))
{
return false;
}
break;
}
}
case token_type::literal_false:
{
if (JSON_UNLIKELY(not sax->boolean(false)))
{
return false;
}
break;
}
case token_type::literal_null:
{
if (JSON_UNLIKELY(not sax->null()))
{
return false;
}
break;
}
case token_type::literal_true:
{
if (JSON_UNLIKELY(not sax->boolean(true)))
{
return false;
}
break;
}
case token_type::value_integer:
{
if (JSON_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer())))
{
return false;
}
break;
}
case token_type::value_string:
{
if (JSON_UNLIKELY(not sax->string(m_lexer.get_string())))
{
return false;
}
break;
}
case token_type::value_unsigned:
{
if (JSON_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned())))
{
return false;
}
break;
}
case token_type::parse_error:
{
// using "uninitialized" to avoid "expected" message
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized)));
}
default: // the last token was unexpected
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value)));
}
}
}
else
{
skip_to_state_evaluation = false;
}
// we reached this line after we successfully parsed a value
if (states.empty())
{
// empty stack: we reached the end of the hieararchy: done
return true;
}
else
{
if (states.back()) // array
{
// comma -> next value
if (get_token() == token_type::value_separator)
{
// parse a new value
get_token();
continue;
}
// closing ]
if (JSON_LIKELY(last_token == token_type::end_array))
{
if (JSON_UNLIKELY(not sax->end_array()))
{
return false;
}
// We are done with this array. Before we can parse a
// new value, we need to evaluate the new state first.
// By setting skip_to_state_evaluation to false, we
// are effectively jumping to the beginning of this if.
assert(not states.empty());
states.pop_back();
skip_to_state_evaluation = true;
continue;
}
else
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array)));
}
}
else // object
{
// comma -> next value
if (get_token() == token_type::value_separator)
{
// parse key
if (JSON_UNLIKELY(get_token() != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string)));
}
else
{
if (JSON_UNLIKELY(not sax->key(m_lexer.get_string())))
{
return false;
}
}
// parse separator (:)
if (JSON_UNLIKELY(get_token() != token_type::name_separator))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator)));
}
// parse values
get_token();
continue;
}
// closing }
if (JSON_LIKELY(last_token == token_type::end_object))
{
if (JSON_UNLIKELY(not sax->end_object()))
{
return false;
}
// We are done with this object. Before we can parse a
// new value, we need to evaluate the new state first.
// By setting skip_to_state_evaluation to false, we
// are effectively jumping to the beginning of this if.
assert(not states.empty());
states.pop_back();
skip_to_state_evaluation = true;
continue;
}
else
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object)));
}
}
}
}
}
/// get next token from lexer
token_type get_token()
{
return (last_token = m_lexer.scan());
}
std::string exception_message(const token_type expected)
{
std::string error_msg = "syntax error - ";
if (last_token == token_type::parse_error)
{
error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" +
m_lexer.get_token_string() + "'";
}
else
{
error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token));
}
if (expected != token_type::uninitialized)
{
error_msg += "; expected " + std::string(lexer_t::token_type_name(expected));
}
return error_msg;
}
private:
/// callback function
const parser_callback_t callback = nullptr;
/// the type of the last read token
token_type last_token = token_type::uninitialized;
/// the lexer
lexer_t m_lexer;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
};
}
}

View File

@ -1,25 +0,0 @@
#pragma once
#include <nlohmann/detail/iterators/primitive_iterator.hpp>
namespace nlohmann
{
namespace detail
{
/*!
@brief an iterator value
@note This structure could easily be a union, but MSVC currently does not allow
unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
*/
template<typename BasicJsonType> struct internal_iterator
{
/// iterator for JSON objects
typename BasicJsonType::object_t::iterator object_iterator {};
/// iterator for JSON arrays
typename BasicJsonType::array_t::iterator array_iterator {};
/// generic iterator for all other types
primitive_iterator_t primitive_iterator {};
};
}
}

View File

@ -1,614 +0,0 @@
#pragma once
#include <ciso646> // not
#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
#include <type_traits> // conditional, is_const, remove_const
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/iterators/internal_iterator.hpp>
#include <nlohmann/detail/iterators/primitive_iterator.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
// forward declare, to be able to friend it later on
template<typename IteratorType> class iteration_proxy;
/*!
@brief a template for a bidirectional iterator for the @ref basic_json class
This class implements a both iterators (iterator and const_iterator) for the
@ref basic_json class.
@note An iterator is called *initialized* when a pointer to a JSON value has
been set (e.g., by a constructor or a copy assignment). If the iterator is
default-constructed, it is *uninitialized* and most methods are undefined.
**The library uses assertions to detect calls on uninitialized iterators.**
@requirement The class satisfies the following concept requirements:
-
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
The iterator that can be moved can be moved in both directions (i.e.
incremented and decremented).
@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
*/
template<typename BasicJsonType>
class iter_impl
{
/// allow basic_json to access private members
friend iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
friend BasicJsonType;
friend iteration_proxy<iter_impl>;
using object_t = typename BasicJsonType::object_t;
using array_t = typename BasicJsonType::array_t;
// make sure BasicJsonType is basic_json or const basic_json
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
"iter_impl only accepts (const) basic_json");
public:
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
/// The C++ Standard has never required user-defined iterators to derive from std::iterator.
/// A user-defined iterator should provide publicly accessible typedefs named
/// iterator_category, value_type, difference_type, pointer, and reference.
/// Note that value_type is required to be non-const, even for constant iterators.
using iterator_category = std::bidirectional_iterator_tag;
/// the type of the values when the iterator is dereferenced
using value_type = typename BasicJsonType::value_type;
/// a type to represent differences between iterators
using difference_type = typename BasicJsonType::difference_type;
/// defines a pointer to the type iterated over (value_type)
using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
typename BasicJsonType::const_pointer,
typename BasicJsonType::pointer>::type;
/// defines a reference to the type iterated over (value_type)
using reference =
typename std::conditional<std::is_const<BasicJsonType>::value,
typename BasicJsonType::const_reference,
typename BasicJsonType::reference>::type;
/// default constructor
iter_impl() = default;
/*!
@brief constructor for a given JSON instance
@param[in] object pointer to a JSON object for this iterator
@pre object != nullptr
@post The iterator is initialized; i.e. `m_object != nullptr`.
*/
explicit iter_impl(pointer object) noexcept : m_object(object)
{
assert(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
m_it.object_iterator = typename object_t::iterator();
break;
}
case value_t::array:
{
m_it.array_iterator = typename array_t::iterator();
break;
}
default:
{
m_it.primitive_iterator = primitive_iterator_t();
break;
}
}
}
/*!
@note The conventional copy constructor and copy assignment are implicitly
defined. Combined with the following converting constructor and
assignment, they support: (1) copy from iterator to iterator, (2)
copy from const iterator to const iterator, and (3) conversion from
iterator to const iterator. However conversion from const iterator
to iterator is not defined.
*/
/*!
@brief converting constructor
@param[in] other non-const iterator to copy from
@note It is not checked whether @a other is initialized.
*/
iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
: m_object(other.m_object), m_it(other.m_it) {}
/*!
@brief converting assignment
@param[in,out] other non-const iterator to copy from
@return const/non-const iterator
@note It is not checked whether @a other is initialized.
*/
iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
{
m_object = other.m_object;
m_it = other.m_it;
return *this;
}
private:
/*!
@brief set the iterator to the first value
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
void set_begin() noexcept
{
assert(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
m_it.object_iterator = m_object->m_value.object->begin();
break;
}
case value_t::array:
{
m_it.array_iterator = m_object->m_value.array->begin();
break;
}
case value_t::null:
{
// set to end so begin()==end() is true: null is empty
m_it.primitive_iterator.set_end();
break;
}
default:
{
m_it.primitive_iterator.set_begin();
break;
}
}
}
/*!
@brief set the iterator past the last value
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
void set_end() noexcept
{
assert(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
m_it.object_iterator = m_object->m_value.object->end();
break;
}
case value_t::array:
{
m_it.array_iterator = m_object->m_value.array->end();
break;
}
default:
{
m_it.primitive_iterator.set_end();
break;
}
}
}
public:
/*!
@brief return a reference to the value pointed to by the iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference operator*() const
{
assert(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
assert(m_it.object_iterator != m_object->m_value.object->end());
return m_it.object_iterator->second;
}
case value_t::array:
{
assert(m_it.array_iterator != m_object->m_value.array->end());
return *m_it.array_iterator;
}
case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
default:
{
if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))
{
return *m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
}
}
}
/*!
@brief dereference the iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
pointer operator->() const
{
assert(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
assert(m_it.object_iterator != m_object->m_value.object->end());
return &(m_it.object_iterator->second);
}
case value_t::array:
{
assert(m_it.array_iterator != m_object->m_value.array->end());
return &*m_it.array_iterator;
}
default:
{
if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))
{
return m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
}
}
}
/*!
@brief post-increment (it++)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl const operator++(int)
{
auto result = *this;
++(*this);
return result;
}
/*!
@brief pre-increment (++it)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator++()
{
assert(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
std::advance(m_it.object_iterator, 1);
break;
}
case value_t::array:
{
std::advance(m_it.array_iterator, 1);
break;
}
default:
{
++m_it.primitive_iterator;
break;
}
}
return *this;
}
/*!
@brief post-decrement (it--)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl const operator--(int)
{
auto result = *this;
--(*this);
return result;
}
/*!
@brief pre-decrement (--it)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator--()
{
assert(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
std::advance(m_it.object_iterator, -1);
break;
}
case value_t::array:
{
std::advance(m_it.array_iterator, -1);
break;
}
default:
{
--m_it.primitive_iterator;
break;
}
}
return *this;
}
/*!
@brief comparison: equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator==(const iter_impl& other) const
{
// if objects are not the same, the comparison is undefined
if (JSON_UNLIKELY(m_object != other.m_object))
{
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
}
assert(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
return (m_it.object_iterator == other.m_it.object_iterator);
case value_t::array:
return (m_it.array_iterator == other.m_it.array_iterator);
default:
return (m_it.primitive_iterator == other.m_it.primitive_iterator);
}
}
/*!
@brief comparison: not equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator!=(const iter_impl& other) const
{
return not operator==(other);
}
/*!
@brief comparison: smaller
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator<(const iter_impl& other) const
{
// if objects are not the same, the comparison is undefined
if (JSON_UNLIKELY(m_object != other.m_object))
{
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
}
assert(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators"));
case value_t::array:
return (m_it.array_iterator < other.m_it.array_iterator);
default:
return (m_it.primitive_iterator < other.m_it.primitive_iterator);
}
}
/*!
@brief comparison: less than or equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator<=(const iter_impl& other) const
{
return not other.operator < (*this);
}
/*!
@brief comparison: greater than
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator>(const iter_impl& other) const
{
return not operator<=(other);
}
/*!
@brief comparison: greater than or equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator>=(const iter_impl& other) const
{
return not operator<(other);
}
/*!
@brief add to iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator+=(difference_type i)
{
assert(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
case value_t::array:
{
std::advance(m_it.array_iterator, i);
break;
}
default:
{
m_it.primitive_iterator += i;
break;
}
}
return *this;
}
/*!
@brief subtract from iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator-=(difference_type i)
{
return operator+=(-i);
}
/*!
@brief add to iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl operator+(difference_type i) const
{
auto result = *this;
result += i;
return result;
}
/*!
@brief addition of distance and iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
friend iter_impl operator+(difference_type i, const iter_impl& it)
{
auto result = it;
result += i;
return result;
}
/*!
@brief subtract from iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl operator-(difference_type i) const
{
auto result = *this;
result -= i;
return result;
}
/*!
@brief return difference
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
difference_type operator-(const iter_impl& other) const
{
assert(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
case value_t::array:
return m_it.array_iterator - other.m_it.array_iterator;
default:
return m_it.primitive_iterator - other.m_it.primitive_iterator;
}
}
/*!
@brief access to successor
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference operator[](difference_type n) const
{
assert(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators"));
case value_t::array:
return *std::next(m_it.array_iterator, n);
case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
default:
{
if (JSON_LIKELY(m_it.primitive_iterator.get_value() == -n))
{
return *m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
}
}
}
/*!
@brief return the key of an object iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
const typename object_t::key_type& key() const
{
assert(m_object != nullptr);
if (JSON_LIKELY(m_object->is_object()))
{
return m_it.object_iterator->first;
}
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators"));
}
/*!
@brief return the value of an iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference value() const
{
return operator*();
}
private:
/// associated JSON instance
pointer m_object = nullptr;
/// the actual iterator of the associated instance
internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it;
};
}
}

View File

@ -1,128 +0,0 @@
#pragma once
#include <cstddef> // size_t
#include <string> // string, to_string
#include <iterator> // input_iterator_tag
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
/// proxy class for the items() function
template<typename IteratorType> class iteration_proxy
{
private:
/// helper class for iteration
class iteration_proxy_internal
{
public:
using difference_type = std::ptrdiff_t;
using value_type = iteration_proxy_internal;
using pointer = iteration_proxy_internal*;
using reference = iteration_proxy_internal&;
using iterator_category = std::input_iterator_tag;
private:
/// the iterator
IteratorType anchor;
/// an index for arrays (used to create key names)
std::size_t array_index = 0;
/// last stringified array index
mutable std::size_t array_index_last = 0;
/// a string representation of the array index
mutable std::string array_index_str = "0";
/// an empty string (to return a reference for primitive values)
const std::string empty_str = "";
public:
explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {}
iteration_proxy_internal(const iteration_proxy_internal&) = default;
iteration_proxy_internal& operator=(const iteration_proxy_internal&) = default;
/// dereference operator (needed for range-based for)
iteration_proxy_internal& operator*()
{
return *this;
}
/// increment operator (needed for range-based for)
iteration_proxy_internal& operator++()
{
++anchor;
++array_index;
return *this;
}
/// equality operator (needed for InputIterator)
bool operator==(const iteration_proxy_internal& o) const noexcept
{
return anchor == o.anchor;
}
/// inequality operator (needed for range-based for)
bool operator!=(const iteration_proxy_internal& o) const noexcept
{
return anchor != o.anchor;
}
/// return key of the iterator
const std::string& key() const
{
assert(anchor.m_object != nullptr);
switch (anchor.m_object->type())
{
// use integer array index as key
case value_t::array:
{
if (array_index != array_index_last)
{
array_index_str = std::to_string(array_index);
array_index_last = array_index;
}
return array_index_str;
}
// use key from the object
case value_t::object:
return anchor.key();
// use an empty key for all primitive types
default:
return empty_str;
}
}
/// return value of the iterator
typename IteratorType::reference value() const
{
return anchor.value();
}
};
/// the container to iterate
typename IteratorType::reference container;
public:
/// construct iteration proxy from a container
explicit iteration_proxy(typename IteratorType::reference cont) noexcept
: container(cont) {}
/// return iterator begin (needed for range-based for)
iteration_proxy_internal begin() noexcept
{
return iteration_proxy_internal(container.begin());
}
/// return iterator end (needed for range-based for)
iteration_proxy_internal end() noexcept
{
return iteration_proxy_internal(container.end());
}
};
}
}

View File

@ -1,119 +0,0 @@
#pragma once
#include <cstddef> // ptrdiff_t
#include <iterator> // reverse_iterator
#include <utility> // declval
namespace nlohmann
{
namespace detail
{
//////////////////////
// reverse_iterator //
//////////////////////
/*!
@brief a template for a reverse iterator class
@tparam Base the base iterator type to reverse. Valid types are @ref
iterator (to create @ref reverse_iterator) and @ref const_iterator (to
create @ref const_reverse_iterator).
@requirement The class satisfies the following concept requirements:
-
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
The iterator that can be moved can be moved in both directions (i.e.
incremented and decremented).
- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
It is possible to write to the pointed-to element (only if @a Base is
@ref iterator).
@since version 1.0.0
*/
template<typename Base>
class json_reverse_iterator : public std::reverse_iterator<Base>
{
public:
using difference_type = std::ptrdiff_t;
/// shortcut to the reverse iterator adapter
using base_iterator = std::reverse_iterator<Base>;
/// the reference type for the pointed-to element
using reference = typename Base::reference;
/// create reverse iterator from iterator
explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
: base_iterator(it) {}
/// create reverse iterator from base class
explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
/// post-increment (it++)
json_reverse_iterator const operator++(int)
{
return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
}
/// pre-increment (++it)
json_reverse_iterator& operator++()
{
return static_cast<json_reverse_iterator&>(base_iterator::operator++());
}
/// post-decrement (it--)
json_reverse_iterator const operator--(int)
{
return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
}
/// pre-decrement (--it)
json_reverse_iterator& operator--()
{
return static_cast<json_reverse_iterator&>(base_iterator::operator--());
}
/// add to iterator
json_reverse_iterator& operator+=(difference_type i)
{
return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
}
/// add to iterator
json_reverse_iterator operator+(difference_type i) const
{
return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
}
/// subtract from iterator
json_reverse_iterator operator-(difference_type i) const
{
return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
}
/// return difference
difference_type operator-(const json_reverse_iterator& other) const
{
return base_iterator(*this) - base_iterator(other);
}
/// access to successor
reference operator[](difference_type n) const
{
return *(this->operator+(n));
}
/// return the key of an object iterator
auto key() const -> decltype(std::declval<Base>().key())
{
auto it = --this->base();
return it.key();
}
/// return the value of an iterator
reference value() const
{
auto it = --this->base();
return it.operator * ();
}
};
}
}

View File

@ -1,120 +0,0 @@
#pragma once
#include <cstddef> // ptrdiff_t
#include <limits> // numeric_limits
namespace nlohmann
{
namespace detail
{
/*
@brief an iterator for primitive JSON types
This class models an iterator for primitive JSON types (boolean, number,
string). It's only purpose is to allow the iterator/const_iterator classes
to "iterate" over primitive values. Internally, the iterator is modeled by
a `difference_type` variable. Value begin_value (`0`) models the begin,
end_value (`1`) models past the end.
*/
class primitive_iterator_t
{
private:
using difference_type = std::ptrdiff_t;
static constexpr difference_type begin_value = 0;
static constexpr difference_type end_value = begin_value + 1;
/// iterator as signed integer type
difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
public:
constexpr difference_type get_value() const noexcept
{
return m_it;
}
/// set iterator to a defined beginning
void set_begin() noexcept
{
m_it = begin_value;
}
/// set iterator to a defined past the end
void set_end() noexcept
{
m_it = end_value;
}
/// return whether the iterator can be dereferenced
constexpr bool is_begin() const noexcept
{
return m_it == begin_value;
}
/// return whether the iterator is at end
constexpr bool is_end() const noexcept
{
return m_it == end_value;
}
friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
{
return lhs.m_it == rhs.m_it;
}
friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
{
return lhs.m_it < rhs.m_it;
}
primitive_iterator_t operator+(difference_type n) noexcept
{
auto result = *this;
result += n;
return result;
}
friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
{
return lhs.m_it - rhs.m_it;
}
primitive_iterator_t& operator++() noexcept
{
++m_it;
return *this;
}
primitive_iterator_t const operator++(int) noexcept
{
auto result = *this;
++m_it;
return result;
}
primitive_iterator_t& operator--() noexcept
{
--m_it;
return *this;
}
primitive_iterator_t const operator--(int) noexcept
{
auto result = *this;
--m_it;
return result;
}
primitive_iterator_t& operator+=(difference_type n) noexcept
{
m_it += n;
return *this;
}
primitive_iterator_t& operator-=(difference_type n) noexcept
{
m_it -= n;
return *this;
}
};
}
}

View File

@ -1,696 +0,0 @@
#pragma once
#include <cassert> // assert
#include <numeric> // accumulate
#include <string> // string
#include <vector> // vector
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
template<typename BasicJsonType>
class json_pointer
{
// allow basic_json to access private members
NLOHMANN_BASIC_JSON_TPL_DECLARATION
friend class basic_json;
public:
/*!
@brief create JSON pointer
Create a JSON pointer according to the syntax described in
[Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
@param[in] s string representing the JSON pointer; if omitted, the empty
string is assumed which references the whole JSON value
@throw parse_error.107 if the given JSON pointer @a s is nonempty and does
not begin with a slash (`/`); see example below
@throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is
not followed by `0` (representing `~`) or `1` (representing `/`); see
example below
@liveexample{The example shows the construction several valid JSON pointers
as well as the exceptional behavior.,json_pointer}
@since version 2.0.0
*/
explicit json_pointer(const std::string& s = "")
: reference_tokens(split(s))
{}
/*!
@brief return a string representation of the JSON pointer
@invariant For each JSON pointer `ptr`, it holds:
@code {.cpp}
ptr == json_pointer(ptr.to_string());
@endcode
@return a string representation of the JSON pointer
@liveexample{The example shows the result of `to_string`.,
json_pointer__to_string}
@since version 2.0.0
*/
std::string to_string() const noexcept
{
return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
std::string{},
[](const std::string & a, const std::string & b)
{
return a + "/" + escape(b);
});
}
/// @copydoc to_string()
operator std::string() const
{
return to_string();
}
/*!
@param[in] s reference token to be converted into an array index
@return integer representation of @a s
@throw out_of_range.404 if string @a s could not be converted to an integer
*/
static int array_index(const std::string& s)
{
std::size_t processed_chars = 0;
const int res = std::stoi(s, &processed_chars);
// check if the string was completely read
if (JSON_UNLIKELY(processed_chars != s.size()))
{
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
}
return res;
}
private:
/*!
@brief remove and return last reference pointer
@throw out_of_range.405 if JSON pointer has no parent
*/
std::string pop_back()
{
if (JSON_UNLIKELY(is_root()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
}
auto last = reference_tokens.back();
reference_tokens.pop_back();
return last;
}
/// return whether pointer points to the root document
bool is_root() const
{
return reference_tokens.empty();
}
json_pointer top() const
{
if (JSON_UNLIKELY(is_root()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
}
json_pointer result = *this;
result.reference_tokens = {reference_tokens[0]};
return result;
}
/*!
@brief create and return a reference to the pointed to value
@complexity Linear in the number of reference tokens.
@throw parse_error.109 if array index is not a number
@throw type_error.313 if value cannot be unflattened
*/
BasicJsonType& get_and_create(BasicJsonType& j) const
{
using size_type = typename BasicJsonType::size_type;
auto result = &j;
// in case no reference tokens exist, return a reference to the JSON value
// j which will be overwritten by a primitive value
for (const auto& reference_token : reference_tokens)
{
switch (result->m_type)
{
case detail::value_t::null:
{
if (reference_token == "0")
{
// start a new array if reference token is 0
result = &result->operator[](0);
}
else
{
// start a new object otherwise
result = &result->operator[](reference_token);
}
break;
}
case detail::value_t::object:
{
// create an entry in the object
result = &result->operator[](reference_token);
break;
}
case detail::value_t::array:
{
// create an entry in the array
JSON_TRY
{
result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
break;
}
/*
The following code is only reached if there exists a reference
token _and_ the current value is primitive. In this case, we have
an error situation, because primitive values may only occur as
single value; that is, with an empty list of reference tokens.
*/
default:
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
}
}
return *result;
}
/*!
@brief return a reference to the pointed to value
@note This version does not throw if a value is not present, but tries to
create nested values instead. For instance, calling this function
with pointer `"/this/that"` on a null value is equivalent to calling
`operator[]("this").operator[]("that")` on that value, effectively
changing the null value to an object.
@param[in] ptr a JSON value
@return reference to the JSON value pointed to by the JSON pointer
@complexity Linear in the length of the JSON pointer.
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
BasicJsonType& get_unchecked(BasicJsonType* ptr) const
{
using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
// convert null values to arrays or objects before continuing
if (ptr->m_type == detail::value_t::null)
{
// check if reference token is a number
const bool nums =
std::all_of(reference_token.begin(), reference_token.end(),
[](const char x)
{
return (x >= '0' and x <= '9');
});
// change value to array for numbers or "-" or to object otherwise
*ptr = (nums or reference_token == "-")
? detail::value_t::array
: detail::value_t::object;
}
switch (ptr->m_type)
{
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + reference_token +
"' must not begin with '0'"));
}
if (reference_token == "-")
{
// explicitly treat "-" as index beyond the end
ptr = &ptr->operator[](ptr->m_value.array->size());
}
else
{
// convert array index to number; unchecked access
JSON_TRY
{
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
/*!
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
BasicJsonType& get_checked(BasicJsonType* ptr) const
{
using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + reference_token +
"' must not begin with '0'"));
}
// note: at performs range check
JSON_TRY
{
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
/*!
@brief return a const reference to the pointed to value
@param[in] ptr a JSON value
@return const reference to the JSON value pointed to by the JSON
pointer
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
{
using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" cannot be used for const access
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + reference_token +
"' must not begin with '0'"));
}
// use unchecked array access
JSON_TRY
{
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
/*!
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
const BasicJsonType& get_checked(const BasicJsonType* ptr) const
{
using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + reference_token +
"' must not begin with '0'"));
}
// note: at performs range check
JSON_TRY
{
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
/*!
@brief split the string input to reference tokens
@note This function is only called by the json_pointer constructor.
All exceptions below are documented there.
@throw parse_error.107 if the pointer is not empty or begins with '/'
@throw parse_error.108 if character '~' is not followed by '0' or '1'
*/
static std::vector<std::string> split(const std::string& reference_string)
{
std::vector<std::string> result;
// special case: empty reference string -> no reference tokens
if (reference_string.empty())
{
return result;
}
// check if nonempty reference string begins with slash
if (JSON_UNLIKELY(reference_string[0] != '/'))
{
JSON_THROW(detail::parse_error::create(107, 1,
"JSON pointer must be empty or begin with '/' - was: '" +
reference_string + "'"));
}
// extract the reference tokens:
// - slash: position of the last read slash (or end of string)
// - start: position after the previous slash
for (
// search for the first slash after the first character
std::size_t slash = reference_string.find_first_of('/', 1),
// set the beginning of the first reference token
start = 1;
// we can stop if start == string::npos+1 = 0
start != 0;
// set the beginning of the next reference token
// (will eventually be 0 if slash == std::string::npos)
start = slash + 1,
// find next slash
slash = reference_string.find_first_of('/', start))
{
// use the text between the beginning of the reference token
// (start) and the last slash (slash).
auto reference_token = reference_string.substr(start, slash - start);
// check reference tokens are properly escaped
for (std::size_t pos = reference_token.find_first_of('~');
pos != std::string::npos;
pos = reference_token.find_first_of('~', pos + 1))
{
assert(reference_token[pos] == '~');
// ~ must be followed by 0 or 1
if (JSON_UNLIKELY(pos == reference_token.size() - 1 or
(reference_token[pos + 1] != '0' and
reference_token[pos + 1] != '1')))
{
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
}
}
// finally, store the reference token
unescape(reference_token);
result.push_back(reference_token);
}
return result;
}
/*!
@brief replace all occurrences of a substring by another string
@param[in,out] s the string to manipulate; changed so that all
occurrences of @a f are replaced with @a t
@param[in] f the substring to replace with @a t
@param[in] t the string to replace @a f
@pre The search string @a f must not be empty. **This precondition is
enforced with an assertion.**
@since version 2.0.0
*/
static void replace_substring(std::string& s, const std::string& f,
const std::string& t)
{
assert(not f.empty());
for (auto pos = s.find(f); // find first occurrence of f
pos != std::string::npos; // make sure f was found
s.replace(pos, f.size(), t), // replace with t, and
pos = s.find(f, pos + t.size())) // find next occurrence of f
{}
}
/// escape "~"" to "~0" and "/" to "~1"
static std::string escape(std::string s)
{
replace_substring(s, "~", "~0");
replace_substring(s, "/", "~1");
return s;
}
/// unescape "~1" to tilde and "~0" to slash (order is important!)
static void unescape(std::string& s)
{
replace_substring(s, "~1", "/");
replace_substring(s, "~0", "~");
}
/*!
@param[in] reference_string the reference string to the current value
@param[in] value the value to consider
@param[in,out] result the result object to insert values to
@note Empty objects or arrays are flattened to `null`.
*/
static void flatten(const std::string& reference_string,
const BasicJsonType& value,
BasicJsonType& result)
{
switch (value.m_type)
{
case detail::value_t::array:
{
if (value.m_value.array->empty())
{
// flatten empty array as null
result[reference_string] = nullptr;
}
else
{
// iterate array and use index as reference string
for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
{
flatten(reference_string + "/" + std::to_string(i),
value.m_value.array->operator[](i), result);
}
}
break;
}
case detail::value_t::object:
{
if (value.m_value.object->empty())
{
// flatten empty object as null
result[reference_string] = nullptr;
}
else
{
// iterate object and use keys as reference string
for (const auto& element : *value.m_value.object)
{
flatten(reference_string + "/" + escape(element.first), element.second, result);
}
}
break;
}
default:
{
// add primitive value with its reference string
result[reference_string] = value;
break;
}
}
}
/*!
@param[in] value flattened JSON
@return unflattened JSON
@throw parse_error.109 if array index is not a number
@throw type_error.314 if value is not an object
@throw type_error.315 if object values are not primitive
@throw type_error.313 if value cannot be unflattened
*/
static BasicJsonType
unflatten(const BasicJsonType& value)
{
if (JSON_UNLIKELY(not value.is_object()))
{
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
}
BasicJsonType result;
// iterate the JSON object values
for (const auto& element : *value.m_value.object)
{
if (JSON_UNLIKELY(not element.second.is_primitive()))
{
JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
}
// assign value to reference pointed to by JSON pointer; Note that if
// the JSON pointer is "" (i.e., points to the whole value), function
// get_and_create returns a reference to result itself. An assignment
// will then create a primitive value.
json_pointer(element.first).get_and_create(result) = element.second;
}
return result;
}
friend bool operator==(json_pointer const& lhs,
json_pointer const& rhs) noexcept
{
return (lhs.reference_tokens == rhs.reference_tokens);
}
friend bool operator!=(json_pointer const& lhs,
json_pointer const& rhs) noexcept
{
return not (lhs == rhs);
}
/// the reference tokens
std::vector<std::string> reference_tokens;
};
}

View File

@ -1,63 +0,0 @@
#pragma once
#include <initializer_list>
#include <utility>
namespace nlohmann
{
namespace detail
{
template<typename BasicJsonType>
class json_ref
{
public:
using value_type = BasicJsonType;
json_ref(value_type&& value)
: owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true)
{}
json_ref(const value_type& value)
: value_ref(const_cast<value_type*>(&value)), is_rvalue(false)
{}
json_ref(std::initializer_list<json_ref> init)
: owned_value(init), value_ref(&owned_value), is_rvalue(true)
{}
template<class... Args>
json_ref(Args&& ... args)
: owned_value(std::forward<Args>(args)...), value_ref(&owned_value), is_rvalue(true)
{}
// class should be movable only
json_ref(json_ref&&) = default;
json_ref(const json_ref&) = delete;
json_ref& operator=(const json_ref&) = delete;
value_type moved_or_copied() const
{
if (is_rvalue)
{
return std::move(*value_ref);
}
return *value_ref;
}
value_type const& operator*() const
{
return *static_cast<value_type const*>(value_ref);
}
value_type const* operator->() const
{
return static_cast<value_type const*>(value_ref);
}
private:
mutable value_type owned_value = nullptr;
value_type* value_ref = nullptr;
const bool is_rvalue;
};
}
}

View File

@ -1,117 +0,0 @@
#pragma once
// This file contains all internal macro definitions
// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
// exclude unsupported compilers
#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)
#if defined(__clang__)
#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
#endif
#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900
#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
#endif
#endif
#endif
// disable float-equal warnings on GCC/clang
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
// disable documentation warnings on clang
#if defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdocumentation"
#endif
// allow for portable deprecation warnings
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#define JSON_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define JSON_DEPRECATED __declspec(deprecated)
#else
#define JSON_DEPRECATED
#endif
// allow to disable exceptions
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
#define JSON_THROW(exception) throw exception
#define JSON_TRY try
#define JSON_CATCH(exception) catch(exception)
#else
#define JSON_THROW(exception) std::abort()
#define JSON_TRY if(true)
#define JSON_CATCH(exception) if(false)
#endif
// override exception macros
#if defined(JSON_THROW_USER)
#undef JSON_THROW
#define JSON_THROW JSON_THROW_USER
#endif
#if defined(JSON_TRY_USER)
#undef JSON_TRY
#define JSON_TRY JSON_TRY_USER
#endif
#if defined(JSON_CATCH_USER)
#undef JSON_CATCH
#define JSON_CATCH JSON_CATCH_USER
#endif
// manual branch prediction
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#define JSON_LIKELY(x) __builtin_expect(!!(x), 1)
#define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
#else
#define JSON_LIKELY(x) x
#define JSON_UNLIKELY(x) x
#endif
// C++ language standard detection
#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
#define JSON_HAS_CPP_17
#define JSON_HAS_CPP_14
#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
#define JSON_HAS_CPP_14
#endif
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
// may be removed in the future once the class is split.
#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \
template<template<typename, typename, typename...> class ObjectType, \
template<typename, typename...> class ArrayType, \
class StringType, class BooleanType, class NumberIntegerType, \
class NumberUnsignedType, class NumberFloatType, \
template<typename> class AllocatorType, \
template<typename, typename = void> class JSONSerializer>
#define NLOHMANN_BASIC_JSON_TPL \
basic_json<ObjectType, ArrayType, StringType, BooleanType, \
NumberIntegerType, NumberUnsignedType, NumberFloatType, \
AllocatorType, JSONSerializer>
/*!
@brief Helper to determine whether there's a key_type for T.
This helper is used to tell associative containers apart from other containers
such as sequence containers. For instance, `std::map` passes the test as it
contains a `mapped_type`, whereas `std::vector` fails the test.
@sa http://stackoverflow.com/a/7728728/266378
@since version 1.0.0, overworked in version 2.0.6
*/
#define NLOHMANN_JSON_HAS_HELPER(type) \
template<typename T> struct has_##type { \
private: \
template<typename U, typename = typename U::type> \
static int detect(U &&); \
static void detect(...); \
public: \
static constexpr bool value = \
std::is_integral<decltype(detect(std::declval<T>()))>::value; \
}

View File

@ -1,22 +0,0 @@
#pragma once
// restore GCC/clang diagnostic settings
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic pop
#endif
#if defined(__clang__)
#pragma GCC diagnostic pop
#endif
// clean up
#undef JSON_CATCH
#undef JSON_THROW
#undef JSON_TRY
#undef JSON_LIKELY
#undef JSON_UNLIKELY
#undef JSON_DEPRECATED
#undef JSON_HAS_CPP_14
#undef JSON_HAS_CPP_17
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
#undef NLOHMANN_BASIC_JSON_TPL
#undef NLOHMANN_JSON_HAS_HELPER

View File

@ -1,278 +0,0 @@
#pragma once
#include <ciso646> // not
#include <cstddef> // size_t
#include <limits> // numeric_limits
#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
#include <utility> // declval
#include <nlohmann/json_fwd.hpp>
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
/*!
@brief detail namespace with internal helper functions
This namespace collects functions that should not be exposed,
implementations of some @ref basic_json methods, and meta-programming helpers.
@since version 2.1.0
*/
namespace detail
{
/////////////
// helpers //
/////////////
template<typename> struct is_basic_json : std::false_type {};
NLOHMANN_BASIC_JSON_TPL_DECLARATION
struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
// alias templates to reduce boilerplate
template<bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
template<typename T>
using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
// implementation of C++14 index_sequence and affiliates
// source: https://stackoverflow.com/a/32223343
template<std::size_t... Ints>
struct index_sequence
{
using type = index_sequence;
using value_type = std::size_t;
static constexpr std::size_t size() noexcept
{
return sizeof...(Ints);
}
};
template<class Sequence1, class Sequence2>
struct merge_and_renumber;
template<std::size_t... I1, std::size_t... I2>
struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
: index_sequence < I1..., (sizeof...(I1) + I2)... > {};
template<std::size_t N>
struct make_index_sequence
: merge_and_renumber < typename make_index_sequence < N / 2 >::type,
typename make_index_sequence < N - N / 2 >::type > {};
template<> struct make_index_sequence<0> : index_sequence<> {};
template<> struct make_index_sequence<1> : index_sequence<0> {};
template<typename... Ts>
using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
/*
Implementation of two C++17 constructs: conjunction, negation. This is needed
to avoid evaluating all the traits in a condition
For example: not std::is_same<void, T>::value and has_value_type<T>::value
will not compile when T = void (on MSVC at least). Whereas
conjunction<negation<std::is_same<void, T>>, has_value_type<T>>::value will
stop evaluating if negation<...>::value == false
Please note that those constructs must be used with caution, since symbols can
become very long quickly (which can slow down compilation and cause MSVC
internal compiler errors). Only use it when you have to (see example ahead).
*/
template<class...> struct conjunction : std::true_type {};
template<class B1> struct conjunction<B1> : B1 {};
template<class B1, class... Bn>
struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
template<class B> struct negation : std::integral_constant<bool, not B::value> {};
// dispatch utility (taken from ranges-v3)
template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
template<> struct priority_tag<0> {};
////////////////////////
// has_/is_ functions //
////////////////////////
// source: https://stackoverflow.com/a/37193089/4116453
template <typename T, typename = void>
struct is_complete_type : std::false_type {};
template <typename T>
struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
NLOHMANN_JSON_HAS_HELPER(mapped_type);
NLOHMANN_JSON_HAS_HELPER(key_type);
NLOHMANN_JSON_HAS_HELPER(value_type);
NLOHMANN_JSON_HAS_HELPER(iterator);
template<bool B, class RealType, class CompatibleObjectType>
struct is_compatible_object_type_impl : std::false_type {};
template<class RealType, class CompatibleObjectType>
struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType>
{
static constexpr auto value =
std::is_constructible<typename RealType::key_type, typename CompatibleObjectType::key_type>::value and
std::is_constructible<typename RealType::mapped_type, typename CompatibleObjectType::mapped_type>::value;
};
template<bool B, class RealType, class CompatibleStringType>
struct is_compatible_string_type_impl : std::false_type {};
template<class RealType, class CompatibleStringType>
struct is_compatible_string_type_impl<true, RealType, CompatibleStringType>
{
static constexpr auto value =
std::is_same<typename RealType::value_type, typename CompatibleStringType::value_type>::value and
std::is_constructible<RealType, CompatibleStringType>::value;
};
template<class BasicJsonType, class CompatibleObjectType>
struct is_compatible_object_type
{
static auto constexpr value = is_compatible_object_type_impl <
conjunction<negation<std::is_same<void, CompatibleObjectType>>,
has_mapped_type<CompatibleObjectType>,
has_key_type<CompatibleObjectType>>::value,
typename BasicJsonType::object_t, CompatibleObjectType >::value;
};
template<class BasicJsonType, class CompatibleStringType>
struct is_compatible_string_type
{
static auto constexpr value = is_compatible_string_type_impl <
conjunction<negation<std::is_same<void, CompatibleStringType>>,
has_value_type<CompatibleStringType>>::value,
typename BasicJsonType::string_t, CompatibleStringType >::value;
};
template<typename BasicJsonType, typename T>
struct is_basic_json_nested_type
{
static auto constexpr value = std::is_same<T, typename BasicJsonType::iterator>::value or
std::is_same<T, typename BasicJsonType::const_iterator>::value or
std::is_same<T, typename BasicJsonType::reverse_iterator>::value or
std::is_same<T, typename BasicJsonType::const_reverse_iterator>::value;
};
template<class BasicJsonType, class CompatibleArrayType>
struct is_compatible_array_type
{
static auto constexpr value =
conjunction<negation<std::is_same<void, CompatibleArrayType>>,
negation<is_compatible_object_type<
BasicJsonType, CompatibleArrayType>>,
negation<std::is_constructible<typename BasicJsonType::string_t,
CompatibleArrayType>>,
negation<is_basic_json_nested_type<BasicJsonType, CompatibleArrayType>>,
has_value_type<CompatibleArrayType>,
has_iterator<CompatibleArrayType>>::value;
};
template<bool, typename, typename>
struct is_compatible_integer_type_impl : std::false_type {};
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIntegerType>
{
// is there an assert somewhere on overflows?
using RealLimits = std::numeric_limits<RealIntegerType>;
using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
static constexpr auto value =
std::is_constructible<RealIntegerType, CompatibleNumberIntegerType>::value and
CompatibleLimits::is_integer and
RealLimits::is_signed == CompatibleLimits::is_signed;
};
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
struct is_compatible_integer_type
{
static constexpr auto value =
is_compatible_integer_type_impl <
std::is_integral<CompatibleNumberIntegerType>::value and
not std::is_same<bool, CompatibleNumberIntegerType>::value,
RealIntegerType, CompatibleNumberIntegerType > ::value;
};
// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
template<typename BasicJsonType, typename T>
struct has_from_json
{
private:
// also check the return type of from_json
template<typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json(
std::declval<BasicJsonType>(), std::declval<T&>()))>::value>>
static int detect(U&&);
static void detect(...);
public:
static constexpr bool value = std::is_integral<decltype(
detect(std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
};
// This trait checks if JSONSerializer<T>::from_json(json const&) exists
// this overload is used for non-default-constructible user-defined-types
template<typename BasicJsonType, typename T>
struct has_non_default_from_json
{
private:
template <
typename U,
typename = enable_if_t<std::is_same<
T, decltype(uncvref_t<U>::from_json(std::declval<BasicJsonType>()))>::value >>
static int detect(U&&);
static void detect(...);
public:
static constexpr bool value = std::is_integral<decltype(detect(
std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
};
// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
template<typename BasicJsonType, typename T>
struct has_to_json
{
private:
template<typename U, typename = decltype(uncvref_t<U>::to_json(
std::declval<BasicJsonType&>(), std::declval<T>()))>
static int detect(U&&);
static void detect(...);
public:
static constexpr bool value = std::is_integral<decltype(detect(
std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
};
template <typename BasicJsonType, typename CompatibleCompleteType>
struct is_compatible_complete_type
{
static constexpr bool value =
not std::is_base_of<std::istream, CompatibleCompleteType>::value and
not is_basic_json<CompatibleCompleteType>::value and
not is_basic_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and
has_to_json<BasicJsonType, CompatibleCompleteType>::value;
};
template <typename BasicJsonType, typename CompatibleType>
struct is_compatible_type
: conjunction<is_complete_type<CompatibleType>,
is_compatible_complete_type<BasicJsonType, CompatibleType>>
{
};
// taken from ranges-v3
template<typename T>
struct static_const
{
static constexpr T value{};
};
template<typename T>
constexpr T static_const<T>::value;
}
}

View File

@ -1,949 +0,0 @@
#pragma once
#include <algorithm> // reverse
#include <array> // array
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
#include <cstring> // memcpy
#include <limits> // numeric_limits
#include <nlohmann/detail/input/binary_reader.hpp>
#include <nlohmann/detail/output/output_adapters.hpp>
namespace nlohmann
{
namespace detail
{
///////////////////
// binary writer //
///////////////////
/*!
@brief serialization to CBOR and MessagePack values
*/
template<typename BasicJsonType, typename CharType>
class binary_writer
{
public:
/*!
@brief create a binary writer
@param[in] adapter output adapter to write to
*/
explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)
{
assert(oa);
}
/*!
@brief[in] j JSON value to serialize
*/
void write_cbor(const BasicJsonType& j)
{
switch (j.type())
{
case value_t::null:
{
oa->write_character(static_cast<CharType>(0xF6));
break;
}
case value_t::boolean:
{
oa->write_character(j.m_value.boolean
? static_cast<CharType>(0xF5)
: static_cast<CharType>(0xF4));
break;
}
case value_t::number_integer:
{
if (j.m_value.number_integer >= 0)
{
// CBOR does not differentiate between positive signed
// integers and unsigned integers. Therefore, we used the
// code from the value_t::number_unsigned case here.
if (j.m_value.number_integer <= 0x17)
{
write_number(static_cast<uint8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0x18));
write_number(static_cast<uint8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0x19));
write_number(static_cast<uint16_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0x1A));
write_number(static_cast<uint32_t>(j.m_value.number_integer));
}
else
{
oa->write_character(static_cast<CharType>(0x1B));
write_number(static_cast<uint64_t>(j.m_value.number_integer));
}
}
else
{
// The conversions below encode the sign in the first
// byte, and the value is converted to a positive number.
const auto positive_number = -1 - j.m_value.number_integer;
if (j.m_value.number_integer >= -24)
{
write_number(static_cast<uint8_t>(0x20 + positive_number));
}
else if (positive_number <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0x38));
write_number(static_cast<uint8_t>(positive_number));
}
else if (positive_number <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0x39));
write_number(static_cast<uint16_t>(positive_number));
}
else if (positive_number <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0x3A));
write_number(static_cast<uint32_t>(positive_number));
}
else
{
oa->write_character(static_cast<CharType>(0x3B));
write_number(static_cast<uint64_t>(positive_number));
}
}
break;
}
case value_t::number_unsigned:
{
if (j.m_value.number_unsigned <= 0x17)
{
write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0x18));
write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0x19));
write_number(static_cast<uint16_t>(j.m_value.number_unsigned));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0x1A));
write_number(static_cast<uint32_t>(j.m_value.number_unsigned));
}
else
{
oa->write_character(static_cast<CharType>(0x1B));
write_number(static_cast<uint64_t>(j.m_value.number_unsigned));
}
break;
}
case value_t::number_float:
{
oa->write_character(get_cbor_float_prefix(j.m_value.number_float));
write_number(j.m_value.number_float);
break;
}
case value_t::string:
{
// step 1: write control byte and the string length
const auto N = j.m_value.string->size();
if (N <= 0x17)
{
write_number(static_cast<uint8_t>(0x60 + N));
}
else if (N <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0x78));
write_number(static_cast<uint8_t>(N));
}
else if (N <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0x79));
write_number(static_cast<uint16_t>(N));
}
else if (N <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0x7A));
write_number(static_cast<uint32_t>(N));
}
// LCOV_EXCL_START
else if (N <= (std::numeric_limits<uint64_t>::max)())
{
oa->write_character(static_cast<CharType>(0x7B));
write_number(static_cast<uint64_t>(N));
}
// LCOV_EXCL_STOP
// step 2: write the string
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
j.m_value.string->size());
break;
}
case value_t::array:
{
// step 1: write control byte and the array size
const auto N = j.m_value.array->size();
if (N <= 0x17)
{
write_number(static_cast<uint8_t>(0x80 + N));
}
else if (N <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0x98));
write_number(static_cast<uint8_t>(N));
}
else if (N <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0x99));
write_number(static_cast<uint16_t>(N));
}
else if (N <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0x9A));
write_number(static_cast<uint32_t>(N));
}
// LCOV_EXCL_START
else if (N <= (std::numeric_limits<uint64_t>::max)())
{
oa->write_character(static_cast<CharType>(0x9B));
write_number(static_cast<uint64_t>(N));
}
// LCOV_EXCL_STOP
// step 2: write each element
for (const auto& el : *j.m_value.array)
{
write_cbor(el);
}
break;
}
case value_t::object:
{
// step 1: write control byte and the object size
const auto N = j.m_value.object->size();
if (N <= 0x17)
{
write_number(static_cast<uint8_t>(0xA0 + N));
}
else if (N <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0xB8));
write_number(static_cast<uint8_t>(N));
}
else if (N <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0xB9));
write_number(static_cast<uint16_t>(N));
}
else if (N <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0xBA));
write_number(static_cast<uint32_t>(N));
}
// LCOV_EXCL_START
else if (N <= (std::numeric_limits<uint64_t>::max)())
{
oa->write_character(static_cast<CharType>(0xBB));
write_number(static_cast<uint64_t>(N));
}
// LCOV_EXCL_STOP
// step 2: write each element
for (const auto& el : *j.m_value.object)
{
write_cbor(el.first);
write_cbor(el.second);
}
break;
}
default:
break;
}
}
/*!
@brief[in] j JSON value to serialize
*/
void write_msgpack(const BasicJsonType& j)
{
switch (j.type())
{
case value_t::null: // nil
{
oa->write_character(static_cast<CharType>(0xC0));
break;
}
case value_t::boolean: // true and false
{
oa->write_character(j.m_value.boolean
? static_cast<CharType>(0xC3)
: static_cast<CharType>(0xC2));
break;
}
case value_t::number_integer:
{
if (j.m_value.number_integer >= 0)
{
// MessagePack does not differentiate between positive
// signed integers and unsigned integers. Therefore, we used
// the code from the value_t::number_unsigned case here.
if (j.m_value.number_unsigned < 128)
{
// positive fixnum
write_number(static_cast<uint8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
{
// uint 8
oa->write_character(static_cast<CharType>(0xCC));
write_number(static_cast<uint8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
{
// uint 16
oa->write_character(static_cast<CharType>(0xCD));
write_number(static_cast<uint16_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
{
// uint 32
oa->write_character(static_cast<CharType>(0xCE));
write_number(static_cast<uint32_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
{
// uint 64
oa->write_character(static_cast<CharType>(0xCF));
write_number(static_cast<uint64_t>(j.m_value.number_integer));
}
}
else
{
if (j.m_value.number_integer >= -32)
{
// negative fixnum
write_number(static_cast<int8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer >= (std::numeric_limits<int8_t>::min)() and
j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
{
// int 8
oa->write_character(static_cast<CharType>(0xD0));
write_number(static_cast<int8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer >= (std::numeric_limits<int16_t>::min)() and
j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
{
// int 16
oa->write_character(static_cast<CharType>(0xD1));
write_number(static_cast<int16_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer >= (std::numeric_limits<int32_t>::min)() and
j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
{
// int 32
oa->write_character(static_cast<CharType>(0xD2));
write_number(static_cast<int32_t>(j.m_value.number_integer));
}
else if (j.m_value.number_integer >= (std::numeric_limits<int64_t>::min)() and
j.m_value.number_integer <= (std::numeric_limits<int64_t>::max)())
{
// int 64
oa->write_character(static_cast<CharType>(0xD3));
write_number(static_cast<int64_t>(j.m_value.number_integer));
}
}
break;
}
case value_t::number_unsigned:
{
if (j.m_value.number_unsigned < 128)
{
// positive fixnum
write_number(static_cast<uint8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
{
// uint 8
oa->write_character(static_cast<CharType>(0xCC));
write_number(static_cast<uint8_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
{
// uint 16
oa->write_character(static_cast<CharType>(0xCD));
write_number(static_cast<uint16_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
{
// uint 32
oa->write_character(static_cast<CharType>(0xCE));
write_number(static_cast<uint32_t>(j.m_value.number_integer));
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
{
// uint 64
oa->write_character(static_cast<CharType>(0xCF));
write_number(static_cast<uint64_t>(j.m_value.number_integer));
}
break;
}
case value_t::number_float:
{
oa->write_character(get_msgpack_float_prefix(j.m_value.number_float));
write_number(j.m_value.number_float);
break;
}
case value_t::string:
{
// step 1: write control byte and the string length
const auto N = j.m_value.string->size();
if (N <= 31)
{
// fixstr
write_number(static_cast<uint8_t>(0xA0 | N));
}
else if (N <= (std::numeric_limits<uint8_t>::max)())
{
// str 8
oa->write_character(static_cast<CharType>(0xD9));
write_number(static_cast<uint8_t>(N));
}
else if (N <= (std::numeric_limits<uint16_t>::max)())
{
// str 16
oa->write_character(static_cast<CharType>(0xDA));
write_number(static_cast<uint16_t>(N));
}
else if (N <= (std::numeric_limits<uint32_t>::max)())
{
// str 32
oa->write_character(static_cast<CharType>(0xDB));
write_number(static_cast<uint32_t>(N));
}
// step 2: write the string
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
j.m_value.string->size());
break;
}
case value_t::array:
{
// step 1: write control byte and the array size
const auto N = j.m_value.array->size();
if (N <= 15)
{
// fixarray
write_number(static_cast<uint8_t>(0x90 | N));
}
else if (N <= (std::numeric_limits<uint16_t>::max)())
{
// array 16
oa->write_character(static_cast<CharType>(0xDC));
write_number(static_cast<uint16_t>(N));
}
else if (N <= (std::numeric_limits<uint32_t>::max)())
{
// array 32
oa->write_character(static_cast<CharType>(0xDD));
write_number(static_cast<uint32_t>(N));
}
// step 2: write each element
for (const auto& el : *j.m_value.array)
{
write_msgpack(el);
}
break;
}
case value_t::object:
{
// step 1: write control byte and the object size
const auto N = j.m_value.object->size();
if (N <= 15)
{
// fixmap
write_number(static_cast<uint8_t>(0x80 | (N & 0xF)));
}
else if (N <= (std::numeric_limits<uint16_t>::max)())
{
// map 16
oa->write_character(static_cast<CharType>(0xDE));
write_number(static_cast<uint16_t>(N));
}
else if (N <= (std::numeric_limits<uint32_t>::max)())
{
// map 32
oa->write_character(static_cast<CharType>(0xDF));
write_number(static_cast<uint32_t>(N));
}
// step 2: write each element
for (const auto& el : *j.m_value.object)
{
write_msgpack(el.first);
write_msgpack(el.second);
}
break;
}
default:
break;
}
}
/*!
@param[in] j JSON value to serialize
@param[in] use_count whether to use '#' prefixes (optimized format)
@param[in] use_type whether to use '$' prefixes (optimized format)
@param[in] add_prefix whether prefixes need to be used for this value
*/
void write_ubjson(const BasicJsonType& j, const bool use_count,
const bool use_type, const bool add_prefix = true)
{
switch (j.type())
{
case value_t::null:
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('Z'));
}
break;
}
case value_t::boolean:
{
if (add_prefix)
oa->write_character(j.m_value.boolean
? static_cast<CharType>('T')
: static_cast<CharType>('F'));
break;
}
case value_t::number_integer:
{
write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix);
break;
}
case value_t::number_unsigned:
{
write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix);
break;
}
case value_t::number_float:
{
write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);
break;
}
case value_t::string:
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('S'));
}
write_number_with_ubjson_prefix(j.m_value.string->size(), true);
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
j.m_value.string->size());
break;
}
case value_t::array:
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('['));
}
bool prefix_required = true;
if (use_type and not j.m_value.array->empty())
{
assert(use_count);
const CharType first_prefix = ubjson_prefix(j.front());
const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
[this, first_prefix](const BasicJsonType & v)
{
return ubjson_prefix(v) == first_prefix;
});
if (same_prefix)
{
prefix_required = false;
oa->write_character(static_cast<CharType>('$'));
oa->write_character(first_prefix);
}
}
if (use_count)
{
oa->write_character(static_cast<CharType>('#'));
write_number_with_ubjson_prefix(j.m_value.array->size(), true);
}
for (const auto& el : *j.m_value.array)
{
write_ubjson(el, use_count, use_type, prefix_required);
}
if (not use_count)
{
oa->write_character(static_cast<CharType>(']'));
}
break;
}
case value_t::object:
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('{'));
}
bool prefix_required = true;
if (use_type and not j.m_value.object->empty())
{
assert(use_count);
const CharType first_prefix = ubjson_prefix(j.front());
const bool same_prefix = std::all_of(j.begin(), j.end(),
[this, first_prefix](const BasicJsonType & v)
{
return ubjson_prefix(v) == first_prefix;
});
if (same_prefix)
{
prefix_required = false;
oa->write_character(static_cast<CharType>('$'));
oa->write_character(first_prefix);
}
}
if (use_count)
{
oa->write_character(static_cast<CharType>('#'));
write_number_with_ubjson_prefix(j.m_value.object->size(), true);
}
for (const auto& el : *j.m_value.object)
{
write_number_with_ubjson_prefix(el.first.size(), true);
oa->write_characters(
reinterpret_cast<const CharType*>(el.first.c_str()),
el.first.size());
write_ubjson(el.second, use_count, use_type, prefix_required);
}
if (not use_count)
{
oa->write_character(static_cast<CharType>('}'));
}
break;
}
default:
break;
}
}
private:
/*
@brief write a number to output input
@param[in] n number of type @a NumberType
@tparam NumberType the type of the number
@note This function needs to respect the system's endianess, because bytes
in CBOR, MessagePack, and UBJSON are stored in network order (big
endian) and therefore need reordering on little endian systems.
*/
template<typename NumberType>
void write_number(const NumberType n)
{
// step 1: write number to array of length NumberType
std::array<CharType, sizeof(NumberType)> vec;
std::memcpy(vec.data(), &n, sizeof(NumberType));
// step 2: write array to output (with possible reordering)
if (is_little_endian)
{
// reverse byte order prior to conversion if necessary
std::reverse(vec.begin(), vec.end());
}
oa->write_characters(vec.data(), sizeof(NumberType));
}
// UBJSON: write number (floating point)
template<typename NumberType, typename std::enable_if<
std::is_floating_point<NumberType>::value, int>::type = 0>
void write_number_with_ubjson_prefix(const NumberType n,
const bool add_prefix)
{
if (add_prefix)
{
oa->write_character(get_ubjson_float_prefix(n));
}
write_number(n);
}
// UBJSON: write number (unsigned integer)
template<typename NumberType, typename std::enable_if<
std::is_unsigned<NumberType>::value, int>::type = 0>
void write_number_with_ubjson_prefix(const NumberType n,
const bool add_prefix)
{
if (n <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)()))
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('i')); // int8
}
write_number(static_cast<uint8_t>(n));
}
else if (n <= (std::numeric_limits<uint8_t>::max)())
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('U')); // uint8
}
write_number(static_cast<uint8_t>(n));
}
else if (n <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('I')); // int16
}
write_number(static_cast<int16_t>(n));
}
else if (n <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('l')); // int32
}
write_number(static_cast<int32_t>(n));
}
else if (n <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('L')); // int64
}
write_number(static_cast<int64_t>(n));
}
else
{
JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
}
}
// UBJSON: write number (signed integer)
template<typename NumberType, typename std::enable_if<
std::is_signed<NumberType>::value and
not std::is_floating_point<NumberType>::value, int>::type = 0>
void write_number_with_ubjson_prefix(const NumberType n,
const bool add_prefix)
{
if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)())
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('i')); // int8
}
write_number(static_cast<int8_t>(n));
}
else if (static_cast<int64_t>((std::numeric_limits<uint8_t>::min)()) <= n and n <= static_cast<int64_t>((std::numeric_limits<uint8_t>::max)()))
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('U')); // uint8
}
write_number(static_cast<uint8_t>(n));
}
else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)())
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('I')); // int16
}
write_number(static_cast<int16_t>(n));
}
else if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('l')); // int32
}
write_number(static_cast<int32_t>(n));
}
else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)())
{
if (add_prefix)
{
oa->write_character(static_cast<CharType>('L')); // int64
}
write_number(static_cast<int64_t>(n));
}
// LCOV_EXCL_START
else
{
JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
}
// LCOV_EXCL_STOP
}
/*!
@brief determine the type prefix of container values
@note This function does not need to be 100% accurate when it comes to
integer limits. In case a number exceeds the limits of int64_t,
this will be detected by a later call to function
write_number_with_ubjson_prefix. Therefore, we return 'L' for any
value that does not fit the previous limits.
*/
CharType ubjson_prefix(const BasicJsonType& j) const noexcept
{
switch (j.type())
{
case value_t::null:
return 'Z';
case value_t::boolean:
return j.m_value.boolean ? 'T' : 'F';
case value_t::number_integer:
{
if ((std::numeric_limits<int8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
{
return 'i';
}
else if ((std::numeric_limits<uint8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
{
return 'U';
}
else if ((std::numeric_limits<int16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
{
return 'I';
}
else if ((std::numeric_limits<int32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
{
return 'l';
}
else // no check and assume int64_t (see note above)
{
return 'L';
}
}
case value_t::number_unsigned:
{
if (j.m_value.number_unsigned <= (std::numeric_limits<int8_t>::max)())
{
return 'i';
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
{
return 'U';
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<int16_t>::max)())
{
return 'I';
}
else if (j.m_value.number_unsigned <= (std::numeric_limits<int32_t>::max)())
{
return 'l';
}
else // no check and assume int64_t (see note above)
{
return 'L';
}
}
case value_t::number_float:
return get_ubjson_float_prefix(j.m_value.number_float);
case value_t::string:
return 'S';
case value_t::array:
return '[';
case value_t::object:
return '{';
default: // discarded values
return 'N';
}
}
static constexpr CharType get_cbor_float_prefix(float)
{
return static_cast<CharType>(0xFA); // Single-Precision Float
}
static constexpr CharType get_cbor_float_prefix(double)
{
return static_cast<CharType>(0xFB); // Double-Precision Float
}
static constexpr CharType get_msgpack_float_prefix(float)
{
return static_cast<CharType>(0xCA); // float 32
}
static constexpr CharType get_msgpack_float_prefix(double)
{
return static_cast<CharType>(0xCB); // float 64
}
static constexpr CharType get_ubjson_float_prefix(float)
{
return 'd'; // float 32
}
static constexpr CharType get_ubjson_float_prefix(double)
{
return 'D'; // float 64
}
private:
/// whether we can assume little endianess
const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess();
/// the output
output_adapter_t<CharType> oa = nullptr;
};
}
}

View File

@ -1,113 +0,0 @@
#pragma once
#include <algorithm> // copy
#include <cstddef> // size_t
#include <ios> // streamsize
#include <iterator> // back_inserter
#include <memory> // shared_ptr, make_shared
#include <ostream> // basic_ostream
#include <string> // basic_string
#include <vector> // vector
namespace nlohmann
{
namespace detail
{
/// abstract output adapter interface
template<typename CharType> struct output_adapter_protocol
{
virtual void write_character(CharType c) = 0;
virtual void write_characters(const CharType* s, std::size_t length) = 0;
virtual ~output_adapter_protocol() = default;
};
/// a type to simplify interfaces
template<typename CharType>
using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
/// output adapter for byte vectors
template<typename CharType>
class output_vector_adapter : public output_adapter_protocol<CharType>
{
public:
explicit output_vector_adapter(std::vector<CharType>& vec) : v(vec) {}
void write_character(CharType c) override
{
v.push_back(c);
}
void write_characters(const CharType* s, std::size_t length) override
{
std::copy(s, s + length, std::back_inserter(v));
}
private:
std::vector<CharType>& v;
};
/// output adapter for output streams
template<typename CharType>
class output_stream_adapter : public output_adapter_protocol<CharType>
{
public:
explicit output_stream_adapter(std::basic_ostream<CharType>& s) : stream(s) {}
void write_character(CharType c) override
{
stream.put(c);
}
void write_characters(const CharType* s, std::size_t length) override
{
stream.write(s, static_cast<std::streamsize>(length));
}
private:
std::basic_ostream<CharType>& stream;
};
/// output adapter for basic_string
template<typename CharType, typename StringType = std::basic_string<CharType>>
class output_string_adapter : public output_adapter_protocol<CharType>
{
public:
explicit output_string_adapter(StringType& s) : str(s) {}
void write_character(CharType c) override
{
str.push_back(c);
}
void write_characters(const CharType* s, std::size_t length) override
{
str.append(s, length);
}
private:
StringType& str;
};
template<typename CharType, typename StringType = std::basic_string<CharType>>
class output_adapter
{
public:
output_adapter(std::vector<CharType>& vec)
: oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}
output_adapter(std::basic_ostream<CharType>& s)
: oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
output_adapter(StringType& s)
: oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}
operator output_adapter_t<CharType>()
{
return oa;
}
private:
output_adapter_t<CharType> oa = nullptr;
};
}
}

View File

@ -1,631 +0,0 @@
#pragma once
#include <algorithm> // reverse, remove, fill, find, none_of
#include <array> // array
#include <cassert> // assert
#include <ciso646> // and, or
#include <clocale> // localeconv, lconv
#include <cmath> // labs, isfinite, isnan, signbit
#include <cstddef> // size_t, ptrdiff_t
#include <cstdint> // uint8_t
#include <cstdio> // snprintf
#include <limits> // numeric_limits
#include <string> // string
#include <type_traits> // is_same
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/conversions/to_chars.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta.hpp>
#include <nlohmann/detail/output/output_adapters.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
///////////////////
// serialization //
///////////////////
template<typename BasicJsonType>
class serializer
{
using string_t = typename BasicJsonType::string_t;
using number_float_t = typename BasicJsonType::number_float_t;
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
static constexpr uint8_t UTF8_ACCEPT = 0;
static constexpr uint8_t UTF8_REJECT = 1;
public:
/*!
@param[in] s output stream to serialize to
@param[in] ichar indentation character to use
*/
serializer(output_adapter_t<char> s, const char ichar)
: o(std::move(s)), loc(std::localeconv()),
thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)),
decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)),
indent_char(ichar), indent_string(512, indent_char)
{}
// delete because of pointer members
serializer(const serializer&) = delete;
serializer& operator=(const serializer&) = delete;
/*!
@brief internal implementation of the serialization function
This function is called by the public member function dump and organizes
the serialization internally. The indentation level is propagated as
additional parameter. In case of arrays and objects, the function is
called recursively.
- strings and object keys are escaped using `escape_string()`
- integer numbers are converted implicitly via `operator<<`
- floating-point numbers are converted to a string using `"%g"` format
@param[in] val value to serialize
@param[in] pretty_print whether the output shall be pretty-printed
@param[in] indent_step the indent level
@param[in] current_indent the current indent level (only used internally)
*/
void dump(const BasicJsonType& val, const bool pretty_print,
const bool ensure_ascii,
const unsigned int indent_step,
const unsigned int current_indent = 0)
{
switch (val.m_type)
{
case value_t::object:
{
if (val.m_value.object->empty())
{
o->write_characters("{}", 2);
return;
}
if (pretty_print)
{
o->write_characters("{\n", 2);
// variable to hold indentation for recursive calls
const auto new_indent = current_indent + indent_step;
if (JSON_UNLIKELY(indent_string.size() < new_indent))
{
indent_string.resize(indent_string.size() * 2, ' ');
}
// first n-1 elements
auto i = val.m_value.object->cbegin();
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
{
o->write_characters(indent_string.c_str(), new_indent);
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\": ", 3);
dump(i->second, true, ensure_ascii, indent_step, new_indent);
o->write_characters(",\n", 2);
}
// last element
assert(i != val.m_value.object->cend());
assert(std::next(i) == val.m_value.object->cend());
o->write_characters(indent_string.c_str(), new_indent);
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\": ", 3);
dump(i->second, true, ensure_ascii, indent_step, new_indent);
o->write_character('\n');
o->write_characters(indent_string.c_str(), current_indent);
o->write_character('}');
}
else
{
o->write_character('{');
// first n-1 elements
auto i = val.m_value.object->cbegin();
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
{
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\":", 2);
dump(i->second, false, ensure_ascii, indent_step, current_indent);
o->write_character(',');
}
// last element
assert(i != val.m_value.object->cend());
assert(std::next(i) == val.m_value.object->cend());
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\":", 2);
dump(i->second, false, ensure_ascii, indent_step, current_indent);
o->write_character('}');
}
return;
}
case value_t::array:
{
if (val.m_value.array->empty())
{
o->write_characters("[]", 2);
return;
}
if (pretty_print)
{
o->write_characters("[\n", 2);
// variable to hold indentation for recursive calls
const auto new_indent = current_indent + indent_step;
if (JSON_UNLIKELY(indent_string.size() < new_indent))
{
indent_string.resize(indent_string.size() * 2, ' ');
}
// first n-1 elements
for (auto i = val.m_value.array->cbegin();
i != val.m_value.array->cend() - 1; ++i)
{
o->write_characters(indent_string.c_str(), new_indent);
dump(*i, true, ensure_ascii, indent_step, new_indent);
o->write_characters(",\n", 2);
}
// last element
assert(not val.m_value.array->empty());
o->write_characters(indent_string.c_str(), new_indent);
dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
o->write_character('\n');
o->write_characters(indent_string.c_str(), current_indent);
o->write_character(']');
}
else
{
o->write_character('[');
// first n-1 elements
for (auto i = val.m_value.array->cbegin();
i != val.m_value.array->cend() - 1; ++i)
{
dump(*i, false, ensure_ascii, indent_step, current_indent);
o->write_character(',');
}
// last element
assert(not val.m_value.array->empty());
dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
o->write_character(']');
}
return;
}
case value_t::string:
{
o->write_character('\"');
dump_escaped(*val.m_value.string, ensure_ascii);
o->write_character('\"');
return;
}
case value_t::boolean:
{
if (val.m_value.boolean)
{
o->write_characters("true", 4);
}
else
{
o->write_characters("false", 5);
}
return;
}
case value_t::number_integer:
{
dump_integer(val.m_value.number_integer);
return;
}
case value_t::number_unsigned:
{
dump_integer(val.m_value.number_unsigned);
return;
}
case value_t::number_float:
{
dump_float(val.m_value.number_float);
return;
}
case value_t::discarded:
{
o->write_characters("<discarded>", 11);
return;
}
case value_t::null:
{
o->write_characters("null", 4);
return;
}
}
}
private:
/*!
@brief dump escaped string
Escape a string by replacing certain special characters by a sequence of an
escape character (backslash) and another character and other control
characters by a sequence of "\u" followed by a four-digit hex
representation. The escaped string is written to output stream @a o.
@param[in] s the string to escape
@param[in] ensure_ascii whether to escape non-ASCII characters with
\uXXXX sequences
@complexity Linear in the length of string @a s.
*/
void dump_escaped(const string_t& s, const bool ensure_ascii)
{
uint32_t codepoint;
uint8_t state = UTF8_ACCEPT;
std::size_t bytes = 0; // number of bytes written to string_buffer
for (std::size_t i = 0; i < s.size(); ++i)
{
const auto byte = static_cast<uint8_t>(s[i]);
switch (decode(state, codepoint, byte))
{
case UTF8_ACCEPT: // decode found a new code point
{
switch (codepoint)
{
case 0x08: // backspace
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'b';
break;
}
case 0x09: // horizontal tab
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 't';
break;
}
case 0x0A: // newline
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'n';
break;
}
case 0x0C: // formfeed
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'f';
break;
}
case 0x0D: // carriage return
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'r';
break;
}
case 0x22: // quotation mark
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = '\"';
break;
}
case 0x5C: // reverse solidus
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = '\\';
break;
}
default:
{
// escape control characters (0x00..0x1F) or, if
// ensure_ascii parameter is used, non-ASCII characters
if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F)))
{
if (codepoint <= 0xFFFF)
{
std::snprintf(string_buffer.data() + bytes, 7, "\\u%04x",
static_cast<uint16_t>(codepoint));
bytes += 6;
}
else
{
std::snprintf(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
static_cast<uint16_t>(0xD7C0 + (codepoint >> 10)),
static_cast<uint16_t>(0xDC00 + (codepoint & 0x3FF)));
bytes += 12;
}
}
else
{
// copy byte to buffer (all previous bytes
// been copied have in default case above)
string_buffer[bytes++] = s[i];
}
break;
}
}
// write buffer and reset index; there must be 13 bytes
// left, as this is the maximal number of bytes to be
// written ("\uxxxx\uxxxx\0") for one code point
if (string_buffer.size() - bytes < 13)
{
o->write_characters(string_buffer.data(), bytes);
bytes = 0;
}
break;
}
case UTF8_REJECT: // decode found invalid UTF-8 byte
{
std::string sn(3, '\0');
snprintf(&sn[0], sn.size(), "%.2X", byte);
JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn));
}
default: // decode found yet incomplete multi-byte code point
{
if (not ensure_ascii)
{
// code point will not be escaped - copy byte to buffer
string_buffer[bytes++] = s[i];
}
break;
}
}
}
if (JSON_LIKELY(state == UTF8_ACCEPT))
{
// write buffer
if (bytes > 0)
{
o->write_characters(string_buffer.data(), bytes);
}
}
else
{
// we finish reading, but do not accept: string was incomplete
std::string sn(3, '\0');
snprintf(&sn[0], sn.size(), "%.2X", static_cast<uint8_t>(s.back()));
JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn));
}
}
/*!
@brief dump an integer
Dump a given integer to output stream @a o. Works internally with
@a number_buffer.
@param[in] x integer number (signed or unsigned) to dump
@tparam NumberType either @a number_integer_t or @a number_unsigned_t
*/
template<typename NumberType, detail::enable_if_t<
std::is_same<NumberType, number_unsigned_t>::value or
std::is_same<NumberType, number_integer_t>::value,
int> = 0>
void dump_integer(NumberType x)
{
// special case for "0"
if (x == 0)
{
o->write_character('0');
return;
}
const bool is_negative = (x <= 0) and (x != 0); // see issue #755
std::size_t i = 0;
while (x != 0)
{
// spare 1 byte for '\0'
assert(i < number_buffer.size() - 1);
const auto digit = std::labs(static_cast<long>(x % 10));
number_buffer[i++] = static_cast<char>('0' + digit);
x /= 10;
}
if (is_negative)
{
// make sure there is capacity for the '-'
assert(i < number_buffer.size() - 2);
number_buffer[i++] = '-';
}
std::reverse(number_buffer.begin(), number_buffer.begin() + i);
o->write_characters(number_buffer.data(), i);
}
/*!
@brief dump a floating-point number
Dump a given floating-point number to output stream @a o. Works internally
with @a number_buffer.
@param[in] x floating-point number to dump
*/
void dump_float(number_float_t x)
{
// NaN / inf
if (not std::isfinite(x))
{
o->write_characters("null", 4);
return;
}
// If number_float_t is an IEEE-754 single or double precision number,
// use the Grisu2 algorithm to produce short numbers which are
// guaranteed to round-trip, using strtof and strtod, resp.
//
// NB: The test below works if <long double> == <double>.
static constexpr bool is_ieee_single_or_double
= (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 24 and std::numeric_limits<number_float_t>::max_exponent == 128) or
(std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 53 and std::numeric_limits<number_float_t>::max_exponent == 1024);
dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
}
void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
{
char* begin = number_buffer.data();
char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
o->write_characters(begin, static_cast<size_t>(end - begin));
}
void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
{
// get number of digits for a float -> text -> float round-trip
static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
// the actual conversion
std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
// negative value indicates an error
assert(len > 0);
// check if buffer was large enough
assert(static_cast<std::size_t>(len) < number_buffer.size());
// erase thousands separator
if (thousands_sep != '\0')
{
const auto end = std::remove(number_buffer.begin(),
number_buffer.begin() + len, thousands_sep);
std::fill(end, number_buffer.end(), '\0');
assert((end - number_buffer.begin()) <= len);
len = (end - number_buffer.begin());
}
// convert decimal point to '.'
if (decimal_point != '\0' and decimal_point != '.')
{
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
if (dec_pos != number_buffer.end())
{
*dec_pos = '.';
}
}
o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
// determine if need to append ".0"
const bool value_is_int_like =
std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
[](char c)
{
return (c == '.' or c == 'e');
});
if (value_is_int_like)
{
o->write_characters(".0", 2);
}
}
/*!
@brief check whether a string is UTF-8 encoded
The function checks each byte of a string whether it is UTF-8 encoded. The
result of the check is stored in the @a state parameter. The function must
be called initially with state 0 (accept). State 1 means the string must
be rejected, because the current byte is not allowed. If the string is
completely processed, but the state is non-zero, the string ended
prematurely; that is, the last byte indicated more bytes should have
followed.
@param[in,out] state the state of the decoding
@param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
@param[in] byte next byte to decode
@return new state
@note The function has been edited: a std::array is used.
@copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
@sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
*/
static uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept
{
static const std::array<uint8_t, 400> utf8d =
{
{
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, // 00..1F
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, // 20..3F
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, // 40..5F
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, // 60..7F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
}
};
const uint8_t type = utf8d[byte];
codep = (state != UTF8_ACCEPT)
? (byte & 0x3fu) | (codep << 6)
: static_cast<uint32_t>(0xff >> type) & (byte);
state = utf8d[256u + state * 16u + type];
return state;
}
private:
/// the output of the serializer
output_adapter_t<char> o = nullptr;
/// a (hopefully) large enough character buffer
std::array<char, 64> number_buffer{{}};
/// the locale
const std::lconv* loc = nullptr;
/// the locale's thousand separator character
const char thousands_sep = '\0';
/// the locale's decimal point character
const char decimal_point = '\0';
/// string buffer
std::array<char, 512> string_buffer{{}};
/// the indentation character
const char indent_char;
/// the indentation string
string_t indent_string;
};
}
}

View File

@ -1,76 +0,0 @@
#pragma once
#include <array> // array
#include <ciso646> // and
#include <cstddef> // size_t
#include <cstdint> // uint8_t
namespace nlohmann
{
namespace detail
{
///////////////////////////
// JSON type enumeration //
///////////////////////////
/*!
@brief the JSON type enumeration
This enumeration collects the different JSON types. It is internally used to
distinguish the stored values, and the functions @ref basic_json::is_null(),
@ref basic_json::is_object(), @ref basic_json::is_array(),
@ref basic_json::is_string(), @ref basic_json::is_boolean(),
@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
@ref basic_json::is_structured() rely on it.
@note There are three enumeration entries (number_integer, number_unsigned, and
number_float), because the library distinguishes these three types for numbers:
@ref basic_json::number_unsigned_t is used for unsigned integers,
@ref basic_json::number_integer_t is used for signed integers, and
@ref basic_json::number_float_t is used for floating-point numbers or to
approximate integers which do not fit in the limits of their respective type.
@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON
value with the default value for a given type
@since version 1.0.0
*/
enum class value_t : std::uint8_t
{
null, ///< null value
object, ///< object (unordered set of name/value pairs)
array, ///< array (ordered collection of values)
string, ///< string value
boolean, ///< boolean value
number_integer, ///< number value (signed integer)
number_unsigned, ///< number value (unsigned integer)
number_float, ///< number value (floating-point)
discarded ///< discarded by the the parser callback function
};
/*!
@brief comparison operator for JSON types
Returns an ordering that is similar to Python:
- order: null < boolean < number < object < array < string
- furthermore, each type is not smaller than itself
- discarded values are not comparable
@since version 1.0.0
*/
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
{
static constexpr std::array<std::uint8_t, 8> order = {{
0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */
}
};
const auto l_index = static_cast<std::size_t>(lhs);
const auto r_index = static_cast<std::size_t>(rhs);
return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index];
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,64 +0,0 @@
#ifndef NLOHMANN_JSON_FWD_HPP
#define NLOHMANN_JSON_FWD_HPP
#include <cstdint> // int64_t, uint64_t
#include <map> // map
#include <memory> // allocator
#include <string> // string
#include <vector> // vector
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
@since version 1.0.0
*/
namespace nlohmann
{
/*!
@brief default JSONSerializer template argument
This serializer ignores the template arguments and uses ADL
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
for serialization.
*/
template<typename T = void, typename SFINAE = void>
struct adl_serializer;
template<template<typename U, typename V, typename... Args> class ObjectType =
std::map,
template<typename U, typename... Args> class ArrayType = std::vector,
class StringType = std::string, class BooleanType = bool,
class NumberIntegerType = std::int64_t,
class NumberUnsignedType = std::uint64_t,
class NumberFloatType = double,
template<typename U> class AllocatorType = std::allocator,
template<typename T, typename SFINAE = void> class JSONSerializer =
adl_serializer>
class basic_json;
/*!
@brief JSON Pointer
A JSON pointer defines a string syntax for identifying a specific value
within a JSON document. It can be used with functions `at` and
`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
@since version 2.0.0
*/
template<typename BasicJsonType>
class json_pointer;
/*!
@brief default JSON class
This type is the default specialization of the @ref basic_json class which
uses the standard template types.
@since version 1.0.0
*/
using json = basic_json<>;
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +0,0 @@
#include <csignal>
#include <iostream>
#include "WallpaperEngine/Application/CApplicationContext.h"
#include "WallpaperEngine/Application/CWallpaperApplication.h"
#include "WallpaperEngine/WebBrowser/CWebBrowserContext.h"
#include "common.h"
WallpaperEngine::Application::CWallpaperApplication* appPointer;
void signalhandler(int sig)
{
if (appPointer == nullptr)
return;
appPointer->signal (sig);
}
void initLogging ()
{
sLog.addOutput (new std::ostream (std::cout.rdbuf ()));
sLog.addError (new std::ostream (std::cerr.rdbuf ()));
}
int main (int argc, char* argv[]) {
initLogging ();
WallpaperEngine::WebBrowser::CWebBrowserContext webBrowserContext(argc, argv);
WallpaperEngine::Application::CApplicationContext appContext (argc, argv);
// halt if the list-properties option was specified
if (appContext.settings.general.onlyListProperties)
return 0;
WallpaperEngine::Application::CWallpaperApplication app (appContext, webBrowserContext);
appPointer = &app;
// attach signals to gracefully stop
std::signal (SIGINT, signalhandler);
std::signal (SIGTERM, signalhandler);
// show the wallpaper application
app.show ();
appPointer = nullptr;
return 0;
}

View File

@ -24,6 +24,12 @@ pkgver() {
)
}
prepare() {
cd "$pkgname"
git submodule update --init --recursive
git -c protocol.file.allow=always submodule update
}
build() {
cmake -B build -S "$pkgname" \
-DCMAKE_BUILD_TYPE='Release' \

View File

@ -1,14 +0,0 @@
{
"patches": [
{
"matches": [
"uniform float u_pointerSpeed;",
"uniform vec2 g_PointerPosition;",
"float pointer= g_PointerPosition.xy * u_pointerSpeed;"
],
"replacements": {
"float pointer= g_PointerPosition.xy * u_pointerSpeed;": "float pointer= (g_PointerPosition.xy * u_pointerSpeed).x;"
}
}
]
}

View File

@ -1,13 +0,0 @@
{
"patches": [
{
"matches": [
"uniform float g_Chromatic;",
"g_AudioSpectrum16Left[g_Chromatic]"
],
"replacements": {
"g_AudioSpectrum16Left[g_Chromatic]": "g_AudioSpectrum16Left[int(g_Chromatic)]"
}
}
]
}

View File

@ -1,16 +0,0 @@
{
"patches": [
{
"matches": [
"varying vec2 v_TexCoord;",
"vec4 scene= texSample2D(g_Texture0, v_TexCoord);",
"float mask= texSample2D(g_Texture1, v_TexCoord.zw).r;"
],
"replacements": {
"varying vec2 v_TexCoord;": "varying vec4 v_TexCoord;",
"vec4 scene= texSample2D(g_Texture0, v_TexCoord);": "vec4 scene= texSample2D(g_Texture0, v_TexCoord.xy);",
"vec3 colour= vec3(pow(abs(c), u_WaveColor * u_Brightness));": "vec3 colour= vec3(pow(abs(c), (u_WaveColor * u_Brightness).x));"
}
}
]
}

View File

@ -1,162 +0,0 @@
#include "fft.h"
namespace External::Android {
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* A Fixed point implementation of Fast Fourier Transform (FFT). Complex numbers
* are represented by 32-bit integers, where higher 16 bits are real part and
* lower ones are imaginary part. Few compromises are made between efficiency,
* accuracy, and maintainability. To make it fast, arithmetic shifts are used
* instead of divisions, and bitwise inverses are used instead of negates. To
* keep it small, only radix-2 Cooley-Tukey algorithm is implemented, and only
* half of the twiddle factors are stored. Although there are still ways to make
* it even faster or smaller, it costs too much on one of the aspects.
*/
#ifdef __arm__
#include <machine/cpu-features.h>
#endif
#define LOG_FFT_SIZE 10
#define MAX_FFT_SIZE (1 << LOG_FFT_SIZE)
static const int32_t twiddle [MAX_FFT_SIZE / 4] = {
0x00008000, 0xff378001, 0xfe6e8002, 0xfda58006, 0xfcdc800a, 0xfc13800f, 0xfb4a8016, 0xfa81801e, 0xf9b88027,
0xf8ef8032, 0xf827803e, 0xf75e804b, 0xf6958059, 0xf5cd8068, 0xf5058079, 0xf43c808b, 0xf374809e, 0xf2ac80b2,
0xf1e480c8, 0xf11c80de, 0xf05580f6, 0xef8d8110, 0xeec6812a, 0xedff8146, 0xed388163, 0xec718181, 0xebab81a0,
0xeae481c1, 0xea1e81e2, 0xe9588205, 0xe892822a, 0xe7cd824f, 0xe7078276, 0xe642829d, 0xe57d82c6, 0xe4b982f1,
0xe3f4831c, 0xe3308349, 0xe26d8377, 0xe1a983a6, 0xe0e683d6, 0xe0238407, 0xdf61843a, 0xde9e846e, 0xdddc84a3,
0xdd1b84d9, 0xdc598511, 0xdb998549, 0xdad88583, 0xda1885be, 0xd95885fa, 0xd8988637, 0xd7d98676, 0xd71b86b6,
0xd65c86f6, 0xd59e8738, 0xd4e1877b, 0xd42487c0, 0xd3678805, 0xd2ab884c, 0xd1ef8894, 0xd13488dd, 0xd0798927,
0xcfbe8972, 0xcf0489be, 0xce4b8a0c, 0xcd928a5a, 0xccd98aaa, 0xcc218afb, 0xcb698b4d, 0xcab28ba0, 0xc9fc8bf5,
0xc9468c4a, 0xc8908ca1, 0xc7db8cf8, 0xc7278d51, 0xc6738dab, 0xc5c08e06, 0xc50d8e62, 0xc45b8ebf, 0xc3a98f1d,
0xc2f88f7d, 0xc2488fdd, 0xc198903e, 0xc0e990a1, 0xc03a9105, 0xbf8c9169, 0xbedf91cf, 0xbe329236, 0xbd86929e,
0xbcda9307, 0xbc2f9371, 0xbb8593dc, 0xbadc9448, 0xba3394b5, 0xb98b9523, 0xb8e39592, 0xb83c9603, 0xb7969674,
0xb6f196e6, 0xb64c9759, 0xb5a897ce, 0xb5059843, 0xb46298b9, 0xb3c09930, 0xb31f99a9, 0xb27f9a22, 0xb1df9a9c,
0xb1409b17, 0xb0a29b94, 0xb0059c11, 0xaf689c8f, 0xaecc9d0e, 0xae319d8e, 0xad979e0f, 0xacfd9e91, 0xac659f14,
0xabcd9f98, 0xab36a01c, 0xaaa0a0a2, 0xaa0aa129, 0xa976a1b0, 0xa8e2a238, 0xa84fa2c2, 0xa7bda34c, 0xa72ca3d7,
0xa69ca463, 0xa60ca4f0, 0xa57ea57e, 0xa4f0a60c, 0xa463a69c, 0xa3d7a72c, 0xa34ca7bd, 0xa2c2a84f, 0xa238a8e2,
0xa1b0a976, 0xa129aa0a, 0xa0a2aaa0, 0xa01cab36, 0x9f98abcd, 0x9f14ac65, 0x9e91acfd, 0x9e0fad97, 0x9d8eae31,
0x9d0eaecc, 0x9c8faf68, 0x9c11b005, 0x9b94b0a2, 0x9b17b140, 0x9a9cb1df, 0x9a22b27f, 0x99a9b31f, 0x9930b3c0,
0x98b9b462, 0x9843b505, 0x97ceb5a8, 0x9759b64c, 0x96e6b6f1, 0x9674b796, 0x9603b83c, 0x9592b8e3, 0x9523b98b,
0x94b5ba33, 0x9448badc, 0x93dcbb85, 0x9371bc2f, 0x9307bcda, 0x929ebd86, 0x9236be32, 0x91cfbedf, 0x9169bf8c,
0x9105c03a, 0x90a1c0e9, 0x903ec198, 0x8fddc248, 0x8f7dc2f8, 0x8f1dc3a9, 0x8ebfc45b, 0x8e62c50d, 0x8e06c5c0,
0x8dabc673, 0x8d51c727, 0x8cf8c7db, 0x8ca1c890, 0x8c4ac946, 0x8bf5c9fc, 0x8ba0cab2, 0x8b4dcb69, 0x8afbcc21,
0x8aaaccd9, 0x8a5acd92, 0x8a0cce4b, 0x89becf04, 0x8972cfbe, 0x8927d079, 0x88ddd134, 0x8894d1ef, 0x884cd2ab,
0x8805d367, 0x87c0d424, 0x877bd4e1, 0x8738d59e, 0x86f6d65c, 0x86b6d71b, 0x8676d7d9, 0x8637d898, 0x85fad958,
0x85beda18, 0x8583dad8, 0x8549db99, 0x8511dc59, 0x84d9dd1b, 0x84a3dddc, 0x846ede9e, 0x843adf61, 0x8407e023,
0x83d6e0e6, 0x83a6e1a9, 0x8377e26d, 0x8349e330, 0x831ce3f4, 0x82f1e4b9, 0x82c6e57d, 0x829de642, 0x8276e707,
0x824fe7cd, 0x822ae892, 0x8205e958, 0x81e2ea1e, 0x81c1eae4, 0x81a0ebab, 0x8181ec71, 0x8163ed38, 0x8146edff,
0x812aeec6, 0x8110ef8d, 0x80f6f055, 0x80def11c, 0x80c8f1e4, 0x80b2f2ac, 0x809ef374, 0x808bf43c, 0x8079f505,
0x8068f5cd, 0x8059f695, 0x804bf75e, 0x803ef827, 0x8032f8ef, 0x8027f9b8, 0x801efa81, 0x8016fb4a, 0x800ffc13,
0x800afcdc, 0x8006fda5, 0x8002fe6e, 0x8001ff37,
};
/* Returns the multiplication of \conj{a} and {b}. */
static inline int32_t mult (int32_t a, int32_t b) {
#if __ARM_ARCH__ >= 6
int32_t t = b;
__asm__ ("smuad %0, %0, %1" : "+r"(t) : "r"(a));
__asm__ ("smusdx %0, %0, %1" : "+r"(b) : "r"(a));
__asm__ ("pkhtb %0, %0, %1, ASR #16" : "+r"(t) : "r"(b));
return t;
#else
return (((a >> 16) * (b >> 16) + static_cast<int16_t> (a) * static_cast<int16_t> (b)) & ~0xFFFF) |
((((a >> 16) * static_cast<int16_t> (b) - static_cast<int16_t> (a) * (b >> 16)) >> 16) & 0xFFFF);
#endif
}
static inline int32_t half (int32_t a) {
#if __ARM_ARCH__ >= 6
__asm__ ("shadd16 %0, %0, %1" : "+r"(a) : "r"(0));
return a;
#else
return ((a >> 1) & ~0x8000) | (a & 0x8000);
#endif
}
void fixed_fft (int n, int32_t* v) {
int scale = LOG_FFT_SIZE, i, p, r;
for (r = 0, i = 1; i < n; ++i) {
for (p = n; !(p & r); p >>= 1, r ^= p)
;
if (i < r) {
int32_t t = v [i];
v [i] = v [r];
v [r] = t;
}
}
for (p = 1; p < n; p <<= 1) {
--scale;
for (i = 0; i < n; i += p << 1) {
int32_t x = half (v [i]);
int32_t y = half (v [i + p]);
v [i] = x + y;
v [i + p] = x - y;
}
for (r = 1; r < p; ++r) {
int32_t w = MAX_FFT_SIZE / 4 - (r << scale);
i = w >> 31;
w = twiddle [(w ^ i) - i] ^ (i << 16);
for (i = r; i < n; i += p << 1) {
int32_t x = half (v [i]);
int32_t y = mult (w, v [i + p]);
v [i] = x - y;
v [i + p] = x + y;
}
}
}
}
void fixed_fft_real (int n, int32_t* v) {
int scale = LOG_FFT_SIZE, m = n >> 1, i;
fixed_fft (n, v);
for (i = 1; i <= n; i <<= 1, --scale)
;
v [0] = mult (~v [0], 0x80008000);
v [m] = half (v [m]);
for (i = 1; i < n >> 1; ++i) {
int32_t x = half (v [i]);
int32_t z = half (v [n - i]);
int32_t y = z - (x ^ 0xFFFF);
x = half (x + (z ^ 0xFFFF));
y = mult (y, twiddle [i << scale]);
v [i] = x - y;
v [n - i] = (x + y) ^ 0xFFFF;
}
}
bool doFft (uint8_t* fft, uint8_t* waveform) {
int32_t workspace [WAVE_BUFFER_SIZE >> 1];
int32_t nonzero = 0;
for (uint32_t i = 0; i < WAVE_BUFFER_SIZE; i += 2) {
workspace [i >> 1] = ((waveform [i] ^ 0x80) << 24) | ((waveform [i + 1] ^ 0x80) << 8);
nonzero |= workspace [i >> 1];
}
if (nonzero) {
fixed_fft_real (WAVE_BUFFER_SIZE >> 1, workspace);
}
for (uint32_t i = 0; i < WAVE_BUFFER_SIZE; i += 2) {
short tmp = workspace [i >> 1] >> 21;
while (tmp > 127 || tmp < -128)
tmp >>= 1;
fft [i] = tmp;
tmp = workspace [i >> 1];
tmp >>= 5;
while (tmp > 127 || tmp < -128)
tmp >>= 1;
fft [i + 1] = tmp;
}
return true;
}
} // namespace External::Android

View File

@ -1,11 +0,0 @@
#pragma once
#include <cstdint>
#include <cstdio>
#include <iostream>
#define WAVE_BUFFER_SIZE 1024
namespace External::Android {
bool doFft (uint8_t* fft, uint8_t* waveform);
}

1
src/External/MimeTypes vendored Submodule

@ -0,0 +1 @@
Subproject commit 8990d6a796bd9904ec3d41e5c150a1ffb48fe32e

@ -0,0 +1 @@
Subproject commit ad4d02220b01c1800e5a4e6671d6d8ca8ab07783

1
src/External/argparse vendored Submodule

@ -0,0 +1 @@
Subproject commit d924b84eba1f0f0adf38b20b7b4829f6f65b6570

@ -0,0 +1 @@
Subproject commit 077d4f3c89bbfbb7951a9011d4551b075f092334

1
src/External/json vendored Submodule

@ -0,0 +1 @@
Subproject commit 4424a0fcc1c7fa640b5c87d26776d99150dacd10

1
src/External/kissfft vendored Submodule

@ -0,0 +1 @@
Subproject commit febd4caeed32e33ad8b2e0bb5ea77542c40f18ec

1
src/External/stb vendored Submodule

@ -0,0 +1 @@
Subproject commit f0569113c93ad095470c54bf34a17b36646bbbb5

View File

@ -6,14 +6,21 @@
#include <sstream>
#include <sys/stat.h>
const char* assets_default_paths [] = {".steam/steam/steamapps/common", ".local/share/Steam/steamapps/common",
const char* assets_default_paths [] = {
".steam/steam/steamapps/common",
".local/share/Steam/steamapps/common",
".var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common",
"snap/steam/common/.local/share/Steam/steamapps/common", nullptr};
"snap/steam/common/.local/share/Steam/steamapps/common",
nullptr
};
const char* workshop_content_default_paths [] = {
".local/share/Steam/steamapps/workshop/content", ".steam/steam/steamapps/workshop/content",
".local/share/Steam/steamapps/workshop/content",
".steam/steam/steamapps/workshop/content",
".var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/workshop/content",
"snap/steam/common/.local/share/Steam/steamapps/workshop/content", nullptr};
"snap/steam/common/.local/share/Steam/steamapps/workshop/content",
nullptr
};
std::filesystem::path detectHomepath () {
char* home = getenv ("HOME");

View File

@ -5,116 +5,225 @@
#include <cstdlib>
#include <cstring>
#include <getopt.h>
#include <iostream>
#include <argparse/argparse.hpp>
#define WORKSHOP_APP_ID 431960
#define APP_DIRECTORY "wallpaper_engine"
using namespace WallpaperEngine::Application;
struct option long_options [] = {
{"screen-root", required_argument, nullptr, 'r'}, {"bg", required_argument, nullptr, 'b'},
{"window", required_argument, nullptr, 'w'}, {"pkg", required_argument, nullptr, 'p'},
{"dir", required_argument, nullptr, 'd'}, {"silent", no_argument, nullptr, 's'},
{"volume", required_argument, nullptr, 'v'}, {"help", no_argument, nullptr, 'h'},
{"fps", required_argument, nullptr, 'f'}, {"assets-dir", required_argument, nullptr, 'a'},
{"screenshot", required_argument, nullptr, 'c'}, {"list-properties", no_argument, nullptr, 'l'},
{"set-property", required_argument, nullptr, 'o'}, {"noautomute", no_argument, nullptr, 'm'},
{"no-audio-processing", no_argument, nullptr, 'g'}, {"no-fullscreen-pause", no_argument, nullptr, 'n'},
{"disable-mouse", no_argument, nullptr, 'e'}, {"scaling", required_argument, nullptr, 't'},
{"clamping", required_argument, nullptr, 't'}, {nullptr, 0, nullptr, 0}};
/* std::hash::operator() isn't constexpr, so it can't be used to get hash values as compile-time constants
* So here is customHash. It skips all spaces, so hashes for " find " and "fi nd" are the same
* Basicly got it from here: https://stackoverflow.com/questions/8317508/hash-function-for-a-string
*/
constexpr size_t customHash (const char* str) {
constexpr size_t A = 54059; /* a prime */
constexpr size_t B = 76963; /* another prime */
constexpr size_t C = 86969; /* yet another prime */
constexpr size_t FIRSTH = 37; /* also prime */
size_t hash = FIRSTH;
while (*str) {
if (*str != ' ') // Skip spaces
hash = (hash * A) ^ (*str * B);
++str;
}
return hash % C;
}
std::string stringPathFixes (const std::string& s) {
if (s.empty ())
return s;
std::string str (s);
// remove single-quotes from the arguments
if (str [0] == '\'' && str [str.size () - 1] == '\'')
str.erase (str.size () - 1, 1).erase (0, 1);
return std::move (str);
}
CApplicationContext::CApplicationContext (int argc, char* argv []) {
// setup structs with sane default values for now
this->settings = {
.general =
{
.onlyListProperties = false,
.assets = "",
.defaultBackground = "",
.screenBackgrounds = {},
.properties = {},
},
.render =
{
.mode = NORMAL_WINDOW,
.maximumFPS = 30,
.pauseOnFullscreen = true,
.window =
{
.geometry = {},
.clamp = WallpaperEngine::Assets::ITexture::TextureFlags::ClampUVs,
.scalingMode = WallpaperEngine::Render::CWallpaperState::TextureUVsScaling::DefaultUVs,
},
},
.audio = {.enabled = true, .volume = 15, .automute = true, .audioprocessing = true},
.mouse =
{
.enabled = true,
},
.screenshot =
{
.take = false,
.path = "",
},
};
int c;
CApplicationContext::CApplicationContext (int argc, char* argv []) :
m_argc (argc),
m_argv (argv) {
std::string lastScreen;
while ((c = getopt_long (argc, argv, "b:r:p:d:shf:a:w:mnt:", long_options, nullptr)) != -1) {
switch (c) {
case 'n': this->settings.render.pauseOnFullscreen = false; break;
argparse::ArgumentParser program ("linux-wallpaperengine", "0.0", argparse::default_arguments::help);
case 'b':
if (lastScreen.empty ())
sLog.exception ("--bg has to go after a --screen-root argument");
auto& backgroundGroup = program.add_group ("Background options");
auto& backgroundMode = backgroundGroup.add_mutually_exclusive_group (false);
// no need to check for previous screen being in the list, as it's the only way for this variable
// to have any value
this->settings.general.screenBackgrounds [lastScreen] = translateBackground (optarg);
this->settings.general.screenScalings [lastScreen] = this->settings.render.window.scalingMode;
backgroundGroup.add_argument ("background id")
.help ("The background to use as default for screens with no background specified")
.action([this](const std::string& value) -> void {
this->settings.general.defaultBackground = translateBackground (value);
});
// update default background if not set
if (this->settings.general.defaultBackground.empty()) {
this->settings.general.defaultBackground = translateBackground (optarg);
backgroundMode.add_argument ("-w", "--window")
.help ("Window geometry to use for the given screen")
.action ([this](const std::string& value) -> void {
if (this->settings.render.mode == DESKTOP_BACKGROUND) {
sLog.exception ("Cannot run in both background and window mode");
}
if (this->settings.render.mode == EXPLICIT_WINDOW) {
sLog.exception ("Only one window at a time can be specified in explicit window mode");
}
break;
case 'o': {
std::string value = optarg;
this->settings.render.mode = EXPLICIT_WINDOW;
if (value.empty ()) {
sLog.exception ("Window geometry cannot be empty");
}
const char* str = value.c_str ();
const char* delim1 = strchr (str, 'x');
const char* delim2 = delim1 ? strchr (delim1 + 1, 'x') : nullptr;
const char* delim3 = delim2 ? strchr (delim2 + 1, 'x') : nullptr;
if (delim1 == nullptr || delim2 == nullptr || delim3 == nullptr) {
sLog.exception ("Window geometry must be in the format: XxYxWxH");
}
this->settings.render.window.geometry.x = strtol(str, nullptr, 10);
this->settings.render.window.geometry.y = strtol (delim1 + 1, nullptr, 10);
this->settings.render.window.geometry.z = strtol (delim2 + 1, nullptr, 10);
this->settings.render.window.geometry.w = strtol (delim3 + 1, nullptr, 10);
})
.append ();
backgroundMode.add_argument ("-r", "--screen-root")
.help ("The screen the following settings will have an effect on")
.action([this, &lastScreen](const std::string& value) -> void {
if (this->settings.general.screenBackgrounds.find (value) != this->settings.general.screenBackgrounds.end ()) {
sLog.exception ("Cannot specify the same screen more than once: ", value);
}
if (this->settings.render.mode == EXPLICIT_WINDOW) {
sLog.exception ("Cannot run in both background and window mode");
}
this->settings.render.mode = DESKTOP_BACKGROUND;
lastScreen = value;
this->settings.general.screenBackgrounds [lastScreen] = "";
this->settings.general.screenScalings [lastScreen] = this->settings.render.window.scalingMode;
this->settings.general.screenClamps [lastScreen] = this->settings.render.window.clamp;
})
.append ();
backgroundGroup.add_argument ("-b", "--bg")
.help ("After --screen-root, specifies the background to use for the given screen")
.action ([this, &lastScreen](const std::string& value) -> void {
this->settings.general.screenBackgrounds [lastScreen] = translateBackground (value);
// set the default background to the last one used
this->settings.general.defaultBackground = translateBackground (value);
})
.append ();
backgroundGroup.add_argument ("--scaling")
.help ("Scaling mode to use when rendering the background, this applies to the previous --window or --screen-root output, or the default background if no other background is specified")
.choices ("stretch", "fit", "fill", "default")
.action([this, &lastScreen](const std::string& value) -> void {
WallpaperEngine::Render::CWallpaperState::TextureUVsScaling mode;
if (value == "stretch") {
mode = WallpaperEngine::Render::CWallpaperState::TextureUVsScaling::StretchUVs;
} else if (value == "fit") {
mode = WallpaperEngine::Render::CWallpaperState::TextureUVsScaling::ZoomFitUVs;
} else if (value == "fill") {
mode = WallpaperEngine::Render::CWallpaperState::TextureUVsScaling::ZoomFillUVs;
} else if (value == "default") {
mode = WallpaperEngine::Render::CWallpaperState::TextureUVsScaling::DefaultUVs;
} else {
sLog.exception ("Invalid scaling mode: ", value);
}
if (this->settings.render.mode == DESKTOP_BACKGROUND) {
this->settings.general.screenScalings [lastScreen] = mode;
} else {
this->settings.render.window.scalingMode = mode;
}
})
.append ();
backgroundGroup.add_argument ("--clamp")
.help ("Clamp mode to use when rendering the background, this applies to the previous --window or --screen-root output, or the default background if no other background is specified")
.choices("clamp", "border", "repeat")
.action([this, &lastScreen](const std::string& value) -> void {
WallpaperEngine::Assets::ITexture::TextureFlags flags;
if (value == "clamp") {
flags = WallpaperEngine::Assets::ITexture::TextureFlags::ClampUVs;
} else if (value == "border") {
flags = WallpaperEngine::Assets::ITexture::TextureFlags::ClampUVsBorder;
} else if (value == "repeat") {
flags = WallpaperEngine::Assets::ITexture::TextureFlags::NoFlags;
} else {
sLog.exception ("Invalid clamp mode: ", value);
}
if (this->settings.render.mode == DESKTOP_BACKGROUND) {
this->settings.general.screenClamps [lastScreen] = flags;
} else {
this->settings.render.window.clamp = flags;
}
});
auto& performanceGroup = program.add_group ("Performance options");
performanceGroup.add_argument ("-f", "--fps")
.help ("Limits the FPS to the given number, useful to keep battery consumption low")
.default_value (30)
.store_into(this->settings.render.maximumFPS);
performanceGroup.add_argument ("--no-fullscreen-pause")
.help ("Prevents the background pausing when an app is fullscreen")
.flag ()
.action ([this](const std::string& value) -> void {
this->settings.render.pauseOnFullscreen = false;
});
auto& audioGroup = program.add_group ("Sound settings");
auto& audioSettingsGroup = audioGroup.add_mutually_exclusive_group (false);
audioSettingsGroup.add_argument ("-v", "--volume")
.help ("Volume for all the sounds in the background")
.default_value (15)
.store_into (this->settings.audio.volume);
audioSettingsGroup.add_argument ("-s", "--silent")
.help ("Mutes all the sound the wallpaper might produce")
.flag ()
.action ([this](const std::string& value) -> void {
this->settings.audio.enabled = false;
});
audioGroup.add_argument ("--noautomute")
.help ("Disables the automute when an app is playing sound")
.flag ()
.action([this](const std::string& value) -> void {
this->settings.audio.automute = false;
});
audioGroup.add_argument ("--no-audio-processing")
.help ("Disables audio processing for backgrounds")
.flag ()
.action ([this](const std::string& value) -> void {
this->settings.audio.audioprocessing = false;
});
auto& screenshotGroup = program.add_group ("Screenshot options");
screenshotGroup.add_argument ("--screenshot")
.help ("Takes a screenshot of the background for it's use with tools like PyWAL")
.default_value ("")
.action ([this](const std::string& value) -> void {
this->settings.screenshot.take = true;
this->settings.screenshot.path = value;
});
screenshotGroup.add_argument ("--screenshot-delay")
.help ("Frames to wait before taking the screenshot")
.default_value (5)
.store_into (this->settings.screenshot.delay);
auto& contentGroup = program.add_group ("Content options");
contentGroup.add_argument ("--assets-dir")
.help ("Folder where the assets are stored")
.default_value ("")
.action ([this](const std::string& value) -> void {
this->settings.general.assets = value;
});
auto& configurationGroup = program.add_group ("Wallpaper configuration options");
configurationGroup.add_argument ("--disable-mouse")
.help ("Disables mouse interaction with the backgrounds")
.flag ()
.action ([this](const std::string& value) -> void {
this->settings.mouse.enabled = false;
});
configurationGroup.add_argument ("--disable-parallax")
.help ("Disables parallax effect for the backgrounds")
.flag ()
.action ([this](const std::string& value) -> void {
this->settings.mouse.disableparallax = true;
});
configurationGroup.add_argument ("-l", "--list-properties")
.help ("List all the available properties and their configuration")
.flag ()
.store_into (this->settings.general.onlyListProperties);
configurationGroup.add_argument ("--set-property", "--property")
.help ("Overrides the default value of the given property")
.action([this](const std::string& value) -> void {
const std::string::size_type equals = value.find ('=');
// properties without value are treated as booleans for now
@ -122,122 +231,44 @@ CApplicationContext::CApplicationContext (int argc, char* argv []) {
this->settings.general.properties [value] = "1";
else
this->settings.general.properties [value.substr (0, equals)] = value.substr (equals + 1);
} break;
});
case 'l': this->settings.general.onlyListProperties = true; break;
auto& debuggingGroup = program.add_group ("Debugging options");
case 'r':
if (this->settings.general.screenBackgrounds.find (optarg) !=
this->settings.general.screenBackgrounds.end ())
sLog.exception ("Cannot specify the same screen more than once: ", optarg);
if (this->settings.render.mode == EXPLICIT_WINDOW)
sLog.exception ("Cannot run in both background and window mode");
debuggingGroup.add_argument ("-z", "--dump-structure")
.help ("Dumps the structure of the backgrounds")
.flag ()
.store_into (this->settings.general.dumpStructure);
this->settings.render.mode = DESKTOP_BACKGROUND;
lastScreen = optarg;
this->settings.general.screenBackgrounds [lastScreen] = "";
this->settings.general.screenScalings [lastScreen] = this->settings.render.window.scalingMode;
break;
program.add_epilog (
"Usage examples:\n"
" linux-wallpaperengine --screen-root HDMI-1 --bg 2317494988 --scaling fill --clamp border\n"
" Runs the background 2317494988 on screen HDMI-1, scaling it to fill the screen and clamping the UVs to the border\n\n"
" linux-wallpaperengine 2317494988\n"
" Previews the background 2317494988 on a window\n\n"
" linux-wallpaperengine --screen-root HDMI-1 --bg 2317494988 --screen-root HDMI-2 --bg 1108150151\n"
" Runs two backgrounds on two screens, one on HDMI-1 and the other on HDMI-2\n\n"
" linux-wallpaperengine --screen-root HDMI-1 --screen-root HDMI-2 2317494988\n"
" Runs the background 2317494988 on two screens, one on HDMI-1 and the other on HDMI-2\n\n"
);
case 'w':
if (this->settings.render.mode == DESKTOP_BACKGROUND)
sLog.exception ("Cannot run in both background and window mode");
program.parse_known_args (argc, argv);
if (optarg != nullptr) {
this->settings.render.mode = EXPLICIT_WINDOW;
// read window geometry
char* pos = optarg;
this->settings.audio.volume = std::max(0, std::min (this->settings.audio.volume, 128));
this->settings.screenshot.delay = std::max (0, std::min (this->settings.screenshot.delay, 5));
if (pos != nullptr)
this->settings.render.window.geometry.x = atoi (pos);
if ((pos = strchr (pos, 'x')) != nullptr)
this->settings.render.window.geometry.y = atoi (pos + 1);
if ((pos = strchr (pos + 1, 'x')) != nullptr)
this->settings.render.window.geometry.z = atoi (pos + 1);
if ((pos = strchr (pos + 1, 'x')) != nullptr)
this->settings.render.window.geometry.w = atoi (pos + 1);
}
break;
// use std::cout on this in case logging is disabled, this way it's easy to look at what is running
std::stringbuf buffer;
std::ostream bufferStream (&buffer);
case 'p':
case 'd':
sLog.error ("--dir/--pkg is deprecated and not used anymore");
this->settings.general.defaultBackground = translateBackground (stringPathFixes (optarg));
break;
bufferStream << "Running with: ";
case 's': this->settings.audio.enabled = false; break;
case 'h':
printHelp (argv [0]);
std::exit (0);
break;
case 'f': this->settings.render.maximumFPS = atoi (optarg); break;
case 'a': this->settings.general.assets = stringPathFixes (optarg); break;
case 'v': this->settings.audio.volume = std::max (atoi (optarg), 128); break;
case 'c':
this->settings.screenshot.take = true;
this->settings.screenshot.path = stringPathFixes (optarg);
break;
case 'm': this->settings.audio.automute = false; break;
case 'g': this->settings.audio.audioprocessing = false; break;
case 'e': this->settings.mouse.enabled = false; break;
case 't': {
size_t hash = customHash (optarg);
// Use a switch statement with the hash
switch (hash) {
// --scale options
case customHash ("stretch"):
this->settings.render.window.scalingMode =
WallpaperEngine::Render::CWallpaperState::TextureUVsScaling::StretchUVs;
break;
case customHash ("fit"):
this->settings.render.window.scalingMode =
WallpaperEngine::Render::CWallpaperState::TextureUVsScaling::ZoomFitUVs;
break;
case customHash ("fill"):
this->settings.render.window.scalingMode =
WallpaperEngine::Render::CWallpaperState::TextureUVsScaling::ZoomFillUVs;
break;
case customHash ("default"):
this->settings.render.window.scalingMode =
WallpaperEngine::Render::CWallpaperState::TextureUVsScaling::DefaultUVs;
break;
// --clamp options
case customHash ("clamp"):
this->settings.render.window.clamp = WallpaperEngine::Assets::ITexture::TextureFlags::ClampUVs;
break;
case customHash ("border"):
this->settings.render.window.clamp =
WallpaperEngine::Assets::ITexture::TextureFlags::ClampUVsBorder;
break;
case customHash ("repeat"):
this->settings.render.window.clamp = WallpaperEngine::Assets::ITexture::TextureFlags::NoFlags;
break;
default:
sLog.error ("Wrong argument:");
sLog.error (optarg);
sLog.exception ("Wrong argument provided for --scale or --clamp option.");
break;
}
} break;
default: sLog.out ("Default on path parsing: ", optarg); break;
}
}
if (this->settings.general.defaultBackground.empty ()) {
if (optind < argc && strlen (argv [optind]) > 0) {
this->settings.general.defaultBackground = translateBackground (argv [optind]);
}
for (int i = 0; i < argc; i ++) {
bufferStream << argv [i];
bufferStream << " ";
}
std::cout << buffer.str() << std::endl;
// perform some extra validation on the inputs
this->validateAssets ();
this->validateScreenshot ();
@ -246,6 +277,23 @@ CApplicationContext::CApplicationContext (int argc, char* argv []) {
this->state.general.keepRunning = true;
this->state.audio.enabled = this->settings.audio.enabled;
this->state.audio.volume = this->settings.audio.volume;
this->state.mouse.enabled = this->settings.mouse.enabled;
#if DEMOMODE
sLog.error ("WARNING: RUNNING IN DEMO MODE WILL STOP WALLPAPERS AFTER 5 SECONDS SO VIDEO CAN BE RECORDED");
// special settings for demomode
this->settings.render.maximumFPS = 30;
this->settings.screenshot.take = false;
this->settings.render.pauseOnFullscreen = false;
#endif /* DEMOMODE */
}
int CApplicationContext::getArgc () const {
return this->m_argc;
}
char** CApplicationContext::getArgv () const {
return this->m_argv;
}
std::filesystem::path CApplicationContext::translateBackground (const std::string& bgIdOrPath) {
@ -270,7 +318,7 @@ void CApplicationContext::validateAssets () {
}
}
void CApplicationContext::validateScreenshot () {
void CApplicationContext::validateScreenshot () const {
if (!this->settings.screenshot.take)
return;
@ -282,35 +330,3 @@ void CApplicationContext::validateScreenshot () {
if (extension != ".bmp" && extension != ".png" && extension != ".jpeg" && extension != ".jpg")
sLog.exception ("Cannot determine screenshot format, unknown extension ", extension);
}
void CApplicationContext::printHelp (const char* route) {
sLog.out ("Usage: ", route, " [options] background_path/background_id");
sLog.out ("");
sLog.out ("where background_path/background_id can be:");
sLog.out ("\tthe ID of the background (for autodetection on your steam installation)");
sLog.out ("\ta full path to the background's folder");
sLog.out ("");
sLog.out ("options:");
sLog.out ("\t--silent\t\t\t\t\tMutes all the sound the wallpaper might produce");
sLog.out ("\t--volume <amount>\t\t\tSets the volume for all the sounds in the background");
sLog.out ("\t--noautomute\t\t\t\tDisables the automute when an app is playing sound");
sLog.out ("\t--no-audio-processing\t\t\t\tDisables audio processing for backgrounds");
sLog.out ("\t--screen-root <screen name>\tDisplay as screen's background");
sLog.out (
"\t--window <geometry>\tRuns in window mode, geometry has to be XxYxWxH and sets the position and size of the window");
sLog.out ("\t--fps <maximum-fps>\t\t\tLimits the FPS to the given number, useful to keep battery consumption low");
sLog.out ("\t--assets-dir <path>\t\t\tFolder where the assets are stored");
sLog.out ("\t--screenshot\t\t\t\tTakes a screenshot of the background");
sLog.out ("\t--list-properties\t\t\tList all the available properties and their possible values");
sLog.out ("\t--set-property <name=value>\tOverrides the default value of the given property");
sLog.out ("\t--no-fullscreen-pause\tPrevents the background pausing when an app is fullscreen");
sLog.out ("\t--disable-mouse\tDisables mouse interactions");
sLog.out (
"\t--bg <background_path/background_id>\tAfter --screen-root uses the specified background only on that screen");
sLog.out (
"\t--scaling <mode>\t Scaling mode for wallpaper. Can be stretch, fit, fill, default. Must be used before wallpaper provided.\n\
\t\t For default wallpaper last specified value will be used.\n\
\t\t Example: ./wallengine --scaling stretch --screen-root eDP-1 --bg 2667198601 --scaling fill --screen-root eDP-2 2667198602");
sLog.out (
"\t--clamping <mode>\t Clamping mode for all wallpapers. Can be clamp, border, repeat. Enables GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, GL_REPEAT accordingly. Default is clamp.");
}

View File

@ -36,18 +36,20 @@ class CApplicationContext {
struct {
/** If the user requested a list of properties for the given background */
bool onlyListProperties;
/** If the user requested a dump of the background structure */
bool dumpStructure;
/** The path to the assets folder */
std::filesystem::path assets;
/** Background to load (provided as the final argument) as fallback for multi-screen setups */
std::filesystem::path defaultBackground;
/** The backgrounds specified for different screens */
std::map<std::string, std::filesystem::path> screenBackgrounds;
/** Properties to change values for */
std::map<std::string, std::string> properties;
/** The scaling mode for different screens */
std::map<std::string, WallpaperEngine::Render::CWallpaperState::TextureUVsScaling> screenScalings;
/** The clamping mode for different screens */
std::map<std::string, WallpaperEngine::Assets::ITexture::TextureFlags> screenClamps;
} general;
/**
@ -89,6 +91,8 @@ class CApplicationContext {
struct {
/** If the mouse movement is enabled */
bool enabled;
/** If the mouse parallax should be disabled */
bool disableparallax;
} mouse;
/**
@ -97,14 +101,60 @@ class CApplicationContext {
struct {
/** If an screenshot should be taken */
bool take;
/** The frames to wait until the screenshot is taken */
int delay;
/** The path to where the screenshot must be saved */
std::filesystem::path path;
} screenshot;
} settings;
} settings = {
.general = {
.onlyListProperties = false,
.dumpStructure = false,
.assets = "",
.defaultBackground = "",
.screenBackgrounds = {},
.properties = {},
.screenScalings = {},
.screenClamps = {},
},
.render = {
.mode = NORMAL_WINDOW,
.maximumFPS = 30,
.pauseOnFullscreen = true,
.window = {
.geometry = {},
.clamp = WallpaperEngine::Assets::ITexture::TextureFlags::ClampUVs,
.scalingMode = WallpaperEngine::Render::CWallpaperState::TextureUVsScaling::DefaultUVs,
},
},
.audio = {
.enabled = true,
.volume = 15,
.automute = true,
.audioprocessing = true,
},
.mouse = {
.enabled = true,
.disableparallax = false,
},
.screenshot = {
.take = false,
.delay = 5,
.path = "",
},
};
CApplicationState state;
[[nodiscard]] int getArgc() const;
[[nodiscard]] char** getArgv() const;
private:
/** Program argument count on startup */
int m_argc;
/** Program arguments on startup */
char** m_argv;
/**
* Validates the assets folder and ensures a valid one is present
*/
@ -113,7 +163,7 @@ class CApplicationContext {
/**
* Validates the screenshot settings
*/
void validateScreenshot ();
void validateScreenshot () const;
/**
* Validates a background parameter and returns the real bgIdOrPath to it
@ -122,10 +172,5 @@ class CApplicationContext {
* @return
*/
static std::filesystem::path translateBackground (const std::string& bgIdOrPath);
/**
* Prints the normal help message
*/
static void printHelp (const char* route);
};
} // namespace WallpaperEngine::Application

View File

@ -1 +0,0 @@
#include "CApplicationState.h"

View File

@ -6,18 +6,15 @@
#include "WallpaperEngine/Assets/CDirectory.h"
#include "WallpaperEngine/Assets/CVirtualContainer.h"
#include "WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.h"
#include "WallpaperEngine/Input/Drivers/CGLFWMouseInput.h"
#include "WallpaperEngine/Logging/CLog.h"
#include "WallpaperEngine/Render/CRenderContext.h"
#include "WallpaperEngine/PrettyPrinter/CPrettyPrinter.h"
#include "WallpaperEngine/Core/Wallpapers/CWeb.h"
#include "WallpaperEngine/Render/Drivers/CVideoFactories.h"
#ifdef ENABLE_WAYLAND
#include "WallpaperEngine/Input/Drivers/CWaylandMouseInput.h"
#include "WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.h"
#endif /* ENABLE_WAYLAND */
#ifdef ENABLE_X11
#include "WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h"
#endif /* ENABLE_X11 */
#if DEMOMODE
#include "recording.h"
#endif /* DEMOMODE */
#include <unistd.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
@ -30,58 +27,28 @@ float g_TimeLast;
float g_Daytime;
namespace WallpaperEngine::Application {
CWallpaperApplication::CWallpaperApplication (CApplicationContext& context,
WallpaperEngine::WebBrowser::CWebBrowserContext& browserContext) :
m_context (context),
m_browserContext (browserContext) {
CWallpaperApplication::CWallpaperApplication (CApplicationContext& context) :
m_context (context) {
this->loadBackgrounds ();
this->setupProperties ();
this->setupBrowser();
}
CWallpaperApplication::~CWallpaperApplication () {
delete m_renderContext;
delete m_videoDriver;
delete m_audioContext;
delete m_audioDriver;
delete m_inputContext;
}
void CWallpaperApplication::setupContainer (CCombinedContainer& container, const std::string& bg) const {
void CWallpaperApplication::setupContainer (const std::shared_ptr<CCombinedContainer>& container, const std::string& bg) const {
const std::filesystem::path basepath = bg;
container.add (new CDirectory (basepath));
container.addPkg (basepath / "scene.pkg");
container.addPkg (basepath / "gifscene.pkg");
container->add (std::make_shared<CDirectory> (basepath));
container->addPkg (basepath / "scene.pkg");
container->addPkg (basepath / "gifscene.pkg");
try {
container.add (new CDirectory (this->m_context.settings.general.assets));
container->add (std::make_shared <CDirectory> (this->m_context.settings.general.assets));
} catch (CAssetLoadException&) {
sLog.exception ("Cannot find a valid assets folder, resolved to ", this->m_context.settings.general.assets);
}
// add two possible patches directories to the container
// hopefully one sticks
bool relative = true;
bool absolute = true;
try {
container.add (new CDirectory ("../share/"));
} catch (CAssetLoadException&) {
relative = false;
}
try {
container.add (new CDirectory (DATADIR));
} catch (CAssetLoadException&) {
absolute = false;
}
if (!relative && !absolute)
sLog.error ("WARNING: Shader patches directory cannot be found, this might make some backgrounds not work "
"properly");
// TODO: move this somewhere else?
auto* virtualContainer = new CVirtualContainer ();
auto virtualContainer = std::make_shared <CVirtualContainer> ();
//
// Had to get a little creative with the effects to achieve the same bloom effect without any custom code
@ -92,84 +59,129 @@ void CWallpaperApplication::setupContainer (CCombinedContainer& container, const
// add the effect file for screen bloom
// add some model for the image element even if it's going to waste rendering cycles
virtualContainer->add ("effects/wpenginelinux/bloomeffect.json",
"{"
"\t\"name\":\"camerabloom_wpengine_linux\","
"\t\"group\":\"wpengine_linux_camera\","
"\t\"dependencies\":[],"
"\t\"passes\":"
"\t["
"\t\t{"
"\t\t\t\"material\": \"materials/util/downsample_quarter_bloom.json\","
"\t\t\t\"target\": \"_rt_4FrameBuffer\","
"\t\t\t\"bind\":"
"\t\t\t["
"\t\t\t\t{"
"\t\t\t\t\t\"name\": \"_rt_FullFrameBuffer\","
"\t\t\t\t\t\"index\": 0"
"\t\t\t\t}"
"\t\t\t]"
"\t\t},"
"\t\t{"
"\t\t\t\"material\": \"materials/util/downsample_eighth_blur_v.json\","
"\t\t\t\"target\": \"_rt_8FrameBuffer\","
"\t\t\t\"bind\":"
"\t\t\t["
"\t\t\t\t{"
"\t\t\t\t\t\"name\": \"_rt_4FrameBuffer\","
"\t\t\t\t\t\"index\": 0"
"\t\t\t\t}"
"\t\t\t]"
"\t\t},"
"\t\t{"
"\t\t\t\"material\": \"materials/util/blur_h_bloom.json\","
"\t\t\t\"target\": \"_rt_Bloom\","
"\t\t\t\"bind\":"
"\t\t\t["
"\t\t\t\t{"
"\t\t\t\t\t\"name\": \"_rt_8FrameBuffer\","
"\t\t\t\t\t\"index\": 0"
"\t\t\t\t}"
"\t\t\t]"
"\t\t},"
"\t\t{"
"\t\t\t\"material\": \"materials/util/combine.json\","
"\t\t\t\"target\": \"_rt_FullFrameBuffer\","
"\t\t\t\"bind\":"
"\t\t\t["
"\t\t\t\t{"
"\t\t\t\t\t\"name\": \"_rt_imageLayerComposite_-1_a\","
"\t\t\t\t\t\"index\": 0"
"\t\t\t\t},"
"\t\t\t\t{"
"\t\t\t\t\t\"name\": \"_rt_Bloom\","
"\t\t\t\t\t\"index\": 1"
"\t\t\t\t}"
"\t\t\t]"
"\t\t}"
"\t]"
"}");
virtualContainer->add (
"effects/wpenginelinux/bloomeffect.json",
{
{"name", "camerabloom_wpengine_linux"},
{"group", "wpengine_linux_camera"},
{"dependencies", json::array ()},
{"passes",
json::array (
{
{
{"material", "materials/util/downsample_quarter_bloom.json"},
{"target", "_rt_4FrameBuffer"},
{
"bind",
json::array (
{
{
{"name", "_rt_FullFrameBuffer"},
{"index", 0}
}
}
)
}
},
{
{"material", "materials/util/downsample_eighth_blur_v.json"},
{"target", "_rt_8FrameBuffer"},
{
"bind",
json::array (
{
{
{"name", "_rt_4FrameBuffer"},
{"index", 0}
}
}
)
}
},
{
{"material", "materials/util/blur_h_bloom.json"},
{"target", "_rt_Bloom"},
{
"bind",
json::array (
{
{
{"name", "_rt_8FrameBuffer"},
{"index", 0}
}
}
)
}
},
{
{"material", "materials/util/combine.json"},
{"target", "_rt_FullFrameBuffer"},
{
"bind",
json::array (
{
{
{"name", "_rt_imageLayerComposite_-1_a"},
{"index", 0}
},
{
{"name", "_rt_Bloom"},
{"index", 1}
}
}
)
}
}
}
),
}
}
);
virtualContainer->add ("models/wpenginelinux.json", "{"
"\t\"material\":\"materials/wpenginelinux.json\""
"}");
virtualContainer->add (
"models/wpenginelinux.json",
{
{"material","materials/wpenginelinux.json"}
}
);
// models require materials, so add that too
virtualContainer->add ("materials/wpenginelinux.json", "{"
"\t\"passes\":"
"\t\t["
"\t\t\t{"
"\t\t\t\t\"blending\": \"normal\","
"\t\t\t\t\"cullmode\": \"nocull\","
"\t\t\t\t\"depthtest\": \"disabled\","
"\t\t\t\t\"depthwrite\": \"disabled\","
"\t\t\t\t\"shader\": \"genericimage2\","
"\t\t\t\t\"textures\": [\"_rt_FullFrameBuffer\"]"
"\t\t\t}"
"\t\t]"
"}");
virtualContainer->add(
"materials/wpenginelinux.json",
{
{"passes", json::array (
{
{
{"blending", "normal"},
{"cullmode", "nocull"},
{"depthtest", "disabled"},
{"depthwrite", "disabled"},
{"shader", "genericimage2"},
{"textures", json::array ({"_rt_FullFrameBuffer"})}
}
}
)}}
);
container.add (virtualContainer);
virtualContainer->add(
"shaders/commands/copy.frag",
"uniform sampler2D g_Texture0;\n"
"in vec2 v_TexCoord;\n"
"void main () {\n"
"out_FragColor = texture (g_Texture0, v_TexCoord);\n"
"}"
);
virtualContainer->add(
"shaders/commands/copy.vert",
"in vec3 a_Position;\n"
"in vec2 a_TexCoord;\n"
"out vec2 v_TexCoord;\n"
"void main () {\n"
"gl_Position = vec4 (a_Position, 1.0);\n"
"v_TexCoord = a_TexCoord;\n"
"}"
);
container->add (virtualContainer);
}
void CWallpaperApplication::loadBackgrounds () {
@ -189,24 +201,24 @@ void CWallpaperApplication::loadBackgrounds () {
}
}
Core::CProject* CWallpaperApplication::loadBackground (const std::string& bg) {
auto* container = new CCombinedContainer ();
std::shared_ptr<Core::CProject> CWallpaperApplication::loadBackground (const std::string& bg) {
const auto container = std::make_shared <CCombinedContainer> ();
this->setupContainer (*container, bg);
this->setupContainer (container, bg);
return Core::CProject::fromFile ("project.json", container);
}
void CWallpaperApplication::setupPropertiesForProject (const Core::CProject* project) {
void CWallpaperApplication::setupPropertiesForProject (const std::shared_ptr<const Core::CProject>& project) {
// show properties if required
for (const auto cur : project->getProperties ()) {
for (const auto& [key, cur] : project->getProperties ()) {
// update the value of the property
auto override = this->m_context.settings.general.properties.find (cur->getName ());
auto override = this->m_context.settings.general.properties.find (key);
if (override != this->m_context.settings.general.properties.end ()) {
sLog.out ("Applying override value for ", cur->getName ());
sLog.out ("Applying override value for ", key);
cur->update (override->second);
cur->set (override->second);
}
if (this->m_context.settings.general.onlyListProperties)
@ -215,35 +227,68 @@ void CWallpaperApplication::setupPropertiesForProject (const Core::CProject* pro
}
void CWallpaperApplication::setupProperties () {
for (const auto& [backgrounc, info] : this->m_backgrounds)
for (const auto& [background, info] : this->m_backgrounds)
this->setupPropertiesForProject (info);
}
void CWallpaperApplication::takeScreenshot (const std::filesystem::path& filename) {
void CWallpaperApplication::setupBrowser () {
bool anyWebProject = std::any_of (
this->m_backgrounds.begin (), this->m_backgrounds.end (),
[](const std::pair<const std::string, std::shared_ptr<const Core::CProject>>& pair) -> bool {
return pair.second->getWallpaper()->is<Core::Wallpapers::CWeb> ();
});
// do not perform any initialization if no web background is present
if (!anyWebProject) {
return;
}
this->m_browserContext = std::make_unique <WebBrowser::CWebBrowserContext> (*this);
}
void CWallpaperApplication::takeScreenshot (const std::filesystem::path& filename) const {
// this should be getting called at the end of the frame, so the right thing should be bound already
const int width = this->m_renderContext->getOutput ().getFullWidth ();
const int height = this->m_renderContext->getOutput ().getFullHeight ();
const bool vflip = this->m_renderContext->getOutput ().renderVFlip ();
// build the output file with stbi_image_write
auto* bitmap = new uint8_t [width * height * sizeof (uint8_t) * 3] {0};
auto* bitmap = new uint8_t [width * height * 3] {0};
int xoffset = 0;
for (const auto& [screen, viewport] : this->m_renderContext->getOutput ().getViewports ()) {
// activate opengl context so we can read from the framebuffer
viewport->makeCurrent ();
// make room for storing the pixel of this viewport
auto* buffer = new uint8_t [viewport->viewport.z * viewport->viewport.w * sizeof (uint8_t) * 3];
const auto bufferSize = (viewport->viewport.z - viewport->viewport.x) * (viewport->viewport.w - viewport->viewport.y) * 3;
auto* buffer = new uint8_t [bufferSize];
const uint8_t* pixel = buffer;
// read the viewport data into the pixel buffer
glReadPixels (viewport->viewport.x, viewport->viewport.y, viewport->viewport.z, viewport->viewport.w, GL_RGB,
GL_UNSIGNED_BYTE, buffer);
glPixelStorei (GL_PACK_ALIGNMENT, 1);
// 4.5 supports glReadnPixels, anything older doesn't...
if (GLEW_VERSION_4_5) {
glReadnPixels (viewport->viewport.x, viewport->viewport.y, viewport->viewport.z, viewport->viewport.w,
GL_RGB, GL_UNSIGNED_BYTE, bufferSize, buffer);
} else {
// fallback to old version
glReadPixels (viewport->viewport.x, viewport->viewport.y, viewport->viewport.z, viewport->viewport.w,
GL_RGB, GL_UNSIGNED_BYTE, buffer);
}
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
sLog.error ("Cannot obtain pixel data for screen ", screen, ". OpenGL error: ", error);
delete [] buffer;
continue;
}
// now get access to the pixels
for (int y = viewport->viewport.w; y > 0; y--) {
for (int y = 0; y < viewport->viewport.w; y++) {
for (int x = 0; x < viewport->viewport.z; x++) {
int xfinal = x + xoffset;
int yfinal = this->m_renderContext->getOutput ().renderVFlip () ? (viewport->viewport.w - y) : y;
int yfinal = vflip ? (viewport->viewport.w - y - 1) : y;
bitmap [yfinal * width * 3 + xfinal * 3] = *pixel++;
bitmap [yfinal * width * 3 + xfinal * 3 + 1] = *pixel++;
@ -267,9 +312,11 @@ void CWallpaperApplication::takeScreenshot (const std::filesystem::path& filenam
} else if (extension == ".jpg" || extension == ".jpeg") {
stbi_write_jpg (filename.c_str (), width, height, 3, bitmap, 100);
}
delete [] bitmap;
}
void CWallpaperApplication::show () {
void CWallpaperApplication::setupOutput () {
const char* XDG_SESSION_TYPE = getenv ("XDG_SESSION_TYPE");
if (!XDG_SESSION_TYPE) {
@ -279,122 +326,94 @@ void CWallpaperApplication::show () {
sLog.debug ("Checking for window servers: ");
#ifdef ENABLE_WAYLAND
sLog.debug ("\twayland");
#endif // ENABLE_WAYLAND
#ifdef ENABLE_X11
sLog.debug ("\tx11");
#endif // ENABLE_X11
#ifdef ENABLE_WAYLAND
bool isWayland = strncmp ("wayland", XDG_SESSION_TYPE, strlen ("wayland")) == 0;
#endif // ENABLE_WAYLAND
#ifdef ENABLE_X11
bool isX11 = strncmp ("x11", XDG_SESSION_TYPE, strlen ("x11")) == 0;
#endif // ENABLE_X11
if (this->m_context.settings.render.mode == CApplicationContext::DESKTOP_BACKGROUND) {
#ifdef ENABLE_WAYLAND
if (isWayland) {
m_videoDriver = new WallpaperEngine::Render::Drivers::CWaylandOpenGLDriver (this->m_context, *this);
m_inputContext =
new WallpaperEngine::Input::CInputContext (new WallpaperEngine::Input::Drivers::CWaylandMouseInput (
reinterpret_cast<WallpaperEngine::Render::Drivers::CWaylandOpenGLDriver*> (m_videoDriver)));
this->m_fullScreenDetector =
new WallpaperEngine::Render::Drivers::Detectors::CWaylandFullScreenDetector (this->m_context);
}
#endif // ENABLE_WAYLAND
#ifdef ENABLE_X11
#ifdef ENABLE_WAYLAND
else
#endif // ENABLE_WAYLAND
if (isX11) {
m_videoDriver =
new WallpaperEngine::Render::Drivers::CGLFWOpenGLDriver ("wallpaperengine", this->m_context, *this);
m_inputContext =
new WallpaperEngine::Input::CInputContext (new WallpaperEngine::Input::Drivers::CGLFWMouseInput (
reinterpret_cast<Render::Drivers::CGLFWOpenGLDriver*> (m_videoDriver)));
this->m_fullScreenDetector = new WallpaperEngine::Render::Drivers::Detectors::CX11FullScreenDetector (
this->m_context, *reinterpret_cast<Render::Drivers::CGLFWOpenGLDriver*> (m_videoDriver));
}
#endif // ENABLE_X11
#ifdef ENABLE_X11
else
#endif // ENABLE_X11
{
sLog.exception (
"Cannot run in background mode, window server could not be detected. XDG_SESSION_TYPE must be wayland or x11");
for (const auto& windowServer : sVideoFactories.getRegisteredDrivers ()) {
sLog.debug("\t", windowServer);
}
this->m_videoDriver = sVideoFactories.createVideoDriver (
this->m_context.settings.render.mode, XDG_SESSION_TYPE, this->m_context, *this);
this->m_fullScreenDetector = sVideoFactories.createFullscreenDetector (XDG_SESSION_TYPE, this->m_context, *this->m_videoDriver);
}
void CWallpaperApplication::setupAudio () {
// ensure audioprocessing is required by any background, and we have it enabled
bool audioProcessingRequired = std::any_of (this->m_backgrounds.begin (), this->m_backgrounds.end (), [](const auto& pair) -> bool {
return pair.second->supportsAudioProcessing ();
});
if (audioProcessingRequired && this->m_context.settings.audio.audioprocessing) {
this->m_audioRecorder = std::make_unique <WallpaperEngine::Audio::Drivers::Recorders::CPulseAudioPlaybackRecorder> ();
} else {
m_videoDriver =
new WallpaperEngine::Render::Drivers::CGLFWOpenGLDriver ("wallpaperengine", this->m_context, *this);
#ifdef ENABLE_WAYLAND
if (isWayland) {
this->m_fullScreenDetector =
new WallpaperEngine::Render::Drivers::Detectors::CWaylandFullScreenDetector (this->m_context);
}
#endif // ENABLE_WAYLAND
#ifdef ENABLE_X11
#ifdef ENABLE_WAYLAND
else
#endif // ENABLE_WAYLAND
if (isX11) {
this->m_fullScreenDetector = new WallpaperEngine::Render::Drivers::Detectors::CX11FullScreenDetector (
this->m_context, *reinterpret_cast<Render::Drivers::CGLFWOpenGLDriver*> (m_videoDriver));
}
#endif // ENABLE_X11
else {
this->m_fullScreenDetector =
new WallpaperEngine::Render::Drivers::Detectors::CFullScreenDetector (this->m_context);
this->m_audioRecorder = std::make_unique <WallpaperEngine::Audio::Drivers::Recorders::CPlaybackRecorder> ();
}
m_inputContext =
new WallpaperEngine::Input::CInputContext (new WallpaperEngine::Input::Drivers::CGLFWMouseInput (
reinterpret_cast<Render::Drivers::CGLFWOpenGLDriver*> (m_videoDriver)));
}
if (this->m_context.settings.audio.audioprocessing) {
this->m_audioRecorder = new WallpaperEngine::Audio::Drivers::Recorders::CPulseAudioPlaybackRecorder ();
if (this->m_context.settings.audio.automute) {
m_audioDetector = std::make_unique <WallpaperEngine::Audio::Drivers::Detectors::CPulseAudioPlayingDetector> (this->m_context, *this->m_fullScreenDetector);
} else {
this->m_audioRecorder = new WallpaperEngine::Audio::Drivers::Recorders::CPlaybackRecorder ();
m_audioDetector = std::make_unique <WallpaperEngine::Audio::Drivers::Detectors::CAudioPlayingDetector> (this->m_context, *this->m_fullScreenDetector);
}
// audio playing detector
WallpaperEngine::Audio::Drivers::Detectors::CPulseAudioPlayingDetector audioDetector (this->m_context,
*this->m_fullScreenDetector);
// initialize sdl audio driver
m_audioDriver =
new WallpaperEngine::Audio::Drivers::CSDLAudioDriver (this->m_context, audioDetector, *this->m_audioRecorder);
m_audioDriver = std::make_unique <WallpaperEngine::Audio::Drivers::CSDLAudioDriver> (this->m_context, *this->m_audioDetector, *this->m_audioRecorder);
// initialize audio context
m_audioContext = new WallpaperEngine::Audio::CAudioContext (*m_audioDriver);
// initialize render context
m_renderContext = new WallpaperEngine::Render::CRenderContext (*m_videoDriver, *m_inputContext, *this);
m_audioContext = std::make_unique <WallpaperEngine::Audio::CAudioContext> (*m_audioDriver);
}
void CWallpaperApplication::prepareOutputs () {
// initialize render context
m_renderContext = std::make_unique <WallpaperEngine::Render::CRenderContext> (*m_videoDriver, *this);
// create a new background for each screen
// set all the specific wallpapers required
for (const auto& [background, info] : this->m_backgrounds) {
m_renderContext->setWallpaper (background,
m_renderContext->setWallpaper (
background,
WallpaperEngine::Render::CWallpaper::fromWallpaper (
info->getWallpaper (), *m_renderContext, *m_audioContext, m_browserContext,
this->m_context.settings.general.screenScalings [background]));
info->getWallpaper (), *m_renderContext, *m_audioContext, m_browserContext.get (),
this->m_context.settings.general.screenScalings [background],
this->m_context.settings.general.screenClamps [background]
)
);
}
}
// wallpapers are setup, free browsesr context if possible
if (!this->m_browserContext.isUsed ()) {
this->m_browserContext.stop ();
}
void CWallpaperApplication::show () {
this->setupOutput ();
this->setupAudio ();
this->prepareOutputs ();
static time_t seconds;
static struct tm* timeinfo;
while (this->m_context.state.general.keepRunning && !m_videoDriver->closeRequested ()) {
if (this->m_context.settings.general.dumpStructure) {
auto prettyPrinter = PrettyPrinter::CPrettyPrinter ();
for (const auto& [background, info] : this->m_renderContext->getWallpapers ()) {
prettyPrinter.printWallpaper (*info);
}
std::cout << prettyPrinter.str () << std::endl;
}
#if DEMOMODE
// ensure only one background is running so everything can be properly caught
if (this->m_renderContext->getWallpapers ().size () > 1) {
sLog.exception ("Demo mode only supports one background");
}
int width = this->m_renderContext->getWallpapers ().begin ()->second->getWidth ();
int height = this->m_renderContext->getWallpapers ().begin ()->second->getHeight ();
std::vector<uint8_t> pixels(width * height * 3);
bool initialized = false;
int frame = 0;
int elapsed_frames = 0;
#endif /* DEMOMODE */
while (this->m_context.state.general.keepRunning) {
// update g_Daytime
time (&seconds);
timeinfo = localtime (&seconds);
g_Daytime = ((timeinfo->tm_hour * 60) + timeinfo->tm_min) / (24.0 * 60.0);
g_Daytime = ((timeinfo->tm_hour * 60.0f) + timeinfo->tm_min) / (24.0f * 60.0f);
// keep track of the previous frame's time
g_TimeLast = g_Time;
@ -403,7 +422,43 @@ void CWallpaperApplication::show () {
// update audio recorder
m_audioDriver->update ();
// update input information
m_inputContext->update ();
m_videoDriver->getInputContext ().update ();
// process driver events
m_videoDriver->dispatchEventQueue ();
if (m_videoDriver->closeRequested()) {
sLog.out ("Stop requested by driver");
this->m_context.state.general.keepRunning = false;
}
#if DEMOMODE
elapsed_frames ++;
// wait for a full render cycle before actually starting
// this gives some extra time for video and web decoders to set themselves up
// because of size changes
if (elapsed_frames > this->m_context.settings.render.maximumFPS) {
if (!initialized) {
width = this->m_renderContext->getWallpapers ().begin ()->second->getWidth ();
height = this->m_renderContext->getWallpapers ().begin ()->second->getHeight ();
pixels.reserve(width * height * 3);
init_encoder ("output.webm", width, height);
initialized = true;
}
glBindFramebuffer (GL_FRAMEBUFFER, this->m_renderContext->getWallpapers ().begin ()->second->getWallpaperFramebuffer());
glPixelStorei (GL_PACK_ALIGNMENT, 1);
glReadPixels (0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels.data ());
write_video_frame (pixels.data ());
frame ++;
// stop after the given framecount
if (frame >= FRAME_COUNT) {
this->m_context.state.general.keepRunning = false;
}
}
#endif /* DEMOMODE */
// check for fullscreen windows and wait until there's none fullscreen
if (this->m_fullScreenDetector->anythingFullscreen () && this->m_context.state.general.keepRunning) {
m_renderContext->setPause (true);
@ -411,20 +466,19 @@ void CWallpaperApplication::show () {
usleep (FULLSCREEN_CHECK_WAIT_TIME);
m_renderContext->setPause (false);
}
// process driver events
m_videoDriver->dispatchEventQueue ();
if (!this->m_context.settings.screenshot.take || m_videoDriver->getFrameCounter () < 5)
if (!this->m_context.settings.screenshot.take || m_videoDriver->getFrameCounter () < this->m_context.settings.screenshot.delay)
continue;
this->takeScreenshot (this->m_context.settings.screenshot.path);
this->m_context.settings.screenshot.take = false;
}
// ensure this is updated as sometimes it might not come from a signal
this->m_context.state.general.keepRunning = false;
sLog.out ("Stopping");
sLog.out ("Stop requested");
#if DEMOMODE
close_encoder ();
#endif /* DEMOMODE */
SDL_Quit ();
}
@ -435,10 +489,11 @@ void CWallpaperApplication::update (Render::Drivers::Output::COutputViewport* vi
}
void CWallpaperApplication::signal (int signal) {
sLog.out ("Stop requested by signal ", signal);
this->m_context.state.general.keepRunning = false;
}
const std::map<std::string, Core::CProject*>& CWallpaperApplication::getBackgrounds () const {
const std::map<std::string, std::shared_ptr<Core::CProject>>& CWallpaperApplication::getBackgrounds () const {
return this->m_backgrounds;
}

View File

@ -10,24 +10,7 @@
#include "WallpaperEngine/Render/CWallpaper.h"
#include "WallpaperEngine/Render/Drivers/CGLFWOpenGLDriver.h"
#include "WallpaperEngine/Render/Drivers/Detectors/CFullScreenDetector.h"
#ifdef ENABLE_WAYLAND
#include "WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.h"
#endif
#ifdef ENABLE_X11
#include "WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h"
#endif
#ifdef ENABLE_WAYLAND
#include "WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h"
#endif
#include "WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.h"
#ifdef ENABLE_X11
#include "WallpaperEngine/Render/Drivers/Output/CX11Output.h"
#endif
#ifdef ENABLE_WAYLAND
#include "WallpaperEngine/Render/Drivers/Output/CWaylandOutput.h"
#endif
#include "WallpaperEngine/Audio/Drivers/CSDLAudioDriver.h"
@ -42,8 +25,7 @@ namespace WallpaperEngine::Application {
*/
class CWallpaperApplication {
public:
explicit CWallpaperApplication (CApplicationContext& context, WallpaperEngine::WebBrowser::CWebBrowserContext& browserContext);
~CWallpaperApplication ();
explicit CWallpaperApplication (CApplicationContext& context);
/**
* Shows the application until it's closed
@ -58,7 +40,7 @@ class CWallpaperApplication {
/**
* @return Maps screens to loaded backgrounds
*/
[[nodiscard]] const std::map<std::string, Core::CProject*>& getBackgrounds () const;
[[nodiscard]] const std::map<std::string, std::shared_ptr<Core::CProject>>& getBackgrounds () const;
/**
* @return The current application context
*/
@ -79,7 +61,7 @@ class CWallpaperApplication {
* @param container
* @param bg
*/
void setupContainer (CCombinedContainer& container, const std::string& bg) const;
void setupContainer (const std::shared_ptr<CCombinedContainer>& container, const std::string& bg) const;
/**
* Loads projects based off the settings
*/
@ -90,7 +72,7 @@ class CWallpaperApplication {
* @param bg
* @return
*/
Core::CProject* loadBackground (const std::string& bg);
[[nodiscard]] std::shared_ptr<Core::CProject> loadBackground (const std::string& bg);
/**
* Prepares all background's values and updates their properties if required
*/
@ -100,26 +82,42 @@ class CWallpaperApplication {
*
* @param project
*/
void setupPropertiesForProject (const Core::CProject* project);
void setupPropertiesForProject (const std::shared_ptr<const Core::CProject>& project);
/**
* Prepares CEF browser to be used
*/
void setupBrowser ();
/**
* Prepares desktop environment-related things (like render, window, fullscreen detector, etc)
*/
void setupOutput ();
/**
* Prepares all audio-related things (like detector, output, etc)
*/
void setupAudio ();
/**
* Prepares the render-context of all the backgrounds so they can be displayed on the screen
*/
void prepareOutputs ();
/**
* Takes an screenshot of the background and saves it to the specified path
*
* @param filename
*/
void takeScreenshot (const std::filesystem::path& filename);
void takeScreenshot (const std::filesystem::path& filename) const;
/** The application context that contains the current app settings */
CApplicationContext& m_context;
/** Maps screens to backgrounds */
std::map<std::string, Core::CProject*> m_backgrounds;
std::map<std::string, std::shared_ptr <Core::CProject>> m_backgrounds {};
WallpaperEngine::Audio::CAudioContext* m_audioContext;
WallpaperEngine::Audio::Drivers::CSDLAudioDriver* m_audioDriver;
WallpaperEngine::Audio::Drivers::Recorders::CPlaybackRecorder* m_audioRecorder;
WallpaperEngine::Input::CInputContext* m_inputContext;
WallpaperEngine::Render::CRenderContext* m_renderContext;
WallpaperEngine::Render::Drivers::CVideoDriver* m_videoDriver;
WallpaperEngine::Render::Drivers::Detectors::CFullScreenDetector* m_fullScreenDetector;
WallpaperEngine::WebBrowser::CWebBrowserContext& m_browserContext;
std::unique_ptr <WallpaperEngine::Audio::Drivers::Detectors::CAudioPlayingDetector> m_audioDetector = nullptr;
std::unique_ptr <WallpaperEngine::Audio::CAudioContext> m_audioContext = nullptr;
std::unique_ptr <WallpaperEngine::Audio::Drivers::CSDLAudioDriver> m_audioDriver = nullptr;
std::unique_ptr <WallpaperEngine::Audio::Drivers::Recorders::CPlaybackRecorder> m_audioRecorder = nullptr;
std::unique_ptr <WallpaperEngine::Render::CRenderContext> m_renderContext = nullptr;
std::unique_ptr <WallpaperEngine::Render::Drivers::CVideoDriver> m_videoDriver = nullptr;
std::unique_ptr <WallpaperEngine::Render::Drivers::Detectors::CFullScreenDetector> m_fullScreenDetector = nullptr;
std::unique_ptr <WallpaperEngine::WebBrowser::CWebBrowserContext> m_browserContext = nullptr;
};
} // namespace WallpaperEngine::Application

View File

@ -10,6 +10,6 @@ class CAssetLoadException final : public std::exception {
[[nodiscard]] const char* what () const noexcept override;
private:
std::string m_message;
std::string m_message {};
};
} // namespace WallpaperEngine::Assets

View File

@ -8,14 +8,14 @@ using namespace WallpaperEngine::Assets;
CCombinedContainer::CCombinedContainer () : CContainer () {}
void CCombinedContainer::add (CContainer* container) {
void CCombinedContainer::add (const std::shared_ptr<CContainer>& container) {
this->m_containers.emplace_back (container);
}
void CCombinedContainer::addPkg (const std::filesystem::path& path) {
try {
// add the package to the list
this->add (new CPackage (path));
this->add (std::make_shared<CPackage> (path));
sLog.out ("Detected ", path.filename (), " file at ", path, ". Adding to list of searchable paths");
} catch (CPackageLoadException&) {
// ignore this error, the package file was not found
@ -26,8 +26,8 @@ void CCombinedContainer::addPkg (const std::filesystem::path& path) {
}
}
std::filesystem::path CCombinedContainer::resolveRealFile (const std::string& filename) const {
for (const auto cur : this->m_containers) {
std::filesystem::path CCombinedContainer::resolveRealFile (const std::filesystem::path& filename) const {
for (const auto& cur : this->m_containers) {
try {
// try to read the file on the current container, if the file doesn't exists
// an exception will be thrown
@ -41,13 +41,13 @@ std::filesystem::path CCombinedContainer::resolveRealFile (const std::string& fi
throw CAssetLoadException (filename, "Cannot resolve file in any of the containers");
}
const uint8_t* CCombinedContainer::readFile (const std::string& filename, uint32_t* length) const {
for (const auto cur : this->m_containers) {
std::shared_ptr<const uint8_t[]> CCombinedContainer::readFile (const std::filesystem::path& filename, uint32_t* length) const {
for (const auto& cur : this->m_containers) {
try {
// try to read the file on the current container, if the file doesn't exists
// an exception will be thrown
return cur->readFile (filename, length);
} catch (CAssetLoadException&) {
} catch (CAssetLoadException& e) {
// not found in this container, next try
}
}

View File

@ -19,7 +19,7 @@ class CCombinedContainer final : public CContainer {
*
* @param container
*/
void add (CContainer* container);
void add (const std::shared_ptr<CContainer>& container);
/**
* Adds the given package to the list
*
@ -27,13 +27,11 @@ class CCombinedContainer final : public CContainer {
*/
void addPkg (const std::filesystem::path& path);
/** @inheritdoc */
[[nodiscard]] std::filesystem::path resolveRealFile (const std::string& filename) const override;
/** @inheritdoc */
[[nodiscard]] const uint8_t* readFile (const std::string& filename, uint32_t* length) const override;
[[nodiscard]] std::filesystem::path resolveRealFile (const std::filesystem::path& filename) const override;
[[nodiscard]] std::shared_ptr<const uint8_t[]> readFile (const std::filesystem::path& filename, uint32_t* length) const override;
private:
/** The list of containers to search files off from */
std::vector<CContainer*> m_containers;
std::vector<std::shared_ptr<CContainer>> m_containers {};
};
}; // namespace WallpaperEngine::Assets

View File

@ -9,26 +9,25 @@
using namespace WallpaperEngine::Assets;
std::filesystem::path CContainer::resolveRealFile (const std::string& filename) const {
std::filesystem::path CContainer::resolveRealFile (const std::filesystem::path& filename) const {
throw CAssetLoadException (filename, "Cannot resolve physical file in this container");
}
const ITexture* CContainer::readTexture (const std::string& filename) const {
std::shared_ptr <const ITexture> CContainer::readTexture (const std::filesystem::path& filename) const {
// get the texture's filename (usually .tex)
const std::string texture = "materials/" + filename + ".tex";
std::filesystem::path texture = "materials" / std::filesystem::path (filename.string ().append (".tex"));
const void* textureContents = this->readFile (texture, nullptr);
const ITexture* result = new CTexture (textureContents);
const auto textureContents = this->readFile (texture, nullptr);
const auto result = std::make_shared<CTexture> (textureContents);
#if !NDEBUG
glObjectLabel (GL_TEXTURE, result->getTextureID (), -1, texture.c_str ());
glObjectLabel (GL_TEXTURE, result->getTextureID (0), -1, texture.c_str ());
#endif /* NDEBUG */
return result;
}
std::string CContainer::readShader (const std::string& filename) const {
std::string CContainer::readShader (const std::filesystem::path& filename) const {
std::filesystem::path shader = filename;
auto it = shader.begin ();
@ -37,7 +36,7 @@ std::string CContainer::readShader (const std::string& filename) const {
const std::filesystem::path workshopId = *it++;
if (++it != shader.end ()) {
const std::filesystem::path shaderfile = *it;
const std::filesystem::path& shaderfile = *it;
try {
shader = std::filesystem::path ("zcompat") / "scene" / "shaders" / workshopId / shaderfile;
@ -51,37 +50,29 @@ std::string CContainer::readShader (const std::string& filename) const {
}
}
return this->readFileAsString ("shaders/" + filename);
return this->readFileAsString ("shaders" / filename);
}
std::string CContainer::readVertexShader (const std::string& filename) const {
return this->readShader (filename + ".vert");
std::string CContainer::readVertexShader (const std::filesystem::path& filename) const {
std::filesystem::path shader = filename;
shader.replace_extension (".vert");
return this->readShader (shader);
}
std::string CContainer::readFragmentShader (const std::string& filename) const {
return this->readShader (filename + ".frag");
std::string CContainer::readFragmentShader (const std::filesystem::path& filename) const {
std::filesystem::path shader = filename;
shader.replace_extension (".frag");
return this->readShader (shader);
}
std::string CContainer::readIncludeShader (const std::string& filename) const {
return this->readFileAsString ("shaders/" + filename);
std::string CContainer::readIncludeShader (const std::filesystem::path& filename) const {
return this->readFileAsString ("shaders" / filename);
}
std::string CContainer::readFileAsString (const std::string& filename) const {
std::string CContainer::readFileAsString (const std::filesystem::path& filename) const {
uint32_t length = 0;
// read file contents and allocate a buffer for a string
const uint8_t* contents = this->readFile (filename, &length);
char* buffer = new char [length + 1];
// ensure there's a 0 at the end
memset (buffer, 0, length + 1);
// copy over the data
memcpy (buffer, contents, length);
// now build the std::string to use
std::string result = buffer;
// free the intermediate buffer used to generate the std::string
delete [] buffer;
return result;
return {
reinterpret_cast<const char*> (this->readFile (filename, &length).get ()), length
};
}

View File

@ -19,18 +19,20 @@ class CContainer {
* @param filename
* @return
*/
[[nodiscard]] virtual std::filesystem::path resolveRealFile (const std::string& filename) const;
[[nodiscard]] virtual std::filesystem::path resolveRealFile (const std::filesystem::path& filename) const;
/**
* Reads the given file from the container and returns it's data
* Additionally sets a length parameter to return back the file's length
*
* The returned string must be deleted[] by the caller
*
* @param filename The file to read
* @param length The file's length after it's been read, null for not getting anything back
*
* @return
*/
[[nodiscard]] virtual const uint8_t* readFile (const std::string& filename, uint32_t* length) const = 0;
[[nodiscard]] virtual std::shared_ptr<const uint8_t[]> readFile (const std::filesystem::path& filename, uint32_t* length) const = 0;
/**
* Wrapper for readFile, appends the texture extension at the end of the filename
@ -39,7 +41,7 @@ class CContainer {
*
* @return
*/
[[nodiscard]] const ITexture* readTexture (const std::string& filename) const;
[[nodiscard]] std::shared_ptr <const ITexture> readTexture (const std::filesystem::path& filename) const;
/**
* Wrapper for readFile, checks for compat versions of the given shader file
@ -48,7 +50,7 @@ class CContainer {
*
* @return The shader code as an string to be used
*/
[[nodiscard]] std::string readShader (const std::string& filename) const;
[[nodiscard]] std::string readShader (const std::filesystem::path& filename) const;
/**
* Wrapper for readFile, appends the .vert extension at the end and opens the given shader file
@ -57,7 +59,7 @@ class CContainer {
*
* @return The shader code as an string to be used
*/
[[nodiscard]] std::string readVertexShader (const std::string& filename) const;
[[nodiscard]] std::string readVertexShader (const std::filesystem::path& filename) const;
/**
* Wrapper for readFile, appends the .frag extension at the end and opens the given shader file
@ -66,7 +68,7 @@ class CContainer {
*
* @return The shader code as an string to be used
*/
[[nodiscard]] std::string readFragmentShader (const std::string& filename) const;
[[nodiscard]] std::string readFragmentShader (const std::filesystem::path& filename) const;
/**
* Wrapper for readFile, appends the .h extension at the end and opens the given shader file
@ -75,7 +77,7 @@ class CContainer {
*
* @return The shader code as an string to be used
*/
[[nodiscard]] std::string readIncludeShader (const std::string& filename) const;
[[nodiscard]] std::string readIncludeShader (const std::filesystem::path& filename) const;
/**
* Reads a file as string
@ -84,6 +86,6 @@ class CContainer {
*
* @return The file's contents as string
*/
[[nodiscard]] std::string readFileAsString (const std::string& filename) const;
[[nodiscard]] std::string readFileAsString (const std::filesystem::path& filename) const;
};
} // namespace WallpaperEngine::Assets

View File

@ -1,61 +1,85 @@
#include "common.h"
#include <sys/stat.h>
#include <utility>
#include "CAssetLoadException.h"
#include "CDirectory.h"
using namespace WallpaperEngine::Assets;
CDirectory::CDirectory (std::filesystem::path basepath) : m_basepath (std::move (basepath)) {
// ensure the specified path exists
struct stat buffer {};
CDirectory::CDirectory (const std::filesystem::path& basepath) {
try {
// resolve the path to it's real location
std::filesystem::path finalpath = std::filesystem::canonical(basepath);
std::filesystem::file_status status = std::filesystem::status (finalpath);
if (stat (this->m_basepath.c_str (), &buffer) != 0)
throw CAssetLoadException (this->m_basepath, "Cannot find directory");
if (!S_ISDIR (buffer.st_mode))
throw CAssetLoadException (this->m_basepath, "Expected directory but found a file");
}
std::filesystem::path CDirectory::resolveRealFile (const std::string& filename) const {
return std::filesystem::path (this->m_basepath) / filename;
}
const uint8_t* CDirectory::readFile (const std::string& filename, uint32_t* length) const {
const std::filesystem::path final = std::filesystem::path (this->m_basepath) / filename;
// first check the cache, if the file is there already just return the data in there
const auto it = this->m_cache.find (final);
if (it != this->m_cache.end ()) {
if (length != nullptr)
*length = it->second.length;
return it->second.address;
if (!std::filesystem::exists (finalpath)) {
throw CAssetLoadException (basepath, "Cannot find directory");
}
if (!std::filesystem::is_directory(status)) {
throw CAssetLoadException (basepath, "Expected directory but found a file");
}
this->m_basepath = finalpath;
} catch (std::bad_alloc&) {
throw CAssetLoadException (basepath, "Cannot allocate memory");
} catch (std::filesystem::filesystem_error& e) {
throw CAssetLoadException (basepath, e.what ());
}
}
std::filesystem::path CDirectory::resolveRealFile (const std::filesystem::path& filename) const {
try {
std::filesystem::path final = std::filesystem::canonical (this->m_basepath / filename);
// first validate the path, so the message doesn't reflect if the file exists or not unless it's under the actual directory
if (final.string ().find (this->m_basepath.string ()) != 0) {
throw CAssetLoadException (filename, "File is not a child of the given directory");
}
std::filesystem::file_status status = std::filesystem::status (final);
if (!std::filesystem::exists (final)) {
throw CAssetLoadException (filename, "Cannot find file");
}
if (!std::filesystem::is_regular_file (status)) {
throw CAssetLoadException (filename, "Expected file but found a directory");
}
return final;
} catch (std::filesystem::filesystem_error& e) {
throw CAssetLoadException (filename, e.what ());
}
}
std::shared_ptr<const uint8_t[]> CDirectory::readFile (const std::filesystem::path& filename, uint32_t* length) const {
std::filesystem::path final = this->resolveRealFile (filename);
FILE* fp = fopen (final.c_str (), "rb");
if (fp == nullptr)
throw CAssetLoadException (filename, "Cannot find file");
if (fp == nullptr) {
throw CAssetLoadException (filename, "Cannot open file for reading");
}
try {
// go to the end, get the position and return to the beginning
fseek (fp, 0, SEEK_END);
const long size = ftell (fp);
fseek (fp, 0, SEEK_SET);
// now read the whole file
auto* contents = new uint8_t [size];
std::shared_ptr<uint8_t[]> contents = std::shared_ptr<uint8_t[]>(new uint8_t [size]);
if (fread (contents, size, 1, fp) != 1) {
delete [] contents;
if (fread (contents.get(), size, 1, fp) != 1) {
throw CAssetLoadException (filename, "Unexpected error when reading the file");
}
if (length != nullptr)
if (length != nullptr) {
*length = size;
}
fclose (fp);
return contents;
} catch (std::filesystem::filesystem_error& e) {
fclose (fp);
throw CAssetLoadException (filename, e.what ());
}
}

View File

@ -14,17 +14,13 @@ namespace WallpaperEngine::Assets {
*/
class CDirectory final : public CContainer {
public:
explicit CDirectory (std::filesystem::path basepath);
explicit CDirectory (const std::filesystem::path& basepath);
/** @inheritdoc */
[[nodiscard]] std::filesystem::path resolveRealFile (const std::string& filename) const override;
/** @inheritdoc */
[[nodiscard]] const uint8_t* readFile (const std::string& filename, uint32_t* length) const override;
[[nodiscard]] std::filesystem::path resolveRealFile (const std::filesystem::path& filename) const override;
[[nodiscard]] std::shared_ptr<const uint8_t[]> readFile (const std::filesystem::path& filename, uint32_t* length) const override;
private:
/** The basepath for the directory */
std::filesystem::path m_basepath;
/** File cache to simplify access to data */
std::map<std::string, CFileEntry> m_cache;
std::filesystem::path m_basepath {};
};
} // namespace WallpaperEngine::Assets

View File

@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <utility>
namespace WallpaperEngine::Assets {
/**
@ -8,15 +9,14 @@ namespace WallpaperEngine::Assets {
*/
class CFileEntry {
public:
CFileEntry (const uint8_t* address, uint32_t length) : address (address), length (length) {}
~CFileEntry () {
delete [] address;
}
CFileEntry (std::shared_ptr<const uint8_t[]> content, uint32_t length) :
content (std::move(content)),
length (length) {}
~CFileEntry() = default;
/** File contents */
const uint8_t* address;
std::shared_ptr<const uint8_t[]> content = nullptr;
/** File length */
uint32_t length;
uint32_t length = 0;
};
} // namespace WallpaperEngine::Assets

View File

@ -1,7 +1,7 @@
#include "CPackage.h"
#include "CAssetLoadException.h"
#include "CPackageLoadException.h"
#include "common.h"
#include "WallpaperEngine/Logging/CLog.h"
#include <sstream>
#include <utility>
@ -11,7 +11,7 @@ using namespace WallpaperEngine::Assets;
class CPackageEntry {
public:
CPackageEntry (std::string filename, uint32_t offset, uint32_t length) :
filename (std::move (filename)),
filename (filename),
offset (offset),
length (length) {}
@ -24,7 +24,7 @@ CPackage::CPackage (std::filesystem::path path) : m_path (std::move (path)) {
this->init ();
}
const uint8_t* CPackage::readFile (const std::string& filename, uint32_t* length) const {
std::shared_ptr<const uint8_t[]> CPackage::readFile (const std::filesystem::path& filename, uint32_t* length) const {
const auto it = this->m_contents.find (filename);
if (it == this->m_contents.end ())
@ -34,12 +34,7 @@ const uint8_t* CPackage::readFile (const std::string& filename, uint32_t* length
if (length != nullptr)
*length = it->second->length;
// clone original first
auto* result = new uint8_t [it->second->length];
memcpy (result, it->second->address, it->second->length);
return result;
return it->second->content;
}
void CPackage::init () {
@ -128,15 +123,13 @@ void CPackage::loadFiles (FILE* fp) {
sLog.exception ("Cannot find file ", cur.filename, " from package ", this->m_path);
// allocate memory for the file's contents and read it from the file
auto* fileContents = new uint8_t [cur.length];
if (fread (fileContents, cur.length, 1, fp) != 1) {
delete [] fileContents;
std::shared_ptr<uint8_t[]> contents = std::shared_ptr<uint8_t[]>(new uint8_t [cur.length]);
if (fread (contents.get(), cur.length, 1, fp) != 1) {
sLog.exception ("Cannot read file ", cur.filename, " contents from package ", this->m_path);
}
// add the file to the map
this->m_contents.insert_or_assign (cur.filename, new CFileEntry (fileContents, cur.length));
this->m_contents.insert_or_assign (cur.filename, std::make_unique<CFileEntry> (contents, cur.length));
}
}

View File

@ -20,7 +20,7 @@ class CPackage final : public CContainer {
public:
explicit CPackage (std::filesystem::path path);
[[nodiscard]] const uint8_t* readFile (const std::string& filename, uint32_t* length) const override;
[[nodiscard]] std::shared_ptr<const uint8_t[]> readFile (const std::filesystem::path& filename, uint32_t* length) const override;
protected:
/**
@ -61,8 +61,8 @@ class CPackage final : public CContainer {
private:
/** The path to the package file */
std::filesystem::path m_path;
std::filesystem::path m_path {};
/** Contents of the package file */
std::map<std::string, CFileEntry*> m_contents;
std::map<std::string, std::unique_ptr<CFileEntry>> m_contents {};
};
} // namespace WallpaperEngine::Assets

View File

@ -10,6 +10,6 @@ class CPackageLoadException final : public std::exception {
[[nodiscard]] const char* what () const noexcept override;
private:
std::string m_message;
std::string m_message {};
};
} // namespace WallpaperEngine::Assets

View File

@ -1,5 +1,5 @@
#include "CTexture.h"
#include "common.h"
#include "WallpaperEngine/Logging/CLog.h"
#include <cstring>
#include <lz4.h>
@ -10,51 +10,12 @@
using namespace WallpaperEngine::Assets;
CTexture::CTexture (const void* fileData) : m_resolution () {
CTexture::CTexture (const std::shared_ptr<const uint8_t[]>& buffer) : m_resolution () {
// ensure the header is parsed
const void* fileData = buffer.get ();
this->m_header = parseHeader (static_cast<const char*> (fileData));
GLint internalFormat;
if (this->isAnimated ()) {
this->m_resolution = {this->m_header->textureWidth, this->m_header->textureHeight, this->m_header->gifWidth,
this->m_header->gifHeight};
} else {
if (this->m_header->freeImageFormat != FreeImageFormat::FIF_UNKNOWN) {
// wpengine-texture format always has one mipmap
// get first image size
auto element = this->m_header->images.find (0)->second.begin ();
// set the texture resolution
this->m_resolution = {(*element)->width, (*element)->height, this->m_header->width, this->m_header->height};
} else {
// set the texture resolution
this->m_resolution = {this->m_header->textureWidth, this->m_header->textureHeight, this->m_header->width,
this->m_header->height};
}
}
if (this->m_header->freeImageFormat != FreeImageFormat::FIF_UNKNOWN) {
internalFormat = GL_RGBA8;
// set some extra information too as it's used for image sizing
// this ensures that a_TexCoord uses the full image instead of just part of it
// TODO: MAYBE IT'S BETTER TO CREATE A TEXTURE OF THE GIVEN SIZE AND COPY OVER WHAT WE READ FROM THE FILE?
/*this->m_header->width = this->m_header->mipmaps [0]->width;
this->m_header->height = this->m_header->mipmaps [0]->height;
this->m_header->textureWidth = this->m_header->mipmaps [0]->width;
this->m_header->textureHeight = this->m_header->mipmaps [0]->height;*/
} else {
// detect the image format and hand it to openGL to be used
switch (this->m_header->format) {
case TextureFormat::DXT5: internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
case TextureFormat::DXT3: internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break;
case TextureFormat::DXT1: internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break;
case TextureFormat::ARGB8888: internalFormat = GL_RGBA8; break;
case TextureFormat::R8: internalFormat = GL_R8; break;
case TextureFormat::RG88: internalFormat = GL_RG8; break;
default: delete this->m_header; sLog.exception ("Cannot determine texture format");
}
}
this->setupResolution ();
GLint internalFormat = this->setupInternalFormat();
// allocate texture ids list
this->m_textureID = new GLuint [this->m_header->imageCount];
@ -65,38 +26,14 @@ CTexture::CTexture (const void* fileData) : m_resolution () {
auto imgEnd = this->m_header->images.end ();
for (int index = 0; imgCur != imgEnd; ++imgCur, index++) {
// bind the texture to assign information to it
glBindTexture (GL_TEXTURE_2D, this->m_textureID [index]);
// set mipmap levels
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, this->m_header->mipmapCount - 1);
// setup texture wrapping and filtering
if (this->m_header->flags & TextureFlags::ClampUVs) {
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} else {
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
if (this->m_header->flags & TextureFlags::NoInterpolation) {
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
} else {
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 8.0f);
this->setupOpenGLParameters (index);
auto cur = imgCur->second.begin ();
auto end = imgCur->second.end ();
for (int32_t level = 0; cur != end; ++cur, level++) {
stbi_uc* handle = nullptr;
void* dataptr = (*cur)->uncompressedData;
void* dataptr = (*cur)->uncompressedData.get ();
int width = (*cur)->width;
int height = (*cur)->height;
uint32_t bufferSize = (*cur)->uncompressedSize;
@ -106,7 +43,7 @@ CTexture::CTexture (const void* fileData) : m_resolution () {
int fileChannels;
dataptr = handle = stbi_load_from_memory (
reinterpret_cast <unsigned char*> ((*cur)->uncompressedData),
reinterpret_cast <unsigned char*> ((*cur)->uncompressedData.get ()),
(*cur)->uncompressedSize,
&width,
&height,
@ -126,13 +63,15 @@ CTexture::CTexture (const void* fileData) : m_resolution () {
case GL_RGBA8:
case GL_RG8:
case GL_R8:
glTexImage2D (GL_TEXTURE_2D, level, internalFormat, width, height, 0, textureFormat,
glTexImage2D (
GL_TEXTURE_2D, level, internalFormat, width, height, 0, textureFormat,
GL_UNSIGNED_BYTE, dataptr);
break;
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
glCompressedTexImage2D (GL_TEXTURE_2D, level, internalFormat, width, height, 0, bufferSize,
glCompressedTexImage2D (
GL_TEXTURE_2D, level, internalFormat, width, height, 0, bufferSize,
dataptr);
break;
default: sLog.exception ("Cannot load texture, unknown format", this->m_header->format);
@ -146,85 +85,141 @@ CTexture::CTexture (const void* fileData) : m_resolution () {
}
}
CTexture::~CTexture () {
if (this->getHeader () == nullptr)
return;
void CTexture::setupResolution () {
if (this->isAnimated ()) {
this->m_resolution = {this->m_header->textureWidth, this->m_header->textureHeight, this->m_header->gifWidth,
this->m_header->gifHeight};
} else {
if (this->m_header->freeImageFormat != FreeImageFormat::FIF_UNKNOWN) {
// wpengine-texture format always has one mipmap
// get first image size
auto element = this->m_header->images.find (0)->second.begin ();
// free the header if it was allocated
delete this->getHeader ();
// set the texture resolution
this->m_resolution = {(*element)->width, (*element)->height, this->m_header->width, this->m_header->height};
} else {
// set the texture resolution
this->m_resolution = {this->m_header->textureWidth, this->m_header->textureHeight, this->m_header->width,
this->m_header->height};
}
}
}
const GLuint CTexture::getTextureID (uint32_t imageIndex) const {
GLint CTexture::setupInternalFormat () {
if (this->m_header->freeImageFormat != FreeImageFormat::FIF_UNKNOWN) {
return GL_RGBA8;
// set some extra information too as it's used for image sizing
// this ensures that a_TexCoord uses the full image instead of just part of it
// TODO: MAYBE IT'S BETTER TO CREATE A TEXTURE OF THE GIVEN SIZE AND COPY OVER WHAT WE READ FROM THE FILE?
/*this->m_header->width = this->m_header->mipmaps [0]->width;
this->m_header->height = this->m_header->mipmaps [0]->height;
this->m_header->textureWidth = this->m_header->mipmaps [0]->width;
this->m_header->textureHeight = this->m_header->mipmaps [0]->height;*/
} else {
// detect the image format and hand it to openGL to be used
switch (this->m_header->format) {
case TextureFormat::DXT5: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
case TextureFormat::DXT3: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break;
case TextureFormat::DXT1: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break;
case TextureFormat::ARGB8888: return GL_RGBA8; break;
case TextureFormat::R8: return GL_R8; break;
case TextureFormat::RG88: return GL_RG8; break;
default: sLog.exception ("Cannot determine texture format");
}
}
}
void CTexture::setupOpenGLParameters (uint32_t textureID) {
// bind the texture to assign information to it
glBindTexture (GL_TEXTURE_2D, this->m_textureID [textureID]);
// set mipmap levels
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, this->m_header->images [textureID].size () - 1);
// setup texture wrapping and filtering
if (this->m_header->flags & TextureFlags::ClampUVs) {
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} else {
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
if (this->m_header->flags & TextureFlags::NoInterpolation) {
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
} else {
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 8.0f);
}
GLuint CTexture::getTextureID (uint32_t imageIndex) const {
// ensure we do not go out of bounds
if (imageIndex > this->m_header->imageCount)
if (imageIndex >= this->m_header->imageCount)
return this->m_textureID [0];
return this->m_textureID [imageIndex];
}
const uint32_t CTexture::getTextureWidth (uint32_t imageIndex) const {
if (imageIndex > this->m_header->imageCount)
uint32_t CTexture::getTextureWidth (uint32_t imageIndex) const {
if (imageIndex >= this->m_header->imageCount)
return this->getHeader ()->textureWidth;
return (*this->m_header->images [imageIndex].begin ())->width;
}
const uint32_t CTexture::getTextureHeight (uint32_t imageIndex) const {
if (imageIndex > this->m_header->imageCount)
uint32_t CTexture::getTextureHeight (uint32_t imageIndex) const {
if (imageIndex >= this->m_header->imageCount)
return this->getHeader ()->textureHeight;
return (*this->m_header->images [imageIndex].begin ())->height;
}
const uint32_t CTexture::getRealWidth () const {
uint32_t CTexture::getRealWidth () const {
return this->isAnimated () ? this->getHeader ()->gifWidth : this->getHeader ()->width;
}
const uint32_t CTexture::getRealHeight () const {
uint32_t CTexture::getRealHeight () const {
return this->isAnimated () ? this->getHeader ()->gifHeight : this->getHeader ()->height;
}
const ITexture::TextureFormat CTexture::getFormat () const {
ITexture::TextureFormat CTexture::getFormat () const {
return this->getHeader ()->format;
}
const ITexture::TextureFlags CTexture::getFlags () const {
ITexture::TextureFlags CTexture::getFlags () const {
return this->getHeader ()->flags;
}
const CTexture::TextureHeader* CTexture::getHeader () const {
return this->m_header;
return this->m_header.get ();
}
const glm::vec4* CTexture::getResolution () const {
return &this->m_resolution;
}
const std::vector<ITexture::TextureFrame*>& CTexture::getFrames () const {
const std::vector<std::shared_ptr<ITexture::TextureFrame>>& CTexture::getFrames () const {
return this->getHeader ()->frames;
}
const bool CTexture::isAnimated () const {
bool CTexture::isAnimated () const {
return this->getHeader ()->isAnimated ();
}
CTexture::TextureMipmap::TextureMipmap () = default;
CTexture::TextureMipmap::~TextureMipmap () {
if (this->compression == 1)
delete this->compressedData;
delete this->uncompressedData;
}
void CTexture::TextureMipmap::decompressData () {
if (this->compression != 1) {
return;
}
this->uncompressedData = new char [this->uncompressedSize];
this->uncompressedData = std::unique_ptr <char[]> (new char [this->uncompressedSize]);
const int result = LZ4_decompress_safe (this->compressedData, this->uncompressedData, this->compressedSize,
const int result = LZ4_decompress_safe (
this->compressedData.get (), this->uncompressedData.get (), this->compressedSize,
this->uncompressedSize);
if (!result)
@ -251,15 +246,9 @@ CTexture::TextureHeader::TextureHeader () :
gifHeight (0),
format (TextureFormat::UNKNOWN),
imageCount (0),
mipmapCount (0) {}
isVideoMp4 (false) {}
CTexture::TextureHeader::~TextureHeader () {
for (const auto& [index, mipmaps] : this->images)
for (const auto cur : mipmaps)
delete cur;
}
CTexture::TextureHeader* CTexture::parseHeader (const char* fileData) {
std::unique_ptr<CTexture::TextureHeader> CTexture::parseHeader (const char* fileData) {
// check the magic value on the header first
if (strncmp (fileData, "TEXV0005", 9) != 0)
sLog.exception ("unexpected texture container type: ", std::string_view (fileData, 9));
@ -271,8 +260,7 @@ CTexture::TextureHeader* CTexture::parseHeader (const char* fileData) {
// jump through the string again
fileData += 9;
auto* header = new TextureHeader;
auto header = std::make_unique <TextureHeader> ();
const auto* pointer = reinterpret_cast<const uint32_t*> (fileData);
header->format = static_cast<TextureFormat> (*pointer++);
@ -289,45 +277,55 @@ CTexture::TextureHeader* CTexture::parseHeader (const char* fileData) {
// get the position of what comes after the texture data
pointer = reinterpret_cast<const uint32_t*> (fileData + 9);
if (strncmp (fileData, "TEXB0003", 9) == 0) {
header->containerVersion = ContainerVersion::TEXB0003;
header->imageCount = *pointer++;
if (strncmp (fileData, "TEXB0004", 9) == 0) {
header->containerVersion = ContainerVersion::TEXB0004;
header->freeImageFormat = static_cast<FreeImageFormat> (*pointer++);
header->isVideoMp4 = *pointer++ == 1;
if (header->freeImageFormat == FIF_UNKNOWN) {
header->freeImageFormat = FIF_MP4;
}
// default to TEXB0003 behavior if no mp4 video is there
if (header->freeImageFormat != FIF_MP4) {
header->containerVersion = ContainerVersion::TEXB0003;
}
} else if (strncmp (fileData, "TEXB0003", 9) == 0) {
header->containerVersion = ContainerVersion::TEXB0003;
header->freeImageFormat = static_cast<FreeImageFormat> (*pointer++);
} else if (strncmp (fileData, "TEXB0002", 9) == 0) {
header->containerVersion = ContainerVersion::TEXB0002;
header->imageCount = *pointer++;
} else if (strncmp (fileData, "TEXB0001", 9) == 0) {
header->containerVersion = ContainerVersion::TEXB0001;
header->imageCount = *pointer++;
} else {
delete header;
sLog.exception ("unknown texture format type: ", std::string_view (fileData, 9));
}
for (uint32_t image = 0; image < header->imageCount; image++) {
// read the number of mipmaps available for this image
header->mipmapCount = *pointer++;
std::vector<TextureMipmap*> mipmaps;
uint32_t mipmapCount = *pointer++;
std::vector<std::shared_ptr<TextureMipmap>> mipmaps;
fileData = reinterpret_cast<const char*> (pointer);
for (uint32_t i = 0; i < header->mipmapCount; i++)
mipmaps.emplace_back (parseMipmap (header, &fileData));
for (uint32_t i = 0; i < mipmapCount; i++)
mipmaps.emplace_back (parseMipmap (header.get (), &fileData));
// add the pixmaps back
header->images.insert (std::pair (image, mipmaps));
header->images.emplace (image, mipmaps);
pointer = reinterpret_cast<const uint32_t*> (fileData);
}
// gifs have extra information after the mipmaps
if (header->isAnimated () == true) {
if (header->isAnimated ()) {
if (strncmp (fileData, "TEXS0002", 9) == 0) {
header->animatedVersion = AnimatedVersion::TEXS0002;
} else if (strncmp (fileData, "TEXS0003", 9) == 0) {
header->animatedVersion = AnimatedVersion::TEXS0003;
} else {
delete header;
sLog.exception ("found animation information of unknown type: ", std::string_view (fileData, 9));
}
@ -353,7 +351,7 @@ CTexture::TextureHeader* CTexture::parseHeader (const char* fileData) {
// ensure gif width and height is right for TEXS0002
if (header->animatedVersion == AnimatedVersion::TEXS0002) {
const TextureFrame* first = *header->frames.begin ();
auto first = *header->frames.begin ();
header->gifWidth = first->width1;
header->gifHeight = first->height1;
@ -363,13 +361,13 @@ CTexture::TextureHeader* CTexture::parseHeader (const char* fileData) {
return header;
}
CTexture::TextureFrame* CTexture::parseAnimation (const char** originalFileData) {
std::shared_ptr<CTexture::TextureFrame> CTexture::parseAnimation (const char** originalFileData) {
const char* fileData = *originalFileData;
// get back the pointer into integer
const auto* pointer = reinterpret_cast<const uint32_t*> (fileData);
// start reading frame information
auto* frame = new TextureFrame ();
auto frame = std::make_shared <TextureFrame> ();
frame->frameNumber = *pointer++;
@ -390,20 +388,38 @@ CTexture::TextureFrame* CTexture::parseAnimation (const char** originalFileData)
return frame;
}
CTexture::TextureMipmap* CTexture::parseMipmap (const TextureHeader* header, const char** originalFileData) {
auto* mipmap = new TextureMipmap ();
std::shared_ptr<CTexture::TextureMipmap> CTexture::parseMipmap (const TextureHeader* header, const char** originalFileData) {
auto mipmap = std::make_shared <TextureMipmap> ();
// get the current position
const char* fileData = *originalFileData;
// get an integer pointer
const auto* pointer = reinterpret_cast<const uint32_t*> (fileData);
// TEXB004 have some extra data (and even json) that we have to take into account
if (header->containerVersion == ContainerVersion::TEXB0004) {
// ignore various params, RePKG doesn't really use them
// and could be related to the editor really, so just ignore them
pointer++;
pointer++;
fileData = reinterpret_cast<const char*> (pointer);
while (*fileData != 0) {
mipmap->json += *fileData++;
}
// skip the null terminator
fileData ++;
pointer = reinterpret_cast<const uint32_t*> (fileData);
}
mipmap->width = *pointer++;
mipmap->height = *pointer++;
if (header->containerVersion == ContainerVersion::TEXB0002 ||
header->containerVersion == ContainerVersion::TEXB0003) {
header->containerVersion == ContainerVersion::TEXB0003 ||
header->containerVersion == ContainerVersion::TEXB0004) {
mipmap->compression = *pointer++;
mipmap->uncompressedSize = *pointer++;
}
@ -419,18 +435,18 @@ CTexture::TextureMipmap* CTexture::parseMipmap (const TextureHeader* header, con
mipmap->uncompressedSize = mipmap->compressedSize;
}
mipmap->uncompressedData = new char [mipmap->uncompressedSize];
mipmap->uncompressedData = std::unique_ptr<char[]>(new char [mipmap->uncompressedSize]);
if (mipmap->compression == 1) {
mipmap->compressedData = new char [mipmap->compressedSize];
mipmap->compressedData = std::unique_ptr<char[]>(new char [mipmap->compressedSize]);
memcpy (mipmap->compressedData, fileData, mipmap->compressedSize);
memcpy (mipmap->compressedData.get (), fileData, mipmap->compressedSize);
mipmap->decompressData ();
// advance to the end of the mipmap
fileData += mipmap->compressedSize;
} else {
memcpy (mipmap->uncompressedData, fileData, mipmap->uncompressedSize);
memcpy (mipmap->uncompressedData.get (), fileData, mipmap->uncompressedSize);
// advance to the end of the mipmap
fileData += mipmap->uncompressedSize;
}

View File

@ -5,6 +5,7 @@
#include <GL/glew.h>
#include <glm/vec4.hpp>
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
@ -19,6 +20,7 @@ class CTexture final : public ITexture {
*/
enum ContainerVersion : int {
UNKNOWN = -1,
TEXB0004 = 4,
TEXB0003 = 3,
TEXB0002 = 2,
TEXB0001 = 1
@ -72,6 +74,7 @@ class CTexture final : public ITexture {
FIF_PICT = 33,
FIF_RAW = 34,
FIF_WEBP = 35,
FIF_MP4 = FIF_WEBP,
FIF_JXR = 36
};
@ -80,9 +83,6 @@ class CTexture final : public ITexture {
*/
class TextureMipmap {
public:
TextureMipmap ();
~TextureMipmap ();
/** Width of the mipmap */
uint32_t width = 0;
/** Height of the mipmap */
@ -94,9 +94,11 @@ class CTexture final : public ITexture {
/** Compress size of the mipmap */
uint32_t compressedSize = 0;
/** Pointer to the compressed data */
char* compressedData = nullptr;
std::unique_ptr<char[]> compressedData = nullptr;
/** Pointer to the uncompressed data */
char* uncompressedData = nullptr;
std::unique_ptr<char[]> uncompressedData = nullptr;
/** JSON data */
std::string json {};
/**
* Performs actual decompression of the compressed data
*/
@ -109,7 +111,7 @@ class CTexture final : public ITexture {
class TextureHeader {
public:
TextureHeader ();
~TextureHeader ();
~TextureHeader () = default;
[[nodiscard]] bool isAnimated () const;
@ -118,57 +120,46 @@ class CTexture final : public ITexture {
/** The version of the animated data */
AnimatedVersion animatedVersion = AnimatedVersion::TEXSUNKN;
/** Flags with extra texture information */
TextureFlags flags;
TextureFlags flags = TextureFlags::NoFlags;
/** Real width of the texture */
uint32_t width;
uint32_t width = 0;
/** Real height of the texture */
uint32_t height;
uint32_t height = 0;
/** Texture width in memory (power of 2) */
uint32_t textureWidth;
uint32_t textureWidth = 0;
/** Texture height in memory (power of 2) */
uint32_t textureHeight;
uint32_t textureHeight = 0;
/** Gif width */
uint32_t gifWidth;
uint32_t gifWidth = 0;
/** Gif height */
uint32_t gifHeight;
uint32_t gifHeight = 0;
/** Texture data format */
TextureFormat format;
TextureFormat format = TextureFormat::UNKNOWN;
/** Free Image format */
FreeImageFormat freeImageFormat = FreeImageFormat::FIF_UNKNOWN;
/** Indicates if we have an MP4 video */
bool isVideoMp4 = false;
/** The amount of images in the texture file */
uint32_t imageCount;
/** Number of mipmap levels on the texture */
uint32_t mipmapCount;
uint32_t imageCount = 0;
/** List of mipmaps */
std::map<uint32_t, std::vector<TextureMipmap*>> images;
std::map<uint32_t, std::vector<std::shared_ptr<TextureMipmap>>> images {};
/** List of animation frames */
std::vector<TextureFrame*> frames;
std::vector<std::shared_ptr<TextureFrame>> frames {};
};
public:
explicit CTexture (const void* fileData);
~CTexture () override;
explicit CTexture (const std::shared_ptr<const uint8_t[]>& fileData);
/** @inheritdoc */
[[nodiscard]] const GLuint getTextureID (uint32_t imageIndex = 0) const override;
/** @inheritdoc */
[[nodiscard]] const uint32_t getTextureWidth (uint32_t imageIndex = 0) const override;
/** @inheritdoc */
[[nodiscard]] const uint32_t getTextureHeight (uint32_t imageIndex = 0) const override;
/** @inheritdoc */
[[nodiscard]] const uint32_t getRealWidth () const override;
/** @inheritdoc */
[[nodiscard]] const uint32_t getRealHeight () const override;
/** @inheritdoc */
[[nodiscard]] const TextureFormat getFormat () const override;
/** @inheritdoc */
[[nodiscard]] const TextureFlags getFlags () const override;
/** @inheritdoc */
[[nodiscard]] GLuint getTextureID (uint32_t imageIndex) const override;
[[nodiscard]] uint32_t getTextureWidth (uint32_t imageIndex) const override;
[[nodiscard]] uint32_t getTextureHeight (uint32_t imageIndex) const override;
[[nodiscard]] uint32_t getRealWidth () const override;
[[nodiscard]] uint32_t getRealHeight () const override;
[[nodiscard]] TextureFormat getFormat () const override;
[[nodiscard]] TextureFlags getFlags () const override;
[[nodiscard]] const glm::vec4* getResolution () const override;
/** @inheritdoc */
[[nodiscard]] const std::vector<TextureFrame*>& getFrames () const override;
/** @inheritdoc */
[[nodiscard]] const bool isAnimated () const override;
[[nodiscard]] const std::vector<std::shared_ptr<TextureFrame>>& getFrames () const override;
[[nodiscard]] bool isAnimated () const override;
private:
/**
@ -182,14 +173,14 @@ class CTexture final : public ITexture {
* @param fileData The point at which to start reading data off from
* @return
*/
static TextureHeader* parseHeader (const char* fileData);
static std::unique_ptr<TextureHeader> parseHeader (const char* fileData);
/**
* Tries to parse an animation frame off the given data pointer
*
* @param originalFileData The point at which to start reading data off from
* @return
*/
static TextureFrame* parseAnimation (const char** originalFileData);
static std::shared_ptr<TextureFrame> parseAnimation (const char** originalFileData);
/**
* Tries to parse mipmap information off the given data pointer
*
@ -197,13 +188,26 @@ class CTexture final : public ITexture {
* @param fileData The point at which to start reading data off from
* @return
*/
static TextureMipmap* parseMipmap (const TextureHeader* header, const char** fileData);
static std::shared_ptr<TextureMipmap> parseMipmap (const TextureHeader* header, const char** fileData);
/**
* Calculate's texture's resolution vec4
*/
void setupResolution ();
/**
* Determines the texture's internal storage format
*/
GLint setupInternalFormat ();
/**
* Prepares openGL parameters for loading texture data
*/
void setupOpenGLParameters (uint32_t textureID);
/** The texture header */
TextureHeader* m_header;
std::unique_ptr<TextureHeader> m_header = nullptr;
/** OpenGL's texture ID */
GLuint* m_textureID;
GLuint* m_textureID = nullptr;
/** Resolution vector of the texture */
glm::vec4 m_resolution;
glm::vec4 m_resolution {};
};
} // namespace WallpaperEngine::Assets

View File

@ -5,21 +5,30 @@
using namespace WallpaperEngine::Assets;
void CVirtualContainer::add (const std::string& filename, const uint8_t* contents, uint32_t length) {
this->m_virtualFiles.insert (std::make_pair (filename, new CFileEntry (contents, length)));
void CVirtualContainer::add (const std::filesystem::path& filename, const std::shared_ptr<const uint8_t[]>& contents, uint32_t length) {
this->m_virtualFiles.emplace (filename, std::make_unique<CFileEntry> (contents, length));
}
void CVirtualContainer::add (const std::string& filename, const std::string& contents) {
auto* copy = new uint8_t [contents.length () + 1];
void CVirtualContainer::add (const std::filesystem::path& filename, const std::string& contents) {
size_t length = contents.length () + 1;
std::shared_ptr<uint8_t[]> copy = std::shared_ptr<uint8_t[]> (new uint8_t [length]);
// copy the text AND the \0
memcpy (copy, contents.c_str (), contents.length () + 1);
memcpy (copy.get(), contents.c_str (), length);
// finally add to the container
this->add (filename, copy, contents.length () + 1);
this->add (filename, copy, length);
}
const uint8_t* CVirtualContainer::readFile (const std::string& filename, uint32_t* length) const {
void CVirtualContainer::add (const std::filesystem::path& filename, const char* contents) {
this->add (filename, std::string (contents));
}
void CVirtualContainer::add (const std::filesystem::path& filename, const json& contents) {
this->add (filename, contents.dump ());
}
std::shared_ptr<const uint8_t[]> CVirtualContainer::readFile (const std::filesystem::path& filename, uint32_t* length) const {
const auto cur = this->m_virtualFiles.find (filename);
if (cur == this->m_virtualFiles.end ())
@ -29,9 +38,5 @@ const uint8_t* CVirtualContainer::readFile (const std::string& filename, uint32_
*length = cur->second->length;
// clone original first
auto* result = new uint8_t [cur->second->length];
memcpy (result, cur->second->address, cur->second->length);
return result;
return cur->second->content;
}

View File

@ -2,11 +2,15 @@
#include <map>
#include <string>
#include <filesystem>
#include "CContainer.h"
#include "CFileEntry.h"
#include <nlohmann/json.hpp>
namespace WallpaperEngine::Assets {
using json = nlohmann::json;
/**
* Virtual container implementation, provides virtual files for the backgrounds to use
*/
@ -19,7 +23,7 @@ class CVirtualContainer final : public CContainer {
* @param contents
* @param length
*/
void add (const std::string& filename, const uint8_t* contents, uint32_t length);
void add (const std::filesystem::path& filename, const std::shared_ptr<const uint8_t[]>& contents, uint32_t length);
/**
* Adds a new file to the virtual container
@ -27,12 +31,27 @@ class CVirtualContainer final : public CContainer {
* @param filename
* @param contents
*/
void add (const std::string& filename, const std::string& contents);
void add (const std::filesystem::path& filename, const std::string& contents);
/**
* Adds a new file to the virtual container
*
* @param filename
* @param contents
*/
void add (const std::filesystem::path& filename, const char* contents);
/**
* Adds a new file to the virtual container from a json object
* @param filename
* @param contents
*/
void add (const std::filesystem::path& filename, const json& contents);
/** @inheritdoc */
const uint8_t* readFile (const std::string& filename, uint32_t* length) const override;
std::shared_ptr<const uint8_t[]> readFile (const std::filesystem::path& filename, uint32_t* length) const override;
private:
/** The recorded files in this virtual container */
std::map<std::string, CFileEntry*> m_virtualFiles;
std::map<std::string, std::unique_ptr<CFileEntry>> m_virtualFiles = {};
};
} // namespace WallpaperEngine::Assets

View File

@ -1,8 +1,10 @@
#pragma once
#include <vector>
#include <memory>
#include <GL/glew.h>
#include <glm/vec4.hpp>
#include <vector>
namespace WallpaperEngine::Assets {
/**
@ -22,19 +24,19 @@ class ITexture {
virtual ~TextureFrame () = default;
/** The image index of this frame */
uint32_t frameNumber;
uint32_t frameNumber = 0;
/** The amount of time this frame spends being displayed */
float frametime;
float frametime = 0.0f;
/** The x position of the frame in the texture */
float x;
float x = 0.0f;
/** The y position of the frame in the texture */
float y;
float y = 0.0f;
/** The width of the frame in the texture */
float width1;
float width2;
float height2;
float width1 = 0.0f;
float width2 = 0.0f;
/** The height of the frame in the texture */
float height1;
float height1 = 0.0f;
float height2 = 0.0f;
};
/**
@ -73,37 +75,37 @@ class ITexture {
* @param imageIndex For animated textures, the frame to get the ID of
* @return The OpenGL texture to use when rendering
*/
[[nodiscard]] virtual const GLuint getTextureID (uint32_t imageIndex = 0) const = 0;
[[nodiscard]] virtual GLuint getTextureID (uint32_t imageIndex) const = 0;
/**
* @param imageIndex For animated textures, the frame to get the ID of
* @return The texture's width
*/
[[nodiscard]] virtual const uint32_t getTextureWidth (uint32_t imageIndex = 0) const = 0;
[[nodiscard]] virtual uint32_t getTextureWidth (uint32_t imageIndex) const = 0;
/**
* @param imageIndex For animated textures, the frame to get the ID of
* @return The texture's height
*/
[[nodiscard]] virtual const uint32_t getTextureHeight (uint32_t imageIndex = 0) const = 0;
[[nodiscard]] virtual uint32_t getTextureHeight (uint32_t imageIndex) const = 0;
/**
* @return The textures real width
*/
[[nodiscard]] virtual const uint32_t getRealWidth () const = 0;
[[nodiscard]] virtual uint32_t getRealWidth () const = 0;
/**
* @return The textures real height
*/
[[nodiscard]] virtual const uint32_t getRealHeight () const = 0;
[[nodiscard]] virtual uint32_t getRealHeight () const = 0;
/**
* @return The texture's memory format
*/
[[nodiscard]] virtual const TextureFormat getFormat () const = 0;
[[nodiscard]] virtual TextureFormat getFormat () const = 0;
/**
* @return The texture's settings
*/
[[nodiscard]] virtual const TextureFlags getFlags () const = 0;
[[nodiscard]] virtual TextureFlags getFlags () const = 0;
/**
* @return The list of frames this texture has
*/
[[nodiscard]] virtual const std::vector<TextureFrame*>& getFrames () const = 0;
[[nodiscard]] virtual const std::vector<std::shared_ptr<TextureFrame>>& getFrames () const = 0;
/**
* @return The texture's resolution vector
*/
@ -111,6 +113,6 @@ class ITexture {
/**
* @return If the texture is animated or not
*/
[[nodiscard]] virtual const bool isAnimated () const = 0;
[[nodiscard]] virtual bool isAnimated () const = 0;
};
} // namespace WallpaperEngine::Assets

View File

@ -1,12 +1,10 @@
#include "CAudioStream.h"
#include "common.h"
#include "WallpaperEngine/Logging/CLog.h"
#include <cassert>
#include <iostream>
#include <cmath>
#include <iostream>
// maximum size of the queue to prevent reading too much data
#define MAX_QUEUE_SIZE (5 * 1024 * 1024)
#define MIN_FRAMES 25
using namespace WallpaperEngine::Audio;
@ -67,7 +65,7 @@ static int audio_read_data_callback (void* streamarg, uint8_t* buffer, int buffe
buffer_size = FFMIN (buffer_size, left);
memcpy (buffer, stream->getBuffer () + stream->getPosition (), buffer_size);
memcpy (buffer, stream->getBuffer ().get() + stream->getPosition (), buffer_size);
// update position
stream->setPosition (stream->getPosition () + buffer_size);
@ -82,7 +80,6 @@ int64_t audio_seek_data_callback (void* streamarg, int64_t offset, int whence) {
switch (whence) {
case SEEK_CUR: stream->setPosition (stream->getPosition () + offset); break;
case SEEK_SET: stream->setPosition (offset); break;
}
@ -90,14 +87,12 @@ int64_t audio_seek_data_callback (void* streamarg, int64_t offset, int whence) {
}
CAudioStream::CAudioStream (CAudioContext& context, const std::string& filename) :
m_audioContext (context),
m_swrctx (nullptr) {
m_audioContext (context) {
this->loadCustomContent (filename.c_str ());
}
CAudioStream::CAudioStream (CAudioContext& context, const uint8_t* buffer, uint32_t length) :
m_audioContext (context),
m_swrctx (nullptr) {
CAudioStream::CAudioStream (CAudioContext& context, std::shared_ptr<const uint8_t[]> buffer, uint32_t length) :
m_audioContext (context) {
// setup a custom context first
this->m_formatContext = avformat_alloc_context ();
@ -120,10 +115,9 @@ CAudioStream::CAudioStream (CAudioContext& context, const uint8_t* buffer, uint3
}
CAudioStream::CAudioStream (CAudioContext& audioContext, AVCodecContext* context) :
m_context (context),
m_queue (new PacketQueue),
m_audioContext (audioContext),
m_swrctx (nullptr) {
m_context (context),
m_queue (new PacketQueue) {
this->initialize ();
}
@ -142,17 +136,14 @@ void CAudioStream::loadCustomContent (const char* filename) {
if (avformat_find_stream_info (this->m_formatContext, nullptr) < 0)
sLog.exception ("Cannot determine file format: ", filename);
bool hasAudioStream = false;
// find the audio stream
for (unsigned int i = 0; i < this->m_formatContext->nb_streams; i++) {
if (this->m_formatContext->streams [i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && hasAudioStream == false) {
hasAudioStream = true;
if (this->m_formatContext->streams [i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && this->m_audioStream == NO_AUDIO_STREAM) {
this->m_audioStream = i;
}
}
if (!hasAudioStream)
if (this->m_audioStream == NO_AUDIO_STREAM)
sLog.exception ("Cannot find an audio stream in file ", filename);
// get the decoder for it and alloc the required context
@ -282,12 +273,11 @@ bool CAudioStream::doQueue (AVPacket* pkt) {
}
void CAudioStream::dequeuePacket (AVPacket* output) {
MyAVPacketList entry;
MyAVPacketList entry{};
SDL_LockMutex (this->m_queue->mutex);
while (this->m_audioContext.getApplicationContext ().state.general.keepRunning) {
#if FF_API_FIFO_OLD_API
int ret = -1;
@ -324,7 +314,7 @@ AVFormatContext* CAudioStream::getFormatContext () {
return this->m_formatContext;
}
unsigned int CAudioStream::getAudioStream () const {
int CAudioStream::getAudioStream () const {
return this->m_audioStream;
}
@ -340,7 +330,7 @@ bool CAudioStream::isRepeat () const {
return this->m_repeat;
}
const uint8_t* CAudioStream::getBuffer () {
std::shared_ptr<const uint8_t[]> CAudioStream::getBuffer () {
return this->m_buffer;
}
@ -369,6 +359,10 @@ int CAudioStream::getQueuePacketCount () {
}
AVRational CAudioStream::getTimeBase () {
if (this->m_audioStream == NO_AUDIO_STREAM) {
return {0, 0};
}
return this->m_formatContext->streams [this->m_audioStream]->time_base;
}

View File

@ -24,6 +24,10 @@ extern "C" {
#define FF_API_OLD_CHANNEL_LAYOUT (LIBAVUTIL_VERSION_MAJOR < 59)
#endif
#define MAX_QUEUE_SIZE (5 * 1024 * 1024)
#define MIN_FRAMES (25)
#define NO_AUDIO_STREAM (-1)
namespace WallpaperEngine::Audio {
class CAudioContext;
@ -33,7 +37,7 @@ class CAudioContext;
class CAudioStream {
public:
CAudioStream (CAudioContext& context, const std::string& filename);
CAudioStream (CAudioContext& context, const uint8_t* buffer, uint32_t length);
CAudioStream (CAudioContext& context, std::shared_ptr<const uint8_t[]> buffer, uint32_t length);
CAudioStream (CAudioContext& audioContext, AVCodecContext* context);
~CAudioStream ();
@ -64,7 +68,7 @@ class CAudioStream {
/**
* @return The audio stream index of the given file
*/
[[nodiscard]] unsigned int getAudioStream () const;
[[nodiscard]] int getAudioStream () const;
/**
* @return If the audio stream can be played or not
*/
@ -84,7 +88,7 @@ class CAudioStream {
/**
* @return The file data buffer
*/
[[nodiscard]] const uint8_t* getBuffer ();
[[nodiscard]] std::shared_ptr<const uint8_t[]> getBuffer ();
/**
* @return The length of the file data buffer
*/
@ -167,23 +171,23 @@ class CAudioStream {
void initialize ();
/** The SwrContext that handles resampling */
SwrContext* m_swrctx;
SwrContext* m_swrctx = nullptr;
/** The audio context this stream will be played under */
CAudioContext& m_audioContext;
/** If this stream was properly initialized or not */
bool m_initialized {};
bool m_initialized = false;
/** Repeat enabled? */
bool m_repeat {};
bool m_repeat = false;
/** The codec context that contains the original audio format information */
AVCodecContext* m_context = nullptr;
/** The format context that controls how data is read off the file */
AVFormatContext* m_formatContext = nullptr;
/** The stream index for the audio being played */
unsigned int m_audioStream;
int m_audioStream = NO_AUDIO_STREAM;
/** File data pointer */
const uint8_t* m_buffer {};
std::shared_ptr<const uint8_t[]> m_buffer = nullptr;
/** The length of the file data pointer */
uint32_t m_length {};
uint32_t m_length = 0;
/** The read position on the file data pointer */
uint32_t m_position = 0;
@ -196,16 +200,16 @@ class CAudioStream {
*/
struct PacketQueue {
#if FF_API_FIFO_OLD_API
AVFifoBuffer* packetList;
AVFifoBuffer* packetList = nullptr;
#else
AVFifo* packetList;
AVFifo* packetList = nullptr;
#endif
int nb_packets;
int size;
int64_t duration;
SDL_mutex* mutex;
SDL_cond* wait;
SDL_cond* cond;
int nb_packets = 0;
int size = 0;
int64_t duration = 0;
SDL_mutex* mutex = nullptr;
SDL_cond* wait = nullptr;
SDL_cond* cond = nullptr;
}* m_queue {};
};
} // namespace WallpaperEngine::Audio

View File

@ -1,8 +1,10 @@
#include "CAudioDriver.h"
namespace WallpaperEngine::Audio::Drivers {
CAudioDriver::CAudioDriver (Application::CApplicationContext& applicationContext,
Detectors::CAudioPlayingDetector& detector, Recorders::CPlaybackRecorder& recorder) :
CAudioDriver::CAudioDriver (
Application::CApplicationContext& applicationContext, Detectors::CAudioPlayingDetector& detector,
Recorders::CPlaybackRecorder& recorder
) :
m_applicationContext (applicationContext),
m_detector (detector),
m_recorder (recorder) {}

View File

@ -29,8 +29,9 @@ class CPulseAudioPlaybackRecorder;
*/
class CAudioDriver {
public:
explicit CAudioDriver (Application::CApplicationContext& applicationContext,
Detectors::CAudioPlayingDetector& detector, Recorders::CPlaybackRecorder& recorder);
explicit CAudioDriver (
Application::CApplicationContext& applicationContext, Detectors::CAudioPlayingDetector& detector,
Recorders::CPlaybackRecorder& recorder);
virtual ~CAudioDriver () = default;
/**

View File

@ -1,5 +1,5 @@
#include "CSDLAudioDriver.h"
#include "common.h"
#include "WallpaperEngine/Logging/CLog.h"
#define SDL_AUDIO_BUFFER_SIZE 4096
#define MAX_AUDIO_FRAME_SIZE 192000
@ -53,8 +53,9 @@ void audio_callback (void* userdata, uint8_t* streamData, int length) {
len1 = streamLength;
// mix the audio
SDL_MixAudioFormat (streamDataPointer, &buffer->audio_buf [buffer->audio_buf_index],
driver->getSpec ().format, len1, driver->getApplicationContext ().state.audio.volume);
SDL_MixAudioFormat (
streamDataPointer, &buffer->audio_buf [buffer->audio_buf_index], driver->getSpec ().format,
len1, driver->getApplicationContext ().state.audio.volume);
streamLength -= len1;
streamDataPointer += len1;
@ -63,10 +64,11 @@ void audio_callback (void* userdata, uint8_t* streamData, int length) {
}
}
CSDLAudioDriver::CSDLAudioDriver (Application::CApplicationContext& applicationContext,
Detectors::CAudioPlayingDetector& detector, Recorders::CPlaybackRecorder& recorder) :
CSDLAudioDriver::CSDLAudioDriver (
Application::CApplicationContext& applicationContext, Detectors::CAudioPlayingDetector& detector,
Recorders::CPlaybackRecorder& recorder
) :
CAudioDriver (applicationContext, detector, recorder),
m_initialized (false),
m_audioSpec () {
if (SDL_InitSubSystem (SDL_INIT_AUDIO) < 0) {
sLog.error ("Cannot initialize SDL audio system, SDL_GetError: ", SDL_GetError ());
@ -75,12 +77,14 @@ CSDLAudioDriver::CSDLAudioDriver (Application::CApplicationContext& applicationC
return;
}
const SDL_AudioSpec requestedSpec = {.freq = 48000,
const SDL_AudioSpec requestedSpec = {
.freq = 48000,
.format = AUDIO_F32,
.channels = 2,
.samples = SDL_AUDIO_BUFFER_SIZE,
.callback = audio_callback,
.userdata = this};
.userdata = this
};
this->m_deviceID =
SDL_OpenAudioDevice (nullptr, false, &requestedSpec, &this->m_audioSpec, SDL_AUDIO_ALLOW_ANY_CHANGE);

View File

@ -15,7 +15,7 @@ namespace WallpaperEngine::Audio::Drivers {
* Audio output buffers for streams being played under SDL
*/
struct CSDLAudioBuffer {
CAudioStream* stream;
CAudioStream* stream = nullptr;
uint8_t audio_buf [(MAX_AUDIO_FRAME_SIZE * 3) / 2] = {0};
unsigned int audio_buf_size = 0;
unsigned int audio_buf_index = 0;
@ -26,7 +26,8 @@ struct CSDLAudioBuffer {
*/
class CSDLAudioDriver final : public CAudioDriver {
public:
CSDLAudioDriver (Application::CApplicationContext& applicationContext, Detectors::CAudioPlayingDetector& detector,
CSDLAudioDriver (
Application::CApplicationContext& applicationContext, Detectors::CAudioPlayingDetector& detector,
Recorders::CPlaybackRecorder& recorder);
~CSDLAudioDriver () override;
@ -52,10 +53,10 @@ class CSDLAudioDriver final : public CAudioDriver {
/** The device's ID */
SDL_AudioDeviceID m_deviceID;
/** If the driver is initialized or not */
bool m_initialized;
bool m_initialized = false;
/** The sound output configuration */
SDL_AudioSpec m_audioSpec;
SDL_AudioSpec m_audioSpec {};
/** All the playable steams */
std::vector<CSDLAudioBuffer*> m_streams;
std::vector<CSDLAudioBuffer*> m_streams {};
};
} // namespace WallpaperEngine::Audio::Drivers

View File

@ -5,8 +5,7 @@ CAudioPlayingDetector::CAudioPlayingDetector (
Application::CApplicationContext& appContext,
const Render::Drivers::Detectors::CFullScreenDetector& fullscreenDetector) :
m_applicationContext (appContext),
m_fullscreenDetector (fullscreenDetector),
m_isPlaying (false) {}
m_fullscreenDetector (fullscreenDetector) {}
bool CAudioPlayingDetector::anythingPlaying () const {
return this->m_isPlaying;
@ -23,4 +22,7 @@ const Render::Drivers::Detectors::CFullScreenDetector& CAudioPlayingDetector::ge
void CAudioPlayingDetector::setIsPlaying (bool newState) {
this->m_isPlaying = newState;
}
void CAudioPlayingDetector::update () {}
} // namespace WallpaperEngine::Audio::Drivers::Detectors

View File

@ -18,7 +18,8 @@ namespace Audio::Drivers::Detectors {
*/
class CAudioPlayingDetector {
public:
CAudioPlayingDetector (Application::CApplicationContext& appContext,
CAudioPlayingDetector (
Application::CApplicationContext& appContext,
const Render::Drivers::Detectors::CFullScreenDetector& fullscreenDetector);
virtual ~CAudioPlayingDetector () = default;
@ -38,7 +39,9 @@ class CAudioPlayingDetector {
/**
* Checks if any audio is playing and updates state accordingly
*/
virtual void update () = 0;
virtual void update ();
protected:
/**
* @return The application context using this detector
*/
@ -49,7 +52,7 @@ class CAudioPlayingDetector {
[[nodiscard]] const Render::Drivers::Detectors::CFullScreenDetector& getFullscreenDetector () const;
private:
bool m_isPlaying;
bool m_isPlaying = false;
Application::CApplicationContext& m_applicationContext;
const Render::Drivers::Detectors::CFullScreenDetector& m_fullscreenDetector;

View File

@ -16,7 +16,7 @@ void sinkInputInfoCallback (pa_context* context, const pa_sink_input_info* info,
// get processid
const char* value = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_PROCESS_ID);
if (value && atoi (value) != getpid () && pa_cvolume_avg (&info->volume) != PA_VOLUME_MUTED)
if (value && strtol (value, nullptr, 10) != getpid () && pa_cvolume_avg (&info->volume) != PA_VOLUME_MUTED)
detector->setIsPlaying (true);
}
@ -32,10 +32,7 @@ void defaultSinkInfoCallback (pa_context* context, const pa_server_info* info, v
CPulseAudioPlayingDetector::CPulseAudioPlayingDetector (
Application::CApplicationContext& appContext,
const Render::Drivers::Detectors::CFullScreenDetector& fullscreenDetector) :
CAudioPlayingDetector (appContext, fullscreenDetector),
m_mainloop (nullptr),
m_mainloopApi (nullptr),
m_context (nullptr) {
CAudioPlayingDetector (appContext, fullscreenDetector) {
this->m_mainloop = pa_mainloop_new ();
this->m_mainloopApi = pa_mainloop_get_api (this->m_mainloop);
this->m_context = pa_context_new (this->m_mainloopApi, "wallpaperengine");

View File

@ -8,15 +8,15 @@
namespace WallpaperEngine::Audio::Drivers::Detectors {
class CPulseAudioPlayingDetector final : public CAudioPlayingDetector {
public:
explicit CPulseAudioPlayingDetector (Application::CApplicationContext& appContext,
const Render::Drivers::Detectors::CFullScreenDetector&);
explicit CPulseAudioPlayingDetector (
Application::CApplicationContext& appContext, const Render::Drivers::Detectors::CFullScreenDetector&);
~CPulseAudioPlayingDetector () override;
void update () override;
private:
pa_mainloop* m_mainloop;
pa_mainloop_api* m_mainloopApi;
pa_context* m_context;
pa_mainloop* m_mainloop = nullptr;
pa_mainloop_api* m_mainloopApi = nullptr;
pa_context* m_context = nullptr;
};
} // namespace WallpaperEngine::Audio::Drivers::Detectors

View File

@ -1,11 +1,9 @@
#include "CPulseAudioPlaybackRecorder.h"
#include "External/Android/fft.h"
#include "WallpaperEngine/Logging/CLog.h"
#include <cmath>
#include <cstring>
#include <glm/common.hpp>
namespace WallpaperEngine::Audio::Drivers::Recorders {
float movetowards (float current, float target, float maxDelta) {
if (abs (target - current) <= maxDelta)
return target;
@ -13,15 +11,17 @@ float movetowards (float current, float target, float maxDelta) {
return current + glm::sign (target - current) * maxDelta;
}
namespace WallpaperEngine::Audio::Drivers::Recorders {
void pa_stream_notify_cb (pa_stream* stream, void* /*userdata*/) {
switch (pa_stream_get_state (stream)) {
case PA_STREAM_FAILED: sLog.error ("Cannot open stream for capture. Audio processing is disabled"); break;
case PA_STREAM_READY: sLog.debug ("Capture stream ready"); break;
default: sLog.debug("pa_stream_get_state unknown result"); break;
}
}
void pa_stream_read_cb (pa_stream* stream, const size_t /*nbytes*/, void* userdata) {
auto* recorder = static_cast<CPulseAudioPlaybackRecorder*> (userdata);
auto* recorder = static_cast<CPulseAudioPlaybackRecorder::SPulseAudioData*> (userdata);
// Careful when to pa_stream_peek() and pa_stream_drop()!
// c.f. https://www.freedesktop.org/software/pulseaudio/doxygen/stream_8h.html#ac2838c449cde56e169224d7fe3d00824
@ -46,27 +46,37 @@ void pa_stream_read_cb (pa_stream* stream, const size_t /*nbytes*/, void* userda
} else if (currentSize > 0 && data) {
size_t dataToCopy = std::min (currentSize, WAVE_BUFFER_SIZE - recorder->currentWritePointer);
memcpy (&recorder->audio_buffer_tmp [recorder->currentWritePointer], data, dataToCopy * sizeof (uint8_t));
// depending on the amount of data available, we might want to read one or multiple frames
size_t end = recorder->currentWritePointer + dataToCopy;
recorder->currentWritePointer += dataToCopy;
// this packet will fill the buffer, perform some extra checks for extra full buffers and get the latest one
if (end == WAVE_BUFFER_SIZE) {
size_t numberOfFullBuffers = (currentSize - dataToCopy) / WAVE_BUFFER_SIZE;
if (recorder->currentWritePointer == WAVE_BUFFER_SIZE) {
// copy to the final buffer
memcpy (recorder->audio_buffer, recorder->audio_buffer_tmp, WAVE_BUFFER_SIZE * sizeof (uint8_t));
// reset the write pointer
if (numberOfFullBuffers > 0) {
// calculate the start of the last block (we need the end of the previous block, hence the - 1)
size_t startOfLastBuffer = std::max(dataToCopy + (numberOfFullBuffers - 1) * WAVE_BUFFER_SIZE, currentSize - WAVE_BUFFER_SIZE);
// copy directly into the final buffer
memcpy (recorder->audioBuffer, &data [startOfLastBuffer], WAVE_BUFFER_SIZE * sizeof (uint8_t));
// copy whatever is left to the read/write buffer
recorder->currentWritePointer = currentSize - startOfLastBuffer - WAVE_BUFFER_SIZE;
memcpy (recorder->audioBufferTmp, &data [startOfLastBuffer + WAVE_BUFFER_SIZE], recorder->currentWritePointer * sizeof (uint8_t));
} else {
// okay, no full extra packets available, copy the rest of the data and flip the buffers
memcpy (&recorder->audioBufferTmp [recorder->currentWritePointer], data, dataToCopy * sizeof (uint8_t));
uint8_t* tmp = recorder->audioBuffer;
recorder->audioBuffer = recorder->audioBufferTmp;
recorder->audioBufferTmp = tmp;
// reset write pointer
recorder->currentWritePointer = 0;
recorder->fullframeReady = true;
}
// any data read left?
if (dataToCopy < currentSize) {
while ((currentSize - dataToCopy) > WAVE_BUFFER_SIZE)
dataToCopy += WAVE_BUFFER_SIZE; // there's more than one full frame available, skip it entirely
// data pending, keep it in the buffer
memcpy (recorder->audio_buffer_tmp, data + dataToCopy, (currentSize - dataToCopy) * sizeof (uint8_t));
recorder->currentWritePointer = currentSize - dataToCopy;
// signal a new frame is ready
recorder->fullFrameReady = true;
} else {
// copy over available data to the tmp buffer and everything should be set
memcpy (&recorder->audioBufferTmp [recorder->currentWritePointer], data, dataToCopy * sizeof (uint8_t));
recorder->currentWritePointer += dataToCopy;
}
}
@ -76,31 +86,34 @@ void pa_stream_read_cb (pa_stream* stream, const size_t /*nbytes*/, void* userda
}
void pa_server_info_cb (pa_context* ctx, const pa_server_info* info, void* userdata) {
auto* recorder = static_cast<CPulseAudioPlaybackRecorder*> (userdata);
auto* recorder = static_cast<CPulseAudioPlaybackRecorder::SPulseAudioData*> (userdata);
pa_sample_spec spec;
spec.format = PA_SAMPLE_U8;
spec.rate = 44100;
spec.channels = 1;
if (recorder->getCaptureStream ()) {
pa_stream_unref (recorder->getCaptureStream ());
// get rid of the reference just in case
recorder->setCaptureStream (nullptr);
if (recorder->captureStream) {
pa_stream_unref (recorder->captureStream);
}
pa_stream* captureStream = pa_stream_new (ctx, "output monitor", &spec, nullptr);
recorder->captureStream = pa_stream_new (ctx, "output monitor", &spec, nullptr);
// store the stream first, if the record start fails there'll still be a reference to it
// so it can be free'd later
recorder->setCaptureStream (captureStream),
pa_stream_set_state_callback (captureStream, &pa_stream_notify_cb, userdata);
pa_stream_set_read_callback (captureStream, &pa_stream_read_cb, userdata);
pa_stream_set_state_callback (recorder->captureStream, &pa_stream_notify_cb, userdata);
pa_stream_set_read_callback (recorder->captureStream, &pa_stream_read_cb, userdata);
std::string monitor_name (info->default_sink_name);
monitor_name += ".monitor";
if (pa_stream_connect_record (captureStream, monitor_name.c_str (), nullptr, PA_STREAM_NOFLAGS) != 0) {
// setup latency
pa_buffer_attr attr {};
// 10 = latency msecs, 750 = max msecs to store
size_t bytesPerSec = pa_bytes_per_second (&spec);
attr.fragsize = bytesPerSec * 10 / 100;
attr.maxlength = attr.fragsize + bytesPerSec * 750 / 100;
if (pa_stream_connect_record (recorder->captureStream, monitor_name.c_str (), &attr, PA_STREAM_ADJUST_LATENCY) != 0) {
sLog.error ("Failed to connect to input for recording");
}
}
@ -131,15 +144,23 @@ void pa_context_notify_cb (pa_context* ctx, void* userdata) {
case PA_CONTEXT_FAILED:
sLog.error ("PulseAudio context initialization failed. Audio processing is disabled");
break;
default:
sLog.debug ("pa_context_get_state unknown result");
break;
}
}
CPulseAudioPlaybackRecorder::CPulseAudioPlaybackRecorder () : m_captureStream (nullptr) {
CPulseAudioPlaybackRecorder::CPulseAudioPlaybackRecorder () :
m_captureData({
.kisscfg = kiss_fftr_alloc (WAVE_BUFFER_SIZE, 0, nullptr, nullptr),
.audioBuffer = new uint8_t [WAVE_BUFFER_SIZE],
.audioBufferTmp = new uint8_t [WAVE_BUFFER_SIZE]
}) {
this->m_mainloop = pa_mainloop_new ();
this->m_mainloopApi = pa_mainloop_get_api (this->m_mainloop);
this->m_context = pa_context_new (this->m_mainloopApi, "wallpaperengine-audioprocessing");
pa_context_set_state_callback (this->m_context, &pa_context_notify_cb, this);
pa_context_set_state_callback (this->m_context, &pa_context_notify_cb, &this->m_captureData);
if (pa_context_connect (this->m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0) {
sLog.error ("PulseAudio connection failed! Audio processing is disabled");
@ -152,54 +173,62 @@ CPulseAudioPlaybackRecorder::CPulseAudioPlaybackRecorder () : m_captureStream (n
}
CPulseAudioPlaybackRecorder::~CPulseAudioPlaybackRecorder () {
if (m_captureData.captureStream) {
pa_stream_unref (m_captureData.captureStream);
}
delete [] this->m_captureData.audioBufferTmp;
delete [] this->m_captureData.audioBuffer;
free (this->m_captureData.kisscfg);
pa_context_disconnect (this->m_context);
pa_mainloop_free (this->m_mainloop);
}
pa_stream* CPulseAudioPlaybackRecorder::getCaptureStream () {
return this->m_captureStream;
}
void CPulseAudioPlaybackRecorder::setCaptureStream (pa_stream* stream) {
this->m_captureStream = stream;
}
void CPulseAudioPlaybackRecorder::update () {
pa_mainloop_iterate (this->m_mainloop, 0, nullptr);
// interpolate current values to the destination
for (int i = 0; i < 64; i++) {
this->audio64 [i] = movetowards (this->audio64 [i], fft_destination64 [i], 0.1f);
this->audio64 [i] = movetowards (this->audio64 [i], this->m_FFTdestination64 [i], 0.3f);
if (i >= 32)
continue;
this->audio32 [i] = movetowards (this->audio32 [i], fft_destination32 [i], 0.1f);
this->audio32 [i] = movetowards (this->audio32 [i], this->m_FFTdestination32 [i], 0.3f);
if (i >= 16)
continue;
this->audio16 [i] = movetowards (this->audio16 [i], fft_destination16 [i], 0.1f);
this->audio16 [i] = movetowards (this->audio16 [i], this->m_FFTdestination16 [i], 0.3f);
}
if (!this->fullframeReady)
if (!this->m_captureData.fullFrameReady)
return;
this->fullframeReady = false;
this->m_captureData.fullFrameReady = false;
External::Android::doFft (audio_fft, audio_buffer);
// convert audio data to deltas so the fft library can properly handle it
for (int i = 0; i < WAVE_BUFFER_SIZE; i ++) {
this->m_audioFFTbuffer [i] = (this->m_captureData.audioBuffer[i] - 128) / 128.0f;
}
for (int i = 0; i < 64; i++) {
const int paramInt = (i + 2) * 2;
float f1 = audio_fft [paramInt];
float f2 = audio_fft [paramInt + 1];
f2 = f1 * f1 + f2 * f2;
f1 = 0.0F;
if (f2 > 0.0F)
f1 = 0.35F * static_cast<float> (log10 (f2));
// perform full fft pass
kiss_fftr (this->m_captureData.kisscfg, this->m_audioFFTbuffer, this->m_FFTinfo);
this->fft_destination64 [i] =
fmin (1.0F, f1 * static_cast<float> (2.0f - pow (M_E, (1.0F - i / 63.0F) * 1.0f - 0.5f)));
this->fft_destination32 [i >> 1] =
fmin (1.0F, f1 * static_cast<float> (2.0f - pow (M_E, (1.0F - i / 31.0F) * 1.0f - 0.5f)));
this->fft_destination16 [i >> 2] =
fmin (1.0F, f1 * static_cast<float> (2.0f - pow (M_E, (1.0F - i / 15.0F) * 1.0f - 0.5f)));
// now reduce to the different bands
// use just one for loop to produce all 3
for (int band = 0; band < 64; band ++) {
int index = band * 2;
float f1 = this->m_FFTinfo[index].r;
float f2 = this->m_FFTinfo[index].i;
f2 = f1 * f1 + f2 * f2; // magnitude
f1 = 0.0f;
if (f2 > 0.0f) {
f1 = 0.35f * log10 (f2);
}
this->m_FFTdestination64 [band] = fmin (1.0f, f1 * static_cast<float> (2.0f - pow (M_E, (1.0f - band / 63.0f) * 1.0f - 0.5f)));
this->m_FFTdestination32 [band >> 1] = fmin (1.0f, f1 * static_cast<float> (2.0f - pow (M_E, (1.0f - band / 31.0f) * 1.0f - 0.5f)));
this->m_FFTdestination16 [band >> 2] = fmin (1.0f, f1 * static_cast<float> (2.0f - pow (M_E, (1.0f - band / 15.0f) * 1.0f - 0.5f)));
}
}
} // namespace WallpaperEngine::Audio::Drivers::Recorders

View File

@ -1,43 +1,43 @@
#pragma once
#include "kiss_fftr.h"
#include "CPlaybackRecorder.h"
#include "External/Android/fft.h"
#include <pulse/pulseaudio.h>
#define WAVE_BUFFER_SIZE 1024
namespace WallpaperEngine::Audio::Drivers::Recorders {
class CPlaybackRecorder;
class CPulseAudioPlaybackRecorder final : public CPlaybackRecorder {
public:
/**
* Struct that contains all the required data for the PulseAudio callbacks
*/
struct SPulseAudioData {
kiss_fftr_cfg kisscfg;
uint8_t* audioBuffer;
uint8_t* audioBufferTmp;
size_t currentWritePointer;
bool fullFrameReady;
pa_stream* captureStream;
};
CPulseAudioPlaybackRecorder ();
~CPulseAudioPlaybackRecorder () override;
void update () override;
/**
* @return The current stream we're capturing from
*/
[[nodiscard]] pa_stream* getCaptureStream ();
/**
* @param stream The new stream to be capturing off from
*/
void setCaptureStream (pa_stream* stream);
uint8_t audio_buffer [WAVE_BUFFER_SIZE] = {0x80};
uint8_t audio_buffer_tmp [WAVE_BUFFER_SIZE] = {0x80};
uint8_t audio_fft [WAVE_BUFFER_SIZE] = {0};
size_t currentWritePointer = 0;
bool fullframeReady = false;
private:
pa_mainloop* m_mainloop;
pa_mainloop_api* m_mainloopApi;
pa_context* m_context;
pa_stream* m_captureStream;
SPulseAudioData m_captureData;
float fft_destination64 [64];
float fft_destination32 [32];
float fft_destination16 [16];
float m_audioFFTbuffer [WAVE_BUFFER_SIZE] = {0.0f};
kiss_fft_cpx m_FFTinfo [WAVE_BUFFER_SIZE / 2 + 1] = {0};
float m_FFTdestination64 [64] = {0};
float m_FFTdestination32 [32] = {0};
float m_FFTdestination16 [16] = {0};
};
} // namespace WallpaperEngine::Audio::Drivers::Recorders

View File

@ -1,5 +1,6 @@
#include "CObject.h"
#include "common.h"
#include <utility>
#include "WallpaperEngine/Core/CProject.h"
#include "WallpaperEngine/Core/Objects/CImage.h"
@ -7,56 +8,65 @@
#include "WallpaperEngine/Core/Objects/CSound.h"
#include "WallpaperEngine/Core/UserSettings/CUserSettingBoolean.h"
#include "WallpaperEngine/Core/Wallpapers/CScene.h"
#include <utility>
#include "WallpaperEngine/Assets/CContainer.h"
#include "WallpaperEngine/Logging/CLog.h"
using namespace WallpaperEngine::Core;
using namespace WallpaperEngine::Assets;
using namespace WallpaperEngine::Core::UserSettings;
CObject::CObject (CScene* scene, CUserSettingBoolean* visible, int id, std::string name, std::string type,
CUserSettingVector3* origin, CUserSettingVector3* scale, const glm::vec3& angles) :
m_scene (scene),
CObject::CObject (
std::shared_ptr <const Core::CProject> project, const CUserSettingBoolean* visible, int id, std::string name,
const CUserSettingVector3* origin, const CUserSettingVector3* scale, const CUserSettingVector3* angles,
std::vector<int> dependencies
) :
m_visible (visible),
m_id (id),
m_name (std::move (name)),
m_type (std::move (type)),
m_name (std::move(name)),
m_origin (origin),
m_scale (scale),
m_angles (angles) {}
m_angles (angles),
m_project (project),
m_dependencies (std::move(dependencies)) {}
CObject* CObject::fromJSON (json data, CScene* scene, CContainer* container) {
std::string json = data.dump ();
const CObject* CObject::fromJSON (
const json& data, std::shared_ptr <const Core::CProject> project, const std::shared_ptr<const CContainer>& container
) {
const auto id = jsonFindRequired <int> (data, "id", "Objects must have id");
const auto visible = jsonFindUserConfig<CUserSettingBoolean> (data, *project, "visible", true);
const auto origin = jsonFindUserConfig<CUserSettingVector3> (data, *project, "origin", {0, 0, 0});
const auto scale = jsonFindUserConfig<CUserSettingVector3> (data, *project, "scale", {1, 1, 1});
const auto angles_val = jsonFindUserConfig<CUserSettingVector3> (data, *project, "angles", glm::vec3 (0, 0, 0));
const auto name = jsonFindRequired <std::string> (data, "name", "Objects must have name");
const auto effects_it = data.find ("effects");
const auto dependencies_it = data.find ("dependencies");
auto id_it = jsonFindRequired (data, "id", "Objects must have id");
auto visible = jsonFindUserConfig<CUserSettingBoolean> (data, "visible", true);
auto origin = jsonFindUserConfig<CUserSettingVector3> (data, "origin", {0, 0, 0});
auto scale = jsonFindUserConfig<CUserSettingVector3> (data, "scale", {1, 1, 1});
auto angles_val = jsonFindDefault<std::string> (data, "angles", "0.0 0.0 0.0");
auto name_it = jsonFindRequired (data, "name", "Objects must have name");
auto effects_it = data.find ("effects");
auto dependencies_it = data.find ("dependencies");
const auto image_it = data.find ("image");
const auto sound_it = data.find ("sound");
const auto particle_it = data.find ("particle");
const auto text_it = data.find ("text");
const auto light_it = data.find ("light");
auto image_it = data.find ("image");
auto sound_it = data.find ("sound");
auto particle_it = data.find ("particle");
auto text_it = data.find ("text");
auto light_it = data.find ("light");
std::vector<int> dependencies;
std::vector<const Objects::CEffect*> effects;
CObject* object;
const CObject* object;
if (dependencies_it != data.end () && dependencies_it->is_array ())
for (const auto& cur : *dependencies_it)
dependencies.push_back (cur);
if (image_it != data.end () && !image_it->is_null ()) {
object = Objects::CImage::fromJSON (scene, data, container, visible, *id_it, *name_it, origin, scale,
WallpaperEngine::Core::aToVector3 (angles_val));
object = Objects::CImage::fromJSON (
project, data, container, visible, id, name, origin, scale, angles_val, effects_it, dependencies);
} else if (sound_it != data.end () && !sound_it->is_null ()) {
object = Objects::CSound::fromJSON (scene, data, visible, *id_it, *name_it, origin, scale,
WallpaperEngine::Core::aToVector3 (angles_val));
object = Objects::CSound::fromJSON (project, data, visible, id, name, origin, scale, angles_val, dependencies);
} else if (particle_it != data.end () && !particle_it->is_null ()) {
/// TODO: XXXHACK -- TO REMOVE WHEN PARTICLE SUPPORT IS PROPERLY IMPLEMENTED
try {
object = Objects::CParticle::fromFile (scene, particle_it->get<std::string> (), container, visible, *id_it,
*name_it, origin, scale);
object = Objects::CParticle::fromFile (
project, particle_it->get<std::string> (), container, visible, id, name, origin, angles_val, scale, dependencies);
} catch (std::runtime_error&) {
return nullptr;
}
@ -67,68 +77,40 @@ CObject* CObject::fromJSON (json data, CScene* scene, CContainer* container) {
/// TODO: XXXHACK -- TO REMOVE WHEN LIGHT SUPPORT IS IMPLEMENTED
return nullptr;
} else {
sLog.exception ("Unknown object type detected: ", *name_it);
sLog.exception ("Unknown object type detected: ", name);
}
if (effects_it != data.end () && effects_it->is_array ()) {
for (auto& cur : *effects_it) {
auto effectVisible = jsonFindUserConfig<CUserSettingBoolean> (cur, "visible", true);
if (!effectVisible->processValue (scene->getProject ().getProperties ()))
continue;
object->insertEffect (Objects::CEffect::fromJSON (cur, effectVisible, object, container));
}
}
if (dependencies_it != data.end () && dependencies_it->is_array ())
for (const auto& cur : *dependencies_it)
object->insertDependency (cur);
return object;
}
glm::vec3 CObject::getOrigin () const {
return this->m_origin->processValue (this->getScene ()->getProject ().getProperties ());
const glm::vec3& CObject::getOrigin () const {
return this->m_origin->getVec3 ();
}
glm::vec3 CObject::getScale () const {
return this->m_scale->processValue (this->getScene ()->getProject ().getProperties ());
const glm::vec3& CObject::getScale () const {
return this->m_scale->getVec3 ();
}
const glm::vec3& CObject::getAngles () const {
return this->m_angles;
return this->m_angles->getVec3 ();
}
const std::string& CObject::getName () const {
return this->m_name;
}
const std::vector<Objects::CEffect*>& CObject::getEffects () const {
return this->m_effects;
}
const std::vector<int>& CObject::getDependencies () const {
return this->m_dependencies;
}
bool CObject::isVisible () const {
// TODO: cache this
return this->m_visible->processValue (this->getScene ()->getProject ().getProperties ());
return this->m_visible->getBool ();
}
CScene* CObject::getScene () const {
return this->m_scene;
std::shared_ptr <const CProject> CObject::getProject () const {
return this->m_project;
}
int CObject::getId () const {
return this->m_id;
}
void CObject::insertEffect (Objects::CEffect* effect) {
this->m_effects.push_back (effect);
}
void CObject::insertDependency (int dependency) {
this->m_dependencies.push_back (dependency);
}

View File

@ -8,7 +8,7 @@
#include "WallpaperEngine/Core/UserSettings/CUserSettingFloat.h"
#include "WallpaperEngine/Core/UserSettings/CUserSettingVector3.h"
namespace WallpaperEngine::Core {
namespace WallpaperEngine::Core::Wallpapers {
class CScene;
}
@ -26,57 +26,61 @@ using namespace WallpaperEngine::Assets;
using namespace WallpaperEngine::Core::UserSettings;
class CObject {
friend class CScene;
friend class Wallpapers::CScene;
public:
static CObject* fromJSON (json data, CScene* scene, CContainer* container);
static const CObject* fromJSON ( const json& data, std::shared_ptr <const Core::CProject> project,
const std::shared_ptr<const CContainer>& container);
template <class T> const T* as () const {
assert (is<T> ());
return reinterpret_cast<const T*> (this);
template <class T> [[nodiscard]] const T* as () const {
if (is <T> ()) {
return static_cast <const T*> (this);
}
template <class T> T* as () {
assert (is<T> ());
return reinterpret_cast<T*> (this);
throw std::bad_cast ();
}
template <class T> bool is () {
return this->m_type == T::Type;
template <class T> [[nodiscard]] T* as () {
if (is <T> ()) {
return static_cast <T*> (this);
}
throw std::bad_cast ();
}
template <class T> [[nodiscard]] bool is () const {
return typeid (*this) == typeid(T);
}
[[nodiscard]] const std::vector<Objects::CEffect*>& getEffects () const;
[[nodiscard]] const std::vector<int>& getDependencies () const;
[[nodiscard]] int getId () const;
[[nodiscard]] glm::vec3 getOrigin () const;
[[nodiscard]] glm::vec3 getScale () const;
[[nodiscard]] const glm::vec3& getOrigin () const;
[[nodiscard]] const glm::vec3& getScale () const;
[[nodiscard]] const glm::vec3& getAngles () const;
[[nodiscard]] const std::string& getName () const;
[[nodiscard]] bool isVisible () const;
[[nodiscard]] CScene* getScene () const;
[[nodiscard]] std::shared_ptr <const Core::CProject> getProject () const;
protected:
CObject (CScene* scene, CUserSettingBoolean* visible, int id, std::string name, std::string type,
CUserSettingVector3* origin, CUserSettingVector3* scale, const glm::vec3& angles);
CObject (
std::shared_ptr <const Core::CProject> scene, const CUserSettingBoolean* visible, int id, std::string name,
const CUserSettingVector3* origin, const CUserSettingVector3* scale, const CUserSettingVector3* angles,
std::vector<int> dependencies);
void insertEffect (Objects::CEffect* effect);
void insertDependency (int dependency);
virtual ~CObject () = default;
private:
std::string m_type;
const CUserSettingBoolean* m_visible;
int m_id = 0;
const std::string m_name;
const CUserSettingVector3* m_origin;
const CUserSettingVector3* m_scale;
const CUserSettingVector3* m_angles;
CUserSettingBoolean* m_visible;
int m_id;
std::string m_name;
CUserSettingVector3* m_origin;
CUserSettingVector3* m_scale;
glm::vec3 m_angles;
const std::vector<int> m_dependencies;
std::vector<Objects::CEffect*> m_effects;
std::vector<int> m_dependencies;
CScene* m_scene;
const std::shared_ptr <const Core::CProject> m_project;
};
} // namespace WallpaperEngine::Core

View File

@ -1,69 +1,95 @@
#include "common.h"
#include <WallpaperEngine/Assets/CContainer.h>
#include <WallpaperEngine/FileSystem/FileSystem.h>
#include <utility>
#include "CProject.h"
#include "WallpaperEngine/Core/Wallpapers/CScene.h"
#include "WallpaperEngine/Core/Wallpapers/CVideo.h"
#include "WallpaperEngine/Core/Wallpapers/CWeb.h"
#include "WallpaperEngine/Logging/CLog.h"
using namespace WallpaperEngine::Core;
using namespace WallpaperEngine::Core::Wallpapers;
using namespace WallpaperEngine::Assets;
CProject::CProject (std::string title, std::string type, CContainer* container) :
m_title (std::move (title)),
m_type (std::move (type)),
m_wallpaper (nullptr),
m_container (container) {}
static int backgroundId = -1;
CProject* CProject::fromFile (const std::string& filename, CContainer* container) {
json content = json::parse (WallpaperEngine::FileSystem::loadFullFile (filename, container));
CProject::CProject (
std::string title, std::string type, std::string workshopid, std::shared_ptr<const CContainer> container,
bool supportsaudioprocessing, const std::map<std::string, std::shared_ptr<Projects::CProperty>>& properties
) :
m_workshopid(std::move(workshopid)),
m_title (std::move(title)),
m_type (std::move(type)),
m_container (std::move(container)),
m_properties (properties),
m_supportsaudioprocessing (supportsaudioprocessing) {}
std::string dependency = jsonFindDefault<std::string> (content, "dependency", "No dependency");
if (dependency == "No dependency") {
std::string title = *jsonFindRequired (content, "title", "Project title missing");
std::string type = *jsonFindRequired (content, "type", "Project type missing");
std::string file = *jsonFindRequired (content, "file", "Project's main file missing");
std::shared_ptr<CProject> CProject::fromFile (const std::string& filename, std::shared_ptr<const CContainer> container) {
json content = json::parse (container->readFileAsString (filename));
const auto dependency = jsonFindDefault<std::string> (content, "dependency", "No dependency");
if (dependency != "No dependency") {
sLog.exception ("Project have dependency. They are not supported, quiting");
}
// workshopid is not required, but we have to use it for some identification stuff,
// so using a static, decreasing number should be enough
bool supportsaudioprocessing = false;
auto type = jsonFindRequired <std::string> (content, "type", "Project type missing");
const auto file = jsonFindRequired <std::string> (content, "file", "Project's main file missing");
auto general = content.find ("general");
CWallpaper* wallpaper;
std::shared_ptr <const CWallpaper> wallpaper = nullptr;
std::map<std::string, std::shared_ptr <Projects::CProperty>> properties;
std::transform (type.begin (), type.end (), type.begin (), tolower);
CProject* project = new CProject (title, type, container);
if (general != content.end ()) {
supportsaudioprocessing = jsonFindDefault (general, "supportsaudioprocessing", false);
const auto properties_it = general->find ("properties");
if (properties_it != general->end ()) {
for (const auto& cur : properties_it->items ()) {
auto property = Projects::CProperty::fromJSON (cur.value (), cur.key ());
if (property == nullptr) {
continue;
}
properties.emplace (property->getName (), std::move (property));
}
}
}
std::shared_ptr<CProject> project = std::make_shared <CProject> (
jsonFindRequired <std::string> (content, "title", "Project title missing"),
type,
jsonFindDefault <std::string> (content, "workshopid", std::to_string (backgroundId--)),
container,
supportsaudioprocessing,
properties
);
if (type == "scene")
wallpaper = CScene::fromFile (file, *project, container);
wallpaper = CScene::fromFile (file, project, container);
else if (type == "video")
wallpaper = new CVideo (file.c_str (), *project);
wallpaper = std::make_shared<CVideo> (file, project);
else if (type == "web")
wallpaper = new CWeb (file.c_str (), *project);
wallpaper = std::make_shared<CWeb> (file, project);
else
sLog.exception ("Unsupported wallpaper type: ", type);
project->setWallpaper (wallpaper);
if (general != content.end ()) {
const auto properties = general->find ("properties");
if (properties != general->end ()) {
for (const auto& cur : properties->items ()) {
Projects::CProperty* property = Projects::CProperty::fromJSON (cur.value (), cur.key ());
if (property != nullptr)
project->insertProperty (property);
}
}
}
return project;
} else {
sLog.exception ("Project have dependency. They are not supported, quiting");
}
}
void CProject::setWallpaper (CWallpaper* wallpaper) {
void CProject::setWallpaper (std::shared_ptr <const CWallpaper> wallpaper) {
this->m_wallpaper = wallpaper;
}
CWallpaper* CProject::getWallpaper () const {
const std::shared_ptr <const CWallpaper> CProject::getWallpaper () const {
return this->m_wallpaper;
}
@ -75,14 +101,18 @@ const std::string& CProject::getType () const {
return this->m_type;
}
const std::vector<Projects::CProperty*>& CProject::getProperties () const {
const std::map<std::string, std::shared_ptr <Projects::CProperty>>& CProject::getProperties () const {
return this->m_properties;
}
CContainer* CProject::getContainer () {
const std::string& CProject::getWorkshopId () const {
return this->m_workshopid;
}
std::shared_ptr<const CContainer> CProject::getContainer () const {
return this->m_container;
}
void CProject::insertProperty (Projects::CProperty* property) {
this->m_properties.push_back (property);
bool CProject::supportsAudioProcessing () const {
return this->m_supportsaudioprocessing;
}

View File

@ -1,11 +1,18 @@
#pragma once
#include <map>
#include <nlohmann/json.hpp>
#include "CWallpaper.h"
#include "WallpaperEngine/Core/Core.h"
#include "WallpaperEngine/Core/Projects/CProperty.h"
#include "WallpaperEngine/Assets/CContainer.h"
namespace WallpaperEngine::Core::Projects {
class CProperty;
}
namespace WallpaperEngine::Core {
using json = nlohmann::json;
using namespace WallpaperEngine::Assets;
@ -14,28 +21,33 @@ class CWallpaper;
class CProject {
public:
static CProject* fromFile (const std::string& filename, CContainer* container);
CProject (
std::string title, std::string type, std::string workshopid, std::shared_ptr<const CContainer> container,
bool supportsaudioprocessing, const std::map<std::string, std::shared_ptr<Projects::CProperty>>& properties);
CWallpaper* getWallpaper () const;
static std::shared_ptr<CProject> fromFile (const std::string& filename, std::shared_ptr<const CContainer> container);
const std::string& getTitle () const;
const std::string& getType () const;
const std::vector<Projects::CProperty*>& getProperties () const;
[[nodiscard]] const std::shared_ptr <const CWallpaper> getWallpaper () const;
[[nodiscard]] const std::string& getTitle () const;
[[nodiscard]] const std::string& getType () const;
[[nodiscard]] const std::map<std::string, std::shared_ptr <Projects::CProperty>>& getProperties () const;
[[nodiscard]] const std::string& getWorkshopId () const;
CContainer* getContainer ();
[[nodiscard]] bool supportsAudioProcessing () const;
[[nodiscard]] std::shared_ptr<const CContainer> getContainer () const;
protected:
CProject (std::string title, std::string type, CContainer* container);
void setWallpaper (CWallpaper* wallpaper);
void insertProperty (Projects::CProperty* property);
void setWallpaper (std::shared_ptr <const CWallpaper> wallpaper);
private:
std::vector<Projects::CProperty*> m_properties;
std::map<std::string, std::shared_ptr<Projects::CProperty>> m_properties;
std::string m_title;
std::string m_type;
CWallpaper* m_wallpaper;
CContainer* m_container;
const std::string m_workshopid;
const std::string m_title;
const std::string m_type;
const bool m_supportsaudioprocessing;
std::shared_ptr <const CWallpaper> m_wallpaper = nullptr;
std::shared_ptr<const CContainer> m_container = nullptr;
};
} // namespace WallpaperEngine::Core

View File

@ -4,8 +4,9 @@
using namespace WallpaperEngine::Core;
CWallpaper::CWallpaper (std::string type, CProject& project) : m_type (std::move (type)), m_project (project) {}
CWallpaper::CWallpaper (std::shared_ptr <const CProject> project) :
m_project (project) {}
CProject& CWallpaper::getProject () const {
std::shared_ptr <const CProject> CWallpaper::getProject () const {
return this->m_project;
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <cassert>
#include <string>
#include "CProject.h"
@ -9,30 +10,35 @@ class CProject;
class CWallpaper {
public:
template <class T> const T* as () const {
assert (is<T> ());
return reinterpret_cast<const T*> (this);
template <class T> [[nodiscard]] const T* as () const {
if (is<T> ()) {
return static_cast <const T*> (this);
}
template <class T> T* as () {
assert (is<T> ());
return reinterpret_cast<T*> (this);
throw std::bad_cast ();
}
template <class T> bool is () {
return this->m_type == T::Type;
template <class T> [[nodiscard]] T* as () {
if (is<T> ()) {
return static_cast <T*> (this);
}
CWallpaper (std::string type, CProject& project);
throw std::bad_cast ();
}
CProject& getProject () const;
template <class T> [[nodiscard]] bool is () const {
return typeid(*this) == typeid(T);
}
std::shared_ptr <const CProject> getProject () const;
protected:
friend class CProject;
private:
CProject& m_project;
explicit CWallpaper (std::shared_ptr <const CProject> project);
virtual ~CWallpaper() = default;
std::string m_type;
private:
std::shared_ptr <const CProject> m_project;
};
} // namespace WallpaperEngine::Core

View File

@ -1,10 +1,9 @@
#include "common.h"
#include "Core.h"
#include "WallpaperEngine/Core/UserSettings/CUserSettingBoolean.h"
#include "WallpaperEngine/Core/UserSettings/CUserSettingFloat.h"
#include "WallpaperEngine/Core/UserSettings/CUserSettingVector3.h"
#include "WallpaperEngine/Logging/CLog.h"
using namespace WallpaperEngine;
using namespace WallpaperEngine::Core::UserSettings;
@ -45,6 +44,42 @@ glm::vec2 Core::aToVector2 (const char* str) {
return {x, y};
}
glm::ivec4 Core::aToVector4i (const char* str) {
int x = strtol (str, const_cast<char**> (&str), 10);
while (*str == ' ')
str++;
int y = strtol (str, const_cast<char**> (&str), 10);
while (*str == ' ')
str++;
int z = strtol (str, const_cast<char**> (&str), 10);
while (*str == ' ')
str++;
int w = strtol (str, const_cast<char**> (&str), 10);
return {x, y, z, w};
}
glm::ivec3 Core::aToVector3i (const char* str) {
int x = strtol (str, const_cast<char**> (&str), 10);
while (*str == ' ')
str++;
int y = strtol (str, const_cast<char**> (&str), 10);
while (*str == ' ')
str++;
int z = strtol (str, const_cast<char**> (&str), 10);
return {x, y, z};
}
glm::ivec2 Core::aToVector2i (const char* str) {
int x = strtol (str, const_cast<char**> (&str), 10);
while (*str == ' ')
str++;
int y = strtol (str, const_cast<char**> (&str), 10);
return {x, y};
}
glm::vec4 Core::aToVector4 (const std::string& str) {
return Core::aToVector4 (str.c_str ());
}
@ -57,6 +92,18 @@ glm::vec2 Core::aToVector2 (const std::string& str) {
return Core::aToVector2 (str.c_str ());
}
glm::ivec4 Core::aToVector4i (const std::string& str) {
return Core::aToVector4i (str.c_str ());
}
glm::ivec3 Core::aToVector3i (const std::string& str) {
return Core::aToVector3i (str.c_str ());
}
glm::ivec2 Core::aToVector2i (const std::string& str) {
return Core::aToVector2i (str.c_str ());
}
glm::vec3 Core::aToColorf (const char* str) {
float r = strtof (str, const_cast<char**> (&str));
while (*str == ' ')
@ -89,17 +136,200 @@ glm::ivec3 Core::aToColori (const std::string& str) {
return aToColori (str.c_str ());
}
nlohmann::json::iterator Core::jsonFindRequired (nlohmann::json& data, const char* key, const char* notFoundMsg) {
auto value = data.find (key);
template <typename T> bool typeCheck (const nlohmann::json::const_iterator& value) {
if (value->type () == nlohmann::detail::value_t::null) {
return false;
}
if (value == data.end ())
sLog.exception ("Cannot find required key (", key, ") in json: ", notFoundMsg);
// type checks
if constexpr ((std::is_same_v<T, float> || std::is_same_v<T, double>) ) {
if (value->type () != nlohmann::detail::value_t::number_float &&
value->type () != nlohmann::detail::value_t::number_integer &&
value->type () != nlohmann::detail::value_t::number_unsigned) {
return false;
}
} else if constexpr (std::is_same_v<T, std::string>) {
if (value->type () != nlohmann::detail::value_t::string) {
return false;
}
} else if constexpr (std::is_same_v<T, bool>) {
if (value->type () != nlohmann::detail::value_t::boolean) {
return false;
}
} else if constexpr (
std::is_same_v<T, glm::vec2> || std::is_same_v<T, glm::vec3> || std::is_same_v<T, glm::vec4> ||
std::is_same_v<T, glm::ivec2> || std::is_same_v<T, glm::ivec3> || std::is_same_v<T, glm::ivec4>) {
if (value->type () != nlohmann::detail::value_t::string) {
return false;
}
}
return value;
return true;
}
nlohmann::json::iterator Core::jsonFindRequired (const nlohmann::json::iterator& data, const char* key,
const char* notFoundMsg) {
template <typename T> const T Core::jsonFindRequired (
const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg
) {
const auto iterator = jsonFindRequired (data, key, notFoundMsg);
// vector types need of special handling
if constexpr (std::is_same_v<T, std::string>) {
// std::strings are special, type checking doesn't need to happen, we just want the string representation
// of whatever is in there
if (iterator->is_number_integer ()) {
return std::to_string (iterator->get <int> ());
} else if (iterator->is_number_float ()) {
return std::to_string (iterator->get <float> ());
} else if (iterator->is_boolean()) {
return std::to_string (iterator->get <bool> ());
} else if (iterator->is_null ()) {
return "null";
} else if (iterator->is_string ()) {
return *iterator;
}
} else if constexpr (std::is_same_v<T, glm::vec4>) {
if (!typeCheck<T> (iterator)) {
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), " expected vector-like-string", ": ", notFoundMsg);
}
return aToVector4 (*iterator);
} else if constexpr (std::is_same_v<T, glm::vec3>) {
if (!typeCheck<T> (iterator)) {
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), " expected vector-like-string", ": ", notFoundMsg);
}
return aToVector3 (*iterator);
} else if constexpr (std::is_same_v<T, glm::vec2>) {
if (!typeCheck<T> (iterator)) {
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), " expected vector-like-string", ": ", notFoundMsg);
}
return aToVector2 (*iterator);
} else if constexpr (std::is_same_v<T, glm::ivec4>) {
if (!typeCheck<T> (iterator)) {
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), " expected vector-like-string", ": ", notFoundMsg);
}
return aToVector4i (*iterator);
} else if constexpr (std::is_same_v<T, glm::ivec3>) {
if (!typeCheck<T> (iterator)) {
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), " expected vector-like-string", ": ", notFoundMsg);
}
return aToVector3i (*iterator);
} else if constexpr (std::is_same_v<T, glm::ivec2>) {
if (!typeCheck<T> (iterator)) {
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), " expected vector-like-string", ": ", notFoundMsg);
}
return aToVector2i (*iterator);
} else if (typeCheck<T> (iterator)) {
return *iterator;
}
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), ": ", notFoundMsg);
}
template const bool Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const std::string Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const int16_t Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const uint16_t Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const int32_t Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const uint32_t Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const int64_t Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const uint64_t Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const float Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const double Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const glm::vec4 Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const glm::vec3 Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const glm::vec2 Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const glm::ivec4 Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const glm::ivec3 Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template const glm::ivec2 Core::jsonFindRequired (const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template <typename T> const T Core::jsonFindRequired (
const nlohmann::json& data, const char* key, const char* notFoundMsg
) {
const auto iterator = jsonFindRequired (data, key, notFoundMsg);
// vector types need of special handling
if constexpr (std::is_same_v<T, std::string>) {
// std::strings are special, type checking doesn't need to happen, we just want the string representation
// of whatever is in there
if (iterator->is_number_integer ()) {
return std::to_string (iterator->get <int> ());
} else if (iterator->is_number_float ()) {
return std::to_string (iterator->get <float> ());
} else if (iterator->is_boolean()) {
return std::to_string (iterator->get <bool> ());
} else if (iterator->is_null ()) {
return "null";
} else if (iterator->is_string ()) {
return *iterator;
}
} else if constexpr (std::is_same_v<T, glm::vec4>) {
if (!typeCheck<T> (iterator)) {
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), " expected vector-like-string", ": ", notFoundMsg);
}
return aToVector4 (*iterator);
} else if constexpr (std::is_same_v<T, glm::vec3>) {
if (!typeCheck<T> (iterator)) {
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), " expected vector-like-string", ": ", notFoundMsg);
}
return aToVector3 (*iterator);
} else if constexpr (std::is_same_v<T, glm::vec2>) {
if (!typeCheck<T> (iterator)) {
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), " expected vector-like-string", ": ", notFoundMsg);
}
return aToVector2 (*iterator);
} else if constexpr (std::is_same_v<T, glm::ivec4>) {
if (!typeCheck<T> (iterator)) {
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), " expected vector-like-string", ": ", notFoundMsg);
}
return aToVector4i (*iterator);
} else if constexpr (std::is_same_v<T, glm::ivec3>) {
if (!typeCheck<T> (iterator)) {
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), " expected vector-like-string", ": ", notFoundMsg);
}
return aToVector3i (*iterator);
} else if constexpr (std::is_same_v<T, glm::ivec2>) {
if (!typeCheck<T> (iterator)) {
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), " expected vector-like-string", ": ", notFoundMsg);
}
return aToVector2i (*iterator);
} else if (typeCheck<T> (iterator)) {
return *iterator;
}
sLog.exception ("key value doesn't match expected type. Got ", iterator->type_name(), ": ", notFoundMsg);
}
template const bool Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const std::string Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const int16_t Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const uint16_t Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const int32_t Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const uint32_t Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const int64_t Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const uint64_t Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const float Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const double Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const glm::vec4 Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const glm::vec3 Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const glm::vec2 Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const glm::ivec4 Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const glm::ivec3 Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
template const glm::ivec2 Core::jsonFindRequired (const nlohmann::json& data, const char* key, const char* notFoundMsg);
nlohmann::json::const_iterator Core::jsonFindRequired (
const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg
) {
auto value = data->find (key);
if (value == data->end ())
@ -108,57 +338,204 @@ nlohmann::json::iterator Core::jsonFindRequired (const nlohmann::json::iterator&
return value;
}
template <typename T> T Core::jsonFindDefault (nlohmann::json& data, const char* key, T defaultValue) {
nlohmann::json::const_iterator Core::jsonFindRequired (
const nlohmann::json& data, const char* key, const char* notFoundMsg
) {
auto value = data.find (key);
if (value == data.end ())
sLog.exception ("Cannot find required key (", key, ") in json: ", notFoundMsg);
return value;
}
template <typename T> const T Core::jsonFindDefault (
const nlohmann::json::const_iterator& data, const char* key, const T defaultValue
) {
const auto value = data->find (key);
if (value == data->end () || value->type () == nlohmann::detail::value_t::null)
return defaultValue;
// vector types need of special handling
if constexpr (std::is_same_v<T, std::string>) {
// std::strings are special, type checking doesn't need to happen, we just want the string representation
// of whatever is in there
if (value->is_number_integer ()) {
return std::to_string (value->get <int> ());
} else if (value->is_number_float ()) {
return std::to_string (value->get <float> ());
} else if (value->is_boolean()) {
return std::to_string (value->get <bool> ());
} else if (value->is_null ()) {
return "null";
} else if (value->is_string ()) {
return *value;
}
} else if constexpr (std::is_same_v<T, glm::vec4>) {
if (!typeCheck<T> (value)) {
return defaultValue;
}
return aToVector4 (*value);
} else if constexpr (std::is_same_v<T, glm::vec3>) {
if (!typeCheck<T> (value)) {
return defaultValue;
}
return aToVector3 (*value);
} else if constexpr (std::is_same_v<T, glm::vec2>) {
if (!typeCheck<T> (value)) {
return defaultValue;
}
return aToVector2 (*value);
} else if constexpr (std::is_same_v<T, glm::ivec4>) {
if (!typeCheck<T> (value)) {
return defaultValue;
}
return aToVector4i (*value);
} else if constexpr (std::is_same_v<T, glm::ivec3>) {
if (!typeCheck<T> (value)) {
return defaultValue;
}
return aToVector3i (*value);
} else if constexpr (std::is_same_v<T, glm::ivec2>) {
if (!typeCheck<T> (value)) {
return defaultValue;
}
return aToVector2i (*value);
} else if (typeCheck<T> (value)) {
return *value;
}
return defaultValue;
}
template const bool Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const bool defaultValue);
template const std::string Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const std::string defaultValue);
template const int16_t Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const int16_t defaultValue);
template const uint16_t Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const uint16_t defaultValue);
template const int32_t Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const int32_t defaultValue);
template const uint32_t Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const uint32_t defaultValue);
template const int64_t Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const int64_t defaultValue);
template const uint64_t Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const uint64_t defaultValue);
template const float Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const float defaultValue);
template const double Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const double defaultValue);
template const glm::vec2 Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const glm::vec2 defaultValue);
template const glm::vec3 Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const glm::vec3 defaultValue);
template const glm::vec4 Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const glm::vec4 defaultValue);
template const glm::ivec2 Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const glm::ivec2 defaultValue);
template const glm::ivec3 Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const glm::ivec3 defaultValue);
template const glm::ivec4 Core::jsonFindDefault (const nlohmann::json::const_iterator& data, const char* key, const glm::ivec4 defaultValue);
template <typename T> const T Core::jsonFindDefault (
const nlohmann::json& data, const char* key, const T defaultValue
) {
const auto value = data.find (key);
if (value == data.end () || value->type () == nlohmann::detail::value_t::null)
return defaultValue;
// type checks
if ((std::is_same_v<T, float> || std::is_same_v<T, double>) ) {
if (value->type () != nlohmann::detail::value_t::number_float &&
value->type () != nlohmann::detail::value_t::number_integer &&
value->type () != nlohmann::detail::value_t::number_unsigned) {
sLog.error (key, " is not of type double or integer, returning default value");
return defaultValue;
}
} else if (std::is_same_v<T, std::string> && value->type () != nlohmann::detail::value_t::string) {
sLog.error (key, " is not of type string, returning default value");
return defaultValue;
} else if (std::is_same_v<T, bool> && value->type () != nlohmann::detail::value_t::boolean) {
sLog.error (key, " is not of type boolean, returning default value");
// vector types need of special handling
if constexpr (std::is_same_v<T, glm::vec4>) {
if (!typeCheck<T> (value)) {
return defaultValue;
}
// TODO: SUPPORT INTEGERS AND OTHER TYPES
return aToVector4 (*value);
} else if constexpr (std::is_same_v<T, glm::vec3>) {
if (!typeCheck<T> (value)) {
return defaultValue;
}
return aToVector3 (*value);
} else if constexpr (std::is_same_v<T, glm::vec2>) {
if (!typeCheck<T> (value)) {
return defaultValue;
}
return aToVector2 (*value);
} else if constexpr (std::is_same_v<T, glm::ivec4>) {
if (!typeCheck<T> (value)) {
return defaultValue;
}
return aToVector4i (*value);
} else if constexpr (std::is_same_v<T, glm::ivec3>) {
if (!typeCheck<T> (value)) {
return defaultValue;
}
return aToVector3i (*value);
} else if constexpr (std::is_same_v<T, glm::ivec2>) {
if (!typeCheck<T> (value)) {
return defaultValue;
}
return aToVector2i (*value);
} else if (typeCheck<T> (value)) {
return *value;
}
return defaultValue;
}
template bool Core::jsonFindDefault (nlohmann::json& data, const char* key, bool defaultValue);
template std::string Core::jsonFindDefault (nlohmann::json& data, const char* key, std::string defaultValue);
template int16_t Core::jsonFindDefault (nlohmann::json& data, const char* key, int16_t defaultValue);
template uint16_t Core::jsonFindDefault (nlohmann::json& data, const char* key, uint16_t defaultValue);
template int32_t Core::jsonFindDefault (nlohmann::json& data, const char* key, int32_t defaultValue);
template uint32_t Core::jsonFindDefault (nlohmann::json& data, const char* key, uint32_t defaultValue);
template int64_t Core::jsonFindDefault (nlohmann::json& data, const char* key, int64_t defaultValue);
template uint64_t Core::jsonFindDefault (nlohmann::json& data, const char* key, uint64_t defaultValue);
template float Core::jsonFindDefault (nlohmann::json& data, const char* key, float defaultValue);
template double Core::jsonFindDefault (nlohmann::json& data, const char* key, double defaultValue);
template const bool Core::jsonFindDefault (const nlohmann::json& data, const char* key, const bool defaultValue);
template const std::string Core::jsonFindDefault (const nlohmann::json& data, const char* key, const std::string defaultValue);
template const int16_t Core::jsonFindDefault (const nlohmann::json& data, const char* key, const int16_t defaultValue);
template const uint16_t Core::jsonFindDefault (const nlohmann::json& data, const char* key, const uint16_t defaultValue);
template const int32_t Core::jsonFindDefault (const nlohmann::json& data, const char* key, const int32_t defaultValue);
template const uint32_t Core::jsonFindDefault (const nlohmann::json& data, const char* key, const uint32_t defaultValue);
template const int64_t Core::jsonFindDefault (const nlohmann::json& data, const char* key, const int64_t defaultValue);
template const uint64_t Core::jsonFindDefault (const nlohmann::json& data, const char* key, const uint64_t defaultValue);
template const float Core::jsonFindDefault (const nlohmann::json& data, const char* key, const float defaultValue);
template const double Core::jsonFindDefault (const nlohmann::json& data, const char* key, const double defaultValue);
template const glm::vec2 Core::jsonFindDefault (const nlohmann::json& data, const char* key, const glm::vec2 defaultValue);
template const glm::vec3 Core::jsonFindDefault (const nlohmann::json& data, const char* key, const glm::vec3 defaultValue);
template const glm::vec4 Core::jsonFindDefault (const nlohmann::json& data, const char* key, const glm::vec4 defaultValue);
template const glm::ivec2 Core::jsonFindDefault (const nlohmann::json& data, const char* key, const glm::ivec2 defaultValue);
template const glm::ivec3 Core::jsonFindDefault (const nlohmann::json& data, const char* key, const glm::ivec3 defaultValue);
template const glm::ivec4 Core::jsonFindDefault (const nlohmann::json& data, const char* key, const glm::ivec4 defaultValue);
template <typename T>
T* Core::jsonFindUserConfig (nlohmann::json& data, const char* key, typename T::data_type defaultValue) {
template <typename T> const T* Core::jsonFindUserConfig (
const nlohmann::json::const_iterator& data, const CProject& project, const char* key, typename T::data_type defaultValue
) {
const auto it = data->find (key);
if (it == data->end () || it->type () == nlohmann::detail::value_t::null)
return T::fromScalar (defaultValue);
return T::fromJSON (*it, project);
}
template const CUserSettingBoolean* Core::jsonFindUserConfig (
const nlohmann::json::const_iterator& data, const CProject& project, const char* key,
CUserSettingBoolean::data_type defaultValue);
template const CUserSettingVector3* Core::jsonFindUserConfig (
const nlohmann::json::const_iterator& data, const CProject& project, const char* key,
CUserSettingVector3::data_type defaultValue);
template const CUserSettingFloat* Core::jsonFindUserConfig (
const nlohmann::json::const_iterator& data, const CProject& project, const char* key,
CUserSettingFloat::data_type defaultValue);
template <typename T> const T* Core::jsonFindUserConfig (
const nlohmann::json& data, const CProject& project, const char* key, typename T::data_type defaultValue
) {
const auto it = data.find (key);
if (it == data.end () || it->type () == nlohmann::detail::value_t::null)
return T::fromScalar (defaultValue);
return T::fromJSON (*it);
return T::fromJSON (*it, project);
}
template CUserSettingBoolean* Core::jsonFindUserConfig (nlohmann::json& data, const char* key,
CUserSettingBoolean::data_type defaultValue);
template CUserSettingVector3* Core::jsonFindUserConfig (nlohmann::json& data, const char* key,
CUserSettingVector3::data_type defaultValue);
template CUserSettingFloat* Core::jsonFindUserConfig (nlohmann::json& data, const char* key,
CUserSettingFloat::data_type defaultValue);
template const CUserSettingBoolean* Core::jsonFindUserConfig (
const nlohmann::json& data, const CProject& project, const char* key, CUserSettingBoolean::data_type defaultValue);
template const CUserSettingVector3* Core::jsonFindUserConfig (
const nlohmann::json& data, const CProject& project, const char* key, CUserSettingVector3::data_type defaultValue);
template const CUserSettingFloat* Core::jsonFindUserConfig (
const nlohmann::json& data, const CProject& project, const char* key, CUserSettingFloat::data_type defaultValue);

View File

@ -1,5 +1,6 @@
#pragma once
#include "CProject.h"
#include <glm/mat4x4.hpp>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
@ -7,6 +8,8 @@
#include <string>
namespace WallpaperEngine::Core {
class CProject;
glm::vec4 aToVector4 (const char* str);
glm::vec3 aToVector3 (const char* str);
glm::vec2 aToVector2 (const char* str);
@ -15,15 +18,34 @@ glm::vec4 aToVector4 (const std::string& str);
glm::vec3 aToVector3 (const std::string& str);
glm::vec2 aToVector2 (const std::string& str);
glm::ivec4 aToVector4i (const char* str);
glm::ivec3 aToVector3i (const char* str);
glm::ivec2 aToVector2i (const char* str);
glm::ivec4 aToVector4i (const std::string& str);
glm::ivec3 aToVector3i (const std::string& str);
glm::ivec2 aToVector2i (const std::string& str);
glm::vec3 aToColorf (const char* str);
glm::vec3 aToColorf (const std::string& str);
glm::ivec3 aToColori (const char* str);
glm::ivec3 aToColori (const std::string& str);
nlohmann::json::iterator jsonFindRequired (nlohmann::json& data, const char* key, const char* notFoundMsg);
nlohmann::json::iterator jsonFindRequired (const nlohmann::json::iterator& data, const char* key,
const char* notFoundMsg);
template <typename T> T jsonFindDefault (nlohmann::json& data, const char* key, T defaultValue);
template <typename T> T* jsonFindUserConfig (nlohmann::json& data, const char* key, typename T::data_type defaultValue);
template <typename T> const T jsonFindRequired (
const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
template <typename T> const T jsonFindRequired (
const nlohmann::json& data, const char* key, const char* notFoundMsg);
nlohmann::json::const_iterator jsonFindRequired (
const nlohmann::json::const_iterator& data, const char* key, const char* notFoundMsg);
nlohmann::json::const_iterator jsonFindRequired (
const nlohmann::json& data, const char* key, const char* notFoundMsg);
template <typename T> const T jsonFindDefault (
const nlohmann::json::const_iterator& data, const char* key, const T defaultValue);
template <typename T> const T jsonFindDefault (
const nlohmann::json& data, const char* key, const T defaultValue);
template <typename T> const T* jsonFindUserConfig (
const nlohmann::json::const_iterator& data, const CProject& project, const char* key, typename T::data_type defaultValue);
template <typename T> const T* jsonFindUserConfig (
const nlohmann::json& data, const CProject& project, const char* key, typename T::data_type defaultValue);
} // namespace WallpaperEngine::Core

View File

@ -0,0 +1,205 @@
#include <algorithm>
#include "CDynamicValue.h"
using namespace WallpaperEngine::Core::DynamicValues;
CDynamicValue::~CDynamicValue() {
for (auto* connection : this->m_incomingConnections) {
connection->destroyOutgoingConnection (this);
}
}
void CDynamicValue::update(float newValue) {
this->m_ivec4 = glm::ivec4(static_cast<int> (newValue));
this->m_ivec3 = glm::ivec3(static_cast<int> (newValue));
this->m_ivec2 = glm::ivec2(static_cast<int> (newValue));
this->m_vec4 = glm::vec4(newValue);
this->m_vec3 = glm::vec3(newValue);
this->m_vec2 = glm::vec2(newValue);
this->m_float = newValue;
this->m_int = static_cast<int> (newValue);
this->m_bool = static_cast<int> (newValue) != 0;
this->propagate ();
}
void CDynamicValue::update(int newValue) {
this->m_ivec4 = glm::ivec4(newValue);
this->m_ivec3 = glm::ivec3(newValue);
this->m_ivec2 = glm::ivec2(newValue);
this->m_vec4 = glm::vec4(static_cast<float> (newValue));
this->m_vec3 = glm::vec3(static_cast<float> (newValue));
this->m_vec2 = glm::vec2(static_cast<float> (newValue));
this->m_float = static_cast<float> (newValue);
this->m_int = newValue;
this->m_bool = newValue != 0;
this->propagate ();
}
void CDynamicValue::update(bool newValue) {
this->m_ivec4 = glm::ivec4(newValue);
this->m_ivec3 = glm::ivec3(newValue);
this->m_ivec2 = glm::ivec2(newValue);
this->m_vec4 = glm::vec4(newValue);
this->m_vec3 = glm::vec3(newValue);
this->m_vec2 = glm::vec2(newValue);
this->m_float = newValue;
this->m_int = newValue;
this->m_bool = newValue;
this->propagate ();
}
void CDynamicValue::update(const glm::vec2& newValue) {
this->m_ivec4 = glm::ivec4(newValue, 0, 0);
this->m_ivec3 = glm::ivec3(newValue, 0);
this->m_ivec2 = glm::ivec2(newValue);
this->m_vec2 = newValue;
this->m_vec3 = glm::vec3(newValue, 0.0f);
this->m_vec4 = glm::vec4(newValue, 0.0f, 0.0f);
this->m_float = newValue.x;
this->m_int = static_cast<int> (newValue.x);
this->m_bool = newValue.x != 0.0f;
this->propagate ();
}
void CDynamicValue::update(const glm::vec3& newValue) {
this->m_ivec4 = glm::ivec4(newValue, 0);
this->m_ivec3 = glm::ivec3(newValue);
this->m_ivec2 = glm::ivec2(newValue);
this->m_vec2 = glm::vec2(newValue);
this->m_vec3 = newValue;
this->m_vec4 = glm::vec4(newValue, 0.0f);
this->m_float = newValue.x;
this->m_int = static_cast<int> (newValue.x);
this->m_bool = newValue.x != 0.0f;
this->propagate ();
}
void CDynamicValue::update(const glm::vec4& newValue) {
this->m_ivec4 = glm::ivec4(newValue);
this->m_ivec3 = glm::ivec3(newValue);
this->m_ivec2 = glm::ivec2(newValue);
this->m_vec2 = glm::vec2(newValue);
this->m_vec3 = glm::vec3(newValue);
this->m_vec4 = newValue;
this->m_float = newValue.x;
this->m_int = static_cast<int> (newValue.x);
this->m_bool = newValue.x != 0.0f;
this->propagate ();
}
void CDynamicValue::update(const glm::ivec2& newValue) {
this->m_ivec4 = glm::ivec4(newValue, 0, 0);
this->m_ivec3 = glm::ivec3(newValue, 0);
this->m_ivec2 = glm::ivec2(newValue);
this->m_vec2 = newValue;
this->m_vec3 = glm::vec3(newValue, 0.0f);
this->m_vec4 = glm::vec4(newValue, 0.0f, 0.0f);
this->m_float = static_cast<float> (newValue.x);
this->m_int = static_cast<int> (newValue.x);
this->m_bool = newValue.x != 0;
this->propagate ();
}
void CDynamicValue::update(const glm::ivec3& newValue) {
this->m_ivec4 = glm::ivec4(newValue, 0);
this->m_ivec3 = glm::ivec3(newValue);
this->m_ivec2 = glm::ivec2(newValue);
this->m_vec2 = glm::vec2(newValue);
this->m_vec3 = newValue;
this->m_vec4 = glm::vec4(newValue, 0.0f);
this->m_float = static_cast<float> (newValue.x);
this->m_int = static_cast<int> (newValue.x);
this->m_bool = newValue.x != 0;
this->propagate ();
}
void CDynamicValue::update(const glm::ivec4& newValue) {
this->m_ivec4 = glm::ivec4(newValue);
this->m_ivec3 = glm::ivec3(newValue);
this->m_ivec2 = glm::ivec2(newValue);
this->m_vec2 = glm::vec2(newValue);
this->m_vec3 = glm::vec3(newValue);
this->m_vec4 = newValue;
this->m_float = static_cast<float> (newValue.x);
this->m_int = static_cast<int> (newValue.x);
this->m_bool = newValue.x != 0;
this->propagate ();
}
void CDynamicValue::connectOutgoing (CDynamicValue* value) const {
this->m_outgoingConnections.push_back (value);
// ensure that new connection has the right value
this->propagate ();
// ensure the other value keeps track of our connection too
value->connectIncoming (this);
}
void CDynamicValue::connectIncoming (const CDynamicValue* value) const {
this->m_incomingConnections.push_back (value);
}
void CDynamicValue::destroyOutgoingConnection (CDynamicValue* value) const {
this->m_outgoingConnections.erase (
std::remove (this->m_outgoingConnections.begin (), this->m_outgoingConnections.end (), value), this->m_outgoingConnections.end ()
);
}
void CDynamicValue::propagate () const {
for (auto* cur : this->m_outgoingConnections) {
cur->m_bool = this->m_bool;
cur->m_int = this->m_int;
cur->m_float = this->m_float;
cur->m_ivec2 = this->m_ivec2;
cur->m_ivec3 = this->m_ivec3;
cur->m_ivec4 = this->m_ivec4;
cur->m_vec2 = this->m_vec2;
cur->m_vec3 = this->m_vec3;
cur->m_vec4 = this->m_vec4;
}
}
const glm::ivec4& CDynamicValue::getIVec4 () const {
return this->m_ivec4;
}
const glm::ivec3& CDynamicValue::getIVec3 () const {
return this->m_ivec3;
}
const glm::ivec2& CDynamicValue::getIVec2 () const {
return this->m_ivec2;
}
const glm::vec4& CDynamicValue::getVec4 () const {
return this->m_vec4;
}
const glm::vec3& CDynamicValue::getVec3 () const {
return this->m_vec3;
}
const glm::vec2& CDynamicValue::getVec2 () const {
return this->m_vec2;
}
const float& CDynamicValue::getFloat () const {
return this->m_float;
}
const int& CDynamicValue::getInt () const {
return this->m_int;
}
const bool& CDynamicValue::getBool () const {
return this->m_bool;
}

View File

@ -0,0 +1,68 @@
#pragma once
#include <functional>
#include <string>
#include <vector>
#include <glm/glm.hpp>
namespace WallpaperEngine::Core::DynamicValues {
class CDynamicValue {
public:
virtual ~CDynamicValue ();
[[nodiscard]] const glm::ivec4& getIVec4 () const;
[[nodiscard]] const glm::ivec3& getIVec3 () const;
[[nodiscard]] const glm::ivec2& getIVec2 () const;
[[nodiscard]] const glm::vec4& getVec4 () const;
[[nodiscard]] const glm::vec3& getVec3 () const;
[[nodiscard]] const glm::vec2& getVec2 () const;
[[nodiscard]] const float& getFloat () const;
[[nodiscard]] const int& getInt () const;
[[nodiscard]] const bool& getBool () const;
/**
* Connects the current instance to the given instance, updating it's values
* based on current instance's changes
*
* @param value
*/
void connectOutgoing (CDynamicValue* value) const;
protected:
void update (float newValue);
void update (int newValue);
void update (bool newValue);
void update (const glm::vec2& newValue);
void update (const glm::vec3& newValue);
void update (const glm::vec4& newValue);
void update (const glm::ivec2& newValue);
void update (const glm::ivec3& newValue);
void update (const glm::ivec4& newValue);
/**
* Registers an incoming connection (another CDynamicValue affecting the current instance's value)
* Useful mainly for destroying the connection on delete
*
* @param value
*/
void connectIncoming (const CDynamicValue* value) const;
void destroyOutgoingConnection (CDynamicValue* value) const;
/**
* Propagates the current value to all it's connections
*/
virtual void propagate () const;
private:
mutable std::vector<CDynamicValue*> m_outgoingConnections = {};
mutable std::vector<const CDynamicValue*> m_incomingConnections = {};
// different values that we will be casted to automagically
glm::ivec4 m_ivec4 = {};
glm::ivec3 m_ivec3 = {};
glm::ivec2 m_ivec2 = {};
glm::vec4 m_vec4 = {};
glm::vec3 m_vec3 = {};
glm::vec2 m_vec2 = {};
float m_float = 0.0f;
int m_int = 0;
bool m_bool = false;
};
};

View File

@ -1,230 +1,319 @@
#include "CEffect.h"
#include "common.h"
#include <iostream>
#include <utility>
#include "WallpaperEngine/Core/CProject.h"
#include "WallpaperEngine/Core/Objects/CImage.h"
#include "WallpaperEngine/Core/Objects/Images/Materials/CPass.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstant.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantFloat.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantInteger.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector2.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector3.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector4.h"
#include "WallpaperEngine/Core/Wallpapers/CScene.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantProperty.h"
#include "WallpaperEngine/Core/UserSettings/CUserSettingBoolean.h"
#include "WallpaperEngine/FileSystem/FileSystem.h"
#include "WallpaperEngine/Logging/CLog.h"
using namespace WallpaperEngine;
using namespace WallpaperEngine::Core::Objects;
using namespace WallpaperEngine::Core::UserSettings;
CEffect::CEffect (std::string name, std::string description, std::string group, std::string preview, CObject* object,
CUserSettingBoolean* visible) :
m_name (std::move (name)),
m_description (std::move (description)),
m_group (std::move (group)),
m_preview (std::move (preview)),
m_object (object),
m_visible (visible) {}
CEffect::CEffect (
std::string name, std::string description, std::string group, std::string preview,
std::shared_ptr <const Core::CProject> project, const CUserSettingBoolean* visible,
std::vector<std::string> dependencies, std::vector<const Effects::CFBO*> fbos,
std::vector<const Images::CMaterial*> materials
) :
m_name (std::move(name)),
m_description (std::move(description)),
m_group (std::move(group)),
m_preview (std::move(preview)),
m_visible (visible),
m_dependencies (std::move(dependencies)),
m_fbos (std::move(fbos)),
m_project (project),
m_materials (std::move(materials)) {}
CEffect* CEffect::fromJSON (json data, CUserSettingBoolean* visible, CObject* object, CContainer* container) {
auto file_it = jsonFindRequired (data, "file", "Object effect must have a file");
auto effectpasses_it = data.find ("passes");
const CEffect* CEffect::fromJSON (
const json& data, const CUserSettingBoolean* visible, std::shared_ptr <const Core::CProject> project,
const Images::CMaterial* material, const std::shared_ptr<const CContainer>& container
) {
const auto file = jsonFindRequired <std::string> (data, "file", "Object effect must have a file");
const auto effectpasses_it = data.find ("passes");
json content = json::parse (WallpaperEngine::FileSystem::loadFullFile (file_it->get<std::string> (), container));
json content = json::parse (container->readFileAsString(file));
auto name_it = jsonFindRequired (content, "name", "Effect must have a name");
auto description = jsonFindDefault<std::string> (content, "description", "");
auto group_it = jsonFindRequired (content, "group", "Effect must have a group");
auto preview = jsonFindDefault<std::string> (content, "preview", "");
auto passes_it = jsonFindRequired (content, "passes", "Effect must have a pass list");
auto dependencies_it = jsonFindRequired (content, "dependencies", "");
auto fbos_it = content.find ("fbos");
const auto effectName = jsonFindRequired <std::string> (content, "name", "Effect must have a name");
const auto passes_it = jsonFindRequired (content, "passes", "Effect must have a pass list");
const auto fbos_it = content.find ("fbos");
auto* effect = new CEffect (*name_it, description, *group_it, preview, object, visible);
CEffect::materialsFromJSON (passes_it, effect, container);
CEffect::dependencyFromJSON (dependencies_it, effect);
// info to override in the pass information, used by material generation
std::map<int, Images::CMaterial::OverrideInfo> overrides;
std::vector<const Effects::CFBO*> fbos;
if (fbos_it != content.end ())
CEffect::fbosFromJSON (fbos_it, effect);
fbos = CEffect::fbosFromJSON (fbos_it);
if (effectpasses_it != data.end ()) {
auto cur = effectpasses_it->begin ();
auto end = effectpasses_it->end ();
for (int passNumber = 0; cur != end; ++cur, passNumber++) {
auto constants_it = cur->find ("constantshadervalues");
auto combos_it = cur->find ("combos");
auto textures_it = cur->find ("textures");
if (constants_it == cur->end () && combos_it == cur->end () && textures_it == cur->end ())
continue;
Images::CMaterial* material = effect->getMaterials ().at (passNumber);
for (const auto& passCur : material->getPasses ()) {
if (textures_it != cur->end ()) {
std::vector<std::string>::size_type textureNumber = 0;
for (const auto& texturesCur : (*textures_it)) {
std::string texture;
if (texturesCur.is_null ()) {
if (textureNumber == 0) {
auto* image = object->as<CImage> ();
auto passTextures = (*image->getMaterial ()->getPasses ().begin ())->getTextures ();
if (passTextures.empty ()) {
// TODO: SET CHECKERBOARD TEXTURE AS DEFAULT IN THESE SITUATIONS
texture = "";
} else {
texture = *passTextures.begin ();
}
} else {
texture = "";
}
} else {
texture = texturesCur;
overrides = overridesFromJSON (effectpasses_it, material, project);
}
const auto& passTextures = passCur->getTextures ();
if (textureNumber < passTextures.size ())
passCur->setTexture (textureNumber, texture);
else
passCur->insertTexture (texture);
textureNumber++;
}
}
if (combos_it != cur->end ()) {
CEffect::combosFromJSON (combos_it, passCur);
}
if (constants_it != cur->end ()) {
CEffect::constantsFromJSON (constants_it, passCur);
}
}
}
}
return effect;
return new CEffect (
effectName,
jsonFindDefault<std::string> (content, "description", ""),
jsonFindRequired <std::string> (content, "group", "Effect must have a group"),
jsonFindDefault<std::string> (content, "preview", ""),
project,
visible,
dependenciesFromJSON (jsonFindRequired (content, "dependencies", "")),
fbos,
materialsFromJSON (passes_it, effectName, container, overrides)
);
}
void CEffect::combosFromJSON (const json::const_iterator& combos_it, Core::Objects::Images::Materials::CPass* pass) {
for (const auto& cur : combos_it->items ())
pass->insertCombo (cur.key (), cur.value ());
std::map<std::string, int> CEffect::combosFromJSON (const json::const_iterator& combos_it) {
std::map<std::string, int> combos;
for (const auto& cur : combos_it->items ()) {
auto uppercase = std::string (cur.key ());
std::transform (uppercase.begin (), uppercase.end (), uppercase.begin (), ::toupper);
combos.emplace (uppercase, cur.value ());
}
return combos;
}
void CEffect::constantsFromJSON (const json::const_iterator& constants_it,
Core::Objects::Images::Materials::CPass* pass) {
std::map<std::string, const Core::Objects::Effects::Constants::CShaderConstant*> CEffect::constantsFromJSON (
const json::const_iterator& constants_it, std::shared_ptr <const Core::CProject> project
) {
std::map<std::string, const Core::Objects::Effects::Constants::CShaderConstant*> constants;
for (auto& cur : constants_it->items ()) {
auto val = cur.value ();
Effects::Constants::CShaderConstant* constant;
Effects::Constants::CShaderConstant* constant = nullptr;
// if the constant is an object, that means the constant has some extra information
// for the UI, take the value, which is what we need
// TODO: SUPPORT USER SETTINGS HERE
if (cur.value ().is_object ()) {
auto user = cur.value ().find ("user");
auto it = cur.value ().find ("value");
if (it == cur.value ().end ()) {
sLog.error ("Found object for shader constant without \"value\" member");
if (user == cur.value ().end () && it == cur.value ().end ()) {
sLog.error (R"(Found object for shader constant without "value" and "user" setting)");
continue;
}
if (user != cur.value ().end () && user->is_string ()) {
// look for a property with the correct name
const auto& properties = project->getProperties ();
const auto property = properties.find (*user);
if (property != properties.end ()) {
constant = new Effects::Constants::CShaderConstantProperty (property->second);
} else {
sLog.error ("Shader constant pointing to non-existant project property: ", user->get <std::string> ());
val = it.value ();
}
} else {
val = it.value ();
}
}
// TODO: REFACTOR THIS SO IT'S NOT SO DEEP INTO THE FUNCTION
if (constant == nullptr) {
if (val.is_number_float ()) {
constant = new Effects::Constants::CShaderConstantFloat (val.get<float> ());
} else if (val.is_number_integer ()) {
constant = new Effects::Constants::CShaderConstantInteger (val.get<int> ());
} else if (val.is_string ()) {
// try a vector 4 first, then a vector3 and then a vector 2
constant = new Effects::Constants::CShaderConstantVector4 (WallpaperEngine::Core::aToVector4 (val));
// count the amount of spaces to determine which type of vector we have
std::string value = val;
size_t spaces =
std::count_if (value.begin (), value.end (), [&] (const auto& item) { return item == ' '; });
if (spaces == 1) {
constant =
new Effects::Constants::CShaderConstantVector2 (WallpaperEngine::Core::aToVector2 (value));
} else if (spaces == 2) {
constant =
new Effects::Constants::CShaderConstantVector3 (WallpaperEngine::Core::aToVector3 (value));
} else if (spaces == 3) {
constant =
new Effects::Constants::CShaderConstantVector4 (WallpaperEngine::Core::aToVector4 (value));
} else {
sLog.exception ("unknown shader constant type ", value);
}
} else {
sLog.exception ("unknown shader constant type ", val);
}
pass->insertConstant (cur.key (), constant);
}
constants.emplace (cur.key (), constant);
}
return constants;
}
void CEffect::fbosFromJSON (const json::const_iterator& fbos_it, CEffect* effect) {
std::vector<const Effects::CFBO*> CEffect::fbosFromJSON (const json::const_iterator& fbos_it) {
std::vector<const Effects::CFBO*> fbos;
for (const auto& cur : (*fbos_it))
effect->insertFBO (Effects::CFBO::fromJSON (cur));
fbos.push_back (Effects::CFBO::fromJSON (cur));
return fbos;
}
void CEffect::dependencyFromJSON (const json::const_iterator& dependencies_it, CEffect* effect) {
std::vector<std::string> CEffect::dependenciesFromJSON (const json::const_iterator& dependencies_it) {
std::vector<std::string> dependencies;
for (const auto& cur : (*dependencies_it))
effect->insertDependency (cur);
dependencies.push_back (cur);
return dependencies;
}
void CEffect::materialsFromJSON (const json::const_iterator& passes_it, CEffect* effect, CContainer* container) {
std::vector<const Images::CMaterial*> CEffect::materialsFromJSON (
const json::const_iterator& passes_it, const std::string& name, const std::shared_ptr<const CContainer>& container,
std::map<int, Images::CMaterial::OverrideInfo> overrides
) {
std::vector<const Images::CMaterial*> materials;
int materialNumber = -1;
for (const auto& cur : (*passes_it)) {
auto materialfile = cur.find ("material");
auto target = cur.find ("target");
auto bind = cur.find ("bind");
++materialNumber;
const auto materialfile = cur.find ("material");
const auto target_it = cur.find ("target");
const auto bind_it = cur.find ("bind");
const auto command_it = cur.find ("command");
const auto compose_it = cur.find ("compose");
const Images::CMaterial* material = nullptr;
if (materialfile == cur.end ())
sLog.exception ("Found an effect ", effect->m_name, " without material");
Images::CMaterial* material;
if (target == cur.end ())
material = Images::CMaterial::fromFile (materialfile->get<std::string> (), container);
else
material = Images::CMaterial::fromFile (materialfile->get<std::string> (), *target, container);
if (bind != cur.end ()) {
for (const auto& bindCur : (*bind))
material->insertTextureBind (Effects::CBind::fromJSON (bindCur));
if (compose_it != cur.end ()) {
sLog.error ("Composing materials is not supported yet...");
}
effect->insertMaterial (material);
if (materialfile != cur.end ()) {
std::map<int, const Effects::CBind*> textureBindings;
if (bind_it != cur.end ()) {
for (const auto& bindCur : (*bind_it)) {
const auto* bind = Effects::CBind::fromJSON (bindCur);
textureBindings.emplace (bind->getIndex (), bind);
}
}
const Images::CMaterial::OverrideInfo* overrideInfo = nullptr;
const auto overrideIt = overrides.find (materialNumber);
if (overrideIt != overrides.end ()) {
overrideInfo = &overrideIt->second;
}
if (target_it == cur.end ()) {
material = Images::CMaterial::fromFile (
materialfile->get<std::string> (), container, false, textureBindings, overrideInfo);
} else {
material = Images::CMaterial::fromFile (
materialfile->get<std::string> (), *target_it, container, false, textureBindings, overrideInfo);
}
} else if (command_it != cur.end ()) {
material = Images::CMaterial::fromCommand (cur);
} else {
sLog.exception ("Material without command nor material file: ", name, " (", materialNumber,")");
}
materials.push_back (material);
}
return materials;
}
std::map<int, Images::CMaterial::OverrideInfo> CEffect::overridesFromJSON (
const json::const_iterator& passes_it, const Images::CMaterial* material,
std::shared_ptr <const Core::CProject> project
) {
std::map<int, Images::CMaterial::OverrideInfo> result;
int materialNumber = -1;
for (const auto& cur : (*passes_it)) {
++materialNumber;
auto constants_it = cur.find ("constantshadervalues");
auto combos_it = cur.find ("combos");
auto textures_it = cur.find ("textures");
Images::CMaterial::OverrideInfo override;
int textureNumber = -1;
if (combos_it != cur.end ()) {
override.combos = CEffect::combosFromJSON (combos_it);
}
if (constants_it != cur.end ()) {
override.constants = CEffect::constantsFromJSON (constants_it, project);
}
if (textures_it != cur.end ()) {
// TODO: MAYBE CHANGE THIS TO BE SOMEWHERE ELSE? THIS IS REALLY MODIFYING THE DATA
// BUT IT'S USEFUL TO HAVE TO SIMPLIFY RENDERING CODE
for (const auto& texture : (*textures_it)) {
++textureNumber;
std::string name;
if (texture.is_null () && textureNumber > 0) {
continue;
}
if (textureNumber == 0) {
auto passTextures = (*material->getPasses ().begin ())->getTextures ();
if (passTextures.empty()) {
continue;
} else {
name = passTextures.begin ()->second;
}
} else {
name = texture;
}
override.textures.emplace (textureNumber, name);
}
}
result.emplace (materialNumber, override);
}
return result;
}
const std::vector<std::string>& CEffect::getDependencies () const {
return this->m_dependencies;
}
const std::vector<Images::CMaterial*>& CEffect::getMaterials () const {
const std::vector<const Images::CMaterial*>& CEffect::getMaterials () const {
return this->m_materials;
}
const std::vector<Effects::CFBO*>& CEffect::getFbos () const {
const std::vector<const Effects::CFBO*>& CEffect::getFbos () const {
return this->m_fbos;
}
bool CEffect::isVisible () const {
return this->m_visible->processValue (this->m_object->getScene ()->getProject ().getProperties ());
const Core::CProject& CEffect::getProject () const {
return *this->m_project;
}
Effects::CFBO* CEffect::findFBO (const std::string& name) {
bool CEffect::isVisible () const {
return this->m_visible->getBool ();
}
const Effects::CFBO* CEffect::findFBO (const std::string& name) {
for (const auto& cur : this->m_fbos)
if (cur->getName () == name)
return cur;
sLog.exception ("cannot find fbo ", name);
}
void CEffect::insertDependency (const std::string& dep) {
this->m_dependencies.push_back (dep);
}
void CEffect::insertMaterial (Images::CMaterial* material) {
this->m_materials.push_back (material);
}
void CEffect::insertFBO (Effects::CFBO* fbo) {
this->m_fbos.push_back (fbo);
}

View File

@ -9,6 +9,7 @@
namespace WallpaperEngine::Core {
class CObject;
class CProject;
}
namespace WallpaperEngine::Core::UserSettings {
@ -25,11 +26,15 @@ using namespace WallpaperEngine::Core::UserSettings;
*/
class CEffect {
public:
CEffect (std::string name, std::string description, std::string group, std::string preview, CObject* object,
CUserSettingBoolean* visible);
CEffect (
std::string name, std::string description, std::string group, std::string preview,
std::shared_ptr <const Core::CProject>, const CUserSettingBoolean* visible,
std::vector<std::string> dependencies, std::vector<const Effects::CFBO*> fbos,
std::vector<const Images::CMaterial*> materials);
static CEffect* fromJSON (json data, UserSettings::CUserSettingBoolean* visible, CObject* object,
CContainer* container);
static const CEffect* fromJSON (
const json& data, const CUserSettingBoolean* visible, std::shared_ptr <const Core::CProject> project,
const Images::CMaterial* material, const std::shared_ptr<const CContainer>& container);
/**
* @return List of dependencies for the effect to work
@ -38,15 +43,19 @@ class CEffect {
/**
* @return List of materials the effect applies
*/
[[nodiscard]] const std::vector<Images::CMaterial*>& getMaterials () const;
[[nodiscard]] const std::vector<const Images::CMaterial*>& getMaterials () const;
/**
* @return The list of FBOs to be used for this effect
*/
[[nodiscard]] const std::vector<Effects::CFBO*>& getFbos () const;
[[nodiscard]] const std::vector<const Effects::CFBO*>& getFbos () const;
/**
* @return If the effect is visible or not
*/
[[nodiscard]] bool isVisible () const;
/**
* @return The project this effect is part of
*/
[[nodiscard]] const CProject& getProject () const;
/**
* Searches the FBOs list for the given FBO
*
@ -54,39 +63,40 @@ class CEffect {
*
* @return
*/
Effects::CFBO* findFBO (const std::string& name);
const Effects::CFBO* findFBO (const std::string& name);
protected:
static void constantsFromJSON (const json::const_iterator& constants_it,
Core::Objects::Images::Materials::CPass* pass);
static void combosFromJSON (const json::const_iterator& combos_it, Core::Objects::Images::Materials::CPass* pass);
static void fbosFromJSON (const json::const_iterator& fbos_it, CEffect* effect);
static void dependencyFromJSON (const json::const_iterator& dependencies_it, CEffect* effect);
static void materialsFromJSON (const json::const_iterator& passes_it, CEffect* effect, CContainer* container);
void insertDependency (const std::string& dep);
void insertMaterial (Images::CMaterial* material);
void insertFBO (Effects::CFBO* fbo);
static std::map<std::string, const Core::Objects::Effects::Constants::CShaderConstant*> constantsFromJSON (
const json::const_iterator& constants_it, std::shared_ptr <const Core::CProject> project);
static std::map<std::string, int> combosFromJSON (const json::const_iterator& combos_it);
static std::vector<const Effects::CFBO*> fbosFromJSON (const json::const_iterator& fbos_it);
static std::vector<std::string> dependenciesFromJSON (const json::const_iterator& dependencies_it);
static std::vector<const Images::CMaterial*> materialsFromJSON (
const json::const_iterator& passes_it, const std::string& name,
const std::shared_ptr<const CContainer>& container, std::map<int, Images::CMaterial::OverrideInfo>);
static std::map<int, Images::CMaterial::OverrideInfo> overridesFromJSON (
const json::const_iterator& passes_it, const Images::CMaterial* material,
std::shared_ptr <const Core::CProject> project);
private:
/** Effect's name */
std::string m_name;
const std::string m_name;
/** Effect's description used in the UI */
std::string m_description;
const std::string m_description;
/** Effect's group used in the UI */
std::string m_group;
const std::string m_group;
/** A project that previews the given effect, used in the UI */
std::string m_preview;
/** The object the effect applies to */
CObject* m_object;
const std::string m_preview;
/** If the effect is visible or not */
UserSettings::CUserSettingBoolean* m_visible;
const UserSettings::CUserSettingBoolean* m_visible;
/** Project this effect is part of */
std::shared_ptr <const Core::CProject> m_project;
/** List of dependencies for the effect */
std::vector<std::string> m_dependencies;
const std::vector<std::string> m_dependencies;
/** List of materials the effect applies */
std::vector<Images::CMaterial*> m_materials;
const std::vector<const Images::CMaterial*> m_materials;
/** List of FBOs required for this effect */
std::vector<Effects::CFBO*> m_fbos;
const std::vector<const Effects::CFBO*> m_fbos;
};
} // namespace WallpaperEngine::Core::Objects

Some files were not shown because too many files have changed in this diff Show More