]> git.tdb.fi Git - libs/gui.git/blob - source/graphics/x11/display.cpp
Extend the video mode API to support multiple monitors
[libs/gui.git] / source / graphics / x11 / display.cpp
1 #include <iostream>
2 #include <X11/Xlib.h>
3 #ifdef WITH_XF86VIDMODE
4 #include <X11/extensions/xf86vmode.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 }
42
43
44 namespace Msp {
45 namespace Graphics {
46
47 Display::Display(const string &disp_name):
48         primary_monitor(0),
49         priv(new Private)
50 {
51         if(disp_name.empty())
52                 priv->display = XOpenDisplay(0);
53         else
54                 priv->display = XOpenDisplay(disp_name.c_str());
55         if(!priv->display)
56                 throw runtime_error("XOpenDisplay");
57
58         XSetErrorHandler(x_error_handler);
59
60 #ifdef WITH_XF86VIDMODE
61         int screen = DefaultScreen(priv->display);
62
63         monitors.push_back(Monitor());
64         Monitor &monitor = monitors.back();
65         primary_monitor = &monitor;
66
67         int nmodes;
68         XF86VidModeModeInfo **infos;
69         XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
70
71         XF86VidModeModeLine modeline;
72         int dotclock;
73         XF86VidModeGetModeLine(priv->display, screen, &dotclock, &modeline);
74
75         for(int i=0; i<nmodes; ++i)
76         {
77                 XF86VidModeModeInfo &info = *infos[i];
78         
79                 VideoMode mode(info.hdisplay, info.vdisplay);
80                 mode.monitor = &monitor;
81                 if(info.htotal && info.vtotal)
82                         mode.rate = info.dotclock/(info.htotal*info.vtotal);
83                 modes.push_back(mode);
84                 monitor.video_modes.push_back(&modes.back());
85
86                 if(info.htotal==modeline.htotal && info.vtotal==modeline.vtotal && info.dotclock==static_cast<unsigned>(dotclock))
87                         monitor.desktop_mode = &modes.back();
88         }
89
90         XFree(infos);
91 #endif
92 }
93
94 Display::~Display()
95 {
96         XCloseDisplay(priv->display);
97         delete priv;
98 }
99
100 void Display::set_mode(const VideoMode &mode, bool)
101 {
102 #ifdef WITH_XF86VIDMODE
103         int screen = DefaultScreen(priv->display);
104
105         int nmodes;
106         XF86VidModeModeInfo **infos;
107         XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
108         for(int i=0; i<nmodes; ++i)
109         {
110                 XF86VidModeModeInfo &info = *infos[i];
111
112                 unsigned rate = 0;
113                 if(info.htotal && info.vtotal)
114                         rate = info.dotclock/(info.htotal*info.vtotal);
115                 if(info.hdisplay==mode.width && info.vdisplay==mode.height && (mode.rate==0 || rate==mode.rate))
116                 {
117                         XF86VidModeSwitchToMode(priv->display, screen, &info);
118                         XF86VidModeSetViewPort(priv->display, screen, 0, 0);
119                         return;
120                 }
121         }
122
123         throw unsupported_video_mode(mode);
124 #else
125         (void)mode;
126         throw runtime_error("no xf86vidmode support");
127 #endif
128 }
129
130 bool Display::process_events()
131 {
132         int pending = XPending(priv->display);
133         if(pending==0)
134                 return false;
135
136         for(; pending--;)
137         {
138                 Window::Event event;
139                 XNextEvent(priv->display, &event.xevent);
140
141                 check_error();
142
143                 map<WindowHandle, Window *>::iterator j = priv->windows.find(event.xevent.xany.window);
144                 if(j!=priv->windows.end())
145                 {
146                         /* Filter keyboard autorepeat.  If this packet is a KeyRelease and
147                         the next one is a KeyPress with the exact same parameters, they
148                         indicate autorepeat and must be dropped. */
149                         if(event.xevent.type==KeyRelease && !j->second->get_keyboard_autorepeat() && pending>0)
150                         {
151                                 XKeyEvent &kev = event.xevent.xkey;
152                                 XEvent ev2;
153                                 XPeekEvent(priv->display, &ev2);
154                                 if(ev2.type==KeyPress)
155                                 {
156                                         XKeyEvent &kev2 = ev2.xkey;
157                                         if(kev2.window==kev.window && kev2.time==kev.time && kev2.keycode==kev.keycode)
158                                         {
159                                                 XNextEvent(priv->display, &ev2);
160                                                 --pending;
161                                                 continue;
162                                         }
163                                 }
164                         }
165
166                         j->second->event(event);
167                 }
168         }
169
170         return true;
171 }
172
173 void Display::check_error()
174 {
175         if(error_flag)
176         {
177                 error_flag = false;
178                 throw runtime_error(error_msg);
179         }
180 }
181
182 } // namespace Msp
183 } // namespace Graphics