CWallpaperApplication.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. #include "CWallpaperApplication.h"
  2. #include "Steam/FileSystem/FileSystem.h"
  3. #include "WallpaperEngine/Application/CApplicationState.h"
  4. #include "WallpaperEngine/Assets/CAssetLoadException.h"
  5. #include "WallpaperEngine/Assets/CDirectory.h"
  6. #include "WallpaperEngine/Assets/CVirtualContainer.h"
  7. #include "WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.h"
  8. #include "WallpaperEngine/Core/Wallpapers/CVideo.h"
  9. #include "WallpaperEngine/Input/Drivers/CGLFWMouseInput.h"
  10. #include "WallpaperEngine/Logging/CLog.h"
  11. #include "WallpaperEngine/Render/CRenderContext.h"
  12. #include "WallpaperEngine/Input/Drivers/CWaylandMouseInput.h"
  13. #include "WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.h"
  14. #include "WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h"
  15. #include <unistd.h>
  16. #define FULLSCREEN_CHECK_WAIT_TIME 250
  17. float g_Time;
  18. float g_TimeLast;
  19. float g_Daytime;
  20. namespace WallpaperEngine::Application {
  21. CWallpaperApplication::CWallpaperApplication (CApplicationContext& context,
  22. WallpaperEngine::WebBrowser::CWebBrowserContext& browserContext) :
  23. m_context (context),
  24. m_defaultBackground (nullptr),
  25. m_browserContext (browserContext) {
  26. this->loadBackgrounds ();
  27. this->setupProperties ();
  28. }
  29. CWallpaperApplication::~CWallpaperApplication () {
  30. delete m_renderContext;
  31. delete m_videoDriver;
  32. delete m_audioContext;
  33. delete m_audioDriver;
  34. delete m_inputContext;
  35. }
  36. void CWallpaperApplication::setupContainer (CCombinedContainer& container, const std::string& bg) const {
  37. const std::filesystem::path basepath = bg;
  38. container.add (new CDirectory (basepath));
  39. container.addPkg (basepath / "scene.pkg");
  40. container.addPkg (basepath / "gifscene.pkg");
  41. container.add (new CDirectory (this->m_context.settings.general.assets));
  42. // add two possible patches directories to the container
  43. // hopefully one sticks
  44. bool relative = true;
  45. bool absolute = true;
  46. try {
  47. container.add (new CDirectory ("../share/"));
  48. } catch (CAssetLoadException&) {
  49. relative = false;
  50. }
  51. try {
  52. container.add (new CDirectory (DATADIR));
  53. } catch (CAssetLoadException&) {
  54. absolute = false;
  55. }
  56. if (!relative && !absolute)
  57. sLog.error ("WARNING: Shader patches directory cannot be found, this might make some backgrounds not work "
  58. "properly");
  59. // TODO: move this somewhere else?
  60. auto* virtualContainer = new CVirtualContainer ();
  61. //
  62. // Had to get a little creative with the effects to achieve the same bloom effect without any custom code
  63. // these virtual files are loaded by an image in the scene that takes current _rt_FullFrameBuffer and
  64. // applies the bloom effect to render it out to the screen
  65. //
  66. // add the effect file for screen bloom
  67. // add some model for the image element even if it's going to waste rendering cycles
  68. virtualContainer->add ("effects/wpenginelinux/bloomeffect.json",
  69. "{"
  70. "\t\"name\":\"camerabloom_wpengine_linux\","
  71. "\t\"group\":\"wpengine_linux_camera\","
  72. "\t\"dependencies\":[],"
  73. "\t\"passes\":"
  74. "\t["
  75. "\t\t{"
  76. "\t\t\t\"material\": \"materials/util/downsample_quarter_bloom.json\","
  77. "\t\t\t\"target\": \"_rt_4FrameBuffer\","
  78. "\t\t\t\"bind\":"
  79. "\t\t\t["
  80. "\t\t\t\t{"
  81. "\t\t\t\t\t\"name\": \"_rt_FullFrameBuffer\","
  82. "\t\t\t\t\t\"index\": 0"
  83. "\t\t\t\t}"
  84. "\t\t\t]"
  85. "\t\t},"
  86. "\t\t{"
  87. "\t\t\t\"material\": \"materials/util/downsample_eighth_blur_v.json\","
  88. "\t\t\t\"target\": \"_rt_8FrameBuffer\","
  89. "\t\t\t\"bind\":"
  90. "\t\t\t["
  91. "\t\t\t\t{"
  92. "\t\t\t\t\t\"name\": \"_rt_4FrameBuffer\","
  93. "\t\t\t\t\t\"index\": 0"
  94. "\t\t\t\t}"
  95. "\t\t\t]"
  96. "\t\t},"
  97. "\t\t{"
  98. "\t\t\t\"material\": \"materials/util/blur_h_bloom.json\","
  99. "\t\t\t\"target\": \"_rt_Bloom\","
  100. "\t\t\t\"bind\":"
  101. "\t\t\t["
  102. "\t\t\t\t{"
  103. "\t\t\t\t\t\"name\": \"_rt_8FrameBuffer\","
  104. "\t\t\t\t\t\"index\": 0"
  105. "\t\t\t\t}"
  106. "\t\t\t]"
  107. "\t\t},"
  108. "\t\t{"
  109. "\t\t\t\"material\": \"materials/util/combine.json\","
  110. "\t\t\t\"target\": \"_rt_FullFrameBuffer\","
  111. "\t\t\t\"bind\":"
  112. "\t\t\t["
  113. "\t\t\t\t{"
  114. "\t\t\t\t\t\"name\": \"_rt_imageLayerComposite_-1_a\","
  115. "\t\t\t\t\t\"index\": 0"
  116. "\t\t\t\t},"
  117. "\t\t\t\t{"
  118. "\t\t\t\t\t\"name\": \"_rt_Bloom\","
  119. "\t\t\t\t\t\"index\": 1"
  120. "\t\t\t\t}"
  121. "\t\t\t]"
  122. "\t\t}"
  123. "\t]"
  124. "}");
  125. virtualContainer->add ("models/wpenginelinux.json", "{"
  126. "\t\"material\":\"materials/wpenginelinux.json\""
  127. "}");
  128. // models require materials, so add that too
  129. virtualContainer->add ("materials/wpenginelinux.json", "{"
  130. "\t\"passes\":"
  131. "\t\t["
  132. "\t\t\t{"
  133. "\t\t\t\t\"blending\": \"normal\","
  134. "\t\t\t\t\"cullmode\": \"nocull\","
  135. "\t\t\t\t\"depthtest\": \"disabled\","
  136. "\t\t\t\t\"depthwrite\": \"disabled\","
  137. "\t\t\t\t\"shader\": \"genericimage2\","
  138. "\t\t\t\t\"textures\": [\"_rt_FullFrameBuffer\"]"
  139. "\t\t\t}"
  140. "\t\t]"
  141. "}");
  142. container.add (virtualContainer);
  143. }
  144. void CWallpaperApplication::loadBackgrounds () {
  145. // load default background if specified
  146. if (!this->m_context.settings.general.defaultBackground.empty ()) {
  147. this->m_defaultBackground = this->loadBackground (this->m_context.settings.general.defaultBackground);
  148. }
  149. for (const auto& [background, path] : this->m_context.settings.general.screenBackgrounds) {
  150. // screens with no background should use the default
  151. if (path.empty ()) {
  152. this->m_backgrounds [background] = this->m_defaultBackground;
  153. } else {
  154. this->m_backgrounds [background] = this->loadBackground (path);
  155. }
  156. }
  157. }
  158. Core::CProject* CWallpaperApplication::loadBackground (const std::string& bg) {
  159. auto* container = new CCombinedContainer ();
  160. this->setupContainer (*container, bg);
  161. return Core::CProject::fromFile ("project.json", container);
  162. }
  163. void CWallpaperApplication::setupPropertiesForProject (const Core::CProject* project) {
  164. // show properties if required
  165. for (const auto cur : project->getProperties ()) {
  166. // update the value of the property
  167. auto override = this->m_context.settings.general.properties.find (cur->getName ());
  168. if (override != this->m_context.settings.general.properties.end ()) {
  169. sLog.out ("Applying override value for ", cur->getName ());
  170. cur->update (override->second);
  171. }
  172. if (this->m_context.settings.general.onlyListProperties)
  173. sLog.out (cur->dump ());
  174. }
  175. }
  176. void CWallpaperApplication::setupProperties () {
  177. for (const auto& [backgrounc, info] : this->m_backgrounds)
  178. this->setupPropertiesForProject (info);
  179. if (this->m_defaultBackground != nullptr)
  180. this->setupPropertiesForProject (this->m_defaultBackground);
  181. }
  182. void CWallpaperApplication::takeScreenshot (const std::filesystem::path& filename, FREE_IMAGE_FORMAT format) {
  183. // this should be getting called at the end of the frame, so the right thing should be bound already
  184. const int width = this->m_renderContext->getOutput ().getFullWidth ();
  185. const int height = this->m_renderContext->getOutput ().getFullHeight ();
  186. // build the output file with FreeImage
  187. static FIBITMAP* bitmap = FreeImage_Allocate (width, height, 24);
  188. RGBQUAD color;
  189. int xoffset = 0;
  190. for (const auto& [screen, viewport] : this->m_renderContext->getOutput ().getViewports ()) {
  191. // activate opengl context so we can read from the framebuffer
  192. viewport->makeCurrent ();
  193. // make room for storing the pixel of this viewport
  194. auto* buffer = new uint8_t [viewport->viewport.z * viewport->viewport.w * sizeof (uint8_t) * 3];
  195. const uint8_t* pixel = buffer;
  196. // read the viewport data into the pixel buffer
  197. glReadPixels (viewport->viewport.x, viewport->viewport.y, viewport->viewport.z, viewport->viewport.w, GL_RGB,
  198. GL_UNSIGNED_BYTE, buffer);
  199. // now get access to the pixels
  200. for (int y = viewport->viewport.w; y > 0; y--) {
  201. for (int x = 0; x < viewport->viewport.z; x++) {
  202. color.rgbRed = *pixel++;
  203. color.rgbGreen = *pixel++;
  204. color.rgbBlue = *pixel++;
  205. // set the pixel in the destination
  206. FreeImage_SetPixelColor (bitmap, x + xoffset,
  207. this->m_renderContext->getOutput ().renderVFlip () ? (viewport->viewport.w - y) : y, &color);
  208. }
  209. }
  210. if (viewport->single)
  211. xoffset += viewport->viewport.z;
  212. // free the buffer allocated for the viewport
  213. delete [] buffer;
  214. }
  215. // finally save the file
  216. FreeImage_Save (format, bitmap, filename.c_str (), 0);
  217. FreeImage_Unload (bitmap);
  218. }
  219. void CWallpaperApplication::show () {
  220. const char* XDG_SESSION_TYPE = getenv ("XDG_SESSION_TYPE");
  221. if (!XDG_SESSION_TYPE) {
  222. sLog.exception (
  223. "Cannot read environment variable XDG_SESSION_TYPE, window server detection failed. Please ensure proper values are set");
  224. }
  225. #ifdef ENABLE_WAYLAND
  226. bool isWayland = strncmp ("wayland", XDG_SESSION_TYPE, strlen ("wayland")) == 0;
  227. #endif // ENABLE_WAYLAND
  228. #ifdef ENABLE_X11
  229. bool isX11 = strncmp ("x11", XDG_SESSION_TYPE, strlen ("x11")) == 0;
  230. #endif // ENABLE_X11
  231. if (this->m_context.settings.render.mode == CApplicationContext::DESKTOP_BACKGROUND) {
  232. #ifdef ENABLE_WAYLAND
  233. if (isWayland) {
  234. m_videoDriver = new WallpaperEngine::Render::Drivers::CWaylandOpenGLDriver (this->m_context, *this);
  235. m_inputContext =
  236. new WallpaperEngine::Input::CInputContext (new WallpaperEngine::Input::Drivers::CWaylandMouseInput (
  237. reinterpret_cast<WallpaperEngine::Render::Drivers::CWaylandOpenGLDriver*> (m_videoDriver)));
  238. this->m_fullScreenDetector =
  239. new WallpaperEngine::Render::Drivers::Detectors::CWaylandFullScreenDetector (this->m_context);
  240. }
  241. #endif // ENABLE_WAYLAND
  242. #ifdef ENABLE_X11
  243. #ifdef ENABLE_WAYLAND
  244. else
  245. #endif // ENABLE_WAYLAND
  246. if (isX11) {
  247. m_videoDriver =
  248. new WallpaperEngine::Render::Drivers::CGLFWOpenGLDriver ("wallpaperengine", this->m_context, *this);
  249. m_inputContext =
  250. new WallpaperEngine::Input::CInputContext (new WallpaperEngine::Input::Drivers::CGLFWMouseInput (
  251. reinterpret_cast<Render::Drivers::CGLFWOpenGLDriver*> (m_videoDriver)));
  252. this->m_fullScreenDetector = new WallpaperEngine::Render::Drivers::Detectors::CX11FullScreenDetector (
  253. this->m_context, *reinterpret_cast<Render::Drivers::CGLFWOpenGLDriver*> (m_videoDriver));
  254. }
  255. #endif // ENABLE_X11
  256. else {
  257. sLog.exception (
  258. "Cannot run in background mode, window server could not be detected. XDG_SESSION_TYPE must be wayland or x11");
  259. }
  260. } else {
  261. m_videoDriver =
  262. new WallpaperEngine::Render::Drivers::CGLFWOpenGLDriver ("wallpaperengine", this->m_context, *this);
  263. #ifdef ENABLE_WAYLAND
  264. if (isWayland) {
  265. this->m_fullScreenDetector =
  266. new WallpaperEngine::Render::Drivers::Detectors::CWaylandFullScreenDetector (this->m_context);
  267. }
  268. #endif // ENABLE_WAYLAND
  269. #ifdef ENABLE_X11
  270. #ifdef ENABLE_WAYLAND
  271. else
  272. #endif // ENABLE_WAYLAND
  273. if (isX11) {
  274. this->m_fullScreenDetector = new WallpaperEngine::Render::Drivers::Detectors::CX11FullScreenDetector (
  275. this->m_context, *reinterpret_cast<Render::Drivers::CGLFWOpenGLDriver*> (m_videoDriver));
  276. }
  277. #endif // ENABLE_X11
  278. else {
  279. this->m_fullScreenDetector =
  280. new WallpaperEngine::Render::Drivers::Detectors::CFullScreenDetector (this->m_context);
  281. }
  282. m_inputContext = new WallpaperEngine::Input::CInputContext (new WallpaperEngine::Input::Drivers::CGLFWMouseInput (
  283. reinterpret_cast<Render::Drivers::CGLFWOpenGLDriver*> (m_videoDriver)));
  284. }
  285. if (this->m_context.settings.audio.audioprocessing) {
  286. this->m_audioRecorder = new WallpaperEngine::Audio::Drivers::Recorders::CPulseAudioPlaybackRecorder ();
  287. } else {
  288. this->m_audioRecorder = new WallpaperEngine::Audio::Drivers::Recorders::CPlaybackRecorder ();
  289. }
  290. // audio playing detector
  291. WallpaperEngine::Audio::Drivers::Detectors::CPulseAudioPlayingDetector audioDetector (this->m_context,
  292. *this->m_fullScreenDetector);
  293. // initialize sdl audio driver
  294. m_audioDriver =
  295. new WallpaperEngine::Audio::Drivers::CSDLAudioDriver (this->m_context, audioDetector, *this->m_audioRecorder);
  296. // initialize audio context
  297. m_audioContext = new WallpaperEngine::Audio::CAudioContext (*m_audioDriver);
  298. // initialize render context
  299. m_renderContext = new WallpaperEngine::Render::CRenderContext (*m_videoDriver, *m_inputContext, *this);
  300. // create a new background for each screen
  301. // set all the specific wallpapers required
  302. for (const auto& [background, info] : this->m_backgrounds) {
  303. m_renderContext->setWallpaper (background, WallpaperEngine::Render::CWallpaper::fromWallpaper (
  304. info->getWallpaper (), *m_renderContext, *m_audioContext, m_browserContext,
  305. this->m_context.settings.general.screenScalings [background]));
  306. }
  307. // wallpapers are setup, free browsesr context if possible
  308. if (!this->m_browserContext.isUsed ()) {
  309. this->m_browserContext.stop ();
  310. }
  311. static time_t seconds;
  312. static struct tm* timeinfo;
  313. while (this->m_context.state.general.keepRunning && !m_videoDriver->closeRequested ()) {
  314. // update g_Daytime
  315. time (&seconds);
  316. timeinfo = localtime (&seconds);
  317. g_Daytime = ((timeinfo->tm_hour * 60) + timeinfo->tm_min) / (24.0 * 60.0);
  318. // keep track of the previous frame's time
  319. g_TimeLast = g_Time;
  320. // calculate the current time value
  321. g_Time = m_videoDriver->getRenderTime ();
  322. // update audio recorder
  323. m_audioDriver->update ();
  324. // update input information
  325. m_inputContext->update ();
  326. // process driver events
  327. m_videoDriver->dispatchEventQueue ();
  328. if (!this->m_context.settings.screenshot.take || m_videoDriver->getFrameCounter () < 5)
  329. continue;
  330. this->takeScreenshot (this->m_context.settings.screenshot.path,
  331. this->m_context.settings.screenshot.format);
  332. this->m_context.settings.screenshot.take = false;
  333. }
  334. // ensure this is updated as sometimes it might not come from a signal
  335. this->m_context.state.general.keepRunning = false;
  336. sLog.out ("Stop requested");
  337. SDL_Quit ();
  338. }
  339. void CWallpaperApplication::update (Render::Drivers::Output::COutputViewport* viewport) {
  340. // check for fullscreen windows and wait until there's none fullscreen
  341. while (this->m_fullScreenDetector->anythingFullscreen () && this->m_context.state.general.keepRunning)
  342. usleep (FULLSCREEN_CHECK_WAIT_TIME);
  343. // render the scene
  344. m_renderContext->render (viewport);
  345. }
  346. void CWallpaperApplication::signal (int signal) {
  347. this->m_context.state.general.keepRunning = false;
  348. }
  349. const std::map<std::string, Core::CProject*>& CWallpaperApplication::getBackgrounds () const {
  350. return this->m_backgrounds;
  351. }
  352. Core::CProject* CWallpaperApplication::getDefaultBackground () const {
  353. return this->m_defaultBackground;
  354. }
  355. CApplicationContext& CWallpaperApplication::getContext () const {
  356. return this->m_context;
  357. }
  358. const WallpaperEngine::Render::Drivers::Output::COutput& CWallpaperApplication::getOutput () const {
  359. return this->m_renderContext->getOutput ();
  360. }
  361. } // namespace WallpaperEngine::Application