3 This file is part of libmspgbase
4 Copyright © 2007-2008 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>
19 #include "display_priv.h"
26 LRESULT CALLBACK wndproc_(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
30 CREATESTRUCT *cs=reinterpret_cast<CREATESTRUCT *>(lparam);
31 SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(cs->lpCreateParams));
35 Msp::Graphics::Window *wnd=reinterpret_cast<Msp::Graphics::Window *>(GetWindowLong(hwnd, 0));
36 Msp::Graphics::Window::Event ev;
40 if(wnd && wnd->event(ev))
44 return DefWindowProc(hwnd, msg, wparam, lparam);
53 WindowOptions::WindowOptions():
61 Window::Window(Display &dpy, unsigned w, unsigned h, bool fs):
66 options.fullscreen=fs;
71 Window::Window(Display &dpy, const WindowOptions &opts):
82 CloseWindow(priv->window);
84 XDestroyWindow(display.get_private().display, priv->window);
86 if(priv->invisible_cursor)
87 XFreeCursor(display.get_private().display, priv->invisible_cursor);
90 display.remove_window(*this);
92 if(options.fullscreen)
93 display.restore_mode();
96 void Window::set_title(const string &title)
99 SetWindowText(priv->window, title.c_str());
101 vector<unsigned char> buf(title.begin(), title.end());
104 prop.encoding=XA_STRING;
106 prop.nitems=title.size();
107 XSetWMName(display.get_private().display, priv->window, &prop);
108 display.check_error();
112 void Window::reconfigure(const WindowOptions &opts)
114 bool fullscreen_changed=(opts.fullscreen!=options.fullscreen);
120 SetRect(&rect, 0, 0, options.width, options.height);
122 int style=(options.fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW);
123 if(!options.resizable)
124 style&=~WS_THICKFRAME;
125 int exstyle=(options.fullscreen ? WS_EX_APPWINDOW : WS_EX_OVERLAPPEDWINDOW);
126 AdjustWindowRectEx(&rect, style, false, exstyle);
128 if(fullscreen_changed)
131 SetWindowLong(priv->window, GWL_EXSTYLE, exstyle);
132 SetWindowLong(priv->window, GWL_STYLE, style);
136 if(options.fullscreen)
137 SetWindowPos(priv->window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOZORDER);
139 SetWindowPos(priv->window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOMOVE|SWP_NOZORDER);
141 ::Display *dpy=display.get_private().display;
143 if(fullscreen_changed)
146 XSetWindowAttributes attr;
147 attr.override_redirect=options.fullscreen;
148 XChangeWindowAttributes(dpy, priv->window, CWOverrideRedirect, &attr);
153 if(options.resizable)
157 hints.flags=PMinSize|PMaxSize;
158 hints.min_width=hints.max_width=options.width;
159 hints.min_height=hints.max_height=options.height;
161 XSetWMNormalHints(dpy, priv->window, &hints);
163 if(options.fullscreen)
164 XMoveResizeWindow(dpy, priv->window, 0, 0, options.width, options.height);
166 XResizeWindow(dpy, priv->window, options.width, options.height);
169 if(options.fullscreen)
170 display.set_mode(VideoMode(options.width, options.height));
171 else if(fullscreen_changed)
172 display.restore_mode();
175 void Window::show_cursor(bool s)
180 ::Display *dpy=display.get_private().display;
183 XUndefineCursor(dpy, priv->window);
186 if(!priv->invisible_cursor)
188 int screen=DefaultScreen(dpy);
190 XImage *img=XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap, 0, &data, 1, 1, 8, 1);
192 Pixmap pm=XCreatePixmap(dpy, priv->window, 1, 1, 1);
193 GC gc=XCreateGC(dpy, pm, 0, 0);
194 XPutImage(dpy, pm, gc, img, 0, 0, 0, 0, 1, 1);
197 black.pixel=BlackPixel(dpy, screen);
198 XQueryColor(dpy, DefaultColormap(dpy, screen), &black);
200 priv->invisible_cursor=XCreatePixmapCursor(dpy, pm, pm, &black, &black, 0, 0);
203 XFreePixmap(dpy, pm);
207 XDefineCursor(dpy, priv->window, priv->invisible_cursor);
215 ShowWindow(priv->window, SW_SHOWNORMAL);
217 XMapRaised(display.get_private().display, priv->window);
218 display.check_error();
225 ShowWindow(priv->window, SW_HIDE);
227 XUnmapWindow(display.get_private().display, priv->window);
228 display.check_error();
237 static bool wndclass_created=false;
239 if(!wndclass_created)
243 wndcl.cbSize=sizeof(WNDCLASSEX);
245 wndcl.lpfnWndProc=&wndproc_;
247 wndcl.cbWndExtra=sizeof(Window *);
248 wndcl.hInstance=reinterpret_cast<HINSTANCE>(Application::get_data());
250 wndcl.hCursor=LoadCursor(0, IDC_ARROW);
251 wndcl.hbrBackground=0;
252 wndcl.lpszMenuName=0;
253 wndcl.lpszClassName="mspgbase";
256 if(!RegisterClassEx(&wndcl))
257 throw Exception("Couldn't register window class");
259 wndclass_created=true;
263 SetRect(&rect, 0, 0, options.width, options.height);
265 int style=(options.fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW);
266 if(!options.resizable)
267 style&=~WS_THICKFRAME;
268 int exstyle=(options.fullscreen ? WS_EX_APPWINDOW : WS_EX_OVERLAPPEDWINDOW);
269 AdjustWindowRectEx(&rect, style, false, exstyle);
271 priv->window=CreateWindowEx(exstyle,
275 CW_USEDEFAULT, CW_USEDEFAULT,
276 rect.right-rect.left, rect.bottom-rect.top,
279 reinterpret_cast<HINSTANCE>(Application::get_data()),
282 throw Exception("CreateWindowEx failed");
284 if(options.fullscreen)
285 display.set_mode(VideoMode(options.width, options.height));
288 ::Display *dpy=display.get_private().display;
290 priv->wm_delete_window=XInternAtom(dpy, "WM_DELETE_WINDOW", true);
291 priv->invisible_cursor=0;
293 XSetWindowAttributes attr;
294 attr.override_redirect=options.fullscreen;
295 attr.event_mask=ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask|EnterWindowMask;
297 priv->window=XCreateWindow(dpy,
298 DefaultRootWindow(dpy),
300 options.width, options.height,
305 CWOverrideRedirect|CWEventMask, &attr);
307 XSetWMProtocols(dpy, priv->window, &priv->wm_delete_window, 1);
309 if(!options.resizable)
312 hints.flags=PMinSize|PMaxSize;
313 hints.min_width=hints.max_width=options.width;
314 hints.min_height=hints.max_height=options.height;
315 XSetWMNormalHints(dpy, priv->window, &hints);
318 if(options.fullscreen)
320 display.set_mode(VideoMode(options.width, options.height));
321 XWarpPointer(dpy, None, priv->window, 0, 0, 0, 0, options.width/2, options.height/2);
325 display.add_window(*this);
326 display.check_error();
329 bool Window::event(const Event &evnt)
332 WPARAM wp=evnt.wparam;
333 LPARAM lp=evnt.lparam;
337 signal_key_press.emit(wp, 0, wp);
340 signal_key_release.emit(wp, 0);
343 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
346 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
349 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
352 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
355 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
358 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
361 signal_pointer_motion.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
364 options.width=LOWORD(lp);
365 options.height=HIWORD(lp);
366 signal_resize.emit(options.width, options.height);
375 const XEvent &ev=evnt.xevent;
379 signal_button_press.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
382 signal_button_release.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
385 signal_pointer_motion.emit(ev.xmotion.x, ev.xmotion.y);
390 XLookupString(const_cast<XKeyEvent *>(&ev.xkey), buf, sizeof(buf), 0, 0);
391 // XXX Handle the result according to locale
392 signal_key_press.emit(XKeycodeToKeysym(display.get_private().display, ev.xkey.keycode, 0), ev.xkey.state, buf[0]);
396 signal_key_release.emit(XKeycodeToKeysym(display.get_private().display, ev.xkey.keycode, 0), ev.xkey.state);
398 case ConfigureNotify:
399 options.width=ev.xconfigure.width;
400 options.height=ev.xconfigure.height;
401 signal_resize.emit(options.width, options.height);
404 if(ev.xclient.data.l[0]==static_cast<long>(priv->wm_delete_window))
408 XSetInputFocus(display.get_private().display, priv->window, RevertToParent, CurrentTime);
411 if(options.fullscreen)
412 XGrabPointer(display.get_private().display, priv->window, true, None, GrabModeAsync, GrabModeAsync, priv->window, None, CurrentTime);
421 } // namespace Graphics