]> git.tdb.fi Git - libs/gui.git/blob - source/graphics/x11/display.cpp
Split platform-specific parts into separate directories
[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/strings/format.h>
7 #include <msp/strings/lexicalcast.h>
8 #include "display.h"
9 #include "display_private.h"
10
11 using namespace std;
12
13 namespace {
14
15 bool error_flag = false;
16 std::string error_msg;
17
18 int x_error_handler(Display *display, XErrorEvent *event)
19 {
20         char err[128];
21         XGetErrorText(display, event->error_code, err, sizeof(err));
22
23         string request_code = Msp::lexical_cast<string, int>(event->request_code);
24         char req[128];
25         XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), request_code.c_str(), req, sizeof(req));
26
27         string msg = Msp::format("Request %s failed with %s [%08X]", req, err, event->resourceid);
28         if(error_flag)
29                 cerr<<"Discarding error: "<<msg<<'\n';
30         else
31         {
32                 cerr<<msg<<'\n';
33                 error_msg = msg;
34                 error_flag = true;
35         }
36
37         return 0;
38 }
39
40 }
41
42
43 namespace Msp {
44 namespace Graphics {
45
46 Display::Display(const string &disp_name):
47         priv(new Private)
48 {
49         if(disp_name.empty())
50                 priv->display = XOpenDisplay(0);
51         else
52                 priv->display = XOpenDisplay(disp_name.c_str());
53         if(!priv->display)
54                 throw runtime_error("XOpenDisplay");
55
56         XSetErrorHandler(x_error_handler);
57
58 #ifdef WITH_XF86VIDMODE
59         int screen = DefaultScreen(priv->display);
60
61         int nmodes;
62         XF86VidModeModeInfo **infos;
63         XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
64         for(int i=0; i<nmodes; ++i)
65         {
66                 XF86VidModeModeInfo &info = *infos[i];
67         
68                 VideoMode mode(info.hdisplay, info.vdisplay);
69                 if(info.htotal && info.vtotal)
70                         mode.rate = info.dotclock/(info.htotal*info.vtotal);
71                 modes.push_back(mode);
72         }
73
74         XFree(infos);
75
76         XF86VidModeModeLine modeline;
77         int dotclock;
78         XF86VidModeGetModeLine(priv->display, screen, &dotclock, &modeline);
79         orig_mode = VideoMode(modeline.hdisplay, modeline.vdisplay);
80         if(modeline.htotal && modeline.vtotal)
81                 orig_mode.rate = dotclock/(modeline.htotal*modeline.vtotal);
82 #endif
83 }
84
85 Display::~Display()
86 {
87         XCloseDisplay(priv->display);
88         delete priv;
89 }
90
91 void Display::set_mode(const VideoMode &mode)
92 {
93 #ifdef WITH_XF86VIDMODE
94         int screen = DefaultScreen(priv->display);
95
96         int nmodes;
97         XF86VidModeModeInfo **infos;
98         XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
99         for(int i=0; i<nmodes; ++i)
100         {
101                 XF86VidModeModeInfo &info = *infos[i];
102
103                 unsigned rate = 0;
104                 if(info.htotal && info.vtotal)
105                         rate = info.dotclock/(info.htotal*info.vtotal);
106                 if(info.hdisplay==mode.width && info.vdisplay==mode.height && (mode.rate==0 || rate==mode.rate))
107                 {
108                         XF86VidModeSwitchToMode(priv->display, screen, &info);
109                         XF86VidModeSetViewPort(priv->display, screen, 0, 0);
110                         return;
111                 }
112         }
113
114         throw unsupported_video_mode(mode);
115 #else
116         (void)mode;
117         throw runtime_error("no xf86vidmode support");
118 #endif
119 }
120
121 bool Display::process_events()
122 {
123         int pending = XPending(priv->display);
124         if(pending==0)
125                 return false;
126
127         for(; pending--;)
128         {
129                 Window::Event event;
130                 XNextEvent(priv->display, &event.xevent);
131
132                 check_error();
133
134                 map<WindowHandle, Window *>::iterator j = priv->windows.find(event.xevent.xany.window);
135                 if(j!=priv->windows.end())
136                 {
137                         /* Filter keyboard autorepeat.  If this packet is a KeyRelease and
138                         the next one is a KeyPress with the exact same parameters, they
139                         indicate autorepeat and must be dropped. */
140                         if(event.xevent.type==KeyRelease && !j->second->get_keyboard_autorepeat() && pending>0)
141                         {
142                                 XKeyEvent &kev = event.xevent.xkey;
143                                 XEvent ev2;
144                                 XPeekEvent(priv->display, &ev2);
145                                 if(ev2.type==KeyPress)
146                                 {
147                                         XKeyEvent &kev2 = ev2.xkey;
148                                         if(kev2.window==kev.window && kev2.time==kev.time && kev2.keycode==kev.keycode)
149                                         {
150                                                 XNextEvent(priv->display, &ev2);
151                                                 --pending;
152                                                 continue;
153                                         }
154                                 }
155                         }
156
157                         j->second->event(event);
158                 }
159         }
160
161         return true;
162 }
163
164 void Display::check_error()
165 {
166         if(error_flag)
167         {
168                 error_flag = false;
169                 throw runtime_error(error_msg);
170         }
171 }
172
173 } // namespace Msp
174 } // namespace Graphics