5 #ifdef WITH_XF86VIDMODE
6 #include <X11/extensions/xf86vmode.h>
11 #include <msp/core/application.h>
14 #include "display_priv.h"
21 LRESULT CALLBACK wndproc_(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
25 CREATESTRUCT *cs = reinterpret_cast<CREATESTRUCT *>(lparam);
26 SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(cs->lpCreateParams));
30 Msp::Graphics::Window *wnd = reinterpret_cast<Msp::Graphics::Window *>(GetWindowLong(hwnd, 0));
31 Msp::Graphics::Window::Event ev;
35 if(wnd && wnd->event(ev))
39 return DefWindowProc(hwnd, msg, wparam, lparam);
42 Bool match_event_type(Display *, XEvent *event, XPointer arg)
44 return event->type==*reinterpret_cast<int *>(arg);
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):
81 kbd_autorepeat = true;
86 static bool wndclass_created = false;
92 wndcl.cbSize = sizeof(WNDCLASSEX);
94 wndcl.lpfnWndProc = &wndproc_;
96 wndcl.cbWndExtra = sizeof(Window *);
97 wndcl.hInstance = reinterpret_cast<HINSTANCE>(Application::get_data());
99 wndcl.hCursor = LoadCursor(0, IDC_ARROW);
100 wndcl.hbrBackground = 0;
101 wndcl.lpszMenuName = 0;
102 wndcl.lpszClassName = "mspgbase";
105 if(!RegisterClassEx(&wndcl))
106 throw system_error("RegisterClassEx");
108 wndclass_created = true;
112 SetRect(&rect, 0, 0, options.width, options.height);
114 int style = (options.fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW);
115 if(!options.resizable)
116 style &= ~WS_THICKFRAME;
117 int exstyle = (options.fullscreen ? WS_EX_APPWINDOW : WS_EX_OVERLAPPEDWINDOW);
118 AdjustWindowRectEx(&rect, style, false, exstyle);
120 priv->window = CreateWindowEx(exstyle,
124 CW_USEDEFAULT, CW_USEDEFAULT,
125 rect.right-rect.left, rect.bottom-rect.top,
128 reinterpret_cast<HINSTANCE>(Application::get_data()),
131 throw system_error("CreateWindowEx");
134 ::Display *dpy = display.get_private().display;
136 priv->wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", true);
137 priv->invisible_cursor = 0;
139 XSetWindowAttributes attr;
140 attr.override_redirect = options.fullscreen;
141 attr.event_mask = ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask|EnterWindowMask;
143 priv->window = XCreateWindow(dpy,
144 DefaultRootWindow(dpy),
146 options.width, options.height,
151 CWOverrideRedirect|CWEventMask, &attr);
153 XSetWMProtocols(dpy, priv->window, &priv->wm_delete_window, 1);
155 if(!options.resizable)
158 hints.flags = PMinSize|PMaxSize;
159 hints.min_width=hints.max_width = options.width;
160 hints.min_height=hints.max_height = options.height;
161 XSetWMNormalHints(dpy, priv->window, &hints);
166 display.add_window(*this);
167 display.check_error();
174 CloseWindow(priv->window);
176 XDestroyWindow(display.get_private().display, priv->window);
178 if(priv->invisible_cursor)
179 XFreeCursor(display.get_private().display, priv->invisible_cursor);
182 display.remove_window(*this);
184 if(options.fullscreen)
185 display.restore_mode();
190 void Window::set_title(const string &title)
193 SetWindowText(priv->window, title.c_str());
195 vector<unsigned char> buf(title.begin(), title.end());
197 prop.value = &buf[0];
198 prop.encoding = XA_STRING;
200 prop.nitems = title.size();
201 XSetWMName(display.get_private().display, priv->window, &prop);
202 display.check_error();
206 void Window::reconfigure(const WindowOptions &opts)
208 bool fullscreen_changed = (opts.fullscreen!=options.fullscreen);
209 resizing = (opts.width!=options.width || opts.height!=options.height);
215 SetRect(&rect, 0, 0, options.width, options.height);
217 int style = (options.fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW);
218 if(!options.resizable)
219 style &= ~WS_THICKFRAME;
220 int exstyle = (options.fullscreen ? WS_EX_APPWINDOW : WS_EX_OVERLAPPEDWINDOW);
221 AdjustWindowRectEx(&rect, style, false, exstyle);
223 if(fullscreen_changed)
226 SetWindowLong(priv->window, GWL_EXSTYLE, exstyle);
227 SetWindowLong(priv->window, GWL_STYLE, style);
231 if(options.fullscreen)
232 SetWindowPos(priv->window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOZORDER);
234 SetWindowPos(priv->window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOMOVE|SWP_NOZORDER);
236 ::Display *dpy = display.get_private().display;
238 bool was_visible = visible;
239 if(fullscreen_changed)
245 // Wait for the window to be unmapped. This makes window managers happy.
247 int ev_type = UnmapNotify;
248 XPeekIfEvent(dpy, &ev, match_event_type, reinterpret_cast<char *>(&ev_type));
251 XSetWindowAttributes attr;
252 attr.override_redirect = options.fullscreen;
253 XChangeWindowAttributes(dpy, priv->window, CWOverrideRedirect, &attr);
257 if(options.resizable)
261 hints.flags = PMinSize|PMaxSize;
262 hints.min_width=hints.max_width = options.width;
263 hints.min_height=hints.max_height = options.height;
265 XSetWMNormalHints(dpy, priv->window, &hints);
267 if(options.fullscreen)
268 XMoveResizeWindow(dpy, priv->window, 0, 0, options.width, options.height);
270 XResizeWindow(dpy, priv->window, options.width, options.height);
272 if(fullscreen_changed)
281 if(options.fullscreen)
282 display.set_mode(VideoMode(options.width, options.height));
283 else if(fullscreen_changed)
284 display.restore_mode();
288 void Window::set_keyboard_autorepeat(bool r)
293 void Window::show_cursor(bool s)
298 ::Display *dpy = display.get_private().display;
301 XUndefineCursor(dpy, priv->window);
304 if(!priv->invisible_cursor)
306 int screen = DefaultScreen(dpy);
308 Pixmap pm = XCreatePixmap(dpy, priv->window, 1, 1, 1);
309 GC gc = XCreateGC(dpy, pm, 0, 0);
310 XSetFunction(dpy, gc, GXclear);
311 XDrawPoint(dpy, pm, gc, 0, 0);
315 black.pixel = BlackPixel(dpy, screen);
316 XQueryColor(dpy, DefaultColormap(dpy, screen), &black);
318 priv->invisible_cursor = XCreatePixmapCursor(dpy, pm, pm, &black, &black, 0, 0);
320 XFreePixmap(dpy, pm);
322 XDefineCursor(dpy, priv->window, priv->invisible_cursor);
327 void Window::warp_pointer(int x, int y)
330 XWarpPointer(display.get_private().display, None, priv->window, 0, 0, 0, 0, x, y);
340 ShowWindow(priv->window, SW_SHOWNORMAL);
342 XMapRaised(display.get_private().display, priv->window);
346 if(options.fullscreen)
348 display.set_mode(VideoMode(options.width, options.height));
350 XWarpPointer(display.get_private().display, None, priv->window, 0, 0, 0, 0, options.width/2, options.height/2);
358 ShowWindow(priv->window, SW_HIDE);
360 XUnmapWindow(display.get_private().display, priv->window);
364 if(options.fullscreen)
365 display.restore_mode();
368 bool Window::event(const Event &evnt)
371 WPARAM wp = evnt.wparam;
372 LPARAM lp = evnt.lparam;
376 signal_key_press.emit(wp, 0, wp);
379 signal_key_release.emit(wp, 0);
382 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
385 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
388 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
391 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
394 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
397 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
401 unsigned btn = (HIWORD(wp)&0x8000) ? 5 : 4;
402 signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), btn, 0);
403 signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), btn, 0);
407 signal_pointer_motion.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
410 options.width = LOWORD(lp);
411 options.height = HIWORD(lp);
412 signal_resize.emit(options.width, options.height);
421 const XEvent &ev = evnt.xevent;
425 signal_button_press.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
428 signal_button_release.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
431 signal_pointer_motion.emit(ev.xmotion.x, ev.xmotion.y);
436 XLookupString(const_cast<XKeyEvent *>(&ev.xkey), buf, sizeof(buf), 0, 0);
437 // XXX Handle the result according to locale
438 signal_key_press.emit(XKeycodeToKeysym(display.get_private().display, ev.xkey.keycode, 0), ev.xkey.state, buf[0]);
442 signal_key_release.emit(XKeycodeToKeysym(display.get_private().display, ev.xkey.keycode, 0), ev.xkey.state);
444 case ConfigureNotify:
445 if((ev.xconfigure.width==static_cast<int>(options.width) && ev.xconfigure.height==static_cast<int>(options.height)) == resizing)
447 options.width = ev.xconfigure.width;
448 options.height = ev.xconfigure.height;
450 signal_resize.emit(options.width, options.height);
452 #ifdef WITH_XF86VIDMODE
453 if(options.fullscreen)
455 ::Display *dpy = display.get_private().display;
456 int screen = DefaultScreen(dpy);
457 XF86VidModeSetViewPort(dpy, screen, ev.xconfigure.x, ev.xconfigure.y);
462 if(ev.xclient.data.l[0]==static_cast<long>(priv->wm_delete_window))
466 if(options.fullscreen)
467 XSetInputFocus(display.get_private().display, priv->window, RevertToParent, CurrentTime);
470 if(options.fullscreen)
471 XGrabPointer(display.get_private().display, priv->window, true, None, GrabModeAsync, GrabModeAsync, priv->window, None, CurrentTime);
480 } // namespace Graphics