]> git.tdb.fi Git - libs/gui.git/commitdiff
Extend the video mode API to support multiple monitors
authorMikko Rasa <tdb@tdb.fi>
Wed, 25 Sep 2013 18:33:57 +0000 (21:33 +0300)
committerMikko Rasa <tdb@tdb.fi>
Wed, 25 Sep 2013 18:41:32 +0000 (21:41 +0300)
Xf86vidmode can't support this and will be replaced with XRandR in the
imminent future.

source/graphics/display.cpp
source/graphics/display.h
source/graphics/display_private.h
source/graphics/monitor.cpp [new file with mode: 0644]
source/graphics/monitor.h [new file with mode: 0644]
source/graphics/videomode.cpp
source/graphics/videomode.h
source/graphics/windows/display.cpp
source/graphics/windows/display_platform.h
source/graphics/x11/display.cpp
source/graphics/x11/display_platform.h

index deaa5b560e7819db973b1f6a9fc2d94068bdf65e..22dd705df90c772e6901ae30f618f87db787077a 100644 (file)
@@ -17,6 +17,35 @@ void Display::remove_window(Window &wnd)
        priv->windows.erase(wnd.get_private().window);
 }
 
+const VideoMode &Display::get_desktop_mode() const
+{
+       if(!primary_monitor || !primary_monitor->desktop_mode)
+               throw logic_error("no desktop mode");
+       return *primary_monitor->desktop_mode;
+}
+
+void Display::restore_mode()
+{
+       for(list<Monitor>::const_iterator i=monitors.begin(); i!=monitors.end(); ++i)
+               if(i->desktop_mode)
+                       set_mode(*i->desktop_mode, false);
+}
+
+const VideoMode *Display::find_matching_mode(const VideoMode &mode) const
+{
+       for(list<VideoMode>::const_iterator i=modes.begin(); i!=modes.end(); ++i)
+       {
+               if(mode.monitor && i->monitor!=mode.monitor)
+                       continue;
+               if(mode.rate && i->rate!=mode.rate)
+                       continue;
+               if(i->width==mode.width && i->height==mode.height)
+                       return &*i;
+       }
+
+       return 0;
+}
+
 void Display::tick()
 {
        check_error();
index 370bf0dea81013c03d2212ae77e2cfabd9cb7981..8d2f61873495bb4056dd276c25f4f6e65e5fa37c 100644 (file)
@@ -4,6 +4,7 @@
 #include <list>
 #include <stdexcept>
 #include <string>
+#include "monitor.h"
 #include "videomode.h"
 
 namespace Msp {
@@ -17,8 +18,9 @@ public:
        struct Private;
 
 private:
+       std::list<Monitor> monitors;
+       Monitor *primary_monitor;
        std::list<VideoMode> modes;
-       VideoMode orig_mode;
        Private *priv;
 
 public:
@@ -30,11 +32,15 @@ public:
        void add_window(Window &);
        void remove_window(Window &);
 
+       const std::list<Monitor> &get_monitors() const { return monitors; }
        const std::list<VideoMode> &get_modes() const { return modes; }
-       const VideoMode &get_desktop_mode() const { return orig_mode; }
-       void set_mode(const VideoMode &);
-       void restore_mode() { set_mode(orig_mode); }
+       const VideoMode &get_desktop_mode() const;
+       void set_mode(const VideoMode &, bool = false);
+       void restore_mode();
+private:
+       const VideoMode *find_matching_mode(const VideoMode &) const;
 
+public:
        void tick();
 private:
        bool process_events();
index ec772b0870577ef9bd5e45f9f0fe837258ae4ef8..e5ece56ca050629af9bf6b7b62c9b15327bfd518 100644 (file)
@@ -2,6 +2,7 @@
 #define MSP_GRAPHICS_DISPLAY_PRIVATE_H_
 
 #include <map>
+#include <vector>
 #include "display.h"
 #include "display_platform.h"
 #include "window_private.h"
@@ -13,6 +14,7 @@ struct Display::Private
 {
        DisplayHandle display;
        std::map<WindowHandle, Window *> windows;
+       std::vector<MonitorHandle> monitors;
 };
 
 } // namespace Graphics
diff --git a/source/graphics/monitor.cpp b/source/graphics/monitor.cpp
new file mode 100644 (file)
index 0000000..0bc33ef
--- /dev/null
@@ -0,0 +1,14 @@
+#include "monitor.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Graphics {
+
+Monitor::Monitor():
+       index(0),
+       desktop_mode(0)
+{ }
+
+} // namespace Graphics
+} // namespace Msp
diff --git a/source/graphics/monitor.h b/source/graphics/monitor.h
new file mode 100644 (file)
index 0000000..d5e49bb
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef MSP_GRAPHICS_MONITOR_H_
+#define MSP_GRAPHICS_MONITOR_H_
+
+#include <list>
+#include <string>
+
+namespace Msp {
+namespace Graphics {
+
+struct VideoMode;
+
+struct Monitor
+{
+       unsigned index;
+       std::list<const VideoMode *> video_modes;
+       const VideoMode *desktop_mode;
+
+       Monitor();
+};
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif
index a07112a1eb5be6c2ff68dd8ab183e4eb11760927..5c9e821449c14b966b75562da13417fdcea615e8 100644 (file)
@@ -8,5 +8,20 @@ unsupported_video_mode::unsupported_video_mode(const VideoMode &mode):
        runtime_error(format("%dx%d", mode.width, mode.height))
 { }
 
+
+VideoMode::VideoMode():
+       monitor(0),
+       width(0),
+       height(0),
+       rate(0)
+{ }
+
+VideoMode::VideoMode(unsigned w, unsigned h):
+       monitor(0),
+       width(w),
+       height(h),
+       rate(0)
+{ }
+
 } // namespace Graphics
 } // namespace Msp
