This website is obsolete. Please follow this link or use a search engine to get at the new document root.

A short introduction to game programming on X-Window with the GTK+ toolkit

Return to index

The origin of this document

This short introdution was initially an answer to a question that was appearing rather frenquently on the Gnome mailing-list (gnome-list@gnome.org).

A lot of people that do not have a strong programming experience with the X-Window system often forget that it is a network-transparent solution. It is thus impossible to do direct pixel manipulation of the image that appears on the screen, and therefore to obtain a pointer to that data, since it isn't in the same process (it is often not even on the same machine).

The following was a pure ASCII e-mail, it has been reformatted in HTML (I have essentially added paragraph markup) to integrate nicely with any web browser. The "[clip]" lines mention that some of the original content of the question was suppressed.

The original e-mail (reformatted)

To: gnome-list@gnome.org
Subject: Re: Fast Image Manipulation?
Date: Wed, 18 Nov 1998 14:48:12 +0100

[clip]

> Sorry, I realize I can't play with the actual display bits.

[clip]

Hello everybody,

As this is a subject that pops up quite often, I think I'll devote some time to explain the whole thing (or at least what I'm understanding of it) ...

General introdution

As Gtk / Gnome use gdk, which is basically a thin layer over X calls, the reasoning in this document also applies to raw X programming. I will list the X equivalents to Gdk calls between brackets, e.g. gdk_draw_line() [XDrawLine()]. The name of the function calls/structures may also be wrong, since I'm not on my Linux machine to write this...

So, we will consider three kinds of objects : the GdkImage [XImage], the GdkPixmap [XPixmap] and another player, the GdkImlibImage [ImlibImage].

What's a GdkImage ?

A GdkImage is an image (as you should have guessed) that resides in the adress space of your application. You can therefore manipulate it yourself. It has the same byte/bit organization that the video memory of the graphic mode your X server is currently using. That means you must have your own manipulation routines to be able to draw in it, for any mode the server may support (even the very exotic ones), or your app is basically broken.

Some people (read : game coders) therefore require that you put your server in a particular mode (e.g. 8bpp) before running their apps because they were unable to code the other modes (for speed or laziness reasons). But keep in mind this is not possible for everyone, since a given mode is not even guaranteed to exist on a particular platform.

Another problem with the GdkImage model is that at any time you want to update the screen, you must transfer the (whole ?) image in the server's adress space. When using X trough a network, or in the case of some old/broken servers, this has to be done using TCP/IP (uck !). However most of the new X servers support another way to transfer the image : it's the famous MITSHM (M.I.T. shared memory) extension to the X server. Note that even in this case your image has to be copied two times : first to the memory the two processes share, and then in the video memory.

Games like XQuake or XDoom do use XImages to do their work (however restricting you to a 8bpp server only - at least last time I checked), because their major task is the rendering of the image. When the calculated image is ready, they just have to flip it on the screen, and they're done.

What's a GtkPixmap

A GtkPixmap is a server side image. You can't manipulate its byte/bits directly since it is not in the adress space of your program (you cannot get a pointer to its data). These pixmaps are the main reason an X server can grow fairly large memory-wise : it contains every pixmap of every X app in its own memory. But don't be afraid, this is just displacing memory, since you no longer need it in your app, so there's no waste.

The advantage is that since the X server owns the pixmaps, you can use any gdk_draw_*() [XDraw*()] call on them. The server may even cache some pixmaps in video memory, and use the graphic board's accelerator to draw line/boxes on them (1). When the times comes to update the screen, the server only has to do one memory copy from the pixmap to the screen memory. If the pixmaps were already in video memory, the server could even use the board's accelerator (1).

Platform games (or any game that contains sprites) should use the pixmap approach : When starting, they transfer the sprites as pixmaps on the server, and then they only have to use gdk_draw_pixmap() [XDrawPixmap()] calls to put them on the screen. Also note that a pixmap using game should also perform very well over a network (or a broken/old X server that doesn't support SHM), since the only wire traffic consists in the X calls.

1) This is of course X server implementation dependant !

What's a GdkImlibImage ?

