]> git.tdb.fi Git - libs/gui.git/blob - source/graphics/x11/display.cpp
Avoid compiler warning for a declared but unused function
[libs/gui.git] / source / graphics / x11 / display.cpp
1 #include <iostream>
2 #include <X11/Xlib.h>
3 #ifdef WITH_XRANDR
4 #include <X11/extensions/Xrandr.h>
5 #endif
6 #include <msp/io/print.h>
7 #include <msp/strings/format.h>
8 #include <msp/strings/lexicalcast.h>
9 #include "display.h"
10 #include "display_private.h"
11
12 using namespace std;
13
14 namespace {
15
16 bool error_flag = false;
17 std::string error_msg;
18
19 int x_error_handler(Display *display, XErrorEvent *event)
20 {
21         char err[128];
22         XGetErrorText(display, event->error_code, err, sizeof(err));
23
24         string request_code = Msp::lexical_cast<string, int>(event->request_code);
25         char req[128];
26         XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), request_code.c_str(), req, sizeof(req));
27
28         string msg = Msp::format("Request %s failed with %s [%08X]", req, err, event->resourceid);
29         if(error_flag)
30                 Msp::IO::print(Msp::IO::cerr, "Discarding error: %s\n", msg);
31         else
32         {
33                 Msp::IO::print(Msp::IO::cerr, "%s\n", msg);
34                 error_msg = msg;
35                 error_flag = true;
36         }
37
38         return 0;
39 }
40
41 #ifdef WITH_XRANDR
42 inline Msp::Graphics::VideoRotation rotation_from_sys(Rotation r)
43 {
44         switch(r)
45         {
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;
50         }
51 }
52
53 inline Rotation rotation_to_sys(Msp::Graphics::VideoRotation r)
54 {
55         switch(r)
56         {
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;
61         }
62 }
63
64 inline bool monitor_x_compare(const Msp::Graphics::Monitor &m1, const Msp::Graphics::Monitor &m2)
65 {
66         if(m1.desktop_mode && !m2.desktop_mode)
67                 return true;
68         return m1.x<m2.x;
69 }
70 #endif
71
72 inline unsigned mode_width(const Msp::Graphics::VideoMode &m, Msp::Graphics::VideoRotation r)
73 {
74         if(r==Msp::Graphics::ROTATE_RIGHT || r==Msp::Graphics::ROTATE_LEFT)
75                 return m.height;
76         else
77                 return m.width;
78 }
79
80 }
81
82
83 namespace Msp {
84 namespace Graphics {
85
86 Display::Display(const string &disp_name):
87         primary_monitor(0),
88         priv(new Private)
89 {
90         if(disp_name.empty())
91                 priv->display = XOpenDisplay(0);
92         else
93                 priv->display = XOpenDisplay(disp_name.c_str());
94         if(!priv->display)
95                 throw runtime_error("XOpenDisplay");
96
97         XSetErrorHandler(x_error_handler);
98
99         err_dialog = new ErrorDialog(this);
100
101 #ifdef WITH_XRANDR
102         int event_base;
103         int error_base;
104         if(XRRQueryExtension(priv->display, &event_base, &error_base))
105         {
106                 int major, minor;
107                 XRRQueryVersion(priv->display, &major, &minor);
108                 if(major>1 || (major==1 && minor>=2))
109                 {
110                         WindowHandle root = DefaultRootWindow(priv->display);
111                         XRRScreenResources *res = XRRGetScreenResources(priv->display, root);
112                         RROutput primary = XRRGetOutputPrimary(priv->display, root);
113
114                         map<RRMode, XRRModeInfo *> modes_by_id;
115                         for(int i=0; i<res->nmode; ++i)
116                                 modes_by_id[res->modes[i].id] = &res->modes[i];
117
118                         for(int i=0; i<res->noutput; ++i)
119                         {
120                                 XRROutputInfo *output = XRRGetOutputInfo(priv->display, res, res->outputs[i]);
121                                 XRRCrtcInfo *crtc = (output->crtc ? XRRGetCrtcInfo(priv->display, res, output->crtc) : 0);
122
123                                 monitors.push_back(Monitor());
124                                 Monitor &monitor = monitors.back();
125                                 monitor.index = monitors.size()-1;
126                                 priv->monitors.push_back(res->outputs[i]);
127
128                                 if(crtc)
129                                 {
130                                         monitor.desktop_rotation = rotation_from_sys(crtc->rotation);
131                                         monitor.current_rotation = monitor.desktop_rotation;
132                                         monitor.x = crtc->x;
133                                         monitor.y = crtc->y;
134                                 }
135
136                                 if(res->outputs[i]==primary)
137                                         primary_monitor = &monitor;
138
139                                 for(int j=0; j<output->nmode; ++j)
140                                 {
141                                         map<RRMode, XRRModeInfo *>::iterator k = modes_by_id.find(output->modes[j]);
142                                         if(k==modes_by_id.end())
143                                                 continue;
144
145                                         XRRModeInfo *info = k->second;
146
147                                         VideoMode mode(info->width, info->height);
148                                         mode.index = modes.size();
149                                         mode.monitor = &monitor;
150                                         mode.rate = info->dotClock/(info->hTotal*info->vTotal);
151                                         if(find_mode(mode))
152                                                 continue;
153
154                                         modes.push_back(mode);
155                                         priv->modes.push_back(info->id);
156                                         monitor.video_modes.push_back(&modes.back());
157
158                                         if(crtc && info->id==crtc->mode)
159                                         {
160                                                 monitor.desktop_mode = &modes.back();
161                                                 monitor.current_mode = monitor.desktop_mode;
162                                         }
163                                 }
164
165                                 XRRFreeOutputInfo(output);
166                                 if(crtc)
167                                         XRRFreeCrtcInfo(crtc);
168                         }
169
170                         XRRFreeScreenResources(res);
171
172                         monitors.sort(monitor_x_compare);
173                         Monitor *prev_enabled = 0;
174                         for(list<Monitor>::iterator i=monitors.begin(); i!=monitors.end(); ++i)
175                                 if(i->desktop_mode)
176                                 {
177                                         i->next_left = prev_enabled;
178                                         if(prev_enabled)
179                                                 prev_enabled->next_right = &*i;
180                                         prev_enabled = &*i;
181                                 }
182
183                         if(!primary_monitor || !primary_monitor->desktop_mode)
184                         {
185                                 // XRandR didn't give a sensible primary monitor.  Try to guess one.
186                                 unsigned largest = 0;
187                                 for(list<Monitor>::iterator i=monitors.begin(); i!=monitors.end(); ++i)
188                                         if(i->desktop_mode)
189                                         {
190                                                 unsigned size = i->desktop_mode->width*i->desktop_mode->height;
191                                                 if(size>largest)
192                                                 {
193                                                         largest = size;
194                                                         primary_monitor = &*i;
195                                                 }
196                                         }
197                         }
198                 }
199         }
200 #endif
201 }
202
203 Display::~Display()
204 {
205         XCloseDisplay(priv->display);
206         delete priv;
207         delete err_dialog;
208 }
209
210 void Display::set_mode(const VideoMode &requested_mode, bool exclusive)
211 {
212 #ifdef WITH_XRANDR
213         const VideoMode *mode = find_mode(requested_mode);
214         if(!mode)
215                 throw unsupported_video_mode(requested_mode);
216
217         VideoRotation requested_rotation = requested_mode.rotation;
218         if(requested_rotation==ROTATE_ANY)
219                 requested_rotation = mode->monitor->desktop_rotation;
220
221         WindowHandle root = DefaultRootWindow(priv->display);
222         XRRScreenResources *res = XRRGetScreenResources(priv->display, root);
223         RROutput output = priv->monitors[mode->monitor->index];
224         XRROutputInfo *output_info = XRRGetOutputInfo(priv->display, res, output);
225
226         // Check if the output already has a CRTC and find a free one if it doesn't
227         RRCrtc crtc = output_info->crtc;
228         XRRCrtcInfo *crtc_info = 0;
229         if(crtc)
230                 crtc_info = XRRGetCrtcInfo(priv->display, res, crtc);
231         else
232         {
233                 for(int i=0; i<res->ncrtc; ++i)
234                 {
235                         crtc_info = XRRGetCrtcInfo(priv->display, res, res->crtcs[i]);
236                         if(!crtc_info->noutput)
237                         {
238                                 crtc = res->crtcs[i];
239                                 break;
240                         }
241                         XRRFreeCrtcInfo(crtc_info);
242                 }
243
244                 if(!crtc)
245                 {
246                         XRRFreeOutputInfo(output_info);
247                         throw unsupported_video_mode(requested_mode);
248                 }
249         }
250
251         int x = 0;
252         int y = 0;
253
254         if(exclusive)
255         {
256                 // Disable other outputs for exclusive mode
257                 for(list<Monitor>::iterator i=monitors.begin(); i!=monitors.end(); ++i)
258                         if(&*i!=mode->monitor)
259                         {
260                                 XRROutputInfo *o = XRRGetOutputInfo(priv->display, res, priv->monitors[i->index]);
261                                 if(o->crtc)
262                                         XRRSetCrtcConfig(priv->display, res, o->crtc, CurrentTime, 0, 0, 0, RR_Rotate_0, 0, 0);
263                                 XRRFreeOutputInfo(o);
264
265                                 i->current_mode = 0;
266                                 i->current_rotation = ROTATE_NORMAL;
267                                 i->x = 0;
268                                 i->y = 0;
269                         }
270         }
271         else
272         {
273                 const Monitor *left = mode->monitor->next_left;
274                 while(left && !left->current_mode)
275                         left = left->next_left;
276
277                 if(left)
278                 {
279                         x = left->x+mode_width(*left->current_mode, left->current_rotation);
280                         y = left->y;
281                 }
282         }
283
284         XRRSetCrtcConfig(priv->display, res, crtc, CurrentTime, x, y, priv->modes[mode->index], rotation_to_sys(requested_rotation), &output, 1);
285
286         list<Monitor>::iterator i;
287         for(i=monitors.begin(); i!=monitors.end(); ++i)
288                 if(&*i==mode->monitor)
289                 {
290                         i->current_mode = mode;
291                         i->current_rotation = requested_rotation;
292                         i->x = x;
293                         i->y = y;
294
295                         x += mode_width(*mode, requested_rotation);
296                         ++i;
297                         break;
298                 }
299
300         for(; i!=monitors.end(); ++i)
301                 if(i->current_mode)
302                 {
303                         XRROutputInfo *o = XRRGetOutputInfo(priv->display, res, priv->monitors[i->index]);
304                         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);
305
306                         XRRPanning panning;
307                         panning.timestamp = CurrentTime;
308                         panning.left = x;
309                         panning.top = y;
310                         panning.width = i->current_mode->width;
311                         panning.height = i->current_mode->height;
312                         panning.track_left = panning.left;
313                         panning.track_top = panning.top;
314                         panning.track_width = panning.width;
315                         panning.track_height = panning.height;
316                         panning.border_left = 0;
317                         panning.border_top = 0;
318                         panning.border_right = 0;
319                         panning.border_bottom = 0;
320                         XRRSetPanning(priv->display, res, o->crtc, &panning);
321
322                         XRRFreeOutputInfo(o);
323
324                         i->x = x;
325                         i->y = y;
326
327                         x += mode_width(*i->current_mode, i->current_rotation);
328                 }
329
330         XRRFreeOutputInfo(output_info);
331         XRRFreeCrtcInfo(crtc_info);
332         XRRFreeScreenResources(res);
333 #else
334         (void)requested_mode;
335         (void)exclusive;
336         throw runtime_error("no xrandr support");
337 #endif
338 }
339
340 bool Display::process_events()
341 {
342         int pending = XPending(priv->display);
343         if(pending==0)
344                 return false;
345
346         for(; pending--;)
347         {
348                 Window::Event event;
349                 XNextEvent(priv->display, &event.xevent);
350
351                 check_error();
352
353                 map<WindowHandle, Window *>::iterator j = priv->windows.find(event.xevent.xany.window);
354                 if(j!=priv->windows.end())
355                 {
356                         /* Filter keyboard autorepeat.  If this packet is a KeyRelease and
357                         the next one is a KeyPress with the exact same parameters, they
358                         indicate autorepeat and must be dropped. */
359                         if(event.xevent.type==KeyRelease && !j->second->get_keyboard_autorepeat() && pending>0)
360                         {
361                                 XKeyEvent &kev = event.xevent.xkey;
362                                 XEvent ev2;
363                                 XPeekEvent(priv->display, &ev2);
364                                 if(ev2.type==KeyPress)
365                                 {
366                                         XKeyEvent &kev2 = ev2.xkey;
367                                         if(kev2.window==kev.window && kev2.time==kev.time && kev2.keycode==kev.keycode)
368                                         {
369                                                 XNextEvent(priv->display, &ev2);
370                                                 --pending;
371                                                 continue;
372                                         }
373                                 }
374                         }
375
376                         j->second->event(event);
377                 }
378         }
379
380         return true;
381 }
382
383 void Display::check_error()
384 {
385         if(error_flag)
386         {
387                 error_flag = false;
388                 throw runtime_error(error_msg);
389         }
390 }
391
392 } // namespace Msp
393 } // namespace Graphics