X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Fgraphics%2Fx11%2Fdisplay.cpp;h=b5888c6459620bca2fc118d923e8b98e10d609ea;hb=e82345f2c1286e684789854c7dbe32411bb0b05f;hp=6fee30b26ff6873d88b1f69e1b6ec789358e1c06;hpb=89ec4c335953556ca069960adf6ea3a5dc3bb499;p=libs%2Fgui.git diff --git a/source/graphics/x11/display.cpp b/source/graphics/x11/display.cpp index 6fee30b..b5888c6 100644 --- a/source/graphics/x11/display.cpp +++ b/source/graphics/x11/display.cpp @@ -1,20 +1,21 @@ -#include +#include "display.h" +#include "display_private.h" #include -#ifdef WITH_XF86VIDMODE -#include +#include +#ifdef WITH_XRANDR +#include #endif +#include #include #include #include -#include "display.h" -#include "display_private.h" using namespace std; namespace { bool error_flag = false; -std::string error_msg; +string error_msg; int x_error_handler(Display *display, XErrorEvent *event) { @@ -38,6 +39,30 @@ int x_error_handler(Display *display, XErrorEvent *event) return 0; } +#ifdef WITH_XRANDR +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; + } +} +#endif + } @@ -45,11 +70,10 @@ namespace Msp { namespace Graphics { Display::Display(const string &disp_name): - primary_monitor(0), priv(new Private) { if(disp_name.empty()) - priv->display = XOpenDisplay(0); + priv->display = XOpenDisplay(nullptr); else priv->display = XOpenDisplay(disp_name.c_str()); if(!priv->display) @@ -57,37 +81,164 @@ Display::Display(const string &disp_name): XSetErrorHandler(x_error_handler); -#ifdef WITH_XF86VIDMODE - int screen = DefaultScreen(priv->display); + priv->root_window = DefaultRootWindow(priv->display); - monitors.push_back(Monitor()); - Monitor &monitor = monitors.back(); - primary_monitor = &monitor; + err_dialog = new ErrorDialog(this); - int nmodes; - XF86VidModeModeInfo **infos; - XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos); +#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)) + { + XRRScreenResources *res = XRRGetScreenResources(priv->display, priv->root_window); + RROutput primary = XRRGetOutputPrimary(priv->display, priv->root_window); + Atom edid_prop = XInternAtom(priv->display, RR_PROPERTY_RANDR_EDID, true); + + map modes_by_id; + for(int i=0; inmode; ++i) + modes_by_id[res->modes[i].id] = &res->modes[i]; + + int primary_index = -1; + vector mode_monitor_indices; + vector desktop_mode_indices; + for(int i=0; inoutput; ++i) + { + XRROutputInfo *output = XRRGetOutputInfo(priv->display, res, res->outputs[i]); + XRRCrtcInfo *crtc = (output->crtc ? XRRGetCrtcInfo(priv->display, res, output->crtc) : nullptr); - XF86VidModeModeLine modeline; - int dotclock; - XF86VidModeGetModeLine(priv->display, screen, &dotclock, &modeline); + monitors.push_back(Monitor()); + Monitor &monitor = monitors.back(); + monitor.index = monitors.size()-1; + monitor.name.assign(output->name, output->nameLen); + priv->monitors.push_back(res->outputs[i]); - for(int i=0; i(dotclock)) - monitor.desktop_mode = &modes.back(); - } + if(edid_prop) + { + Atom prop_type; + int prop_format; + unsigned long length; + unsigned long overflow; + unsigned char *edid = nullptr; + XRRGetOutputProperty(priv->display, res->outputs[i], edid_prop, 0, 32, false, false, XA_INTEGER, &prop_type, &prop_format, &length, &overflow, &edid); + if(prop_type==XA_INTEGER && prop_format==8) + { + for(unsigned j=0; j<4; ++j) + { + unsigned offset = 54+j*18; + if(edid[offset]==0 && edid[offset+1]==0 && edid[offset+3]==0xFC) + { + unsigned k; + for(k=0; (k<13 && edid[offset+5+k]!=0x0A); ++k) ; + monitor.name.assign(reinterpret_cast(edid+offset+5), k); + } + } + } + XFree(edid); + } + + if(crtc) + { + monitor.desktop_settings.rotation = rotation_from_sys(crtc->rotation); + monitor.desktop_settings.x = crtc->x; + monitor.desktop_settings.y = crtc->y; + } + + if(res->outputs[i]==primary) + primary_index = monitor.index; + + unsigned first_mode = modes.size(); + int desktop_mode_index = -1; + for(int j=0; jnmode; ++j) + { + auto k = modes_by_id.find(output->modes[j]); + if(k==modes_by_id.end()) + continue; + + XRRModeInfo *info = k->second; + float rate = static_cast(info->dotClock)/(info->hTotal*info->vTotal); + + if(any_of(modes.begin()+first_mode, modes.end(), [&info, rate](const VideoMode &m){ + return (m.width==info->width && m.height==info->height && abs(m.rate-rate)<0.01f); + })) + continue; + + VideoMode mode(info->width, info->height); + mode.index = modes.size(); + mode.rate = rate; + + modes.push_back(mode); + priv->modes.push_back(info->id); + mode_monitor_indices.push_back(monitor.index); + + if(crtc && info->id==crtc->mode) + desktop_mode_index = mode.index; + } + desktop_mode_indices.push_back(desktop_mode_index); + + XRRFreeOutputInfo(output); + if(crtc) + XRRFreeCrtcInfo(crtc); + } + + XRRFreeScreenResources(res); + + for(unsigned i=0; i=0) + monitors[i].desktop_settings.mode = &modes[j]; + monitors[i].current_settings = monitors[i].desktop_settings; + } + + sort(monitors, [](const Msp::Graphics::Monitor &m1, const Msp::Graphics::Monitor &m2){ + if(m1.desktop_settings.mode && !m2.desktop_settings.mode) + return true; + if(!m1.desktop_settings.mode && m2.desktop_settings.mode) + return false; + return m1.desktop_settings.xvideo_modes.push_back(&modes[i]); + } + + Monitor *prev_enabled = nullptr; + for(Monitor &m: monitors) + if(m.desktop_settings.mode) + { + m.next_left = prev_enabled; + if(prev_enabled) + prev_enabled->next_right = &m; + prev_enabled = &m; + } + + if(primary_index>=0) + primary_monitor = &*find_member(monitors, static_cast(primary_index), &Monitor::index); + + if(!primary_monitor || !primary_monitor->desktop_settings.mode) + { + // XRandR didn't give a sensible primary monitor. Try to guess one. + unsigned largest = 0; + for(Monitor &m: monitors) + if(const VideoMode *desktop_mode = m.desktop_settings.mode) + { + unsigned size = desktop_mode->width*desktop_mode->height; + if(size>largest) + { + largest = size; + primary_monitor = &m; + } + } + } + } + } #endif } @@ -95,35 +246,94 @@ Display::~Display() { XCloseDisplay(priv->display); delete priv; + delete err_dialog; } -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; imonitor->desktop_settings.rotation; + + XRRScreenResources *res = XRRGetScreenResources(priv->display, priv->root_window); + 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 = nullptr; + if(crtc) + crtc_info = XRRGetCrtcInfo(priv->display, res, crtc); + else { - XF86VidModeModeInfo &info = *infos[i]; + for(int i=0; incrtc; ++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); + /* Due to the semantics of find_mode, the mode's monitor pointer must point + to one of the elements of the monitors list, which is non-const here. */ + Monitor *monitor = const_cast(mode->monitor); + + if(exclusive) + { + monitor->current_settings.mode = mode; + monitor->current_settings.rotation = requested_rotation; + monitor->current_settings.x = 0; + monitor->current_settings.y = 0; + + // Disable other outputs for exclusive mode + for(Monitor &m: monitors) + if(&m!=mode->monitor) + { + XRROutputInfo *o = XRRGetOutputInfo(priv->display, res, priv->monitors[m.index]); + if(o->crtc) + XRRSetCrtcConfig(priv->display, res, o->crtc, CurrentTime, 0, 0, 0, RR_Rotate_0, nullptr, 0); + XRRFreeOutputInfo(o); + + m.current_settings.mode = nullptr; + m.current_settings.rotation = ROTATE_NORMAL; + m.current_settings.x = 0; + m.current_settings.y = 0; + } + } + else + { + monitor->current_settings.x = monitor->desktop_settings.x; + monitor->current_settings.y = monitor->desktop_settings.y; + } + + RRMode mode_id = priv->modes[mode->index]; + int x = monitor->current_settings.x; + int y = monitor->current_settings.y; + Rotation sys_rot = rotation_to_sys(requested_rotation); + XRRSetCrtcConfig(priv->display, res, crtc, CurrentTime, x, y, mode_id, sys_rot, &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 } @@ -140,7 +350,7 @@ bool Display::process_events() check_error(); - map::iterator j = priv->windows.find(event.xevent.xany.window); + auto j = priv->windows.find(event.xevent.xany.window); if(j!=priv->windows.end()) { /* Filter keyboard autorepeat. If this packet is a KeyRelease and