]> git.tdb.fi Git - libs/gui.git/blob - source/gbase/display.cpp
5d79ca5e9246210caa05472308e092f5b17c297b
[libs/gui.git] / source / gbase / display.cpp
1 /* $Id$
2
3 This file is part of libmspgbase
4 Copyright © 2007-2008  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include <iostream>
9 #ifndef WIN32
10 #include <X11/Xlib.h>
11 #include <X11/extensions/xf86vmode.h>
12 #endif
13 #include <msp/core/except.h>
14 #include <msp/strings/formatter.h>
15 #include <msp/strings/lexicalcast.h>
16 #include "display.h"
17 #include "window.h"
18 #include "display_priv.h"
19
20 using namespace std;
21
22 namespace {
23
24 bool error_flag=false;
25 std::string error_msg;
26
27 #ifndef WIN32
28 int x_error_handler(Display *display, XErrorEvent *event)
29 {
30         char err[128];
31         XGetErrorText(display, event->error_code, err, sizeof(err));
32
33         string request_code=Msp::lexical_cast(static_cast<int>(event->request_code));
34         char req[128];
35         XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), request_code.c_str(), req, sizeof(req));
36
37         string msg=Msp::format("Request %s failed with %s [%08X]", req, err, event->resourceid);
38         if(error_flag)
39                 cerr<<"Discarding error: "<<msg<<'\n';
40         else
41         {
42                 cerr<<msg<<'\n';
43                 error_msg=msg;
44                 error_flag=true;
45         }
46
47         return 0;
48 }
49 #endif
50
51 }
52
53 namespace Msp {
54 namespace Graphics {
55
56 Display::Display(const string &disp_name):
57         priv(new Private)
58 {
59 #ifdef WIN32
60         (void)disp_name;
61
62         for(unsigned i=0;; ++i)
63         {
64                 DEVMODE info;
65                 if(!EnumDisplaySettings(0, i, &info))
66                         break;
67
68                 VideoMode mode(info.dmPelsWidth, info.dmPelsHeight);
69                 mode.rate=info.dmDisplayFrequency;
70                 modes.push_back(mode);
71         }
72         
73         DEVMODE info;
74         if(EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &info))
75         {
76                 orig_mode=VideoMode(info.dmPelsWidth, info.dmPelsHeight);
77                 orig_mode.rate=info.dmDisplayFrequency;
78         }
79 #else
80         if(disp_name.empty())
81                 priv->display=XOpenDisplay(0);
82         else
83                 priv->display=XOpenDisplay(disp_name.c_str());
84         if(!priv->display)
85                 throw Exception("Couldn't open X display");
86
87         XSetErrorHandler(x_error_handler);
88
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 }
114
115 Display::~Display()
116 {
117 #ifndef WIN32
118         XCloseDisplay(priv->display);
119         delete priv;
120 #endif
121 }
122
123 void Display::add_window(Window &wnd)
124 {
125         priv->windows[wnd.get_private().window]=&wnd;
126 }
127
128 void Display::remove_window(Window &wnd)
129 {
130         priv->windows.erase(wnd.get_private().window);
131 }
132
133 void Display::set_mode(const VideoMode &mode)
134 {
135 #ifdef WIN32
136         DEVMODE info;
137         info.dmSize=sizeof(DEVMODE);
138         info.dmFields=DM_PELSWIDTH|DM_PELSHEIGHT;
139         info.dmPelsWidth=mode.width;
140         info.dmPelsHeight=mode.height;
141         if(mode.rate)
142         {
143                 info.dmFields|=DM_DISPLAYFREQUENCY;
144                 info.dmDisplayFrequency=mode.rate;
145         }
146
147         ChangeDisplaySettings(&info, CDS_FULLSCREEN);
148 #else
149         int screen=DefaultScreen(priv->display);
150
151         int nmodes;
152         XF86VidModeModeInfo **infos;
153         XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
154         for(int i=0; i<nmodes; ++i)
155         {
156                 XF86VidModeModeInfo &info=*infos[i];
157
158                 unsigned rate=0;
159                 if(info.htotal && info.vtotal)
160                         rate=info.dotclock/(info.htotal*info.vtotal);
161                 if(info.hdisplay==mode.width && info.vdisplay==mode.height && (mode.rate==0 || rate==mode.rate))
162                 {
163                         XF86VidModeSwitchToMode(priv->display, screen, &info);
164                         XF86VidModeSetViewPort(priv->display, screen, 0, 0);
165                         return;
166                 }
167         }
168
169         throw InvalidParameterValue("Requested mode 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