CWallpaperApplication.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #include "CWallpaperApplication.h"
  2. #include "Steam/FileSystem/FileSystem.h"
  3. #include "WallpaperEngine/Assets/CDirectory.h"
  4. #include "WallpaperEngine/Assets/CVirtualContainer.h"
  5. #include "WallpaperEngine/Audio/Drivers/CSDLAudioDriver.h"
  6. #include "WallpaperEngine/Core/CVideo.h"
  7. #include "WallpaperEngine/Logging/CLog.h"
  8. #include "WallpaperEngine/Render/CRenderContext.h"
  9. #include "WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.h"
  10. #include "WallpaperEngine/Render/Drivers/Output/CX11Output.h"
  11. #include "WallpaperEngine/Application/CApplicationState.h"
  12. #include "WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h"
  13. #include "WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.h"
  14. #include <unistd.h>
  15. float g_Time;
  16. float g_TimeLast;
  17. namespace WallpaperEngine::Application
  18. {
  19. CWallpaperApplication::CWallpaperApplication (CApplicationContext& context) :
  20. m_context (context),
  21. m_defaultBackground (nullptr)
  22. {
  23. this->loadBackgrounds ();
  24. this->setupProperties ();
  25. }
  26. void CWallpaperApplication::setupContainer (CCombinedContainer& container, const std::string& bg) const
  27. {
  28. std::filesystem::path basepath = bg;
  29. container.add (new CDirectory (basepath));
  30. container.addPkg (basepath / "scene.pkg");
  31. container.addPkg (basepath / "gifscene.pkg");
  32. container.add (new CDirectory (this->m_context.settings.general.assets));
  33. // add two possible patches directories to the container
  34. // hopefully one sticks
  35. bool relative = true;
  36. bool absolute = true;
  37. try
  38. {
  39. container.add (new CDirectory ("../share/"));
  40. }
  41. catch (std::runtime_error& ex)
  42. {
  43. relative = false;
  44. }
  45. try
  46. {
  47. container.add (new CDirectory (DATADIR));
  48. }
  49. catch (std::runtime_error& ex)
  50. {
  51. absolute = false;
  52. }
  53. if (!relative && !absolute)
  54. sLog.error (
  55. "WARNING: Shader patches directory cannot be found, this might make some backgrounds not work "
  56. "properly"
  57. );
  58. // TODO: move this somewhere else?
  59. auto* virtualContainer = new CVirtualContainer ();
  60. //
  61. // Had to get a little creative with the effects to achieve the same bloom effect without any custom code
  62. // these virtual files are loaded by an image in the scene that takes current _rt_FullFrameBuffer and
  63. // applies the bloom effect to render it out to the screen
  64. //
  65. // add the effect file for screen bloom
  66. // add some model for the image element even if it's going to waste rendering cycles
  67. virtualContainer->add (
  68. "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. );
  126. virtualContainer->add (
  127. "models/wpenginelinux.json",
  128. "{"
  129. "\t\"material\":\"materials/wpenginelinux.json\""
  130. "}"
  131. );
  132. // models require materials, so add that too
  133. virtualContainer->add (
  134. "materials/wpenginelinux.json",
  135. "{"
  136. "\t\"passes\":"
  137. "\t\t["
  138. "\t\t\t{"
  139. "\t\t\t\t\"blending\": \"normal\","
  140. "\t\t\t\t\"cullmode\": \"nocull\","
  141. "\t\t\t\t\"depthtest\": \"disabled\","
  142. "\t\t\t\t\"depthwrite\": \"disabled\","
  143. "\t\t\t\t\"shader\": \"genericimage2\","
  144. "\t\t\t\t\"textures\": [\"_rt_FullFrameBuffer\"]"
  145. "\t\t\t}"
  146. "\t\t]"
  147. "}"
  148. );
  149. container.add (virtualContainer);
  150. }
  151. void CWallpaperApplication::loadBackgrounds ()
  152. {
  153. for (const auto& it : this->m_context.settings.general.screenBackgrounds)
  154. {
  155. // ignore the screen settings if there was no background specified
  156. // the default will be used
  157. if (it.second.empty ())
  158. continue;
  159. this->m_backgrounds[it.first] = this->loadBackground (it.second);
  160. }
  161. // load the default project if required
  162. if (!this->m_context.settings.general.defaultBackground.empty ())
  163. this->m_defaultBackground = this->loadBackground (this->m_context.settings.general.defaultBackground);
  164. }
  165. Core::CProject* CWallpaperApplication::loadBackground (const std::string& bg)
  166. {
  167. auto* container = new CCombinedContainer ();
  168. this->setupContainer (*container, bg);
  169. return Core::CProject::fromFile ("project.json", container);
  170. }
  171. void CWallpaperApplication::setupPropertiesForProject (Core::CProject* project)
  172. {
  173. // show properties if required
  174. for (auto cur : project->getProperties ())
  175. {
  176. // update the value of the property
  177. auto override = this->m_context.settings.general.properties.find (cur->getName ());
  178. if (override != this->m_context.settings.general.properties.end ())
  179. {
  180. sLog.out ("Applying override value for ", cur->getName ());
  181. cur->update (override->second);
  182. }
  183. if (this->m_context.settings.general.onlyListProperties)
  184. sLog.out (cur->dump ());
  185. }
  186. }
  187. void CWallpaperApplication::setupProperties ()
  188. {
  189. for (const auto& it : this->m_backgrounds)
  190. this->setupPropertiesForProject (it.second);
  191. if (this->m_defaultBackground != nullptr)
  192. this->setupPropertiesForProject (this->m_defaultBackground);
  193. }
  194. void CWallpaperApplication::takeScreenshot (
  195. const Render::CRenderContext& context, const std::filesystem::path& filename, FREE_IMAGE_FORMAT format
  196. )
  197. {
  198. // this should be getting called at the end of the frame, so the right thing should be bound already
  199. int width = context.getOutput ()->getFullWidth ();
  200. int height = context.getOutput ()->getFullHeight ();
  201. // make room for storing the pixel data
  202. uint8_t* buffer = new uint8_t[width * height * sizeof (uint8_t) * 3];
  203. uint8_t* pixel = buffer;
  204. // read the image into the buffer
  205. glReadPixels (0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer);
  206. // build the output file with FreeImage
  207. FIBITMAP* bitmap = FreeImage_Allocate (width, height, 24);
  208. RGBQUAD color;
  209. // now get access to the pixels
  210. for (int y = height; y > 0; y--)
  211. {
  212. for (int x = 0; x < width; x++)
  213. {
  214. color.rgbRed = *pixel++;
  215. color.rgbGreen = *pixel++;
  216. color.rgbBlue = *pixel++;
  217. // set the pixel in the destination
  218. FreeImage_SetPixelColor (bitmap, x, y, &color);
  219. }
  220. }
  221. // finally save the file
  222. FreeImage_Save (format, bitmap, filename.c_str (), 0);
  223. // free all the used memory
  224. delete[] buffer;
  225. FreeImage_Unload (bitmap);
  226. }
  227. void CWallpaperApplication::show ()
  228. {
  229. // initialize OpenGL driver
  230. WallpaperEngine::Render::Drivers::CX11OpenGLDriver videoDriver ("wallpaperengine", this->m_context);
  231. // initialize the input subsystem
  232. WallpaperEngine::Input::CInputContext inputContext (videoDriver);
  233. // output requested
  234. WallpaperEngine::Render::Drivers::Output::COutput* output;
  235. // fullscreen detector is common for the different render modes
  236. WallpaperEngine::Render::Drivers::Detectors::CX11FullScreenDetector fullscreenDetector (this->m_context, videoDriver);
  237. // stereo mix recorder for audio processing
  238. WallpaperEngine::Audio::Drivers::Recorders::CPulseAudioPlaybackRecorder audioRecorder;
  239. // audio playing detector
  240. WallpaperEngine::Audio::Drivers::Detectors::CPulseAudioPlayingDetector audioDetector (this->m_context, fullscreenDetector);
  241. // initialize sdl audio driver
  242. WallpaperEngine::Audio::Drivers::CSDLAudioDriver audioDriver (this->m_context, audioDetector, audioRecorder);
  243. // initialize audio context
  244. WallpaperEngine::Audio::CAudioContext audioContext (audioDriver);
  245. // initialize the requested output
  246. switch (this->m_context.settings.render.mode)
  247. {
  248. case CApplicationContext::EXPLICIT_WINDOW:
  249. case CApplicationContext::NORMAL_WINDOW:
  250. output = new WallpaperEngine::Render::Drivers::Output::CGLFWWindowOutput (this->m_context, videoDriver, fullscreenDetector);
  251. break;
  252. case CApplicationContext::X11_BACKGROUND:
  253. output = new WallpaperEngine::Render::Drivers::Output::CX11Output (this->m_context, videoDriver, fullscreenDetector);
  254. break;
  255. }
  256. // initialize render context
  257. WallpaperEngine::Render::CRenderContext context (output, videoDriver, inputContext, *this);
  258. // set all the specific wallpapers required
  259. for (const auto& it : this->m_backgrounds)
  260. context.setWallpaper (
  261. it.first,
  262. WallpaperEngine::Render::CWallpaper::fromWallpaper (it.second->getWallpaper (), context, audioContext)
  263. );
  264. // set the default rendering wallpaper if available
  265. if (this->m_defaultBackground != nullptr)
  266. context.setDefaultWallpaper (WallpaperEngine::Render::CWallpaper::fromWallpaper (
  267. this->m_defaultBackground->getWallpaper (), context, audioContext
  268. ));
  269. float startTime, endTime, minimumTime = 1.0f / this->m_context.settings.render.maximumFPS;
  270. while (!videoDriver.closeRequested () && this->m_context.state.general.keepRunning)
  271. {
  272. // update audio recorder
  273. audioDriver.update ();
  274. // update input information
  275. inputContext.update ();
  276. // keep track of the previous frame's time
  277. g_TimeLast = g_Time;
  278. // calculate the current time value
  279. g_Time = videoDriver.getRenderTime ();
  280. // get the start time of the frame
  281. startTime = g_Time;
  282. // render the scene
  283. context.render ();
  284. // get the end time of the frame
  285. endTime = videoDriver.getRenderTime ();
  286. // ensure the frame time is correct to not overrun FPS
  287. if ((endTime - startTime) < minimumTime)
  288. usleep ((minimumTime - (endTime - startTime)) * CLOCKS_PER_SEC);
  289. if (!this->m_context.settings.screenshot.take || videoDriver.getFrameCounter () != 5)
  290. continue;
  291. this->takeScreenshot (context, this->m_context.settings.screenshot.path, this->m_context.settings.screenshot.format);
  292. // disable screenshot just in case the counter overflows
  293. this->m_context.settings.screenshot.take = false;
  294. }
  295. // ensure this is updated as sometimes it might not come from a signal
  296. this->m_context.state.general.keepRunning = false;
  297. sLog.out ("Stop requested");
  298. SDL_Quit ();
  299. }
  300. void CWallpaperApplication::signal (int signal)
  301. {
  302. this->m_context.state.general.keepRunning = false;
  303. }
  304. const std::map<std::string, Core::CProject*>& CWallpaperApplication::getBackgrounds () const
  305. {
  306. return this->m_backgrounds;
  307. }
  308. Core::CProject* CWallpaperApplication::getDefaultBackground () const
  309. {
  310. return this->m_defaultBackground;
  311. }
  312. CApplicationContext& CWallpaperApplication::getContext () const
  313. {
  314. return this->m_context;
  315. }
  316. }