CX11FullScreenDetector.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. #include "CX11FullScreenDetector.h"
  2. #include "WallpaperEngine/Logging/CLog.h"
  3. #include <X11/Xlib.h>
  4. #include <X11/extensions/Xrandr.h>
  5. #include "WallpaperEngine/Render/Drivers/CX11OpenGLDriver.h"
  6. namespace WallpaperEngine::Render::Drivers::Detectors
  7. {
  8. void CustomXIOErrorExitHandler (Display* dsp, void* userdata)
  9. {
  10. auto context = static_cast <CX11FullScreenDetector*> (userdata);
  11. sLog.debugerror ("Critical XServer error detected. Attempting to recover...");
  12. // refetch all the resources
  13. context->reset ();
  14. }
  15. int CustomXErrorHandler (Display* dpy, XErrorEvent* event)
  16. {
  17. sLog.debugerror ("Detected X error");
  18. return 0;
  19. }
  20. int CustomXIOErrorHandler (Display* dsp)
  21. {
  22. sLog.debugerror ("Detected X error");
  23. return 0;
  24. }
  25. CX11FullScreenDetector::CX11FullScreenDetector (Application::CApplicationContext& appContext, CX11OpenGLDriver& driver) :
  26. CFullScreenDetector (appContext),
  27. m_driver (driver)
  28. {
  29. // do not use previous handler, it might stop the app under weird circumstances
  30. // these handlers might be replaced by other X11-specific functionality, they
  31. // should only be used to ignore X11 errors and nothing else
  32. // so this doesn't affect functionality
  33. XSetErrorHandler (CustomXErrorHandler);
  34. XSetIOErrorHandler (CustomXIOErrorHandler);
  35. this->initialize ();
  36. }
  37. CX11FullScreenDetector::~CX11FullScreenDetector ()
  38. {
  39. this->stop ();
  40. }
  41. bool CX11FullScreenDetector::anythingFullscreen () const
  42. {
  43. if (!this->getApplicationContext ().settings.render.pauseOnFullscreen)
  44. return false;
  45. // stop rendering if anything is fullscreen
  46. bool isFullscreen = false;
  47. XWindowAttributes attribs;
  48. Window _;
  49. Window* children;
  50. unsigned int nchildren;
  51. isFullscreen = false;
  52. if (!XQueryTree (this->m_display, this->m_root, &_, &_, &children, &nchildren))
  53. return false;
  54. auto ourWindow = reinterpret_cast <Window> (this->m_driver.getWindow ());
  55. Window parentWindow;
  56. {
  57. Window root, * schildren = NULL;
  58. unsigned int num_children;
  59. if (!XQueryTree (m_display, ourWindow, &root, &parentWindow, &schildren, &num_children))
  60. return false;
  61. if (schildren)
  62. XFree ((char*) children);
  63. }
  64. for (int i = 0; i < nchildren; i++)
  65. {
  66. if (!XGetWindowAttributes (this->m_display, children [i], &attribs))
  67. continue;
  68. // ignore ourselves
  69. if (ourWindow == children [i] || parentWindow == children [i])
  70. continue;
  71. if (attribs.map_state != IsViewable)
  72. continue;
  73. // compare width and height with the different screens we have
  74. for (const auto& screen : this->m_screens)
  75. {
  76. if (
  77. attribs.x == screen.viewport.x && attribs.y == screen.viewport.y &&
  78. attribs.width == screen.viewport.z && attribs.height == screen.viewport.w
  79. )
  80. {
  81. isFullscreen = true;
  82. break;
  83. }
  84. }
  85. }
  86. XFree (children);
  87. return isFullscreen;
  88. }
  89. void CX11FullScreenDetector::reset ()
  90. {
  91. this->stop ();
  92. this->initialize ();
  93. }
  94. void CX11FullScreenDetector::initialize ()
  95. {
  96. this->m_display = XOpenDisplay (nullptr);
  97. // set the error handling to try and recover from X disconnections
  98. #ifdef HAVE_XSETIOERROREXITHANDLER
  99. XSetIOErrorExitHandler (this->m_display, CustomXIOErrorExitHandler, this);
  100. #endif /* HAVE_XSETIOERROREXITHANDLER */
  101. int xrandr_result, xrandr_error;
  102. if (!XRRQueryExtension (this->m_display, &xrandr_result, &xrandr_error))
  103. {
  104. sLog.error ("XRandr is not present, fullscreen detection might not work");
  105. return;
  106. }
  107. this->m_root = DefaultRootWindow (this->m_display);
  108. XRRScreenResources* screenResources = XRRGetScreenResources (this->m_display, this->m_root);
  109. if (screenResources == nullptr)
  110. {
  111. sLog.error ("Cannot detect screen sizes using xrandr, fullscreen detection might not work");
  112. return;
  113. }
  114. for (int i = 0; i < screenResources->noutput; i ++)
  115. {
  116. XRROutputInfo* info = XRRGetOutputInfo (this->m_display, screenResources, screenResources->outputs [i]);
  117. // screen not in use, ignore it
  118. if (info == nullptr || info->connection != RR_Connected)
  119. continue;
  120. XRRCrtcInfo* crtc = XRRGetCrtcInfo (this->m_display, screenResources, info->crtc);
  121. // screen not active, ignore it
  122. if (crtc == nullptr)
  123. continue;
  124. // add the screen to the list of screens
  125. this->m_screens.push_back (
  126. {
  127. {crtc->x, crtc->y, crtc->width, crtc->height},
  128. info->name
  129. }
  130. );
  131. XRRFreeCrtcInfo (crtc);
  132. }
  133. XRRFreeScreenResources (screenResources);
  134. }
  135. void CX11FullScreenDetector::stop ()
  136. {
  137. if (this->m_display == nullptr)
  138. return;
  139. XCloseDisplay (this->m_display);
  140. this->m_display = nullptr;
  141. }
  142. }