]> git.tdb.fi Git - libs/gui.git/blobdiff - source/window.cpp
Add names for input devices
[libs/gui.git] / source / window.cpp
index 75a88dce6f3331e82917ea161de7bc604a67b5e7..da877e966679b8ffd8f66b362a9f8feec2b6e695 100644 (file)
@@ -6,42 +6,45 @@ Distributed under the LGPL
 */
 
 #include <vector>
-#include <GL/glx.h>
-#include <msp/core/error.h>
+#ifndef WIN32
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#else
+#include <windowsx.h>
+#endif
+#include <msp/core/application.h>
+#include <msp/core/except.h>
+#include "display.h"
 #include "window.h"
 
 using namespace std;
 
-#include <msp/strings/lexicalcast.h>
+#include <iostream>
 
 namespace Msp {
+namespace Graphics {
 
-DisplayOptions::DisplayOptions():
+WindowOptions::WindowOptions():
        width(640),
        height(480),
-       fullscreen(false)
+       fullscreen(false),
+       resizable(false)
 { }
 
 
-/**
-Initializes the class but does not open the window.  Intended for use by
-derived classes.
-*/
-Window::Window():
-       display(0),
-       window(0)
-{ }
-
-Window::Window(unsigned w, unsigned h)
+Window::Window(Display &dpy, unsigned w, unsigned h, bool fs):
+       display(dpy)
 {
        options.width=w;
        options.height=h;
+       options.fullscreen=fs;
 
        init();
 }
 
-Window::Window(const DisplayOptions &dopt):
-       options(dopt)
+Window::Window(Display &dpy, const WindowOptions &opts):
+       display(dpy),
+       options(opts)
 {
        init();
 }
@@ -49,101 +52,271 @@ Window::Window(const DisplayOptions &dopt):
 Window::~Window()
 {
        if(window)
-               XDestroyWindow(display, window);
-       if(display)
-               XCloseDisplay(display);
-}
+#ifdef WIN32
+               CloseWindow(window);
+#else
+               XDestroyWindow(display.get_display(), window);
+#endif
 
-void Window::show()
-{
-       XMapRaised(display, window);
+       display.remove_window(this);
+
+       if(options.fullscreen)
+               display.restore_mode();
 }
 
-void Window::hide()
+void Window::set_title(const string &title)
 {
-       XUnmapWindow(display, window);
+#ifdef WIN32
+       SetWindowText(window, title.c_str());
+#else
+       vector<unsigned char> buf(title.begin(), title.end());
+       XTextProperty prop;
+       prop.value=&buf[0];
+       prop.encoding=XA_STRING;
+       prop.format=8;
+       prop.nitems=title.size();
+       XSetWMName(display.get_display(), window, &prop);
+       display.check_error();
+#endif
 }
 
-void Window::tick()
+void Window::reconfigure(const WindowOptions &opts)
 {
-       while(1)
-       {
-               int pending=XPending(display);
-               if(pending==0)
-                       break;
+       bool fullscreen_changed=(opts.fullscreen!=options.fullscreen);
 
-               for(int i=0; i<pending; ++i)
-               {
-                       XEvent event;
-                       XNextEvent(display, &event);
-                       process_event(event);
-               }
+       options=opts;
+
+#ifdef WIN32
+       // XXX Preserve position
+       MoveWindow(window, 0, 0, options.width, options.height, false);
+
+       (void)fullscreen_changed;
+#else
+       ::Display *dpy=display.get_display();
+
+       XMoveResizeWindow(dpy, window, 0, 0, options.width, options.height);
+
+       if(fullscreen_changed)
+       {
+               hide();
+               XSetWindowAttributes attr;
+               attr.override_redirect=options.fullscreen;
+               XChangeWindowAttributes(dpy, window, CWOverrideRedirect, &attr);
+               show();
        }
+
+       if(options.fullscreen)
+               display.set_mode(VideoMode(options.width, options.height));
+       else if(fullscreen_changed)
+               display.restore_mode();
+#endif
 }
 
-void Window::prepare()
+void Window::show()
 {
-       if(options.display.empty())
-               display=XOpenDisplay(0);
-       else
-               display=XOpenDisplay(options.display.c_str());
-       if(!display)
-               throw Exception("Couldn't open X display");
+#ifdef WIN32
+       ShowWindow(window, SW_SHOWNORMAL);
+#else
+       XMapRaised(display.get_display(), window);
+       display.check_error();
+#endif
+}
 
-       //XSetErrorHandler(x_error_handler);
+void Window::hide()
+{
+#ifdef WIN32
+       ShowWindow(window, SW_HIDE);
+#else
+       XUnmapWindow(display.get_display(), window);
+       display.check_error();
+#endif
 }
 
 void Window::init()
 {
-       prepare();
+#ifdef WIN32
+       static bool wndclass_created=false;
+
+       if(!wndclass_created)
+       {
+               WNDCLASSEX wndcl;
 
-       window=XCreateWindow(display, DefaultRootWindow(display), 0, 0, options.width, options.height, 0, CopyFromParent, InputOutput, CopyFromParent, 0, 0);
+               wndcl.cbSize=sizeof(WNDCLASSEX);
+               wndcl.style=0;
+               wndcl.lpfnWndProc=&wndproc_;
+               wndcl.cbClsExtra=0;
+               wndcl.cbWndExtra=sizeof(Window *);
+               wndcl.hInstance=reinterpret_cast<HINSTANCE>(Application::get_data());
+               wndcl.hIcon=0;
+               wndcl.hCursor=LoadCursor(0, IDC_ARROW);
+               wndcl.hbrBackground=0;
+               wndcl.lpszMenuName=0;
+               wndcl.lpszClassName="mspgbase";
+               wndcl.hIconSm=0;
+
+               if(!RegisterClassEx(&wndcl))
+                       throw Exception("Couldn't register window class");
+
+               wndclass_created=true;
+       }
+
+       RECT rect;
+       rect.left=0;
+       rect.top=0;
+       rect.right=options.width;
+       rect.bottom=options.height;
+       AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, false, WS_EX_OVERLAPPEDWINDOW);
+
+       window=CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
+               "mspgbase",
+               "Window",
+               WS_OVERLAPPEDWINDOW,
+               CW_USEDEFAULT, CW_USEDEFAULT,
+               rect.right-rect.left, rect.bottom-rect.top,
+               0,
+               0,
+               reinterpret_cast<HINSTANCE>(Application::get_data()),
+               this);
        if(!window)
-               throw Exception("Couldn't create a window");
+               throw Exception("CreateWindowEx failed");
+
+#else
+       ::Display *dpy=display.get_display();
+
+       wm_delete_window=XInternAtom(dpy, "WM_DELETE_WINDOW", true);
 
-       XSelectInput(display, window, ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask);
+       XSetWindowAttributes attr;
+       attr.override_redirect=options.fullscreen;
+       attr.event_mask=ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask|EnterWindowMask;
+
+       window=XCreateWindow(dpy,
+               DefaultRootWindow(dpy),
+               0, 0,
+               options.width, options.height,
+               0,
+               CopyFromParent,
+               InputOutput,
+               CopyFromParent,
+               CWOverrideRedirect|CWEventMask, &attr);
+
+       XSetWMProtocols(dpy, window, &wm_delete_window, 1);
+
+       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);
+       }
+#endif
+
+       display.add_window(this);
+       display.check_error();
 }
 
