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