3 This file is part of libmspgbase
4 Copyright © 2007 Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
10 #include <X11/Xatom.h>
11 #include <X11/Xutil.h>
15 #include <msp/core/application.h>
16 #include <msp/core/except.h>
25 WindowOptions::WindowOptions():
33 Window::Window(Display &dpy, unsigned w, unsigned h, bool fs):
38 options.fullscreen=fs;
43 Window::Window(Display &dpy, const WindowOptions &opts):
56 XDestroyWindow(display.get_display(), window);
59 XFreeCursor(display.get_display(), invisible_cursor);
62 display.remove_window(this);
64 if(options.fullscreen)
65 display.restore_mode();
68 void Window::set_title(const string &title)
71 SetWindowText(window, title.c_str());
73 vector<unsigned char> buf(title.begin(), title.end());
76 prop.encoding=XA_STRING;
78 prop.nitems=title.size();
79 XSetWMName(display.get_display(), window, &prop);
80 display.check_error();
84 void Window::reconfigure(const WindowOptions &opts)
86 bool fullscreen_changed=(opts.fullscreen!=options.fullscreen);
92 SetRect(&rect, 0, 0, options.width, options.height);
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);
100 if(fullscreen_changed)
103 SetWindowLong(window, GWL_EXSTYLE, exstyle);
104 SetWindowLong(window, GWL_STYLE, style);
108 if(options.fullscreen)
109 SetWindowPos(window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOZORDER);
111 SetWindowPos(window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOMOVE|SWP_NOZORDER);
113 (void)fullscreen_changed;
115 ::Display *dpy=display.get_display();
117 if(fullscreen_changed)
120 XSetWindowAttributes attr;
121 attr.override_redirect=options.fullscreen;
122 XChangeWindowAttributes(dpy, window, CWOverrideRedirect, &attr);
127 if(options.resizable)
131 hints.flags=PMinSize|PMaxSize;
132 hints.min_width=hints.max_width=options.width;
133 hints.min_height=hints.max_height=options.height;
135 XSetWMNormalHints(dpy, window, &hints);
137 if(options.fullscreen)
138 XMoveResizeWindow(dpy, window, 0, 0, options.width, options.height);
140 XResizeWindow(dpy, window, options.width, options.height);
143 if(options.fullscreen)
144 display.set_mode(VideoMode(options.width, options.height));
145 else if(fullscreen_changed)
146 display.restore_mode();
149 void Window::show_cursor(bool s)
154 ::Display *dpy=display.get_display();
157 XUndefineCursor(dpy, window);
160 if(!invisible_cursor)
162 int screen=DefaultScreen(dpy);
164 XImage *img=XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap, 0, &data, 1, 1, 8, 1);
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);
171 black.pixel=BlackPixel(dpy, screen);
172 XQueryColor(dpy, DefaultColormap(dpy, screen), &black);
174 invisible_cursor=XCreatePixmapCursor(dpy, pm, pm, &black, &black, 0, 0);
177 XFreePixmap(dpy, pm);
181 XDefineCursor(dpy, window, invisible_cursor);
189 ShowWindow(window, SW_SHOWNORMAL);
191 XMapRaised(display.get_display(), window);
192 display.check_error();
199 ShowWindow(window, SW_HIDE);
201 XUnmapWindow(display.get_display(), window);
202 display.check_error();
209 static bool wndclass_created=false;
211 if(!wndclass_created)
215 wndcl.cbSize=sizeof(WNDCLASSEX);
217 wndcl.lpfnWndProc=&wndproc_;
219 wndcl.cbWndExtra=sizeof(Window *);
220 wndcl.hInstance=reinterpret_cast<HINSTANCE>(Application::get_data());
222 wndcl.hCursor=LoadCursor(0, IDC_ARROW);
223 wndcl.hbrBackground=0;
224 wndcl.lpszMenuName=0;
225 wndcl.lpszClassName="mspgbase";
228 if(!RegisterClassEx(&wndcl))
229 throw Exception("Couldn't register window class");
231 wndclass_created=true;
235 SetRect(&rect, 0, 0, options.width, options.height);
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);
243 window=CreateWindowEx(exstyle,
247 CW_USEDEFAULT, CW_USEDEFAULT,
248 rect.right-rect.left, rect.bottom-rect.top,
251 reinterpret_cast<HINSTANCE>(Application::get_data()),
254 throw Exception("CreateWindowEx failed");
256 if(options.fullscreen)
257 display.set_mode(VideoMode(options.width, options.height));
260 ::Display *dpy=display.get_display();
262 wm_delete_window=XInternAtom(dpy, "WM_DELETE_WINDOW", true);
265 XSetWindowAttributes attr;
266 attr.override_redirect=options.fullscreen;
267 attr.event_mask=ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask|EnterWindowMask;
269 window=XCreateWindow(dpy,
270 DefaultRootWindow(dpy),
272 options.width, options.height,
277 CWOverrideRedirect|CWEventMask, &attr);
279 XSetWMProtocols(dpy, window, &wm_delete_window, 1);
281 if(!options.resizable)
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);
290 if(options.fullscreen)
292 display.set_mode(VideoMode(options.width, options.height));
293 XWarpPointer(dpy, None, window, 0, 0, 0, 0, options.width/2, options.height/2);
297 display.add_window(this);
298 display.check_error();
302 void Window::event(const XEvent &ev)
307 signal_button_press.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
310 signal_button_release.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
313 signal_pointer_motion.emit(ev.xmotion.x, ev.xmotion.y);
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]);
324 signal_key_release.emit(XKeycodeToKeysym(display.get_display(), ev.xkey.keycode, 0), ev.xkey.state);
326 case ConfigureNotify:
327 options.width=ev.xconfigure.width;
328 options.height=ev.xconfigure.height;
329 signal_resize.emit(options.width, options.height);
332 if(ev.xclient.data.l[0]==static_cast<long>(wm_delete_window))
336 XSetInputFocus(display.get_display(), window, RevertToParent, CurrentTime);
339 if(options.fullscreen)
340 XGrabPointer(display.get_display(), window, true, None, GrabModeAsync, GrabModeAsync, window, None, CurrentTime);
348 int Window::wndproc(UINT msg, WPARAM wp, LPARAM lp)
353 signal_key_press.emit(wp, 0, wp);
356 signal_key_release.emit(wp, 0);
359 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
362 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
365 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
368 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
371 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
374 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
377 signal_pointer_motion.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
380 options.width=LOWORD(lp);
381 options.height=HIWORD(lp);
382 signal_resize.emit(options.width, options.height);
394 LRESULT CALLBACK Window::wndproc_(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
398 CREATESTRUCT *cs=reinterpret_cast<CREATESTRUCT *>(lparam);
399 SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(cs->lpCreateParams));
403 Window *wnd=reinterpret_cast<Window *>(GetWindowLong(hwnd, 0));
404 if(wnd && wnd->wndproc(msg, wparam, lparam))
408 return DefWindowProc(hwnd, msg, wparam, lparam);
412 } // namespace Graphics