From: Mikko Rasa Date: Wed, 25 Sep 2013 18:33:57 +0000 (+0300) Subject: Extend the video mode API to support multiple monitors X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=89ec4c335953556ca069960adf6ea3a5dc3bb499;p=libs%2Fgui.git Extend the video mode API to support multiple monitors Xf86vidmode can't support this and will be replaced with XRandR in the imminent future. --- diff --git a/source/graphics/display.cpp b/source/graphics/display.cpp index deaa5b5..22dd705 100644 --- a/source/graphics/display.cpp +++ b/source/graphics/display.cpp @@ -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::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::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(); diff --git a/source/graphics/display.h b/source/graphics/display.h index 370bf0d..8d2f618 100644 --- a/source/graphics/display.h +++ b/source/graphics/display.h @@ -4,6 +4,7 @@ #include #include #include +#include "monitor.h" #include "videomode.h" namespace Msp { @@ -17,8 +18,9 @@ public: struct Private; private: + std::list monitors; + Monitor *primary_monitor; std::list modes; - VideoMode orig_mode; Private *priv; public: @@ -30,11 +32,15 @@ public: void add_window(Window &); void remove_window(Window &); + const std::list &get_monitors() const { return monitors; } const std::list &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(); diff --git a/source/graphics/display_private.h b/source/graphics/display_private.h index ec772b0..e5ece56 100644 --- a/source/graphics/display_private.h +++ b/source/graphics/display_private.h @@ -2,6 +2,7 @@ #define MSP_GRAPHICS_DISPLAY_PRIVATE_H_ #include +#include #include "display.h" #include "display_platform.h" #include "window_private.h" @@ -13,6 +14,7 @@ struct Display::Private { DisplayHandle display; std::map windows; + std::vector monitors; }; } // namespace Graphics diff --git a/source/graphics/monitor.cpp b/source/graphics/monitor.cpp new file mode 100644 index 0000000..0bc33ef --- /dev/null +++ b/source/graphics/monitor.cpp @@ -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 index 0000000..d5e49bb --- /dev/null +++ b/source/graphics/monitor.h @@ -0,0 +1,24 @@ +#ifndef MSP_GRAPHICS_MONITOR_H_ +#define MSP_GRAPHICS_MONITOR_H_ + +#include +#include + +namespace Msp { +namespace Graphics { + +struct VideoMode; + +struct Monitor +{ + unsigned index; + std::list video_modes; + const VideoMode *desktop_mode; + + Monitor(); +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/videomode.cpp b/source/graphics/videomode.cpp index a07112a..5c9e821 100644 --- a/source/graphics/videomode.cpp +++ b/source/graphics/videomode.cpp @@ -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 diff --git a/source/graphics/videomode.h b/source/graphics/videomode.h index d883d17..4054b4b 100644 --- a/source/graphics/videomode.h +++ b/source/graphics/videomode.h @@ -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 diff --git a/source/graphics/windows/display.cpp b/source/graphics/windows/display.cpp index 64a6c43..ae19f57 100644 --- a/source/graphics/windows/display.cpp +++ b/source/graphics/windows/display.cpp @@ -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, ¤t); + + 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() diff --git a/source/graphics/windows/display_platform.h b/source/graphics/windows/display_platform.h index dac4dd7..fd7cce3 100644 --- a/source/graphics/windows/display_platform.h +++ b/source/graphics/windows/display_platform.h @@ -5,6 +5,7 @@ namespace Msp { namespace Graphics { typedef void *DisplayHandle; +typedef std::string MonitorHandle; } // namespace Graphics } // namespace Msp diff --git a/source/graphics/x11/display.cpp b/source/graphics/x11/display.cpp index b68f9bd..6fee30b 100644 --- a/source/graphics/x11/display.cpp +++ b/source/graphics/x11/display.cpp @@ -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(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); diff --git a/source/graphics/x11/display_platform.h b/source/graphics/x11/display_platform.h index 1ebe616..f56d37c 100644 --- a/source/graphics/x11/display_platform.h +++ b/source/graphics/x11/display_platform.h @@ -7,6 +7,7 @@ namespace Msp { namespace Graphics { typedef ::Display *DisplayHandle; +typedef int MonitorHandle; } // namespace Graphics } // namespace Msp