Vala, OpenGL and Gtk+ sample code with shaders on Gnu/Linux

This is the follow up post for the discoveries which I was doing for myself to unify OpenGL support with Gtk+. Here, you can find a first post, I have shared towards this direction.
The content of the trivial shader program: vertex-shader.txt

#version 130

attribute vec4 vPosition;

void main () {
	gl_Position = vPosition;
}

Here,
fragment-shader.txt

#version 130

precision mediump float;
uniform vec4 vColor;

void main () {
	gl_FragColor = vColor;
}
using GL;

namespace Evg {
	public class Shader {
		private bool loaded;
		private uint type;
		private uint id;

		public Shader () {
			this.loaded = false;
			this.id = 0;
		}

		~Shader () {
			this.delete ();
		}

		public bool load_from_string (string shader, uint type) {
			this.id = glCreateShader (type);

			stdout.printf ("id is %u\n", id);

			glShaderSource (id, shader);
			glCompileShader (id);

			int status;
			glGetShaderiv (id, GL_COMPILE_STATUS, out status);

			if (status == GL_FALSE) {
				stdout.printf ("shader info log: '%s'\n", glGetShaderInfoLog (this.id));
				return false;
			}

			this.type = type;
			this.loaded = true;

			return true;
		}

		public bool load_from_file (string path, uint type) {
			File file = File.new_for_path (path);

			if (!file.query_exists ()) {
				stderr.printf ("File '%s' doesn't exist.\n", file.get_path ());
				return false;
			}

			try {
				DataInputStream dis = new DataInputStream (file.read ());
				StringBuilder sb = new StringBuilder ();
				string line;

				while ((line = dis.read_line ()) != null) {
					sb.append_printf ("%s\n", line);
				}

				return this.load_from_string (sb.str, type);
			} catch (Error e) {
				error ("%s", e.message);
			}
		}

		public bool is_loaded () {
			return this.loaded;
		}

		public uint get_id () {
			return this.id;
		}

		public void delete () {
			if (!this.loaded) return;
			this.loaded = false;
			glDeleteShader (this.id);
		}
	}
}
using GL;

namespace Evg {
	public class Program {
		private bool linked;
		private uint id;

		public Program () {
			this.id = 0;
			this.linked = false;
		}

		public uint get_id () {
			return this.id;
		}

		public bool create () {
			this.id = glCreateProgram ();
			return (this.id != 0);
		}

		public bool attach_shader (Evg.Shader shader) {
			if (!shader.is_loaded ())
				return false;

			glAttachShader (this.id, shader.get_id ());
			return true;
		}

		public bool is_linked () {
			return this.linked;
		}

		public bool is_created () {
			return (this.id != 0);
		}

		public bool link () {
			glLinkProgram (this.id);

			int status;
			glGetProgramiv (this.id, GL_LINK_STATUS, out status);
			this.linked = (status == GL_TRUE);

			if (!this.linked) {
				stdout.printf ("program info log: '%s'\n", glGetProgramInfoLog (this.id));
			}

			return this.linked;
		}

		public void use () {
			if (this.linked)
				glUseProgram (this.id);
		}

		public void delete () {
			if (!this.linked)
				return;

			this.linked = false;
			glDeleteProgram (this.id);
			this.id = 0;
		}

		~Program () {
			this.delete ();
		}
	}
}
using GLX;
using GL;

class GLXSample : Gtk.Window {

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

	private Evg.Shader vertex_shader;
	private Evg.Shader fragment_shader;
	private Evg.Program program;
	private Triangle triangle;

    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.realize.connect (on_realize);
        drawing_area.draw.connect (on_draw);

