/* $Id$
This file is part of libmspgbase
-Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Copyright © 2007-2008 Mikko Rasa, Mikkosoft Productions
Distributed under the LGPL
*/
#include <msp/core/except.h>
#include "display.h"
#include "window.h"
+#include "display_priv.h"
using namespace std;
-#include <iostream>
+namespace {
+
+#ifdef WIN32
+LRESULT CALLBACK wndproc_(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ if(msg==WM_CREATE)
+ {
+ CREATESTRUCT *cs=reinterpret_cast<CREATESTRUCT *>(lparam);
+ SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(cs->lpCreateParams));
+ }
+ else
+ {
+ Msp::Graphics::Window *wnd=reinterpret_cast<Msp::Graphics::Window *>(GetWindowLong(hwnd, 0));
+ Msp::Graphics::Window::Event ev;
+ ev.msg=msg;
+ ev.wparam=wparam;
+ ev.lparam=lparam;
+ if(wnd && wnd->event(ev))
+ return 0;
+ }
+
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+#else
+Bool match_event_type(Display *, XEvent *event, XPointer arg)
+{
+ return event->type==reinterpret_cast<int>(arg);
+}
+#endif
+
+}
namespace Msp {
namespace Graphics {
Window::~Window()
{
- if(window)
+ if(priv->window)
#ifdef WIN32
- CloseWindow(window);
+ CloseWindow(priv->window);
#else
- XDestroyWindow(display.get_display(), window);
+ XDestroyWindow(display.get_private().display, priv->window);
+
+ if(priv->invisible_cursor)
+ XFreeCursor(display.get_private().display, priv->invisible_cursor);
#endif
- display.remove_window(this);
+ display.remove_window(*this);
if(options.fullscreen)
display.restore_mode();
void Window::set_title(const string &title)
{
#ifdef WIN32
- SetWindowText(window, title.c_str());
+ SetWindowText(priv->window, title.c_str());
#else
vector<unsigned char> buf(title.begin(), title.end());
XTextProperty prop;
prop.encoding=XA_STRING;
prop.format=8;
prop.nitems=title.size();
- XSetWMName(display.get_display(), window, &prop);
+ XSetWMName(display.get_private().display, priv->window, &prop);
display.check_error();
#endif
}
options=opts;
#ifdef WIN32
- // XXX Preserve position
- MoveWindow(window, 0, 0, options.width, options.height, false);
+ RECT rect;
+ SetRect(&rect, 0, 0, options.width, options.height);
- (void)fullscreen_changed;
-#else
- ::Display *dpy=display.get_display();
+ int style=(options.fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW);
+ if(!options.resizable)
+ style&=~WS_THICKFRAME;
+ int exstyle=(options.fullscreen ? WS_EX_APPWINDOW : WS_EX_OVERLAPPEDWINDOW);
+ AdjustWindowRectEx(&rect, style, false, exstyle);
+
+ if(fullscreen_changed)
+ {
+ hide();
+ SetWindowLong(priv->window, GWL_EXSTYLE, exstyle);
+ SetWindowLong(priv->window, GWL_STYLE, style);
+ show();
+ }
- XMoveResizeWindow(dpy, window, 0, 0, options.width, options.height);
+ if(options.fullscreen)
+ SetWindowPos(priv->window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOZORDER);
+ else
+ SetWindowPos(priv->window, 0, 0, 0, rect.right-rect.left, rect.bottom-rect.top, SWP_NOMOVE|SWP_NOZORDER);
+#else
+ ::Display *dpy=display.get_private().display;
if(fullscreen_changed)
{
hide();
+
+ // Wait for the window to be unmapped. This makes window managers happy.
+ XEvent ev;
+ XPeekIfEvent(dpy, &ev, match_event_type, reinterpret_cast<XPointer>(UnmapNotify));
+
XSetWindowAttributes attr;
attr.override_redirect=options.fullscreen;
- XChangeWindowAttributes(dpy, window, CWOverrideRedirect, &attr);
+ XChangeWindowAttributes(dpy, priv->window, CWOverrideRedirect, &attr);
show();
}
+ XSizeHints hints;
+ if(options.resizable)
+ hints.flags=0;
+ else
+ {
+ hints.flags=PMinSize|PMaxSize;
+ hints.min_width=hints.max_width=options.width;
+ hints.min_height=hints.max_height=options.height;
+ }
+ XSetWMNormalHints(dpy, priv->window, &hints);
+
+ if(options.fullscreen)
+ XMoveResizeWindow(dpy, priv->window, 0, 0, options.width, options.height);
+ else
+ XResizeWindow(dpy, priv->window, options.width, options.height);
+#endif
+
if(options.fullscreen)
display.set_mode(VideoMode(options.width, options.height));
else if(fullscreen_changed)
display.restore_mode();
+}
+
+void Window::show_cursor(bool s)
+{
+#ifdef WIN32
+ ShowCursor(s);
+#else
+ ::Display *dpy=display.get_private().display;
+
+ if(s)
+ XUndefineCursor(dpy, priv->window);
+ else
+ {
+ if(!priv->invisible_cursor)
+ {
+ int screen=DefaultScreen(dpy);
+
+ Pixmap pm=XCreatePixmap(dpy, priv->window, 1, 1, 1);
+ GC gc=XCreateGC(dpy, pm, 0, 0);
+ XSetFunction(dpy, gc, GXclear);
+ XDrawPoint(dpy, pm, gc, 0, 0);
+ XFreeGC(dpy, gc);
+
+ XColor black;
+ black.pixel=BlackPixel(dpy, screen);
+ XQueryColor(dpy, DefaultColormap(dpy, screen), &black);
+
+ priv->invisible_cursor=XCreatePixmapCursor(dpy, pm, pm, &black, &black, 0, 0);
+
+ XFreePixmap(dpy, pm);
+ }
+ XDefineCursor(dpy, priv->window, priv->invisible_cursor);
+ }
#endif
}
void Window::show()
{
#ifdef WIN32
- ShowWindow(window, SW_SHOWNORMAL);
+ ShowWindow(priv->window, SW_SHOWNORMAL);
#else
- XMapRaised(display.get_display(), window);
- display.check_error();
+ XMapRaised(display.get_private().display, priv->window);
#endif
}
void Window::hide()
{
#ifdef WIN32
- ShowWindow(window, SW_HIDE);
+ ShowWindow(priv->window, SW_HIDE);
#else
- XUnmapWindow(display.get_display(), window);
- display.check_error();
+ XUnmapWindow(display.get_private().display, priv->window);
#endif
}
void Window::init()
{
+ priv=new Private;
+
#ifdef WIN32
static bool wndclass_created=false;
}
RECT rect;
- rect.left=0;
- rect.top=0;
- rect.right=options.width;
- rect.bottom=options.height;
- AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, false, WS_EX_OVERLAPPEDWINDOW);
+ SetRect(&rect, 0, 0, options.width, options.height);
+
+ int style=(options.fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW);
+ if(!options.resizable)
+ style&=~WS_THICKFRAME;
+ int exstyle=(options.fullscreen ? WS_EX_APPWINDOW : WS_EX_OVERLAPPEDWINDOW);
+ AdjustWindowRectEx(&rect, style, false, exstyle);
- window=CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
+ priv->window=CreateWindowEx(exstyle,
"mspgbase",
"Window",
- WS_OVERLAPPEDWINDOW,
+ style,
CW_USEDEFAULT, CW_USEDEFAULT,
rect.right-rect.left, rect.bottom-rect.top,
0,
0,
reinterpret_cast<HINSTANCE>(Application::get_data()),
this);
- if(!window)
+ if(!priv->window)
throw Exception("CreateWindowEx failed");
+ if(options.fullscreen)
+ display.set_mode(VideoMode(options.width, options.height));
+
#else
- ::Display *dpy=display.get_display();
+ ::Display *dpy=display.get_private().display;
- wm_delete_window=XInternAtom(dpy, "WM_DELETE_WINDOW", true);
+ priv->wm_delete_window=XInternAtom(dpy, "WM_DELETE_WINDOW", true);
+ priv->invisible_cursor=0;
XSetWindowAttributes attr;
attr.override_redirect=options.fullscreen;
attr.event_mask=ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask|EnterWindowMask;
- window=XCreateWindow(dpy,
+ priv->window=XCreateWindow(dpy,
DefaultRootWindow(dpy),
0, 0,
options.width, options.height,
CopyFromParent,
CWOverrideRedirect|CWEventMask, &attr);
- XSetWMProtocols(dpy, window, &wm_delete_window, 1);
+ XSetWMProtocols(dpy, priv->window, &priv->wm_delete_window, 1);
+
+ if(!options.resizable)
+ {
+ XSizeHints hints;
+ hints.flags=PMinSize|PMaxSize;
+ hints.min_width=hints.max_width=options.width;
+ hints.min_height=hints.max_height=options.height;
+ XSetWMNormalHints(dpy, priv->window, &hints);
+ }
if(options.fullscreen)
{
display.set_mode(VideoMode(options.width, options.height));
- XWarpPointer(dpy, None, window, 0, 0, 0, 0, options.width/2, options.height/2);
+ XWarpPointer(dpy, None, priv->window, 0, 0, 0, 0, options.width/2, options.height/2);
}
#endif
- display.add_window(this);
+ display.add_window(*this);
display.check_error();
}
-#ifndef WIN32
-void Window::event(const XEvent &ev)
+bool Window::event(const Event &evnt)
{
+#ifdef WIN32
+ WPARAM wp=evnt.wparam;
+ LPARAM lp=evnt.lparam;
+ switch(evnt.msg)
+ {
+ case WM_KEYDOWN:
+ signal_key_press.emit(wp, 0, wp);
+ break;
+ case WM_KEYUP:
+ signal_key_release.emit(wp, 0);
+ break;
+ case WM_LBUTTONDOWN:
+ signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
+ break;
+ case WM_LBUTTONUP:
+ signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
+ break;
+ case WM_MBUTTONDOWN:
+ signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
+ break;
+ case WM_MBUTTONUP:
+ signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
+ break;
+ case WM_RBUTTONDOWN:
+ signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
+ break;
+ case WM_RBUTTONUP:
+ signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
+ break;
+ case WM_MOUSEMOVE:
+ signal_pointer_motion.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
+ break;
+ case WM_SIZE:
+ options.width=LOWORD(lp);
+ options.height=HIWORD(lp);
+ signal_resize.emit(options.width, options.height);
+ break;
+ case WM_CLOSE:
+ signal_close.emit();
+ break;
+ default:
+ return false;
+ }
+#else
+ const XEvent &ev=evnt.xevent;
switch(ev.type)
{
case ButtonPress:
char buf[16];
XLookupString(const_cast<XKeyEvent *>(&ev.xkey), buf, sizeof(buf), 0, 0);
// XXX Handle the result according to locale
- signal_key_press.emit(ev.xkey.keycode, ev.xkey.state, buf[0]);
+ signal_key_press.emit(XKeycodeToKeysym(display.get_private().display, ev.xkey.keycode, 0), ev.xkey.state, buf[0]);
}
break;
case KeyRelease:
- signal_key_release.emit(ev.xkey.keycode, ev.xkey.state);
+ signal_key_release.emit(XKeycodeToKeysym(display.get_private().display, ev.xkey.keycode, 0), ev.xkey.state);
break;
case ConfigureNotify:
options.width=ev.xconfigure.width;
signal_resize.emit(options.width, options.height);
break;
case ClientMessage:
- if(ev.xclient.data.l[0]==static_cast<long>(wm_delete_window))
+ if(ev.xclient.data.l[0]==static_cast<long>(priv->wm_delete_window))
signal_close.emit();
break;
case EnterNotify:
- XSetInputFocus(display.get_display(), window, RevertToParent, CurrentTime);
+ if(options.fullscreen)
+ XSetInputFocus(display.get_private().display, priv->window, RevertToParent, CurrentTime);
break;
case MapNotify:
if(options.fullscreen)
- XGrabPointer(display.get_display(), window, true, None, GrabModeAsync, GrabModeAsync, window, None, CurrentTime);
- break;
- default:;
- }
-}
-#endif
-
-#ifdef WIN32
-int Window::wndproc(UINT msg, WPARAM wp, LPARAM lp)
-{
- switch(msg)
- {
- case WM_KEYDOWN:
- signal_key_press.emit((lp>>16)&0x1FF, 0, wp);
- break;
- case WM_KEYUP:
- signal_key_release.emit((lp>>16)&0x1FF, 0);
- break;
- case WM_LBUTTONDOWN:
- signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
- break;
- case WM_LBUTTONUP:
- signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 1, 0);
- break;
- case WM_MBUTTONDOWN:
- signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
- break;
- case WM_MBUTTONUP:
- signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 2, 0);
- break;
- case WM_RBUTTONDOWN:
- signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
- break;
- case WM_RBUTTONUP:
- signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), 3, 0);
- break;
- case WM_MOUSEMOVE:
- signal_pointer_motion.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
- break;
- case WM_CLOSE:
- signal_close.emit();
+ XGrabPointer(display.get_private().display, priv->window, true, None, GrabModeAsync, GrabModeAsync, priv->window, None, CurrentTime);
break;
default:
- return 0;
+ return false;
}
-
- return 1;
-}
-
-LRESULT CALLBACK Window::wndproc_(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
-{
- if(msg==WM_CREATE)
- {
- CREATESTRUCT *cs=reinterpret_cast<CREATESTRUCT *>(lparam);
- SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(cs->lpCreateParams));
- }
- else
- {
- Window *wnd=reinterpret_cast<Window *>(GetWindowLong(hwnd, 0));
- if(wnd && wnd->wndproc(msg, wparam, lparam))
- return 0;
- }
-
- return DefWindowProc(hwnd, msg, wparam, lparam);
-}
#endif
+ return true;
+}
} // namespace Graphics
} // namespace Msp