]> git.tdb.fi Git - libs/gui.git/blob - source/gbase/display.cpp
Implement video mode changing and fullscreen on win32
[libs/gui.git] / source / gbase / display.cpp
1 /* $Id$
2
3 This file is part of libmspgbase
4 Copyright © 2007 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
19 using namespace std;
20
21 namespace {
22
23 bool error_flag=false;
24 std::string error_msg;
25
26 #ifndef WIN32
27 int x_error_handler(Display *display, XErrorEvent *event)
28 {
29         char err[128];
30         XGetErrorText(display, event->error_code, err, sizeof(err));
31
32         string request_code=Msp::lexical_cast(static_cast<int>(event->request_code));
33         char req[128];
34         XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), request_code.c_str(), req, sizeof(req));
35
36         string msg=Msp::format("Request %s failed with %s [%08X]", req, err, event->resourceid);
37         if(error_flag)
38                 cerr<<"Discarding error: "<<msg<<'\n';
39         else
40         {
41                 cerr<<msg<<'\n';
42                 error_msg=msg;
43                 error_flag=true;
44         }
45
46         return 0;
47 }
48 #endif
49
50 }
51
52 namespace Msp {
53 namespace Graphics {
54
55 Display::Display(const string &disp_name)
56 {
57 #ifdef WIN32
58         (void)disp_name;
59
60         for(unsigned i=0;; ++i)
61         {
62                 DEVMODE info;
63                 if(!EnumDisplaySettings(0, i, &info))
64                         break;
65
66                 VideoMode mode(info.dmPelsWidth, info.dmPelsHeight);
67                 mode.rate=info.dmDisplayFrequency;
68                 modes.push_back(mode);
69         }
70         
71         DEVMODE info;
72         if(EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &info))
73         {
74                 orig_mode=VideoMode(info.dmPelsWidth, info.dmPelsHeight);
75                 orig_mode.rate=info.dmDisplayFrequency;
76         }
77 #else
78         if(disp_name.empty())
79                 display=XOpenDisplay(0);
80         else
81                 display=XOpenDisplay(disp_name.c_str());
82         if(!display)
83                 throw Exception("Couldn't open X display");
84
85         XSetErrorHandler(x_error_handler);
86
87         int screen=DefaultScreen(display);
88
89         int nmodes;
90         XF86VidModeModeInfo **infos;
91         XF86VidModeGetAllModeLines(display, screen, &nmodes, &infos);
92         for(int i=0; i<nmodes; ++i)
93         {
94                 XF86VidModeModeInfo &info=*infos[i];
95         
96                 VideoMode mode(info.hdisplay, info.vdisplay);
97                 if(info.htotal && info.vtotal)
98                         mode.rate=info.dotclock/(info.htotal*info.vtotal);
99                 modes.push_back(mode);
100         }
101
102         XFree(infos);
103
104         XF86VidModeModeLine modeline;
105         int dotclock;
106         XF86VidModeGetModeLine(display, screen, &dotclock, &modeline);
107         orig_mode=VideoMode(modeline.hdisplay, modeline.vdisplay);
108         if(modeline.htotal && modeline.vtotal)
109                 orig_mode.rate=dotclock/(modeline.htotal*modeline.vtotal);
110 #endif
111 }
112
113 Display::~Display()
114 {
115 #ifndef WIN32
116         XCloseDisplay(display);
117         display=0;
118 #endif
119 }
120
121 void Display::add_window(Window *wnd)
122 {
123         windows[wnd->get_handle()]=wnd;
124 }
125
126 void Display::remove_window(Window *wnd)
127 {
128         windows.erase(wnd->get_handle());
129 }
130
131 void Display::set_mode(const VideoMode &mode)
132 {
133 #ifdef WIN32
134         DEVMODE info;
135         info.dmSize=sizeof(DEVMODE);
136         info.dmFields=DM_PELSWIDTH|DM_PELSHEIGHT;
137         info.dmPelsWidth=mode.width;
138         info.dmPelsHeight=mode.height;
139         if(mode.rate)
140         {
141                 info.dmFields|=DM_DISPLAYFREQUENCY;
142                 info.dmDisplayFrequency=mode.rate;
143         }
144
145         ChangeDisplaySettings(&info, CDS_FULLSCREEN);
146 #else
147         int screen=DefaultScreen(display);
148
149         int nmodes;
150         XF86VidModeModeInfo **infos;
151         XF86VidModeGetAllModeLines(display, screen, &nmodes, &infos);
152         for(int i=0; i<nmodes; ++i)
153         {
154                 XF86VidModeModeInfo &info=*infos[i];
155
156                 unsigned rate=0;
157                 if(info.htotal && info.vtotal)
158                         rate=info.dotclock/(info.htotal*info.vtotal);
159                 if(info.hdisplay==mode.width && info.vdisplay==mode.height && (mode.rate==0 || rate==mode.rate))
160                 {
161                         XF86VidModeSwitchToMode(display, screen, &info);
162                         XF86VidModeSetViewPort(display, screen, 0, 0);
163                         return;
164                 }
165         }
166
167         throw InvalidParameterValue("Requested mode not supported");
168 #endif
169 }
170
171 void Display::tick()
172 {
173         check_error();
174
175         while(1)
176         {
177 #ifdef WIN32
178                 MSG msg;
179                 if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
180                         DispatchMessage(&msg);
181                 else
182                         break;
183 #else
184                 int pending=XPending(display);
185                 if(pending==0)
186                         break;
187
188                 for(int i=0; i<pending; ++i)
189                 {
190                         XEvent event;
191                         XNextEvent(display, &event);
192
193                         check_error();
194
195                         map<WindowHandle, Window *>::iterator j=windows.find(event.xany.window);
196                         if(j!=windows.end())
197                                 j->second->event(event);
198                 }
199 #endif
200         }
201 }
202
203 void Display::check_error()
204 {
205         if(error_flag)
206         {
207                 error_flag=false;
208                 throw Exception(error_msg);
209         }
210 }
211
212 } // namespace Graphics
213 } // namespace Msp