]> git.tdb.fi Git - libs/gui.git/blobdiff - source/graphics/x11/display.cpp
Retain monitor positions when setting non-exclusive modes
[libs/gui.git] / source / graphics / x11 / display.cpp
index 6f677c3048d7a7ee96b1db5f585847490503f7f8..972cc8236f25024eec7fee0167811a4aeb76814b 100644 (file)
@@ -1,5 +1,5 @@
-#include <iostream>
 #include <X11/Xlib.h>
+#include <X11/Xatom.h>
 #ifdef WITH_XRANDR
 #include <X11/extensions/Xrandr.h>
 #endif
@@ -38,6 +38,7 @@ int x_error_handler(Display *display, XErrorEvent *event)
        return 0;
 }
 
+#ifdef WITH_XRANDR
 inline Msp::Graphics::VideoRotation rotation_from_sys(Rotation r)
 {
        switch(r)
@@ -60,12 +61,13 @@ inline Rotation rotation_to_sys(Msp::Graphics::VideoRotation r)
        }
 }
 
-bool monitor_x_compare(const Msp::Graphics::Monitor &m1, const Msp::Graphics::Monitor &m2)
+inline bool monitor_x_compare(const Msp::Graphics::Monitor &m1, const Msp::Graphics::Monitor &m2)
 {
-       if(m1.desktop_mode && !m2.desktop_mode)
+       if(m1.desktop_settings.mode && !m2.desktop_settings.mode)
                return true;
-       return m1.x<m2.x;
+       return m1.desktop_settings.x<m2.desktop_settings.x;
 }
