CWallpaperApplication.cpp 18 KB

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