mirror of
https://github.com/Almamu/linux-wallpaperengine.git
synced 2025-07-14 05:12:25 +08:00
412 lines
15 KiB
C++
412 lines
15 KiB
C++
#include "CWallpaperApplication.h"
|
|
|
|
#include "Steam/FileSystem/FileSystem.h"
|
|
#include "WallpaperEngine/Assets/CDirectory.h"
|
|
#include "WallpaperEngine/Assets/CVirtualContainer.h"
|
|
#include "WallpaperEngine/Core/CVideo.h"
|
|
#include "WallpaperEngine/Logging/CLog.h"
|
|
#include "WallpaperEngine/Render/CRenderContext.h"
|
|
#include "WallpaperEngine/Application/CApplicationState.h"
|
|
#include "WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.h"
|
|
|
|
#include <unistd.h>
|
|
|
|
float g_Time;
|
|
float g_TimeLast;
|
|
float g_Daytime;
|
|
|
|
namespace WallpaperEngine::Application
|
|
{
|
|
CWallpaperApplication::CWallpaperApplication (CApplicationContext& context) :
|
|
m_context (context),
|
|
m_defaultBackground (nullptr)
|
|
{
|
|
this->loadBackgrounds ();
|
|
this->setupProperties ();
|
|
}
|
|
|
|
void CWallpaperApplication::setupContainer (CCombinedContainer& container, const std::string& bg) const
|
|
{
|
|
std::filesystem::path basepath = bg;
|
|
|
|
container.add (new CDirectory (basepath));
|
|
container.addPkg (basepath / "scene.pkg");
|
|
container.addPkg (basepath / "gifscene.pkg");
|
|
container.add (new CDirectory (this->m_context.settings.general.assets));
|
|
|
|
// add two possible patches directories to the container
|
|
// hopefully one sticks
|
|
bool relative = true;
|
|
bool absolute = true;
|
|
|
|
try
|
|
{
|
|
container.add (new CDirectory ("../share/"));
|
|
}
|
|
catch (std::runtime_error& ex)
|
|
{
|
|
relative = false;
|
|
}
|
|
|
|
try
|
|
{
|
|
container.add (new CDirectory (DATADIR));
|
|
}
|
|
catch (std::runtime_error& ex)
|
|
{
|
|
absolute = false;
|
|
}
|
|
|
|
if (!relative && !absolute)
|
|
sLog.error (
|
|
"WARNING: Shader patches directory cannot be found, this might make some backgrounds not work "
|
|
"properly"
|
|
);
|
|
|
|
// TODO: move this somewhere else?
|
|
auto* virtualContainer = new CVirtualContainer ();
|
|
|
|
//
|
|
// 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
|
|
virtualContainer->add (
|
|
"effects/wpenginelinux/bloomeffect.json",
|
|
"{"
|
|
"\t\"name\":\"camerabloom_wpengine_linux\","
|
|
"\t\"group\":\"wpengine_linux_camera\","
|
|
"\t\"dependencies\":[],"
|
|
"\t\"passes\":"
|
|
"\t["
|
|
"\t\t{"
|
|
"\t\t\t\"material\": \"materials/util/downsample_quarter_bloom.json\","
|
|
"\t\t\t\"target\": \"_rt_4FrameBuffer\","
|
|
"\t\t\t\"bind\":"
|
|
"\t\t\t["
|
|
"\t\t\t\t{"
|
|
"\t\t\t\t\t\"name\": \"_rt_FullFrameBuffer\","
|
|
"\t\t\t\t\t\"index\": 0"
|
|
"\t\t\t\t}"
|
|
"\t\t\t]"
|
|
"\t\t},"
|
|
"\t\t{"
|
|
"\t\t\t\"material\": \"materials/util/downsample_eighth_blur_v.json\","
|
|
"\t\t\t\"target\": \"_rt_8FrameBuffer\","
|
|
"\t\t\t\"bind\":"
|
|
"\t\t\t["
|
|
"\t\t\t\t{"
|
|
"\t\t\t\t\t\"name\": \"_rt_4FrameBuffer\","
|
|
"\t\t\t\t\t\"index\": 0"
|
|
"\t\t\t\t}"
|
|
"\t\t\t]"
|
|
"\t\t},"
|
|
"\t\t{"
|
|
"\t\t\t\"material\": \"materials/util/blur_h_bloom.json\","
|
|
"\t\t\t\"target\": \"_rt_Bloom\","
|
|
"\t\t\t\"bind\":"
|
|
"\t\t\t["
|
|
"\t\t\t\t{"
|
|
"\t\t\t\t\t\"name\": \"_rt_8FrameBuffer\","
|
|
"\t\t\t\t\t\"index\": 0"
|
|
"\t\t\t\t}"
|
|
"\t\t\t]"
|
|
"\t\t},"
|
|
"\t\t{"
|
|
"\t\t\t\"material\": \"materials/util/combine.json\","
|
|
"\t\t\t\"target\": \"_rt_FullFrameBuffer\","
|
|
"\t\t\t\"bind\":"
|
|
"\t\t\t["
|
|
"\t\t\t\t{"
|
|
"\t\t\t\t\t\"name\": \"_rt_imageLayerComposite_-1_a\","
|
|
"\t\t\t\t\t\"index\": 0"
|
|
"\t\t\t\t},"
|
|
"\t\t\t\t{"
|
|
"\t\t\t\t\t\"name\": \"_rt_Bloom\","
|
|
"\t\t\t\t\t\"index\": 1"
|
|
"\t\t\t\t}"
|
|
"\t\t\t]"
|
|
"\t\t}"
|
|
"\t]"
|
|
"}"
|
|
);
|
|
|
|
virtualContainer->add (
|
|
"models/wpenginelinux.json",
|
|
"{"
|
|
"\t\"material\":\"materials/wpenginelinux.json\""
|
|
"}"
|
|
);
|
|
|
|
// models require materials, so add that too
|
|
virtualContainer->add (
|
|
"materials/wpenginelinux.json",
|
|
"{"
|
|
"\t\"passes\":"
|
|
"\t\t["
|
|
"\t\t\t{"
|
|
"\t\t\t\t\"blending\": \"normal\","
|
|
"\t\t\t\t\"cullmode\": \"nocull\","
|
|
"\t\t\t\t\"depthtest\": \"disabled\","
|
|
"\t\t\t\t\"depthwrite\": \"disabled\","
|
|
"\t\t\t\t\"shader\": \"genericimage2\","
|
|
"\t\t\t\t\"textures\": [\"_rt_FullFrameBuffer\"]"
|
|
"\t\t\t}"
|
|
"\t\t]"
|
|
"}"
|
|
);
|
|
|
|
container.add (virtualContainer);
|
|
}
|
|
|
|
void CWallpaperApplication::loadBackgrounds ()
|
|
{
|
|
for (const auto& it : this->m_context.settings.general.screenBackgrounds)
|
|
{
|
|
// ignore the screen settings if there was no background specified
|
|
// the default will be used
|
|
if (it.second.empty ())
|
|
continue;
|
|
|
|
this->m_backgrounds[it.first] = this->loadBackground (it.second);
|
|
}
|
|
|
|
// load the default project if required
|
|
if (!this->m_context.settings.general.defaultBackground.empty ())
|
|
this->m_defaultBackground = this->loadBackground (this->m_context.settings.general.defaultBackground);
|
|
}
|
|
|
|
Core::CProject* CWallpaperApplication::loadBackground (const std::string& bg)
|
|
{
|
|
auto* container = new CCombinedContainer ();
|
|
|
|
this->setupContainer (*container, bg);
|
|
|
|
return Core::CProject::fromFile ("project.json", container);
|
|
}
|
|
|
|
void CWallpaperApplication::setupPropertiesForProject (Core::CProject* project)
|
|
{
|
|
// show properties if required
|
|
for (auto cur : project->getProperties ())
|
|
{
|
|
// update the value of the property
|
|
auto override = this->m_context.settings.general.properties.find (cur->getName ());
|
|
|
|
if (override != this->m_context.settings.general.properties.end ())
|
|
{
|
|
sLog.out ("Applying override value for ", cur->getName ());
|
|
|
|
cur->update (override->second);
|
|
}
|
|
|
|
if (this->m_context.settings.general.onlyListProperties)
|
|
sLog.out (cur->dump ());
|
|
}
|
|
}
|
|
|
|
void CWallpaperApplication::setupProperties ()
|
|
{
|
|
for (const auto& it : this->m_backgrounds)
|
|
this->setupPropertiesForProject (it.second);
|
|
|
|
if (this->m_defaultBackground != nullptr)
|
|
this->setupPropertiesForProject (this->m_defaultBackground);
|
|
}
|
|
|
|
void CWallpaperApplication::takeScreenshot (
|
|
const Render::CRenderContext& context, const std::filesystem::path& filename, FREE_IMAGE_FORMAT format
|
|
)
|
|
{
|
|
// this should be getting called at the end of the frame, so the right thing should be bound already
|
|
|
|
int width = context.getOutput ()->getFullWidth ();
|
|
int height = context.getOutput ()->getFullHeight ();
|
|
|
|
// make room for storing the pixel data
|
|
uint8_t* buffer = new uint8_t[width * height * sizeof (uint8_t) * 3];
|
|
uint8_t* pixel = buffer;
|
|
|
|
// read the image into the buffer
|
|
glReadPixels (0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer);
|
|
|
|
// build the output file with FreeImage
|
|
FIBITMAP* bitmap = FreeImage_Allocate (width, height, 24);
|
|
RGBQUAD color;
|
|
|
|
// now get access to the pixels
|
|
for (int y = height; y > 0; y--)
|
|
{
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
color.rgbRed = *pixel++;
|
|
color.rgbGreen = *pixel++;
|
|
color.rgbBlue = *pixel++;
|
|
|
|
// set the pixel in the destination
|
|
FreeImage_SetPixelColor (bitmap, x, y, &color);
|
|
}
|
|
}
|
|
|
|
// finally save the file
|
|
FreeImage_Save (format, bitmap, filename.c_str (), 0);
|
|
|
|
// free all the used memory
|
|
delete[] buffer;
|
|
|
|
FreeImage_Unload (bitmap);
|
|
}
|
|
|
|
void CWallpaperApplication::show ()
|
|
{
|
|
// initialize OpenGL driver
|
|
#ifdef ENABLE_WAYLAND
|
|
const bool WAYLAND = getenv("WAYLAND_DISPLAY") && this->m_context.settings.render.mode == CApplicationContext::WAYLAND_LAYER_SHELL;
|
|
if (WAYLAND) {
|
|
videoDriver = std::make_unique<WallpaperEngine::Render::Drivers::CWaylandOpenGLDriver>("wallpaperengine", this->m_context, this);
|
|
inputContext = std::make_unique<WallpaperEngine::Input::CInputContext>(*(WallpaperEngine::Render::Drivers::CWaylandOpenGLDriver*)videoDriver.get());
|
|
} else {
|
|
#endif
|
|
videoDriver = std::make_unique<WallpaperEngine::Render::Drivers::CX11OpenGLDriver>("wallpaperengine", this->m_context);
|
|
inputContext = std::make_unique<WallpaperEngine::Input::CInputContext>(*(WallpaperEngine::Render::Drivers::CX11OpenGLDriver*)videoDriver.get());
|
|
#ifdef ENABLE_WAYLAND
|
|
}
|
|
|
|
if (WAYLAND)
|
|
fullscreenDetector = std::make_unique<WallpaperEngine::Render::Drivers::Detectors::CWaylandFullScreenDetector>(this->m_context, *(WallpaperEngine::Render::Drivers::CWaylandOpenGLDriver*)videoDriver.get());
|
|
else
|
|
#endif
|
|
fullscreenDetector = std::make_unique<WallpaperEngine::Render::Drivers::Detectors::CX11FullScreenDetector>(this->m_context, *videoDriver);
|
|
// stereo mix recorder for audio processing
|
|
WallpaperEngine::Audio::Drivers::Recorders::CPulseAudioPlaybackRecorder audioRecorder;
|
|
// audio playing detector
|
|
WallpaperEngine::Audio::Drivers::Detectors::CPulseAudioPlayingDetector audioDetector (this->m_context, *fullscreenDetector);
|
|
// initialize sdl audio driver
|
|
audioDriver = std::make_unique<WallpaperEngine::Audio::Drivers::CSDLAudioDriver> (this->m_context, audioDetector, audioRecorder);
|
|
// initialize audio context
|
|
audioContext = std::make_unique<WallpaperEngine::Audio::CAudioContext> (*audioDriver);
|
|
|
|
// initialize the requested output
|
|
switch (this->m_context.settings.render.mode)
|
|
{
|
|
case CApplicationContext::EXPLICIT_WINDOW:
|
|
case CApplicationContext::NORMAL_WINDOW:
|
|
output = new WallpaperEngine::Render::Drivers::Output::CGLFWWindowOutput (this->m_context, *videoDriver, *fullscreenDetector);
|
|
break;
|
|
|
|
case CApplicationContext::X11_BACKGROUND:
|
|
output = new WallpaperEngine::Render::Drivers::Output::CX11Output (this->m_context, *videoDriver, *fullscreenDetector);
|
|
break;
|
|
#ifdef ENABLE_WAYLAND
|
|
case CApplicationContext::WAYLAND_LAYER_SHELL:
|
|
output = new WallpaperEngine::Render::Drivers::Output::CWaylandOutput (this->m_context, *videoDriver, *fullscreenDetector);
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
// initialize render context
|
|
context = std::make_unique<WallpaperEngine::Render::CRenderContext> (output, *videoDriver, *inputContext, *this);
|
|
|
|
// set all the specific wallpapers required
|
|
for (const auto& it : this->m_backgrounds)
|
|
context->setWallpaper (
|
|
it.first,
|
|
WallpaperEngine::Render::CWallpaper::fromWallpaper (it.second->getWallpaper (), *context, *audioContext)
|
|
);
|
|
|
|
// set the default rendering wallpaper if available
|
|
if (this->m_defaultBackground != nullptr)
|
|
context->setDefaultWallpaper (WallpaperEngine::Render::CWallpaper::fromWallpaper (
|
|
this->m_defaultBackground->getWallpaper (), *context, *audioContext
|
|
));
|
|
|
|
#ifdef ENABLE_WAYLAND
|
|
if (WAYLAND) {
|
|
renderFrame();
|
|
|
|
while (this->m_context.state.general.keepRunning && !videoDriver->closeRequested ()) {
|
|
videoDriver->dispatchEventQueue();
|
|
}
|
|
} else {
|
|
#endif
|
|
while (!videoDriver->closeRequested () && this->m_context.state.general.keepRunning) {
|
|
renderFrame();
|
|
}
|
|
#ifdef ENABLE_WAYLAND
|
|
}
|
|
#endif
|
|
|
|
// ensure this is updated as sometimes it might not come from a signal
|
|
this->m_context.state.general.keepRunning = false;
|
|
|
|
sLog.out ("Stop requested");
|
|
|
|
SDL_Quit ();
|
|
}
|
|
|
|
void CWallpaperApplication::renderFrame() {
|
|
|
|
static float startTime, endTime, minimumTime = 1.0f / this->m_context.settings.render.maximumFPS;
|
|
static time_t seconds;
|
|
static struct tm* timeinfo;
|
|
|
|
// update g_Daytime
|
|
time (&seconds);
|
|
timeinfo = localtime(&seconds);
|
|
g_Daytime = ((timeinfo->tm_hour * 60) + timeinfo->tm_min) / (24.0 * 60.0);
|
|
|
|
// update audio recorder
|
|
audioDriver->update ();
|
|
// update input information
|
|
inputContext->update ();
|
|
// keep track of the previous frame's time
|
|
g_TimeLast = g_Time;
|
|
// calculate the current time value
|
|
g_Time = videoDriver->getRenderTime ();
|
|
// get the start time of the frame
|
|
startTime = g_Time;
|
|
// render the scene
|
|
context->render ();
|
|
// get the end time of the frame
|
|
endTime = videoDriver->getRenderTime ();
|
|
|
|
// ensure the frame time is correct to not overrun FPS
|
|
if ((endTime - startTime) < minimumTime)
|
|
usleep ((minimumTime - (endTime - startTime)) * CLOCKS_PER_SEC);
|
|
|
|
if (!this->m_context.settings.screenshot.take || videoDriver->getFrameCounter () != 5)
|
|
return;
|
|
|
|
this->takeScreenshot (*context, this->m_context.settings.screenshot.path, this->m_context.settings.screenshot.format);
|
|
// disable screenshot just in case the counter overflows
|
|
this->m_context.settings.screenshot.take = false;
|
|
}
|
|
|
|
void CWallpaperApplication::signal (int signal)
|
|
{
|
|
this->m_context.state.general.keepRunning = false;
|
|
}
|
|
|
|
const std::map<std::string, Core::CProject*>& CWallpaperApplication::getBackgrounds () const
|
|
{
|
|
return this->m_backgrounds;
|
|
}
|
|
|
|
Core::CProject* CWallpaperApplication::getDefaultBackground () const
|
|
{
|
|
return this->m_defaultBackground;
|
|
}
|
|
|
|
CApplicationContext& CWallpaperApplication::getContext () const
|
|
{
|
|
return this->m_context;
|
|
}
|
|
|
|
WallpaperEngine::Render::Drivers::Output::COutput* CWallpaperApplication::getOutput() const {
|
|
return this->output;
|
|
}
|
|
} |