#include #include #include #include // This is an implementation of SimpleViewer that is designed to be subclassed // and used as a GtkDrawingArea in a GTK application. Because of the implementation // of GTK, I was unable to derive from GtkWidget and instead had to "wrap" it. // Conceptually, however, you can think of an OSGGTKDrawingArea as both an OSG // Viewer AND GtkDrawingArea. // // While it is possible to use this class directly, it won't end up doing anything // interesting without calls to queueDraw, which ideally are done in the user's // subclass implementation (see: osgviewerGTK). class OSGGTKDrawingArea : public osgViewer::Viewer { GtkWidget* _widget; GdkGLConfig* _glconfig; GdkGLContext* _context; GdkGLDrawable* _drawable; osg::ref_ptr _gw; unsigned int _state; osgGA::EventQueue& _queue; static OSGGTKDrawingArea* _self(gpointer self) { return static_cast(self); } // A simple helper function to connect us to the various GTK signals. void _connect(const char* name, GCallback callback) { g_signal_connect(G_OBJECT(_widget), name, callback, this); } void _realize (GtkWidget*); void _unrealize (GtkWidget*); bool _expose_event (GtkWidget*, GdkEventExpose*); bool _configure_event (GtkWidget*, GdkEventConfigure*); bool _motion_notify_event (GtkWidget*, GdkEventMotion*); bool _button_press_event (GtkWidget*, GdkEventButton*); bool _key_press_event (GtkWidget*, GdkEventKey*); // The following functions are static "wrappers" so that we can invoke the // bound methods of a class instance by passing the "this" pointer as the // self argument and invoking it explicitly. static void _srealize(GtkWidget* widget, gpointer self) { _self(self)->_realize(widget); } static void _sunrealize(GtkWidget* widget, gpointer self) { _self(self)->_unrealize(widget); } static bool _sexpose_event(GtkWidget* widget, GdkEventExpose* expose, gpointer self) { return _self(self)->_expose_event(widget, expose); } static bool _sconfigure_event( GtkWidget* widget, GdkEventConfigure* event, gpointer self ) { return _self(self)->_configure_event(widget, event); } static bool _smotion_notify_event( GtkWidget* widget, GdkEventMotion* event, gpointer self ) { return _self(self)->_motion_notify_event(widget, event); } static bool _sbutton_press_event( GtkWidget* widget, GdkEventButton* event, gpointer self ) { return _self(self)->_button_press_event(widget, event); } static bool _skey_press_event( GtkWidget* widget, GdkEventKey* event, gpointer self ) { return _self(self)->_key_press_event(widget, event); } protected: // You can override these in your subclass if you'd like. :) // Right now they're fairly uninformative, but they could be easily extended. // Note that the "state" information isn't passed around to each function // but is instead stored and abstracted internally. See below. virtual void gtkRealize () {}; virtual void gtkUnrealize () {}; virtual bool gtkExpose () { return true; }; // The new width and height. virtual bool gtkConfigure(int, int) { return true; }; // The "normalized" coordinates of the mouse. virtual bool gtkMotionNotify(double, double) { return true; }; // The "normalized" coordinates of the mouse and the mouse button code on down. virtual bool gtkButtonPress(double, double, unsigned int) { return true; }; // The "normalized" coordinates of the mouse and mouse button code on release. virtual bool gtkButtonRelease(double, double, unsigned int) { return true; } // The X key value on down. virtual bool gtkKeyPress(unsigned int) { return true; }; // The X key value on release. virtual bool gtkKeyRelease(unsigned int) { return true; }; // These functions wrap state tests of the most recent state in the // GtkDrawingArea. inline bool stateShift() { return _state & GDK_SHIFT_MASK; } inline bool stateLock() { return _state & GDK_LOCK_MASK; } inline bool stateControl() { return _state & GDK_CONTROL_MASK; } inline bool stateMod() { return _state & ( GDK_MOD1_MASK | GDK_MOD2_MASK | GDK_MOD3_MASK | GDK_MOD4_MASK | GDK_MOD5_MASK ); } inline bool stateButton() { return _state & ( GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK ); } public: OSGGTKDrawingArea (); ~OSGGTKDrawingArea (); bool createWidget(int, int); GtkWidget* getWidget() { return _widget; } bool gtkGLBegin() { if(_drawable and _context) return gdk_gl_drawable_gl_begin(_drawable, _context); else return false; } void gtkGLEnd() { if(_drawable) gdk_gl_drawable_gl_end(_drawable); } // Because of GTK's internal double buffering, I'm not sure if we're really // taking advantage of OpenGL's internal swapping. bool gtkGLSwap() { if(_drawable and gdk_gl_drawable_is_double_buffered(_drawable)) { gdk_gl_drawable_swap_buffers(_drawable); return true; } else { glFlush(); return false; } } void queueDraw() { gtk_widget_queue_draw(_widget); } };