From 0913ba6fdd0e28740e0b521f275b9ce82c4a1b7a Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 26 Sep 2013 19:29:18 +0300 Subject: [PATCH] Expose a lot more information through the Monitor struct This includes current mode and location in a multi-monitor setup. Rotated monitors and modes are also supported. It's enough for many common cases, though more exotic layouts still cannot be restored. I'm not certain exactly how Windows handles multiple monitors. Additional research is required. --- source/graphics/monitor.cpp | 9 ++- source/graphics/monitor.h | 9 ++- source/graphics/videomode.cpp | 6 +- source/graphics/videomode.h | 10 +++ source/graphics/windows/display.cpp | 4 + source/graphics/x11/display.cpp | 118 +++++++++++++++++++++++++++- 6 files changed, 147 insertions(+), 9 deletions(-) diff --git a/source/graphics/monitor.cpp b/source/graphics/monitor.cpp index 0bc33ef..1ac4383 100644 --- a/source/graphics/monitor.cpp +++ b/source/graphics/monitor.cpp @@ -7,7 +7,14 @@ namespace Graphics { Monitor::Monitor(): index(0), - desktop_mode(0) + desktop_mode(0), + desktop_rotation(ROTATE_NORMAL), + current_mode(0), + current_rotation(ROTATE_NORMAL), + x(0), + y(0), + next_left(0), + next_right(0) { } } // namespace Graphics diff --git a/source/graphics/monitor.h b/source/graphics/monitor.h index d5e49bb..18586db 100644 --- a/source/graphics/monitor.h +++ b/source/graphics/monitor.h @@ -3,17 +3,22 @@ #include #include +#include "videomode.h" namespace Msp { namespace Graphics { -struct VideoMode; - struct Monitor { unsigned index; std::list video_modes; const VideoMode *desktop_mode; + VideoRotation desktop_rotation; + const VideoMode *current_mode; + VideoRotation current_rotation; + int x, y; + const Monitor *next_left; + const Monitor *next_right; Monitor(); }; diff --git a/source/graphics/videomode.cpp b/source/graphics/videomode.cpp index 888cad8..b3ca948 100644 --- a/source/graphics/videomode.cpp +++ b/source/graphics/videomode.cpp @@ -14,7 +14,8 @@ VideoMode::VideoMode(): monitor(0), width(0), height(0), - rate(0) + rate(0), + rotation(ROTATE_ANY) { } VideoMode::VideoMode(unsigned w, unsigned h): @@ -22,7 +23,8 @@ VideoMode::VideoMode(unsigned w, unsigned h): monitor(0), width(w), height(h), - rate(0) + rate(0), + rotation(ROTATE_ANY) { } } // namespace Graphics diff --git a/source/graphics/videomode.h b/source/graphics/videomode.h index 328d6ea..2836e11 100644 --- a/source/graphics/videomode.h +++ b/source/graphics/videomode.h @@ -17,6 +17,15 @@ public: }; +enum VideoRotation +{ + ROTATE_ANY, + ROTATE_NORMAL, + ROTATE_LEFT, + ROTATE_RIGHT, + ROTATE_INVERTED +}; + struct VideoMode { unsigned index; @@ -24,6 +33,7 @@ struct VideoMode unsigned width; unsigned height; unsigned rate; + VideoRotation rotation; VideoMode(); VideoMode(unsigned, unsigned); diff --git a/source/graphics/windows/display.cpp b/source/graphics/windows/display.cpp index 43f8699..6553a8c 100644 --- a/source/graphics/windows/display.cpp +++ b/source/graphics/windows/display.cpp @@ -78,6 +78,10 @@ void Display::set_mode(const VideoMode &requested_mode, bool) LONG ret = ChangeDisplaySettingsEx(priv->monitors[mode->monitor->index].c_str(), &info, NULL, CDS_FULLSCREEN, NULL); if(ret!=DISP_CHANGE_SUCCESSFUL) throw unsupported_video_mode(requested_mode); + + for(list::iterator i=monitors.begin(); i!=monitors.end(); ++i) + if(&*i==mode->monitor) + i->current_mode = mode; } bool Display::process_events() diff --git a/source/graphics/x11/display.cpp b/source/graphics/x11/display.cpp index 2f276a3..368b67c 100644 --- a/source/graphics/x11/display.cpp +++ b/source/graphics/x11/display.cpp @@ -38,6 +38,43 @@ int x_error_handler(Display *display, XErrorEvent *event) return 0; } +inline Msp::Graphics::VideoRotation rotation_from_sys(Rotation r) +{ + switch(r) + { + case RR_Rotate_90: return Msp::Graphics::ROTATE_RIGHT; + case RR_Rotate_180: return Msp::Graphics::ROTATE_INVERTED; + case RR_Rotate_270: return Msp::Graphics::ROTATE_LEFT; + default: return Msp::Graphics::ROTATE_NORMAL; + } +} + +inline Rotation rotation_to_sys(Msp::Graphics::VideoRotation r) +{ + switch(r) + { + case Msp::Graphics::ROTATE_RIGHT: return RR_Rotate_90; + case Msp::Graphics::ROTATE_INVERTED: return RR_Rotate_180; + case Msp::Graphics::ROTATE_LEFT: return RR_Rotate_270; + default: return RR_Rotate_0; + } +} + +bool monitor_x_compare(const Msp::Graphics::Monitor &m1, const Msp::Graphics::Monitor &m2) +{ + if(m1.desktop_mode && !m2.desktop_mode) + return true; + return m1.xmonitors.push_back(res->outputs[i]); + if(crtc) + { + monitor.desktop_rotation = rotation_from_sys(crtc->rotation); + monitor.current_rotation = monitor.desktop_rotation; + monitor.x = crtc->x; + monitor.y = crtc->y; + } + if(res->outputs[i]==primary) primary_monitor = &monitor; @@ -107,7 +152,10 @@ 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; + } } XRRFreeOutputInfo(output); @@ -116,6 +164,17 @@ Display::Display(const string &disp_name): } XRRFreeScreenResources(res); + + monitors.sort(monitor_x_compare); + Monitor *prev_enabled = 0; + for(list::iterator i=monitors.begin(); i!=monitors.end(); ++i) + if(i->desktop_mode) + { + i->next_left = prev_enabled; + if(prev_enabled) + prev_enabled->next_right = &*i; + prev_enabled = &*i; + } } } #endif @@ -134,6 +193,10 @@ void Display::set_mode(const VideoMode &requested_mode, bool exclusive) 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; + WindowHandle root = DefaultRootWindow(priv->display); XRRScreenResources *res = XRRGetScreenResources(priv->display, root); RROutput output = priv->monitors[mode->monitor->index]; @@ -164,20 +227,67 @@ void Display::set_mode(const VideoMode &requested_mode, bool exclusive) } } + int x = 0; + int y = 0; + if(exclusive) { // Disable other outputs for exclusive mode - for(unsigned i=0; imonitors.size(); ++i) - if(i!=mode->monitor->index) + for(list::iterator i=monitors.begin(); i!=monitors.end(); ++i) + if(&*i!=mode->monitor) { - XRROutputInfo *o = XRRGetOutputInfo(priv->display, res, priv->monitors[i]); + XRROutputInfo *o = XRRGetOutputInfo(priv->display, res, priv->monitors[i->index]); if(o->crtc) 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; } } + else + { + const Monitor *left = mode->monitor->next_left; + while(left && !left->current_mode) + left = left->next_left; - XRRSetCrtcConfig(priv->display, res, crtc, CurrentTime, 0, 0, priv->modes[mode->index], RR_Rotate_0, &output, 1); + if(left) + { + x = left->x+mode_width(*left->current_mode, left->current_rotation); + y = left->y; + } + } + + XRRSetCrtcConfig(priv->display, res, crtc, CurrentTime, x, y, priv->modes[mode->index], rotation_to_sys(requested_rotation), &output, 1); + + list::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); + } XRRFreeOutputInfo(output_info); XRRFreeCrtcInfo(crtc_info); -- 2.45.2