]> git.tdb.fi Git - libs/gui.git/blob - source/gbase/window.cpp
Implement video mode changing and fullscreen on win32
[libs/gui.git] / source / gbase / window.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 <vector>
9 #ifndef WIN32
10 #include <X11/Xatom.h>
11 #include <X11/Xutil.h>
12 #else
13 #include <windowsx.h>
14 #endif
15 #include <msp/core/application.h>
16 #include <msp/core/except.h>
17 #include "display.h"
18 #include "window.h"
19
20 using namespace std;
21
22 namespace Msp {
23 namespace Graphics {
24
25 WindowOptions::WindowOptions():
26         width(640),
27         height(480),
28         fullscreen(false),
29         resizable(false)
30 { }
31
32
33 Window::Window(Display &dpy, unsigned w, unsigned h, bool fs):
34         display(dpy)
35 {
36         options.width=w;
37         options.height=h;
38         options.fullscreen=fs;
39
40         init();
41 }
42
43 Window::Window(Display &dpy, const WindowOptions &opts):
44         display(dpy),
45         options(opts)
46 {
47         init();
48 }
49
50 Window::~Window()
51 {
52         if(window)
53 #ifdef WIN32
54                 CloseWindow(window);
55 #else
56                 XDestroyWindow(display.get_display(), window);
57 #endif
58
59         display.remove_window(this);
60
61         if(options.fullscreen)
62                 display.restore_mode();
63 }
64
65 void Window::set_title(const string &title)
66 {
67 #ifdef WIN32
68         SetWindowText(window, title.c_str());
69 #else
70         vector<unsigned char> buf(title.begin(), title.end());
71         XTextProperty prop;
72         prop.value=&buf[0];
73         prop.encoding=XA_STRING;
74         prop.format=8;
75         prop.nitems=title.size();
76         XSetWMName(display.get_display(), window, &prop);
77         display.check_error();
78 #endif
79 }
80
81 void Window::reconfigure(const WindowOptions &opts)
82 {
83         bool fullscreen_changed=(opts.fullscreen!=options.fullscreen);
84
85         options=opts;
86
87 #ifdef WIN32
88         RECT rect;
89         SetRect(&rect, 0, 0, options.width, options.height);
90
91         int style=(options.fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW);
92         if(!options.resizable)
93                 style&=~WS_THICKFRAME;
94         int exstyle=(options.fullscreen ? WS_EX_APPWINDOW : WS_EX_OVERLAPPEDWINDOW);
95         AdjustWindowRectEx(&rect, style, false, exstyle);
96
97         if(fullscreen_changed)
98         {
99                 hide();
100                 SetWindowLong(window, GWL_EXSTYLE, exstyle);
101                 SetWindowLong(window, GWL_STYLE, style);
102                 show();
103         }
104
105         if(options.fullscreen)
106                 SetWindowPos(window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOZORDER);
107         else
108                 SetWindowPos(window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOMOVE|SWP_NOZORDER);
109
110         (void)fullscreen_changed;
111 #else
112         ::Display *dpy=display.get_display();
113
114         if(fullscreen_changed)
115         {
116                 hide();
117                 XSetWindowAttributes attr;
118                 attr.override_redirect=options.fullscreen;
119                 XChangeWindowAttributes(dpy, window, CWOverrideRedirect, &attr);
120                 show();
121         }
122
123         XSizeHints hints;
124         if(options.resizable)
125                 hints.flags=0;
126         else
127         {
128                 hints.flags=PMinSize|PMaxSize;
129                 hints.min_width=hints.max_width=options.width;
130                 hints.min_height=hints.max_height=options.height;
131         }
132         XSetWMNormalHints(dpy, window, &hints);
133
134         if(options.fullscreen)
135                 XMoveResizeWindow(dpy, window, 0, 0, options.width, options.height);
136         else
137                 XResizeWindow(dpy, window, options.width, options.height);
138 #endif
139
140         if(options.fullscreen)
141                 display.set_mode(VideoMode(options.width, options.height));
142         else if(fullscreen_changed)
143                 display.restore_mode();
144 }
145
146 void Window::show()
147 {
148 #ifdef WIN32
149         ShowWindow(window, SW_SHOWNORMAL);
150 #else
151         XMapRaised(display.get_display(), window);
152         display.check_error();
153 #endif
154 }
155
156 void Window::hide()
157 {
158 #ifdef WIN32
159         ShowWindow(window, SW_HIDE);
160 #else
161         XUnmapWindow(display.get_display(), window);
162         display.check_error();
163 #endif
164 }
165
166 void Window::init()
167 {
168 #ifdef WIN32
169         static bool wndclass_created=false;
170
171         if(!wndclass_created)
172         {
173                 WNDCLASSEX wndcl;
174
175                 wndcl.cbSize=sizeof(WNDCLASSEX);
176                 wndcl.style=0;
177                 wndcl.lpfnWndProc=&wndproc_;
178                 wndcl.cbClsExtra=0;
179                 wndcl.cbWndExtra=sizeof(Window *);
180                 wndcl.hInstance=reinterpret_cast<HINSTANCE>(Application::get_data());
181                 wndcl.hIcon=0;
182                 wndcl.hCursor=LoadCursor(0, IDC_ARROW);
183                 wndcl.hbrBackground=0;
184                 wndcl.lpszMenuName=0;
185                 wndcl.lpszClassName="mspgbase";
186                 wndcl.hIconSm=0;
187
188                 if(!RegisterClassEx(&wndcl))
189                         throw Exception("Couldn't register window class");
190
191                 wndclass_created=true;
192         }
193
194         RECT rect;
195         SetRect(&rect, 0, 0, options.width, options.height);
196
197         int style=(options.fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW);
198         if(!options.resizable)
199                 style&=~WS_THICKFRAME;
200         int exstyle=(options.fullscreen ? WS_EX_APPWINDOW : WS_EX_OVERLAPPEDWINDOW);
201         AdjustWindowRectEx(&rect, style, false, exstyle);
202
203         window=CreateWindowEx(exstyle,
204                 "mspgbase",
205                 "Window",
206                 style,
207                 CW_USEDEFAULT, CW_USEDEFAULT,
208                 rect.right-rect.left, rect.bottom-rect.top,
209                 0,
210                 0,
211                 reinterpret_cast<HINSTANCE>(Application::get_data()),
212                 this);
213         if(!window)
214                 throw Exception("CreateWindowEx failed");
215
216         if(options.fullscreen)
217                 display.set_mode(VideoMode(options.width, options.height));
218
219 #else
220         ::Display *dpy=display.get_display();
221
222         wm_delete_window=XInternAtom(dpy, "WM_DELETE_WINDOW", true);
223
224         XSetWindowAttributes attr;
225         attr.override_redirect=options.fullscreen;
226         attr.event_mask=ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask|EnterWindowMask;
227
228         window=XCreateWindow(dpy,
229                 DefaultRootWindow(dpy),
230                 0, 0,
231                 options.width, options.height,
232                 0,
233                 CopyFromParent,
234                 InputOutput,
235                 CopyFromParent,
236                 CWOverrideRedirect|CWEventMask, &attr);
237
238         XSetWMProtocols(dpy, window, &wm_delete_window, 1);
239
240         if(!options.resizable)
241         {
242                 XSizeHints hints;
243                 hints.flags=PMinSize|PMaxSize;
244                 hints.min_width=hints.max_width=options.width;
245                 hints.min_height=hints.max_height=options.height;
246                 XSetWMNormalHints(dpy, window, &hints);
247         }
248
249         if(options.fullscreen)
250         {
251                 display.set_mode(VideoMode(options.width, options.height));
252                 XWarpPointer(dpy, None, window, 0, 0, 0, 0, options.width/2, options.height/2);
253         }
254 #endif
255
256         display.add_window(this);
257         display.check_error();
258 }
259
260 #ifndef WIN32
261 void Window::event(const XEvent &ev)
262 {
263         switch(ev.type)
264         {
265         case ButtonPress:
266                 signal_button_press.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
267                 break;
268         case ButtonRelease:
269                 signal_button_release.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
270                 break;
271         case MotionNotify:
272                 signal_pointer_motion.emit(ev.xmotion.x, ev.xmotion.y);
273                 break;
274         case KeyPress:
275                 {
276                         char buf[16];
277                         XLookupString(const_cast<XKeyEvent *>(&ev.xkey), buf, sizeof(buf), 0, 0);
278                         // XXX Handle the result according to locale
279                         signal_key_press.emit(XKeycodeToKeysym(display.get_display(), ev.xkey.keycode, 0), ev.xkey.state, buf[0]);
280                 }
281                 break;
282         case KeyRelease:
283                 signal_key_release.emit(XKeycodeToKeysym(display.get_display(), ev.xkey.keycode, 0), ev.xkey.state);
284                 break;
285         case ConfigureNotify:
286                 options.width=ev.xconfigure.width;
287                 options.height=ev.xconfigure.height;
288                 signal_resize.emit(options.width, options.height);
289                 break;
290         case ClientMessage:
291                 if(ev.xclient.data.l[0]==static_cast<long>(wm_delete_window))
292                         signal_close.emit();
293                 break;
294         case EnterNotify:
295                 XSetInputFocus(display.get_display(), window, RevertToParent, CurrentTime);
296                 break;
297         case MapNotify:
298                 if(options.fullscreen)
299                         XGrabPointer(display.get_display(), window, true, None, GrabModeAsync, GrabModeAsync, window, None, CurrentTime);
300                 break;
301         default:;
302         }
303 }
304 #endif
305
306 #ifdef WIN32
307 int Window::wndproc(UINT msg, WPARAM wp, LPARAM lp)
308 {
309         switch(msg)
310         {
311         case WM_KEYDOWN:
312                 signal_key_press.emit(wp, 0, wp);
313                 break;
314         case WM_KEYUP:
315                 signal_key_release.emit(wp, 0);
316                 break;
317         case WM_LBUTTONDOWN:
318                 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
319                 break;
320         case WM_LBUTTONUP:
321                 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
322                 break;
323         case WM_MBUTTONDOWN:
324                 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
325                 break;
326         case WM_MBUTTONUP:
327                 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
328                 break;
329         case WM_RBUTTONDOWN:
330                 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
331                 break;
332         case WM_RBUTTONUP:
333                 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
334                 break;
335         case WM_MOUSEMOVE:
336                 signal_pointer_motion.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
337                 break;
338         case WM_SIZE:
339                 options.width=LOWORD(lp);
340                 options.height=HIWORD(lp);
341                 signal_resize.emit(options.width, options.height);
342                 break;
343         case WM_CLOSE:
344                 signal_close.emit();
345                 break;
346         default:
347                 return 0;
348         }
349
350         return 1;
351 }
352
353 LRESULT CALLBACK Window::wndproc_(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
354 {
355         if(msg==WM_CREATE)
356         {
357                 CREATESTRUCT *cs=reinterpret_cast<CREATESTRUCT *>(lparam);
358                 SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(cs->lpCreateParams));
359         }
360         else
361         {
362                 Window *wnd=reinterpret_cast<Window *>(GetWindowLong(hwnd, 0));
363                 if(wnd && wnd->wndproc(msg, wparam, lparam))
364                         return 0;
365         }
366
367         return DefWindowProc(hwnd, msg, wparam, lparam);
368 }
369 #endif
370
371 } // namespace Graphics
372 } // namespace Msp