| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840 |
- #include "WallpaperApplication.h"
- #include "Steam/FileSystem/FileSystem.h"
- #include "WallpaperEngine/Application/ApplicationState.h"
- #include "WallpaperEngine/Assets/AssetLoadException.h"
- #include "WallpaperEngine/Audio/Drivers/Detectors/PulseAudioPlayingDetector.h"
- #include "WallpaperEngine/FileSystem/Container.h"
- #include "WallpaperEngine/Logging/Log.h"
- #include "WallpaperEngine/Render/Drivers/VideoFactories.h"
- #include "WallpaperEngine/Render/RenderContext.h"
- #include "WallpaperEngine/Data/Dumpers/StringPrinter.h"
- #include "WallpaperEngine/Data/Parsers/ProjectParser.h"
- #include "WallpaperEngine/Data/Model/Property.h"
- #include "WallpaperEngine/Data/Model/Wallpaper.h"
- #include "WallpaperEngine/Debugging/CallStack.h"
- #if DEMOMODE
- #include "recording.h"
- #endif /* DEMOMODE */
- #include <algorithm>
- #include <numeric>
- #include <unistd.h>
- #define STB_IMAGE_WRITE_IMPLEMENTATION
- #include <stb_image_write.h>
- #define FULLSCREEN_CHECK_WAIT_TIME 250
- float g_Time;
- float g_TimeLast;
- float g_Daytime;
- using namespace WallpaperEngine::Assets;
- using namespace WallpaperEngine::Application;
- using namespace WallpaperEngine::Data::Model;
- using namespace WallpaperEngine::FileSystem;
- void CustomGLDebugCallback(GLenum source,
- GLenum type,
- GLuint id,
- GLenum severity,
- GLsizei length,
- const GLchar *message,
- const void *userParam) {
- if (severity != GL_DEBUG_SEVERITY_HIGH) {
- return;
- }
- sLog.error("OpenGL error: ", message, ", type: ", type, ", id: ", id);
- std::vector<WallpaperEngine::Debugging::CallStack::CallInfo> callInfo;
- WallpaperEngine::Debugging::CallStack::GetCalls(callInfo);
- for(std::vector<WallpaperEngine::Debugging::CallStack::CallInfo>::size_type i = 0; i < callInfo.size(); ++i) {
- fprintf(stderr,
- "[%3lu] %15lu: %s in %s\n",
- callInfo.size()-i,
- callInfo[i].offset,
- callInfo[i].function.c_str(),
- callInfo[i].module.c_str()
- );
- }
- }
- WallpaperApplication::WallpaperApplication (ApplicationContext& context) :
- m_context (context) {
- this->loadBackgrounds ();
- this->setupProperties ();
- this->setupBrowser();
- this->initializePlaylists ();
- }
- AssetLocatorUniquePtr WallpaperApplication::setupAssetLocator (const std::string& bg) const {
- auto container = std::make_unique <Container> ();
- const std::filesystem::path path = bg;
- container->mount (path, "/");
- try {
- container->mount (path / "scene.pkg", "/");
- } catch (std::runtime_error&) { }
- try {
- container->mount (path / "gifscene.pkg", "/");
- } catch (std::runtime_error&) { }
- try {
- container->mount (this->m_context.settings.general.assets, "/");
- } catch (std::runtime_error&) {
- sLog.exception ("Cannot find a valid assets folder, resolved to ", this->m_context.settings.general.assets);
- }
- // mount the current directory as root
- try {
- container->mount (std::filesystem::current_path (), "/");
- } catch (std::runtime_error&) { }
- auto& vfs = container->getVFS ();
- //
- // Had to get a little creative with the effects to achieve the same bloom effect without any custom code
- // these virtual files are loaded by an image in the scene that takes current _rt_FullFrameBuffer and
- // applies the bloom effect to render it out to the screen
- //
- // add the effect file for screen bloom
- // add some model for the image element even if it's going to waste rendering cycles
- vfs.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}
- }
- }
- )
- }
- }
- }
- ),
- }
- }
- );
- vfs.add (
- "models/wpenginelinux.json",
- {
- {"material","materials/wpenginelinux.json"}
- }
- );
- vfs.add(
- "materials/wpenginelinux.json",
- {
- {"passes", JSON::array (
- {
- {
- {"blending", "normal"},
- {"cullmode", "nocull"},
- {"depthtest", "disabled"},
- {"depthwrite", "disabled"},
- {"shader", "genericimage2"},
- {"textures", JSON::array ({"_rt_FullFrameBuffer"})}
- }
- }
- )}}
- );
- vfs.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"
- "}"
- );
- vfs.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"
- "}"
- );
- return std::make_unique <AssetLocator> (std::move (container));
- }
- void WallpaperApplication::loadBackgrounds () {
- if (this->m_context.settings.render.mode == ApplicationContext::NORMAL_WINDOW ||
- this->m_context.settings.render.mode == ApplicationContext::EXPLICIT_WINDOW) {
- auto path = this->m_context.settings.general.defaultBackground;
- if (this->m_context.settings.general.defaultPlaylist.has_value () &&
- !this->m_context.settings.general.defaultPlaylist->items.empty ()) {
- path = this->m_context.settings.general.defaultPlaylist->items.front ();
- }
- this->m_backgrounds ["default"] = this->loadBackground (path);
- return;
- }
- for (const auto& [screen, path] : this->m_context.settings.general.screenBackgrounds) {
- // screens with no screen should use the default
- if (path.empty ()) {
- this->m_backgrounds [screen] = this->loadBackground (this->m_context.settings.general.defaultBackground);
- } else {
- this->m_backgrounds [screen] = this->loadBackground (path);
- }
- }
- }
- ProjectUniquePtr WallpaperApplication::loadBackground (const std::string& bg) {
- auto container = this->setupAssetLocator (bg);
- auto json = WallpaperEngine::Data::JSON::JSON::parse (container->readString ("project.json"));
- return WallpaperEngine::Data::Parsers::ProjectParser::parse (json, std::move(container));
- }
- std::vector<std::size_t> WallpaperApplication::buildPlaylistOrder (
- const ApplicationContext::PlaylistDefinition& definition) {
- std::vector<std::size_t> order (definition.items.size ());
- std::iota (order.begin (), order.end (), 0);
- if (definition.settings.order == "random") {
- std::shuffle (order.begin (), order.end (), this->m_playlistRng);
- }
- return order;
- }
- void WallpaperApplication::initializePlaylists () {
- const bool hasDefaultPlaylist = this->m_context.settings.general.defaultPlaylist.has_value ();
- const bool hasScreenPlaylists = !this->m_context.settings.general.screenPlaylists.empty ();
- if (!hasDefaultPlaylist && !hasScreenPlaylists)
- return;
- const auto now = std::chrono::steady_clock::now ();
- auto registerPlaylist = [this, now](const std::string& key, const ApplicationContext::PlaylistDefinition& playlist,
- std::optional<std::filesystem::path> currentPath) {
- if (playlist.items.empty ())
- return;
- ActivePlaylist state;
- state.definition = playlist;
- state.order = this->buildPlaylistOrder (playlist);
- if (state.order.empty ())
- return;
- if (currentPath.has_value ()) {
- state.orderIndex = 0;
- for (std::size_t i = 0; i < state.order.size (); i ++) {
- if (playlist.items [state.order [i]] == currentPath.value ()) {
- state.orderIndex = i;
- break;
- }
- }
- }
- const uint32_t delayMinutes = std::max<uint32_t> (1, state.definition.settings.delayMinutes);
- state.nextSwitch = now + std::chrono::minutes (delayMinutes);
- state.lastUpdate = now;
- this->m_activePlaylists.insert_or_assign (key, std::move (state));
- };
- if (hasDefaultPlaylist &&
- (this->m_context.settings.render.mode == ApplicationContext::NORMAL_WINDOW ||
- this->m_context.settings.render.mode == ApplicationContext::EXPLICIT_WINDOW)) {
- const auto& playlist = this->m_context.settings.general.defaultPlaylist.value ();
- const auto currentPath = playlist.items.empty ()
- ? std::optional<std::filesystem::path> {this->m_context.settings.general.defaultBackground}
- : std::optional<std::filesystem::path> {playlist.items.front ()};
- registerPlaylist ("default", playlist, currentPath);
- }
- for (const auto& [screen, playlist] : this->m_context.settings.general.screenPlaylists) {
- const auto current = this->m_context.settings.general.screenBackgrounds.find (screen);
- const auto currentPath = current != this->m_context.settings.general.screenBackgrounds.end ()
- ? std::optional<std::filesystem::path> {current->second}
- : std::nullopt;
- registerPlaylist (screen, playlist, currentPath);
- }
- }
- void WallpaperApplication::ensureBrowserForProject (const Project& project) {
- if (!project.wallpaper->is<Web> ())
- return;
- if (!this->m_browserContext) {
- this->m_browserContext = std::make_unique <WebBrowser::WebBrowserContext> (*this);
- }
- }
- bool WallpaperApplication::makeAnyViewportCurrent () const {
- if (!this->m_renderContext)
- return false;
- const auto& viewports = this->m_renderContext->getOutput ().getViewports ();
- if (viewports.empty ())
- return false;
- viewports.begin ()->second->makeCurrent ();
- return true;
- }
- bool WallpaperApplication::preflightWallpaper (const std::string& path) {
- try {
- // avoid mutating state, just ensure project.json parses
- auto container = this->setupAssetLocator (path);
- const auto json = WallpaperEngine::Data::JSON::JSON::parse (container->readString ("project.json"));
- if (!json.contains ("type") || !json.contains ("file")) {
- sLog.error ("Preflight failed for ", path, ": missing required fields");
- return false;
- }
- return true;
- } catch (const std::exception& e) {
- sLog.error ("Preflight failed for ", path, ": ", e.what ());
- return false;
- }
- }
- bool WallpaperApplication::selectNextCandidate (ActivePlaylist& playlist, std::size_t& outOrderIndex) {
- if (playlist.order.empty ())
- return false;
- std::size_t attempts = 0;
- std::size_t candidateOrderIndex = outOrderIndex;
- while (attempts < playlist.order.size ()) {
- const auto candidateIndex = playlist.order [candidateOrderIndex];
- if (!playlist.failedIndices.contains (candidateIndex)) {
- outOrderIndex = candidateOrderIndex;
- return true;
- }
- attempts++;
- candidateOrderIndex = (candidateOrderIndex + 1) % playlist.order.size ();
- }
- return false;
- }
- void WallpaperApplication::advancePlaylist (
- const std::string& screen, ActivePlaylist& playlist,
- const std::chrono::steady_clock::time_point& now) {
- if (playlist.order.empty ())
- return;
- playlist.orderIndex = (playlist.orderIndex + 1) % playlist.order.size ();
- if (playlist.orderIndex == 0 && playlist.definition.settings.order == "random") {
- std::shuffle (playlist.order.begin (), playlist.order.end (), this->m_playlistRng);
- }
- std::size_t candidateOrderIndex = playlist.orderIndex;
- if (!this->selectNextCandidate (playlist, candidateOrderIndex)) {
- sLog.error ("All playlist items failed for ", screen, ", keeping current wallpaper");
- const uint32_t delayMinutes = std::max<uint32_t> (1, playlist.definition.settings.delayMinutes);
- playlist.nextSwitch = now + std::chrono::minutes (delayMinutes);
- return;
- }
- const auto candidateIndex = playlist.order [candidateOrderIndex];
- const auto& candidatePath = playlist.definition.items [candidateIndex];
- if (!this->preflightWallpaper (candidatePath.string ())) {
- playlist.failedIndices.insert (candidateIndex);
- if (!this->selectNextCandidate (playlist, candidateOrderIndex)) {
- sLog.error ("All playlist items failed for ", screen, ", keeping current wallpaper");
- const uint32_t delayMinutes = std::max<uint32_t> (1, playlist.definition.settings.delayMinutes);
- playlist.nextSwitch = now + std::chrono::minutes (delayMinutes);
- return;
- }
- }
- playlist.orderIndex = candidateOrderIndex;
- const auto& nextPath = playlist.definition.items [playlist.order [playlist.orderIndex]];
- bool loaded = false;
- try {
- if (!this->makeAnyViewportCurrent ()) {
- sLog.error ("Cannot switch playlist on ", screen, ": no active viewport");
- throw std::runtime_error ("No viewport available");
- }
- auto project = this->loadBackground (nextPath.string ());
- this->setupPropertiesForProject (*project);
- this->ensureBrowserForProject (*project);
- this->m_backgrounds [screen] = std::move (project);
- const auto scalingIt = this->m_context.settings.general.screenScalings.find (screen);
- const auto clampIt = this->m_context.settings.general.screenClamps.find (screen);
- const auto scaling = scalingIt != this->m_context.settings.general.screenScalings.end ()
- ? scalingIt->second
- : this->m_context.settings.render.window.scalingMode;
- const auto clamp = clampIt != this->m_context.settings.general.screenClamps.end ()
- ? clampIt->second
- : this->m_context.settings.render.window.clamp;
- if (this->m_renderContext) {
- this->m_renderContext->setWallpaper (
- screen,
- WallpaperEngine::Render::CWallpaper::fromWallpaper (
- *this->m_backgrounds [screen]->wallpaper,
- *this->m_renderContext, *this->m_audioContext, this->m_browserContext.get (),
- scaling,
- clamp
- )
- );
- }
- this->m_context.settings.general.screenBackgrounds [screen] = nextPath;
- loaded = true;
- } catch (const std::exception& e) {
- sLog.error ("Failed to advance playlist on ", screen, ": ", e.what ());
- }
- if (!loaded) {
- playlist.failedIndices.insert (playlist.order [playlist.orderIndex]);
- // Keep current position; next timer tick will retry advancement
- sLog.error ("Failed to load wallpaper for ", screen, ", will retry on next cycle");
- }
- const uint32_t delayMinutes = std::max<uint32_t> (1, playlist.definition.settings.delayMinutes);
- playlist.nextSwitch = now + std::chrono::minutes (delayMinutes);
- }
- void WallpaperApplication::updatePlaylists () {
- if (this->m_activePlaylists.empty ())
- return;
- const auto now = std::chrono::steady_clock::now ();
- for (auto& [screen, playlist] : this->m_activePlaylists) {
- playlist.lastUpdate = now;
- if (playlist.definition.settings.mode != "timer")
- continue;
- if (playlist.definition.items.size () <= 1)
- continue;
- if (now < playlist.nextSwitch)
- continue;
- this->advancePlaylist (screen, playlist, now);
- }
- }
- void WallpaperApplication::setupPropertiesForProject (const Project& project) {
- // show properties if required
- for (const auto& [key, cur] : project.properties) {
- // update the value of the property
- 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 ", key);
- cur->update (override->second);
- }
- if (this->m_context.settings.general.onlyListProperties) {
- sLog.out (cur->dump ());
- }
- }
- }
- void WallpaperApplication::setupProperties () {
- for (const auto& [background, info] : this->m_backgrounds)
- this->setupPropertiesForProject (*info);
- }
- void WallpaperApplication::setupBrowser () {
- bool anyWebProject = std::any_of (
- this->m_backgrounds.begin (), this->m_backgrounds.end (),
- [](const std::pair<const std::string, ProjectUniquePtr>& pair) -> bool {
- return pair.second->wallpaper->is<Web> ();
- }
- );
- // do not perform any initialization if no web background is present
- if (!anyWebProject || this->m_browserContext) {
- return;
- }
- this->m_browserContext = std::make_unique <WebBrowser::WebBrowserContext> (*this);
- }
- void WallpaperApplication::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 * 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
- 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
- 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);
- }
- if (const GLenum error = glGetError (); 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 = 0; y < viewport->viewport.w; y++) {
- for (int x = 0; x < viewport->viewport.z; x++) {
- const int xfinal = x + xoffset;
- const int yfinal = vflip ? (viewport->viewport.w - y - 1) : y;
- bitmap [yfinal * width * 3 + xfinal * 3] = *pixel++;
- bitmap [yfinal * width * 3 + xfinal * 3 + 1] = *pixel++;
- bitmap [yfinal * width * 3 + xfinal * 3 + 2] = *pixel++;
- }
- }
- if (viewport->single)
- xoffset += viewport->viewport.z;
- // free the buffer allocated for the viewport
- delete [] buffer;
- }
- if (const auto extension = filename.extension (); extension == ".bmp") {
- stbi_write_bmp (filename.c_str (), width, height, 3, bitmap);
- } else if (extension == ".png") {
- stbi_write_png (filename.c_str (), width, height, 3, bitmap, width * 3);
- } else if (extension == ".jpg" || extension == ".jpeg") {
- stbi_write_jpg (filename.c_str (), width, height, 3, bitmap, 100);
- }
- delete [] bitmap;
- }
- void WallpaperApplication::setupOutput () {
- const char* XDG_SESSION_TYPE = getenv ("XDG_SESSION_TYPE");
- if (!XDG_SESSION_TYPE) {
- sLog.exception (
- "Cannot read environment variable XDG_SESSION_TYPE, window server detection failed. Please ensure proper values are set");
- }
- sLog.debug ("Checking for window servers: ");
- 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 WallpaperApplication::setupAudio () {
- // ensure audioprocessing is required by any background, and we have it enabled
- const bool audioProcessingRequired = std::ranges::any_of (
- this->m_backgrounds,
- [](const std::pair<const std::string, ProjectUniquePtr>& pair) -> bool {
- return pair.second->supportsAudioProcessing;
- }
- );
- if (audioProcessingRequired && this->m_context.settings.audio.audioprocessing) {
- this->m_audioRecorder = std::make_unique <WallpaperEngine::Audio::Drivers::Recorders::PulseAudioPlaybackRecorder> ();
- } else {
- this->m_audioRecorder = std::make_unique <WallpaperEngine::Audio::Drivers::Recorders::PlaybackRecorder> ();
- }
- if (this->m_context.settings.audio.automute) {
- m_audioDetector = std::make_unique <WallpaperEngine::Audio::Drivers::Detectors::PulseAudioPlayingDetector> (this->m_context, *this->m_fullScreenDetector);
- } else {
- m_audioDetector = std::make_unique <WallpaperEngine::Audio::Drivers::Detectors::AudioPlayingDetector> (this->m_context, *this->m_fullScreenDetector);
- }
- // initialize sdl audio driver
- m_audioDriver = std::make_unique <WallpaperEngine::Audio::Drivers::SDLAudioDriver> (this->m_context, *this->m_audioDetector, *this->m_audioRecorder);
- // initialize audio context
- m_audioContext = std::make_unique <WallpaperEngine::Audio::AudioContext> (*m_audioDriver);
- }
- void WallpaperApplication::prepareOutputs () {
- // initialize render context
- m_renderContext = std::make_unique <WallpaperEngine::Render::RenderContext> (*m_videoDriver, *this);
- // create a new background for each screen
- // set all the specific wallpapers required
- for (const auto& [background, info] : this->m_backgrounds) {
- const auto scalingIt = this->m_context.settings.general.screenScalings.find (background);
- const auto clampIt = this->m_context.settings.general.screenClamps.find (background);
- const auto scaling = scalingIt != this->m_context.settings.general.screenScalings.end ()
- ? scalingIt->second
- : this->m_context.settings.render.window.scalingMode;
- const auto clamp = clampIt != this->m_context.settings.general.screenClamps.end ()
- ? clampIt->second
- : this->m_context.settings.render.window.clamp;
- m_renderContext->setWallpaper (
- background,
- WallpaperEngine::Render::CWallpaper::fromWallpaper (
- *info->wallpaper, *m_renderContext, *m_audioContext, m_browserContext.get (),
- scaling,
- clamp
- )
- );
- }
- }
- void WallpaperApplication::setupOpenGLDebugging () {
- #if !NDEBUG
- glDebugMessageCallback(CustomGLDebugCallback, nullptr);
- glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
- #endif
- }
- void WallpaperApplication::show () {
- this->setupOutput ();
- this->setupAudio ();
- this->prepareOutputs ();
- this->setupOpenGLDebugging ();
- static time_t seconds;
- static struct tm* timeinfo;
- if (this->m_context.settings.general.dumpStructure) {
- auto prettyPrinter = Data::Dumpers::StringPrinter ();
- for (const auto& [background, info] : this->m_renderContext->getWallpapers ()) {
- prettyPrinter.printWallpaper (info->getWallpaperData ());
- }
- 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;
- #endif /* DEMOMODE */
- while (this->m_context.state.general.keepRunning) {
- // update g_Daytime
- time (&seconds);
- timeinfo = localtime (&seconds);
- 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;
- // calculate the current time value
- g_Time = m_videoDriver->getRenderTime ();
- // update audio recorder
- m_audioDriver->update ();
- // update input information
- 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
- // 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 (m_videoDriver->getFrameCounter () > (uint32_t) 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) {
- this->m_isPaused = true;
- this->m_pauseStart = std::chrono::steady_clock::now ();
- m_renderContext->setPause (true);
- while (this->m_fullScreenDetector->anythingFullscreen () && this->m_context.state.general.keepRunning)
- usleep (FULLSCREEN_CHECK_WAIT_TIME);
- m_renderContext->setPause (false);
- // account for paused duration in playlist timers
- const auto pausedNow = std::chrono::steady_clock::now ();
- const auto pausedDuration = pausedNow - this->m_pauseStart;
- for (auto& [_, playlist] : this->m_activePlaylists) {
- if (!playlist.definition.settings.updateOnPause) {
- playlist.nextSwitch += pausedDuration;
- playlist.lastUpdate += pausedDuration;
- }
- }
- this->m_isPaused = false;
- }
- this->updatePlaylists ();
- 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;
- }
- sLog.out ("Stopping");
- #if DEMOMODE
- close_encoder ();
- #endif /* DEMOMODE */
- SDL_Quit ();
- }
- void WallpaperApplication::update (Render::Drivers::Output::OutputViewport* viewport) {
- // render the scene
- m_renderContext->render (viewport);
- }
- void WallpaperApplication::signal (int signal) {
- sLog.out ("Stop requested by signal ", signal);
- this->m_context.state.general.keepRunning = false;
- }
- const std::map<std::string, ProjectUniquePtr>& WallpaperApplication::getBackgrounds () const {
- return this->m_backgrounds;
- }
- ApplicationContext& WallpaperApplication::getContext () const {
- return this->m_context;
- }
- const WallpaperEngine::Render::Drivers::Output::Output& WallpaperApplication::getOutput () const {
- return this->m_renderContext->getOutput ();
- }
|