5 #ifdef WITH_XF86VIDMODE
6 #include <X11/extensions/xf86vmode.h>
11 #include <msp/core/application.h>
12 #include <msp/core/systemerror.h>
15 #include "display_priv.h"
22 LRESULT CALLBACK wndproc_(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
26 CREATESTRUCT *cs = reinterpret_cast<CREATESTRUCT *>(lparam);
27 SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(cs->lpCreateParams));
31 Msp::Graphics::Window *wnd = reinterpret_cast<Msp::Graphics::Window *>(GetWindowLong(hwnd, 0));
32 Msp::Graphics::Window::Event ev;
36 if(wnd && wnd->event(ev))
40 return DefWindowProc(hwnd, msg, wparam, lparam);
43 Bool match_event_type(Display *, XEvent *event, XPointer arg)
45 return event->type==*reinterpret_cast<int *>(arg);
54 WindowOptions::WindowOptions():
62 Window::Window(Display &dpy, unsigned w, unsigned h, bool fs):
67 options.fullscreen = fs;
72 Window::Window(Display &dpy, const WindowOptions &opts):
82 kbd_autorepeat = true;
87 static bool wndclass_created = false;
93 wndcl.cbSize = sizeof(WNDCLASSEX);
95 wndcl.lpfnWndProc = &wndproc_;
97 wndcl.cbWndExtra = sizeof(Window *);
98 wndcl.hInstance = reinterpret_cast<HINSTANCE>(Application::get_data());
100 wndcl.hCursor = LoadCursor(0, IDC_ARROW);
101 wndcl.hbrBackground = 0;
102 wndcl.lpszMenuName = 0;
103 wndcl.lpszClassName = "mspgui";
106 if(!RegisterClassEx(&wndcl))
107 throw system_error("RegisterClassEx");
109 wndclass_created = true;
113 SetRect(&rect, 0, 0, options.width, options.height);
115 int style = (options.fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW);
116 if(!options.resizable)
117 style &= ~WS_THICKFRAME;
118 int exstyle = (options.fullscreen ? WS_EX_APPWINDOW : WS_EX_OVERLAPPEDWINDOW);
119 AdjustWindowRectEx(&rect, style, false, exstyle);
121 priv->window = CreateWindowEx(exstyle,
125 CW_USEDEFAULT, CW_USEDEFAULT,
126 rect.right-rect.left, rect.bottom-rect.top,
129 reinterpret_cast<HINSTANCE>(Application::get_data()),
132 throw system_error("CreateWindowEx");
135 ::Display *dpy = display.get_private().display;
137 priv->wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", true);
138 priv->invisible_cursor = 0;
140 XSetWindowAttributes attr;
141 attr.override_redirect = options.fullscreen;
142 attr.event_mask = ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask|EnterWindowMask;
144 priv->window = XCreateWindow(dpy,
145 DefaultRootWindow(dpy),
147 options.width, options.height,
152 CWOverrideRedirect|CWEventMask, &attr);
154 XSetWMProtocols(dpy, priv->window, &priv->wm_delete_window, 1);
156 if(!options.resizable)
159 hints.flags = PMinSize|PMaxSize;
160 hints.min_width=hints.max_width = options.width;
161 hints.min_height=hints.max_height = options.height;
162 XSetWMNormalHints(dpy, priv->window, &hints);
167 display.add_window(*this);
168 display.check_error();
175 CloseWindow(priv->window);
177 XDestroyWindow(display.get_private().display, priv->window);
179 if(priv->invisible_cursor)
180 XFreeCursor(display.get_private().display, priv->invisible_cursor);
183 display.remove_window(*this);
185 if(options.fullscreen)
186 display.restore_mode();
191 void Window::set_title(const string &title)
194 SetWindowText(priv->window, title.c_str());
196 vector<unsigned char> buf(title.begin(), title.end());
198 prop.value = &buf[0];
199 prop.encoding = XA_STRING;
201 prop.nitems = title.size();
202 XSetWMName(display.get_private().display, priv->window, &prop);
203 display.check_error();
207 void Window::reconfigure(const WindowOptions &opts)
209 bool fullscreen_changed = (opts.fullscreen!=options.fullscreen);
210 resizing = (opts.width!=options.width || opts.height!=options.height);
216 SetRect(&rect, 0, 0, options.width, options.height);
218 int style = (options.fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW);
219 if(!options.resizable)
220 style &= ~WS_THICKFRAME;
221 int exstyle = (options.fullscreen ? WS_EX_APPWINDOW : WS_EX_OVERLAPPEDWINDOW);
222 AdjustWindowRectEx(&rect, style, false, exstyle);
224 if(fullscreen_changed)
227 SetWindowLong(priv->window, GWL_EXSTYLE, exstyle);
228 SetWindowLong(priv->window, GWL_STYLE, style);
232 if(options.fullscreen)
233 SetWindowPos(priv->window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOZORDER);
235 SetWindowPos(priv->window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOMOVE|SWP_NOZORDER);
237 ::Display *dpy = display.get_private().display;
239 bool was_visible = visible;
240 if(fullscreen_changed)
246 // Wait for the window to be unmapped. This makes window managers happy.
248 int ev_type = UnmapNotify;
249 XPeekIfEvent(dpy, &ev, match_event_type, reinterpret_cast<char *>(&ev_type));
252 XSetWindowAttributes attr;
253 attr.override_redirect = options.fullscreen;
254 XChangeWindowAttributes(dpy, priv->window, CWOverrideRedirect, &attr);
258 if(options.resizable)
262 hints.flags = PMinSize|PMaxSize;
263 hints.min_width=hints.max_width = options.width;
264 hints.min_height=hints.max_height = options.height;
266 XSetWMNormalHints(dpy, priv->window, &hints);
268 if(options.fullscreen)
269 XMoveResizeWindow(dpy, priv->window, 0, 0, options.width, options.height);
271 XResizeWindow(dpy, priv->window, options.width, options.height);
273 if(fullscreen_changed)
282 if(options.fullscreen)
283 display.set_mode(VideoMode(options.width, options.height));
284 else if(fullscreen_changed)
285 display.restore_mode();
289 void Window::set_keyboard_autorepeat(bool r)
294 void Window::show_cursor(bool s)
299 ::Display *dpy = display.get_private().display;
302 XUndefineCursor(dpy, priv->window);
305 if(!priv->invisible_cursor)
307 int screen = DefaultScreen(dpy);
309 Pixmap pm = XCreatePixmap(dpy, priv->window, 1, 1, 1);
310 GC gc = XCreateGC(dpy, pm, 0, 0);
311 XSetFunction(dpy, gc, GXclear);
312 XDrawPoint(dpy, pm, gc, 0, 0);
316 black.pixel = BlackPixel(dpy, screen);
317 XQueryColor(dpy, DefaultColormap(dpy, screen), &black);
319 priv->invisible_cursor = XCreatePixmapCursor(dpy, pm, pm, &black, &black, 0, 0);
321 XFreePixmap(dpy, pm);
323 XDefineCursor(dpy, priv->window, priv->invisible_cursor);
328 void Window::warp_pointer(int x, int y)
331 XWarpPointer(display.get_private().display, None, priv->window, 0, 0, 0, 0, x, y);
341 ShowWindow(priv->window, SW_SHOWNORMAL);
343 XMapRaised(display.get_private().display, priv->window);
347 if(options.fullscreen)
349 display.set_mode(VideoMode(options.width, options.height));
351 XWarpPointer(display.get_private().display, None, priv->window, 0, 0, 0, 0, options.width/2, options.height/2);
359 ShowWindow(priv->window, SW_HIDE);
361 XUnmapWindow(display.get_private().display, priv->window);
365 if(options.fullscreen)
366 display.restore_mode();
369 bool Window::event(const Event &evnt)
384 signal_input_event.emit(evnt);
387 options.width = LOWORD(evnt.lparam);
388 options.height = HIWORD(evnt.lparam);
389 signal_resize.emit(options.width, options.height);
398 const XEvent &ev = evnt.xevent;
406 signal_input_event.emit(evnt);
408 case ConfigureNotify:
409 if((ev.xconfigure.width==static_cast<int>(options.width) && ev.xconfigure.height==static_cast<int>(options.height)) == resizing)
411 options.width = ev.xconfigure.width;
412 options.height = ev.xconfigure.height;
414 signal_resize.emit(options.width, options.height);
416 #ifdef WITH_XF86VIDMODE
417 if(options.fullscreen)
419 ::Display *dpy = display.get_private().display;
420 int screen = DefaultScreen(dpy);
421 XF86VidModeSetViewPort(dpy, screen, ev.xconfigure.x, ev.xconfigure.y);
426 if(ev.xclient.data.l[0]==static_cast<long>(priv->wm_delete_window))
430 if(options.fullscreen)
431 XSetInputFocus(display.get_private().display, priv->window, RevertToParent, CurrentTime);
434 if(options.fullscreen)
435 XGrabPointer(display.get_private().display, priv->window, true, None, GrabModeAsync, GrabModeAsync, priv->window, None, CurrentTime);
444 } // namespace Graphics