        this.add (drawing_area);
    }

	private void on_realize (Gtk.Widget widget) {
		if (!glXMakeCurrent (xdisplay,
							 Gdk.X11Window.get_xid (widget.get_window ()),
							 context))
            return;

		stdout.printf ("Vendor: ........... %s\n", glGetString (GL_VENDOR));
		stdout.printf ("Renderer: ......... %s\n", glGetString (GL_RENDERER));
		stdout.printf ("Version: .......... %s\n", glGetString (GL_VERSION));
		stdout.printf ("GLSL version: ..... %s\n",
					   glGetString (GL_SHADING_LANGUAGE_VERSION));

		this.vertex_shader = new Evg.Shader ();

		stdout.printf ("loading vertex shader\n");
		vertex_shader.load_from_file ("vertex-shader.txt", GL_VERTEX_SHADER);
		stdout.printf ("vertex shader is loaded %s\n",
					   vertex_shader.is_loaded ().to_string ());

		this.fragment_shader = new Evg.Shader ();

		stdout.printf ("loading fragment shader\n");
		fragment_shader.load_from_file ("fragment-shader.txt", GL_FRAGMENT_SHADER);
		stdout.printf ("fragment shader is loaded %s\n",
					   fragment_shader.is_loaded ().to_string ());

		this.program = new Evg.Program ();
		this.program.create ();
		stdout.printf ("program is created %s\n", program.is_created ().to_string ());
		this.program.attach_shader (this.vertex_shader);
		this.program.attach_shader (this.fragment_shader);
		this.program.link ();
		this.program.use ();
		stdout.printf ("program is linked %s\n", program.is_linked ().to_string ());

		this.triangle = new Triangle (this.program.get_id ());
	}

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

		int width = widget.get_allocated_width ();
		int height = widget.get_allocated_height ();
		int 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 );

		this.triangle.draw ();

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

        return true;
    }
}

public class Triangle {
	private float[] color = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
	private float[] triangle_vertex = {
         0.0f,  0.622008459f, 0.0f,
        -0.5f, -0.311004243f, 0.0f,
         0.5f, -0.311004243f, 0.0f
    };

	private uint program_id;

	public Triangle (uint program_id) {
		this.program_id = program_id;
	}

	public void draw () {
		int position_id = glGetAttribLocation (this.program_id, "vPosition");
		glEnableVertexAttribArray (position_id);
		glVertexAttribPointer (position_id, 3, GL_FLOAT, false, 12, triangle_vertex);

		int color_id = glGetUniformLocation (this.program_id, "vColor");
		glUniform4fv(color_id, 1, color);

		glDrawArrays (GL_TRIANGLES, 0, 3);

		glDisableVertexAttribArray (position_id);
	}
}

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

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

    Gtk.main ();
}

The content of the makefile:

all:
	valac-0.18 -o glx-test glx-test.vala evg-shader.vala evg-program.vala --vapidir ../vapi --pkg gl --pkg gl3 --pkg gio-2.0 --pkg gtk+-3.0 --pkg glx --pkg gdk-x11-3.0 #--ccode
Advertisements

GSOC with gnome-clocks – Final Report

Well people, the summer is sadly long gone, at least for me, and my summer of coding is approaching its end as well. I must say, that I am very thankful to Google for this opportunity to work in concert with my preferred desktop environment, which is still GNOME. I am glad, that I have seized this frame to finally move myself toward the first contribution to the GNOME project. I really enjoy this time and it’s a great pleasure for me, may be due my awesome mentor Paolo Borelli, I luckily have chosen for me. Thank you. 🙂 Moreover, during this period I have improved my knowledge in different areas and gained a lot of experience.

About the project results: as were reported many times geolocation support has managed to land in last minute to the 3.10 release, which is for sure great!
Moreover, there is even a not yet documented option for our users to toggle the geolocation service:

$ echo "Turn off geolocation support"
$ gsettings set org.gnome.clocks geolocation 'false'
$ echo "Turn on again"
$ gsettings set org.gnome.clocks geolocation 'true'

There is an experimental city images support in gnome-clocks available in the wip/cityimages branch try it out.
Screenshot from 2013-09-21 18:53:33 A youtube video is also available: city images – preview. The video illustrates the functionality of the implemented image providers: flickr image provider requests the corresponding images from the gnome-clocks flickr group, and downloads them for you, so the next time you launch the application they appear instantaneously. The capabilities of the local image provider are also demonstrated: It means, that you always have a possibility to overwrite the suggested image by the flickr provider with your own, you would like to see in gnome-clocks. Therefore you just need to put the corresponding image file into the folder:

