Turorial for OpenGL and Gtk3+ combined, written in Vala

This post is still work in progress!
The main target of this code sample, even if it’s trivial one, is to run a openGL based application on top of the modern window library Gtk+3 on Gnu/Linux. It must be possible to run this openGL code combined with Gtk+ on windows, but I personally do not know how to do it, as I rarely use windows myself. Well, actually, I use it only to set up it for my girlfriend or to solve the endless number of issues my girlfriend has ran into. If you, by the way, know howto extend this simple example, written in vala, so it can be launched on windows, please share it with me.)) The reason for this incompatibility is that here the X11 related functions calls are used to establish the incorporation between Gtk3, Gdk and OpenGL on the other side.

This sample code for simplicity reasons covers only the openGL 1 API. The example with vertex and fragment shaders, respectively, will be considered in the succeeding posts.
Here, therefore we first create a new custom window by inheriting the Gtk window class functionality and add a drawing area to it. The drawing area afterwards will be responsible for the GL context. Below you can find a class template, which includes the step by step recipe, needed yet to be implemented.

/* here the opengl and X related libraries are included */
using GLX;
using GL;

class GLXSample : Gtk.Window {
    public GLXSample( ) {
        /* 1. adding custom setting for the newly created window */

        /* 2. preparing and intializing the opengl context */

        /* 3. creating the drawing area and adding it to the window */
        var drawing_area = new Gtk.DrawingArea( );
        /* adjusting the drawing area parameters */

        /* adding two events listeners: */
        /* the first handles the opengl content on window resize events */
        /* followed by the event which is responsible for the drawing */
        drawing_area.configure_event.connect( on_configure_event );
        drawing_area.draw.connect( on_draw );

        this.add( drawing_area );
    }

    private bool on_configure_event( Gtk.Widget widget, Gdk.EventConfigure event ) {
        /* adapting the opengl content on window size change */
        return true;
    }

    private bool on_draw( Gtk.Widget widget, Cairo.Context cr ) {
        /* drawing the opengl content here */
        return true;
    }
}

void main( string[] args ) {
    Gtk.init( ref args );

    var sample = new GLXSample( );
    sample.show_all( );

    Gtk.main( );
}

So, after implementing all the introduced steps will result in the following working code sample:

using GLX;
using GL;

class GLXSample : Gtk.Window {

    private unowned X.Display xdisplay;
    private GLX.Context context;
    private GLX.XVisualInfo xvinfo;

    public GLXSample( ) {
        this.title = "OpenGL with GLX";
        this.set_reallocate_redraws( true );
        this.destroy.connect( Gtk.main_quit );

        int[] attrlist = {
            GLX_RGBA,
            GLX_RED_SIZE, 1,
            GLX_GREEN_SIZE, 1,
            GLX_BLUE_SIZE, 1,
            GLX_DOUBLEBUFFER, 0
        };

        this.xdisplay = Gdk.x11_get_default_xdisplay( );
        
        if( !glXQueryExtension( xdisplay, null, null ) ) {
            stderr.printf( "OpenGL not supported\n" );
        }

        this.xvinfo = glXChooseVisual( xdisplay, Gdk.x11_get_default_screen( ), attrlist );
        
        if( xvinfo == null ) {
            stderr.printf( "Error configuring OpenGL\n" );
        }

        var drawing_area = new Gtk.DrawingArea( );
        drawing_area.set_size_request( 300, 300 );
        drawing_area.set_double_buffered( false );

        this.context = glXCreateContext( xdisplay, xvinfo, null, true );

        drawing_area.configure_event.connect( on_configure_event );
        drawing_area.draw.connect( on_draw );

        add( drawing_area );
    }

    private bool on_configure_event( Gtk.Widget widget, Gdk.EventConfigure event ) {
        if( !glXMakeCurrent( xdisplay, Gdk.X11Window.get_xid( widget.get_window( ) ), context ) )
            return false;

        GLsizei width = widget.get_allocated_width( );
        GLsizei height = widget.get_allocated_height( );
        GLsizei size = int.min( width, height );
		
        glViewport( (width - size ) / 2, (height - size ) / 2, size , size );

        return true;
    }

    private bool on_draw( Gtk.Widget widget, Cairo.Context cr ) {
        if( !glXMakeCurrent( xdisplay, Gdk.X11Window.get_xid( widget.get_window( ) ), context ) )
            return false;

        glClear( GL_COLOR_BUFFER_BIT );

        glBegin( GL_TRIANGLES );
            glIndexi( 0 );
            glColor3f( 1.0f, 0.0f, 0.0f );
            glVertex2i( 0, 1 );
            glIndexi( 0 );
            glColor3f( 0.0f, 1.0f, 0.0f );
            glVertex2i( -1, -1 );
            glIndexi( 0 );
            glColor3f( 0.0f, 0.0f, 1.0f );
            glVertex2i( 1, -1 );
        glEnd( );

        glXSwapBuffers( xdisplay, Gdk.X11Window.get_xid( widget.get_window( ) ) );

        return true;
    }
}

void main( string[] args ) {
    Gtk.init( ref args );

    var sample = new GLXSample( );
    sample.show_all( );

    Gtk.main( );
}

Here you can find a working makefile, which should make the compilation process easier.

all:
	valac --pkg gtk+-3.0 --pkg gdk-x11-3.0 --vapidir ../vapi --pkg gl --pkg glx glx-sample.vala -o glx-sample
About these ads

One thought on “Turorial for OpenGL and Gtk3+ combined, written in Vala

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s