+#endif
 
 inline unsigned mode_width(const Msp::Graphics::VideoMode &m, Msp::Graphics::VideoRotation r)
 {
@@ -83,7 +85,8 @@ namespace Graphics {
 
 Display::Display(const string &disp_name):
        primary_monitor(0),
-       priv(new Private)
+       priv(new Private),
+       focus_window(0)
 {
        if(disp_name.empty())
                priv->display = XOpenDisplay(0);
@@ -94,6 +97,10 @@ Display::Display(const string &disp_name):
 
        XSetErrorHandler(x_error_handler);
 
+       priv->root_window = DefaultRootWindow(priv->display);
+
+       err_dialog = new ErrorDialog(this);
+
 #ifdef WITH_XRANDR
        int event_base;
        int error_base;
@@ -103,9 +110,9 @@ Display::Display(const string &disp_name):
                XRRQueryVersion(priv->display, &major, &minor);
                if(major>1 || (major==1 && minor>=2))
                {
-                       WindowHandle root = DefaultRootWindow(priv->display);
-                       XRRScreenResources *res = XRRGetScreenResources(priv->display, root);
-                       RROutput primary = XRRGetOutputPrimary(priv->display, root);
+                       XRRScreenResources *res = XRRGetScreenResources(priv->display, priv->root_window);
+                       RROutput primary = XRRGetOutputPrimary(priv->display, priv->root_window);
+                       Atom edid_prop = XInternAtom(priv->display, RR_PROPERTY_RANDR_EDID, true);
 
                        map<RRMode, XRRModeInfo *> modes_by_id;
                        for(int i=0; i<res->nmode; ++i)
@@ -119,14 +126,38 @@ Display::Display(const string &disp_name):
                                monitors.push_back(Monitor());
                                Monitor &monitor = monitors.back();
                                monitor.index = monitors.size()-1;
+                               monitor.name.assign(output->name, output->nameLen);
                                priv->monitors.push_back(res->outputs[i]);
 
+                               if(edid_prop)
+                               {
+                                       Atom prop_type;
+                                       int prop_format;
+                                       unsigned long length;
+                                       unsigned long overflow;
+                                       unsigned char *edid = 0;
+                                       XRRGetOutputProperty(priv->display, res->outputs[i], edid_prop, 0, 32, false, false, XA_INTEGER, &prop_type, &prop_format, &length, &overflow, &edid);
+                                       if(prop_type==XA_INTEGER && prop_format==8)
+                                       {
+                                               for(unsigned j=0; j<4; ++j)
+                                               {
+                                                       unsigned offset = 54+j*18;
+                                                       if(edid[offset]==0 && edid[offset+1]==0 && edid[offset+3]==0xFC)
+                                                       {
+                                                               unsigned k;
+                                                               for(k=0; (k<13 && edid[offset+5+k]!=0x0A); ++k) ;
+                                                               monitor.name.assign(reinterpret_cast<char *>(edid+offset+5), k);
+                                                       }
+                                               }
+                                       }
+                                       XFree(edid);
+                               }
+
                                if(crtc)
                                {
-                                       monitor.desktop_rotation = rotation_from_sys(crtc->rotation);
-                                       monitor.current_rotation = monitor.desktop_rotation;
-                                       monitor.x = crtc->x;
-                                       monitor.y = crtc->y;
+                                       monitor.desktop_settings.rotation = rotation_from_sys(crtc->rotation);
+                                       monitor.desktop_settings.x = crtc->x;
+                                       monitor.desktop_settings.y = crtc->y;
                                }
 
                                if(res->outputs[i]==primary)
@@ -143,8 +174,8 @@ Display::Display(const string &disp_name):
                                        VideoMode mode(info->width, info->height);
                                        mode.index = modes.size();
                                        mode.monitor = &monitor;
-                                       mode.rate = info->dotClock/(info->hTotal*info->vTotal);
-                                       if(find_matching_mode(mode))
+                                       mode.rate = static_cast<float>(info->dotClock)/(info->hTotal*info->vTotal);
+                                       if(find_mode(mode, 0.01f))
                                                continue;
 
                                        modes.push_back(mode);
@@ -152,12 +183,11 @@ Display::Display(const string &disp_name):
                                        monitor.video_modes.push_back(&modes.back());
 
                                        if(crtc && info->id==crtc->mode)
-                                       {
-                                               monitor.desktop_mode = &modes.back();
-                                               monitor.current_mode = monitor.desktop_mode;
-                                       }
+                                               monitor.desktop_settings.mode = &modes.back();
                                }
 
+                               monitor.current_settings = monitor.desktop_settings;
+
                                XRRFreeOutputInfo(output);
                                if(crtc)
                                        XRRFreeCrtcInfo(crtc);
@@ -168,7 +198,7 @@ Display::Display(const string &disp_name):
                        monitors.sort(monitor_x_compare);
                        Monitor *prev_enabled = 0;
                        for(list<Monitor>::iterator i=monitors.begin(); i!=monitors.end(); ++i)
-                               if(i->desktop_mode)
+                               if(i->desktop_settings.mode)
                                {
                                        i->next_left = prev_enabled;
                                        if(prev_enabled)
@@ -176,14 +206,14 @@ Display::Display(const string &disp_name):
                                        prev_enabled = &*i;
                                }
 
-                       if(!primary_monitor || !primary_monitor->desktop_mode)
+                       if(!primary_monitor || !primary_monitor->desktop_settings.mode)
                        {
                                // XRandR didn't give a sensible primary monitor.  Try to guess one.
                                unsigned largest = 0;
                                for(list<Monitor>::iterator i=monitors.begin(); i!=monitors.end(); ++i)
-                                       if(i->desktop_mode)
+                                       if(const VideoMode *desktop_mode = i->desktop_settings.mode)
                                        {
-                                               unsigned size = i->desktop_mode->width*i->desktop_mode->height;
+                                               unsigned size = desktop_mode->width*desktop_mode->height;
                                                if(size>largest)
                                                {
                                                        largest = size;
@@ -200,21 +230,21 @@ Display::~Display()
 {
        XCloseDisplay(priv->display);
        delete priv;
+       delete err_dialog;
 }
 
 void Display::set_mode(const VideoMode &requested_mode, bool exclusive)
 {
 #ifdef WITH_XRANDR
-       const VideoMode *mode = find_matching_mode(requested_mode);
+       const VideoMode *mode = find_mode(requested_mode);
        if(!mode)
                throw unsupported_video_mode(requested_mode);
 
        VideoRotation requested_rotation = requested_mode.rotation;
        if(requested_rotation==ROTATE_ANY)
-               requested_rotation = mode->monitor->desktop_rotation;
+               requested_rotation = mode->monitor->desktop_settings.rotation;
 
-       WindowHandle root = DefaultRootWindow(priv->display);
-       XRRScreenResources *res = XRRGetScreenResources(priv->display, root);
+       XRRScreenResources *res = XRRGetScreenResources(priv->display, priv->root_window);
        RROutput output = priv->monitors[mode->monitor->index];
        XRROutputInfo *output_info = XRRGetOutputInfo(priv->display, res, output);
 
@@ -243,11 +273,17 @@ void Display::set_mode(const VideoMode &requested_mode, bool exclusive)
                }
        }
 
-       int x = 0;
-       int y = 0;
+       /* Due to the semantics of find_mode, the mode's monitor pointer must point
+       to one of the elements of the monitors list, which is non-const here. */
+       Monitor *monitor = const_cast<Monitor *>(mode->monitor);
 
        if(exclusive)
        {
+               monitor->current_settings.mode = mode;
+               monitor->current_settings.rotation = requested_rotation;
+               monitor->current_settings.x = 0;
+               monitor->current_settings.y = 0;
+
                // Disable other outputs for exclusive mode
                for(list<Monitor>::iterator i=monitors.begin(); i!=monitors.end(); ++i)
                        if(&*i!=mode->monitor)
@@ -257,53 +293,23 @@ void Display::set_mode(const VideoMode &requested_mode, bool exclusive)
                                        XRRSetCrtcConfig(priv->display, res, o->crtc, CurrentTime, 0, 0, 0, RR_Rotate_0, 0, 0);
                                XRRFreeOutputInfo(o);
 
-                               i->current_mode = 0;
-                               i->current_rotation = ROTATE_NORMAL;
-                               i->x = 0;
-                               i->y = 0;
+                               i->current_settings.mode = 0;
+                               i->current_settings.rotation = ROTATE_NORMAL;
+                               i->current_settings.x = 0;
+                               i->current_settings.y = 0;
                        }
        }
        else
        {
-               const Monitor *left = mode->monitor->next_left;
-               while(left && !left->current_mode)
-                       left = left->next_left;
-
-               if(left)
-               {
-                       x = left->x+mode_width(*left->current_mode, left->current_rotation);
-                       y = left->y;
-               }
+               monitor->current_settings.x = monitor->desktop_settings.x;
+               monitor->current_settings.y = monitor->desktop_settings.y;
        }
 
-       XRRSetCrtcConfig(priv->display, res, crtc, CurrentTime, x, y, priv->modes[mode->index], rotation_to_sys(requested_rotation), &output, 1);
-
-       list<Monitor>::iterator i;
-       for(i=monitors.begin(); i!=monitors.end(); ++i)
-               if(&*i==mode->monitor)
-               {
-                       i->current_mode = mode;
-                       i->current_rotation = requested_rotation;
-                       i->x = x;
-                       i->y = y;
-
-                       x += mode_width(*mode, requested_rotation);
-                       ++i;
-                       break;
-               }
-
-       for(; i!=monitors.end(); ++i)
-               if(i->current_mode)
-               {
-                       XRROutputInfo *o = XRRGetOutputInfo(priv->display, res, priv->monitors[i->index]);
-                       XRRSetCrtcConfig(priv->display, res, o->crtc, CurrentTime, x, y, priv->modes[i->current_mode->index], rotation_to_sys(i->current_rotation), &priv->monitors[i->index], 1);
-                       XRRFreeOutputInfo(o);
-
-                       i->x = x;
-                       i->y = y;
-
-                       x += mode_width(*i->current_mode, i->current_rotation);
-               }
+       RRMode mode_id = priv->modes[mode->index];
+       int x = monitor->current_settings.x;
+       int y = monitor->current_settings.y;
+       Rotation sys_rot = rotation_to_sys(requested_rotation);
+       XRRSetCrtcConfig(priv->display, res, crtc, CurrentTime, x, y, mode_id, sys_rot, &output, 1);
 
        XRRFreeOutputInfo(output_info);
        XRRFreeCrtcInfo(crtc_info);