~/.local/share/gnome-clocks/city-images/

Be aware of the following name convention for the image files. They should be named like: “berlin-germany-day.jpg” At the moment you can have two images for each location; one for a day and one for a night, the latter having the suffix “…-night.jpg” in its file name.

More information about gnome-clocks enhancements are available on the project page. Take care 🙂

Aaaah, and one more thank you for my sponsored travel to the GUADEC this year, it was just amazing 🙂
sponsored-badge-simple

Vala support for the Libgweather

The recent version of libgweather library from git master comes with the Vala bindings support. At least the Clocks App (gnome-clocks) will heavily rely on this bindings support and therefore we are very thankful to Giovanni Campagna for his fruitful cooperation and good and friendly support.

Please note additionally, that the new version of libgweather will deprecate some function calls. To obtain a new instance of the Location object the function “gweather_location_get_world” should be used instead of “gweather_location_new_world”. “gweather_location_new_world” is deprecated and should be omitted in the newly written code. There are many more, so for more details please refer to the official documentation.

In favour of this good announcement I would like to share with you a small sample code written in Vala, in which some capabilities of the libgweather library are briefly demonstrated: This example reveals how to obtain a timezone name for almost all corresponding major cities.
libgweather
Sample app:

public class LocationInfo {
    private Gtk.Window window;
    private Gtk.Box box;
    private GWeather.LocationEntry location_entry;
    private Gtk.Label city_label;
    private Gtk.Label timezone_label;

    public LocationInfo () {
        Gtk.Builder builder = new Gtk.Builder ();

        try {
            builder.add_from_file ("window.ui");
        } catch (Error e) {}

        this.window = builder.get_object ("window") as Gtk.Window;

        this.window.title = "libgweather testing";
        this.window.border_width = 10;
        this.window.window_position = Gtk.WindowPosition.CENTER;
        this.window.destroy.connect (Gtk.main_quit);

        this.box = builder.get_object ("box") as Gtk.Box;
        this.location_entry =
          new GWeather.LocationEntry (GWeather.Location.get_world ());
        this.location_entry.set_activates_default (true);
        this.location_entry.changed.connect (this.location_defined);
        this.box.pack_start (location_entry);

        this.city_label = builder.get_object ("city") as Gtk.Label;
        this.timezone_label = builder.get_object ("time_zone") as Gtk.Label;
    }

    public void show () {
        this.window.show_all ();
    }

    private void location_defined () {
        GWeather.Location? l = null;
        GWeather.Timezone? t = null;

        if (this.location_entry.get_text () != "") {
            l = this.location_entry.get_location ();
        }

		if (l != null) {
			t = l.get_timezone ();

			this.city_label.set_text (l.get_city_name ());

			if (t != null)
				this.timezone_label.set_text (t.get_tzid ());
			else
				this.timezone_label.set_text ("Unknown");
		}
		else {
			this.city_label.set_text ("");
			this.timezone_label.set_text ("");
		}
	}

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

        var location_info = new LocationInfo ();
        location_info.show ();
	
		Gtk.main ();
	}
}

The code can be compiled with the following Makefile:

all:
	valac --pkg gtk+-3.0 --target-glib=2.38 gweather-sample.vala \
		--pkg gweather-3.0 -X -DGWEATHER_I_KNOW_THIS_IS_UNSTABLE

The source code can be found here: libgweather-vala.tar.gz.

Вышла новая версия файлового менаджера Thunar 1.6.1

Совсем недавно, пару дней назад вышла в свет новая версия осень быстрого и использующего мало системных ресурсов файлового менеджера thunar версии 1.6.1. Одна из основных добавленных функций в новой версии это возможность использования вкладок, что иллюстрированно на картинке. thunar входит с состав программ быстрой и современной графической поверхности Xfce. Так что если у вас есть старый компьютер пятилетней давности, то у вас появляется уникальная возможность установить на него совершенно бесплатную операционную систему xubuntu, разрабатывающуюся многими тысячами программистов по всему земному шару и конечно при поддержки обширного ряда крупных компаний. Так что не удивляйтесь, если ваш старый компьютер с установленным xubuntu будет работать быстрее, только что купленного ноутбука с Windows 8!!! Добро пожаловать в мир неисчерпанных возможностей))

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

