|
|
@@ -0,0 +1,176 @@
|
|
|
+#include "CX11FullScreenDetector.h"
|
|
|
+#include "WallpaperEngine/Logging/CLog.h"
|
|
|
+
|
|
|
+#include <X11/Xlib.h>
|
|
|
+#include <X11/extensions/Xrandr.h>
|
|
|
+
|
|
|
+
|
|
|
+namespace WallpaperEngine::Render::Drivers::Detectors
|
|
|
+{
|
|
|
+ void CustomXIOErrorExitHandler (Display* dsp, void* userdata)
|
|
|
+ {
|
|
|
+ auto context = static_cast <CX11FullScreenDetector*> (userdata);
|
|
|
+
|
|
|
+ sLog.debugerror ("Critical XServer error detected. Attempting to recover...");
|
|
|
+
|
|
|
+ // refetch all the resources
|
|
|
+ context->reset ();
|
|
|
+ }
|
|
|
+
|
|
|
+ int CustomXErrorHandler (Display* dpy, XErrorEvent* event)
|
|
|
+ {
|
|
|
+ sLog.debugerror ("Detected X error");
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ int CustomXIOErrorHandler (Display* dsp)
|
|
|
+ {
|
|
|
+ sLog.debugerror ("Detected X error");
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ CX11FullScreenDetector::CX11FullScreenDetector (CVideoDriver& driver) :
|
|
|
+ m_driver (driver)
|
|
|
+ {
|
|
|
+ // do not use previous handler, it might stop the app under weird circumstances
|
|
|
+ // these handlers might be replaced by other X11-specific functionality, they
|
|
|
+ // should only be used to ignore X11 errors and nothing else
|
|
|
+ // so this doesn't affect functionality
|
|
|
+ XSetErrorHandler (CustomXErrorHandler);
|
|
|
+ XSetIOErrorHandler (CustomXIOErrorHandler);
|
|
|
+
|
|
|
+ this->initialize ();
|
|
|
+ }
|
|
|
+
|
|
|
+ CX11FullScreenDetector::~CX11FullScreenDetector ()
|
|
|
+ {
|
|
|
+ this->stop ();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool CX11FullScreenDetector::anythingFullscreen () const
|
|
|
+ {
|
|
|
+ // stop rendering if anything is fullscreen
|
|
|
+ bool isFullscreen = false;
|
|
|
+ XWindowAttributes attribs;
|
|
|
+ Window _;
|
|
|
+ Window* children;
|
|
|
+ unsigned int nchildren;
|
|
|
+
|
|
|
+ isFullscreen = false;
|
|
|
+
|
|
|
+ if (!XQueryTree (this->m_display, this->m_root, &_, &_, &children, &nchildren))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ auto ourWindow = reinterpret_cast <Window> (this->m_driver.getWindowHandle ());
|
|
|
+ Window parentWindow;
|
|
|
+
|
|
|
+ {
|
|
|
+ Window root, * schildren = NULL;
|
|
|
+ unsigned int num_children;
|
|
|
+
|
|
|
+ if (!XQueryTree (m_display, ourWindow, &root, &parentWindow, &schildren, &num_children))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (schildren)
|
|
|
+ XFree ((char*) children);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i = 0; i < nchildren; i++)
|
|
|
+ {
|
|
|
+ if (!XGetWindowAttributes (this->m_display, children [i], &attribs))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ // ignore ourselves
|
|
|
+ if (ourWindow == children [i] || parentWindow == children [i])
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (attribs.map_state != IsViewable)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ // compare width and height with the different screens we have
|
|
|
+ for (const auto& screen : this->m_screens)
|
|
|
+ {
|
|
|
+ if (
|
|
|
+ attribs.x == screen.viewport.x && attribs.y == screen.viewport.y &&
|
|
|
+ attribs.width == screen.viewport.z && attribs.height == screen.viewport.w
|
|
|
+ )
|
|
|
+ {
|
|
|
+ isFullscreen = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ XFree (children);
|
|
|
+
|
|
|
+ return isFullscreen;
|
|
|
+ }
|
|
|
+
|
|
|
+ void CX11FullScreenDetector::reset ()
|
|
|
+ {
|
|
|
+ this->stop ();
|
|
|
+ this->initialize ();
|
|
|
+ }
|
|
|
+
|
|
|
+ void CX11FullScreenDetector::initialize ()
|
|
|
+ {
|
|
|
+ this->m_display = XOpenDisplay (nullptr);
|
|
|
+
|
|
|
+ // set the error handling to try and recover from X disconnections
|
|
|
+#ifdef HAVE_XSETIOERROREXITHANDLER
|
|
|
+ XSetIOErrorExitHandler (this->m_display, CustomXIOErrorExitHandler, this);
|
|
|
+#endif /* HAVE_XSETIOERROREXITHANDLER */
|
|
|
+
|
|
|
+ int xrandr_result, xrandr_error;
|
|
|
+
|
|
|
+ if (!XRRQueryExtension (this->m_display, &xrandr_result, &xrandr_error))
|
|
|
+ {
|
|
|
+ sLog.error ("XRandr is not present, fullscreen detection might not work");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this->m_root = DefaultRootWindow (this->m_display);
|
|
|
+ XRRScreenResources* screenResources = XRRGetScreenResources (this->m_display, this->m_root);
|
|
|
+
|
|
|
+ if (screenResources == nullptr)
|
|
|
+ {
|
|
|
+ sLog.error ("Cannot detect screen sizes using xrandr, fullscreen detection might not work");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i = 0; i < screenResources->noutput; i ++)
|
|
|
+ {
|
|
|
+ XRROutputInfo* info = XRRGetOutputInfo (this->m_display, screenResources, screenResources->outputs [i]);
|
|
|
+
|
|
|
+ // screen not in use, ignore it
|
|
|
+ if (info == nullptr || info->connection != RR_Connected)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ XRRCrtcInfo* crtc = XRRGetCrtcInfo (this->m_display, screenResources, info->crtc);
|
|
|
+
|
|
|
+ // add the screen to the list of screens
|
|
|
+ this->m_screens.push_back (
|
|
|
+ {
|
|
|
+ {crtc->x, crtc->y, crtc->width, crtc->height},
|
|
|
+ info->name
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ XRRFreeCrtcInfo (crtc);
|
|
|
+ }
|
|
|
+
|
|
|
+ XRRFreeScreenResources (screenResources);
|
|
|
+ }
|
|
|
+
|
|
|
+ void CX11FullScreenDetector::stop ()
|
|
|
+ {
|
|
|
+ if (this->m_display == nullptr)
|
|
|
+ return;
|
|
|
+
|
|
|
+ XCloseDisplay (this->m_display);
|
|
|
+ this->m_display = nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|