-void Window::process_event(const XEvent &event)
+#ifndef WIN32
+void Window::event(const XEvent &ev)
 {
-       switch(event.type)
+       switch(ev.type)
        {
        case ButtonPress:
-               signal_button_press.emit(event.xbutton.x, event.xbutton.y, event.xbutton.button, event.xbutton.state);
+               signal_button_press.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
                break;
        case ButtonRelease:
-               signal_button_release.emit(event.xbutton.x, event.xbutton.y, event.xbutton.button, event.xbutton.state);
+               signal_button_release.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
                break;
        case MotionNotify:
-               signal_pointer_motion.emit(event.xmotion.x, event.xmotion.y);
+               signal_pointer_motion.emit(ev.xmotion.x, ev.xmotion.y);
                break;
        case KeyPress:
                {
                        char buf[16];
-                       XLookupString(const_cast<XKeyEvent *>(&event.xkey), buf, sizeof(buf), 0, 0);
+                       XLookupString(const_cast<XKeyEvent *>(&ev.xkey), buf, sizeof(buf), 0, 0);
                        // XXX Handle the result according to locale
-                       signal_key_press.emit(event.xkey.keycode, event.xkey.state, buf[0]);
+                       signal_key_press.emit(ev.xkey.keycode, ev.xkey.state, buf[0]);
                }
                break;
        case KeyRelease:
-               signal_key_release.emit(event.xkey.keycode, event.xkey.state);
+               signal_key_release.emit(ev.xkey.keycode, ev.xkey.state);
                break;
        case ConfigureNotify:
-               options.width=event.xconfigure.width;
-               options.height=event.xconfigure.height;
+               options.width=ev.xconfigure.width;
+               options.height=ev.xconfigure.height;
+               signal_resize.emit(options.width, options.height);
+               break;
+       case ClientMessage:
+               if(ev.xclient.data.l[0]==static_cast<long>(wm_delete_window))
+                       signal_close.emit();
+               break;
+       case EnterNotify:
+               XSetInputFocus(display.get_display(), window, RevertToParent, CurrentTime);
+               break;
+       case MapNotify:
+               if(options.fullscreen)
+                       XGrabPointer(display.get_display(), window, true, None, GrabModeAsync, GrabModeAsync, window, None, CurrentTime);
                break;
        default:;
        }
 }
+#endif
 
-int Window::x_error_handler(Display *display, XErrorEvent *error)
+#ifdef WIN32
+int Window::wndproc(UINT msg, WPARAM wp, LPARAM lp)
 {
-       char buf[128];
-       XGetErrorText(display, error->error_code, buf, sizeof(buf));
-       /*string request_code=lexical_cast(error->request_code);
-       char buf2[1024];
-       XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), buf, buf2, sizeof(buf2));*/
-       throw Exception(buf);
+       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();
+               break;
+       default:
+               return 0;
+       }
+
+       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
 
+} // namespace Graphics
 } // namespace Msp