United theme for both gtk2 and gtk3 versions (in Gentoo with Gnome 3.6)

The default gtk+ themes do not look great at least in my eyes of a user. Additionally, if you use both gkt2 and gtk3 within your session, they also look different. And It seems that, I am not alone, there are people, which share my opinion regarding this style issue.

Fortunately, there is a one good looking theme named x11-themes/light-themes , which brings Ambiance and Radiance themes from ubuntu and depends on the theme engine called unico(x11-themes/gtk-engines-unico). The good point is that, this theme looks the same good with both gtk versions.

$ emerge -1av light-themes

Thereby the gtk-engines-unico will be pulled as a dependency if the USE flag gtk3 is enabled.

In order to apply this theme in Gnome 3.6 the Ambiance theme should be selected in the tweak tool as illustrated in the figure below:

As a result all application windows will looks accordingly. The exemplary window is shown for gnome-terminal:

If you also wish to have the same icon set used in ubuntu [1], which is named Humanity, an additional ebuild humanity-icon-theme should be emerged. At the time of this writing, however, this ebuild is not yet officially included in the portage tree. Nevertheless, you can add this ebuild manually. This process requires additional steps, which are luckily trivial. To accelerate the process of adding this ebuild to the official gentoo tree you can file a bug report, in which you describe your wishes by optionally providing the ebuild itself. The ebuild humanity-icon-theme-0.6.2.ebuild I used is listed below:

# Copyright 1999-2013 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $

EAPI=5
inherit gnome2-utils


DESCRIPTION="A nice and well polished icon theme"
HOMEPAGE="http://packages.ubuntu.com/raring/human-icon-theme"
SRC_URI="mirror://ubuntu/pool/main/h/${PN}/${PN}_${PV}.tar.gz"

LICENSE="CC-BY-SA-3.0"
SLOT="0"
KEYWORDS="~amd64 ~x86"
IUSE=""

RESTRICT="mirror binchecks strip"

RDEPEND="x11-themes/gnome-icon-theme
		 x11-themes/tangerine-icon-theme"

src_install() {
	insinto /usr/share/icons
	doins -r Humanity
	doins -r Humanity-Dark
}

pkg_preinst() { gnome2_icon_savelist; }
pkg_postinst() { gnome2_icon_cache_update; }
pkg_postrm() { gnome2_icon_cache_update; }

Similar ebuilds can be found in other overlays [2].

Next, I describe how to create a local overlay, where you can put your custom ebuilds. More information about this topic can be found here [3]. To add his ebuild to your local tree, called an overlay, simply create a directory structure

$ mkdir -p /usr/local/overlay/x11-themes/humanity-icon-theme/

navigate to it and save the above introduced ebuild file there. Afterwards, call the command

$ ebuild humanity-icon-theme-0.6.2.ebuild manifest

which tests the ebuild for errors and creates a Manifest file. Then, just tell portage system, where you local overlay is hiding by adding this line

...
PORTDIR_OVERLAY="/usr/local/overlay/"
...

to the /etc/portage/make.conf file, and sync your tree and emerge it (If necessary you need to keyword it as well, because it was marked as unstable ~amd64 by me in the ebuild file).

So, after installing it and applying the changes in the tweak tool by changing the icon set to Humanity the nautilus 3.6 will look similar to the one shown in the following figure:

Please note, that some icons in the Humanity set were changed [4] and especially the one of the nautilus. If you would like the have icons you used to observe during your ubuntu usage, bacause in Gentoo you make a choice, simply do the above steps by adding and emerging the corresponding ebuild by changing version of package in the ending of the ebuild file.

References:
[1] humanity-icon-theme
[2] list of overlays
[3] local overlay
[4] changes in the icon set

