Compare commits

...

4 Commits

Author SHA1 Message Date
Almamu
73f716da84 refactor: new project parser 2025-08-12 03:36:44 +02:00
Almamu
be0fc25e72 chore: better error reporting for parameters, highlighting --help usage. fixess #303 and should improve #275
Some checks failed
CMake / build-x11 (ubuntu-22.04) (push) Has been cancelled
CMake / build-x11 (ubuntu-24.04) (push) Has been cancelled
CMake / build-x11-wayland (ubuntu-22.04) (push) Has been cancelled
CMake / build-x11-wayland (ubuntu-24.04) (push) Has been cancelled
CMake / build-wayland (ubuntu-22.04) (push) Has been cancelled
CMake / build-wayland (ubuntu-24.04) (push) Has been cancelled
2025-05-17 03:23:05 +02:00
Almamu
3c334aac29 chore: better error reporting after fix for #300
Some checks failed
CMake / build-x11 (ubuntu-22.04) (push) Has been cancelled
CMake / build-x11 (ubuntu-24.04) (push) Has been cancelled
CMake / build-x11-wayland (ubuntu-22.04) (push) Has been cancelled
CMake / build-x11-wayland (ubuntu-24.04) (push) Has been cancelled
CMake / build-wayland (ubuntu-22.04) (push) Has been cancelled
CMake / build-wayland (ubuntu-24.04) (push) Has been cancelled
2025-05-15 22:28:23 +02:00
Almamu
555b488951 fix: make background id optional so usages of -b, --bg do not require a fallback background (as expected) fixes #300 2025-05-15 22:20:20 +02:00
56 changed files with 2704 additions and 138 deletions

View File

@ -1,31 +0,0 @@
---
name: Background doesn't load properly
about: Use this to report backgrounds that are not loading (cannot find files, black
image, effects not working, etc)
title: "[BGFIX]"
labels: bug
assignees: Almamu
---
**Wallpaper Engine Background(s)**
Link(s) to the background(s) in the steam workshop or their background ID(s).
**Console output**
When a background cannot be loaded, it's usually due to some unexpected error that is logged into the terminal. Please attach the program's output so we can properly debug it if needed.
**Screenshots**
If you have any screenshot of it working on Windows that'll help so we can compare both outputs
**Desktop (please complete the following information):**
- OS: [e.g. Arch Linux]
- Desktop Environment: [e.g. GNOME, CINNAMON, KDE...]
- Window Manager: [if in doubt, just leave it empty]
**Additional context**
Any additional information about your setup

58
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View File

@ -0,0 +1,58 @@
name: "Bug Report"
description: "Report a bug or unexpected behavior."
labels: [bug]
body:
- type: markdown
attributes:
value: |
Please fill out this form to report a bug.
- type: input
id: summary
attributes:
label: Bug Summary
description: Briefly describe the bug.
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
description: Step-by-step instructions to reproduce the bug.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
validations:
required: false
- type: textarea
id: actual
attributes:
label: Actual Behavior / Logs
validations:
required: false
- type: input
id: os
attributes:
label: Operating System and Version
validations:
required: true
- type: input
id: de
attributes:
label: Desktop Environment (GNOME, KDE, etc.)
validations:
required: true
- type: input
id: x11_wayland
attributes:
label: Display Server (X11/Wayland)
validations:
required: true
- type: textarea
id: extra
attributes:
label: Additional Context or Screenshots
validations:
required: false

View File

@ -0,0 +1,46 @@
name: "Wallpaper Compatibility Issue"
description: "Report a problem with a specific Wallpaper Engine wallpaper."
labels: [compatibility, wallpaper]
body:
- type: input
id: wallpaper
attributes:
label: Wallpaper Name, URL or ID
validations:
required: true
- type: textarea
id: issue
attributes:
label: Describe the Issue
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
validations:
required: false
- type: textarea
id: logs
attributes:
label: Logs or Crash Output
validations:
required: false
- type: input
id: os
attributes:
label: Operating System and Version
validations:
required: true
- type: input
id: de
attributes:
label: Desktop Environment (GNOME, KDE, etc.)
validations:
required: true
- type: input
id: x11_wayland
attributes:
label: Display Server (X11/Wayland)
validations:
required: true

View File

@ -0,0 +1,32 @@
name: "Feature Request"
description: "Propose a new feature or enhancement."
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
Use this form to suggest new features or improvements.
- type: textarea
id: feature
attributes:
label: Describe the Feature
validations:
required: true
- type: textarea
id: problem
attributes:
label: Problem this Feature Addresses (if any)
validations:
required: false
- type: textarea
id: alternatives
attributes:
label: Alternatives Considered
validations:
required: false
- type: textarea
id: context
attributes:
label: Additional Context or Mockups
validations:
required: false

View File

@ -0,0 +1,38 @@
name: "Installation/Usage Help"
description: "Request help with installing or using linux-wallpaperengine."
labels: [question, support]
body:
- type: markdown
attributes:
value: |
Need help? Fill this out so we can assist you better.
- type: textarea
id: issue
attributes:
label: What are you trying to do, and what's the problem?
validations:
required: true
- type: textarea
id: attempted
attributes:
label: What have you tried so far?
validations:
required: false
- type: textarea
id: error_logs
attributes:
label: Errors or Log Output
validations:
required: false
- type: input
id: system
attributes:
label: OS, Desktop Environment, X11/Wayland
validations:
required: true
- type: input
id: install_method
attributes:
label: Installation Method (e.g., AUR, Flatpak, build from source)
validations:
required: true

View File

@ -1,10 +0,0 @@
---
name: Other issues
about: Use this to report general issues with the software
title: ''
labels: ''
assignees: Almamu
---

22
.github/ISSUE_TEMPLATE/refactor.yml vendored Normal file
View File

@ -0,0 +1,22 @@
name: "Code Cleanup / Refactor"
description: "Suggest internal improvements like refactoring or technical debt cleanup."
labels: [refactor, maintenance]
body:
- type: textarea
id: what
attributes:
label: What needs improvement?
validations:
required: true
- type: textarea
id: why
attributes:
label: Why is this necessary or beneficial?
validations:
required: false
- type: textarea
id: impact
attributes:
label: Potential Impact on Users
validations:
required: false

View File

