3 #include <X11/extensions/Xrandr.h>
5 #include <msp/io/print.h>
6 #include <msp/strings/format.h>
7 #include <msp/strings/lexicalcast.h>
9 #include "display_private.h"
15 bool error_flag = false;
16 std::string error_msg;
18 int x_error_handler(Display *display, XErrorEvent *event)
21 XGetErrorText(display, event->error_code, err, sizeof(err));
23 string request_code = Msp::lexical_cast<string, int>(event->request_code);
25 XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), request_code.c_str(), req, sizeof(req));
27 string msg = Msp::format("Request %s failed with %s [%08X]", req, err, event->resourceid);
29 Msp::IO::print(Msp::IO::cerr, "Discarding error: %s\n", msg);
32 Msp::IO::print(Msp::IO::cerr, "%s\n", msg);
41 inline Msp::Graphics::VideoRotation rotation_from_sys(Rotation r)
45 case RR_Rotate_90: return Msp::Graphics::ROTATE_RIGHT;
46 case RR_Rotate_180: return Msp::Graphics::ROTATE_INVERTED;
47 case RR_Rotate_270: return Msp::Graphics::ROTATE_LEFT;
48 default: return Msp::Graphics::ROTATE_NORMAL;
52 inline Rotation rotation_to_sys(Msp::Graphics::VideoRotation r)
56 case Msp::Graphics::ROTATE_RIGHT: return RR_Rotate_90;
57 case Msp::Graphics::ROTATE_INVERTED: return RR_Rotate_180;
58 case Msp::Graphics::ROTATE_LEFT: return RR_Rotate_270;
59 default: return RR_Rotate_0;
63 inline bool monitor_x_compare(const Msp::Graphics::Monitor &m1, const Msp::Graphics::Monitor &m2)
65 if(m1.desktop_mode && !m2.desktop_mode)
71 inline unsigned mode_width(const Msp::Graphics::VideoMode &m, Msp::Graphics::VideoRotation r)
73 if(r==Msp::Graphics::ROTATE_RIGHT || r==Msp::Graphics::ROTATE_LEFT)
85 Display::Display(const string &disp_name):
91 priv->display = XOpenDisplay(0);
93 priv->display = XOpenDisplay(disp_name.c_str());
95 throw runtime_error("XOpenDisplay");
97 XSetErrorHandler(x_error_handler);
99 priv->root_window = DefaultRootWindow(priv->display);
101 err_dialog = new ErrorDialog(this);
106 if(XRRQueryExtension(priv->display, &event_base, &error_base))
109 XRRQueryVersion(priv->display, &major, &minor);
110 if(major>1 || (major==1 && minor>=2))
112 XRRScreenResources *res = XRRGetScreenResources(priv->display, priv->root_window);
113 RROutput primary = XRRGetOutputPrimary(priv->display, priv->root_window);
115 map<RRMode, XRRModeInfo *> modes_by_id;
116 for(int i=0; i<res->nmode; ++i)
117 modes_by_id[res->modes[i].id] = &res->modes[i];
119 for(int i=0; i<res->noutput; ++i)
121 XRROutputInfo *output = XRRGetOutputInfo(priv->display, res, res->outputs[i]);
122 XRRCrtcInfo *crtc = (output->crtc ? XRRGetCrtcInfo(priv->display, res, output->crtc) : 0);
124 monitors.push_back(Monitor());
125 Monitor &monitor = monitors.back();
126 monitor.index = monitors.size()-1;
127 monitor.name.assign(output->name, output->nameLen);
128 priv->monitors.push_back(res->outputs[i]);
132 monitor.desktop_rotation = rotation_from_sys(crtc->rotation);
133 monitor.current_rotation = monitor.desktop_rotation;
138 if(res->outputs[i]==primary)
139 primary_monitor = &monitor;
141 for(int j=0; j<output->nmode; ++j)
143 map<RRMode, XRRModeInfo *>::iterator k = modes_by_id.find(output->modes[j]);
144 if(k==modes_by_id.end())
147 XRRModeInfo *info = k->second;
149 VideoMode mode(info->width, info->height);
150 mode.index = modes.size();
151 mode.monitor = &monitor;
152 mode.rate = static_cast<float>(info->dotClock)/(info->hTotal*info->vTotal);
153 if(find_mode(mode, 0.01f))
156 modes.push_back(mode);
157 priv->modes.push_back(info->id);
158 monitor.video_modes.push_back(&modes.back());
160 if(crtc && info->id==crtc->mode)
162 monitor.desktop_mode = &modes.back();
163 monitor.current_mode = monitor.desktop_mode;
167 XRRFreeOutputInfo(output);
169 XRRFreeCrtcInfo(crtc);
172 XRRFreeScreenResources(res);
174 monitors.sort(monitor_x_compare);
175 Monitor *prev_enabled = 0;
176 for(list<Monitor>::iterator i=monitors.begin(); i!=monitors.end(); ++i)
179 i->next_left = prev_enabled;
181 prev_enabled->next_right = &*i;
185 if(!primary_monitor || !primary_monitor->desktop_mode)
187 // XRandR didn't give a sensible primary monitor. Try to guess one.
188 unsigned largest = 0;
189 for(list<Monitor>::iterator i=monitors.begin(); i!=monitors.end(); ++i)
192 unsigned size = i->desktop_mode->width*i->desktop_mode->height;
196 primary_monitor = &*i;
207 XCloseDisplay(priv->display);
212 void Display::set_mode(const VideoMode &requested_mode, bool exclusive)
215 const VideoMode *mode = find_mode(requested_mode);
217 throw unsupported_video_mode(requested_mode);
219 VideoRotation requested_rotation = requested_mode.rotation;
220 if(requested_rotation==ROTATE_ANY)
221 requested_rotation = mode->monitor->desktop_rotation;
223 XRRScreenResources *res = XRRGetScreenResources(priv->display, priv->root_window);
224 RROutput output = priv->monitors[mode->monitor->index];
225 XRROutputInfo *output_info = XRRGetOutputInfo(priv->display, res, output);
227 // Check if the output already has a CRTC and find a free one if it doesn't
228 RRCrtc crtc = output_info->crtc;
229 XRRCrtcInfo *crtc_info = 0;
231 crtc_info = XRRGetCrtcInfo(priv->display, res, crtc);
234 for(int i=0; i<res->ncrtc; ++i)
236 crtc_info = XRRGetCrtcInfo(priv->display, res, res->crtcs[i]);
237 if(!crtc_info->noutput)
239 crtc = res->crtcs[i];
242 XRRFreeCrtcInfo(crtc_info);
247 XRRFreeOutputInfo(output_info);
248 throw unsupported_video_mode(requested_mode);
257 // Disable other outputs for exclusive mode
258 for(list<Monitor>::iterator i=monitors.begin(); i!=monitors.end(); ++i)
259 if(&*i!=mode->monitor)
261 XRROutputInfo *o = XRRGetOutputInfo(priv->display, res, priv->monitors[i->index]);
263 XRRSetCrtcConfig(priv->display, res, o->crtc, CurrentTime, 0, 0, 0, RR_Rotate_0, 0, 0);
264 XRRFreeOutputInfo(o);
267 i->current_rotation = ROTATE_NORMAL;
274 const Monitor *left = mode->monitor->next_left;
275 while(left && !left->current_mode)
276 left = left->next_left;
280 x = left->x+mode_width(*left->current_mode, left->current_rotation);
285 XRRSetCrtcConfig(priv->display, res, crtc, CurrentTime, x, y, priv->modes[mode->index], rotation_to_sys(requested_rotation), &output, 1);
287 list<Monitor>::iterator i;
288 for(i=monitors.begin(); i!=monitors.end(); ++i)
289 if(&*i==mode->monitor)
291 i->current_mode = mode;
292 i->current_rotation = requested_rotation;
296 x += mode_width(*mode, requested_rotation);
301 for(; i!=monitors.end(); ++i)
304 XRROutputInfo *o = XRRGetOutputInfo(priv->display, res, priv->monitors[i->index]);
305 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);
308 panning.timestamp = CurrentTime;
311 panning.width = i->current_mode->width;
312 panning.height = i->current_mode->height;
313 panning.track_left = panning.left;
314 panning.track_top = panning.top;
315 panning.track_width = panning.width;
316 panning.track_height = panning.height;
317 panning.border_left = 0;
318 panning.border_top = 0;
319 panning.border_right = 0;
320 panning.border_bottom = 0;
321 XRRSetPanning(priv->display, res, o->crtc, &panning);
323 XRRFreeOutputInfo(o);
328 x += mode_width(*i->current_mode, i->current_rotation);
331 XRRFreeOutputInfo(output_info);
332 XRRFreeCrtcInfo(crtc_info);
333 XRRFreeScreenResources(res);
335 (void)requested_mode;
337 throw runtime_error("no xrandr support");
341 bool Display::process_events()
343 int pending = XPending(priv->display);
350 XNextEvent(priv->display, &event.xevent);
354 map<WindowHandle, Window *>::iterator j = priv->windows.find(event.xevent.xany.window);
355 if(j!=priv->windows.end())
357 /* Filter keyboard autorepeat. If this packet is a KeyRelease and
358 the next one is a KeyPress with the exact same parameters, they
359 indicate autorepeat and must be dropped. */
360 if(event.xevent.type==KeyRelease && !j->second->get_keyboard_autorepeat() && pending>0)
362 XKeyEvent &kev = event.xevent.xkey;
364 XPeekEvent(priv->display, &ev2);
365 if(ev2.type==KeyPress)
367 XKeyEvent &kev2 = ev2.xkey;
368 if(kev2.window==kev.window && kev2.time==kev.time && kev2.keycode==kev.keycode)
370 XNextEvent(priv->display, &ev2);
377 j->second->event(event);
384 void Display::check_error()
389 throw runtime_error(error_msg);
394 } // namespace Graphics