]> git.tdb.fi Git - libs/gui.git/blob - source/graphics/display.cpp
Consistently label the graphics part as graphics
[libs/gui.git] / source / graphics / display.cpp
1 #include <iostream>
2 #ifndef WIN32
3 #include <X11/Xlib.h>
4 #ifdef WITH_XF86VIDMODE
5 #include <X11/extensions/xf86vmode.h>
6 #endif
7 #endif
8 #include <msp/strings/format.h>
9 #include <msp/strings/lexicalcast.h>
10 #include "display.h"
11 #include "window.h"
12 #include "display_priv.h"
13
14 using namespace std;
15
16 namespace {
17
18 bool error_flag = false;
19 std::string error_msg;
20
21 #ifndef WIN32
22 int x_error_handler(Display *display, XErrorEvent *event)
23 {
24         char err[128];
25         XGetErrorText(display, event->error_code, err, sizeof(err));
26
27         string request_code = Msp::lexical_cast(static_cast<int>(event->request_code));
28         char req[128];
29         XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), request_code.c_str(), req, sizeof(req));
30
31         string msg = Msp::format("Request %s failed with %s [%08X]", req, err, event->resourceid);
32         if(error_flag)
33                 cerr<<"Discarding error: "<<msg<<'\n';
34         else
35         {
36                 cerr<<msg<<'\n';
37                 error_msg = msg;
38                 error_flag = true;
39         }
40
41         return 0;
42 }
43 #endif
44
45 }
46
47 namespace Msp {
48 namespace Graphics {
49
50 unsupported_video_mode::unsupported_video_mode(const VideoMode &mode):
51         runtime_error(format("%dx%d", mode.width, mode.height))
52 { }
53
54
55 Display::Display(const string &disp_name):
56         priv(new Private)
57 {
58 #ifdef WIN32
59         (void)disp_name;
60
61         for(unsigned i=0;; ++i)
62         {
63                 DEVMODE info;
64                 if(!EnumDisplaySettings(0, i, &info))
65                         break;
66
67                 VideoMode mode(info.dmPelsWidth, info.dmPelsHeight);
68                 mode.rate = info.dmDisplayFrequency;
69                 modes.push_back(mode);
70         }
71         
72         DEVMODE info;
73         if(EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &info))
74         {
75                 orig_mode = VideoMode(info.dmPelsWidth, info.dmPelsHeight);
76                 orig_mode.rate = info.dmDisplayFrequency;
77         }
78 #else
79         if(disp_name.empty())
80                 priv->display = XOpenDisplay(0);
81         else
82                 priv->display = XOpenDisplay(disp_name.c_str());
83         if(!priv->display)
84                 throw runtime_error("XOpenDisplay");
85
86         XSetErrorHandler(x_error_handler);
87
88 #ifdef WITH_XF86VIDMODE
89         int screen = DefaultScreen(priv->display);
90
91         int nmodes;
92         XF86VidModeModeInfo **infos;
93         XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
94         for(int i=0; i<nmodes; ++i)
95         {
96                 XF86VidModeModeInfo &info = *infos[i];
97         
98                 VideoMode mode(info.hdisplay, info.vdisplay);
99                 if(info.htotal && info.vtotal)
100                         mode.rate = info.dotclock/(info.htotal*info.vtotal);
101                 modes.push_back(mode);
102         }
103
104         XFree(infos);
105
106         XF86VidModeModeLine modeline;
107         int dotclock;
108         XF86VidModeGetModeLine(priv->display, screen, &dotclock, &modeline);
109         orig_mode = VideoMode(modeline.hdisplay, modeline.vdisplay);
110         if(modeline.htotal && modeline.vtotal)
111                 orig_mode.rate = dotclock/(modeline.htotal*modeline.vtotal);
112 #endif
113 #endif
114 }
115
116 Display::~Display()
117 {
118 #ifndef WIN32
119         XCloseDisplay(priv->display);
120         delete priv;
121 #endif
122 }
123
124 void Display::add_window(Window &wnd)
125 {
126         priv->windows[wnd.get_private().window] = &wnd;
127 }
128
129 void Display::remove_window(Window &wnd)
130 {
131         priv->windows.erase(wnd.get_private().window);
132 }
133
134 void Display::set_mode(const VideoMode &mode)
135 {
136 #if defined(WIN32)
137         DEVMODE info;
138         info.dmSize = sizeof(DEVMODE);
139         info.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT;
140         info.dmPelsWidth = mode.width;
141         info.dmPelsHeight = mode.height;
142         if(mode.rate)
143         {
144                 info.dmFields |= DM_DISPLAYFREQUENCY;
145                 info.dmDisplayFrequency = mode.rate;
146         }
147
148         LONG ret = ChangeDisplaySettings(&info, CDS_FULLSCREEN);
149         if(ret!=DISP_CHANGE_SUCCESSFUL)
150                 throw unsupported_video_mode(mode);
151 #elif defined(WITH_XF86VIDMODE)
152         int screen = DefaultScreen(priv->display);
153
154         int nmodes;
155         XF86VidModeModeInfo **infos;
156         XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
157         for(int i=0; i<nmodes; ++i)
158         {
159                 XF86VidModeModeInfo &info = *infos[i];
160
161                 unsigned rate = 0;
162                 if(info.htotal && info.vtotal)
163                         rate = info.dotclock/(info.htotal*info.vtotal);
164                 if(info.hdisplay==mode.width && info.vdisplay==mode.height && (mode.rate==0 || rate==mode.rate))
165                 {
166                         XF86VidModeSwitchToMode(priv->display, screen, &info);
167                         XF86VidModeSetViewPort(priv->display, screen, 0, 0);
168                         return;
169                 }
170         }
171
172         throw unsupported_video_mode(mode);
173 #else
174         (void)mode;
175         throw runtime_error("no xf86vidmode support");
176 #endif
177 }
178
179 void Display::tick()
180 {
181         check_error();
182
183         while(1)
184         {
185 #ifdef WIN32
186                 MSG msg;
187                 if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
188                         DispatchMessage(&msg);
189                 else
190                         break;
191 #else
192                 int pending = XPending(priv->display);
193                 if(pending==0)
194                         break;
195
196                 for(; pending--;)
197                 {
198                         Window::Event event;
199                         XNextEvent(priv->display, &event.xevent);
200
201                         check_error();
202
203                         map<WindowHandle, Window *>::iterator j = priv->windows.find(event.xevent.xany.window);
204                         if(j!=priv->windows.end())
205                         {
206                                 /* Filter keyboard autorepeat.  If this packet is a KeyRelease and
207                                 the next one is a KeyPress with the exact same parameters, they
208                                 indicate autorepeat and must be dropped. */
209                                 if(event.xevent.type==KeyRelease && !j->second->get_keyboard_autorepeat() && pending>0)
210                                 {
211                                         XKeyEvent &kev = event.xevent.xkey;
212                                         XEvent ev2;
213                                         XPeekEvent(priv->display, &ev2);
214                                         if(ev2.type==KeyPress)
215                                         {
216                                                 XKeyEvent &kev2 = ev2.xkey;
217                                                 if(kev2.window==kev.window && kev2.time==kev.time && kev2.keycode==kev.keycode)
218                                                 {
219                                                         XNextEvent(priv->display, &ev2);
220                                                         --pending;
221                                                         continue;
222                                                 }
223                                         }
224                                 }
225
226                                 j->second->event(event);
227                         }
228                 }
229 #endif
230         }
231 }
232
233 void Display::check_error()
234 {
235         if(error_flag)
236         {
237                 error_flag = false;
238                 throw runtime_error(error_msg);
239         }
240 }
241
242 } // namespace Graphics
243 } // namespace Msp