Using librsvg library and cairo graphics with vala

Recently, I was doing some research regarding possibilities of chess pieces drawing on a gtk+ widget. Taking out the most of the ideas from the glchess source code I have written a little application with vala, which shows a preview of chess pieces, found on wikipedia’s chess piece site or here on wikimedia and I would like to memorize it for me and share it with you. Especially, I liked the Maurizio Monge chess piece set.

So, in the following code snippet is demonstrated how to:

  • draw something in gtk+-3.0 within a widget named DrawingArea
  • access and read svg vector graphic files with a librsvg library and to render them afterwards with cairo
  • proceed mouse wheel and mouse click events using gdk and gtk+ respectively
  • transfer a svg file to a temporary created surface in memory for a later accelerated rendering on the widget

So, there are snapshots:



And the code below:

class PiecePreviewWindow : Gtk.Window
{
	public PiecePreviewWindow( ) {
		set_title( "Piece Preview" );
		add( new PieceView( ) );
		show_all( );
	}

	public override void destroy( ) {
		Gtk.main_quit( );
	}

	public static int main( string[] args ) {
		Gtk.init( ref args );
		new PiecePreviewWindow( );
		Gtk.main( );

		return 0;
	}
}

class PieceView : Gtk.DrawingArea
{
	public PieceView( ) {
		set_size_request( 400, 400 );
		add_events( Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.SCROLL_MASK );

		temp_surface = new Cairo.ImageSurface( Cairo.Format.ARGB32, 12 * square_size, square_size );

		string format = "./pieces/Maurizio Monge/Chess_Maurizio_Monge_Fantasy_%s%s.svg";
		string[] color = { "b", "w" };
		string[] piece = { "p", "r", "n", "b", "q", "k" };

		for( int c = 0; c < 2; c ++ ) {
			for(int p = 0; p < 6; p ++ ) {
				render_piece( format.printf( color[c], piece[p] ), 6 * c + p );
			}
		}
	}

	public override bool draw( Cairo.Context cr ) {
		cr.set_source_rgb( 0.5, 0.5, 0.5 );
		cr.rectangle( 0.0, 0.0, 400.0, 400.0 );
		cr.fill( );

		cr.scale( 400.0 / square_size, 400.0 / square_size );
		cr.set_source_surface( temp_surface, -offset * square_size, 0 );
		cr.rectangle( 0, 0, square_size, square_size );
		cr.clip( );
		cr.paint( );

		return false;
	}

	void render_piece( string file_name, int offset ) {
		Rsvg.Handle handle;
		try {
			handle = new Rsvg.Handle.from_file( file_name );
		} catch( Error e ) {
			stderr.printf( "can not open svg file\n" );
			return;
		}

		temp_cr = new Cairo.Context( temp_surface );
		temp_cr.save( );
		temp_cr.translate( square_size * offset, 0 );
		temp_cr.scale( (double) square_size / handle.width, (double) square_size / handle.height );
		handle.render_cairo( temp_cr );
		temp_cr.restore( );
	}

	public override bool button_press_event( Gdk.EventButton event ) {
		if( event.button  == 1 ) {
		 	change_piece( true );
		} else if( event.button == 3 ) {
			change_piece( false );
		} else
			return false;

		return true;
	}

	public override bool scroll_event( Gdk.EventScroll event ) {
		if( event.direction == Gdk.ScrollDirection.UP ) {
			change_piece( true );
		} else if( event.direction == Gdk.ScrollDirection.DOWN ) {
			change_piece( false );
		} else
			return false;
		return true;
	}

	private void change_piece( bool forward ) {
		if( forward ) offset ++;
		else offset --;

		if( offset < 0 ) offset = 11; 		if( offset > 11) offset = 0;

		queue_draw( );
	}

	private Cairo.ImageSurface temp_surface;
	private Cairo.Context temp_cr;
	private int square_size = 500;
	private int offset;
}

All this can be compiled with

# valac gtk-svg.vala --pkg librsvg-2.0 --pkg gtk+-3.0