4 #include <X11/extensions/Xrandr.h>
6 #include <msp/io/print.h>
7 #include <msp/strings/format.h>
8 #include <msp/strings/lexicalcast.h>
10 #include "display_private.h"
16 bool error_flag = false;
17 std::string error_msg;
19 int x_error_handler(Display *display, XErrorEvent *event)
22 XGetErrorText(display, event->error_code, err, sizeof(err));
24 string request_code = Msp::lexical_cast<string, int>(event->request_code);
26 XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), request_code.c_str(), req, sizeof(req));
28 string msg = Msp::format("Request %s failed with %s [%08X]", req, err, event->resourceid);
30 Msp::IO::print(Msp::IO::cerr, "Discarding error: %s\n", msg);
33 Msp::IO::print(Msp::IO::cerr, "%s\n", msg);
42 inline Msp::Graphics::VideoRotation rotation_from_sys(Rotation r)
46 case RR_Rotate_90: return Msp::Graphics::ROTATE_RIGHT;
47 case RR_Rotate_180: return Msp::Graphics::ROTATE_INVERTED;
48 case RR_Rotate_270: return Msp::Graphics::ROTATE_LEFT;
49 default: return Msp::Graphics::ROTATE_NORMAL;
53 inline Rotation rotation_to_sys(Msp::Graphics::VideoRotation r)
57 case Msp::Graphics::ROTATE_RIGHT: return RR_Rotate_90;
58 case Msp::Graphics::ROTATE_INVERTED: return RR_Rotate_180;
59 case Msp::Graphics::ROTATE_LEFT: return RR_Rotate_270;
60 default: return RR_Rotate_0;
64 inline bool monitor_x_compare(const Msp::Graphics::Monitor &m1, const Msp::Graphics::Monitor &m2)
66 if(m1.desktop_settings.mode && !m2.desktop_settings.mode)
68 return m1.desktop_settings.x<m2.desktop_settings.x;
72 inline unsigned mode_width(const Msp::Graphics::VideoMode &m, Msp::Graphics::VideoRotation r)
74 if(r==Msp::Graphics::ROTATE_RIGHT || r==Msp::Graphics::ROTATE_LEFT)
86 Display::Display(const string &disp_name):
92 priv->display = XOpenDisplay(0);
94 priv->display = XOpenDisplay(disp_name.c_str());
96 throw runtime_error("XOpenDisplay");
98 XSetErrorHandler(x_error_handler);
100 priv->root_window = DefaultRootWindow(priv->display);
102 err_dialog = new ErrorDialog(this);
107 if(XRRQueryExtension(priv->display, &event_base, &error_base))
110 XRRQueryVersion(priv->display, &major, &minor);
111 if(major>1 || (major==1 && minor>=2))
113 XRRScreenResources *res = XRRGetScreenResources(priv->display, priv->root_window);
114 RROutput primary = XRRGetOutputPrimary(priv->display, priv->root_window);
115 Atom edid_prop = XInternAtom(priv->display, RR_PROPERTY_RANDR_EDID, true);
117 map<RRMode, XRRModeInfo *> modes_by_id;
118 for(int i=0; i<res->nmode; ++i)
119 modes_by_id[res->modes[i].id] = &res->modes[i];
121 for(int i=0; i<res->noutput; ++i)
123 XRROutputInfo *output = XRRGetOutputInfo(priv->display, res, res->outputs[i]);
124 XRRCrtcInfo *crtc = (output->crtc ? XRRGetCrtcInfo(priv->display, res, output->crtc) : 0);
126 monitors.push_back(Monitor());
127 Monitor &monitor = monitors.back();
128 monitor.index = monitors.size()-1;
129 monitor.name.assign(output->name, output->nameLen);
130 priv->monitors.push_back(res->outputs[i]);
136 unsigned long length;
137 unsigned long overflow;
138 unsigned char *edid = 0;
139 XRRGetOutputProperty(priv->display, res->outputs[i], edid_prop, 0, 32, false, false, XA_INTEGER, &prop_type, &prop_format, &length, &overflow, &edid);
140 if(prop_type==XA_INTEGER && prop_format==8)
142 for(unsigned j=0; j<4; ++j)
144 unsigned offset = 54+j*18;
145 if(edid[offset]==0 && edid[offset+1]==0 && edid[offset+3]==0xFC)
148 for(k=0; (k<13 && edid[offset+5+k]!=0x0A); ++k) ;
149 monitor.name.assign(reinterpret_cast<char *>(edid+offset+5), k);
158 monitor.desktop_settings.rotation = rotation_from_sys(crtc->rotation);
159 monitor.desktop_settings.x = crtc->x;
160 monitor.desktop_settings.y = crtc->y;
163 if(res->outputs[i]==primary)
164 primary_monitor = &monitor;
166 for(int j=0; j<output->nmode; ++j)
168 map<RRMode, XRRModeInfo *>::iterator k = modes_by_id.find(output->modes[j]);
169 if(k==modes_by_id.end())
172 XRRModeInfo *info = k->second;
174 VideoMode mode(info->width, info->height);
175 mode.index = modes.size();
176 mode.monitor = &monitor;
177 mode.rate = static_cast<float>(info->dotClock)/(info->hTotal*info->vTotal);
178 if(find_mode(mode, 0.01f))
181 modes.push_back(mode);
182 priv->modes.push_back(info->id);
183 monitor.video_modes.push_back(&modes.back());
185 if(crtc && info->id==crtc->mode)
186 monitor.desktop_settings.mode = &modes.back();
189 monitor.current_settings = monitor.desktop_settings;
191 XRRFreeOutputInfo(output);
193 XRRFreeCrtcInfo(crtc);
196 XRRFreeScreenResources(res);
198 monitors.sort(monitor_x_compare);
199 Monitor *prev_enabled = 0;
200 for(list<Monitor>::iterator i=monitors.begin(); i!=monitors.end(); ++i)
201 if(i->desktop_settings.mode)
203 i->next_left = prev_enabled;
205 prev_enabled->next_right = &*i;
209 if(!primary_monitor || !primary_monitor->desktop_settings.mode)
211 // XRandR didn't give a sensible primary monitor. Try to guess one.
212 unsigned largest = 0;
213 for(list<Monitor>::iterator i=monitors.begin(); i!=monitors.end(); ++i)
214 if(const VideoMode *desktop_mode = i->desktop_settings.mode)
216 unsigned size = desktop_mode->width*desktop_mode->height;
220 primary_monitor = &*i;
231 XCloseDisplay(priv->display);
236 void Display::set_mode(const VideoMode &requested_mode, bool exclusive)
239 const VideoMode *mode = find_mode(requested_mode);
241 throw unsupported_video_mode(requested_mode);
243 VideoRotation requested_rotation = requested_mode.rotation;
244 if(requested_rotation==ROTATE_ANY)
245 requested_rotation = mode->monitor->desktop_settings.rotation;
247 XRRScreenResources *res = XRRGetScreenResources(priv->display, priv->root_window);
248 RROutput output = priv->monitors[mode->monitor->index];
249 XRROutputInfo *output_info = XRRGetOutputInfo(priv->display, res, output);
251 // Check if the output already has a CRTC and find a free one if it doesn't
252 RRCrtc crtc = output_info->crtc;
253 XRRCrtcInfo *crtc_info = 0;
255 crtc_info = XRRGetCrtcInfo(priv->display, res, crtc);
258 for(int i=0; i<res->ncrtc; ++i)
260 crtc_info = XRRGetCrtcInfo(priv->display, res, res->crtcs[i]);
261 if(!crtc_info->noutput)
263 crtc = res->crtcs[i];
266 XRRFreeCrtcInfo(crtc_info);
271 XRRFreeOutputInfo(output_info);
272 throw unsupported_video_mode(requested_mode);
281 // Disable other outputs for exclusive mode
282 for(list<Monitor>::iterator i=monitors.begin(); i!=monitors.end(); ++i)
283 if(&*i!=mode->monitor)
285 XRROutputInfo *o = XRRGetOutputInfo(priv->display, res, priv->monitors[i->index]);
287 XRRSetCrtcConfig(priv->display, res, o->crtc, CurrentTime, 0, 0, 0, RR_Rotate_0, 0, 0);
288 XRRFreeOutputInfo(o);
290 i->current_settings.mode = 0;
291 i->current_settings.rotation = ROTATE_NORMAL;
292 i->current_settings.x = 0;
293 i->current_settings.y = 0;
298 const Monitor *left = mode->monitor->next_left;
299 while(left && !left->current_settings.mode)
300 left = left->next_left;
304 const Monitor::Settings &cs = left->current_settings;
305 x = cs.x+mode_width(*cs.mode, cs.rotation);
310 XRRSetCrtcConfig(priv->display, res, crtc, CurrentTime, x, y, priv->modes[mode->index], rotation_to_sys(requested_rotation), &output, 1);
312 list<Monitor>::iterator i;
313 for(i=monitors.begin(); i!=monitors.end(); ++i)
314 if(&*i==mode->monitor)
316 i->current_settings.mode = mode;
317 i->current_settings.rotation = requested_rotation;
318 i->current_settings.x = x;
319 i->current_settings.y = y;
321 x += mode_width(*mode, requested_rotation);
326 for(; i!=monitors.end(); ++i)
327 if(i->current_settings.mode)
329 XRROutputInfo *o = XRRGetOutputInfo(priv->display, res, priv->monitors[i->index]);
330 XRRSetCrtcConfig(priv->display, res, o->crtc, CurrentTime, x, y, priv->modes[i->current_settings.mode->index], rotation_to_sys(i->current_settings.rotation), &priv->monitors[i->index], 1);
333 panning.timestamp = CurrentTime;
336 panning.width = i->current_settings.mode->width;
337 panning.height = i->current_settings.mode->height;
338 panning.track_left = panning.left;
339 panning.track_top = panning.top;
340 panning.track_width = panning.width;
341 panning.track_height = panning.height;
342 panning.border_left = 0;
343 panning.border_top = 0;
344 panning.border_right = 0;
345 panning.border_bottom = 0;
346 XRRSetPanning(priv->display, res, o->crtc, &panning);
348 XRRFreeOutputInfo(o);
350 i->current_settings.x = x;
351 i->current_settings.y = y;
353 x += mode_width(*i->current_settings.mode, i->current_settings.rotation);
356 XRRFreeOutputInfo(output_info);
357 XRRFreeCrtcInfo(crtc_info);
358 XRRFreeScreenResources(res);
360 (void)requested_mode;
362 throw runtime_error("no xrandr support");
366 bool Display::process_events()
368 int pending = XPending(priv->display);
375 XNextEvent(priv->display, &event.xevent);
379 map<WindowHandle, Window *>::iterator j = priv->windows.find(event.xevent.xany.window);
380 if(j!=priv->windows.end())
382 /* Filter keyboard autorepeat. If this packet is a KeyRelease and
383 the next one is a KeyPress with the exact same parameters, they
384 indicate autorepeat and must be dropped. */
385 if(event.xevent.type==KeyRelease && !j->second->get_keyboard_autorepeat() && pending>0)
387 XKeyEvent &kev = event.xevent.xkey;
389 XPeekEvent(priv->display, &ev2);
390 if(ev2.type==KeyPress)
392 XKeyEvent &kev2 = ev2.xkey;
393 if(kev2.window==kev.window && kev2.time==kev.time && kev2.keycode==kev.keycode)
395 XNextEvent(priv->display, &ev2);
402 j->second->event(event);
409 void Display::check_error()
414 throw runtime_error(error_msg);
419 } // namespace Graphics