Well, this one has nothing to do with the previous ones. It doesn't even even belong to gdk or X, but rather comes from the gdk_imlib [Imlib] library. A GdkImlibImage basically stores an array of RGB triples describing an image (and also some private things). You can load various image formats into them using gdk_imlib, and you can render them to GtkPixmaps [XPixmaps] using gdk_imlib too (the latter takes care of the pixmap format, as well as palette and dithering issues).

Please don't even try to implement a game using GdkImlibImages ! While Raster's (Imlib's author) scaling/rendering code is Great Stuff[TM], the GdkImlibImages were not created for that purpose. Only imagine the CPU overhead of converting your RGB data to a screen image (including two memory copies in the shared memory's case - which is the better), using arbitrarily complex conversion/dithering bit manipulations (you don't know on what server you're running), the whole thing at 30+ fps!

Gdk version 1.1.2 (?) and higher also contain gdk_rgb*() calls to draw a rgb image on the screen. But it has to be used only when other methods are not possible (e.g. if the data comes from a RGB video source (1), you need to convert it anyway, so chances are gdk_rgb functions are already optimized for that).

1) Does such a thing exist ? Perhaps we'll need gdk_yuv*() calls ;).

Your very own case

[ Update : The aforementioned "very own case" was the creation of a small realtime strategy game like the well known StarCraft, Command & Conquer, etc. ]

In the case of a StarCr*ft-like game, I would suggest the following approach :

Say we are creating a new game, called Star Conquer. The two opposite camps are be the humans and the fromens (I put a lot of imagination into that one ;). The whole set of background tiles is stored in a file (tiles.png). All the sprites are stored in humans.png and fromens.png. Of course whe have a table describing at which coordinates we can find any frame in any file.

The game startup looks like :

Ok, now what we need is the main game loop. It should look like the following function :


/* NOTE: this is pseudo-but-looks-like-real-code,
   so cut'n'paste won't work :) */
void game_loop_do_one_iteration(void)
{
	/* tile the background */
	for(first_tile; last_tile; tile++) {
		tid = get_tile_from_map_at_coordinates(x, y);
		get_tile_coordinates_from_table(tid, tile_table, 
						&tile_x, &tile_y);
		gdk_draw_pixmap(double_buffer, scr_x, scr_y, tile_pixmap,
				tile_x, tile_y, TILE_WIDTH, TILE_HEIGHT);
	}
	/* draw the buddies */
	for(first_buddy; last_buddy; buddy++) {
		get_buddy_frame_from_table(buddy->type, buddy->frame++,
					   buddy->table, &buddy_x, &buddy_y,
					   &buddy_width, &buddy_height);
		/* of course, since buddies are not rectangular, there should
		   be a bit of masking, etc., but ... */
		gdk_draw_pixmap(double_buffer, buddy->pos_x, buddy->pos_y, 
				buddy->pixmap, buddy_x, buddy_y,
				buddy_width, buddy_height);
	}
 	/* update the screen */
	gdk_draw_pixmap(drawing_area->window, 0, 0, double_buffer, 0, 0,
			game_area_width, game_area_height);
	/* this will be the trickier part */
	move_buddies_with_at_least_a_bit_of_AI();
}

    

Then you connect that function to a timeout, and you're done. The AI part is left as an exercise to the reader :). There are some facts you should examine :

1) Since not hardcoding them would save me a lot of work when converting your game into a Kdemen vs Gnomes wargame - hmm ... pleasing scenario, I would be ROTFL :)

Conclusion

Well, I hope all this will be of use for somebody. Since these are questions that pop up very often, I hope this document will enlighten some X / Gdk 'newbies' (1) :).

Of course the function/structure names in this document may be wrong, since I don't do much X programming for now and I'm not in front of my Linux (2). box. But hey man, man is your man.

Feel free to correct this and distribute at will - even commercially and without releasing the source code (3) if you wish ... I'm also sorry for my bad english, but I would be glad to hear of corrections - that's the way I learn !

Cu,
Damien.

1) Note that I'm one of them, I have just collected pieces of information around.

2) Especially for RMS : read GNU/Linux :)

3) Oh my, I'm tired of these licence flamewars. [ Update : This document should only published unmodified (except with the author's permission) and accompanied by the contact information available. Any modification must be returned to the author. ]

Additional information

Please follow this link for information on how to contact me.

Return to index


dash@foobox.net