#include <iostream>
#include <X11/Xlib.h>
-#ifdef WITH_XF86VIDMODE
-#include <X11/extensions/xf86vmode.h>
+#ifdef WITH_XRANDR
+#include <X11/extensions/Xrandr.h>
#endif
#include <msp/io/print.h>
#include <msp/strings/format.h>
XSetErrorHandler(x_error_handler);
-#ifdef WITH_XF86VIDMODE
- int screen = DefaultScreen(priv->display);
+#ifdef WITH_XRANDR
+ int event_base;
+ int error_base;
+ if(XRRQueryExtension(priv->display, &event_base, &error_base))
+ {
+ int major, minor;
+ 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);
- monitors.push_back(Monitor());
- Monitor &monitor = monitors.back();
- primary_monitor = &monitor;
+ map<RRMode, XRRModeInfo *> modes_by_id;
+ for(int i=0; i<res->nmode; ++i)
+ modes_by_id[res->modes[i].id] = &res->modes[i];
- int nmodes;
- XF86VidModeModeInfo **infos;
- XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
+ for(int i=0; i<res->noutput; ++i)
+ {
+ XRROutputInfo *output = XRRGetOutputInfo(priv->display, res, res->outputs[i]);
+ XRRCrtcInfo *crtc = (output->crtc ? XRRGetCrtcInfo(priv->display, res, output->crtc) : 0);
- XF86VidModeModeLine modeline;
- int dotclock;
- XF86VidModeGetModeLine(priv->display, screen, &dotclock, &modeline);
+ monitors.push_back(Monitor());
+ Monitor &monitor = monitors.back();
+ monitor.index = monitors.size()-1;
+ priv->monitors.push_back(res->outputs[i]);
- 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();
- }
+ if(res->outputs[i]==primary)
+ primary_monitor = &monitor;
+
+ for(int j=0; j<output->nmode; ++j)
+ {
+ map<RRMode, XRRModeInfo *>::iterator k = modes_by_id.find(output->modes[j]);
+ if(k==modes_by_id.end())
+ continue;
+
+ XRRModeInfo *info = k->second;
+
+ 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))
+ continue;
+
+ modes.push_back(mode);
+ priv->modes.push_back(info->id);
+ monitor.video_modes.push_back(&modes.back());
+
+ if(crtc && info->id==crtc->mode)
+ monitor.desktop_mode = &modes.back();
+ }
+
+ XRRFreeOutputInfo(output);
+ if(crtc)
+ XRRFreeCrtcInfo(crtc);
+ }
- XFree(infos);
+ XRRFreeScreenResources(res);
+ }
+ }
#endif
}
delete priv;
}
-void Display::set_mode(const VideoMode &mode, bool)
+void Display::set_mode(const VideoMode &requested_mode, bool exclusive)
{
-#ifdef WITH_XF86VIDMODE
- int screen = DefaultScreen(priv->display);
-
- int nmodes;
- XF86VidModeModeInfo **infos;
- XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
- for(int i=0; i<nmodes; ++i)
+#ifdef WITH_XRANDR
+ const VideoMode *mode = find_matching_mode(requested_mode);
+ if(!mode)
+ throw unsupported_video_mode(requested_mode);
+
+ WindowHandle root = DefaultRootWindow(priv->display);
+ XRRScreenResources *res = XRRGetScreenResources(priv->display, root);
+ RROutput output = priv->monitors[mode->monitor->index];
+ XRROutputInfo *output_info = XRRGetOutputInfo(priv->display, res, output);
+
+ // Check if the output already has a CRTC and find a free one if it doesn't
+ RRCrtc crtc = output_info->crtc;
+ XRRCrtcInfo *crtc_info = 0;
+ if(crtc)
+ crtc_info = XRRGetCrtcInfo(priv->display, res, crtc);
+ else
{
- XF86VidModeModeInfo &info = *infos[i];
+ for(int i=0; i<res->ncrtc; ++i)
+ {
+ crtc_info = XRRGetCrtcInfo(priv->display, res, res->crtcs[i]);
+ if(!crtc_info->noutput)
+ {
+ crtc = res->crtcs[i];
+ break;
+ }
+ XRRFreeCrtcInfo(crtc_info);
+ }
- unsigned rate = 0;
- if(info.htotal && info.vtotal)
- rate = info.dotclock/(info.htotal*info.vtotal);
- if(info.hdisplay==mode.width && info.vdisplay==mode.height && (mode.rate==0 || rate==mode.rate))
+ if(!crtc)
{
- XF86VidModeSwitchToMode(priv->display, screen, &info);
- XF86VidModeSetViewPort(priv->display, screen, 0, 0);
- return;
+ XRRFreeOutputInfo(output_info);
+ throw unsupported_video_mode(requested_mode);
}
}
- throw unsupported_video_mode(mode);
+ if(exclusive)
+ {
+ // Disable other outputs for exclusive mode
+ for(unsigned i=0; i<priv->monitors.size(); ++i)
+ if(i!=mode->monitor->index)
+ {
+ XRROutputInfo *o = XRRGetOutputInfo(priv->display, res, priv->monitors[i]);
+ if(o->crtc)
+ XRRSetCrtcConfig(priv->display, res, o->crtc, CurrentTime, 0, 0, 0, RR_Rotate_0, 0, 0);
+ XRRFreeOutputInfo(o);
+ }
+ }
+
+ XRRSetCrtcConfig(priv->display, res, crtc, CurrentTime, 0, 0, priv->modes[mode->index], RR_Rotate_0, &output, 1);
+
+ XRRFreeOutputInfo(output_info);
+ XRRFreeCrtcInfo(crtc_info);
+ XRRFreeScreenResources(res);
#else
- (void)mode;
- throw runtime_error("no xf86vidmode support");
+ (void)requested_mode;
+ (void)exclusive;
+ throw runtime_error("no xrandr support");
#endif
}