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();
#include <list>
#include <stdexcept>
#include <string>
+#include "monitor.h"
#include "videomode.h"
namespace Msp {
struct Private;
private:
+ std::list<Monitor> monitors;
+ Monitor *primary_monitor;
std::list<VideoMode> modes;
- VideoMode orig_mode;
Private *priv;
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();
#define MSP_GRAPHICS_DISPLAY_PRIVATE_H_
#include <map>
+#include <vector>
#include "display.h"
#include "display_platform.h"
#include "window_private.h"
{
DisplayHandle display;
std::map<WindowHandle, Window *> windows;
+ std::vector<MonitorHandle> monitors;
};
} // namespace Graphics
--- /dev/null
+#include "monitor.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Graphics {
+
+Monitor::Monitor():
+ index(0),
+ desktop_mode(0)
+{ }
+
+} // namespace Graphics
+} // namespace Msp
--- /dev/null
+#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
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
namespace Msp {
namespace Graphics {
+struct Monitor;
struct VideoMode;
class unsupported_video_mode: public std::runtime_error
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
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();
+ }
}
}
{
}
-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()
namespace Graphics {
typedef void *DisplayHandle;
+typedef std::string MonitorHandle;
} // namespace Graphics
} // namespace Msp
namespace Graphics {
Display::Display(const string &disp_name):
+ primary_monitor(0),
priv(new Private)
{
if(disp_name.empty())
#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
}
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);
namespace Graphics {
typedef ::Display *DisplayHandle;
+typedef int MonitorHandle;
} // namespace Graphics
} // namespace Msp