CRenderContext.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. #include "common.h"
  2. #include <iostream>
  3. #include <unistd.h>
  4. #include <X11/Xlib.h>
  5. #include <X11/Xatom.h>
  6. #include <X11/extensions/Xrandr.h>
  7. #include <GL/glew.h>
  8. #include <GL/glx.h>
  9. #include "CRenderContext.h"
  10. #include "CVideo.h"
  11. #define DEFAULT_WINDOW_WIDTH 1280
  12. #define DEFAULT_WINDOW_HEIGHT 720
  13. using namespace WallpaperEngine::Render;
  14. XErrorHandler originalErrorHandler;
  15. void CustomXIOErrorExitHandler (Display* dsp, void* userdata)
  16. {
  17. auto context = static_cast <CRenderContext*> (userdata);
  18. #ifdef DEBUG
  19. sLog.debugerror ("Critical XServer error detected. Attempting to recover...");
  20. #endif /* DEBUG */
  21. // refetch all the resources
  22. context->initialize ();
  23. }
  24. int CustomXErrorHandler (Display* dpy, XErrorEvent* event)
  25. {
  26. #ifdef DEBUG
  27. sLog.debugerror ("Detected X error");
  28. #endif /* DEBUG */
  29. // call the original handler so we can keep some information reporting
  30. originalErrorHandler (dpy, event);
  31. return 0;
  32. }
  33. int CustomXIOErrorHandler (Display* dsp)
  34. {
  35. #ifdef DEBUG
  36. sLog.debugerror ("Detected X error");
  37. #endif /* DEBUG */
  38. return 0;
  39. }
  40. CRenderContext::CRenderContext (std::vector <std::string> screens, CVideoDriver& driver, CContainer& container, CWallpaperApplication& app) :
  41. m_wallpaper (nullptr),
  42. m_screens (std::move (screens)),
  43. m_driver (driver),
  44. m_container (container),
  45. m_app (app),
  46. m_textureCache (new CTextureCache (*this))
  47. {
  48. this->initialize ();
  49. }
  50. void CRenderContext::initialize ()
  51. {
  52. if (this->m_screens.empty () == true)
  53. this->setupWindow ();
  54. else
  55. this->setupScreens ();
  56. }
  57. void CRenderContext::setupWindow ()
  58. {
  59. this->m_driver.showWindow ();
  60. this->m_driver.resizeWindow ({DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT});
  61. }
  62. void CRenderContext::setupScreens ()
  63. {
  64. this->m_viewports.clear ();
  65. this->m_display = XOpenDisplay (nullptr);
  66. // set the error handling to try and recover from X disconnections
  67. #ifdef HAVE_XSETIOERROREXITHANDLER
  68. XSetIOErrorExitHandler (this->m_display, CustomXIOErrorExitHandler, this);
  69. #endif /* HAVE_XSETIOERROREXITHANDLER */
  70. originalErrorHandler = XSetErrorHandler (CustomXErrorHandler);
  71. XSetIOErrorHandler (CustomXIOErrorHandler);
  72. int xrandr_result, xrandr_error;
  73. if (!XRRQueryExtension (this->m_display, &xrandr_result, &xrandr_error))
  74. {
  75. sLog.error ("XRandr is not present, cannot detect specified screens, running in window mode");
  76. return;
  77. }
  78. Window root = DefaultRootWindow (this->m_display);
  79. int fullWidth = DisplayWidth (this->m_display, DefaultScreen (this->m_display));
  80. int fullHeight = DisplayHeight (this->m_display, DefaultScreen (this->m_display));
  81. XRRScreenResources* screenResources = XRRGetScreenResources (this->m_display, DefaultRootWindow (this->m_display));
  82. // there are some situations where xrandr returns null (like screen not using the extension)
  83. if (screenResources == nullptr)
  84. return;
  85. // create the pixmap and gc used for this display, most situations only have one "display"
  86. this->m_pixmap = XCreatePixmap (this->m_display, DefaultRootWindow (this->m_display), fullWidth, fullHeight, 24);
  87. this->m_gc = XCreateGC (this->m_display, this->m_pixmap, 0, nullptr);
  88. // fill the whole pixmap with black for now
  89. XFillRectangle (this->m_display, this->m_pixmap, this->m_gc, 0, 0, fullWidth, fullHeight);
  90. for (int i = 0; i < screenResources->noutput; i ++)
  91. {
  92. XRROutputInfo* info = XRRGetOutputInfo (this->m_display, screenResources, screenResources->outputs [i]);
  93. // there are some situations where xrandr returns null (like screen not using the extension)
  94. if (info == nullptr || info->connection != RR_Connected)
  95. continue;
  96. for (const auto& cur : this->m_screens)
  97. {
  98. if (strcmp (info->name, cur.c_str ()) != 0)
  99. continue;
  100. XRRCrtcInfo* crtc = XRRGetCrtcInfo (this->m_display, screenResources, info->crtc);
  101. sLog.out ("Found requested screen: ", info->name, " -> ", crtc->x, "x", crtc->y, ":", crtc->width, "x", crtc->height);
  102. this->m_viewports.push_back ({{crtc->x, crtc->y, crtc->width, crtc->height}, cur});
  103. XRRFreeCrtcInfo (crtc);
  104. }
  105. XRRFreeOutputInfo (info);
  106. }
  107. XRRFreeScreenResources (screenResources);
  108. // create the fbo that will handle the screen
  109. this->m_fbo = new CFBO(
  110. "_sc_FullFrameBuffer",
  111. ITexture::TextureFormat::ARGB8888,
  112. ITexture::TextureFlags::NoFlags,
  113. 1.0,
  114. fullWidth, fullHeight,
  115. fullWidth, fullHeight
  116. );
  117. // set the window background so the pixmap is drawn
  118. XSetWindowBackgroundPixmap(this->m_display, root, this->m_pixmap);
  119. this->m_imageData = new char [fullWidth * fullHeight * 4];
  120. // create the image for X11 to be able to copy it over
  121. this->m_image = XCreateImage (this->m_display, CopyFromParent, 24, ZPixmap, 0, this->m_imageData, fullWidth, fullHeight, 32, 0);
  122. }
  123. CRenderContext::~CRenderContext ()
  124. {
  125. if (this->m_screens.empty () == false)
  126. {
  127. // free any used resource
  128. XDestroyImage (this->m_image);
  129. XFreeGC (this->m_display, this->m_gc);
  130. XFreePixmap (this->m_display, this->m_pixmap);
  131. XCloseDisplay (this->m_display);
  132. }
  133. }
  134. void CRenderContext::renderScreens ()
  135. {
  136. bool firstFrame = true;
  137. bool renderFrame = true;
  138. int fullWidth = DisplayWidth (this->m_display, DefaultScreen (this->m_display));
  139. int fullHeight = DisplayHeight (this->m_display, DefaultScreen (this->m_display));
  140. Window root = DefaultRootWindow (this->m_display);
  141. for (const auto& cur : this->m_viewports)
  142. {
  143. if (DEBUG)
  144. {
  145. std::string str = "Rendering to screen " + cur.name;
  146. glPushDebugGroup (GL_DEBUG_SOURCE_APPLICATION, 0, -1, str.c_str ());
  147. }
  148. // render the background
  149. this->m_wallpaper->render (cur.viewport, false, renderFrame, firstFrame);
  150. // scenes need to render a new frame for each viewport as they produce different results
  151. // but videos should only be rendered once per group of viewports
  152. firstFrame = false;
  153. renderFrame = !this->m_wallpaper->is <CVideo> ();
  154. if (DEBUG)
  155. glPopDebugGroup ();
  156. }
  157. // read the full texture into the image
  158. glReadPixels (0, 0, fullWidth, fullHeight, GL_BGRA, GL_UNSIGNED_BYTE, (void*) this->m_imageData);
  159. // put the image back into the screen
  160. XPutImage (this->m_display, this->m_pixmap, this->m_gc, this->m_image, 0, 0, 0, 0, fullWidth, fullHeight);
  161. // _XROOTPMAP_ID & ESETROOT_PMAP_ID allow other programs (compositors) to
  162. // edit the background. Without these, other programs will clear the screen.
  163. // it also forces the compositor to refresh the background (tested with picom)
  164. Atom prop_root = XInternAtom(this->m_display, "_XROOTPMAP_ID", False);
  165. Atom prop_esetroot = XInternAtom(this->m_display, "ESETROOT_PMAP_ID", False);
  166. XChangeProperty(this->m_display, root, prop_root, XA_PIXMAP, 32, PropModeReplace, (unsigned char *) &this->m_pixmap, 1);
  167. XChangeProperty(this->m_display, root, prop_esetroot, XA_PIXMAP, 32, PropModeReplace, (unsigned char *) &this->m_pixmap, 1);
  168. XClearWindow(this->m_display, root);
  169. XFlush(this->m_display);
  170. }
  171. void CRenderContext::renderWindow ()
  172. {
  173. // render the background to the window directly
  174. this->m_wallpaper->render ({0, 0, this->m_driver.getFramebufferSize ()}, true);
  175. }
  176. void CRenderContext::render ()
  177. {
  178. if (this->m_wallpaper == nullptr)
  179. return;
  180. // ensure mouse information is up to date before drawing any frame
  181. this->m_mouse->update ();
  182. if (this->m_viewports.empty () == false)
  183. this->renderScreens ();
  184. else
  185. this->renderWindow ();
  186. this->m_driver.swapBuffers ();
  187. }
  188. void CRenderContext::setWallpaper (CWallpaper* wallpaper)
  189. {
  190. this->m_wallpaper = wallpaper;
  191. // update the wallpaper's texcoords based on the mode we're running
  192. if (this->m_screens.empty () == false)
  193. {
  194. GLfloat texCoords [] = {
  195. 0.0f, 1.0f,
  196. 1.0f, 1.0f,
  197. 0.0f, 0.0f,
  198. 0.0f, 0.0f,
  199. 1.0f, 1.0f,
  200. 1.0f, 0.0f
  201. };
  202. this->m_wallpaper->updateTexCoord (texCoords, sizeof (texCoords));
  203. this->m_wallpaper->setDestinationFramebuffer (this->m_fbo->getFramebuffer ());
  204. }
  205. }
  206. CMouseInput* CRenderContext::getMouse () const
  207. {
  208. return this->m_mouse;
  209. }
  210. void CRenderContext::setMouse (CMouseInput* mouse)
  211. {
  212. this->m_mouse = mouse;
  213. }
  214. CWallpaper* CRenderContext::getWallpaper () const
  215. {
  216. return this->m_wallpaper;
  217. }
  218. const CContainer& CRenderContext::getContainer () const
  219. {
  220. return this->m_container;
  221. }
  222. const CWallpaperApplication& CRenderContext::getApp () const
  223. {
  224. return this->m_app;
  225. }
  226. const ITexture* CRenderContext::resolveTexture (const std::string& name)
  227. {
  228. return this->m_textureCache->resolve (name);
  229. }