@ -198,7 +198,8 @@ if(X11_SUPPORT_FOUND)
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/Detectors/CX11FullScreenDetector.h
src/WallpaperEngine/Data/Model/UserSetting.h)
SET(X11_INCLUDES
${X11_INCLUDE_DIR}
${XRANDR_INCLUDE_DIR})
@ -510,9 +511,41 @@ add_executable(
src/WallpaperEngine/Core/Objects/Images/Materials/CPass.cpp
src/WallpaperEngine/Core/Objects/Images/Materials/CPass.h
src/WallpaperEngine/Data/Model/Types.h
src/WallpaperEngine/Data/Model/Project.h
src/WallpaperEngine/Data/Model/Wallpaper.h
src/WallpaperEngine/Data/Model/Object.h
src/WallpaperEngine/Data/Model/Material.h
src/WallpaperEngine/Data/Model/Effect.h
src/WallpaperEngine/Data/Model/Model.h
src/WallpaperEngine/Data/Utils/TypeCaster.cpp
src/WallpaperEngine/Data/Utils/TypeCaster.h
src/WallpaperEngine/Data/Parsers/ProjectParser.cpp
src/WallpaperEngine/Data/Parsers/ProjectParser.h
src/WallpaperEngine/Data/Parsers/WallpaperParser.cpp
src/WallpaperEngine/Data/Parsers/WallpaperParser.h
src/WallpaperEngine/Data/Parsers/UserSettingParser.cpp
src/WallpaperEngine/Data/Parsers/UserSettingParser.h
src/WallpaperEngine/Data/Parsers/ObjectParser.cpp
src/WallpaperEngine/Data/Parsers/ObjectParser.h
src/WallpaperEngine/Data/Parsers/MaterialParser.cpp
src/WallpaperEngine/Data/Parsers/MaterialParser.h
src/WallpaperEngine/Data/Parsers/ModelParser.cpp
src/WallpaperEngine/Data/Parsers/ModelParser.h
src/WallpaperEngine/Data/Parsers/ShaderConstantParser.cpp
src/WallpaperEngine/Data/Parsers/ShaderConstantParser.h
src/WallpaperEngine/Data/Builders/UserSettingBuilder.h
src/WallpaperEngine/Data/Builders/VectorBuilder.cpp
src/WallpaperEngine/Data/Builders/VectorBuilder.h
${WAYLAND_SOURCES}
${X11_SOURCES}
${DEMOMODE_SOURCES})
${DEMOMODE_SOURCES}
src/WallpaperEngine/Data/Dumpers/StringPrinter.cpp
src/WallpaperEngine/Data/Dumpers/StringPrinter.h
src/WallpaperEngine/Data/JSON.cpp
src/WallpaperEngine/Data/Parsers/EffectParser.cpp
src/WallpaperEngine/Data/Parsers/EffectParser.h)
target_link_libraries (linux-wallpaperengine PUBLIC
${OPENGL_LIBRARIES}

View File

@ -26,8 +26,11 @@ CApplicationContext::CApplicationContext (int argc, char* argv []) :
backgroundGroup.add_argument ("background id")
.help ("The background to use as default for screens with no background specified")
.default_value ("")
.action([this](const std::string& value) -> void {
this->settings.general.defaultBackground = translateBackground (value);
if (!value.empty()) {
this->settings.general.defaultBackground = translateBackground (value);
}
});
backgroundMode.add_argument ("-w", "--window")
@ -252,40 +255,48 @@ CApplicationContext::CApplicationContext (int argc, char* argv []) :
" Runs the background 2317494988 on two screens, one on HDMI-1 and the other on HDMI-2\n\n"
);
program.parse_known_args (argc, argv);
try {
program.parse_known_args (argc, argv);
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 (this->settings.general.defaultBackground.empty ()) {
throw std::runtime_error ("At least one background ID must be specified");
}
// 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);
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));
bufferStream << "Running with: ";
// 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);
for (int i = 0; i < argc; i ++) {
bufferStream << argv [i];
bufferStream << " ";
}
bufferStream << "Running with: ";
std::cout << buffer.str() << std::endl;
// perform some extra validation on the inputs
this->validateAssets ();
this->validateScreenshot ();
for (int i = 0; i < argc; i ++) {
bufferStream << argv [i];
bufferStream << " ";
}
// setup application state
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;
std::cout << buffer.str() << std::endl;
// perform some extra validation on the inputs
this->validateAssets ();
this->validateScreenshot ();
// setup application state
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;
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 */
} catch (const std::runtime_error& e) {
throw std::runtime_error (std::string (e.what()) + ". Use " + std::string (argv[0]) + " --help for more information");
}
}
int CApplicationContext::getArgc () const {

View File

@ -12,6 +12,9 @@
#include "WallpaperEngine/Core/Wallpapers/CWeb.h"
#include "WallpaperEngine/Render/Drivers/CVideoFactories.h"
#include "WallpaperEngine/Data/Parsers/ProjectParser.h"
#include "WallpaperEngine/Data/Dumpers/StringPrinter.h"
#if DEMOMODE
#include "recording.h"
#endif /* DEMOMODE */
@ -37,18 +40,18 @@ CWallpaperApplication::CWallpaperApplication (CApplicationContext& context) :
void CWallpaperApplication::setupContainer (const std::shared_ptr<CCombinedContainer>& container, const std::string& bg) const {
const std::filesystem::path basepath = bg;
container->add (std::make_shared<CDirectory> (basepath));
container->add (std::make_unique<CDirectory> (basepath));
container->addPkg (basepath / "scene.pkg");
container->addPkg (basepath / "gifscene.pkg");
try {
container->add (std::make_shared <CDirectory> (this->m_context.settings.general.assets));
container->add (std::make_unique <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);
}
// TODO: move this somewhere else?
auto virtualContainer = std::make_shared <CVirtualContainer> ();
auto virtualContainer = std::make_unique <CVirtualContainer> ();
//
// Had to get a little creative with the effects to achieve the same bloom effect without any custom code
@ -181,7 +184,7 @@ void CWallpaperApplication::setupContainer (const std::shared_ptr<CCombinedConta
"}"
);
container->add (virtualContainer);
container->add (std::move(virtualContainer));
}
void CWallpaperApplication::loadBackgrounds () {
@ -206,6 +209,16 @@ std::shared_ptr<Core::CProject> CWallpaperApplication::loadBackground (const std
this->setupContainer (container, bg);
// do the parsing with the new parser first
auto json = WallpaperEngine::Data::JSON::JSON::parse (container->readFileAsString ("project.json"));
const auto project = WallpaperEngine::Data::Parsers::ProjectParser::parse (json, container);
auto dumper = WallpaperEngine::Data::Dumpers::StringPrinter ();
dumper.printWallpaper (*project->wallpaper);
std::cout << dumper.str () << std::endl;
return Core::CProject::fromFile ("project.json", container);
}

View File

@ -8,14 +8,14 @@ using namespace WallpaperEngine::Assets;
CCombinedContainer::CCombinedContainer () : CContainer () {}
void CCombinedContainer::add (const std::shared_ptr<CContainer>& container) {
this->m_containers.emplace_back (container);
void CCombinedContainer::add (std::unique_ptr<CContainer> container) {
this->m_containers.emplace_back (std::move(container));
}
void CCombinedContainer::addPkg (const std::filesystem::path& path) {
try {
// add the package to the list
this->add (std::make_shared<CPackage> (path));
this->add (std::make_unique<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

View File

@ -19,7 +19,7 @@ class CCombinedContainer final : public CContainer {
*
* @param container
*/
void add (const std::shared_ptr<CContainer>& container);
void add (std::unique_ptr<CContainer> container);
/**
* Adds the given package to the list
*
@ -32,6 +32,6 @@ class CCombinedContainer final : public CContainer {
private:
/** The list of containers to search files off from */
std::vector<std::shared_ptr<CContainer>> m_containers {};
std::vector<std::unique_ptr<CContainer>> m_containers {};
};
}; // namespace WallpaperEngine::Assets

View File

@ -6,6 +6,7 @@
#include <string>
namespace WallpaperEngine::Assets {
/**
* File container, provides access to files for backgrounds
*/

View File

@ -509,7 +509,7 @@ template <typename T> const T* Core::jsonFindUserConfig (
if (it == data->end () || it->type () == nlohmann::detail::value_t::null)
return T::fromScalar (defaultValue);
return T::fromJSON (*it, project);
return T::fromJSON (*it, project.getProperties ());
}
template const CUserSettingBoolean* Core::jsonFindUserConfig (
@ -530,7 +530,7 @@ template <typename T> const T* Core::jsonFindUserConfig (
if (it == data.end () || it->type () == nlohmann::detail::value_t::null)
return T::fromScalar (defaultValue);
return T::fromJSON (*it, project);
return T::fromJSON (*it, project.getProperties ());
}
template const CUserSettingBoolean* Core::jsonFindUserConfig (

View File

@ -5,8 +5,19 @@
#include <vector>
#include <glm/glm.hpp>
namespace WallpaperEngine::Data::Parsers {
class UserSettingParser;
}
namespace WallpaperEngine::Data::Builders {
class UserSettingBuilder;
}
namespace WallpaperEngine::Core::DynamicValues {
class CDynamicValue {
//TODO: THIS SHOULD BE CHANGED ONCE THINGS ARE FINISHED
friend class WallpaperEngine::Data::Parsers::UserSettingParser;
friend class WallpaperEngine::Data::Builders::UserSettingBuilder;
public:
virtual ~CDynamicValue ();

View File

@ -14,6 +14,10 @@ using namespace WallpaperEngine::Core::DynamicValues;
*/
class CProperty : public CDynamicValue {
public:
using UniquePtr = std::unique_ptr<CProperty>;
using SharedPtr = std::shared_ptr<CProperty>;
using WeakPtr = std::weak_ptr<CProperty>;
typedef std::function<void(const CProperty*)> function_type;
virtual ~CProperty () = default;
static std::shared_ptr<CProperty> fromJSON (const json& data, const std::string& name);

View File

@ -39,7 +39,7 @@ CUserSettingBoolean::CUserSettingBoolean (
}
}
const CUserSettingBoolean* CUserSettingBoolean::fromJSON (const nlohmann::json& data, const CProject& project) {
const CUserSettingBoolean* CUserSettingBoolean::fromJSON (const nlohmann::json& data, const std::map <std::string, std::shared_ptr <Projects::CProperty>>& properties) {
bool hasCondition = false;
std::shared_ptr <const Projects::CProperty> sourceProperty = nullptr;
bool defaultValue;
@ -61,11 +61,10 @@ const CUserSettingBoolean* CUserSettingBoolean::fromJSON (const nlohmann::json&
jsonFindRequired <std::string> (userIt, "condition", "Condition for conditional setting must be present");
}
for (const auto& [key, property] : project.getProperties ()) {
if (key == source) {
sourceProperty = property;
break;
}
const auto propertyIt = properties.find (source);
if (propertyIt != properties.end ()) {
sourceProperty = propertyIt->second;
}
if (sourceProperty == nullptr) {

View File

@ -12,7 +12,7 @@ class CUserSettingBoolean : public CUserSettingValue {
public:
typedef bool data_type;
static const CUserSettingBoolean* fromJSON (const nlohmann::json& data, const CProject& project);
static const CUserSettingBoolean* fromJSON (const nlohmann::json& data, const std::map <std::string, std::shared_ptr <Projects::CProperty>>& properties);
static const CUserSettingBoolean* fromScalar (bool value);
private:

View File

@ -33,7 +33,7 @@ CUserSettingFloat::CUserSettingFloat (
}
}
const CUserSettingFloat* CUserSettingFloat::fromJSON (const nlohmann::json& data, const CProject& project) {
const CUserSettingFloat* CUserSettingFloat::fromJSON (const nlohmann::json& data, const std::map <std::string, std::shared_ptr <Projects::CProperty>>& properties) {
float defaultValue;
std::string source;
std::string expectedValue;
@ -55,11 +55,10 @@ const CUserSettingFloat* CUserSettingFloat::fromJSON (const nlohmann::json& data
jsonFindRequired <std::string> (userIt, "condition", "Condition for conditional setting must be present");
}
for (const auto& [key, property] : project.getProperties ()) {
if (key == source) {
sourceProperty = property;
break;
}
const auto propertyIt = properties.find (source);
if (propertyIt != properties.end ()) {
sourceProperty = propertyIt->second;
}
if (sourceProperty == nullptr) {

View File

@ -12,7 +12,7 @@ class CUserSettingFloat : public CUserSettingValue {
public:
typedef float data_type;
static const CUserSettingFloat* fromJSON (const nlohmann::json& data, const CProject& project);
static const CUserSettingFloat* fromJSON (const nlohmann::json& data, const std::map <std::string, std::shared_ptr <Projects::CProperty>>& properties);
static const CUserSettingFloat* fromScalar (float value);
private:

View File

@ -33,7 +33,7 @@ CUserSettingVector3::CUserSettingVector3 (
}
}
const CUserSettingVector3* CUserSettingVector3::fromJSON (const nlohmann::json& data, const CProject& project) {
const CUserSettingVector3* CUserSettingVector3::fromJSON (const nlohmann::json& data, const std::map <std::string, std::shared_ptr <Projects::CProperty>>& properties) {
bool hasCondition = false;
std::shared_ptr <const Projects::CProperty> sourceProperty = nullptr;
glm::vec3 defaultValue;
@ -55,11 +55,10 @@ const CUserSettingVector3* CUserSettingVector3::fromJSON (const nlohmann::json&
jsonFindRequired <std::string> (userIt, "condition", "Condition for conditional setting must be present");
}
for (const auto& [key, property] : project.getProperties ()) {
if (key == source) {
sourceProperty = property;
break;
}
const auto propertyIt = properties.find (source);
if (propertyIt != properties.end ()) {
sourceProperty = propertyIt->second;
}
if (sourceProperty == nullptr) {

View File

@ -14,7 +14,7 @@ class CUserSettingVector3 : public CUserSettingValue {
public:
typedef glm::vec3 data_type;
static const CUserSettingVector3* fromJSON (const nlohmann::json& data, const CProject& project);
static const CUserSettingVector3* fromJSON (const nlohmann::json& data, const std::map <std::string, std::shared_ptr <Projects::CProperty>>& properties);
static const CUserSettingVector3* fromScalar (glm::vec3 value);
private:

View File

@ -0,0 +1,23 @@
#pragma once
#include "WallpaperEngine/Data/Model/Types.h"
#include "WallpaperEngine/Data/Model/UserSetting.h"
namespace WallpaperEngine::Data::Builders {
using namespace WallpaperEngine::Data::Model;
class UserSettingBuilder {
public:
template <typename T>
static UserSettingSharedPtr fromValue (T defaultValue) {
DynamicValueUniquePtr value = std::make_unique <DynamicValue> ();
value->update (defaultValue);
return std::make_shared <UserSetting> (UserSetting {
.value = std::move (value),
.property = PropertyWeakPtr (),
.condition = std::nullopt,
});
}
};
} // namespace WallpaperEngine::Data::Builders

View File

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

View File

@ -0,0 +1,178 @@
#pragma once
#include <cstring>
#include <string>
#include <glm/glm.hpp>
#include "WallpaperEngine/Logging/CLog.h"
namespace WallpaperEngine::Data::Builders {
class VectorBuilder {
private:
/**
* Convert template that calls the proper std::strto* function
* based on the incoming type
*
* @tparam type
* @param str
* @return
*/
template <typename type>
static type convert (const char* str);
public:
/**
* Takes the string and returns the vector size (2, 3 or 4)
*
* TODO: THIS SHOULD BE MOVED, RENAMED OR PLACED SOMEWHERE WHERE IT MAKES MORE SENSE
*
* @param str
* @return
*/
static int preparseSize (const std::string& str) {
const char* p = str.c_str ();
const char* first = strchr (p, ' ');
const char* second = first ? strchr (first + 1, ' ') : nullptr;
const char* third = second ? strchr (second + 1, ' ') : nullptr;
if (first == nullptr) {
sLog.exception ("Invalid vector format: " + str + " (too few values, expected: 2, 3 or 4)");
} else if (second == nullptr) {
return 2;
} else if (third == nullptr) {
return 3;
} else {
return 4;
}
}
/**
* Takes a string value and parses it into a glm::vec.
* This particular parsing uses spaces as separators and basic std::strto* functions
* for the actual parsing of the values.
*
* @tparam length Vector length
* @tparam type Vector storage type
* @tparam qualifier Precision qualifier
*
* @param str The string to parse the vector from
*
* @return
*/
template <int length, typename type, glm::qualifier qualifier>
static glm::vec<length, type, qualifier> parse (const std::string& str) {
const char* p = str.c_str ();
// get up to 4 spaces
const char* first = strchr (p, ' ');
const char* second = first ? strchr (first + 1, ' ') : nullptr;
const char* third = second ? strchr (second + 1, ' ') : nullptr;
// validate lengths against what was found in the strings
if constexpr (length == 1) {
if (first != nullptr) {
sLog.exception ("Invalid vector format: " + str + " (too many values, expected: ", length, ")");
}
} else if constexpr (length == 2) {
if (first == nullptr) {
sLog.exception ("Invalid vector format: " + str + " (too few values, expected: ", length, ")");
} else if (second != nullptr) {
sLog.exception ("Invalid vector format: " + str + " (too many values, expected: ", length, ")");
}
} else if constexpr (length == 3) {
if (first == nullptr || second == nullptr) {
sLog.exception ("Invalid vector format: " + str + " (too few values, expected: ", length, ")");
} else if (third != nullptr) {
sLog.exception ("Invalid vector format: " + str + " (too many values, expected: ", length, ")");
}
} else if constexpr (length == 4) {
if (first == nullptr || second == nullptr || third == nullptr) {
sLog.exception ("Invalid vector format: " + str + " (too few values, expected: ", length, ")");
}
} else {
sLog.exception ("Invalid vector length: ", length);
}
// lengths validated, values can be used directly without issues
if constexpr (length == 1) {
return {
convert <type> (p)
};
} else if constexpr (length == 2) {
return {
convert <type> (p),
convert <type> (first + 1)
};
} else if constexpr (length == 3) {
return {
convert <type> (p),
convert <type> (first + 1),
convert <type> (second + 1)
};
} else {
return {
convert <type> (p),
convert <type> (first + 1),
convert <type> (second + 1),
convert <type> (third + 1)
};
}
}
};
template <>
inline float VectorBuilder::convert<float> (const char* str) {
return std::strtof (str, nullptr);
}
template <>
inline int VectorBuilder::convert<int> (const char* str) {
return std::strtol (str, nullptr, 10);
}
template <>
inline unsigned int VectorBuilder::convert<unsigned int> (const char* str) {
return std::strtoul (str, nullptr, 10);
}
template <>
inline double VectorBuilder::convert<double> (const char* str) {
return std::strtod (str, nullptr);
}
template <>
inline uint8_t VectorBuilder::convert<uint8_t> (const char* str) {
return std::strtoul (str, nullptr, 10);
}
template <>
inline uint16_t VectorBuilder::convert<uint16_t> (const char* str) {
return std::strtoul (str, nullptr, 10);
}
template <>
inline uint64_t VectorBuilder::convert<uint64_t> (const char* str) {
return std::strtoull (str, nullptr, 10);
}
template <>
inline int8_t VectorBuilder::convert<int8_t> (const char* str) {
return std::strtol (str, nullptr, 10);
}
template <>
inline int16_t VectorBuilder::convert<int16_t> (const char* str) {
return std::strtol (str, nullptr, 10);
}
template <>
inline int64_t VectorBuilder::convert<int64_t> (const char* str) {
return std::strtoll (str, nullptr, 10);
}
template <>
inline bool VectorBuilder::convert<bool> (const char* str) {
return std::strtoul (str, nullptr, 10) > 0;
}
} // namespace WallpaperEngine::Data::Parsers

View File

@ -0,0 +1,394 @@
#include <utility>
#include "StringPrinter.h"
#include "WallpaperEngine/Data/Model/Wallpaper.h"
using namespace WallpaperEngine::Data::Dumpers;
using namespace WallpaperEngine::Data::Model;
StringPrinter::StringPrinter (std::string indentationCharacter) :
m_out (&this->m_buffer),
m_indentationCharacter (std::move(indentationCharacter)) { }
void StringPrinter::printWallpaper (const Wallpaper& wallpaper) {
bool isScene = wallpaper.is <Scene> ();
bool isVideo = wallpaper.is <Video> ();
bool isWeb = wallpaper.is <Web> ();
if (isVideo) {
const auto video = wallpaper.as <Video> ();
this->m_out << "Video wallpaper: " << video->filename;
this->lineEnd ();
} else if (isWeb) {
const auto web = wallpaper.as <Web> ();
this->m_out << "Web wallpaper: " << web->filename;
this->lineEnd ();
} else if (isScene) {
const auto scene = wallpaper.as <Scene> ();
this->m_out << "Scene wallpaper: ";
this->increaseIndentation ();
this->lineEnd ();
// TODO: IMPLEMENT FBO PRINTING, AS THIS WASN'T REALLY REFLECTION HOW IT ACTUALLY WORKS
this->m_out << "Objects count: " << scene->objects.size ();
this->increaseIndentation ();
for (const auto& object : scene->objects) {
this->lineEnd ();
this->printObject (*object.second);
this->lineEnd ();
}
this->decreaseIndentation ();
}
}
void StringPrinter::printObject (const Object& object) {
this->m_out << "Object " << object.id << " " << object.name << ":";
this->increaseIndentation ();
this->lineEnd ();
this->m_out << "Dependencies (" << object.dependencies.size () << "): ";
for (const auto& dependency : object.dependencies) {
this->m_out << dependency << ", ";
}
if (object.is <Image> ()) {
this->lineEnd ();
this->printImage (*object.as <Image> ());
} else if (object.is <Sound> ()) {
this->lineEnd ();
this->printSound (*object.as <Sound> ());
}
this->decreaseIndentation ();
}
void StringPrinter::printImage (const Image& image) {
this->printModel (*image.model);
this->lineEnd ();
this->m_out << "Image effects count: " << image.effects.size ();
this->increaseIndentation ();
for (const auto& effect : image.effects) {
this->lineEnd ();
this->printImageEffect (*effect);
}
this->decreaseIndentation ();
}
void StringPrinter::printSound (const Sound& sound) {
this->m_out << "Sounds: " << sound.sounds.size ();
this->increaseIndentation ();
for (const auto& filename : sound.sounds) {
this->lineEnd ();
this->m_out << "Filename " << filename;
}
this->decreaseIndentation ();
}
void StringPrinter::printModel (const ModelStruct& model) {
this->m_out << "Model " << model.filename << ":";
this->increaseIndentation ();
this->lineEnd ();
this->m_out << "Autosize: " << model.autosize;
this->lineEnd ();
this->m_out << "Passthrough: " << model.passthrough;
this->lineEnd ();
this->m_out << "Fullscreen: " << model.fullscreen;
this->lineEnd ();
this->m_out << "No padding: " << model.nopadding;
this->lineEnd ();
this->m_out << "Solid layer: " << model.solidlayer;
this->lineEnd ();
if (model.width.has_value ()) {
this->m_out << "Width: " << model.width.value ();
this->lineEnd ();
}
if (model.height.has_value ()) {
this->m_out << "Height: " << model.height.value ();
this->lineEnd ();
}
this->printMaterial (*model.material);
this->decreaseIndentation ();
}
void StringPrinter::printImageEffect (const ImageEffect& effect) {
this->m_out << "Image effect " << effect.id << ":";
this->increaseIndentation ();
this->lineEnd ();
this->m_out << "Default visibility status: " << effect.visible->value->getBool ();
this->printEffect (*effect.effect);
this->lineEnd ();
this->m_out << "Effect overrides count: " << effect.passOverrides.size ();
this->increaseIndentation ();
for (const auto& override : effect.passOverrides) {
this->printImageEffectPassOverride (*override);
}
this->decreaseIndentation ();
this->decreaseIndentation ();
}
void StringPrinter::printImageEffectPassOverride (const ImageEffectPassOverride& imageEffectPass) {
this->lineEnd ();
this->m_out << "Pass override " << imageEffectPass.id << ":";
this->increaseIndentation ();
this->lineEnd ();
this->m_out << "Textures count: " << imageEffectPass.textures.size ();
if (!imageEffectPass.textures.empty ()) {
this->increaseIndentation ();
for (const auto& texture : imageEffectPass.textures) {
this->lineEnd ();
this->m_out << "Texture " << texture.first << ": " << texture.second;
}
this->decreaseIndentation ();
}
this->lineEnd ();
this->m_out << "Combos count: " << imageEffectPass.combos.size ();
if (!imageEffectPass.combos.empty ()) {
this->increaseIndentation ();
for (const auto& combo : imageEffectPass.combos) {
this->lineEnd ();
this->m_out << "Combo " << combo.first << "=" << combo.second;
}
this->decreaseIndentation ();
}
this->lineEnd ();
this->m_out << "Constants count: " << imageEffectPass.constants.size ();
if (!imageEffectPass.constants.empty ()) {
this->increaseIndentation ();
for (const auto& constant : imageEffectPass.constants) {
this->lineEnd ();
this->m_out << "Constant " << constant.first << "=" << constant.second->toString();
}
this->decreaseIndentation ();
}
this->decreaseIndentation ();
}
void StringPrinter::printEffect (const Effect& effect) {
this->lineEnd ();
this->m_out << "Effect " << effect.name << ":";
this->increaseIndentation ();
if (!effect.description.empty ()) {
this->lineEnd ();
this->m_out << "Description: " << effect.description;
}
if (!effect.dependencies.empty ()) {
this->lineEnd ();
this->m_out << "Dependencies count: " << effect.dependencies.size ();
this->increaseIndentation ();
for (const auto& dependency : effect.dependencies) {
this->lineEnd ();
this->m_out << "Dependency: " << dependency;
}
this->decreaseIndentation ();
}
if (!effect.group.empty ()) {
this->lineEnd ();
this->m_out << "Group: " << effect.group;
}
if (!effect.preview.empty ()) {
this->lineEnd ();
this->m_out << "Preview: " << effect.preview;
}
this->lineEnd ();
this->m_out << "Passes count: " << effect.passes.size ();
this->increaseIndentation ();
int passId = 0;
for (const auto& pass : effect.passes) {
this->lineEnd ();
this->m_out << "Pass " << ++passId << ":";
this->increaseIndentation ();
this->lineEnd ();
this->printEffectPass (*pass);
this->decreaseIndentation ();
}
this->decreaseIndentation ();
this->lineEnd ();
this->m_out << "FBOs count: " << effect.fbos.size ();
this->increaseIndentation ();
for (const auto& fbo : effect.fbos) {
this->lineEnd ();
this->printFBO (*fbo);
}
this->decreaseIndentation ();
this->decreaseIndentation ();
}
void StringPrinter::printEffectPass (const EffectPass& effectPass) {
if (effectPass.command.has_value ()) {
const auto& command = effectPass.command.value ();
this->m_out << "Command: " << (command.command == Command_Copy ? "copy" : "swap");
this->lineEnd ();
this->m_out << "Source: " << command.source;
this->lineEnd ();
this->m_out << "Target: " << command.target;
} else {
if (effectPass.target.has_value ()) {
this->m_out << "Target: " << effectPass.target.value ();
}
if (!effectPass.binds.empty ()) {
if (effectPass.target.has_value ()) {
this->lineEnd ();
}
this->m_out << "Binds count: " << effectPass.binds.size ();
this->increaseIndentation ();
for (const auto& bind : effectPass.binds) {
this->lineEnd ();
this->m_out << "Bind " << bind.first << ": " << bind.second;
}
this->decreaseIndentation ();
}
if (effectPass.target.has_value () || !effectPass.binds.empty ()) {
this->lineEnd ();
}
this->printMaterial (*effectPass.material);
}
}
void StringPrinter::printFBO (const FBO& fbo) {
this->m_out << "FBO " << fbo.name << ":";
this->increaseIndentation ();
this->lineEnd ();
this->m_out << "Format: " << fbo.format;
this->lineEnd ();
this->m_out << "Scale: " << fbo.scale;
this->decreaseIndentation ();
}
void StringPrinter::printMaterial (const Material& material) {
this->m_out << "Material " << material.filename << ":";
this->increaseIndentation ();
this->lineEnd ();
this->m_out << "Passes count: " << material.passes.size ();
this->increaseIndentation ();
for (const auto& pass : material.passes) {
this->lineEnd ();
this->printMaterialPass (*pass);
}
this->decreaseIndentation ();
this->decreaseIndentation ();
}
void StringPrinter::printMaterialPass (const MaterialPass& materialPass) {
this->m_out << "Pass " << materialPass.shader << ":";
this->increaseIndentation ();
this->lineEnd ();
this->m_out << "Depth test: " << materialPass.depthtest;
this->lineEnd ();
this->m_out << "Depth write: " << materialPass.depthwrite;
this->lineEnd ();
this->m_out << "Blend mode: " << materialPass.blending;
this->lineEnd ();
this->m_out << "Culling mode: " << materialPass.cullmode;
if (!materialPass.textures.empty ()) {
this->lineEnd ();
this->m_out << "Textures count: " << materialPass.textures.size ();
this->increaseIndentation ();
for (const auto& texture : materialPass.textures) {
this->lineEnd ();
this->m_out << "Texture " << texture.first << ": " << texture.second;
}
this->decreaseIndentation ();
}
if (!materialPass.combos.empty ()) {
this->lineEnd ();
this->m_out << "Combos count: " << materialPass.combos.size ();
this->increaseIndentation ();
for (const auto& combo : materialPass.combos) {
this->lineEnd ();
this->m_out << "Combo " << combo.first << ": " << combo.second;
}
this->decreaseIndentation ();
}
this->decreaseIndentation ();
}
void StringPrinter::indentation () {
for (int i = 0; i < this->m_level; i++)
this->m_out << this->m_indentationCharacter;
}
void StringPrinter::lineEnd () {
this->m_out << "\n";
this->indentation ();
}
void StringPrinter::increaseIndentation () {
this->m_level ++;
}
void StringPrinter::decreaseIndentation () {
this->m_level --;
}
std::string StringPrinter::str () {
return this->m_buffer.str ();
}

View File

@ -0,0 +1,130 @@
#pragma once
#include <string>
#include <sstream>
#include "WallpaperEngine/Data/Model/Types.h"
namespace WallpaperEngine::Data::Dumpers {
using namespace WallpaperEngine::Data::Model;
class StringPrinter {
public:
explicit StringPrinter (std::string indentationCharacter = "\t");
~StringPrinter () = default;
/**
* @return The contents of the pretty printer buffer
*/
std::string str ();
/**
* Prints the information of the given wallpaper
*
* @param wallpaper
*/
void printWallpaper (const Wallpaper& wallpaper);
/**
* Prints the information of the given object
*
* @param object
*/
void printObject (const Object& object);
/**
* Prints the information of the given image
*
* @param image
*/
void printImage (const Image& image);
/**
* Prints the information of the given sound
*
* @param sound
*/
void printSound (const Sound& sound);
/**
* Prints the information of the given model
*
* @param model
*/
void printModel (const ModelStruct& model);
/**
* Prints the information of the given image effect
*
* @param imageEffect
*/
void printImageEffect (const ImageEffect& imageEffect);
/**
* Prints the information of the given image effect pass
*
* @param imageEffectPass
*/
void printImageEffectPassOverride (const ImageEffectPassOverride& imageEffectPass);
/**
* Prints the information of the given FBO
*
* @param fbo
*/
void printFBO (const FBO& fbo);
/**
* Prints the information of the given material
*
* @param material
*/
void printMaterial (const Material& material);
/**
* Prints the information of the given material pass
*
* @param materialPass
*/
void printMaterialPass (const MaterialPass& materialPass);
/**
* Prints the information of the given effect
*
* @param effect
*/
void printEffect (const Effect& effect);
/**
* Prints the information of the given effect
*
* @param effectPass
*/
void printEffectPass (const EffectPass& effectPass);
private:
/**
* Printss the current identation level
*/
void indentation ();
/**
* Prints an end of line and indentates the next line to the aproppiate level
*/
void lineEnd ();
/**
* Increases the indentation level and prints a new line up to the specified level
*/
void increaseIndentation ();
/**
* Decreases the indentation level and prints a new line up to the specified level
*/
void decreaseIndentation ();
int m_level = 0;
std::stringbuf m_buffer = {};
std::ostream m_out;
std::string m_indentationCharacter;
};
} // namespace WallpaperEngine::Data::Dumpers

View File

@ -0,0 +1,12 @@
#include "JSON.h"
#include "WallpaperEngine/Data/Parsers/UserSettingParser.h"
using namespace WallpaperEngine::Data::JSON;
using namespace WallpaperEngine::Data::Model;
UserSettingSharedPtr JsonExtensions::user (const std::string& key, const Properties& properties) const {
const auto value = this->require (key, "User setting without default value must be present");
return UserSettingParser::parse (value, properties);
}

View File

@ -0,0 +1,168 @@
#pragma once
#include <glm/detail/qualifier.hpp>
#include <glm/detail/type_vec1.hpp>
#include <nlohmann/json.hpp>
#include <optional>
#include <string>
#include <utility>
#include "WallpaperEngine/Data/Builders/UserSettingBuilder.h"
#include "WallpaperEngine/Data/Builders/VectorBuilder.h"
#include "WallpaperEngine/Data/Model/Types.h"
#include "WallpaperEngine/Logging/CLog.h"
namespace WallpaperEngine::Data::Parsers {
class UserSettingParser;
}
namespace WallpaperEngine::Data::JSON {
using namespace WallpaperEngine::Data::Builders;
using namespace WallpaperEngine::Data::Parsers;
using namespace WallpaperEngine::Data::Model;
// sfinae to detect the type for template specialization
template <typename T>
struct is_glm_vec : std::false_type {};
template <glm::length_t L, typename S, glm::qualifier Q>
struct is_glm_vec<glm::vec<L, S, Q>> : std::true_type {};
// traits used to guess the type of the vector and it's length
template <typename T>
struct GlmVecTraits;
template <glm::length_t L, typename T, glm::qualifier Q>
struct GlmVecTraits<glm::vec<L, T, Q>> {
static constexpr int length = L;
using type = T;
static constexpr glm::qualifier qualifier = Q;
};
class JsonExtensions;
using JSON = nlohmann::basic_json<
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
JsonExtensions
>;
/**
* Small extensions class that is used as base class of nlohmann's implementation.
*
* Provides shorthand methods to reduce the amount of handling required for specific situations
* (mainly throwing readable exceptions when a value is missing, optional/default values, user settings...)
*/
class JsonExtensions {
public:
using base_type = JSON;
template <typename T, typename std::enable_if_t<is_glm_vec<T>::value, int> = 0>
[[nodiscard]] T get () const {
constexpr int length = GlmVecTraits<T>::length;
constexpr glm::qualifier qualifier = GlmVecTraits<T>::qualifier;
// call the specialized version of the function
return get <length, typename GlmVecTraits<T>::type, qualifier> ();
}
template <int length, typename type, glm::qualifier qualifier>
[[nodiscard]] glm::vec <length, type, qualifier> get () const {
return VectorBuilder::parse <length, type, qualifier> (this->base ().get <std::string> ());
}
[[nodiscard]] base_type require (const std::string& key, const std::string& message) const {
auto base = this->base ();
auto it = base.find (key);
if (it == base.end ()) {
sLog.exception (message);
}
return *it;
}
template <typename T>
[[nodiscard]] T require (const std::string& key, const std::string& message) const {
auto base = this->base ();
auto it = base.find (key);
if (it == base.end ()) {
sLog.exception (message);
}
return (*it);
}
[[nodiscard]] std::optional <base_type> optional (const std::string& key) const noexcept {
auto base = this->base ();
auto it = base.find (key);
auto result = std::optional<base_type> {};
if (it != base.end ()) {
result.emplace (*it);
}
return result;
}
template <typename T>
[[nodiscard]] std::optional <T> optional (const std::string& key) const noexcept {
auto base = this->base ();
auto it = base.find (key);
if (it == base.end ()) {
return std::nullopt;
}
return *it;
}
template <typename T>
[[nodiscard]] T optional (const std::string& key, T defaultValue) const noexcept {
auto base = this->base ();
auto it = base.find (key);
if (it == base.end ()) {
return defaultValue;
}
return (*it);
}
[[nodiscard]] UserSettingSharedPtr user (const std::string& key, const Properties& properties) const;
template <typename T>
[[nodiscard]] UserSettingSharedPtr user (const std::string& key, const Properties& properties, T defaultValue) const {
const auto value = this->optional (key);
if (!value.has_value ()) {
return UserSettingBuilder::fromValue <T> (defaultValue);
}
// performs a second lookup, but handles the actual call to UserSettingParser outside of this header
// this resolving the include loop
return this->user (key, properties);
}
template <int length, typename type, glm::qualifier qualifier>
operator glm::vec <length, type, qualifier> () const {
return get <length, type, qualifier> ();
}
template <typename T, typename std::enable_if_t<is_glm_vec<T>::value> = 0>
operator T () const {
constexpr int length = GlmVecTraits<T>::length;
constexpr glm::qualifier qualifier = GlmVecTraits<T>::qualifier;
// call the specialized version of the function
return operator glm::vec <length, typename GlmVecTraits<T>::type, qualifier> ();
}
private:
/**
* @return The base json object to be used by the extension methods
*/
[[nodiscard]] const base_type& base () const {
return *static_cast<const base_type*> (this);
}
};
} // namespace WallpaperEngine::Data::JSON

View File

@ -0,0 +1,54 @@
#pragma once
#include <string>
#include "Types.h"
namespace WallpaperEngine::Data::Model {
enum PassCommandType {
Command_Copy = 0,
Command_Swap = 1
};
struct FBO {
std::string name;
std::string format;
float scale;
};
struct PassCommand {
/** The type of command to execute */
PassCommandType command;
/** The target of the command (where to draw to) */
std::string target;
/** The source of the command (where to draw from) */
std::string source;
};
struct EffectPass {
/** The material to use for this effect's pass */
MaterialUniquePtr material;
/** Texture bindings for this effect's pass */
std::map <int, std::string> binds;
/** The command this material executes (if specified) */
std::optional <PassCommand> command;
/** The target this material renders to (if specified) */
std::optional <std::string> target;
};
struct Effect {
/** Effect's name for the UI */
std::string name;
/** Effect's description for the UI */
std::string description;
/** Effect's group for the UI */
std::string group;
/** Effect's preview project */
std::string preview;
/** Effect's dependencies */
std::vector <std::string> dependencies;
/** The different passes for this effect */
std::vector <EffectPassUniquePtr> passes;
/** The fbos declared by this effect */
std::vector <FBOUniquePtr> fbos;
};
} // namespace WallpaperEngine::Data::Model

View File

@ -0,0 +1,36 @@
#pragma once
#include <map>
#include <vector>
#include <string>
#include <optional>
#include "Types.h"
namespace WallpaperEngine::Data::Model {
struct MaterialPass {
// TODO: WRITE ENUMS FOR THESE
/** Blending mode */
std::string blending;
/** Culling mode */
std::string cullmode;
/** Depth test mode */
std::string depthtest;
/** Depth write mode */
std::string depthwrite;
/** Shader file to use for this pass */
std::string shader;
/** List of textures defined for this pass */
std::map <int, std::string> textures;
/** The combos and their values to pass onto the shader */
std::map <std::string, int> combos;
};
struct Material {
/** The name of the file this material is defined in */
std::string filename;
/** The passes that compose this material */
std::vector <MaterialPassUniquePtr> passes;
};
} // namespace WallpaperEngine::Data::Model

View File

@ -0,0 +1,27 @@
#pragma once
#include "Types.h"
namespace WallpaperEngine::Data::Model {
// TODO: FIND A BETTER NAMING SO THIS DOESN'T COLLIDE WITH THE NAMESPACE ITSELF
struct ModelStruct {
/** The filename of the model */
std::string filename;
/** The material used for this model */
MaterialUniquePtr material;
/** Whether this model is a solid layer */
bool solidlayer;
/** Whether this model is a fullscreen layer */
bool fullscreen;
/** Whether this model is a passthrough layer */
bool passthrough;
/** Whether this models's size should be determined automatically or not */
bool autosize;
/** Whether this models's padding should be disabled or not */
bool nopadding;
/** Not sure what's used for */
std::optional<int> width;
/** Not sure what's used for */
std::optional<int> height;
};
} // namespace WallpaperEngine::Data::Model

View File

@ -0,0 +1,122 @@
#pragma once
#include <utility>
#include <vector>
#include <string>
#include <glm/glm.hpp>
#include "Types.h"
#include "Effect.h"
#include "Material.h"
#include "Model.h"
#include "WallpaperEngine/Data/Utils/TypeCaster.h"
namespace WallpaperEngine::Data::Model {
using TypeCaster = WallpaperEngine::Data::Utils::TypeCaster;
struct ObjectData {
int id;
std::string name;
std::vector <int> dependencies;
};
//TODO: CHECK IF THE SEMANTICS FOR MEMORY ARE RIGHT HERE
/**
* Base class for all objects, represents a single object in the scene
*
* @see Image
* @see Sound
* @see Particle
* @see Text
* @see Light
*/
class Object : public TypeCaster, public ObjectData {
public:
explicit Object (ObjectData data) noexcept : ObjectData (std::move (data)), TypeCaster () {};
~Object () override = default;
};
/**
* Overrides effect's passes configuration
*
* @see ImageEffect
* @see EffectPass
*/
struct ImageEffectPassOverride {
int id;
ComboMap combos;
ShaderConstantMap constants;
std::map <int, std::string> textures;
};
/**
* Override information for an specific effect
*
* @see ImageEffect
* @see Effect
* @see EffectPass
* @see ImageEffectPass
*/
struct ImageEffect {
/** Not sure what it's used for */
int id;
/** Effect's name for the editor */
std::string name;
/** If this effect is visible or not */
UserSettingSharedPtr visible;
/** Pass overrides to apply to the effect's passes */
std::vector <ImageEffectPassOverrideUniquePtr> passOverrides;
/** The effect definition */
EffectUniquePtr effect;
};
struct ImageData {
/** The point of origin of the image */
UserSettingSharedPtr origin;
/** The scale of the image */
UserSettingSharedPtr scale;
/** The rotation of the image */
UserSettingSharedPtr angles;
/** If the image is visible or not */
UserSettingSharedPtr visible;
/** The alpha of the image */
UserSettingSharedPtr alpha;
/** The color of the image */
UserSettingSharedPtr color;
// TODO: WRITE A COUPLE OF ENUMS FOR THIS
/** The alignment of the image */
std::string alignment;
/** The size of the image in pixels */
glm::vec2 size;
/** Parallax depth used for parallax scrolling */
glm::vec2 parallaxDepth;
/** The color blending mode for this image */
int colorBlendMode;
/** The brightness of the image */
float brightness;
/** The material in use for this image */
ModelUniquePtr model;
/** The effects applied to this image after the material is rendered */
std::vector <ImageEffectUniquePtr> effects;
};
class Image : public Object, public ImageData {
public:
explicit Image (ObjectData data, ImageData imageData) noexcept : Object (std::move(data)), ImageData (std::move (imageData)) {};
~Image () override = default;
};
struct SoundData {
/** Playback mode, loop, */
std::optional <std::string> playbackmode;
std::vector <std::string> sounds;
};
class Sound : public Object, public SoundData {
public:
explicit Sound (ObjectData data, SoundData soundData) noexcept : Object (std::move(data)), SoundData (std::move (soundData)) {};
~Sound () override = default;
};
} // namespace WallpaperEngine::Data::Model

View File

@ -0,0 +1,38 @@
#pragma once
#include <string>
#include <memory>
#include "Types.h"
namespace WallpaperEngine::Data::Model {
using json = WallpaperEngine::Data::JSON::JSON;
/**
* Represents a wallpaper engine project
*/
struct Project {
public:
enum Type {
Type_Scene = 0,
Type_Web = 1,
Type_Video = 2,
Type_Unknown = 3
};
/** Wallpapers title */
std::string title;
/** Wallpaper's type */
Type type;
/** Workshop ID of the background or a negative id if not present */
std::string workshopId;
/** Indicates if the background uses audio processing or not */
bool supportsAudioProcessing;
/** All the available properties that the project defines for the user to change */
Properties properties;
/** The wallpaper this project defines */
WallpaperSharedPtr wallpaper;
/** VFS to access the project's files */
ContainerWeakPtr container;
};
};

View File

@ -0,0 +1,86 @@
#pragma once
#include <memory>
#include "WallpaperEngine/Assets/CContainer.h"
#include "WallpaperEngine/Core/DynamicValues/CDynamicValue.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstant.h"
#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantProperty.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/Projects/CProperty.h"
namespace WallpaperEngine::Data::Model {
struct Project;
class Wallpaper;
class Scene;
class Web;
class Video;
class UserSetting;
class Object;
class Sound;
class Image;
struct ImageEffect;
struct ImageEffectPassOverride;
class Particle;
struct Material;
struct MaterialPass;
struct FBO;
struct Effect;
struct EffectPass;
struct ModelStruct;
// TODO: REMOVE ONCE THESE ARE RENAMED AND MOVED
using Container = WallpaperEngine::Assets::CContainer;
using ShaderConstant = WallpaperEngine::Core::Objects::Effects::Constants::CShaderConstant;
using ShaderConstantProperty = WallpaperEngine::Core::Objects::Effects::Constants::CShaderConstantProperty;
using ShaderConstantFloat = WallpaperEngine::Core::Objects::Effects::Constants::CShaderConstantFloat;
using ShaderConstantInteger = WallpaperEngine::Core::Objects::Effects::Constants::CShaderConstantInteger;
using ShaderConstantVector2 = WallpaperEngine::Core::Objects::Effects::Constants::CShaderConstantVector2;
using ShaderConstantVector3 = WallpaperEngine::Core::Objects::Effects::Constants::CShaderConstantVector3;
using ShaderConstantVector4 = WallpaperEngine::Core::Objects::Effects::Constants::CShaderConstantVector4;
using Property = WallpaperEngine::Core::Projects::CProperty;
using PropertySharedPtr = std::shared_ptr <Property>;
using PropertyWeakPtr = std::weak_ptr <Property>;
using Properties = std::map <std::string, PropertySharedPtr>;
using DynamicValue = WallpaperEngine::Core::DynamicValues::CDynamicValue;
using DynamicValueUniquePtr = std::unique_ptr <DynamicValue>;
using DynamicValueSharedPtr = std::shared_ptr <DynamicValue>;
using DynamicValueWeakPtr = std::weak_ptr <DynamicValue>;
using UserSettingSharedPtr = std::shared_ptr <UserSetting>;
using UserSettingWeakPtr = std::weak_ptr <UserSetting>;
using ShaderConstantUniquePtr = std::unique_ptr <ShaderConstant>;
// TODO: UP TO THIS POINT
using ShaderConstantMap = std::map <std::string, ShaderConstantUniquePtr>;
using ProjectSharedPtr = std::shared_ptr <Project>;
using WallpaperSharedPtr = std::shared_ptr <Wallpaper>;
using WallpaperWeakPtr = std::weak_ptr <Wallpaper>;
using ProjectWeakPtr = std::weak_ptr <Project>;
using ContainerSharedPtr = std::shared_ptr <Container>;
using ContainerWeakPtr = std::weak_ptr <Container>;
using SceneSharedPtr = std::shared_ptr <Scene>;
using WebSharedPtr = std::shared_ptr <Web>;
using VideoSharedPtr = std::shared_ptr <Video>;
using ObjectUniquePtr = std::unique_ptr <Object>;
using SoundUniquePtr = std::unique_ptr <Sound>;
using ImageUniquePtr = std::unique_ptr <Image>;
using ParticleUniquePtr = std::unique_ptr <Particle>;
using MaterialUniquePtr = std::unique_ptr <Material>;
using MaterialPassUniquePtr = std::unique_ptr <MaterialPass>;
using EffectUniquePtr = std::unique_ptr <Effect>;
using ImageEffectUniquePtr = std::unique_ptr <ImageEffect>;
using ImageEffectPassOverrideUniquePtr = std::unique_ptr <ImageEffectPassOverride>;
using EffectPassUniquePtr = std::unique_ptr <EffectPass>;
using FBOUniquePtr = std::unique_ptr <FBO>;
using ModelUniquePtr = std::unique_ptr <ModelStruct>;
using ObjectMap = std::map<int, ObjectUniquePtr>;
using ComboMap = std::map<std::string, int>;
using TextureMap = std::map<int, std::string>;
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <memory>
#include <optional>
#include "Types.h"
#include "WallpaperEngine/Core/DynamicValues/CDynamicValue.h"
#include "WallpaperEngine/Core/Projects/CProperty.h"
namespace WallpaperEngine::Data::Model {
struct ConditionInfo {
public:
std::string name;
std::string condition;
};
struct UserSetting {
public:
/**
* The value of this setting, can be a few different things:
* - a value connected to the property
* - a default value
* - a static value
*/
DynamicValueUniquePtr value;
/** The property this setting takes the value from (if specified) */
PropertyWeakPtr property;
/** Condition required for this setting, this should be possible to run in JS' V8 */
std::optional <ConditionInfo> condition;
};
} // namespace WallpaperEngine::Data::Model

View File

@ -0,0 +1,119 @@
#pragma once
#include <memory>
#include <glm/glm.hpp>
#include <utility>
#include "Types.h"
#include "Object.h"
#include "WallpaperEngine/Data/Utils/TypeCaster.h"
#include "WallpaperEngine/Data/JSON.h"
namespace WallpaperEngine::Data::Model {
using json = WallpaperEngine::Data::JSON::JSON;
using TypeCaster = WallpaperEngine::Data::Utils::TypeCaster;
struct WallpaperData {
std::string filename;
ProjectWeakPtr project;
};
class Wallpaper : public TypeCaster, public WallpaperData {
public:
explicit Wallpaper (WallpaperData data) noexcept : WallpaperData (std::move(data)), TypeCaster () {};
~Wallpaper () override = default;
};
class Video : public Wallpaper {
public:
explicit Video (WallpaperData data) noexcept : Wallpaper (std::move(data)) {}
~Video () override = default;
};
class Web : public Wallpaper {
public:
explicit Web (WallpaperData data) noexcept : Wallpaper (std::move(data)) {}
~Web () override = default;
};
struct SceneData {
struct {
glm::vec3 ambient;
glm::vec3 skylight;
UserSettingSharedPtr clear;
} colors;
/**
* Camera configuration
*/
struct {
/** Enable fade effect */
bool fade;
/** Used by the software to allow the users to preview the background or not? */
bool preview;
/**
* Bloom effect configuration
*/
struct {
/** If bloom is enabled or not */
UserSettingSharedPtr enabled;
/** Bloom's strength to pass onto the shader */
UserSettingSharedPtr strength;
/** Bloom's threshold to pass onto the shader */
UserSettingSharedPtr threshold;
} bloom;
/**
* Parallax effect configuration
*/
struct {
bool enabled;
float amount;
float delay;
float mouseInfluence;
} parallax;
/**
* Shake effect configuration
*/
struct {
bool enabled;
float amplitude;
float roughness;
float speed;
} shake;
/**
* Position configuration
*/
struct {
glm::vec3 center;
glm::vec3 eye;
glm::vec3 up;
} configuration;
/**
* Projection information
*/
struct {
int width;
int height;
bool isAuto;
} projection;
} camera;
ObjectMap objects;
ProjectWeakPtr project;
};
class Scene : public Wallpaper, public SceneData {
public:
explicit Scene (WallpaperData data, SceneData sceneData) noexcept : SceneData (std::move(sceneData)), Wallpaper (data) {}
~Scene () override = default;
// TODO: ADD OBJECTS HERE
};
} // namespace WallpaperEngine::Data::Model

View File

@ -0,0 +1,95 @@
#include "EffectParser.h"
#include "MaterialParser.h"
#include "WallpaperEngine/Data/Model/Material.h"
#include "WallpaperEngine/Data/Model/Effect.h"
#include "WallpaperEngine/Data/Model/Project.h"
using namespace WallpaperEngine::Data::Parsers;
using namespace WallpaperEngine::Data::Model;
EffectUniquePtr EffectParser::load (const ProjectWeakPtr& project, const std::string& filename) {
const auto effectJson = JSON::parse (project.lock ()->container.lock ()->readFileAsString (filename));
return parse (effectJson, project);
}
EffectUniquePtr EffectParser::parse (const JSON& it, const ProjectWeakPtr& project) {
const auto dependencies = it.optional ("dependencies");
return std::make_unique <Effect> (Effect {
.name = it.optional <std::string> ("name", ""),
.description = it.optional <std::string> ("description", ""),
.group = it.optional <std::string> ("group", ""),
.preview = it.optional <std::string> ("preview", ""),
.dependencies = dependencies.has_value () ? parseDependencies (*dependencies) : std::vector <std::string> {},
.passes = parseEffectPasses (it.require ("passes", "Effect file must have passes"), project),
});
}
std::vector <std::string> EffectParser::parseDependencies (const JSON& it) {
std::vector <std::string> result = {};
if (!it.is_array ()) {
return result;
}
for (const auto& cur : it) {
result.push_back (cur);
}
return result;
}
std::vector <EffectPassUniquePtr> EffectParser::parseEffectPasses (const JSON& it, const ProjectWeakPtr& project) {
std::vector <EffectPassUniquePtr> result = {};
if (!it.is_array ()) {
return result;
}
for (const auto& cur : it) {
const auto binds = cur.optional ("binds");
std::optional <PassCommand> command = std::nullopt;
if (cur.contains ("command")) {
command = {
.command = cur.require <std::string> ("command", "Material command must have a command") == "copy"
? Command_Copy
: Command_Swap,
.target = cur.require <std::string> ("target", "Material command must have a target"),
.source = cur.require <std::string> ("source", "Material command must have a source"),
};
}
result.push_back (std::make_unique <EffectPass> (EffectPass {
.material = MaterialParser::load (project, cur.require <std::string> ("material", "Effect pass must have a material")),
.binds = binds.has_value () ? parseBinds (binds.value ()) : std::map<int, std::string> {},
.command = command,
// target is a bit special: if the material is a command this will be nullopt
// and the actual target will be set in the command itself, otherwise it will
// be set to whatever is in the json, which is not required to be present for
// normal materials
.target = command.has_value () ? std::nullopt : cur.optional <std::string> ("target"),
}));
}
return result;
}
std::map <int, std::string> EffectParser::parseBinds (const JSON& it) {
std::map <int, std::string> result = {};
if (!it.is_array ()) {
return result;
}
for (const auto& cur : it) {
result.emplace (
cur.require ("index", "Texture binds must have an index"),
cur.require ("name", "Texture bind must name the FBO that should be used")
);
}
return result;
}

View File

@ -0,0 +1,20 @@
#pragma once
#include "WallpaperEngine/Data/JSON.h"
#include "WallpaperEngine/Data/Model/Types.h"
namespace WallpaperEngine::Data::Parsers {
using JSON = WallpaperEngine::Data::JSON::JSON;
using namespace WallpaperEngine::Data::Model;
class EffectParser {
public:
static EffectUniquePtr load (const ProjectWeakPtr& project, const std::string& filename);
private:
static EffectUniquePtr parse (const JSON& it, const ProjectWeakPtr& project);
static std::vector <std::string> parseDependencies (const JSON& it);
static std::vector <EffectPassUniquePtr> parseEffectPasses (const JSON& it, const ProjectWeakPtr& project);
static std::map <int, std::string> parseBinds (const JSON& it);
};
} // namespace WallpaperEngine::Data::Parsers

View File

@ -0,0 +1,86 @@
#include "MaterialParser.h"
#include "WallpaperEngine/Data/Model/Material.h"
#include "WallpaperEngine/Data/Model/Project.h"
using namespace WallpaperEngine::Data::Parsers;
using namespace WallpaperEngine::Data::Model;
MaterialUniquePtr MaterialParser::load (const ProjectWeakPtr& project, const std::string& filename) {
const auto materialJson = JSON::parse (project.lock ()->container.lock ()->readFileAsString (filename));
return parse (materialJson, project, filename);
}
MaterialUniquePtr MaterialParser::parse (const JSON& it, const ProjectWeakPtr& project, const std::string& filename) {
return std::make_unique <Material> (Material {
.filename = filename,
//TODO: PARSE PASSES
.passes = parsePasses (it.require ("passes", "Material must have passes to render"), project),
});
}
std::vector <MaterialPassUniquePtr> MaterialParser::parsePasses (const JSON& it, const ProjectWeakPtr& project) {
std::vector <MaterialPassUniquePtr> result = {};
if (!it.is_array ()) {
return result;
}
for (const auto& cur : it) {
result.push_back (parsePass (cur, project));
}
return result;
}
MaterialPassUniquePtr MaterialParser::parsePass (const JSON& it, const ProjectWeakPtr& project) {
const auto textures = it.optional ("textures");
const auto combos = it.optional ("combos");
const auto constants = it.optional ("constants");
return std::make_unique <MaterialPass>(MaterialPass {
//TODO: REMOVE THIS UGLY STD::STRING CREATION
.blending = it.optional ("blending", std::string ("normal")),
.cullmode = it.optional ("cullmode", std::string ("disabled")),
.depthtest = it.optional ("depthtest", std::string ("disabled")),
.depthwrite = it.optional ("depthwrite", std::string ("disabled")),
.shader = it.require <std::string> ("shader", "Material pass must have a shader"),
.textures = textures.has_value () ? parseTextures (*textures) : std::map <int, std::string> {},
.combos = combos.has_value () ? parseCombos (*combos) : std::map <std::string, int> {},
});
}
std::map <int, std::string> MaterialParser::parseTextures (const JSON& it) {
std::map <int, std::string> result = {};
if (!it.is_array ()) {
return result;
}
int index = 0;
for (const auto& cur : it) {
if (!it.is_null ()) {
result.emplace (index, cur);
}
index++;
}
return result;
}
std::map <std::string, int> MaterialParser::parseCombos (const JSON& it) {
std::map <std::string, int> result = {};
if (!it.is_object ()) {
return result;
}
for (const auto& cur : it.items ()) {
result.emplace (cur.key (), cur.value ());
}
return result;
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "WallpaperEngine/Data/JSON.h"
#include "WallpaperEngine/Data/Model/Types.h"
namespace WallpaperEngine::Data::Parsers {
using JSON = WallpaperEngine::Data::JSON::JSON;
using namespace WallpaperEngine::Data::Model;
class MaterialParser {
public:
static MaterialUniquePtr load (const ProjectWeakPtr& project, const std::string& filename);
static MaterialUniquePtr parse (const JSON& it, const ProjectWeakPtr& project, const std::string& filename);
static std::vector <MaterialPassUniquePtr> parsePasses (const JSON& it, const ProjectWeakPtr& project);
static MaterialPassUniquePtr parsePass (const JSON& it, const ProjectWeakPtr& project);
static std::map <int, std::string> parseTextures (const JSON& it);
static std::map <std::string, int> parseCombos (const JSON& it);
};
} // namespace WallpaperEngine::Data::Parsers

View File

@ -0,0 +1,31 @@
#include "ModelParser.h"
#include "MaterialParser.h"
#include "WallpaperEngine/Data/Model/Model.h"
#include "WallpaperEngine/Data/Model/Project.h"
#include "WallpaperEngine/Data/Model/Material.h"
using namespace WallpaperEngine::Data::Parsers;
using namespace WallpaperEngine::Data::Model;
ModelUniquePtr ModelParser::load (const ProjectWeakPtr& project, const std::string& filename) {
const auto model = JSON::parse (project.lock ()->container.lock ()->readFileAsString (filename));
return parse (model, project, filename);
}
ModelUniquePtr ModelParser::parse (const JSON& file, const ProjectWeakPtr& project, const std::string& filename) {
const auto material = file.require <std::string> ("material", "Model must have a material");
return std::make_unique <ModelStruct> (ModelStruct {
.filename = filename,
.material = MaterialParser::load (project, material),
.solidlayer = file.optional ("solidlayer", false),
.fullscreen = file.optional ("fullscreen", false),
.passthrough = file.optional ("passthrough", false),
.autosize = file.optional ("autosize", false),
.nopadding = file.optional ("nopadding", false),
.width = file.optional <int> ("width"),
.height = file.optional <int> ("height"),
});
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "WallpaperEngine/Data/JSON.h"
#include "WallpaperEngine/Data/Model/Types.h"
namespace WallpaperEngine::Data::Parsers {
using JSON = WallpaperEngine::Data::JSON::JSON;
using namespace WallpaperEngine::Data::Model;
class ModelParser {
public:
static ModelUniquePtr load (const ProjectWeakPtr& project, const std::string& filename);
static ModelUniquePtr parse (const JSON& file, const ProjectWeakPtr& project, const std::string& filename);
};
} // namespace WallpaperEngine::Data::Parsers

View File

@ -0,0 +1,187 @@
#include "ObjectParser.h"
#include "ModelParser.h"
#include "EffectParser.h"
#include "ShaderConstantParser.h"
#include "WallpaperEngine/Data/Model/Object.h"
#include "WallpaperEngine/Data/Model/Project.h"
#include "WallpaperEngine/Logging/CLog.h"
using namespace WallpaperEngine::Data::Parsers;
using namespace WallpaperEngine::Data::Model;
ObjectUniquePtr ObjectParser::parse (const JSON& it, const ProjectWeakPtr& project) {
const auto imageIt = it.find ("image");
const auto soundIt = it.find ("sound");
const auto particleIt = it.find ("particle");
const auto textIt = it.find ("text");
const auto lightIt = it.find ("light");
const auto basedata = ObjectData {
.id = it.require <int> ("id", "Object must have an id"),
.name = it.require <std::string> ("name", "Object must have a name"),
.dependencies = parseDependencies (it),
};
if (imageIt != it.end () && imageIt->is_string ()) {
return parseImage (it, project, basedata, *imageIt);
} else if (soundIt != it.end () && soundIt->is_array ()) {
return parseSound (it, project, basedata);
} else if (particleIt != it.end ()) {
sLog.error ("Particle objects are not supported yet");
} else if (textIt != it.end ()) {
sLog.error ("Text objects are not supported yet");
} else if (lightIt != it.end ()) {
sLog.error ("Light objects are not supported yet");
} else {
// dump the object for now, might want to change later
sLog.exception ("Unknown object type found: ", it.dump ());
}
return std::make_unique <Object> (basedata);
}
std::vector<int> ObjectParser::parseDependencies (const JSON& it) {
const auto dependenciesIt = it.find ("dependencies");
if (dependenciesIt == it.end () || !dependenciesIt->is_array ()) {
return {};
}
std::vector<int> result = {};
for (const auto& cur : *dependenciesIt) {
result.push_back (cur);
}
return result;
}
SoundUniquePtr ObjectParser::parseSound (const JSON& it, const ProjectWeakPtr& project, ObjectData base) {
const auto soundIt = it.require ("sound", "Object must have a sound");
std::vector<std::string> sounds = {};
for (const auto& cur : soundIt) {
sounds.push_back (cur);
}
return std::make_unique <Sound> (
std::move (base),
SoundData {
.playbackmode = it.optional <std::string> ("playbackmode"),
.sounds = sounds,
}
);
}
ImageUniquePtr ObjectParser::parseImage (
const JSON& it, const ProjectWeakPtr& project, ObjectData base, const std::string& image) {
const auto& properties = project.lock ()->properties;
const auto& effects = it.optional ("effects");
return std::make_unique <Image> (
std::move (base),
ImageData {
.origin = it.user ("origin", properties, glm::vec3 (0.0f)),
.scale = it.user ("scale", properties, glm::vec3 (1.0f)),
.angles = it.user ("angles", properties, glm::vec3 (0.0)),
.visible = it.user ("visible", properties, true),
.alpha = it.user ("alpha", properties, 1.0f),
.color = it.user ("color", properties, glm::vec3 (1.0f)),
.alignment = it.optional ("alignment", std::string ("center")),
.size = it.optional ("size", glm::vec2 (0.0f)),
.parallaxDepth = it.optional ("parallaxDepth", glm::vec2 (0.0f)),
.colorBlendMode = it.optional ("colorBlendMode", 0),
.brightness = it.optional ("brightness", 1.0f),
.model = ModelParser::load (project, image),
.effects = effects.has_value () ? parseEffects (*effects, project) : std::vector <ImageEffectUniquePtr> {},
}
);
}
std::vector <ImageEffectUniquePtr> ObjectParser::parseEffects (const JSON& it, const ProjectWeakPtr& project) {
if (!it.is_array ()) {
return {};
}
std::vector <ImageEffectUniquePtr> result = {};
for (const auto& cur : it) {
result.push_back (parseEffect (cur, project));
}
return result;
}
ImageEffectUniquePtr ObjectParser::parseEffect (const JSON& it, const ProjectWeakPtr& project) {
const auto& passsOverrides = it.optional ("passes");
return std::make_unique <ImageEffect> (ImageEffect {
.id = it.require <int> ("id", "Image effect must have an id"),
.name = it.require <std::string> ("name", "Image effect must have a name"),
.visible = it.user ("visible", project.lock ()->properties, true),
.passOverrides = passsOverrides.has_value () ? parseEffectPassOverrides (passsOverrides.value (), project) : std::vector <ImageEffectPassOverrideUniquePtr> {},
.effect = EffectParser::load (project, it.require ("file", "Image effect must have an effect"))
});
}
std::vector <ImageEffectPassOverrideUniquePtr> ObjectParser::parseEffectPassOverrides (const JSON& it, const ProjectWeakPtr& project) {
if (!it.is_array ()) {
return {};
}
std::vector <ImageEffectPassOverrideUniquePtr> result = {};
for (const auto& cur : it) {
result.push_back (parseEffectPass (cur, project));
}
return result;
}
ImageEffectPassOverrideUniquePtr ObjectParser::parseEffectPass (const JSON& it, const ProjectWeakPtr& project) {
const auto& combos = it.optional ("combos");
const auto& textures = it.optional ("textures");
const auto& constants = it.optional ("constantshadervalues");
// TODO: PARSE CONSTANT SHADER VALUES AND FIND REFS?
return std::make_unique <ImageEffectPassOverride> (ImageEffectPassOverride {
.id = it.require <int> ("id", "Image effect pass must have an id"),
.combos = combos.has_value () ? parseComboMap (combos.value ()) : ComboMap {},
.constants = constants.has_value () ? ShaderConstantParser::parse (constants.value (), project) : ShaderConstantMap {},
.textures = textures.has_value () ? parseTextureMap (textures.value ()) : TextureMap {},
});
}
TextureMap ObjectParser::parseTextureMap (const JSON& it) {
if (!it.is_array ()) {
return {};
}
TextureMap result = {};
int textureIndex = -1;
for (const auto& cur : it) {
textureIndex ++;
if (cur.is_null ()) {
continue;
}
result.emplace (textureIndex, cur);
}
return result;
}
ComboMap ObjectParser::parseComboMap (const JSON& it) {
if (!it.is_object ()) {
return {};
}
ComboMap result = {};
for (const auto& cur : it.items ()) {
result.emplace (cur.key (), cur.value ());
}
return result;
}

View File

@ -0,0 +1,32 @@
#pragma once
#include <vector>
#include "WallpaperEngine/Data/JSON.h"
#include "WallpaperEngine/Data/Model/Object.h"
namespace WallpaperEngine::Data::Model {
struct ObjectData;
}
namespace WallpaperEngine::Data::Parsers {
using JSON = WallpaperEngine::Data::JSON::JSON;
using namespace WallpaperEngine::Data::Model;
class ObjectParser {
public:
static ObjectUniquePtr parse (const JSON& it, const ProjectWeakPtr& project);
private:
static std::vector<int> parseDependencies (const JSON& it);
static SoundUniquePtr parseSound (const JSON& it, const ProjectWeakPtr& project, ObjectData base);
static ImageUniquePtr parseImage (
const JSON& it, const ProjectWeakPtr& project, ObjectData base, const std::string& image);
static std::vector <ImageEffectUniquePtr> parseEffects (const JSON& it, const ProjectWeakPtr& project);
static ImageEffectUniquePtr parseEffect (const JSON& it, const ProjectWeakPtr& project);
static std::vector <ImageEffectPassOverrideUniquePtr> parseEffectPassOverrides (const JSON& it, const ProjectWeakPtr& project);
static ImageEffectPassOverrideUniquePtr parseEffectPass (const JSON& it, const ProjectWeakPtr& project);
static TextureMap parseTextureMap (const JSON& it);
static ComboMap parseComboMap (const JSON& it);
};
} // namespace WallpaperEngine::Data::Parsers

View File

@ -0,0 +1,68 @@
#include <algorithm>
#include "ProjectParser.h"
#include "WallpaperEngine/Logging/CLog.h"
#include "WallpaperParser.h"
using namespace WallpaperEngine::Data::Parsers;
static int backgroundId = 0;
ProjectSharedPtr ProjectParser::parse (const JSON& data, const ContainerWeakPtr& container) {
const auto general = data.optional ("general");
auto type = data.require <std::string> ("type", "Project type missing");
// lowercase for consistency
std::transform (type.begin (), type.end (), type.begin (), tolower);
auto result = std::make_shared <Project> (Project {
.title = data.require <std::string> ("title", "Project title missing"),
.type = parseType (type),
.workshopId = data.optional ("workshopid", std::to_string (--backgroundId)),
.supportsAudioProcessing = general.has_value () && general.value ().optional ("supportsAudioProcessing", false),
.properties = parseProperties (general),
.container = container,
});
result->wallpaper = WallpaperParser::parse (data.require ("file", "Project's main file missing"), result);
return result;
}
Project::Type ProjectParser::parseType (const std::string& type) {
if (type == "scene") {
return Project::Type_Scene;
} else if (type == "video") {
return Project::Type_Video;
} else if (type == "web") {
return Project::Type_Web;
}
sLog.exception ("Unsupported project type ", type);
}
Properties ProjectParser::parseProperties (const std::optional <JSON>& data) {
if (!data.has_value ()) {
return {};
}
const auto properties = data.value ().optional ("properties");
if (!properties.has_value ()) {
return {};
}
Properties result = {};
// TODO: CHANGE THIS ONCE THE PROPERTIES PARSING IS HANDLED IN THE NEW TYPES
// THESE ARE COMPLEX TYPES THAT INCLUDE SOME RUNTIME INFO THAT ISN'T EXPLICITLY DATA
// SO THIS WILL NEED A RETHINK IN THE FUTURE
for (const auto& cur : properties.value ().items ()) {
const auto& property = Property::fromJSON (cur.value (), cur.key ());
result.emplace (property->getName (), property);
}
return result;
}

View File

@ -0,0 +1,24 @@
#pragma once
#include <nlohmann/json.hpp>
#include "WallpaperEngine/Data/JSON.h"
#include "WallpaperEngine/Data/Model/Project.h"
namespace WallpaperEngine::Data::Parsers {
using JSON = WallpaperEngine::Data::JSON::JSON;
using namespace WallpaperEngine::Data::Model;
/**
* Parses a project file and returns an object representing it
*/
class ProjectParser {
public:
static ProjectSharedPtr parse (const JSON& data, const ContainerWeakPtr& container);
private:
static Project::Type parseType (const std::string& type);
static Properties parseProperties (const std::optional <JSON>& data);
};
} // namespace WallpaperEngine::Data::Parsers

View File

@ -0,0 +1,77 @@
#include "ShaderConstantParser.h"
#include "WallpaperEngine/Data/Model/Project.h"
using namespace WallpaperEngine::Data::Parsers;
using namespace WallpaperEngine::Data::Model;
ShaderConstantMap ShaderConstantParser::parse (const JSON& it, const ProjectWeakPtr& project) {
if (!it.is_object ()) {
return {};
}
std::map <std::string, ShaderConstantUniquePtr> result = {};
for (const auto& cur : it.items ()) {
result.emplace (cur.key(), parseConstant (cur.value(), project));
}
return result;
}
ShaderConstantUniquePtr ShaderConstantParser::parseConstant (const JSON& it, const ProjectWeakPtr& project) {
ShaderConstant* constant = nullptr;
auto valueIt = it;
if (it.is_object ()) {
auto user = it.find ("user");
auto value = it.find ("value");
if (user == it.end () && value == it.end ()) {
sLog.error (R"(Found object for shader constant without "value" and "user" setting)");
return ShaderConstantUniquePtr (constant);
}
if (user != it.end () && user->is_string ()) {
const auto& properties = project.lock ()->properties;
const auto& propertyIt = properties.find (*user);
if (propertyIt != properties.end ()) {
constant = new ShaderConstantProperty (propertyIt->second);
} else {
sLog.error ("Shader constant pointing to non-existant project property: ", user->get <std::string> ());
}
} else {
valueIt = *value;
}
}
// TODO: REFACTOR THIS, SAME OLD THING WE HAD BEFORE, THESE SHADERCONSTANT CLASSES HAVE TO GO AND WE SHOULD HAVE JUST ONE
if (constant == nullptr) {
if (valueIt.is_number_float ()) {
constant = new ShaderConstantFloat (valueIt);
} else if (valueIt.is_number_integer ()) {
constant = new ShaderConstantInteger (valueIt);
} else if (valueIt.is_string ()) {
// TODO: USE VECTOR PARSER HERE? MAKE USE OF CDYNAMICVALUE FOR SHADERCONSTANTS TOO
// count the number of spaces to determine which type of vector we have
std::string value = valueIt;
size_t spaces =
std::count_if (value.begin (), value.end (), [&] (const auto& item) { return item == ' '; });
if (spaces == 1) {
constant = new ShaderConstantVector2 (valueIt);
} else if (spaces == 2) {
constant = new ShaderConstantVector3 (valueIt);
} else if (spaces == 3) {
constant = new ShaderConstantVector4 (valueIt);
} else {
sLog.exception ("unknown shader constant type ", value);
}
}
}
return ShaderConstantUniquePtr (constant);
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "WallpaperEngine/Data/JSON.h"
#include "WallpaperEngine/Data/Model/Types.h"
namespace WallpaperEngine::Data::Parsers {
using JSON = WallpaperEngine::Data::JSON::JSON;
using namespace WallpaperEngine::Data::Model;
class ShaderConstantParser {
public:
static ShaderConstantMap parse (const JSON& it, const ProjectWeakPtr& project);
static ShaderConstantUniquePtr parseConstant (const JSON& it, const ProjectWeakPtr& project);
};
} // namespace WallpaperEngine::Data::Parsers

View File

@ -0,0 +1,69 @@
#include "UserSettingParser.h"
#include "WallpaperEngine/Data/Model/UserSetting.h"
using namespace WallpaperEngine::Data::Parsers;
using namespace WallpaperEngine::Data::Builders;
UserSettingSharedPtr UserSettingParser::parse (const json& data, const Properties& properties) {
DynamicValueUniquePtr value = std::make_unique <DynamicValue> ();
PropertyWeakPtr property;
std::optional<ConditionInfo> condition;
auto valueIt = data;
if (data.is_object ()) {
const auto user = data.optional ("user");
valueIt = data.require ("value", "User setting must have a value");
if (user.has_value ()) {
std::string source;
const auto& it = user.value ();
if (it.is_string ()) {
source = it;
} else {
condition = ConditionInfo {
.name = it.require <std::string> ("name", "Name for conditional setting must be present"),
.condition = it.require <std::string> ("condition", "Condition for conditional setting must be present"),
};
source = condition.value ().name;
}
const auto propertyIt = properties.find (source);
if (propertyIt != properties.end ()) {
property = propertyIt->second;
}
}
}
// actual value parsing
if (valueIt.is_string ()) {
std::string str = valueIt;
int size = VectorBuilder::preparseSize (str);
//TODO: VALIDATE THIS IS RIGHT?
if (size == 2) {
value->update ((glm::vec2) valueIt);
} else if (size == 3) {
value->update ((glm::vec3) valueIt);
} else {
value->update ((glm::vec4) valueIt);
}
} else if (valueIt.is_number_integer ()) {
value->update (valueIt.get <int> ());
} else if (valueIt.is_number_float ()) {
value->update (valueIt.get <float> ());
} else if (valueIt.is_boolean ()) {
value->update (valueIt.get <bool> ());
} else {
sLog.exception ("Unsupported user setting type ", valueIt.type_name ());
}
return std::make_shared <UserSetting> (UserSetting {
.value = std::move (value),
.property = property,
.condition = condition,
});
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "WallpaperEngine/Data/JSON.h"
#include "WallpaperEngine/Data/Model/Types.h"
#include "WallpaperEngine/Data/Model/UserSetting.h"
#include "WallpaperEngine/Logging/CLog.h"
namespace WallpaperEngine::Data::Parsers {
using json = WallpaperEngine::Data::JSON::JSON;
using namespace WallpaperEngine::Data::Model;
class UserSettingParser {
public:
static UserSettingSharedPtr parse (const json& data, const Properties& properties);
};
} // namespace WallpaperEngine::Data::Parsers

View File

@ -0,0 +1,106 @@
#include "WallpaperParser.h"
#include "ObjectParser.h"
#include "WallpaperEngine/Data/Model/Project.h"
#include "WallpaperEngine/Data/Model/Wallpaper.h"
#include "WallpaperEngine/Logging/CLog.h"
using namespace WallpaperEngine::Data::Parsers;
WallpaperSharedPtr WallpaperParser::parse (const JSON& file, const ProjectWeakPtr& project) {
switch (project.lock ()->type) {
case Project::Type_Scene:
return std::dynamic_pointer_cast <Wallpaper> (parseScene (file, project));
case Project::Type_Video:
return std::dynamic_pointer_cast <Wallpaper> (parseVideo (file, project));
case Project::Type_Web:
return std::dynamic_pointer_cast <Wallpaper> (parseWeb (file, project));
default:
sLog.exception ("Unexpected project type value found... This is likely a bug");
}
}
SceneSharedPtr WallpaperParser::parseScene (const JSON& file, const ProjectWeakPtr& project) {
const auto scene = JSON::parse (project.lock ()->container.lock ()->readFileAsString (file));
const auto camera = scene.require ("camera", "Scenes must have a camera section");
const auto general = scene.require ("general", "Scenes must have a general section");
const auto projection = general.require ("orthogonalprojection", "General section must have orthogonal projection info");
const auto objects = scene.require ("objects", "Scenes must have an objects section");
const auto& properties = project.lock ()->properties;
// TODO: FIND IF THESE DEFAULTS ARE SENSIBLE OR NOT AND PERFORM PROPER VALIDATION WHEN CAMERA PREVIEW AND CAMERA
// PARALLAX ARE PRESENT
return std::make_shared <Scene> (
WallpaperData {
.filename = "",
.project = project
}, SceneData {
.colors = {
.ambient = general.optional ("ambientcolor", glm::vec3 (0.0f)),
.skylight = general.optional ("skylightcolor", glm::vec3 (0.0f)),
.clear = general.user ("clearcolor", properties, glm::vec3 (1.0f)),
},
.camera = {
.fade = general.optional ("camerafade", false),
.preview = general.optional ("camerapreview", false),
.bloom = {
.enabled = general.user ("bloom", properties, false),
.strength = general.user ("bloomstrength", properties, 0.0f),
.threshold = general.user ("bloomthreshold", properties, 0.0f),
},
.parallax = {
.enabled = general.optional ("cameraparallax", false),
.amount = general.optional ("cameraparallaxamount", 1.0f),
.delay = general.optional ("cameraparallaxdelay", 0.0f),
.mouseInfluence = general.optional ("cameraparallaxmouseinfluence", 1.0f),
},
.shake = {
.enabled = general.optional ("camerashake", false),
.amplitude = general.optional ("camerashakeamplitude", 0.0f),
.roughness = general.optional ("camerashakeroughness", 0.0f),
.speed = general.optional ("camerashakespeed", 0.0f),
},
.configuration = {
.center = camera.require <glm::vec3> ("center", "Camera must have a center position"),
.eye = camera.require <glm::vec3> ("eye", "Camera must have an eye position"),
.up = camera.require <glm::vec3> ("up", "Camera must have an up position"),
},
.projection = {
.width = projection.require <int> ("width", "Projection must have a width"),
.height = projection.require <int> ("height", "Projection must have a height"),
.isAuto = projection.optional ("auto", false)
}
},
.objects = parseObjects (objects, project)
}
);
}
VideoSharedPtr WallpaperParser::parseVideo (const JSON& file, const ProjectWeakPtr& project) {
return std::make_unique <Video> (WallpaperData {
.filename = file,
.project = project
});
}
WebSharedPtr WallpaperParser::parseWeb (const JSON& file, const ProjectWeakPtr& project) {
return std::make_unique <Web> (WallpaperData {
.filename = file,
.project = project,
});
}
ObjectMap WallpaperParser::parseObjects (const JSON& objects, const ProjectWeakPtr& project) {
ObjectMap result = {};
for (const auto& cur : objects) {
auto object = ObjectParser::parse (cur, project);
//TODO: DO WE REALLY WANT TO DIRECTLY CONSTRUCT UNIQUE AND SHARED PTRS EVERYWHERE?
// SHOULDN'T THAT BE HANDLED BY CALLING CODE (LIKE THIS) INSTEAD?
result.emplace (object->id, std::move (object));
}
return result;
}

View File

@ -0,0 +1,20 @@
#pragma once
#include "WallpaperEngine/Data/JSON.h"
#include "WallpaperEngine/Data/Model/Types.h"
namespace WallpaperEngine::Data::Parsers {
using JSON = WallpaperEngine::Data::JSON::JSON;
using namespace WallpaperEngine::Data::Model;
class WallpaperParser {
public:
static WallpaperSharedPtr parse (const JSON& file, const ProjectWeakPtr& project);
private:
static SceneSharedPtr parseScene (const JSON& file, const ProjectWeakPtr& project);
static VideoSharedPtr parseVideo (const JSON& file, const ProjectWeakPtr& project);
static WebSharedPtr parseWeb (const JSON& file, const ProjectWeakPtr& project);
static ObjectMap parseObjects (const JSON& objects, const ProjectWeakPtr& project);
};
} // namespace WallpaperEngine::Data::Parsers

View File

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

View File

@ -0,0 +1,36 @@
#pragma once
#include <stdexcept>
namespace WallpaperEngine::Data::Utils {
/**
* A simple implementation of type and sub-types casting to be as explicit
* as possible while being performant
*
* Uses typeid from C++17, so base types must have a virtual, default destructor
*/
class TypeCaster {
public:
virtual ~TypeCaster() = default;
template <class T> [[nodiscard]] const T* as () const {
if (is<T> ()) {
return static_cast <const T*> (this);
}
throw std::bad_cast ();
}
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);
}
};
} // namespace WallpaperEngine::Data::Utils

View File

@ -20,47 +20,52 @@ void initLogging () {
}
int main (int argc, char* argv[]) {
// if type parameter is specified, this is a subprocess, so no logging should be enabled from our side
bool enableLogging = true;
std::string typeZygote = "--type=zygote";
std::string typeUtility = "--type=utility";
try {
// if type parameter is specified, this is a subprocess, so no logging should be enabled from our side
bool enableLogging = true;
std::string typeZygote = "--type=zygote";
std::string typeUtility = "--type=utility";
for (int i = 1; i < argc; i ++) {
if (strncmp (typeZygote.c_str(), argv[i], typeZygote.size()) == 0) {
enableLogging = false;
break;
} else if (strncmp (typeUtility.c_str(), argv[i], typeUtility.size()) == 0) {
enableLogging = false;
break;
for (int i = 1; i < argc; i ++) {
if (strncmp (typeZygote.c_str(), argv[i], typeZygote.size()) == 0) {
enableLogging = false;
break;
} else if (strncmp (typeUtility.c_str(), argv[i], typeUtility.size()) == 0) {
enableLogging = false;
break;
}
}
}
if (enableLogging) {
initLogging ();
}
if (enableLogging) {
initLogging ();
}
WallpaperEngine::Application::CApplicationContext appContext (argc, argv);
WallpaperEngine::Application::CApplicationContext appContext (argc, argv);
// halt if the list-properties option was specified
if (appContext.settings.general.onlyListProperties)
return 0;
app = new WallpaperEngine::Application::CWallpaperApplication (appContext);
// attach signals to gracefully stop
std::signal (SIGINT, signalhandler);
std::signal (SIGTERM, signalhandler);
std::signal (SIGKILL, signalhandler);
// show the wallpaper application
app->show ();
// remove signal handlers before destroying app
std::signal (SIGINT, SIG_DFL);
std::signal (SIGTERM, SIG_DFL);
std::signal (SIGKILL, SIG_DFL);
delete app;
// halt if the list-properties option was specified
if (appContext.settings.general.onlyListProperties)
return 0;
app = new WallpaperEngine::Application::CWallpaperApplication (appContext);
// attach signals to gracefully stop
std::signal (SIGINT, signalhandler);
std::signal (SIGTERM, signalhandler);
std::signal (SIGKILL, signalhandler);
// show the wallpaper application
app->show ();
// remove signal handlers before destroying app
std::signal (SIGINT, SIG_DFL);
std::signal (SIGTERM, SIG_DFL);
std::signal (SIGKILL, SIG_DFL);
delete app;
return 0;
} catch (const std::exception& e) {
std::cerr << e.what () << std::endl;
return 1;
}
}