From: Mikko Rasa Date: Fri, 26 Aug 2011 10:41:12 +0000 (+0300) Subject: Consistently label the graphics part as graphics X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=1023b38fa278cea71fba3d2881e1bfde930cd025;p=libs%2Fgui.git Consistently label the graphics part as graphics Fix multiple inclusion guards in the input headers --- diff --git a/Build b/Build index 79b8cbc..6a78535 100644 --- a/Build +++ b/Build @@ -50,9 +50,9 @@ package "mspgbase" }; }; - headers "msp/gbase" + headers "msp/graphics" { - source "source/gbase"; + source "source/graphics"; install true; }; @@ -64,7 +64,7 @@ package "mspgbase" library "mspgbase" { - source "source/gbase"; + source "source/graphics"; source "source/input"; install true; }; diff --git a/source/gbase/display.cpp b/source/gbase/display.cpp deleted file mode 100644 index da036a1..0000000 --- a/source/gbase/display.cpp +++ /dev/null @@ -1,243 +0,0 @@ -#include -#ifndef WIN32 -#include -#ifdef WITH_XF86VIDMODE -#include -#endif -#endif -#include -#include -#include "display.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(static_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; -} - -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); - } -} - -} // namespace Graphics -} // namespace Msp diff --git a/source/gbase/display.h b/source/gbase/display.h deleted file mode 100644 index 662e73f..0000000 --- a/source/gbase/display.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef MSP_GBASE_DISPLAY_H_ -#define MSP_GBASE_DISPLAY_H_ - -#include -#include -#include -#include - -namespace Msp { -namespace Graphics { - -class Window; - -struct VideoMode -{ - unsigned width; - unsigned height; - unsigned rate; - - VideoMode(): width(0), height(0), rate(0) { } - VideoMode(unsigned w, unsigned h): width(w), height(h), rate(0) { } -}; - - -class unsupported_video_mode: public std::runtime_error -{ -public: - unsupported_video_mode(const VideoMode &); - virtual ~unsupported_video_mode() throw () { } -}; - - -class Display -{ -public: - struct Private; - -private: - std::list modes; - VideoMode orig_mode; - Private *priv; - -public: - Display(const std::string &disp_name = std::string()); - ~Display(); - - const Private &get_private() const { return *priv; } - - void add_window(Window &); - void remove_window(Window &); - - const std::list &get_modes() const { return modes; } - void set_mode(const VideoMode &); - void restore_mode() { set_mode(orig_mode); } - - void tick(); - void check_error(); -}; - -} // namespace Graphics -} // namespace Msp - -#endif diff --git a/source/gbase/display_priv.h b/source/gbase/display_priv.h deleted file mode 100644 index f522812..0000000 --- a/source/gbase/display_priv.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef MSP_GBASE_DISPLAY_PRIV_H_ -#define MSP_GBASE_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/gbase/drawcontext.cpp b/source/gbase/drawcontext.cpp deleted file mode 100644 index 0396567..0000000 --- a/source/gbase/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/gbase/drawcontext.h b/source/gbase/drawcontext.h deleted file mode 100644 index 2717479..0000000 --- a/source/gbase/drawcontext.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef MSP_GBASE_DRAWCONTEXT_H_ -#define MSP_GBASE_DRAWCONTEXT_H_ - -namespace Msp { -namespace Graphics { - -class Display; -class Window; - -class DrawContext -{ -private: - struct Private; - - Display &display; - Window &window; - Private *priv; - -public: - DrawContext(Window &); - ~DrawContext(); - - Window &get_window() const { return window; } - unsigned get_depth() const; - unsigned char *get_data(); - void update(); -}; - -} // namespace Graphics -} // namespace Msp - -#endif diff --git a/source/gbase/eventsource.h b/source/gbase/eventsource.h deleted file mode 100644 index 5f09426..0000000 --- a/source/gbase/eventsource.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MSP_GBASE_EVENTSOURCE_H_ -#define MSP_GBASE_EVENTSOURCE_H_ - -namespace Msp { -namespace Graphics { - -class EventSource -{ -public: - sigc::signal signal_key_press; - sigc::signal signal_key_release; - sigc::signal signal_button_press; - sigc::signal signal_button_release; - sigc::signal signal_pointer_motion; - sigc::signal signal_resize; - -protected: - EventSource() { } -public: - virtual ~EventSource() { } - - virtual unsigned get_width() const = 0; - virtual unsigned get_height() const = 0; -}; - -} // namespace Graphics -} // namespace Msp - -#endif diff --git a/source/gbase/glcontext.cpp b/source/gbase/glcontext.cpp deleted file mode 100644 index b713830..0000000 --- a/source/gbase/glcontext.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include -#ifdef WIN32 -#include -#endif -#ifdef WITH_OPENGL -#include -#ifndef WIN32 -#include -#endif -#endif -#include -#include -#include "display.h" -#include "glcontext.h" -#include "window.h" -#include "display_priv.h" - -namespace Msp { -namespace Graphics { - -GLOptions::GLOptions(): - alpha(false), - stencil(false), - doublebuffer(true), - multisample(0) -{ } - - -unsupported_gl_mode::unsupported_gl_mode(const GLOptions &opts): - runtime_error(format("{ .alpha=%s, .stencil=%s, .doublebuffer=%s, .multisample=%d }", - opts.alpha, opts.stencil, opts.doublebuffer, opts.multisample)) -{ } - - -#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 - - window.signal_resize.connect(sigc::mem_fun(this, &GLContext::window_resized)); -} - -GLContext::~GLContext() -{ -#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 -} - -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; -#endif -} - -} // namespace Graphics -} // namespace Msp diff --git a/source/gbase/glcontext.h b/source/gbase/glcontext.h deleted file mode 100644 index dfac328..0000000 --- a/source/gbase/glcontext.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef MSP_GBASE_GLCONTEXT_H_ -#define MSP_GBASE_GLCONTEXT_H_ - -#include - -namespace Msp { -namespace Graphics { - -class Display; -class Window; - -struct GLOptions -{ - bool alpha; - bool stencil; - bool doublebuffer; - unsigned multisample; - - GLOptions(); -}; - - -class unsupported_gl_mode: public std::runtime_error -{ -public: - unsupported_gl_mode(const GLOptions &); - virtual ~unsupported_gl_mode() throw () { } -}; - - -class GLContext -{ -private: - struct Private; - - Display &display; - Window &window; - Private *priv; - -public: - GLContext(Window &wnd, const GLOptions &opts = GLOptions()); - ~GLContext(); - - void swap_buffers(); -private: - void window_resized(unsigned, unsigned); -}; - -} // namespace Graphics -} // namespace Msp - -#endif diff --git a/source/gbase/image.cpp b/source/gbase/image.cpp deleted file mode 100644 index 3360b72..0000000 --- a/source/gbase/image.cpp +++ /dev/null @@ -1,274 +0,0 @@ -#ifdef WITH_DEVIL -#include -#endif -#ifdef WITH_LIBPNG -#include -#include -#include -#endif -#include "image.h" - -using namespace std; - -namespace Msp { -namespace Graphics { - -struct Image::Private -{ -#ifdef WITH_DEVIL - unsigned id; -#endif -#ifdef WITH_LIBPNG - PixelFormat fmt; - unsigned width; - unsigned height; - char *data; -#endif - - Private(); -}; - -Image::Private::Private() -{ -#ifdef WITH_DEVIL - id = 0; -#endif -#ifdef WITH_LIBPNG - fmt = RGB; - width = 0; - height = 0; - data = 0; -#endif -} - - -namespace { - -#ifdef WITH_LIBPNG -void read(png_struct *png, png_byte *data, png_size_t size) -{ - IO::Base *in = reinterpret_cast(png_get_io_ptr(png)); - in->read(reinterpret_cast(data), size); -} - -void load_png(IO::Base &in, Image::Private &priv) -{ - png_struct *png = 0; - png_info *info = 0; - priv.data = 0; - - try - { - png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); - info = png_create_info_struct(png); - - if(setjmp(png_jmpbuf(png))) - throw bad_image_data("PNG error"); - - png_set_read_fn(png, &in, read); - png_read_info(png, info); - png_uint_32 width; - png_uint_32 height; - int depth; - int color; - png_get_IHDR(png, info, &width, &height, &depth, &color, 0, 0, 0); - priv.width = width; - priv.height = height; - if(depth!=8) - throw unsupported_image_format("depth!=8"); - switch(color) - { - case PNG_COLOR_TYPE_PALETTE: priv.fmt = COLOR_INDEX; break; - case PNG_COLOR_TYPE_GRAY: priv.fmt = LUMINANCE; break; - case PNG_COLOR_TYPE_GRAY_ALPHA: priv.fmt = LUMINANCE_ALPHA; break; - case PNG_COLOR_TYPE_RGB: priv.fmt = RGB; break; - case PNG_COLOR_TYPE_RGB_ALPHA: priv.fmt = RGBA; break; - default: throw unsupported_image_format("unknown color type"); - } - - unsigned nchans = png_get_channels(png, info); - if(nchans==4 && priv.fmt==RGB) - png_set_strip_alpha(png); - - unsigned rowstride = priv.width*nchans; - priv.data = new char[rowstride*priv.height]; - for(unsigned y=0; y(priv.data+rowstride*(priv.height-1-y)), 0); - - png_read_end(png, 0); - png_destroy_read_struct(&png, &info, 0); - } - catch(...) - { - png_destroy_read_struct(&png, &info, 0); - delete[] priv.data; - throw; - } -} -#endif - -#ifdef WITH_DEVIL -void ensure_devil_image(unsigned &id) -{ - static bool init_done = false; - - if(!init_done) - { - ilInit(); - ilEnable(IL_ORIGIN_SET); - ilOriginFunc(IL_ORIGIN_LOWER_LEFT); - init_done = true; - } - - if(!id) - ilGenImages(1, &id); -} -#endif - -} - - -Image::Image(): - priv(new Private) -{ -#if !defined(WITH_DEVIL) && !defined(WITH_LIBPNG) - throw runtime_error("no image support"); -#endif -} - -Image::~Image() -{ -#ifdef WITH_DEVIL - if(priv->id) - ilDeleteImages(1, &priv->id); -#endif -#ifdef WITH_LIBPNG - delete[] priv->data; -#endif - delete priv; -} - -void Image::load_file(const string &fn) -{ -#ifdef WITH_LIBPNG - if(fn.size()>4 && !fn.compare(fn.size()-4, 4, ".png")) - { - IO::BufferedFile file(fn); - load_png(file, *priv); - } - else -#endif - { -#ifdef WITH_DEVIL - ensure_devil_image(priv->id); - ilBindImage(priv->id); - if(!ilLoadImage(const_cast(fn.c_str()))) - throw bad_image_data("IL error"); -#else - throw unsupported_image_format("DevIL needed for non-PNG images"); -#endif - } - (void)fn; -} - -void Image::load_memory(const void *data, unsigned size) -{ -#ifdef WITH_LIBPNG - if(!png_sig_cmp(reinterpret_cast(const_cast(data)), 0, 8)) - { - IO::Memory mem(reinterpret_cast(data), size); - load_png(mem, *priv); - } - else -#endif - { -#ifdef WITH_DEVIL - ensure_devil_image(priv->id); - ilBindImage(priv->id); - if(!ilLoadL(IL_TYPE_UNKNOWN, const_cast(data), size)) - throw bad_image_data("IL error"); -#else - throw unsupported_image_format("DevIL needed for non-PNG images"); -#endif - } - (void)data; - (void)size; -} - -PixelFormat Image::get_format() const -{ -#ifdef WITH_LIBPNG - if(priv->data) - return priv->fmt; -#endif -#ifdef WITH_DEVIL - if(priv->id) - { - ilBindImage(priv->id); - switch(ilGetInteger(IL_IMAGE_FORMAT)) - { - case IL_COLOR_INDEX: return COLOR_INDEX; - case IL_LUMINANCE: return LUMINANCE; - case IL_LUMINANCE_ALPHA: return LUMINANCE_ALPHA; - case IL_RGB: return RGB; - case IL_RGBA: return RGBA; - case IL_BGR: return BGR; - case IL_BGRA: return BGRA; - // XXX bad, should throw when loading - default: throw invalid_argument("unknown pixel format in image"); - } - } -#endif - return RGB; -} - -unsigned Image::get_width() const -{ -#ifdef WITH_LIBPNG - if(priv->data) - return priv->width; -#endif -#ifdef WITH_DEVIL - if(priv->id) - { - ilBindImage(priv->id); - return ilGetInteger(IL_IMAGE_WIDTH); - } -#endif - return 0; -} - -unsigned Image::get_height() const -{ -#ifdef WITH_LIBPNG - if(priv->data) - return priv->height; -#endif -#ifdef WITH_DEVIL - if(priv->id) - { - ilBindImage(priv->id); - return ilGetInteger(IL_IMAGE_HEIGHT); - } -#endif - return 0; -} - -const void *Image::get_data() const -{ -#ifdef WITH_LIBPNG - if(priv->data) - return priv->data; -#endif -#ifdef WITH_DEVIL - if(priv->id) - { - ilBindImage(priv->id); - return ilGetData(); - } -#endif - return 0; -} - -} // namespace Graphics -} // namespace Msp diff --git a/source/gbase/image.h b/source/gbase/image.h deleted file mode 100644 index aec1b0d..0000000 --- a/source/gbase/image.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef MSP_GBASE_IMAGE_H_ -#define MSP_GBASE_IMAGE_H_ - -#include -#include "pixelformat.h" - -namespace Msp { -namespace Graphics { - -class unsupported_image_format: public std::runtime_error -{ -public: - unsupported_image_format(const std::string &w): std::runtime_error(w) { } - virtual ~unsupported_image_format() throw() { } -}; - -class bad_image_data: public std::runtime_error -{ -public: - bad_image_data(const std::string &w): std::runtime_error(w) { } - virtual ~bad_image_data() throw() { } -}; - - -class Image -{ -public: - struct Private; - -private: - Private *priv; - -public: - Image(); - ~Image(); - - void load_file(const std::string &); - void load_memory(const void *, unsigned); - PixelFormat get_format() const; - unsigned get_width() const; - unsigned get_height() const; - const void *get_data() const; -}; - -} // namespace Graphics -} // namespace Msp - -#endif diff --git a/source/gbase/pixelformat.h b/source/gbase/pixelformat.h deleted file mode 100644 index 64b79ee..0000000 --- a/source/gbase/pixelformat.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MSP_GBASE_PIXELFORMAT_H_ -#define MSP_GBASE_PIXELFORMAT_H_ - -namespace Msp { -namespace Graphics { - -enum PixelFormat -{ - COLOR_INDEX, - LUMINANCE, - LUMINANCE_ALPHA, - RGB, - RGBA, - BGR, - BGRA -}; - -} // namespace Graphics -} // namespace Msp - -#endif diff --git a/source/gbase/simplewindow.cpp b/source/gbase/simplewindow.cpp deleted file mode 100644 index c782bab..0000000 --- a/source/gbase/simplewindow.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "simplewindow.h" - -namespace Msp { -namespace Graphics { - -SimpleWindow::SimpleWindow(unsigned w, unsigned h, bool fs): - Window(dpy, w, h, fs) -{ } - -void SimpleWindow::tick() -{ - dpy.tick(); -} - - -SimpleGLWindow::SimpleGLWindow(unsigned w, unsigned h, bool fs): - SimpleWindow(w, h, fs), - gl_ctx(*this) -{ } - -void SimpleGLWindow::swap_buffers() -{ - gl_ctx.swap_buffers(); -} - -} // namespace Graphics -} // namespace Msp diff --git a/source/gbase/simplewindow.h b/source/gbase/simplewindow.h deleted file mode 100644 index 807e69f..0000000 --- a/source/gbase/simplewindow.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef MSP_GBASE_SIMPLEWINDOW_H_ -#define MSP_GBASE_SIMPLEWINDOW_H_ - -#include "display.h" -#include "glcontext.h" -#include "window.h" - -namespace Msp { -namespace Graphics { - -/** -Helper class for SimpleWindow. -*/ -class SimpleWindowBase -{ -protected: - Display dpy; - - SimpleWindowBase() { } -}; - - -/** -A simplified Window that encapsulates a Display. -*/ -class SimpleWindow: public SimpleWindowBase, public Window -{ -public: - SimpleWindow(unsigned, unsigned, bool =false); - - void tick(); -}; - - -/** -A SimpleWindow bundled with a GLContext. -*/ -class SimpleGLWindow: public SimpleWindow -{ -private: - GLContext gl_ctx; - -public: - SimpleGLWindow(unsigned, unsigned, bool =false); - GLContext &get_gl_context() { return gl_ctx; } - void swap_buffers(); -}; - -} // namespace Graphics -} // namespace Msp - -#endif diff --git a/source/gbase/window.cpp b/source/gbase/window.cpp deleted file mode 100644 index 77959de..0000000 --- a/source/gbase/window.cpp +++ /dev/null @@ -1,481 +0,0 @@ -#include -#ifndef WIN32 -#include -#include -#ifdef WITH_XF86VIDMODE -#include -#endif -#else -#include -#endif -#include -#include "display.h" -#include "window.h" -#include "display_priv.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 { - -WindowOptions::WindowOptions(): - width(640), - height(480), - fullscreen(false), - resizable(false) -{ } - - -Window::Window(Display &dpy, unsigned w, unsigned h, bool fs): - display(dpy) -{ - options.width = w; - options.height = h; - options.fullscreen = fs; - - init(); -} - -Window::Window(Display &dpy, const WindowOptions &opts): - display(dpy), - options(opts) -{ - init(); -} - -void Window::init() -{ - visible = false; - kbd_autorepeat = true; - 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 = "mspgbase"; - 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, - "mspgbase", - "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 - - display.add_window(*this); - display.check_error(); -} - -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 - - display.remove_window(*this); - - if(options.fullscreen) - display.restore_mode(); - - 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 - - if(visible) - { - if(options.fullscreen) - display.set_mode(VideoMode(options.width, options.height)); - else if(fullscreen_changed) - display.restore_mode(); - } -} - -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 - 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 - } -} - -void Window::hide() -{ -#ifdef WIN32 - ShowWindow(priv->window, SW_HIDE); -#else - XUnmapWindow(display.get_private().display, priv->window); -#endif - visible = false; - - if(options.fullscreen) - display.restore_mode(); -} - -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_MOUSEWHEEL: - { - unsigned btn = (HIWORD(wp)&0x8000) ? 5 : 4; - signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), btn, 0); - signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), btn, 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: - signal_button_press.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state); - break; - case ButtonRelease: - signal_button_release.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state); - break; - case MotionNotify: - signal_pointer_motion.emit(ev.xmotion.x, ev.xmotion.y); - break; - case KeyPress: - { - char buf[16]; - XLookupString(const_cast(&ev.xkey), buf, sizeof(buf), 0, 0); - // XXX Handle the result according to locale - 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(XKeycodeToKeysym(display.get_private().display, ev.xkey.keycode, 0), ev.xkey.state); - 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/gbase/window.h b/source/gbase/window.h deleted file mode 100644 index a390b8c..0000000 --- a/source/gbase/window.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef MSP_GBASE_WINDOW_H_ -#define MSP_GBASE_WINDOW_H_ - -#include -#include -#include "eventsource.h" - -namespace Msp { -namespace Graphics { - -class Display; - -struct WindowOptions -{ - unsigned width; - unsigned height; - bool fullscreen; - bool resizable; - - WindowOptions(); -}; - -class Window: public EventSource -{ -public: - struct Private; - struct Event; - - sigc::signal signal_close; - -protected: - Display &display; - WindowOptions options; - bool visible; - bool kbd_autorepeat; - bool resizing; - Private *priv; - -public: - Window(Display &, unsigned w, unsigned h, bool fs = false); - Window(Display &, const WindowOptions &); -private: - void init(); -public: - virtual ~Window(); - - void set_title(const std::string &); - void reconfigure(const WindowOptions &); - void set_keyboard_autorepeat(bool); - bool get_keyboard_autorepeat() const { return kbd_autorepeat; } - void show_cursor(bool); - void warp_pointer(int, int); - - Display &get_display() const { return display; } - const WindowOptions &get_options() const { return options; } - virtual unsigned get_width() const { return options.width; } - virtual unsigned get_height() const { return options.height; } - const Private &get_private() const { return *priv; } - - void show(); - void hide(); - - bool event(const Event &evnt); -}; - -} // namespace Graphics -} // namespace Msp - -#endif diff --git a/source/gbase/window_priv.h b/source/gbase/window_priv.h deleted file mode 100644 index 24d71a3..0000000 --- a/source/gbase/window_priv.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef MSP_GBASE_WINDOW_PRIV_H_ -#define MSP_GBASE_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/display.cpp b/source/graphics/display.cpp new file mode 100644 index 0000000..da036a1 --- /dev/null +++ b/source/graphics/display.cpp @@ -0,0 +1,243 @@ +#include +#ifndef WIN32 +#include +#ifdef WITH_XF86VIDMODE +#include +#endif +#endif +#include +#include +#include "display.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(static_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; +} + +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); + } +} + +} // namespace Graphics +} // namespace Msp diff --git a/source/graphics/display.h b/source/graphics/display.h new file mode 100644 index 0000000..b47a550 --- /dev/null +++ b/source/graphics/display.h @@ -0,0 +1,63 @@ +#ifndef MSP_GRAPHICS_DISPLAY_H_ +#define MSP_GRAPHICS_DISPLAY_H_ + +#include +#include +#include +#include + +namespace Msp { +namespace Graphics { + +class Window; + +struct VideoMode +{ + unsigned width; + unsigned height; + unsigned rate; + + VideoMode(): width(0), height(0), rate(0) { } + VideoMode(unsigned w, unsigned h): width(w), height(h), rate(0) { } +}; + + +class unsupported_video_mode: public std::runtime_error +{ +public: + unsupported_video_mode(const VideoMode &); + virtual ~unsupported_video_mode() throw () { } +}; + + +class Display +{ +public: + struct Private; + +private: + std::list modes; + VideoMode orig_mode; + Private *priv; + +public: + Display(const std::string &disp_name = std::string()); + ~Display(); + + const Private &get_private() const { return *priv; } + + void add_window(Window &); + void remove_window(Window &); + + const std::list &get_modes() const { return modes; } + void set_mode(const VideoMode &); + void restore_mode() { set_mode(orig_mode); } + + void tick(); + void check_error(); +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/display_priv.h b/source/graphics/display_priv.h new file mode 100644 index 0000000..5e82f9d --- /dev/null +++ b/source/graphics/display_priv.h @@ -0,0 +1,20 @@ +#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/drawcontext.cpp b/source/graphics/drawcontext.cpp new file mode 100644 index 0000000..0396567 --- /dev/null +++ b/source/graphics/drawcontext.cpp @@ -0,0 +1,121 @@ +#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/drawcontext.h b/source/graphics/drawcontext.h new file mode 100644 index 0000000..957e153 --- /dev/null +++ b/source/graphics/drawcontext.h @@ -0,0 +1,32 @@ +#ifndef MSP_GRAPHICS_DRAWCONTEXT_H_ +#define MSP_GRAPHICS_DRAWCONTEXT_H_ + +namespace Msp { +namespace Graphics { + +class Display; +class Window; + +class DrawContext +{ +private: + struct Private; + + Display &display; + Window &window; + Private *priv; + +public: + DrawContext(Window &); + ~DrawContext(); + + Window &get_window() const { return window; } + unsigned get_depth() const; + unsigned char *get_data(); + void update(); +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/eventsource.h b/source/graphics/eventsource.h new file mode 100644 index 0000000..3da2e94 --- /dev/null +++ b/source/graphics/eventsource.h @@ -0,0 +1,29 @@ +#ifndef MSP_GRAPHICS_EVENTSOURCE_H_ +#define MSP_GRAPHICS_EVENTSOURCE_H_ + +namespace Msp { +namespace Graphics { + +class EventSource +{ +public: + sigc::signal signal_key_press; + sigc::signal signal_key_release; + sigc::signal signal_button_press; + sigc::signal signal_button_release; + sigc::signal signal_pointer_motion; + sigc::signal signal_resize; + +protected: + EventSource() { } +public: + virtual ~EventSource() { } + + virtual unsigned get_width() const = 0; + virtual unsigned get_height() const = 0; +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/glcontext.cpp b/source/graphics/glcontext.cpp new file mode 100644 index 0000000..b713830 --- /dev/null +++ b/source/graphics/glcontext.cpp @@ -0,0 +1,187 @@ +#include +#ifdef WIN32 +#include +#endif +#ifdef WITH_OPENGL +#include +#ifndef WIN32 +#include +#endif +#endif +#include +#include +#include "display.h" +#include "glcontext.h" +#include "window.h" +#include "display_priv.h" + +namespace Msp { +namespace Graphics { + +GLOptions::GLOptions(): + alpha(false), + stencil(false), + doublebuffer(true), + multisample(0) +{ } + + +unsupported_gl_mode::unsupported_gl_mode(const GLOptions &opts): + runtime_error(format("{ .alpha=%s, .stencil=%s, .doublebuffer=%s, .multisample=%d }", + opts.alpha, opts.stencil, opts.doublebuffer, opts.multisample)) +{ } + + +#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 + + window.signal_resize.connect(sigc::mem_fun(this, &GLContext::window_resized)); +} + +GLContext::~GLContext() +{ +#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 +} + +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; +#endif +} + +} // namespace Graphics +} // namespace Msp diff --git a/source/graphics/glcontext.h b/source/graphics/glcontext.h new file mode 100644 index 0000000..db9df4e --- /dev/null +++ b/source/graphics/glcontext.h @@ -0,0 +1,52 @@ +#ifndef MSP_GRAPHICS_GLCONTEXT_H_ +#define MSP_GRAPHICS_GLCONTEXT_H_ + +#include + +namespace Msp { +namespace Graphics { + +class Display; +class Window; + +struct GLOptions +{ + bool alpha; + bool stencil; + bool doublebuffer; + unsigned multisample; + + GLOptions(); +}; + + +class unsupported_gl_mode: public std::runtime_error +{ +public: + unsupported_gl_mode(const GLOptions &); + virtual ~unsupported_gl_mode() throw () { } +}; + + +class GLContext +{ +private: + struct Private; + + Display &display; + Window &window; + Private *priv; + +public: + GLContext(Window &wnd, const GLOptions &opts = GLOptions()); + ~GLContext(); + + void swap_buffers(); +private: + void window_resized(unsigned, unsigned); +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/image.cpp b/source/graphics/image.cpp new file mode 100644 index 0000000..3360b72 --- /dev/null +++ b/source/graphics/image.cpp @@ -0,0 +1,274 @@ +#ifdef WITH_DEVIL +#include +#endif +#ifdef WITH_LIBPNG +#include +#include +#include +#endif +#include "image.h" + +using namespace std; + +namespace Msp { +namespace Graphics { + +struct Image::Private +{ +#ifdef WITH_DEVIL + unsigned id; +#endif +#ifdef WITH_LIBPNG + PixelFormat fmt; + unsigned width; + unsigned height; + char *data; +#endif + + Private(); +}; + +Image::Private::Private() +{ +#ifdef WITH_DEVIL + id = 0; +#endif +#ifdef WITH_LIBPNG + fmt = RGB; + width = 0; + height = 0; + data = 0; +#endif +} + + +namespace { + +#ifdef WITH_LIBPNG +void read(png_struct *png, png_byte *data, png_size_t size) +{ + IO::Base *in = reinterpret_cast(png_get_io_ptr(png)); + in->read(reinterpret_cast(data), size); +} + +void load_png(IO::Base &in, Image::Private &priv) +{ + png_struct *png = 0; + png_info *info = 0; + priv.data = 0; + + try + { + png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + info = png_create_info_struct(png); + + if(setjmp(png_jmpbuf(png))) + throw bad_image_data("PNG error"); + + png_set_read_fn(png, &in, read); + png_read_info(png, info); + png_uint_32 width; + png_uint_32 height; + int depth; + int color; + png_get_IHDR(png, info, &width, &height, &depth, &color, 0, 0, 0); + priv.width = width; + priv.height = height; + if(depth!=8) + throw unsupported_image_format("depth!=8"); + switch(color) + { + case PNG_COLOR_TYPE_PALETTE: priv.fmt = COLOR_INDEX; break; + case PNG_COLOR_TYPE_GRAY: priv.fmt = LUMINANCE; break; + case PNG_COLOR_TYPE_GRAY_ALPHA: priv.fmt = LUMINANCE_ALPHA; break; + case PNG_COLOR_TYPE_RGB: priv.fmt = RGB; break; + case PNG_COLOR_TYPE_RGB_ALPHA: priv.fmt = RGBA; break; + default: throw unsupported_image_format("unknown color type"); + } + + unsigned nchans = png_get_channels(png, info); + if(nchans==4 && priv.fmt==RGB) + png_set_strip_alpha(png); + + unsigned rowstride = priv.width*nchans; + priv.data = new char[rowstride*priv.height]; + for(unsigned y=0; y(priv.data+rowstride*(priv.height-1-y)), 0); + + png_read_end(png, 0); + png_destroy_read_struct(&png, &info, 0); + } + catch(...) + { + png_destroy_read_struct(&png, &info, 0); + delete[] priv.data; + throw; + } +} +#endif + +#ifdef WITH_DEVIL +void ensure_devil_image(unsigned &id) +{ + static bool init_done = false; + + if(!init_done) + { + ilInit(); + ilEnable(IL_ORIGIN_SET); + ilOriginFunc(IL_ORIGIN_LOWER_LEFT); + init_done = true; + } + + if(!id) + ilGenImages(1, &id); +} +#endif + +} + + +Image::Image(): + priv(new Private) +{ +#if !defined(WITH_DEVIL) && !defined(WITH_LIBPNG) + throw runtime_error("no image support"); +#endif +} + +Image::~Image() +{ +#ifdef WITH_DEVIL + if(priv->id) + ilDeleteImages(1, &priv->id); +#endif +#ifdef WITH_LIBPNG + delete[] priv->data; +#endif + delete priv; +} + +void Image::load_file(const string &fn) +{ +#ifdef WITH_LIBPNG + if(fn.size()>4 && !fn.compare(fn.size()-4, 4, ".png")) + { + IO::BufferedFile file(fn); + load_png(file, *priv); + } + else +#endif + { +#ifdef WITH_DEVIL + ensure_devil_image(priv->id); + ilBindImage(priv->id); + if(!ilLoadImage(const_cast(fn.c_str()))) + throw bad_image_data("IL error"); +#else + throw unsupported_image_format("DevIL needed for non-PNG images"); +#endif + } + (void)fn; +} + +void Image::load_memory(const void *data, unsigned size) +{ +#ifdef WITH_LIBPNG + if(!png_sig_cmp(reinterpret_cast(const_cast(data)), 0, 8)) + { + IO::Memory mem(reinterpret_cast(data), size); + load_png(mem, *priv); + } + else +#endif + { +#ifdef WITH_DEVIL + ensure_devil_image(priv->id); + ilBindImage(priv->id); + if(!ilLoadL(IL_TYPE_UNKNOWN, const_cast(data), size)) + throw bad_image_data("IL error"); +#else + throw unsupported_image_format("DevIL needed for non-PNG images"); +#endif + } + (void)data; + (void)size; +} + +PixelFormat Image::get_format() const +{ +#ifdef WITH_LIBPNG + if(priv->data) + return priv->fmt; +#endif +#ifdef WITH_DEVIL + if(priv->id) + { + ilBindImage(priv->id); + switch(ilGetInteger(IL_IMAGE_FORMAT)) + { + case IL_COLOR_INDEX: return COLOR_INDEX; + case IL_LUMINANCE: return LUMINANCE; + case IL_LUMINANCE_ALPHA: return LUMINANCE_ALPHA; + case IL_RGB: return RGB; + case IL_RGBA: return RGBA; + case IL_BGR: return BGR; + case IL_BGRA: return BGRA; + // XXX bad, should throw when loading + default: throw invalid_argument("unknown pixel format in image"); + } + } +#endif + return RGB; +} + +unsigned Image::get_width() const +{ +#ifdef WITH_LIBPNG + if(priv->data) + return priv->width; +#endif +#ifdef WITH_DEVIL + if(priv->id) + { + ilBindImage(priv->id); + return ilGetInteger(IL_IMAGE_WIDTH); + } +#endif + return 0; +} + +unsigned Image::get_height() const +{ +#ifdef WITH_LIBPNG + if(priv->data) + return priv->height; +#endif +#ifdef WITH_DEVIL + if(priv->id) + { + ilBindImage(priv->id); + return ilGetInteger(IL_IMAGE_HEIGHT); + } +#endif + return 0; +} + +const void *Image::get_data() const +{ +#ifdef WITH_LIBPNG + if(priv->data) + return priv->data; +#endif +#ifdef WITH_DEVIL + if(priv->id) + { + ilBindImage(priv->id); + return ilGetData(); + } +#endif + return 0; +} + +} // namespace Graphics +} // namespace Msp diff --git a/source/graphics/image.h b/source/graphics/image.h new file mode 100644 index 0000000..db65010 --- /dev/null +++ b/source/graphics/image.h @@ -0,0 +1,48 @@ +#ifndef MSP_GRAPHICS_IMAGE_H_ +#define MSP_GRAPHICS_IMAGE_H_ + +#include +#include "pixelformat.h" + +namespace Msp { +namespace Graphics { + +class unsupported_image_format: public std::runtime_error +{ +public: + unsupported_image_format(const std::string &w): std::runtime_error(w) { } + virtual ~unsupported_image_format() throw() { } +}; + +class bad_image_data: public std::runtime_error +{ +public: + bad_image_data(const std::string &w): std::runtime_error(w) { } + virtual ~bad_image_data() throw() { } +}; + + +class Image +{ +public: + struct Private; + +private: + Private *priv; + +public: + Image(); + ~Image(); + + void load_file(const std::string &); + void load_memory(const void *, unsigned); + PixelFormat get_format() const; + unsigned get_width() const; + unsigned get_height() const; + const void *get_data() const; +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/pixelformat.h b/source/graphics/pixelformat.h new file mode 100644 index 0000000..891ba7c --- /dev/null +++ b/source/graphics/pixelformat.h @@ -0,0 +1,21 @@ +#ifndef MSP_GRAPHICS_PIXELFORMAT_H_ +#define MSP_GRAPHICS_PIXELFORMAT_H_ + +namespace Msp { +namespace Graphics { + +enum PixelFormat +{ + COLOR_INDEX, + LUMINANCE, + LUMINANCE_ALPHA, + RGB, + RGBA, + BGR, + BGRA +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/simplewindow.cpp b/source/graphics/simplewindow.cpp new file mode 100644 index 0000000..c782bab --- /dev/null +++ b/source/graphics/simplewindow.cpp @@ -0,0 +1,27 @@ +#include "simplewindow.h" + +namespace Msp { +namespace Graphics { + +SimpleWindow::SimpleWindow(unsigned w, unsigned h, bool fs): + Window(dpy, w, h, fs) +{ } + +void SimpleWindow::tick() +{ + dpy.tick(); +} + + +SimpleGLWindow::SimpleGLWindow(unsigned w, unsigned h, bool fs): + SimpleWindow(w, h, fs), + gl_ctx(*this) +{ } + +void SimpleGLWindow::swap_buffers() +{ + gl_ctx.swap_buffers(); +} + +} // namespace Graphics +} // namespace Msp diff --git a/source/graphics/simplewindow.h b/source/graphics/simplewindow.h new file mode 100644 index 0000000..f7bb0ce --- /dev/null +++ b/source/graphics/simplewindow.h @@ -0,0 +1,52 @@ +#ifndef MSP_GRAPHICS_SIMPLEWINDOW_H_ +#define MSP_GRAPHICS_SIMPLEWINDOW_H_ + +#include "display.h" +#include "glcontext.h" +#include "window.h" + +namespace Msp { +namespace Graphics { + +/** +Helper class for SimpleWindow. +*/ +class SimpleWindowBase +{ +protected: + Display dpy; + + SimpleWindowBase() { } +}; + + +/** +A simplified Window that encapsulates a Display. +*/ +class SimpleWindow: public SimpleWindowBase, public Window +{ +public: + SimpleWindow(unsigned, unsigned, bool =false); + + void tick(); +}; + + +/** +A SimpleWindow bundled with a GLContext. +*/ +class SimpleGLWindow: public SimpleWindow +{ +private: + GLContext gl_ctx; + +public: + SimpleGLWindow(unsigned, unsigned, bool =false); + GLContext &get_gl_context() { return gl_ctx; } + void swap_buffers(); +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/window.cpp b/source/graphics/window.cpp new file mode 100644 index 0000000..77959de --- /dev/null +++ b/source/graphics/window.cpp @@ -0,0 +1,481 @@ +#include +#ifndef WIN32 +#include +#include +#ifdef WITH_XF86VIDMODE +#include +#endif +#else +#include +#endif +#include +#include "display.h" +#include "window.h" +#include "display_priv.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 { + +WindowOptions::WindowOptions(): + width(640), + height(480), + fullscreen(false), + resizable(false) +{ } + + +Window::Window(Display &dpy, unsigned w, unsigned h, bool fs): + display(dpy) +{ + options.width = w; + options.height = h; + options.fullscreen = fs; + + init(); +} + +Window::Window(Display &dpy, const WindowOptions &opts): + display(dpy), + options(opts) +{ + init(); +} + +void Window::init() +{ + visible = false; + kbd_autorepeat = true; + 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 = "mspgbase"; + 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, + "mspgbase", + "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 + + display.add_window(*this); + display.check_error(); +} + +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 + + display.remove_window(*this); + + if(options.fullscreen) + display.restore_mode(); + + 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 + + if(visible) + { + if(options.fullscreen) + display.set_mode(VideoMode(options.width, options.height)); + else if(fullscreen_changed) + display.restore_mode(); + } +} + +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 + 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 + } +} + +void Window::hide() +{ +#ifdef WIN32 + ShowWindow(priv->window, SW_HIDE); +#else + XUnmapWindow(display.get_private().display, priv->window); +#endif + visible = false; + + if(options.fullscreen) + display.restore_mode(); +} + +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_MOUSEWHEEL: + { + unsigned btn = (HIWORD(wp)&0x8000) ? 5 : 4; + signal_button_press.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), btn, 0); + signal_button_release.emit(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), btn, 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: + signal_button_press.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state); + break; + case ButtonRelease: + signal_button_release.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state); + break; + case MotionNotify: + signal_pointer_motion.emit(ev.xmotion.x, ev.xmotion.y); + break; + case KeyPress: + { + char buf[16]; + XLookupString(const_cast(&ev.xkey), buf, sizeof(buf), 0, 0); + // XXX Handle the result according to locale + 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(XKeycodeToKeysym(display.get_private().display, ev.xkey.keycode, 0), ev.xkey.state); + 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 new file mode 100644 index 0000000..38b9217 --- /dev/null +++ b/source/graphics/window.h @@ -0,0 +1,69 @@ +#ifndef MSP_GRAPHICS_WINDOW_H_ +#define MSP_GRAPHICS_WINDOW_H_ + +#include +#include +#include "eventsource.h" + +namespace Msp { +namespace Graphics { + +class Display; + +struct WindowOptions +{ + unsigned width; + unsigned height; + bool fullscreen; + bool resizable; + + WindowOptions(); +}; + +class Window: public EventSource +{ +public: + struct Private; + struct Event; + + sigc::signal signal_close; + +protected: + Display &display; + WindowOptions options; + bool visible; + bool kbd_autorepeat; + bool resizing; + Private *priv; + +public: + Window(Display &, unsigned w, unsigned h, bool fs = false); + Window(Display &, const WindowOptions &); +private: + void init(); +public: + virtual ~Window(); + + void set_title(const std::string &); + void reconfigure(const WindowOptions &); + void set_keyboard_autorepeat(bool); + bool get_keyboard_autorepeat() const { return kbd_autorepeat; } + void show_cursor(bool); + void warp_pointer(int, int); + + Display &get_display() const { return display; } + const WindowOptions &get_options() const { return options; } + virtual unsigned get_width() const { return options.width; } + virtual unsigned get_height() const { return options.height; } + const Private &get_private() const { return *priv; } + + void show(); + void hide(); + + bool event(const Event &evnt); +}; + +} // namespace Graphics +} // namespace Msp + +#endif diff --git a/source/graphics/window_priv.h b/source/graphics/window_priv.h new file mode 100644 index 0000000..f5e9be8 --- /dev/null +++ b/source/graphics/window_priv.h @@ -0,0 +1,42 @@ +#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/input/binarycontrol.h b/source/input/binarycontrol.h index 851c612..8a156f7 100644 --- a/source/input/binarycontrol.h +++ b/source/input/binarycontrol.h @@ -1,5 +1,5 @@ -#ifndef MSP_GBASE_BINARYCONTROL_H_ -#define MSP_GBASE_BINARYCONTROL_H_ +#ifndef MSP_INPUT_BINARYCONTROL_H_ +#define MSP_INPUT_BINARYCONTROL_H_ #include "control.h" diff --git a/source/input/control.h b/source/input/control.h index a2f002c..924bbec 100644 --- a/source/input/control.h +++ b/source/input/control.h @@ -1,5 +1,5 @@ -#ifndef MSP_GBASE_CONTROL_H_ -#define MSP_GBASE_CONTROL_H_ +#ifndef MSP_INPUT_CONTROL_H_ +#define MSP_INPUT_CONTROL_H_ #include #include diff --git a/source/input/device.h b/source/input/device.h index 43d180d..49d858b 100644 --- a/source/input/device.h +++ b/source/input/device.h @@ -1,5 +1,5 @@ -#ifndef MSP_GBASE_INPUTDEVICE_H_ -#define MSP_GBASE_INPUTDEVICE_H_ +#ifndef MSP_INPUT_INPUTDEVICE_H_ +#define MSP_INPUT_INPUTDEVICE_H_ #include #include diff --git a/source/input/hub.h b/source/input/hub.h index 9fe57dd..8ec2fad 100644 --- a/source/input/hub.h +++ b/source/input/hub.h @@ -1,5 +1,5 @@ -#ifndef MSP_GBASE_INPUTHUB_H_ -#define MSP_GBASE_INPUTHUB_H_ +#ifndef MSP_INPUT_INPUTHUB_H_ +#define MSP_INPUT_INPUTHUB_H_ #include "device.h" diff --git a/source/input/keyboard.cpp b/source/input/keyboard.cpp index 344048e..02ff12e 100644 --- a/source/input/keyboard.cpp +++ b/source/input/keyboard.cpp @@ -3,7 +3,7 @@ #else #include #endif -#include +#include #include #include "keyboard.h" #include "keys.h" diff --git a/source/input/keyboard.h b/source/input/keyboard.h index eaca017..3e5b6b8 100644 --- a/source/input/keyboard.h +++ b/source/input/keyboard.h @@ -1,7 +1,7 @@ -#ifndef MSP_GBASE_KEYBOARD_H_ -#define MSP_GBASE_KEYBOARD_H_ +#ifndef MSP_INPUT_KEYBOARD_H_ +#define MSP_INPUT_KEYBOARD_H_ -#include +#include #include "device.h" namespace Msp { diff --git a/source/input/mouse.h b/source/input/mouse.h index eb5e9d6..5f7e714 100644 --- a/source/input/mouse.h +++ b/source/input/mouse.h @@ -1,7 +1,7 @@ -#ifndef MSP_GBASE_MOUSE_H_ -#define MSP_GBASE_MOUSE_H_ +#ifndef MSP_INPUT_MOUSE_H_ +#define MSP_INPUT_MOUSE_H_ -#include +#include #include "device.h" namespace Msp { diff --git a/source/input/smoothcontrol.h b/source/input/smoothcontrol.h index fb6f84e..78edbb3 100644 --- a/source/input/smoothcontrol.h +++ b/source/input/smoothcontrol.h @@ -1,5 +1,5 @@ -#ifndef MSP_GBASE_SMOOTHCONTROL_H_ -#define MSP_GBASE_SMOOTHCONTROL_H_ +#ifndef MSP_INPUT_SMOOTHCONTROL_H_ +#define MSP_INPUT_SMOOTHCONTROL_H_ #include "control.h"