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