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