index d883d17d26ac8572928951ac68356f6d214f90bc..4054b4bb1308398e5278d926d082d811c529d003 100644 (file)
@@ -4,6 +4,7 @@
 namespace Msp {
 namespace Graphics {
 
+struct Monitor;
 struct VideoMode;
 
 class unsupported_video_mode: public std::runtime_error
@@ -16,12 +17,13 @@ public:
 
 struct VideoMode
 {
+       const Monitor *monitor;
        unsigned width;
        unsigned height;
        unsigned rate;
 
-       VideoMode(): width(0), height(0), rate(0) { }
-       VideoMode(unsigned w, unsigned h): width(w), height(h), rate(0) { }
+       VideoMode();
+       VideoMode(unsigned, unsigned);
 };
 
 } // namespace Graphics
index 64a6c4307e340c897dbe5fad293625945fa13d3a..ae19f57c2c3bf59de14d9ba5f509ddbbf4eb8e17 100644 (file)
@@ -8,24 +8,48 @@ namespace Msp {
 namespace Graphics {
 
 Display::Display(const string &):
+       primary_monitor(0),
        priv(new Private)
 {
        for(unsigned i=0;; ++i)
        {
-               DEVMODE info;
-               if(!EnumDisplaySettings(0, i, &info))
+               DISPLAY_DEVICE adapter_dev;
+               adapter_dev.cb = sizeof(adapter_dev);
+               if(!EnumDisplayDevices(0, i, &adapter_dev, 0))
                        break;
 
-               VideoMode mode(info.dmPelsWidth, info.dmPelsHeight);
-               mode.rate = info.dmDisplayFrequency;
-               modes.push_back(mode);
-       }
-       
-       DEVMODE info;
-       if(EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &info))
-       {
-               orig_mode = VideoMode(info.dmPelsWidth, info.dmPelsHeight);
-               orig_mode.rate = info.dmDisplayFrequency;
+               if(adapter_dev.StateFlags&DISPLAY_DEVICE_MIRRORING_DRIVER)
+                       continue;
+
+               monitors.push_back(Monitor());
+               Monitor &monitor = monitors.back();
+               monitor.index = monitors.size()-1;
+               priv->monitors.push_back(adapter_dev.DeviceName);
+
+               if(adapter_dev.StateFlags&DISPLAY_DEVICE_PRIMARY_DEVICE)
+                       primary_monitor = &monitor;
+
+               DEVMODE current;
+               bool have_current = EnumDisplaySettings(adapter_dev.DeviceName, ENUM_CURRENT_SETTINGS, &current);
+
+               for(unsigned j=0;; ++j)
+               {
+                       DEVMODE info;
+                       if(!EnumDisplaySettings(adapter_dev.DeviceName, j, &info))
+                               break;
+
+                       VideoMode mode(info.dmPelsWidth, info.dmPelsHeight);
+                       mode.monitor = &monitor;
+                       mode.rate = info.dmDisplayFrequency;
+                       if(find_matching_mode(mode))
+                               continue;
+
+                       modes.push_back(mode);
+                       monitor.video_modes.push_back(&modes.back());
+
+                       if(have_current && info.dmPelsWidth==current.dmPelsWidth && info.dmPelsHeight==current.dmPelsHeight && info.dmDisplayFrequency==current.dmDisplayFrequency)
+                               monitor.desktop_mode = &modes.back();
+               }
        }
 }
 
@@ -33,22 +57,26 @@ Display::~Display()
 {
 }
 
-void Display::set_mode(const VideoMode &mode)
+void Display::set_mode(const VideoMode &requested_mode, bool)
 {
+       const VideoMode *mode = find_matching_mode(requested_mode);
+       if(!mode)
+               throw unsupported_video_mode(requested_mode);
+
        DEVMODE info;
        info.dmSize = sizeof(DEVMODE);
        info.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT;
-       info.dmPelsWidth = mode.width;
-       info.dmPelsHeight = mode.height;
-       if(mode.rate)
+       info.dmPelsWidth = mode->width;
+       info.dmPelsHeight = mode->height;
+       if(requested_mode.rate)
        {
                info.dmFields |= DM_DISPLAYFREQUENCY;
-               info.dmDisplayFrequency = mode.rate;
+               info.dmDisplayFrequency = mode->rate;
        }
 
-       LONG ret = ChangeDisplaySettings(&info, CDS_FULLSCREEN);
+       LONG ret = ChangeDisplaySettingsEx(priv->monitors[mode->monitor->index].c_str(), &info, NULL, CDS_FULLSCREEN, NULL);
        if(ret!=DISP_CHANGE_SUCCESSFUL)
-               throw unsupported_video_mode(mode);
+               throw unsupported_video_mode(requested_mode);
 }
 
 bool Display::process_events()
index dac4dd70f14a60e42a4ecf8b7e7831d0246e8cf5..fd7cce3f6f53cb12348e36c4f5f58185c5cc94bd 100644 (file)
@@ -5,6 +5,7 @@ namespace Msp {
 namespace Graphics {
 
 typedef void *DisplayHandle;
+typedef std::string MonitorHandle;
 
 } // namespace Graphics
 } // namespace Msp
index b68f9bd2283aa8c71c93561ed5a768a8330c069a..6fee30b26ff6873d88b1f69e1b6ec789358e1c06 100644 (file)
@@ -45,6 +45,7 @@ namespace Msp {
 namespace Graphics {
 
 Display::Display(const string &disp_name):
+       primary_monitor(0),
        priv(new Private)
 {
        if(disp_name.empty())
@@ -59,27 +60,34 @@ Display::Display(const string &disp_name):
 #ifdef WITH_XF86VIDMODE
        int screen = DefaultScreen(priv->display);
 
+       monitors.push_back(Monitor());
+       Monitor &monitor = monitors.back();
+       primary_monitor = &monitor;
+
        int nmodes;
        XF86VidModeModeInfo **infos;
        XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
+
+       XF86VidModeModeLine modeline;
+       int dotclock;
+       XF86VidModeGetModeLine(priv->display, screen, &dotclock, &modeline);
+
        for(int i=0; i<nmodes; ++i)
        {
                XF86VidModeModeInfo &info = *infos[i];
        
                VideoMode mode(info.hdisplay, info.vdisplay);
+               mode.monitor = &monitor;
                if(info.htotal && info.vtotal)
                        mode.rate = info.dotclock/(info.htotal*info.vtotal);
                modes.push_back(mode);
+               monitor.video_modes.push_back(&modes.back());
+
+               if(info.htotal==modeline.htotal && info.vtotal==modeline.vtotal && info.dotclock==static_cast<unsigned>(dotclock))
+                       monitor.desktop_mode = &modes.back();
        }
 
        XFree(infos);
-
-       XF86VidModeModeLine modeline;
-       int dotclock;
-       XF86VidModeGetModeLine(priv->display, screen, &dotclock, &modeline);
-       orig_mode = VideoMode(modeline.hdisplay, modeline.vdisplay);
-       if(modeline.htotal && modeline.vtotal)
-               orig_mode.rate = dotclock/(modeline.htotal*modeline.vtotal);
 #endif
 }
 
@@ -89,7 +97,7 @@ Display::~Display()
        delete priv;
 }
 
-void Display::set_mode(const VideoMode &mode)
+void Display::set_mode(const VideoMode &mode, bool)
 {
 #ifdef WITH_XF86VIDMODE
        int screen = DefaultScreen(priv->display);
index 1ebe61635c2beb1639f13aa8d3bad7acd8cff190..f56d37c1789a81af760f3476953ad11787db46b9 100644 (file)
@@ -7,6 +7,7 @@ namespace Msp {
 namespace Graphics {
 
 typedef ::Display *DisplayHandle;
+typedef int MonitorHandle;
 
 } // namespace Graphics
 } // namespace Msp