--- /dev/null
+/* $Id$ */
+
+package "mspgbase"
+{
+ description "Mikkosoft Productions graphics base library";
+ version "0.0";
+
+ require "mspcore";
+ require "sigc++-2.0";
+ require "Xlib";
+
+ library "mspgbase"
+ {
+ source "source";
+ install true;
+ install_headers "msp/gbase";
+ };
+};
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <vector>
+#include <msp/core/error.h>
+#include "glwindow.h"
+
+using namespace std;
+
+#include <iostream>
+
+namespace Msp {
+
+GLDisplayOptions::GLDisplayOptions():
+ alpha(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()
+{
+ glXMakeCurrent(display, window, 0);
+ glXDestroyContext(display, context);
+}
+
+void GLWindow::swap_buffers()
+{
+ glXSwapBuffers(display, window);
+}
+
+void GLWindow::init()
+{
+ prepare();
+
+ vector<int> attribs;
+ attribs.push_back(GLX_RGBA);
+ attribs.push_back(GLX_BUFFER_SIZE);
+ attribs.push_back(24);
+ if(gl_options.alpha)
+ {
+ attribs.push_back(GLX_ALPHA_SIZE);
+ attribs.push_back(1);
+ }
+ if(gl_options.doublebuffer)
+ attribs.push_back(GLX_DOUBLEBUFFER);
+ if(gl_options.multisample>0)
+ {
+ attribs.push_back(GLX_SAMPLE_BUFFERS_ARB);
+ attribs.push_back(gl_options.multisample);
+ }
+ attribs.push_back(0);
+
+ XVisualInfo *visual=glXChooseVisual(display, DefaultScreen(display), &attribs.front());
+ if(!visual)
+ throw Exception("Couldn't get a matching GLX visual");
+
+ context=glXCreateContext(display, visual, 0, true);
+ if(!context)
+ throw Exception("Couldn't create a GLX context");
+
+ window=XCreateWindow(display, DefaultRootWindow(display), 0, 0, options.width, options.height, 0, CopyFromParent, InputOutput, CopyFromParent, 0, 0);
+ if(!window)
+ throw Exception("Couldn't create a window");
+
+ XSelectInput(display, window, ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask);
+
+ glXMakeCurrent(display, window, context);
+}
+
+} // 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 doublebuffer;
+ unsigned multisample;
+
+ GLDisplayOptions();
+};
+
+class GLWindow: public Window
+{
+protected:
+ typedef GLXContext Context;
+
+ GLDisplayOptions gl_options;
+ Context context;
+
+public:
+ GLWindow(unsigned, unsigned);
+ GLWindow(const DisplayOptions &, const GLDisplayOptions &);
+ ~GLWindow();
+
+ void swap_buffers();
+protected:
+ void init();
+};
+
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "inputdevice.h"
+
+namespace Msp {
+namespace Input {
+
+bool Device::get_button_state(unsigned btn)
+{
+ if(btn>buttons.size())
+ return false;
+
+ return buttons[btn];
+}
+
+float Device::get_axis_value(unsigned axis)
+{
+ if(axis>axes.size())
+ return 0;
+
+ return axes[axis];
+}
+
+void Device::set_button_state(unsigned btn, bool state, bool event)
+{
+ if(btn>=buttons.size())
+ buttons.resize(btn+1, false);
+
+ if(state!=buttons[btn])
+ {
+ buttons[btn]=state;
+
+ if(event)
+ {
+ if(state)
+ signal_button_press.emit(btn);
+ else
+ signal_button_release.emit(btn);
+ }
+ }
+}
+
+void Device::set_axis_value(unsigned axis, float value, bool event)
+{
+ if(axis>=axes.size())
+ axes.resize(axis+1, 0);
+
+ if(value!=axes[axis])
+ {
+ float old=axes[axis];
+ axes[axis]=value;
+
+ if(event)
+ signal_axis_motion.emit(axis, value, value-old);
+ }
+}
+
+} // namespace Input
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GBASE_INPUTDEVICE_H_
+#define MSP_GBASE_INPUTDEVICE_H_
+
+#include <vector>
+#include <sigc++/signal.h>
+
+namespace Msp {
+namespace Input {
+
+/**
+Base class for input devices. Input devices have two types of controls:
+buttons and axes. Buttons are either on or off. Axes have a floating-point
+value, with range depending on the device.
+*/
+class Device
+{
+public:
+ sigc::signal<void, unsigned> signal_button_press;
+ sigc::signal<void, unsigned> signal_button_release;
+ sigc::signal<void, unsigned, float, float> signal_axis_motion;
+
+protected:
+ std::vector<char> buttons;
+ std::vector<float> axes;
+
+public:
+ virtual ~Device() { }
+ bool get_button_state(unsigned);
+ float get_axis_value(unsigned);
+protected:
+ void set_button_state(unsigned, bool, bool);
+ void set_axis_value(unsigned, float, bool);
+};
+
+} // namespace Input
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GBASE_INPUTHUB_H_
+#define MSP_GBASE_INPUTHUB_H_
+
+#include "inputdevice.h"
+
+namespace Msp {
+namespace Input {
+
+class Hub: public Device
+{
+};
+
+} // namespace Input
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "keyboard.h"
+#include "window.h"
+
+namespace Msp {
+namespace Input {
+
+Keyboard::Keyboard(Window &window)
+{
+ buttons.resize(256, false);
+
+ window.signal_key_press.connect(sigc::mem_fun(this, &Keyboard::key_press));
+}
+
+void Keyboard::key_press(unsigned key, unsigned, unsigned)
+{
+ set_button_state(key, true, true);
+}
+
+void Keyboard::key_release(unsigned key, unsigned)
+{
+ set_button_state(key, false, true);
+}
+
+} // namespace Input
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GBASE_KEYBOARD_H_
+#define MSP_GBASE_KEYBOARD_H_
+
+#include "inputdevice.h"
+
+namespace Msp {
+
+class Window;
+
+namespace Input {
+
+class Keyboard: public Device
+{
+public:
+ Keyboard(Window &);
+protected:
+ void key_press(unsigned, unsigned, unsigned);
+ void key_release(unsigned, unsigned);
+};
+
+} // namespace Input
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "mouse.h"
+#include "window.h"
+
+namespace Msp {
+namespace Input {
+
+Mouse::Mouse(Window &w):
+ window(w),
+ axis_scale(0.01)
+{
+ buttons.resize(3);
+ axes.resize(2);
+
+ window.signal_button_press.connect(sigc::mem_fun(this, &Mouse::button_press));
+ window.signal_button_release.connect(sigc::mem_fun(this, &Mouse::button_release));
+ window.signal_pointer_motion.connect(sigc::mem_fun(this, &Mouse::pointer_motion));
+}
+
+void Mouse::button_press(int, int, unsigned btn, unsigned)
+{
+ set_button_state(btn, true, true);
+}
+
+void Mouse::button_release(int, int, unsigned btn, unsigned)
+{
+ set_button_state(btn, false, true);
+}
+
+void Mouse::pointer_motion(int x, int y)
+{
+ set_axis_value(0, x*axis_scale, true);
+ set_axis_value(1, y*axis_scale, true);
+}
+
+} // namespace Input
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GBASE_MOUSE_H_
+#define MSP_GBASE_MOUSE_H_
+
+#include "inputdevice.h"
+
+namespace Msp {
+
+class Window;
+
+namespace Input {
+
+class Mouse: public Device
+{
+private:
+ Window &window;
+ float axis_scale;
+
+public:
+ Mouse(Window &);
+private:
+ void button_press(int, int, unsigned, unsigned);
+ void button_release(int, int, unsigned, unsigned);
+ void pointer_motion(int, int);
+};
+
+} // namespace Input
+} // 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 <GL/glx.h>
+#include <msp/core/error.h>
+#include "window.h"
+
+using namespace std;
+
+#include <msp/strings/lexicalcast.h>
+
+namespace Msp {
+
+DisplayOptions::DisplayOptions():
+ width(640),
+ height(480),
+ fullscreen(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)
+{
+ options.width=w;
+ options.height=h;
+
+ init();
+}
+
+Window::Window(const DisplayOptions &dopt):
+ options(dopt)
+{
+ init();
+}
+
+Window::~Window()
+{
+ if(window)
+ XDestroyWindow(display, window);
+ if(display)
+ XCloseDisplay(display);
+}
+
+void Window::show()
+{
+ XMapRaised(display, window);
+}
+
+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");
+
+ //XSetErrorHandler(x_error_handler);
+}
+
+void Window::init()
+{
+ prepare();
+
+ window=XCreateWindow(display, DefaultRootWindow(display), 0, 0, options.width, options.height, 0, CopyFromParent, InputOutput, CopyFromParent, 0, 0);
+ if(!window)
+ throw Exception("Couldn't create a window");
+
+ XSelectInput(display, window, ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask);
+}
+
+void Window::process_event(const XEvent &event)
+{
+ switch(event.type)
+ {
+ case ButtonPress:
+ signal_button_press.emit(event.xbutton.x, event.xbutton.y, event.xbutton.button, event.xbutton.state);
+ break;
+ case ButtonRelease:
+ signal_button_release.emit(event.xbutton.x, event.xbutton.y, event.xbutton.button, event.xbutton.state);
+ break;
+ case MotionNotify:
+ signal_pointer_motion.emit(event.xmotion.x, event.xmotion.y);
+ break;
+ case KeyPress:
+ {
+ char buf[16];
+ XLookupString(const_cast<XKeyEvent *>(&event.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]);
+ }
+ break;
+ case KeyRelease:
+ signal_key_release.emit(event.xkey.keycode, event.xkey.state);
+ break;
+ case ConfigureNotify:
+ options.width=event.xconfigure.width;
+ options.height=event.xconfigure.height;
+ break;
+ default:;
+ }
+}
+
+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 Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GBASE_WINDOW_H_
+#define MSP_GBASE_WINDOW_H_
+
+#include <string>
+#include <X11/Xlib.h>
+#include <sigc++/signal.h>
+
+namespace Msp {
+
+struct DisplayOptions
+{
+ std::string display;
+ unsigned width;
+ unsigned height;
+ bool fullscreen;
+
+ DisplayOptions();
+};
+
+class Window
+{
+public:
+ sigc::signal<void, int, int, unsigned, unsigned> signal_button_press;
+ sigc::signal<void, int, int, unsigned, unsigned> signal_button_release;
+ sigc::signal<void, int, int> signal_pointer_motion;
+ sigc::signal<void, unsigned, unsigned, unsigned> signal_key_press;
+ sigc::signal<void, unsigned, unsigned> signal_key_release;
+
+protected:
+ typedef ::Window Handle;
+
+ Display *display;
+ DisplayOptions options;
+ Handle window;
+
+ Window();
+public:
+ Window(unsigned, unsigned);
+ Window(const DisplayOptions &);
+ virtual ~Window();
+
+ unsigned get_width() const { return options.width; }
+ unsigned get_height() const { return options.height; }
+ void show();
+ void hide();
+ void tick();
+protected:
+ void prepare();
+ void init();
+ void process_event(const XEvent &);
+
+ static int x_error_handler(Display *, XErrorEvent *);
+};
+
+} // namespace Msp
+
+#endif