X11FullScreenDetector.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. #include "X11FullScreenDetector.h"
  2. #include "WallpaperEngine/Logging/Log.h"
  3. #include <X11/Xlib.h>
  4. #include <X11/extensions/Xrandr.h>
  5. #include "WallpaperEngine/Render/Drivers/GLFWOpenGLDriver.h"
  6. #include "WallpaperEngine/Render/Drivers/VideoFactories.h"
  7. namespace WallpaperEngine::Render::Drivers::Detectors {
  8. void CustomXIOErrorExitHandler (Display* dsp, void* userdata) {
  9. const auto context = static_cast<X11FullScreenDetector*> (userdata);
  10. sLog.debugerror ("Critical XServer error detected. Attempting to recover...");
  11. context->reset ();
  12. }
  13. int CustomXErrorHandler (Display* dpy, XErrorEvent* event) {
  14. sLog.debugerror ("Detected X error");
  15. return 0;
  16. }
  17. int CustomXIOErrorHandler (Display* dsp) {
  18. sLog.debugerror ("Detected X error");
  19. return 0;
  20. }
  21. X11FullScreenDetector::X11FullScreenDetector (Application::ApplicationContext& appContext, VideoDriver& driver) :
  22. FullScreenDetector (appContext), m_display (nullptr), m_root (0), m_driver (driver) {
  23. try {
  24. // throws if m_driver isn't actually a GLFWOpenGLDriver, so we can catch the misuse
  25. std::ignore = dynamic_cast<GLFWOpenGLDriver&> (this->m_driver);
  26. } catch (std::exception&) {
  27. sLog.exception ("X11 FullScreen Detector initialized with the wrong video driver... This is a bug...");
  28. }
  29. // not chaining the previous handler: these only need to ignore X11 errors, and chaining
  30. // could stop the app under weird circumstances
  31. XSetErrorHandler (CustomXErrorHandler);
  32. XSetIOErrorHandler (CustomXIOErrorHandler);
  33. this->initialize ();
  34. }
  35. X11FullScreenDetector::~X11FullScreenDetector () { this->stop (); }
  36. bool X11FullScreenDetector::anythingFullscreen () const {
  37. bool isFullscreen = false;
  38. XWindowAttributes attribs;
  39. Window _;
  40. Window* children;
  41. unsigned int nchildren;
  42. if (!XQueryTree (this->m_display, this->m_root, &_, &_, &children, &nchildren)) {
  43. return false;
  44. }
  45. const auto ourWindow = reinterpret_cast<Window> (dynamic_cast<GLFWOpenGLDriver&> (this->m_driver).getWindow ());
  46. Window parentWindow;
  47. {
  48. Window root, *schildren = nullptr;
  49. unsigned int num_children;
  50. if (!XQueryTree (this->m_display, ourWindow, &root, &parentWindow, &schildren, &num_children)) {
  51. return false;
  52. }
  53. if (schildren) {
  54. XFree (children);
  55. }
  56. }
  57. for (unsigned int i = 0; i < nchildren; i++) {
  58. if (!XGetWindowAttributes (this->m_display, children[i], &attribs)) {
  59. continue;
  60. }
  61. if (ourWindow == children[i] || parentWindow == children[i]) {
  62. continue;
  63. }
  64. if (attribs.map_state != IsViewable) {
  65. continue;
  66. }
  67. for (const auto& [name, viewport] : this->m_screens) {
  68. if (attribs.x == viewport.x && attribs.y == viewport.y && attribs.width == viewport.z
  69. && attribs.height == viewport.w) {
  70. isFullscreen = true;
  71. break;
  72. }
  73. }
  74. }
  75. XFree (children);
  76. return isFullscreen;
  77. }
  78. void X11FullScreenDetector::reset () {
  79. this->stop ();
  80. this->initialize ();
  81. }
  82. void X11FullScreenDetector::initialize () {
  83. this->m_display = XOpenDisplay (nullptr);
  84. // recover from X disconnections instead of aborting
  85. #ifdef HAVE_XSETIOERROREXITHANDLER
  86. XSetIOErrorExitHandler (this->m_display, CustomXIOErrorExitHandler, this);
  87. #endif /* HAVE_XSETIOERROREXITHANDLER */
  88. int xrandr_result, xrandr_error;
  89. if (!XRRQueryExtension (this->m_display, &xrandr_result, &xrandr_error)) {
  90. sLog.error ("XRandr is not present, fullscreen detection might not work");
  91. return;
  92. }
  93. this->m_root = DefaultRootWindow (this->m_display);
  94. XRRScreenResources* screenResources = XRRGetScreenResources (this->m_display, this->m_root);
  95. if (screenResources == nullptr) {
  96. sLog.error ("Cannot detect screen sizes using xrandr, fullscreen detection might not work");
  97. return;
  98. }
  99. for (int i = 0; i < screenResources->noutput; i++) {
  100. const XRROutputInfo* info = XRRGetOutputInfo (this->m_display, screenResources, screenResources->outputs[i]);
  101. // screen not in use, ignore it
  102. if (info == nullptr || info->connection != RR_Connected) {
  103. continue;
  104. }
  105. XRRCrtcInfo* crtc = XRRGetCrtcInfo (this->m_display, screenResources, info->crtc);
  106. // screen not active, ignore it
  107. if (crtc == nullptr) {
  108. continue;
  109. }
  110. this->m_screens.emplace (std::string (info->name), glm::ivec4 (crtc->x, crtc->y, crtc->width, crtc->height));
  111. XRRFreeCrtcInfo (crtc);
  112. }
  113. XRRFreeScreenResources (screenResources);
  114. }
  115. void X11FullScreenDetector::stop () {
  116. if (this->m_display == nullptr) {
  117. return;
  118. }
  119. XCloseDisplay (this->m_display);
  120. this->m_display = nullptr;
  121. }
  122. __attribute__ ((constructor)) void registerX11FullscreenDetector () {
  123. sVideoFactories.registerFullscreenDetector (
  124. "x11", [] (ApplicationContext& context, VideoDriver& driver) -> std::unique_ptr<FullScreenDetector> {
  125. return std::make_unique<X11FullScreenDetector> (context, driver);
  126. }
  127. );
  128. }
  129. } // namespace WallpaperEngine::Render::Drivers::Detectors