Add class Display for access to the graphical environemnt before any windows are created
Replace GLWindow with an independent GLContext class
Put stuff into namespace Graphics
Support for querying and changing video modes
Handle Xlib errors
require "sigc++-2.0";
require "xlib";
+ build_info
+ {
+ library "Xxf86vm";
+ };
+
library "mspgbase"
{
source "source";
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <iostream>
+#include <X11/Xlib.h>
+#include <X11/extensions/xf86vmode.h>
+#include <msp/core/except.h>
+#include <msp/strings/formatter.h>
+#include <msp/strings/lexicalcast.h>
+#include "display.h"
+#include "window.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(static_cast<int>(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: "<<msg<<'\n';
+ else
+ {
+ cerr<<msg<<'\n';
+ error_msg=msg;
+ error_flag=true;
+ }
+
+ return 0;
+}
+
+}
+
+namespace Msp {
+namespace Graphics {
+
+Display::Display(const string &disp_name)
+{
+ if(disp_name.empty())
+ display=XOpenDisplay(0);
+ else
+ display=XOpenDisplay(disp_name.c_str());
+ if(!display)
+ throw Exception("Couldn't open X display");
+
+ XSetErrorHandler(x_error_handler);
+
+ int nmodes;
+ XF86VidModeModeInfo **infos;
+ XF86VidModeGetAllModeLines(display, DefaultScreen(display), &nmodes, &infos);
+ for(int i=0; i<nmodes; ++i)
+ {
+ XF86VidModeModeInfo &info=*infos[i];
+
+ VideoMode mode;
+ mode.width=info.hdisplay;
+ mode.height=info.vdisplay;
+ mode.rate=info.dotclock/(info.htotal*info.vtotal);
+ modes.push_back(mode);
+ }
+}
+
+Display::~Display()
+{
+ XCloseDisplay(display);
+ display=0;
+}
+
+void Display::add_window(Window *wnd)
+{
+ windows[wnd->get_handle()]=wnd;
+}
+
+void Display::remove_window(Window *wnd)
+{
+ windows.erase(wnd->get_handle());
+}
+
+void Display::set_mode(const VideoMode &mode)
+{
+ int screen=DefaultScreen(display);
+
+ int nmodes;
+ XF86VidModeModeInfo **infos;
+ XF86VidModeGetAllModeLines(display, screen, &nmodes, &infos);
+ for(int i=0; i<nmodes; ++i)
+ {
+ XF86VidModeModeInfo &info=*infos[i];
+
+ unsigned rate=info.dotclock/(info.htotal*info.vtotal);
+ if(info.hdisplay==mode.width && info.vdisplay==mode.height && rate==mode.rate)
+ {
+ XF86VidModeSwitchToMode(display, screen, &info);
+ return;
+ }
+ }
+
+ throw InvalidParameterValue("Requested mode not supported");
+}
+
+void Display::tick()
+{
+ check_error();
+
+ while(1)
+ {
+ int pending=XPending(display);
+ if(pending==0)
+ break;
+
+ for(int i=0; i<pending; ++i)
+ {
+ XEvent event;
+ XNextEvent(display, &event);
+
+ check_error();
+
+ map<WindowHandle, Window *>::iterator j=windows.find(event.xany.window);
+ if(j!=windows.end())
+ j->second->event(event);
+ }
+ }
+}
+
+void Display::check_error()
+{
+ if(error_flag)
+ {
+ error_flag=false;
+ throw Exception(error_msg);
+ }
+}
+
+} // namespace Graphics
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GBASE_DISPLAY_H_
+#define MSP_GBASE_DISPLAY_H_
+
+#include <list>
+#include <map>
+#include <string>
+#include "types.h"
+
+namespace Msp {
+namespace Graphics {
+
+class Window;
+
+struct VideoMode
+{
+ unsigned width;
+ unsigned height;
+ unsigned rate;
+};
+
+class Display
+{
+private:
+ ::Display *display;
+ std::list<VideoMode> modes;
+ std::map<WindowHandle, Window *> windows;
+
+public:
+ Display(const std::string &disp_name=std::string());
+ ~Display();
+
+ ::Display *get_display() const { return display; }
+
+ void add_window(Window *);
+ void remove_window(Window *);
+
+ const std::list<VideoMode> &get_modes() const { return modes; }
+ void set_mode(const VideoMode &);
+
+ void tick();
+ void check_error();
+};
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <vector>
+#include <msp/core/except.h>
+#include "display.h"
+#include "glcontext.h"
+#include "window.h"
+
+namespace Msp {
+namespace Graphics {
+
+GLOptions::GLOptions():
+ alpha(false),
+ stencil(false),
+ doublebuffer(true),
+ multisample(0)
+{ }
+
+
+GLContext::GLContext(Display &d, const GLOptions &opts):
+ display(d)
+{
+ std::vector<int> 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_display();
+ XVisualInfo *vi=glXChooseVisual(dpy, DefaultScreen(dpy), &attribs.front());
+ if(!vi)
+ throw Exception("Couldn't find a GLX visual");
+ context=glXCreateContext(dpy, vi, 0, true);
+
+ XSetWindowAttributes attr;
+ attr.colormap=XCreateColormap(dpy, DefaultRootWindow(dpy), vi->visual, AllocNone);
+
+ window=XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, 1024, 768, 0, vi->depth, InputOutput, vi->visual, CWColormap, &attr);
+
+ glXMakeCurrent(dpy, window, context);
+}
+
+void GLContext::attach(Window &wnd)
+{
+ XReparentWindow(display.get_display(), window, wnd.get_handle(), 0, 0);
+ XMapWindow(display.get_display(), window);
+
+ wnd.signal_resize.connect(sigc::mem_fun(this, &GLContext::window_resized));
+ window_resized(wnd.get_width(), wnd.get_height());
+}
+
+void GLContext::swap_buffers()
+{
+ glXSwapBuffers(display.get_display(), window);
+}
+
+void GLContext::window_resized(unsigned w, unsigned h)
+{
+ XMoveResizeWindow(display.get_display(), window, 0, 0, w, h);
+ glViewport(0, 0, w, h);
+}
+
+} // namespace Graphics
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GBASE_GLCONTEXT_H_
+#define MSP_GBASE_GLCONTEXT_H_
+
+#include <GL/glx.h>
+#include "types.h"
+
+namespace Msp {
+namespace Graphics {
+
+class Display;
+
+struct GLOptions
+{
+ bool alpha;
+ bool stencil;
+ bool doublebuffer;
+ unsigned multisample;
+
+ GLOptions();
+};
+
+class GLContext
+{
+private:
+ typedef GLXContext Context;
+
+ Display &display;
+ Context context;
+ GLXWindow glx_wnd;
+ WindowHandle window;
+
+public:
+ GLContext(Display &dpy, const GLOptions &opts);
+ void attach(Window &wnd);
+ void swap_buffers();
+private:
+ void window_resized(unsigned, unsigned);
+};
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif
+++ /dev/null
-/* $Id$
-
-This file is part of libmspgbase
-Copyright © 2007 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
-#include <vector>
-#include <msp/core/except.h>
-#include "glwindow.h"
-
-using namespace std;
-
-namespace Msp {
-
-GLDisplayOptions::GLDisplayOptions():
- alpha(false),
- stencil(false),
- doublebuffer(true),
- multisample(0)
-{ }
-
-
-GLWindow::GLWindow(unsigned w, unsigned h)
-{
- options.width=w;
- options.height=h;
- init();
-}
-
-GLWindow::GLWindow(const DisplayOptions &dopt, const GLDisplayOptions &gl_dopt)
-{
- options=dopt;
- gl_options=gl_dopt;
- init();
-}
-
-GLWindow::~GLWindow()
-{
- glXMakeContextCurrent(display, 0, 0, 0);
- glXDestroyWindow(display, glx_wnd);
- glXDestroyContext(display, context);
-}
-
-void GLWindow::swap_buffers()
-{
- glXSwapBuffers(display, glx_wnd);
-}
-
-void GLWindow::init()
-{
- prepare();
-
- vector<int> attribs;
-
- attribs.push_back(GLX_RENDER_TYPE);
- attribs.push_back(GLX_RGBA_BIT);
-
- attribs.push_back(GLX_DRAWABLE_TYPE);
- attribs.push_back(GLX_WINDOW_BIT);
-
- attribs.push_back(GLX_DEPTH_SIZE);
- attribs.push_back(1);
-
- if(gl_options.alpha)
- {
- attribs.push_back(GLX_ALPHA_SIZE);
- attribs.push_back(1);
- }
-
- if(gl_options.stencil)
- {
- attribs.push_back(GLX_STENCIL_SIZE);
- attribs.push_back(1);
- }
-
- if(gl_options.doublebuffer)
- {
- attribs.push_back(GLX_DOUBLEBUFFER);
- attribs.push_back(true);
- }
-
- if(gl_options.multisample>0)
- {
- attribs.push_back(GLX_SAMPLE_BUFFERS_ARB);
- attribs.push_back(gl_options.multisample);
- }
-
- attribs.push_back(None);
-
- int count;
- GLXFBConfig *config=glXChooseFBConfig(display, DefaultScreen(display), &attribs.front(), &count);
- if(!config)
- throw Exception("Couldn't get a GLX framebuffer configuration");
-
- context=glXCreateNewContext(display, config[0], GLX_RGBA_TYPE, 0, true);
- if(!context)
- throw Exception("Couldn't create a GLX context");
-
- XVisualInfo *vi=glXGetVisualFromFBConfig(display, config[0]);
- Handle root=RootWindow(display, vi->screen);
-
- Colormap cmap=XCreateColormap(display, root, vi->visual, AllocNone);
- XSetWindowAttributes attr;
- attr.colormap=cmap;
- attr.override_redirect=options.fullscreen;
-
- Handle wnd=XCreateWindow(display, root, 0, 0, options.width, options.height, 0, vi->depth, InputOutput, vi->visual, CWColormap|CWOverrideRedirect, &attr);
- set_window(wnd);
-
- glx_wnd=glXCreateWindow(display, config[0], wnd, 0);
-
- glXMakeContextCurrent(display, glx_wnd, glx_wnd, context);
-
- XFree(config);
-}
-
-void GLWindow::on_event(const XEvent &event)
-{
- if(event.type==ConfigureNotify)
- glViewport(0, 0, options.width, options.height);
-}
-
-} // namespace Msp
+++ /dev/null
-/* $Id$
-
-This file is part of libmspgbase
-Copyright © 2007 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
-#ifndef MSP_GBASE_GLWINDOW_H_
-#define MSP_GBASE_GLWINDOW_H_
-
-#include <GL/glx.h>
-#include "window.h"
-
-namespace Msp {
-
-struct GLDisplayOptions
-{
- bool alpha;
- bool stencil;
- bool doublebuffer;
- unsigned multisample;
-
- GLDisplayOptions();
-};
-
-class GLWindow: public Window
-{
-protected:
- typedef GLXContext Context;
-
- GLDisplayOptions gl_options;
- GLXWindow glx_wnd;
- Context context;
-
-public:
- GLWindow(unsigned, unsigned);
- GLWindow(const DisplayOptions &, const GLDisplayOptions &);
- ~GLWindow();
-
- void swap_buffers();
-protected:
- void init();
- virtual void on_event(const XEvent &event);
-};
-
-} // namespace Msp
-
-#endif
*/
#include "keyboard.h"
-#include "window.h"
namespace Msp {
namespace Input {
-Keyboard::Keyboard(Window &window)
+Keyboard::Keyboard(Graphics::Window &window)
{
buttons.resize(256, false);
#define MSP_GBASE_KEYBOARD_H_
#include "inputdevice.h"
+#include "window.h"
namespace Msp {
-
-class Window;
-
namespace Input {
class Keyboard: public Device
{
public:
- Keyboard(Window &);
+ Keyboard(Graphics::Window &);
protected:
void key_press(unsigned, unsigned, unsigned);
void key_release(unsigned, unsigned);
*/
#include "mouse.h"
-#include "window.h"
namespace Msp {
namespace Input {
-Mouse::Mouse(Window &w):
+Mouse::Mouse(Graphics::Window &w):
window(w),
axis_scale(0.01)
{
#define MSP_GBASE_MOUSE_H_
#include "inputdevice.h"
+#include "window.h"
namespace Msp {
-
-class Window;
-
namespace Input {
class Mouse: public Device
{
private:
- Window &window;
+ Graphics::Window &window;
float axis_scale;
public:
- Mouse(Window &);
+ Mouse(Graphics::Window &);
private:
void button_press(int, int, unsigned, unsigned);
void button_release(int, int, unsigned, unsigned);
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GBASE_TYPES_H_
+#define MSP_GBASE_TYPES_H_
+
+#include <X11/Xlib.h>
+
+namespace Msp {
+namespace Graphics {
+
+typedef ::Window WindowHandle;
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <msp/core/except.h>
+#include "display.h"
#include "window.h"
using namespace std;
namespace Msp {
+namespace Graphics {
-DisplayOptions::DisplayOptions():
+WindowOptions::WindowOptions():
width(640),
height(480),
- fullscreen(false)
+ fullscreen(false),
+ resizable(false)
{ }
-/**
-Initializes the class but does not open the window. Intended for use by
-derived classes.
-*/
-Window::Window():
- display(0),
- window(0)
-{ }
-
-Window::Window(unsigned w, unsigned h)
+Window::Window(Display &dpy, unsigned w, unsigned h, bool fs):
+ display(dpy)
{
options.width=w;
options.height=h;
+ options.fullscreen=fs;
init();
}
-Window::Window(const DisplayOptions &dopt):
- options(dopt)
+Window::Window(Display &dpy, const WindowOptions &opts):
+ display(dpy),
+ options(opts)
{
init();
}
Window::~Window()
{
if(window)
- XDestroyWindow(display, window);
- if(display)
- XCloseDisplay(display);
+ XDestroyWindow(display.get_display(), window);
+
+ display.remove_window(this);
}
void Window::set_title(const string &title)
prop.encoding=XA_STRING;
prop.format=8;
prop.nitems=title.size();
- XSetWMName(display, window, &prop);
+ XSetWMName(display.get_display(), window, &prop);
+ display.check_error();
}
void Window::show()
{
- XMapRaised(display, window);
+ XMapRaised(display.get_display(), window);
+ display.check_error();
}
void Window::hide()
{
- XUnmapWindow(display, window);
-}
-
-void Window::tick()
-{
- while(1)
- {
- int pending=XPending(display);
- if(pending==0)
- break;
-
- for(int i=0; i<pending; ++i)
- {
- XEvent event;
- XNextEvent(display, &event);
- process_event(event);
- }
- }
-}
-
-void Window::prepare()
-{
- if(options.display.empty())
- display=XOpenDisplay(0);
- else
- display=XOpenDisplay(options.display.c_str());
- if(!display)
- throw Exception("Couldn't open X display");
-
- wm_delete_window=XInternAtom(display, "WM_DELETE_WINDOW", true);
-
- /* Throwing from the error handler doesn't work too well and I don't know
- how to dig up all the information that Xlib gives by default, so disable
- custom error handling for now. */
- //XSetErrorHandler(x_error_handler);
-}
-
-void Window::set_window(Handle wnd)
-{
- window=wnd;
-
- XSelectInput(display, window, ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask|EnterWindowMask);
-
- XSetWMProtocols(display, window, &wm_delete_window, 1);
+ XUnmapWindow(display.get_display(), window);
+ display.check_error();
}
void Window::init()
{
- prepare();
+ ::Display *dpy=display.get_display();
+
+ wm_delete_window=XInternAtom(dpy, "WM_DELETE_WINDOW", true);
XSetWindowAttributes attr;
attr.override_redirect=options.fullscreen;
+ attr.event_mask=ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask|EnterWindowMask;
- Handle wnd=XCreateWindow(display, DefaultRootWindow(display), 0, 0, options.width, options.height, 0, CopyFromParent, InputOutput, CopyFromParent, CWOverrideRedirect, &attr);
- set_window(wnd);
+ window=XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, options.width, options.height, 0, CopyFromParent, InputOutput, CopyFromParent, CWOverrideRedirect|CWEventMask, &attr);
+
+ XSetWMProtocols(dpy, window, &wm_delete_window, 1);
+
+ display.add_window(this);
+ display.check_error();
}
-void Window::process_event(const XEvent &event)
+void Window::event(const XEvent &ev)
{
- switch(event.type)
+ switch(ev.type)
{
case ButtonPress:
- signal_button_press.emit(event.xbutton.x, event.xbutton.y, event.xbutton.button, event.xbutton.state);
+ signal_button_press.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
break;
case ButtonRelease:
- signal_button_release.emit(event.xbutton.x, event.xbutton.y, event.xbutton.button, event.xbutton.state);
+ signal_button_release.emit(ev.xbutton.x, ev.xbutton.y, ev.xbutton.button, ev.xbutton.state);
break;
case MotionNotify:
- signal_pointer_motion.emit(event.xmotion.x, event.xmotion.y);
+ signal_pointer_motion.emit(ev.xmotion.x, ev.xmotion.y);
break;
case KeyPress:
{
char buf[16];
- XLookupString(const_cast<XKeyEvent *>(&event.xkey), buf, sizeof(buf), 0, 0);
+ XLookupString(const_cast<XKeyEvent *>(&ev.xkey), buf, sizeof(buf), 0, 0);
// XXX Handle the result according to locale
- signal_key_press.emit(event.xkey.keycode, event.xkey.state, buf[0]);
+ signal_key_press.emit(ev.xkey.keycode, ev.xkey.state, buf[0]);
}
break;
case KeyRelease:
- signal_key_release.emit(event.xkey.keycode, event.xkey.state);
+ signal_key_release.emit(ev.xkey.keycode, ev.xkey.state);
break;
case ConfigureNotify:
- options.width=event.xconfigure.width;
- options.height=event.xconfigure.height;
+ options.width=ev.xconfigure.width;
+ options.height=ev.xconfigure.height;
signal_resize.emit(options.width, options.height);
break;
case ClientMessage:
- if(event.xclient.data.l[0]==static_cast<long>(wm_delete_window))
+ if(ev.xclient.data.l[0]==static_cast<long>(wm_delete_window))
signal_close.emit();
break;
case EnterNotify:
- XSetInputFocus(display, window, RevertToParent, CurrentTime);
+ XSetInputFocus(display.get_display(), window, RevertToParent, CurrentTime);
break;
default:;
}
-
- on_event(event);
-}
-
-int Window::x_error_handler(Display *display, XErrorEvent *error)
-{
- char buf[128];
- XGetErrorText(display, error->error_code, buf, sizeof(buf));
- /*string request_code=lexical_cast(error->request_code);
- char buf2[1024];
- XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), buf, buf2, sizeof(buf2));*/
- throw Exception(buf);
}
+} // namespace Graphics
} // namespace Msp
#define MSP_GBASE_WINDOW_H_
#include <string>
-#include <X11/Xlib.h>
#include <sigc++/signal.h>
+#include "types.h"
namespace Msp {
+namespace Graphics {
-struct DisplayOptions
+class Display;
+
+struct WindowOptions
{
- std::string display;
unsigned width;
unsigned height;
bool fullscreen;
+ bool resizable;
- DisplayOptions();
+ WindowOptions();
};
class Window
sigc::signal<void> signal_close;
protected:
- typedef ::Window Handle;
-
- Display *display;
- DisplayOptions options;
- Handle window;
- Atom wm_delete_window;
+ Display &display;
+ WindowOptions options;
+ WindowHandle window;
+ Atom wm_delete_window;
- Window();
public:
- Window(unsigned w, unsigned h);
- Window(const DisplayOptions &dopt);
- virtual ~Window();
+ Window(Display &, unsigned w, unsigned h, bool fs=false);
+ Window(Display &, const WindowOptions &);
+ ~Window();
void set_title(const std::string &);
unsigned get_width() const { return options.width; }
unsigned get_height() const { return options.height; }
+ WindowHandle get_handle() const { return window; }
+
void show();
void hide();
- void tick();
+
+ void event(const XEvent &ev);
protected:
- void prepare();
- void set_window(Handle wnd);
void init();
- void process_event(const XEvent &event);
- virtual void on_event(const XEvent &event) { (void)event; }
-
- static int x_error_handler(Display *, XErrorEvent *);
};
+} // namespace Graphics
} // namespace Msp
#endif