2 #include "display_private.h"
6 #include <X11/extensions/Xrandr.h>
8 #include <msp/core/algorithm.h>
9 #include <msp/io/print.h>
10 #include <msp/strings/format.h>
11 #include <msp/strings/lexicalcast.h>
17 bool error_flag = false;
20 int x_error_handler(Display *display, XErrorEvent *event)
23 XGetErrorText(display, event->error_code, err, sizeof(err));
25 string request_code = Msp::lexical_cast<string, int>(event->request_code);
27 XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), request_code.c_str(), req, sizeof(req));
29 string msg = Msp::format("Request %s failed with %s [%08X]", req, err, event->resourceid);
31 Msp::IO::print(Msp::IO::cerr, "Discarding error: %s\n", msg);
34 Msp::IO::print(Msp::IO::cerr, "%s\n", msg);
43 inline Msp::Graphics::VideoRotation rotation_from_sys(Rotation r)
47 case RR_Rotate_90: return Msp::Graphics::ROTATE_RIGHT;
48 case RR_Rotate_180: return Msp::Graphics::ROTATE_INVERTED;
49 case RR_Rotate_270: return Msp::Graphics::ROTATE_LEFT;
50 default: return Msp::Graphics::ROTATE_NORMAL;
54 inline Rotation rotation_to_sys(Msp::Graphics::VideoRotation r)
58 case Msp::Graphics::ROTATE_RIGHT: return RR_Rotate_90;
59 case Msp::Graphics::ROTATE_INVERTED: return RR_Rotate_180;
60 case Msp::Graphics::ROTATE_LEFT: return RR_Rotate_270;
61 default: return RR_Rotate_0;
65 inline bool monitor_x_compare(const Msp::Graphics::Monitor &m1, const Msp::Graphics::Monitor &m2)
67 if(m1.desktop_settings.mode && !m2.desktop_settings.mode)
69 if(!m1.desktop_settings.mode && m2.desktop_settings.mode)
71 return m1.desktop_settings.x<m2.desktop_settings.x;
75 inline unsigned mode_width(const Msp::Graphics::VideoMode &m, Msp::Graphics::VideoRotation r)
77 if(r==Msp::Graphics::ROTATE_RIGHT || r==Msp::Graphics::ROTATE_LEFT)
89 Display::Display(const string &disp_name):
93 priv->display = XOpenDisplay(nullptr);
95 priv->display = XOpenDisplay(disp_name.c_str());
97 throw runtime_error("XOpenDisplay");
99 XSetErrorHandler(x_error_handler);
101 priv->root_window = DefaultRootWindow(priv->display);
103 err_dialog = new ErrorDialog(this);
108 if(XRRQueryExtension(priv->display, &event_base, &error_base))
111 XRRQueryVersion(priv->display, &major, &minor);
112 if(major>1 || (major==1 && minor>=2))
114 XRRScreenResources *res = XRRGetScreenResources(priv->display, priv->root_window);
115 RROutput primary = XRRGetOutputPrimary(priv->display, priv->root_window);
116 Atom edid_prop = XInternAtom(priv->display, RR_PROPERTY_RANDR_EDID, true);
118 map<RRMode, XRRModeInfo *> modes_by_id;
119 for(int i=0; i<res->nmode; ++i)
120 modes_by_id[res->modes[i].id] = &res->modes[i];
122 int primary_index = -1;
123 vector<unsigned> mode_monitor_indices;
124 vector<int> desktop_mode_indices;
125 for(int i=0; i<res->noutput; ++i)
127 XRROutputInfo *output = XRRGetOutputInfo(priv->display, res, res->outputs[i]);
128 XRRCrtcInfo *crtc = (output->crtc ? XRRGetCrtcInfo(priv->display, res, output->crtc) : nullptr);
130 monitors.push_back(Monitor());
131 Monitor &monitor = monitors.back();
132 monitor.index = monitors.size()-1;
133 monitor.name.assign(output->name, output->nameLen);
134 priv->monitors.push_back(res->outputs[i]);
140 unsigned long length;
141 unsigned long overflow;
142 unsigned char *edid = nullptr;
143 XRRGetOutputProperty(priv->display, res->outputs[i], edid_prop, 0, 32, false, false, XA_INTEGER, &prop_type, &prop_format, &length, &overflow, &edid);
144 if(prop_type==XA_INTEGER && prop_format==8)
146 for(unsigned j=0; j<4; ++j)
148 unsigned offset = 54+j*18;
149 if(edid[offset]==0 && edid[offset+1]==0 && edid[offset+3]==0xFC)
152 for(k=0; (k<13 && edid[offset+5+k]!=0x0A); ++k) ;
153 monitor.name.assign(reinterpret_cast<char *>(edid+offset+5), k);
162 monitor.desktop_settings.rotation = rotation_from_sys(crtc->rotation);
163 monitor.desktop_settings.x = crtc->x;
164 monitor.desktop_settings.y = crtc->y;
167 if(res->outputs[i]==primary)
168 primary_index = monitor.index;
170 unsigned first_mode = modes.size();
171 int desktop_mode_index = -1;
172 for(int j=0; j<output->nmode; ++j)
174 auto k = modes_by_id.find(output->modes[j]);
175 if(k==modes_by_id.end())
178 XRRModeInfo *info = k->second;
179 float rate = static_cast<float>(info->dotClock)/(info->hTotal*info->vTotal);
181 if(any_of(modes.begin()+first_mode, modes.end(), [&info, rate](const VideoMode &m){
182 return (m.width==info->width && m.height==info->height && abs(m.rate-rate)<0.01f);
186 VideoMode mode(info->width, info->height);
187 mode.index = modes.size();
190 modes.push_back(mode);
191 priv->modes.push_back(info->id);
192 mode_monitor_indices.push_back(monitor.index);
194 if(crtc && info->id==crtc->mode)
195 desktop_mode_index = mode.index;
197 desktop_mode_indices.push_back(desktop_mode_index);
199 XRRFreeOutputInfo(output);
201 XRRFreeCrtcInfo(crtc);
204 XRRFreeScreenResources(res);
206 for(unsigned i=0; i<monitors.size(); ++i)
208 int j = desktop_mode_indices[i];
210 monitors[i].desktop_settings.mode = &modes[j];
211 monitors[i].current_settings = monitors[i].desktop_settings;
214 sort(monitors, monitor_x_compare);
216 for(unsigned i=0; i<modes.size(); ++i)
218 auto j = find_member(monitors, mode_monitor_indices[i], &Monitor::index);
219 modes[i].monitor = &*j;
220 j->video_modes.push_back(&modes[i]);
223 Monitor *prev_enabled = nullptr;
224 for(Monitor &m: monitors)
225 if(m.desktop_settings.mode)
227 m.next_left = prev_enabled;
229 prev_enabled->next_right = &m;
234 primary_monitor = &*find_member(monitors, static_cast<unsigned>(primary_index), &Monitor::index);
236 if(!primary_monitor || !primary_monitor->desktop_settings.mode)
238 // XRandR didn't give a sensible primary monitor. Try to guess one.
239 unsigned largest = 0;
240 for(Monitor &m: monitors)
241 if(const VideoMode *desktop_mode = m.desktop_settings.mode)
243 unsigned size = desktop_mode->width*desktop_mode->height;
247 primary_monitor = &m;
258 XCloseDisplay(priv->display);
263 void Display::set_mode(const VideoMode &requested_mode, bool exclusive)
266 const VideoMode *mode = find_mode(requested_mode);
268 throw unsupported_video_mode(requested_mode);
270 VideoRotation requested_rotation = requested_mode.rotation;
271 if(requested_rotation==ROTATE_ANY)
272 requested_rotation = mode->monitor->desktop_settings.rotation;
274 XRRScreenResources *res = XRRGetScreenResources(priv->display, priv->root_window);
275 RROutput output = priv->monitors[mode->monitor->index];
276 XRROutputInfo *output_info = XRRGetOutputInfo(priv->display, res, output);
278 // Check if the output already has a CRTC and find a free one if it doesn't
279 RRCrtc crtc = output_info->crtc;
280 XRRCrtcInfo *crtc_info = nullptr;
282 crtc_info = XRRGetCrtcInfo(priv->display, res, crtc);
285 for(int i=0; i<res->ncrtc; ++i)
287 crtc_info = XRRGetCrtcInfo(priv->display, res, res->crtcs[i]);
288 if(!crtc_info->noutput)
290 crtc = res->crtcs[i];
293 XRRFreeCrtcInfo(crtc_info);
298 XRRFreeOutputInfo(output_info);
299 throw unsupported_video_mode(requested_mode);
303 /* Due to the semantics of find_mode, the mode's monitor pointer must point
304 to one of the elements of the monitors list, which is non-const here. */
305 Monitor *monitor = const_cast<Monitor *>(mode->monitor);
309 monitor->current_settings.mode = mode;
310 monitor->current_settings.rotation = requested_rotation;
311 monitor->current_settings.x = 0;
312 monitor->current_settings.y = 0;
314 // Disable other outputs for exclusive mode
315 for(Monitor &m: monitors)
316 if(&m!=mode->monitor)
318 XRROutputInfo *o = XRRGetOutputInfo(priv->display, res, priv->monitors[m.index]);
320 XRRSetCrtcConfig(priv->display, res, o->crtc, CurrentTime, 0, 0, 0, RR_Rotate_0, nullptr, 0);
321 XRRFreeOutputInfo(o);
323 m.current_settings.mode = nullptr;
324 m.current_settings.rotation = ROTATE_NORMAL;
325 m.current_settings.x = 0;
326 m.current_settings.y = 0;
331 monitor->current_settings.x = monitor->desktop_settings.x;
332 monitor->current_settings.y = monitor->desktop_settings.y;
335 RRMode mode_id = priv->modes[mode->index];
336 int x = monitor->current_settings.x;
337 int y = monitor->current_settings.y;
338 Rotation sys_rot = rotation_to_sys(requested_rotation);
339 XRRSetCrtcConfig(priv->display, res, crtc, CurrentTime, x, y, mode_id, sys_rot, &output, 1);
341 XRRFreeOutputInfo(output_info);
342 XRRFreeCrtcInfo(crtc_info);
343 XRRFreeScreenResources(res);
345 (void)requested_mode;
347 throw runtime_error("no xrandr support");
351 bool Display::process_events()
353 int pending = XPending(priv->display);
360 XNextEvent(priv->display, &event.xevent);
364 auto j = priv->windows.find(event.xevent.xany.window);
365 if(j!=priv->windows.end())
367 /* Filter keyboard autorepeat. If this packet is a KeyRelease and
368 the next one is a KeyPress with the exact same parameters, they
369 indicate autorepeat and must be dropped. */
370 if(event.xevent.type==KeyRelease && !j->second->get_keyboard_autorepeat() && pending>0)
372 XKeyEvent &kev = event.xevent.xkey;
374 XPeekEvent(priv->display, &ev2);
375 if(ev2.type==KeyPress)
377 XKeyEvent &kev2 = ev2.xkey;
378 if(kev2.window==kev.window && kev2.time==kev.time && kev2.keycode==kev.keycode)
380 XNextEvent(priv->display, &ev2);
387 j->second->event(event);
394 void Display::check_error()
399 throw runtime_error(error_msg);
404 } // namespace Graphics