3 This file is part of libmspgbase
4 Copyright © 2007-2010 Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
10 #include <X11/Xatom.h>
11 #include <X11/Xutil.h>
12 #ifdef WITH_XF86VIDMODE
13 #include <X11/extensions/xf86vmode.h>
18 #include <msp/core/application.h>
19 #include <msp/core/except.h>
22 #include "display_priv.h"
29 LRESULT CALLBACK wndproc_(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
33 CREATESTRUCT *cs=reinterpret_cast<CREATESTRUCT *>(lparam);
34 SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(cs->lpCreateParams));
38 Msp::Graphics::Window *wnd=reinterpret_cast<Msp::Graphics::Window *>(GetWindowLong(hwnd, 0));
39 Msp::Graphics::Window::Event ev;
43 if(wnd && wnd->event(ev))
47 return DefWindowProc(hwnd, msg, wparam, lparam);
50 Bool match_event_type(Display *, XEvent *event, XPointer arg)
52 return event->type==*reinterpret_cast<int *>(arg);
61 WindowOptions::WindowOptions():
69 Window::Window(Display &dpy, unsigned w, unsigned h, bool fs):
74 options.fullscreen=fs;
79 Window::Window(Display &dpy, const WindowOptions &opts):
94 static bool wndclass_created=false;
100 wndcl.cbSize=sizeof(WNDCLASSEX);
102 wndcl.lpfnWndProc=&wndproc_;
104 wndcl.cbWndExtra=sizeof(Window *);
105 wndcl.hInstance=reinterpret_cast<HINSTANCE>(Application::get_data());
107 wndcl.hCursor=LoadCursor(0, IDC_ARROW);
108 wndcl.hbrBackground=0;
109 wndcl.lpszMenuName=0;
110 wndcl.lpszClassName="mspgbase";
113 if(!RegisterClassEx(&wndcl))
114 throw Exception("Couldn't register window class");
116 wndclass_created=true;
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 priv->window=CreateWindowEx(exstyle,
132 CW_USEDEFAULT, CW_USEDEFAULT,
133 rect.right-rect.left, rect.bottom-rect.top,
136 reinterpret_cast<HINSTANCE>(Application::get_data()),
139 throw Exception("CreateWindowEx failed");
142 ::Display *dpy=display.get_private().display;
144 priv->wm_delete_window=XInternAtom(dpy, "WM_DELETE_WINDOW", true);
145 priv->invisible_cursor=0;
147 XSetWindowAttributes attr;
148 attr.override_redirect=options.fullscreen;
149 attr.event_mask=ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask|EnterWindowMask;
151 priv->window=XCreateWindow(dpy,
152 DefaultRootWindow(dpy),
154 options.width, options.height,
159 CWOverrideRedirect|CWEventMask, &attr);
161 XSetWMProtocols(dpy, priv->window, &priv->wm_delete_window, 1);
163 if(!options.resizable)
166 hints.flags=PMinSize|PMaxSize;
167 hints.min_width=hints.max_width=options.width;
168 hints.min_height=hints.max_height=options.height;
169 XSetWMNormalHints(dpy, priv->window, &hints);
174 display.add_window(*this);
175 display.check_error();
182 CloseWindow(priv->window);
184 XDestroyWindow(display.get_private().display, priv->window);
186 if(priv->invisible_cursor)
187 XFreeCursor(display.get_private().display, priv->invisible_cursor);
190 display.remove_window(*this);
192 if(options.fullscreen)
193 display.restore_mode();
198 void Window::set_title(const string &title)
201 SetWindowText(priv->window, title.c_str());
203 vector<unsigned char> buf(title.begin(), title.end());
206 prop.encoding=XA_STRING;
208 prop.nitems=title.size();
209 XSetWMName(display.get_private().display, priv->window, &prop);
210 display.check_error();
214 void Window::reconfigure(const WindowOptions &opts)
216 bool fullscreen_changed=(opts.fullscreen!=options.fullscreen);
217 resizing=(opts.width!=options.width || opts.height!=options.height);
223 SetRect(&rect, 0, 0, options.width, options.height);
225 int style=(options.fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW);
226 if(!options.resizable)
227 style&=~WS_THICKFRAME;
228 int exstyle=(options.fullscreen ? WS_EX_APPWINDOW : WS_EX_OVERLAPPEDWINDOW);
229 AdjustWindowRectEx(&rect, style, false, exstyle);
231 if(fullscreen_changed)
234 SetWindowLong(priv->window, GWL_EXSTYLE, exstyle);
235 SetWindowLong(priv->window, GWL_STYLE, style);
239 if(options.fullscreen)
240 SetWindowPos(priv->window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOZORDER);
242 SetWindowPos(priv->window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOMOVE|SWP_NOZORDER);
244 ::Display *dpy=display.get_private().display;
246 bool was_visible=visible;
247 if(fullscreen_changed)
253 // Wait for the window to be unmapped. This makes window managers happy.
255 int ev_type=UnmapNotify;
256 XPeekIfEvent(dpy, &ev, match_event_type, reinterpret_cast<char *>(&ev_type));
259 XSetWindowAttributes attr;
260 attr.override_redirect=options.fullscreen;
261 XChangeWindowAttributes(dpy, priv->window, CWOverrideRedirect, &attr);
265 if(options.resizable)
269 hints.flags=PMinSize|PMaxSize;
270 hints.min_width=hints.max_width=options.width;
271 hints.min_height=hints.max_height=options.height;
273 XSetWMNormalHints(dpy, priv->window, &hints);
275 if(options.fullscreen)
276 XMoveResizeWindow(dpy, priv->window, 0, 0, options.width, options.height);
278 XResizeWindow(dpy, priv->window, options.width, options.height);
280 if(fullscreen_changed)
289 if(options.fullscreen)
290 display.set_mode(VideoMode(options.width, options.height));
291 else if(fullscreen_changed)
292 display.restore_mode();
296 void Window::set_keyboard_autorepeat(bool r)
301 void Window::show_cursor(bool s)
306 ::Display *dpy=display.get_private().display;
309 XUndefineCursor(dpy, priv->window);
312 if(!priv->invisible_cursor)
314 int screen=DefaultScreen(dpy);
316 Pixmap pm=XCreatePixmap(dpy, priv->window, 1, 1, 1);
317 GC gc=XCreateGC(dpy, pm, 0, 0);
318 XSetFunction(dpy, gc, GXclear);
319 XDrawPoint(dpy, pm, gc, 0, 0);
323 black.pixel=BlackPixel(dpy, screen);
324 XQueryColor(dpy, DefaultColormap(dpy, screen), &black);
326 priv->invisible_cursor=XCreatePixmapCursor(dpy, pm, pm, &black, &black, 0, 0);
328 XFreePixmap(dpy, pm);
330 XDefineCursor(dpy, priv->window, priv->invisible_cursor);
335 void Window::warp_pointer(int x, int y)
338 XWarpPointer(display.get_private().display, None, priv->window, 0, 0, 0, 0, x, y);
348 ShowWindow(priv->window, SW_SHOWNORMAL);
350 XMapRaised(display.get_private().display, priv->window);
354 if(options.fullscreen)
356 display.set_mode(VideoMode(options.width, options.height));
358 XWarpPointer(display.get_private().display, None, priv->window, 0, 0, 0, 0, options.width/2, options.height/2);
366 ShowWindow(priv->window, SW_HIDE);
368 XUnmapWindow(display.get_private().display, priv->window);
372 if(options.fullscreen)
373 display.restore_mode();
376 bool Window::event(const Event &evnt)
379 WPARAM wp=evnt.wparam;
380 LPARAM lp=evnt.lparam;
384 signal_key_press.emit(wp, 0, wp);
387 signal_key_release.emit(wp, 0);
390 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
393 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
396 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
399 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
402 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
405 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
409 unsigned btn = (HIWORD(wp)&0x8000) ? 5 : 4;
410 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), btn, 0);
411 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), btn, 0);
415 signal_pointer_motion.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
418 options.width=LOWORD(lp);
419 options.height=HIWORD(lp);
420 signal_resize.emit(options.width, options.height);
429 const XEvent &ev=evnt.xevent;
433 signal_button_press.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
436 signal_button_release.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
439 signal_pointer_motion.emit(ev.xmotion.x, ev.xmotion.y);
444 XLookupString(const_cast<XKeyEvent *>(&ev.xkey), buf, sizeof(buf), 0, 0);
445 // XXX Handle the result according to locale
446 signal_key_press.emit(XKeycodeToKeysym(display.get_private().display, ev.xkey.keycode, 0), ev.xkey.state, buf[0]);
450 signal_key_release.emit(XKeycodeToKeysym(display.get_private().display, ev.xkey.keycode, 0), ev.xkey.state);
452 case ConfigureNotify:
453 if((ev.xconfigure.width==static_cast<int>(options.width) && ev.xconfigure.height==static_cast<int>(options.height)) == resizing)
455 options.width=ev.xconfigure.width;
456 options.height=ev.xconfigure.height;
458 signal_resize.emit(options.width, options.height);
460 #ifdef WITH_XF86VIDMODE
461 if(options.fullscreen)
463 ::Display *dpy=display.get_private().display;
464 int screen=DefaultScreen(dpy);
465 XF86VidModeSetViewPort(dpy, screen, ev.xconfigure.x, ev.xconfigure.y);
470 if(ev.xclient.data.l[0]==static_cast<long>(priv->wm_delete_window))
474 if(options.fullscreen)
475 XSetInputFocus(display.get_private().display, priv->window, RevertToParent, CurrentTime);
478 if(options.fullscreen)
479 XGrabPointer(display.get_private().display, priv->window, true, None, GrabModeAsync, GrabModeAsync, priv->window, None, CurrentTime);
488 } // namespace Graphics