From: Mikko Rasa Date: Fri, 24 May 2013 18:08:06 +0000 (+0300) Subject: Split platform-specific parts into separate directories X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=1aca77b93853ee127ac3bbf6886f7f04920542ef;p=libs%2Fgui.git Split platform-specific parts into separate directories --- diff --git a/Build b/Build index f311834..a0f437c 100644 --- a/Build +++ b/Build @@ -57,6 +57,22 @@ package "mspgui" { source "source/graphics"; source "source/input"; + if_arch "windows" + { + overlay "windows"; + if_feature "opengl" + { + overlay "wgl"; + }; + }; + if_arch "!windows" + { + overlay "x11"; + if_feature "opengl" + { + overlay "glx"; + }; + }; install true; install_map { diff --git a/source/graphics/display.cpp b/source/graphics/display.cpp index 588d492..42b0e8b 100644 --- a/source/graphics/display.cpp +++ b/source/graphics/display.cpp @@ -1,49 +1,10 @@ -#include -#ifndef WIN32 -#include -#ifdef WITH_XF86VIDMODE -#include -#endif -#endif #include -#include #include "display.h" +#include "display_private.h" #include "window.h" -#include "display_priv.h" using namespace std; -namespace { - -bool error_flag = false; -std::string error_msg; - -#ifndef WIN32 -int x_error_handler(Display *display, XErrorEvent *event) -{ - char err[128]; - XGetErrorText(display, event->error_code, err, sizeof(err)); - - string request_code = Msp::lexical_cast(event->request_code); - char req[128]; - XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), request_code.c_str(), req, sizeof(req)); - - string msg = Msp::format("Request %s failed with %s [%08X]", req, err, event->resourceid); - if(error_flag) - cerr<<"Discarding error: "<display = XOpenDisplay(0); - else - priv->display = XOpenDisplay(disp_name.c_str()); - if(!priv->display) - throw runtime_error("XOpenDisplay"); - - XSetErrorHandler(x_error_handler); - -#ifdef WITH_XF86VIDMODE - int screen = DefaultScreen(priv->display); - - int nmodes; - XF86VidModeModeInfo **infos; - XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos); - for(int i=0; idisplay, screen, &dotclock, &modeline); - orig_mode = VideoMode(modeline.hdisplay, modeline.vdisplay); - if(modeline.htotal && modeline.vtotal) - orig_mode.rate = dotclock/(modeline.htotal*modeline.vtotal); -#endif -#endif -} - -Display::~Display() -{ -#ifndef WIN32 - XCloseDisplay(priv->display); - delete priv; -#endif -} - void Display::add_window(Window &wnd) { priv->windows[wnd.get_private().window] = &wnd; @@ -131,112 +23,11 @@ void Display::remove_window(Window &wnd) priv->windows.erase(wnd.get_private().window); } -void Display::set_mode(const VideoMode &mode) -{ -#if defined(WIN32) - DEVMODE info; - info.dmSize = sizeof(DEVMODE); - info.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT; - info.dmPelsWidth = mode.width; - info.dmPelsHeight = mode.height; - if(mode.rate) - { - info.dmFields |= DM_DISPLAYFREQUENCY; - info.dmDisplayFrequency = mode.rate; - } - - LONG ret = ChangeDisplaySettings(&info, CDS_FULLSCREEN); - if(ret!=DISP_CHANGE_SUCCESSFUL) - throw unsupported_video_mode(mode); -#elif defined(WITH_XF86VIDMODE) - int screen = DefaultScreen(priv->display); - - int nmodes; - XF86VidModeModeInfo **infos; - XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos); - for(int i=0; idisplay, screen, &info); - XF86VidModeSetViewPort(priv->display, screen, 0, 0); - return; - } - } - - throw unsupported_video_mode(mode); -#else - (void)mode; - throw runtime_error("no xf86vidmode support"); -#endif -} - void Display::tick() { check_error(); - while(1) - { -#ifdef WIN32 - MSG msg; - if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) - DispatchMessage(&msg); - else - break; -#else - int pending = XPending(priv->display); - if(pending==0) - break; - - for(; pending--;) - { - Window::Event event; - XNextEvent(priv->display, &event.xevent); - - check_error(); - - map::iterator j = priv->windows.find(event.xevent.xany.window); - if(j!=priv->windows.end()) - { - /* Filter keyboard autorepeat. If this packet is a KeyRelease and - the next one is a KeyPress with the exact same parameters, they - indicate autorepeat and must be dropped. */ - if(event.xevent.type==KeyRelease && !j->second->get_keyboard_autorepeat() && pending>0) - { - XKeyEvent &kev = event.xevent.xkey; - XEvent ev2; - XPeekEvent(priv->display, &ev2); - if(ev2.type==KeyPress) - { - XKeyEvent &kev2 = ev2.xkey; - if(kev2.window==kev.window && kev2.time==kev.time && kev2.keycode==kev.keycode) - { - XNextEvent(priv->display, &ev2); - --pending; - continue; - } - } - } - - j->second->event(event); - } - } -#endif - } -} - -void Display::check_error() -{ - if(error_flag) - { - error_flag = false; - throw runtime_error(error_msg); - } + while(process_events()) ; } } // namespace Graphics diff --git a/source/graphics/display.h b/source/graphics/display.h index 83b1853..d311ba7 100644 --- a/source/graphics/display.h +++ b/source/graphics/display.h @@ -2,7 +2,6 @@ #define MSP_GRAPHICS_DISPLAY_H_ #include -#include #include #include @@ -55,6 +54,9 @@ public: void restore_mode() { set_mode(orig_mode); } void tick(); +private: + bool process_events(); +public: void check_error(); }; diff --git a/source/graphics/display_priv.h b/source/graphics/display_priv.h deleted file mode 100644 index 5e82f9d..0000000 --- a/source/graphics/display_priv.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef MSP_GRAPHICS_DISPLAY_PRIV_H_ -#define MSP_GRAPHICS_DISPLAY_PRIV_H_ - -#include "window_priv.h" - -namespace Msp { -namespace Graphics { - -struct Display::Private -{ -#ifndef WIN32 - ::Display *display; -#endif - std::map windows; -}; - -} // namespace Graphics -} // namespace Msp - -#endif diff --git a/source/graphics/display_private.h b/source/graphics/display_private.h new file mode 100644 index 0000000..ec772b0 --- /dev/null +++ b/source/graphics/display_private.h @@ -0,0 +1,21 @@ +#ifndef MSP_GRAPHICS_DISPLAY_PRIVATE_H_ +#define MSP_GRAPHICS_DISPLAY_PRIVATE_H_ + +#include +#include "display.h" +#include "display_platform.h" +#include "window_private.h" + +namespace Msp { +namespace Graphics { + +struct Display::Private +{ + DisplayHandle display; + std::map windows; +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/drawcontext.cpp b/source/graphics/drawcontext.cpp deleted file mode 100644 index 0396567..0000000 --- a/source/graphics/drawcontext.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include -#ifndef WIN32 -#include -#include -#include -#include -#include -#endif -#include "display.h" -#include "drawcontext.h" -#include "window.h" -#include "display_priv.h" - -using namespace std; - -namespace Msp { -namespace Graphics { - -struct DrawContext::Private -{ -#ifndef WIN32 - XImage *image; - bool use_shm; - XShmSegmentInfo shminfo; -#endif -}; - -DrawContext::DrawContext(Window &w): - display(w.get_display()), - window(w) -{ -#ifdef WIN32 - throw runtime_error("no DrawContext support on windows"); -#else - priv = new Private; - - ::Display *dpy = display.get_private().display; - - priv->use_shm = XShmQueryExtension(dpy); - - XWindowAttributes wa; - XGetWindowAttributes(dpy, window.get_private().window, &wa); - - if(priv->use_shm) - { - priv->image = XShmCreateImage(dpy, wa.visual, wa.depth, ZPixmap, 0, &priv->shminfo, wa.width, wa.height); - if(!priv->image) - throw runtime_error("XShmCreateImage"); - - priv->shminfo.shmid = shmget(IPC_PRIVATE, priv->image->bytes_per_line*priv->image->height, IPC_CREAT|0666); - priv->shminfo.shmaddr=priv->image->data = reinterpret_cast(shmat(priv->shminfo.shmid, 0, 0)); - priv->shminfo.readOnly = false; - - XShmAttach(dpy, &priv->shminfo); - - XSync(dpy, false); - display.check_error(); - } - else - { - priv->image = XCreateImage(dpy, wa.visual, wa.depth, ZPixmap, 0, 0, wa.width, wa.height, 8, 0); - if(!priv->image) - throw runtime_error("XCreateImage"); - priv->image->data = new char[priv->image->bytes_per_line*priv->image->height]; - } -#endif -} - -DrawContext::~DrawContext() -{ -#ifndef WIN32 - if(priv->use_shm) - { - XShmDetach(display.get_private().display, &priv->shminfo); - shmdt(priv->shminfo.shmaddr); - shmctl(priv->shminfo.shmid, IPC_RMID, 0); - } - - XDestroyImage(priv->image); -#endif - - delete priv; -} - -unsigned DrawContext::get_depth() const -{ -#ifdef WIN32 - return 0; -#else - return priv->image->bits_per_pixel; -#endif -} - -unsigned char *DrawContext::get_data() -{ -#ifdef WIN32 - return 0; -#else - return reinterpret_cast(priv->image->data); -#endif -} - -void DrawContext::update() -{ -#ifndef WIN32 - ::Display *dpy = display.get_private().display; - WindowHandle wnd = window.get_private().window; - - GC gc = XCreateGC(dpy, wnd, 0, 0); - - if(priv->use_shm) - XShmPutImage(dpy, wnd, gc, priv->image, 0, 0, 0, 0, priv->image->width, priv->image->height, false); - else - XPutImage(dpy, wnd, gc, priv->image, 0, 0, 0, 0, priv->image->width, priv->image->height); - - XFreeGC(dpy, gc); -#endif -} - -} // namespace Graphics -} // namespace Msp diff --git a/source/graphics/glcontext.cpp b/source/graphics/glcontext.cpp index b713830..ab0ea7a 100644 --- a/source/graphics/glcontext.cpp +++ b/source/graphics/glcontext.cpp @@ -1,19 +1,9 @@ -#include -#ifdef WIN32 -#include -#endif -#ifdef WITH_OPENGL -#include -#ifndef WIN32 -#include -#endif -#endif -#include +#include #include -#include "display.h" #include "glcontext.h" #include "window.h" -#include "display_priv.h" + +using namespace std; namespace Msp { namespace Graphics { @@ -32,156 +22,31 @@ unsupported_gl_mode::unsupported_gl_mode(const GLOptions &opts): { } -#ifdef WITH_OPENGL -#ifdef WIN32 -typedef HGLRC Context; -#else -typedef GLXContext Context; -#endif - -struct GLContext::Private -{ - Context context; -#ifndef WIN32 - // In X11, we need to create a window with the chosen visual - WindowHandle subwnd; -#endif -}; -#endif - GLContext::GLContext(Window &wnd, const GLOptions &opts): display(wnd.get_display()), window(wnd) { -#ifdef WITH_OPENGL - priv = new Private; - -#ifdef WIN32 - HDC dc = GetDC(window.get_private().window); - - PIXELFORMATDESCRIPTOR pfd; - memset(&pfd, 0, sizeof(pfd)); - - pfd.nSize = sizeof(pfd); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; - if(opts.doublebuffer) - pfd.dwFlags |= PFD_DOUBLEBUFFER; - pfd.iPixelType = PFD_TYPE_RGBA; - if(opts.alpha) - pfd.cAlphaBits = 1; - pfd.cDepthBits = 1; - if(opts.stencil) - pfd.cStencilBits = 1; - - int pf_index = ChoosePixelFormat(dc, &pfd); - if(!pf_index) - throw unsupported_gl_mode(opts); - SetPixelFormat(dc, pf_index, &pfd); - - priv->context = wglCreateContext(dc); - wglMakeCurrent(dc, priv->context); - - ReleaseDC(window.get_private().window, dc); -#else - std::vector attribs; - - attribs.push_back(GLX_RGBA); - attribs.push_back(GLX_DEPTH_SIZE); - attribs.push_back(1); - - if(opts.alpha) - { - attribs.push_back(GLX_ALPHA_SIZE); - attribs.push_back(1); - } - - if(opts.stencil) - { - attribs.push_back(GLX_STENCIL_SIZE); - attribs.push_back(1); - } - - if(opts.doublebuffer) - attribs.push_back(GLX_DOUBLEBUFFER); - - if(opts.multisample>0) - { - attribs.push_back(GLX_SAMPLE_BUFFERS_ARB); - attribs.push_back(opts.multisample); - } - - attribs.push_back(0); - - ::Display *dpy = display.get_private().display; - - XVisualInfo *vi = glXChooseVisual(dpy, DefaultScreen(dpy), &attribs.front()); - if(!vi) - throw unsupported_gl_mode(opts); - priv->context = glXCreateContext(dpy, vi, 0, true); - - XSetWindowAttributes attr; - attr.colormap = XCreateColormap(dpy, DefaultRootWindow(dpy), vi->visual, AllocNone); - - priv->subwnd = XCreateWindow(dpy, window.get_private().window, 0, 0, window.get_width(), window.get_height(), 0, vi->depth, InputOutput, vi->visual, CWColormap, &attr); - XMapWindow(dpy, priv->subwnd); - - XFree(vi); - - glXMakeCurrent(dpy, priv->subwnd, priv->context); -#endif -#else - (void)wnd; - (void)opts; - throw runtime_error("no OpenGL support"); -#endif + platform_init(opts); window.signal_resize.connect(sigc::mem_fun(this, &GLContext::window_resized)); } -GLContext::~GLContext() +#ifndef WITH_OPENGL +void GLContext::platform_init(const GLOptions &) { -#ifdef WITH_OPENGL -#ifdef WIN32 - wglMakeCurrent(0, 0); - wglDeleteContext(priv->context); -#else - ::Display *dpy = display.get_private().display; - - glXMakeCurrent(dpy, 0, 0); - glXDestroyContext(dpy, priv->context); - XDestroyWindow(dpy, priv->subwnd); -#endif - delete priv; -#endif + throw runtime_error("no OpenGL support"); } +GLContext::~GLContext() +{ } + void GLContext::swap_buffers() -{ -#ifdef WITH_OPENGL -#ifdef WIN32 - HDC dc = GetDC(window.get_private().window); - SwapBuffers(dc); - ReleaseDC(window.get_private().window, dc); -#else - glXSwapBuffers(display.get_private().display, priv->subwnd); -#endif -#endif -} +{ } -void GLContext::window_resized(unsigned w, unsigned h) -{ -#ifdef WITH_OPENGL -#ifndef WIN32 - XMoveResizeWindow(display.get_private().display, priv->subwnd, 0, 0, w, h); -#endif - glViewport(0, 0, w, h); -#else - (void)w; - (void)h; +void GLContext::window_resized(unsigned, unsigned) +{ } #endif -} } // namespace Graphics } // namespace Msp diff --git a/source/graphics/glcontext.h b/source/graphics/glcontext.h index db9df4e..a248090 100644 --- a/source/graphics/glcontext.h +++ b/source/graphics/glcontext.h @@ -39,6 +39,9 @@ private: public: GLContext(Window &wnd, const GLOptions &opts = GLOptions()); +private: + void platform_init(const GLOptions &); +public: ~GLContext(); void swap_buffers(); diff --git a/source/graphics/glx/glcontext.cpp b/source/graphics/glx/glcontext.cpp new file mode 100644 index 0000000..09a4ef8 --- /dev/null +++ b/source/graphics/glx/glcontext.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include "glcontext.h" +#include "display_private.h" +#include "window_private.h" + +using namespace std; + +namespace Msp { +namespace Graphics { + +typedef GLXContext ContextHandle; + +struct GLContext::Private +{ + ContextHandle context; + // We need to create a window with the chosen visual + WindowHandle subwnd; +}; + + +void GLContext::platform_init(const GLOptions &opts) +{ + priv = new Private; + + vector attribs; + + attribs.push_back(GLX_RGBA); + attribs.push_back(GLX_DEPTH_SIZE); + attribs.push_back(1); + + if(opts.alpha) + { + attribs.push_back(GLX_ALPHA_SIZE); + attribs.push_back(1); + } + + if(opts.stencil) + { + attribs.push_back(GLX_STENCIL_SIZE); + attribs.push_back(1); + } + + if(opts.doublebuffer) + attribs.push_back(GLX_DOUBLEBUFFER); + + if(opts.multisample>0) + { + attribs.push_back(GLX_SAMPLE_BUFFERS_ARB); + attribs.push_back(opts.multisample); + } + + attribs.push_back(0); + + DisplayHandle dpy = display.get_private().display; + + XVisualInfo *vi = glXChooseVisual(dpy, DefaultScreen(dpy), &attribs.front()); + if(!vi) + throw unsupported_gl_mode(opts); + priv->context = glXCreateContext(dpy, vi, 0, true); + + XSetWindowAttributes attr; + attr.colormap = XCreateColormap(dpy, DefaultRootWindow(dpy), vi->visual, AllocNone); + + priv->subwnd = XCreateWindow(dpy, window.get_private().window, 0, 0, window.get_width(), window.get_height(), 0, vi->depth, InputOutput, vi->visual, CWColormap, &attr); + XMapWindow(dpy, priv->subwnd); + + XFree(vi); + + glXMakeCurrent(dpy, priv->subwnd, priv->context); +} + +GLContext::~GLContext() +{ + DisplayHandle dpy = display.get_private().display; + + glXMakeCurrent(dpy, 0, 0); + glXDestroyContext(dpy, priv->context); + XDestroyWindow(dpy, priv->subwnd); + + delete priv; +} + +void GLContext::swap_buffers() +{ + glXSwapBuffers(display.get_private().display, priv->subwnd); +} + +void GLContext::window_resized(unsigned w, unsigned h) +{ + XMoveResizeWindow(display.get_private().display, priv->subwnd, 0, 0, w, h); + glViewport(0, 0, w, h); +} + +} // namespace Graphics +} // namespace Msp diff --git a/source/graphics/wgl/glcontext.cpp b/source/graphics/wgl/glcontext.cpp new file mode 100644 index 0000000..1a0ad7a --- /dev/null +++ b/source/graphics/wgl/glcontext.cpp @@ -0,0 +1,70 @@ +#include +#include +#include "glcontext.h" +#include "window_private.h" + +namespace Msp { +namespace Graphics { + +typedef HGLRC ContextHandle; + +struct GLContext::Private +{ + ContextHandle context; +}; + + +void GLContext::platform_init() +{ + priv = new Private; + + HDC dc = GetDC(window.get_private().window); + + PIXELFORMATDESCRIPTOR pfd; + memset(&pfd, 0, sizeof(pfd)); + + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + if(opts.doublebuffer) + pfd.dwFlags |= PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + if(opts.alpha) + pfd.cAlphaBits = 1; + pfd.cDepthBits = 1; + if(opts.stencil) + pfd.cStencilBits = 1; + + int pf_index = ChoosePixelFormat(dc, &pfd); + if(!pf_index) + throw unsupported_gl_mode(opts); + SetPixelFormat(dc, pf_index, &pfd); + + priv->context = wglCreateContext(dc); + wglMakeCurrent(dc, priv->context); + + ReleaseDC(window.get_private().window, dc); +} + +GLContext::~GLContext() +{ + wglMakeCurrent(0, 0); + wglDeleteContext(priv->context); + + delete priv; +} + +void GLContext::swap_buffers() +{ + HDC dc = GetDC(window.get_private().window); + SwapBuffers(dc); + ReleaseDC(window.get_private().window, dc); +} + +void GLContext::window_resized(unsigned w, unsigned h) +{ + glViewport(0, 0, w, h); +} + +} // namespace Graphics +} // namespace Msp diff --git a/source/graphics/window.cpp b/source/graphics/window.cpp index a1353ac..166e49f 100644 --- a/source/graphics/window.cpp +++ b/source/graphics/window.cpp @@ -1,53 +1,9 @@ -#include -#ifndef WIN32 -#include -#include -#ifdef WITH_XF86VIDMODE -#include -#endif -#else -#include -#endif -#include -#include #include "display.h" #include "window.h" -#include "display_priv.h" +#include "window_private.h" using namespace std; -namespace { - -#ifdef WIN32 -LRESULT CALLBACK wndproc_(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -{ - if(msg==WM_CREATE) - { - CREATESTRUCT *cs = reinterpret_cast(lparam); - SetWindowLong(hwnd, 0, reinterpret_cast(cs->lpCreateParams)); - } - else - { - Msp::Graphics::Window *wnd = reinterpret_cast(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(arg); -} -#endif - -} - namespace Msp { namespace Graphics { @@ -83,86 +39,7 @@ void Window::init() resizing = false; priv = new Private; -#ifdef WIN32 - static bool wndclass_created = false; - - if(!wndclass_created) - { - WNDCLASSEX wndcl; - - wndcl.cbSize = sizeof(WNDCLASSEX); - wndcl.style = 0; - wndcl.lpfnWndProc = &wndproc_; - wndcl.cbClsExtra = 0; - wndcl.cbWndExtra = sizeof(Window *); - wndcl.hInstance = reinterpret_cast(Application::get_data()); - wndcl.hIcon = 0; - wndcl.hCursor = LoadCursor(0, IDC_ARROW); - wndcl.hbrBackground = 0; - wndcl.lpszMenuName = 0; - wndcl.lpszClassName = "mspgui"; - wndcl.hIconSm = 0; - - if(!RegisterClassEx(&wndcl)) - throw system_error("RegisterClassEx"); - - wndclass_created = true; - } - - RECT rect; - 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); - - priv->window = CreateWindowEx(exstyle, - "mspgui", - "Window", - style, - CW_USEDEFAULT, CW_USEDEFAULT, - rect.right-rect.left, rect.bottom-rect.top, - 0, - 0, - reinterpret_cast(Application::get_data()), - this); - if(!priv->window) - throw system_error("CreateWindowEx"); - -#else - ::Display *dpy = display.get_private().display; - - 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; - - priv->window = XCreateWindow(dpy, - DefaultRootWindow(dpy), - 0, 0, - options.width, options.height, - 0, - CopyFromParent, - InputOutput, - CopyFromParent, - CWOverrideRedirect|CWEventMask, &attr); - - 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); - } - -#endif + platform_init(); display.add_window(*this); display.check_error(); @@ -170,15 +47,7 @@ void Window::init() Window::~Window() { - if(priv->window) -#ifdef WIN32 - CloseWindow(priv->window); -#else - XDestroyWindow(display.get_private().display, priv->window); - - if(priv->invisible_cursor) - XFreeCursor(display.get_private().display, priv->invisible_cursor); -#endif + platform_cleanup(); display.remove_window(*this); @@ -188,94 +57,13 @@ Window::~Window() delete priv; } -void Window::set_title(const string &title) -{ -#ifdef WIN32 - SetWindowText(priv->window, title.c_str()); -#else - vector 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_private().display, priv->window, &prop); - display.check_error(); -#endif -} - void Window::reconfigure(const WindowOptions &opts) { bool fullscreen_changed = (opts.fullscreen!=options.fullscreen); resizing = (opts.width!=options.width || opts.height!=options.height); options = opts; - -#ifdef WIN32 - RECT rect; - 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); - - if(fullscreen_changed) - { - hide(); - SetWindowLong(priv->window, GWL_EXSTYLE, exstyle); - SetWindowLong(priv->window, GWL_STYLE, style); - show(); - } - - 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; - - bool was_visible = visible; - if(fullscreen_changed) - { - if(was_visible) - { - hide(); - - // Wait for the window to be unmapped. This makes window managers happy. - XEvent ev; - int ev_type = UnmapNotify; - XPeekIfEvent(dpy, &ev, match_event_type, reinterpret_cast(&ev_type)); - } - - XSetWindowAttributes attr; - attr.override_redirect = options.fullscreen; - XChangeWindowAttributes(dpy, priv->window, CWOverrideRedirect, &attr); - } - - 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); - - if(fullscreen_changed) - { - if(was_visible) - show(); - } -#endif + platform_reconfigure(fullscreen_changed); if(visible) { @@ -291,155 +79,26 @@ void Window::set_keyboard_autorepeat(bool r) kbd_autorepeat = r; } -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::warp_pointer(int x, int y) -{ -#ifndef WIN32 - XWarpPointer(display.get_private().display, None, priv->window, 0, 0, 0, 0, x, y); -#else - (void)x; - (void)y; -#endif -} - void Window::show() { -#ifdef WIN32 - ShowWindow(priv->window, SW_SHOWNORMAL); -#else - XMapRaised(display.get_private().display, priv->window); -#endif + platform_show(); visible = true; if(options.fullscreen) { display.set_mode(VideoMode(options.width, options.height)); -#ifndef WIN32 - XWarpPointer(display.get_private().display, None, priv->window, 0, 0, 0, 0, options.width/2, options.height/2); -#endif + warp_pointer(options.width/2, options.height/2); } } void Window::hide() { -#ifdef WIN32 - ShowWindow(priv->window, SW_HIDE); -#else - XUnmapWindow(display.get_private().display, priv->window); -#endif + platform_hide(); visible = false; if(options.fullscreen) display.restore_mode(); } -bool Window::event(const Event &evnt) -{ -#ifdef WIN32 - switch(evnt.msg) - { - case WM_KEYDOWN: - case WM_KEYUP: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_MOUSEWHEEL: - case WM_MOUSEMOVE: - signal_input_event.emit(evnt); - break; - case WM_SIZE: - options.width = LOWORD(evnt.lparam); - options.height = HIWORD(evnt.lparam); - 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: - case ButtonRelease: - case MotionNotify: - case KeyPress: - case KeyRelease: - signal_input_event.emit(evnt); - break; - case ConfigureNotify: - if((ev.xconfigure.width==static_cast(options.width) && ev.xconfigure.height==static_cast(options.height)) == resizing) - { - options.width = ev.xconfigure.width; - options.height = ev.xconfigure.height; - resizing = false; - signal_resize.emit(options.width, options.height); - } -#ifdef WITH_XF86VIDMODE - if(options.fullscreen) - { - ::Display *dpy = display.get_private().display; - int screen = DefaultScreen(dpy); - XF86VidModeSetViewPort(dpy, screen, ev.xconfigure.x, ev.xconfigure.y); - } -#endif - break; - case ClientMessage: - if(ev.xclient.data.l[0]==static_cast(priv->wm_delete_window)) - signal_close.emit(); - break; - case EnterNotify: - if(options.fullscreen) - XSetInputFocus(display.get_private().display, priv->window, RevertToParent, CurrentTime); - break; - case MapNotify: - if(options.fullscreen) - XGrabPointer(display.get_private().display, priv->window, true, None, GrabModeAsync, GrabModeAsync, priv->window, None, CurrentTime); - break; - default: - return false; - } -#endif - return true; -} - } // namespace Graphics } // namespace Msp diff --git a/source/graphics/window.h b/source/graphics/window.h index 942fc8c..55751b3 100644 --- a/source/graphics/window.h +++ b/source/graphics/window.h @@ -46,11 +46,16 @@ public: Window(Display &, const WindowOptions &); private: void init(); + void platform_init(); + void platform_cleanup(); public: virtual ~Window(); void set_title(const std::string &); void reconfigure(const WindowOptions &); +private: + void platform_reconfigure(bool); +public: void set_keyboard_autorepeat(bool); bool get_keyboard_autorepeat() const { return kbd_autorepeat; } void show_cursor(bool); @@ -64,7 +69,11 @@ public: void show(); void hide(); +private: + void platform_show(); + void platform_hide(); +public: bool event(const Event &evnt); }; diff --git a/source/graphics/window_priv.h b/source/graphics/window_priv.h deleted file mode 100644 index f5e9be8..0000000 --- a/source/graphics/window_priv.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef MSP_GRAPHICS_WINDOW_PRIV_H_ -#define MSP_GRAPHICS_WINDOW_PRIV_H_ - -#ifdef WIN32 -#include -#else -#include -#endif - -namespace Msp { -namespace Graphics { - -#ifdef WIN32 -typedef HWND WindowHandle; -#else -typedef ::Window WindowHandle; -#endif - -struct Window::Private -{ - WindowHandle window; -#ifndef WIN32 - Atom wm_delete_window; - Cursor invisible_cursor; -#endif -}; - -struct Window::Event -{ -#ifdef WIN32 - UINT msg; - WPARAM wparam; - LPARAM lparam; -#else - XEvent xevent; -#endif -}; - -} // namespace Graphics -} // namespace Msp - -#endif diff --git a/source/graphics/window_private.h b/source/graphics/window_private.h new file mode 100644 index 0000000..2257b1c --- /dev/null +++ b/source/graphics/window_private.h @@ -0,0 +1,22 @@ +#ifndef MSP_GRAPHICS_WINDOW_PRIVATE_H_ +#define MSP_GRAPHICS_WINDOW_PRIVATE_H_ + +#include "window.h" +#include "window_platform.h" + +namespace Msp { +namespace Graphics { + +struct Window::Private: PlatformWindowPrivate +{ + WindowHandle window; +}; + +struct Window::Event: PlatformEvent +{ +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/windows/display.cpp b/source/graphics/windows/display.cpp new file mode 100644 index 0000000..5cc04c1 --- /dev/null +++ b/source/graphics/windows/display.cpp @@ -0,0 +1,69 @@ +#include +#include "display.h" + +using namespace std; + +namespace Msp { +namespace Graphics { + +Display::Display(const string &) +{ + for(unsigned i=0;; ++i) + { + DEVMODE info; + if(!EnumDisplaySettings(0, i, &info)) + break; + + VideoMode mode(info.dmPelsWidth, info.dmPelsHeight); + mode.rate = info.dmDisplayFrequency; + modes.push_back(mode); + } + + DEVMODE info; + if(EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &info)) + { + orig_mode = VideoMode(info.dmPelsWidth, info.dmPelsHeight); + orig_mode.rate = info.dmDisplayFrequency; + } +} + +Display::~Display() +{ +} + +void Display::set_mode(const VideoMode &mode) +{ + DEVMODE info; + info.dmSize = sizeof(DEVMODE); + info.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT; + info.dmPelsWidth = mode.width; + info.dmPelsHeight = mode.height; + if(mode.rate) + { + info.dmFields |= DM_DISPLAYFREQUENCY; + info.dmDisplayFrequency = mode.rate; + } + + LONG ret = ChangeDisplaySettings(&info, CDS_FULLSCREEN); + if(ret!=DISP_CHANGE_SUCCESSFUL) + throw unsupported_video_mode(mode); +} + +bool Display::process_events() +{ + MSG msg; + if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) + { + DispatchMessage(&msg); + return true; + } + else + return false; +} + +void Display::check_error() +{ +} + +} // namespace Msp +} // namespace Graphics diff --git a/source/graphics/windows/display_platform.h b/source/graphics/windows/display_platform.h new file mode 100644 index 0000000..dac4dd7 --- /dev/null +++ b/source/graphics/windows/display_platform.h @@ -0,0 +1,12 @@ +#ifndef MSP_GRAPHICS_DISPLAY_PLATFORM_H_ +#define MSP_GRAPHICS_DISPLAY_PLATFORM_H_ + +namespace Msp { +namespace Graphics { + +typedef void *DisplayHandle; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/windows/drawcontext.cpp b/source/graphics/windows/drawcontext.cpp new file mode 100644 index 0000000..b887615 --- /dev/null +++ b/source/graphics/windows/drawcontext.cpp @@ -0,0 +1,34 @@ +#include +#include "drawcontext.h" +#include "window.h" + +using namespace std; + +namespace Msp { +namespace Graphics { + +DrawContext::DrawContext(Window &w): + display(w.get_display()), + window(w) +{ + throw runtime_error("no DrawContext support on windows"); +} + +DrawContext::~DrawContext() +{ } + +unsigned DrawContext::get_depth() const +{ + return 0; +} + +unsigned char *DrawContext::get_data() +{ + return 0; +} + +void DrawContext::update() +{ } + +} // namespace Graphics +} // namespace Msp diff --git a/source/graphics/windows/window.cpp b/source/graphics/windows/window.cpp new file mode 100644 index 0000000..c5cdc62 --- /dev/null +++ b/source/graphics/windows/window.cpp @@ -0,0 +1,174 @@ +#include +#include +#include +#include "window.h" +#include "window_private.h" + +using namespace std; + +namespace { + +LRESULT CALLBACK wndproc_(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + if(msg==WM_CREATE) + { + CREATESTRUCT *cs = reinterpret_cast(lparam); + SetWindowLong(hwnd, 0, reinterpret_cast(cs->lpCreateParams)); + } + else + { + Msp::Graphics::Window *wnd = reinterpret_cast(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); +} + +} + +namespace Msp { +namespace Graphics { + +void Window::platform_init() +{ + static bool wndclass_created = false; + + if(!wndclass_created) + { + WNDCLASSEX wndcl; + + wndcl.cbSize = sizeof(WNDCLASSEX); + wndcl.style = 0; + wndcl.lpfnWndProc = &wndproc_; + wndcl.cbClsExtra = 0; + wndcl.cbWndExtra = sizeof(Window *); + wndcl.hInstance = reinterpret_cast(Application::get_data()); + wndcl.hIcon = 0; + wndcl.hCursor = LoadCursor(0, IDC_ARROW); + wndcl.hbrBackground = 0; + wndcl.lpszMenuName = 0; + wndcl.lpszClassName = "mspgui"; + wndcl.hIconSm = 0; + + if(!RegisterClassEx(&wndcl)) + throw system_error("RegisterClassEx"); + + wndclass_created = true; + } + + RECT rect; + 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); + + priv->window = CreateWindowEx(exstyle, + "mspgui", + "Window", + style, + CW_USEDEFAULT, CW_USEDEFAULT, + rect.right-rect.left, rect.bottom-rect.top, + 0, + 0, + reinterpret_cast(Application::get_data()), + this); + if(!priv->window) + throw system_error("CreateWindowEx"); +} + +void Window::platform_cleanup() +{ + if(priv->window) + CloseWindow(priv->window); +} + +void Window::set_title(const string &title) +{ + SetWindowText(priv->window, title.c_str()); +} + +void Window::platform_reconfigure(bool fullscreen_changed) +{ + RECT rect; + 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); + + if(fullscreen_changed) + { + hide(); + SetWindowLong(priv->window, GWL_EXSTYLE, exstyle); + SetWindowLong(priv->window, GWL_STYLE, style); + show(); + } + + 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); +} + +void Window::show_cursor(bool s) +{ + ShowCursor(s); +} + +void Window::warp_pointer(int, int) +{ +} + +void Window::platform_show() +{ + ShowWindow(priv->window, SW_SHOWNORMAL); +} + +void Window::platform_hide() +{ + ShowWindow(priv->window, SW_HIDE); +} + +bool Window::event(const Event &evnt) +{ + switch(evnt.msg) + { + case WM_KEYDOWN: + case WM_KEYUP: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MOUSEWHEEL: + case WM_MOUSEMOVE: + signal_input_event.emit(evnt); + break; + case WM_SIZE: + options.width = LOWORD(evnt.lparam); + options.height = HIWORD(evnt.lparam); + signal_resize.emit(options.width, options.height); + break; + case WM_CLOSE: + signal_close.emit(); + break; + default: + return false; + } + + return true; +} + +} // namespace Graphics +} // namespace Msp diff --git a/source/graphics/windows/window_platform.h b/source/graphics/windows/window_platform.h new file mode 100644 index 0000000..1ff5cf3 --- /dev/null +++ b/source/graphics/windows/window_platform.h @@ -0,0 +1,25 @@ +#ifndef MSP_GRAPHICS_WINDOW_PLATFORM_H_ +#define MSP_GRAPHICS_WINDOW_PLATFORM_H_ + +#include + +namespace Msp { +namespace Graphics { + +typedef HWND WindowHandle; + +struct PlatformWindowPrivate +{ +}; + +struct PlatformEvent +{ + UINT msg; + WPARAM wparam; + LPARAM lparam; +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/x11/display.cpp b/source/graphics/x11/display.cpp new file mode 100644 index 0000000..0e89794 --- /dev/null +++ b/source/graphics/x11/display.cpp @@ -0,0 +1,174 @@ +#include +#include +#ifdef WITH_XF86VIDMODE +#include +#endif +#include +#include +#include "display.h" +#include "display_private.h" + +using namespace std; + +namespace { + +bool error_flag = false; +std::string error_msg; + +int x_error_handler(Display *display, XErrorEvent *event) +{ + char err[128]; + XGetErrorText(display, event->error_code, err, sizeof(err)); + + string request_code = Msp::lexical_cast(event->request_code); + char req[128]; + XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), request_code.c_str(), req, sizeof(req)); + + string msg = Msp::format("Request %s failed with %s [%08X]", req, err, event->resourceid); + if(error_flag) + cerr<<"Discarding error: "<display = XOpenDisplay(0); + else + priv->display = XOpenDisplay(disp_name.c_str()); + if(!priv->display) + throw runtime_error("XOpenDisplay"); + + XSetErrorHandler(x_error_handler); + +#ifdef WITH_XF86VIDMODE + int screen = DefaultScreen(priv->display); + + int nmodes; + XF86VidModeModeInfo **infos; + XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos); + for(int i=0; idisplay, screen, &dotclock, &modeline); + orig_mode = VideoMode(modeline.hdisplay, modeline.vdisplay); + if(modeline.htotal && modeline.vtotal) + orig_mode.rate = dotclock/(modeline.htotal*modeline.vtotal); +#endif +} + +Display::~Display() +{ + XCloseDisplay(priv->display); + delete priv; +} + +void Display::set_mode(const VideoMode &mode) +{ +#ifdef WITH_XF86VIDMODE + int screen = DefaultScreen(priv->display); + + int nmodes; + XF86VidModeModeInfo **infos; + XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos); + for(int i=0; idisplay, screen, &info); + XF86VidModeSetViewPort(priv->display, screen, 0, 0); + return; + } + } + + throw unsupported_video_mode(mode); +#else + (void)mode; + throw runtime_error("no xf86vidmode support"); +#endif +} + +bool Display::process_events() +{ + int pending = XPending(priv->display); + if(pending==0) + return false; + + for(; pending--;) + { + Window::Event event; + XNextEvent(priv->display, &event.xevent); + + check_error(); + + map::iterator j = priv->windows.find(event.xevent.xany.window); + if(j!=priv->windows.end()) + { + /* Filter keyboard autorepeat. If this packet is a KeyRelease and + the next one is a KeyPress with the exact same parameters, they + indicate autorepeat and must be dropped. */ + if(event.xevent.type==KeyRelease && !j->second->get_keyboard_autorepeat() && pending>0) + { + XKeyEvent &kev = event.xevent.xkey; + XEvent ev2; + XPeekEvent(priv->display, &ev2); + if(ev2.type==KeyPress) + { + XKeyEvent &kev2 = ev2.xkey; + if(kev2.window==kev.window && kev2.time==kev.time && kev2.keycode==kev.keycode) + { + XNextEvent(priv->display, &ev2); + --pending; + continue; + } + } + } + + j->second->event(event); + } + } + + return true; +} + +void Display::check_error() +{ + if(error_flag) + { + error_flag = false; + throw runtime_error(error_msg); + } +} + +} // namespace Msp +} // namespace Graphics diff --git a/source/graphics/x11/display_platform.h b/source/graphics/x11/display_platform.h new file mode 100644 index 0000000..1ebe616 --- /dev/null +++ b/source/graphics/x11/display_platform.h @@ -0,0 +1,14 @@ +#ifndef MSP_GRAPHICS_DISPLAY_PLATFORM_H_ +#define MSP_GRAPHICS_DISPLAY_PLATFORM_H_ + +#include + +namespace Msp { +namespace Graphics { + +typedef ::Display *DisplayHandle; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/x11/drawcontext.cpp b/source/graphics/x11/drawcontext.cpp new file mode 100644 index 0000000..e40390f --- /dev/null +++ b/source/graphics/x11/drawcontext.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include +#include "display_private.h" +#include "drawcontext.h" +#include "window_private.h" + +using namespace std; + +namespace Msp { +namespace Graphics { + +struct DrawContext::Private +{ + XImage *image; + bool use_shm; + XShmSegmentInfo shminfo; +}; + +DrawContext::DrawContext(Window &w): + display(w.get_display()), + window(w), + priv(new Private) +{ + DisplayHandle dpy = display.get_private().display; + + priv->use_shm = XShmQueryExtension(dpy); + + XWindowAttributes wa; + XGetWindowAttributes(dpy, window.get_private().window, &wa); + + if(priv->use_shm) + { + priv->image = XShmCreateImage(dpy, wa.visual, wa.depth, ZPixmap, 0, &priv->shminfo, wa.width, wa.height); + if(!priv->image) + throw runtime_error("XShmCreateImage"); + + priv->shminfo.shmid = shmget(IPC_PRIVATE, priv->image->bytes_per_line*priv->image->height, IPC_CREAT|0666); + priv->shminfo.shmaddr=priv->image->data = reinterpret_cast(shmat(priv->shminfo.shmid, 0, 0)); + priv->shminfo.readOnly = false; + + XShmAttach(dpy, &priv->shminfo); + + XSync(dpy, false); + display.check_error(); + } + else + { + priv->image = XCreateImage(dpy, wa.visual, wa.depth, ZPixmap, 0, 0, wa.width, wa.height, 8, 0); + if(!priv->image) + throw runtime_error("XCreateImage"); + priv->image->data = new char[priv->image->bytes_per_line*priv->image->height]; + } +} + +DrawContext::~DrawContext() +{ + if(priv->use_shm) + { + XShmDetach(display.get_private().display, &priv->shminfo); + shmdt(priv->shminfo.shmaddr); + shmctl(priv->shminfo.shmid, IPC_RMID, 0); + } + + XDestroyImage(priv->image); + + delete priv; +} + +unsigned DrawContext::get_depth() const +{ + return priv->image->bits_per_pixel; +} + +unsigned char *DrawContext::get_data() +{ + return reinterpret_cast(priv->image->data); +} + +void DrawContext::update() +{ + DisplayHandle dpy = display.get_private().display; + WindowHandle wnd = window.get_private().window; + + GC gc = XCreateGC(dpy, wnd, 0, 0); + + if(priv->use_shm) + XShmPutImage(dpy, wnd, gc, priv->image, 0, 0, 0, 0, priv->image->width, priv->image->height, false); + else + XPutImage(dpy, wnd, gc, priv->image, 0, 0, 0, 0, priv->image->width, priv->image->height); + + XFreeGC(dpy, gc); +} + +} // namespace Graphics +} // namespace Msp diff --git a/source/graphics/x11/window.cpp b/source/graphics/x11/window.cpp new file mode 100644 index 0000000..577f1d4 --- /dev/null +++ b/source/graphics/x11/window.cpp @@ -0,0 +1,216 @@ +#include +#include +#include +#ifdef WITH_XF86VIDMODE +#include +#endif +#include +#include "display_private.h" +#include "window.h" +#include "window_private.h" + +using namespace std; + +namespace { + +Bool match_event_type(Display *, XEvent *event, XPointer arg) +{ + return event->type==*reinterpret_cast(arg); +} + +} + +namespace Msp { +namespace Graphics { + +void Window::platform_init() +{ + DisplayHandle dpy = display.get_private().display; + + 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; + + priv->window = XCreateWindow(dpy, + DefaultRootWindow(dpy), + 0, 0, + options.width, options.height, + 0, + CopyFromParent, + InputOutput, + CopyFromParent, + CWOverrideRedirect|CWEventMask, &attr); + + 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); + } +} + +void Window::platform_cleanup() +{ + if(priv->window) + XDestroyWindow(display.get_private().display, priv->window); + + if(priv->invisible_cursor) + XFreeCursor(display.get_private().display, priv->invisible_cursor); +} + +void Window::set_title(const string &title) +{ + vector 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_private().display, priv->window, &prop); + display.check_error(); +} + +void Window::platform_reconfigure(bool fullscreen_changed) +{ + DisplayHandle dpy = display.get_private().display; + + bool was_visible = visible; + if(fullscreen_changed) + { + if(was_visible) + { + hide(); + + // Wait for the window to be unmapped. This makes window managers happy. + XEvent ev; + int ev_type = UnmapNotify; + XPeekIfEvent(dpy, &ev, match_event_type, reinterpret_cast(&ev_type)); + } + + XSetWindowAttributes attr; + attr.override_redirect = options.fullscreen; + XChangeWindowAttributes(dpy, priv->window, CWOverrideRedirect, &attr); + } + + 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); + + if(fullscreen_changed && was_visible) + show(); +} + +void Window::show_cursor(bool s) +{ + DisplayHandle 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); + } +} + +void Window::warp_pointer(int x, int y) +{ + XWarpPointer(display.get_private().display, None, priv->window, 0, 0, 0, 0, x, y); +} + +void Window::platform_show() +{ + XMapRaised(display.get_private().display, priv->window); +} + +void Window::platform_hide() +{ + XUnmapWindow(display.get_private().display, priv->window); +} + +bool Window::event(const Event &evnt) +{ + const XEvent &ev = evnt.xevent; + switch(ev.type) + { + case ButtonPress: + case ButtonRelease: + case MotionNotify: + case KeyPress: + case KeyRelease: + signal_input_event.emit(evnt); + break; + case ConfigureNotify: + if((ev.xconfigure.width==static_cast(options.width) && ev.xconfigure.height==static_cast(options.height)) == resizing) + { + options.width = ev.xconfigure.width; + options.height = ev.xconfigure.height; + resizing = false; + signal_resize.emit(options.width, options.height); + } +#ifdef WITH_XF86VIDMODE + if(options.fullscreen) + { + DisplayHandle dpy = display.get_private().display; + int screen = DefaultScreen(dpy); + XF86VidModeSetViewPort(dpy, screen, ev.xconfigure.x, ev.xconfigure.y); + } +#endif + break; + case ClientMessage: + if(ev.xclient.data.l[0]==static_cast(priv->wm_delete_window)) + signal_close.emit(); + break; + case EnterNotify: + if(options.fullscreen) + XSetInputFocus(display.get_private().display, priv->window, RevertToParent, CurrentTime); + break; + case MapNotify: + if(options.fullscreen) + XGrabPointer(display.get_private().display, priv->window, true, None, GrabModeAsync, GrabModeAsync, priv->window, None, CurrentTime); + break; + default: + return false; + } + + return true; +} + +} // namespace Graphics +} // namespace Msp diff --git a/source/graphics/x11/window_platform.h b/source/graphics/x11/window_platform.h new file mode 100644 index 0000000..3c81a6e --- /dev/null +++ b/source/graphics/x11/window_platform.h @@ -0,0 +1,25 @@ +#ifndef MSP_GRAPHICS_WINDOW_PLATFORM_H_ +#define MSP_GRAPHICS_WINDOW_PLATFORM_H_ + +#include + +namespace Msp { +namespace Graphics { + +typedef ::Window WindowHandle; + +struct PlatformWindowPrivate +{ + Atom wm_delete_window; + Cursor invisible_cursor; +}; + +struct PlatformEvent +{ + XEvent xevent; +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/input/keyboard.cpp b/source/input/keyboard.cpp index ab2f9ea..159e5ee 100644 --- a/source/input/keyboard.cpp +++ b/source/input/keyboard.cpp @@ -1,18 +1,7 @@ -#ifdef WIN32 -#include -#else -#include -#include -#endif -#include #include -#include -#include #include "keyboard.h" #include "keys.h" -#define MAPVK_VK_TO_VSC 0 - namespace Msp { namespace Input { @@ -26,59 +15,5 @@ Keyboard::Keyboard(Graphics::Window &w): window.signal_input_event.connect(sigc::mem_fun(this, &Keyboard::input_event)); } -std::string Keyboard::get_button_name(unsigned btn) const -{ - if(btn==0) - return "None"; -#ifndef WIN32 - const char *str = XKeysymToString(key_to_sys(btn)); - if(!str) - return Device::get_button_name(btn); - return str; -#else - char buf[128]; - unsigned scan = MapVirtualKey(key_to_sys(btn), MAPVK_VK_TO_VSC); - if(!GetKeyNameText(scan<<16, buf, sizeof(buf))) - return Device::get_button_name(btn); - return buf; -#endif -} - -void Keyboard::input_event(const Graphics::Window::Event &event) -{ -#ifdef WIN32 - switch(event.msg) - { - case WM_KEYDOWN: - case WM_KEYUP: - set_button_state(key_from_sys(event.wparam), event.msg==WM_KEYDOWN, true); - break; - case WM_CHAR: - signal_character.emit(event.wparam); - break; - } -#else - switch(event.xevent.type) - { - case KeyPress: - case KeyRelease: - { - KeySym keysym = XLookupKeysym(const_cast(&event.xevent.xkey), 0); - if(keysym!=NoSymbol) - if(unsigned key = key_from_sys(keysym)) - set_button_state(key, event.xevent.type==KeyPress, true); - if(event.xevent.type==KeyPress) - { - char ch; - if(XLookupString(const_cast(&event.xevent.xkey), &ch, 1, 0, 0)) - // XLookupString always returns Latin-1 - signal_character.emit(static_cast(ch)); - } - } - break; - } -#endif -} - } // namespace Input } // namespace Msp diff --git a/source/input/keys.cpp b/source/input/keys.cpp index 38d9480..abefeaa 100644 --- a/source/input/keys.cpp +++ b/source/input/keys.cpp @@ -1,107 +1,14 @@ #include #include -#ifdef WIN32 -#include -#else -#include -#include -#endif #include "keys.h" using namespace std; -namespace { - -unsigned keymap[Msp::Input::N_KEYS_]= -{ -#ifndef WIN32 - 0, 0, 0, 0, 0, 0, 0, 0, - XK_BackSpace, XK_Tab, XK_Return, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, XK_Escape, 0, 0, 0, 0, - - XK_space, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, XK_plus, XK_comma, XK_minus, XK_period, XK_slash, - XK_0, XK_1, XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, - XK_8, XK_9, 0, XK_semicolon, XK_less, XK_equal, 0, 0, - - 0, XK_a, XK_b, XK_c, XK_d, XK_e, XK_f, XK_g, - XK_h, XK_i, XK_j, XK_k, XK_l, XK_m, XK_n, XK_o, - XK_p, XK_q, XK_r, XK_s, XK_t, XK_u, XK_v, XK_w, - XK_x, XK_y, XK_z, XK_bracketleft, XK_backslash, XK_bracketright, 0, 0, - - XK_grave, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - - XK_adiaeresis, XK_odiaeresis, XK_udiaeresis, XK_aring, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - - XK_Left, XK_Right, XK_Up, XK_Down, 0, 0, 0, 0, - XK_Home, XK_End, XK_Page_Up, XK_Page_Down, XK_Insert, XK_Delete, 0, 0, - XK_F1, XK_F2, XK_F3, XK_F4, XK_F5, XK_F6, XK_F7, XK_F8, - XK_F9, XK_F10, XK_F11, XK_F12, 0, 0, 0, 0, - - XK_Shift_L, XK_Shift_R, XK_Control_L, XK_Control_R, XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R, - XK_Caps_Lock, XK_Scroll_Lock, XK_Num_Lock, 0, 0, 0, 0, 0, - XK_KP_0, XK_KP_1, XK_KP_2, XK_KP_3, XK_KP_4, XK_KP_5, XK_KP_6, XK_KP_7, - XK_KP_8, XK_KP_9, XK_KP_Add, XK_KP_Subtract, XK_KP_Multiply, XK_KP_Divide, XK_KP_Separator, 0, - - XK_Pause, XK_Print, XK_Menu, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, -#else - 0, 0, 0, 0, 0, 0, 0, 0, - VK_BACK, VK_TAB, VK_RETURN, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0, - - VK_SPACE, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 0, 0, 0, 0, 0, 0, - - 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - - VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, 0, 0, 0, 0, - VK_HOME, VK_END, VK_PRIOR, VK_NEXT, VK_INSERT, VK_DELETE, 0, 0, - 0, VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, - VK_F8, VK_F9, VK_F10, VK_F11, VK_F12, 0, 0, 0, - - VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, VK_RCONTROL, 0, 0, VK_LWIN, VK_RWIN, - VK_CAPITAL, VK_SCROLL, VK_NUMLOCK, 0, 0, 0, 0, 0, - VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, - VK_NUMPAD8, VK_NUMPAD9, VK_ADD, VK_SUBTRACT, VK_MULTIPLY, VK_DIVIDE, VK_SEPARATOR, 0, - - VK_PAUSE, VK_SNAPSHOT, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, -#endif -}; - -} - namespace Msp { namespace Input { +extern unsigned sys_keymap[]; + unsigned key_from_sys(unsigned code) { static bool init_done = false; @@ -110,8 +17,8 @@ unsigned key_from_sys(unsigned code) if(!init_done) { for(unsigned i=0; i=N_KEYS_) throw invalid_argument("key_to_sys"); - return keymap[key]; + return sys_keymap[key]; } } // namespace Input diff --git a/source/input/keys.h b/source/input/keys.h index 3915aa2..c5822f8 100644 --- a/source/input/keys.h +++ b/source/input/keys.h @@ -139,9 +139,6 @@ enum N_KEYS_ = 0x100 }; -extern unsigned key_from_sys(unsigned); -extern unsigned key_to_sys(unsigned); - } // namespace Input } // namespace Msp diff --git a/source/input/keys_private.h b/source/input/keys_private.h new file mode 100644 index 0000000..715b970 --- /dev/null +++ b/source/input/keys_private.h @@ -0,0 +1,13 @@ +#ifndef MSP_INPUT_KEYS_PRIVATE_H_ +#define MSP_INPUT_KEYS_PRIVATE_H_ + +namespace Msp { +namespace Input { + +unsigned key_from_sys(unsigned); +unsigned key_to_sys(unsigned); + +} // namespace Input +} // namespace Msp + +#endif diff --git a/source/input/mouse.cpp b/source/input/mouse.cpp index 2337f70..1b41e36 100644 --- a/source/input/mouse.cpp +++ b/source/input/mouse.cpp @@ -1,8 +1,4 @@ -#ifdef WIN32 -#include -#endif #include -#include #include "mouse.h" namespace Msp { @@ -51,49 +47,5 @@ std::string Mouse::get_axis_name(unsigned axis) const }; } -void Mouse::input_event(const Graphics::Window::Event &event) -{ -#ifdef WIN32 - switch(event.msg) - { - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - set_button_state(1, event.msg==WM_LBUTTONDOWN, true); - break; - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - set_button_state(2, event.msg==WM_MBUTTONDOWN, true); - break; - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - set_button_state(3, event.msg==WM_RBUTTONDOWN, true); - break; - case WM_MOUSEWHEEL: - { - unsigned btn = (HIWORD(event.wparam)&0x8000) ? 5 : 4; - set_button_state(btn, true, true); - set_button_state(btn, false, true); - } - break; - case WM_MOUSEMOVE: - set_axis_value(0, GET_X_LPARAM(event.lparam)*2.0/window.get_width()-1.0, true); - set_axis_value(1, 1.0-GET_Y_LPARAM(event.lparam)*2.0/window.get_height(), true); - break; - } -#else - switch(event.xevent.type) - { - case ButtonPress: - case ButtonRelease: - set_button_state(event.xevent.xbutton.button, event.xevent.type==ButtonPress, true); - break; - case MotionNotify: - set_axis_value(0, event.xevent.xmotion.x*2.0/window.get_width()-1.0, true); - set_axis_value(1, 1.0-event.xevent.xmotion.y*2.0/window.get_height(), true); - break; - } -#endif -} - } // namespace Input } // namespace Msp diff --git a/source/input/windows/keyboard.cpp b/source/input/windows/keyboard.cpp new file mode 100644 index 0000000..24b019e --- /dev/null +++ b/source/input/windows/keyboard.cpp @@ -0,0 +1,38 @@ +#include +#include +#include "keyboard.h" +#include "keys_private.h" + +#define MAPVK_VK_TO_VSC 0 + +namespace Msp { +namespace Input { + +std::string Keyboard::get_button_name(unsigned btn) const +{ + if(btn==0) + return "None"; + + char buf[128]; + unsigned scan = MapVirtualKey(key_to_sys(btn), MAPVK_VK_TO_VSC); + if(!GetKeyNameText(scan<<16, buf, sizeof(buf))) + return Device::get_button_name(btn); + return buf; +} + +void Keyboard::input_event(const Graphics::Window::Event &event) +{ + switch(event.msg) + { + case WM_KEYDOWN: + case WM_KEYUP: + set_button_state(key_from_sys(event.wparam), event.msg==WM_KEYDOWN, true); + break; + case WM_CHAR: + signal_character.emit(event.wparam); + break; + } +} + +} // namespace Input +} // namespace Msp diff --git a/source/input/windows/keys.cpp b/source/input/windows/keys.cpp new file mode 100644 index 0000000..6501196 --- /dev/null +++ b/source/input/windows/keys.cpp @@ -0,0 +1,51 @@ +#include +#include "keys.h" + +namespace Msp { +namespace Input { + +unsigned sys_keymap[N_KEYS_]= +{ + 0, 0, 0, 0, 0, 0, 0, 0, + VK_BACK, VK_TAB, VK_RETURN, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0, + + VK_SPACE, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 0, 0, 0, 0, 0, 0, + + 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, 0, 0, 0, 0, + VK_HOME, VK_END, VK_PRIOR, VK_NEXT, VK_INSERT, VK_DELETE, 0, 0, + 0, VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, + VK_F8, VK_F9, VK_F10, VK_F11, VK_F12, 0, 0, 0, + + VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, VK_RCONTROL, 0, 0, VK_LWIN, VK_RWIN, + VK_CAPITAL, VK_SCROLL, VK_NUMLOCK, 0, 0, 0, 0, 0, + VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, + VK_NUMPAD8, VK_NUMPAD9, VK_ADD, VK_SUBTRACT, VK_MULTIPLY, VK_DIVIDE, VK_SEPARATOR, 0, + + VK_PAUSE, VK_SNAPSHOT, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +} // namespace Input +} // namespace Msp diff --git a/source/input/windows/mouse.cpp b/source/input/windows/mouse.cpp new file mode 100644 index 0000000..77f8488 --- /dev/null +++ b/source/input/windows/mouse.cpp @@ -0,0 +1,39 @@ +#include +#include +#include "mouse.h" + +namespace Msp { +namespace Input { + +void Mouse::input_event(const Graphics::Window::Event &event) +{ + switch(event.msg) + { + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + set_button_state(1, event.msg==WM_LBUTTONDOWN, true); + break; + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + set_button_state(2, event.msg==WM_MBUTTONDOWN, true); + break; + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + set_button_state(3, event.msg==WM_RBUTTONDOWN, true); + break; + case WM_MOUSEWHEEL: + { + unsigned btn = (HIWORD(event.wparam)&0x8000) ? 5 : 4; + set_button_state(btn, true, true); + set_button_state(btn, false, true); + } + break; + case WM_MOUSEMOVE: + set_axis_value(0, GET_X_LPARAM(event.lparam)*2.0/window.get_width()-1.0, true); + set_axis_value(1, 1.0-GET_Y_LPARAM(event.lparam)*2.0/window.get_height(), true); + break; + } +} + +} // namespace Input +} // namepsace Msp diff --git a/source/input/x11/keyboard.cpp b/source/input/x11/keyboard.cpp new file mode 100644 index 0000000..50142c9 --- /dev/null +++ b/source/input/x11/keyboard.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include "keyboard.h" +#include "keys_private.h" + +namespace Msp { +namespace Input { + +std::string Keyboard::get_button_name(unsigned btn) const +{ + if(btn==0) + return "None"; + + const char *str = XKeysymToString(key_to_sys(btn)); + if(!str) + return Device::get_button_name(btn); + return str; +} + +void Keyboard::input_event(const Graphics::Window::Event &event) +{ + switch(event.xevent.type) + { + case KeyPress: + case KeyRelease: + { + KeySym keysym = XLookupKeysym(const_cast(&event.xevent.xkey), 0); + if(keysym!=NoSymbol) + if(unsigned key = key_from_sys(keysym)) + set_button_state(key, event.xevent.type==KeyPress, true); + if(event.xevent.type==KeyPress) + { + char ch; + if(XLookupString(const_cast(&event.xevent.xkey), &ch, 1, 0, 0)) + // XLookupString always returns Latin-1 + signal_character.emit(static_cast(ch)); + } + } + break; + } +} + +} // namespace Input +} // namespace Msp diff --git a/source/input/x11/keys.cpp b/source/input/x11/keys.cpp new file mode 100644 index 0000000..a1bb874 --- /dev/null +++ b/source/input/x11/keys.cpp @@ -0,0 +1,51 @@ +#include +#include "keys.h" + +namespace Msp { +namespace Input { + +unsigned sys_keymap[N_KEYS_]= +{ + 0, 0, 0, 0, 0, 0, 0, 0, + XK_BackSpace, XK_Tab, XK_Return, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, XK_Escape, 0, 0, 0, 0, + + XK_space, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, XK_plus, XK_comma, XK_minus, XK_period, XK_slash, + XK_0, XK_1, XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, + XK_8, XK_9, 0, XK_semicolon, XK_less, XK_equal, 0, 0, + + 0, XK_a, XK_b, XK_c, XK_d, XK_e, XK_f, XK_g, + XK_h, XK_i, XK_j, XK_k, XK_l, XK_m, XK_n, XK_o, + XK_p, XK_q, XK_r, XK_s, XK_t, XK_u, XK_v, XK_w, + XK_x, XK_y, XK_z, XK_bracketleft, XK_backslash, XK_bracketright, 0, 0, + + XK_grave, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + XK_adiaeresis, XK_odiaeresis, XK_udiaeresis, XK_aring, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + XK_Left, XK_Right, XK_Up, XK_Down, 0, 0, 0, 0, + XK_Home, XK_End, XK_Page_Up, XK_Page_Down, XK_Insert, XK_Delete, 0, 0, + XK_F1, XK_F2, XK_F3, XK_F4, XK_F5, XK_F6, XK_F7, XK_F8, + XK_F9, XK_F10, XK_F11, XK_F12, 0, 0, 0, 0, + + XK_Shift_L, XK_Shift_R, XK_Control_L, XK_Control_R, XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R, + XK_Caps_Lock, XK_Scroll_Lock, XK_Num_Lock, 0, 0, 0, 0, 0, + XK_KP_0, XK_KP_1, XK_KP_2, XK_KP_3, XK_KP_4, XK_KP_5, XK_KP_6, XK_KP_7, + XK_KP_8, XK_KP_9, XK_KP_Add, XK_KP_Subtract, XK_KP_Multiply, XK_KP_Divide, XK_KP_Separator, 0, + + XK_Pause, XK_Print, XK_Menu, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +} // namespace Input +} // namespace Msp diff --git a/source/input/x11/mouse.cpp b/source/input/x11/mouse.cpp new file mode 100644 index 0000000..3533d56 --- /dev/null +++ b/source/input/x11/mouse.cpp @@ -0,0 +1,23 @@ +#include +#include "mouse.h" + +namespace Msp { +namespace Input { + +void Mouse::input_event(const Graphics::Window::Event &event) +{ + switch(event.xevent.type) + { + case ButtonPress: + case ButtonRelease: + set_button_state(event.xevent.xbutton.button, event.xevent.type==ButtonPress, true); + break; + case MotionNotify: + set_axis_value(0, event.xevent.xmotion.x*2.0/window.get_width()-1.0, true); + set_axis_value(1, 1.0-event.xevent.xmotion.y*2.0/window.get_height(), true); + break; + } +} + +} // namespace Input +} // namepsace Msp