You likely want something graphical, where the things move. GTK version of the classic arcade game Asteroids.

/*
Name		GTK Asteroids -- asteroids style arcade game
Description	GTK version 30 Aug 2007 by TkTkorrovi in DaniWeb
		of the original Xasteroids version 5, 9 Feb 1993
Keys		Please see the function key_press_event
License		Copyright 1990 by Phil Goetz <goetz@cs.buffalo.edu>
		If you modify the game, feel free to post your version,
		provided that you retain the original copyright notice,
		the credits, and note which version yours was derived
		from and its release date, what changes you made, and
		your release date.
Contributors	Peter Phillips <pphillip@cs.ubc.ca>
		Pat Ryan <pat@jaameri.gsfc.nasa.gov>
		Craig Smith <csmith@cscs.UUCP>
		Doug Merritt <doug@netcom.com>
		Stephen McCamant <alias@mcs.com>
*/
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
 
#define M_BIG		8.0	/* masses */
#define M_MED		4.0
#define M_SMALL		1.0
#define M_SHIP		1.5
#define M_ENEMY		1.0
#define M_BULLET	0.1
#define	ENEMY		96	/* indexes for obj, the order */
#define ENEMYBUL	97	/* they are in is important */
#define	FBUL		98
#define	LASTBUL		102
#define	SHIP		103
#define LASTOBJ		103
#define LASTSHAPE	7
#define SHIPSIZE	28
#define BMAX		300	/* max particles in a boom */
#define LH		20	/* height of font */
#define	pi		3.1415926535897932384
enum {ASTSHAPE1, ASTSHAPE2, ASTSHAPE3, ENBULSH, BULSHAPE, SHIPSHAPE,
	SHIPTHRSHAPE, ENEMYSHAPE};
#define dot(a, b) (a.x * b.x + a.y * b.y) /* dot product */
#define rotinert(a) (obj [a].mass * shapesize [obj [a].shape] * \
	shapesize [obj [a].shape]) /* rotational inertia */
 
struct boomtype {
	struct boomtype *next;
	int dur;
	int part;
	double bcoord [BMAX] [2];
	double bvec [BMAX] [2];
} *blist;
struct {
	int shape;
	int alive;
	int time;
	double mass;
	double x, y;
	double xvel, yvel;
	double rot;
	double rotvel;
} obj [SHIP + 1];
struct { 		/* if you change shapes, change also shapesize */
	double angle;	/* 0 >, pi / 2 v, pi <, 3 * pi / 2 ^ */
	int length;	/* pixels */
} shapes [] [12] = { {
		{0, 0}, {3 * pi / 2, 40}, {0, 20}, {pi / 4, 28}, 
			{pi / 2, 40}, {3 * pi / 4, 28}, {pi, 40},
			{5 * pi / 4, 28}, {3 * pi / 2, 40},
			{7 * pi / 4, 28}, {0, 20}, {0, 0},
	}, {
		{0, 0}, {3 * pi / 2, 20}, {0, 10}, {pi / 4, 14},
			{pi / 2, 20}, {3 * pi / 4, 14}, {pi, 20},
			{5 * pi / 4, 14}, {3 * pi / 2, 20},
			{7 * pi / 4, 14}, {0, 10}, {0, 0},
	}, {
		{0, 0}, {3 * pi / 2, 10}, {0, 5}, {pi / 4, 7},
			{pi / 2, 10}, {3 * pi / 4, 7}, {pi, 10},
			{5 * pi / 4, 7}, {3 * pi / 2, 10},
			{7 * pi / 4, 7}, {0, 5}, {0, 0},
	}, {
		{0, 0}, {7 * pi / 4, 4}, {pi / 4, 4}, {3 * pi / 4, 4},
			{5 * pi / 4, 4}, {0, 0},
	}, {
		{0, 0}, {0, 10}, {0, 0},
	}, {
		{0, 0}, {5 * pi / 4, 28}, {0, 20}, {pi / 4, 28},
			{3 * pi / 4, 28}, {pi, 20},
			{7 * pi / 4, 28}, {0, 0},
	}, {
		{0, 0}, {5 * pi / 4, 28}, {0, 20}, {pi / 4, 28},
			{3 * pi / 4, 28}, {pi, 20}, {7 * pi / 4, 28},
			{3 * pi / 4, 7}, {9 * pi / 8, 13},
			{15 * pi / 8, 13}, {0, 0},
	}, {
		{0, 0}, {pi, 20}, {7 * pi / 4, 28}, {pi / 4, 28},
			{pi, 20}, {0, 0}
	}
};
 
double dscale = 1, speedscale = 1;
int width = 800, height = 600, delay = 8192, cntstart = 1, crashed, pausg,
	newship, quit, newgame, highscore, level, numasts, ships, score,
	shield, energy;
GdkPixmap *pixmap;
cairo_t *cr;
 
void linespec (double *e1, double *e2, int s, int n, double rot, double *a,
	double *b, double *c)
{
	e2 [0] = e1 [0] + shapes [s] [n].length * dscale *
		cos (shapes [s] [n].angle + rot);
	e2 [1] = e1 [1] + shapes [s] [n].length * dscale *
		sin (shapes [s] [n].angle + rot);
	*a = e2 [1] - e1 [1]; /* use classic line intersection algorithm */
	*b = e1 [0] - e2 [0];
	*c = *a * e1 [0] + *b * e1 [1];
}
 
int collide (int a, int b)
{	
	int diff, sa, sb, xd, yd, n, i, j;
	double a1 [2], a2 [2], b1 [2], b2 [2], xcross, aa, ba, ca, ab, bb,
	cb, det, shapesize [] = {44, 21, 10, 2, 1, SHIPSIZE + 1, 35, 20};
 
	sa = obj [a].shape;
	sb = obj [b].shape;
	xd = obj [a].x - obj [b].x;
	yd = obj [a].y - obj [b].y;
	diff = sqrt (xd * xd + yd * yd);
	if (diff >= (shapesize [sa] + shapesize [sb]) * dscale) return 0;
	if (b == SHIP && shield) return 1; /* shield collision */
	if (sa < SHIPSHAPE && sb < SHIPSHAPE) return 1; /* original game */
	a1 [0] = obj [a].x;
	a1 [1] = obj [a].y;
	for (i = 1; shapes [sa] [i].length; i++) {
		linespec (a1, a2, sa, i, obj [a].rot, &aa, &ba, &ca);
		b1 [0] = obj [b].x;
		b1 [1] = obj [b].y;
		for (j = 1; shapes [sb] [j].length; j++) {
			linespec (b1, b2, sb, j, obj [b].rot, &ab, &bb, &cb);
			if (!(det = aa * bb - ab * ba)) return 1;
			xcross = (bb * ca - ba * cb) / det;
			if ((xcross - a1 [0]) * (a2 [0] - xcross) > 0 &&
				(xcross - b1 [0]) * (b2 [0] - xcross) > 0)
				return 1; /* collision */
			for (n = 0; n < 2; n++) b1 [n] = b2 [n];
		}
		for (n = 0; n < 2; n++) a1 [n] = a2 [n];
	}
	return 0; /* no collision */
}
 
/*
|vapab| = |va| * sin (a) = |va x ab| / |ab|
|vapab| = |va| * cos (pi / 2 - a) = (va dot (perp. to ab)) / |ab|
(cross product when 3rd coord is 0)
mass (a) * va_ab + mass (b) * vb_ab = mass (a) * va_abf + mass (b) * vb_abf
va_ab + va_abf = vb_ab + vb_abf
(dividing eq for conservation of kinetic energy by eq for cons. of momentum)
*/
void bounce (int a, int b)
{
	double rotrat, temp;
	int shapesize [] = {44, 21, 10, 2, 1, SHIPSIZE + 1, 35, 20};
	struct {
		double x;
		double y;
		double mag;
	} ab, va, vb, /* vector from a to b, velocities */
		va_ab, vb_ab, vapab, vbpab, /* along, perpenticular to ab */
		va_abf, vb_abf; /* post-collision velocities along ab */
 
	va.x = obj [a].xvel;
	va.y = obj [a].yvel;
	vb.x = obj [b].xvel;
	vb.y = obj [b].yvel;
	ab.x = obj [b].x - obj [a].x;
	ab.y = obj [b].y - obj [a].y;
	ab.mag = sqrt (ab.x * ab.x + ab.y * ab.y);
	va_ab.mag = dot (va, ab) / ab.mag;	/* va_ab = va * cos (a) = */
	va_ab.x = (ab.x * va_ab.mag) / ab.mag;	/* (va dot ab) / |ab| */
	va_ab.y = (ab.y * va_ab.mag) / ab.mag;
	vb_ab.mag = dot (vb, ab) / ab.mag;
	vb_ab.x = (ab.x * vb_ab.mag) / ab.mag;
	vb_ab.y = (ab.y * vb_ab.mag) / ab.mag;
	if (va_ab.mag - vb_ab.mag < 0) return;
	temp = (va.y * ab.x - va.x * ab.y) / (ab.mag * ab.mag);
	vapab.x = -ab.y * temp;
	vapab.y = ab.x * temp;
	temp = (vb.x * ab.y - vb.y * ab.x) / (ab.mag * ab.mag);
	vbpab.x = -ab.y * temp;
	vbpab.y = ab.x * temp;
	temp = obj [a].mass / obj [b].mass;
	vb_abf.x = (temp * (2 * va_ab.x - vb_ab.x) + vb_ab.x) / (1 + temp);
	vb_abf.y = (temp * (2 * va_ab.y - vb_ab.y) + vb_ab.y) / (1 + temp);
	va_abf.x = vb_abf.x + vb_ab.x - va_ab.x;
	va_abf.y = vb_abf.y + vb_ab.y - va_ab.y;
	obj [a].xvel = va_abf.x + vapab.x;
	obj [a].yvel = va_abf.y + vapab.y;
	obj [b].xvel = vb_abf.x + vbpab.x;
	obj [b].yvel = vb_abf.y + vbpab.y;
	rotrat = rotinert (a) / rotinert (b);
	temp = rotrat * (2 * obj [a].rotvel - obj [b].rotvel) / (1 + rotrat);
	obj [a].rotvel = temp + obj [b].rotvel - obj [a].rotvel;
	obj [b].rotvel = temp; /* rotational velocity exchange */
}
 
void boom (int ob, int particles, int duration)
{ 
	int i;
	struct boomtype *b;
	double angle, length, x, y;
 
	b = malloc (sizeof (struct boomtype));
	b->dur = 5 * duration;
	b->part = particles;
	x = obj [ob].x;
	y = obj [ob].y;
	for (i = 0; i < particles; i++) {
		b->bcoord [i] [0] = x;
		b->bcoord [i] [1] = y;
		angle = (rand () % 100) * pi / 50.0;
		length = 3 + rand () % 7;
		b->bvec [i] [0] = cos (angle) * length + obj [ob].xvel;
		b->bvec [i] [1] = sin (angle) * length + obj [ob].yvel;
	}
	b->next = blist;
	blist = b;
}
 
void newast (int *a, int *b)
{
	for (*b = 0; obj [*b].alive; (*b)++);
	obj [*b].x = obj [*a].x;
	obj [*b].y = obj [*a].y;
	obj [*b].xvel = obj [*a].xvel;
	obj [*b].yvel = obj [*a].yvel;
	obj [*b].alive++;
}
 
void upscore (int killer, int up)
{	
	if (killer != ENEMYBUL && killer != SHIP) score += up;
}
 
void blastpair (int a, int b)
{	
	int n;
 
	n = rand () % 64;
	obj [a].xvel = obj [a].xvel + cos (n); /* add random */
	obj [a].yvel = obj [a].yvel + sin (n); /* velocity vector */
	obj [b].xvel = obj [b].xvel - cos (n);
	obj [b].yvel = obj [b].yvel - sin (n);
	obj [a].rotvel = obj [a].rotvel + 0.05;
	obj [b].rotvel = obj [b].rotvel - 0.05;
}
 
void killasteroid (int killer, int a)
{	
	int b = 0, oldb, i;
 
	if (obj [a].shape == ASTSHAPE1) {
		newast (&a, &b);
		obj [b].shape = ASTSHAPE2;
		obj [b].mass = M_MED;
		obj [a].shape = ASTSHAPE2;
		obj [a].mass = M_MED;
		blastpair (a, b);
		boom (a, 30, 12);
		numasts++;
		if (numasts == 2) /* big asteroid was last */
			upscore (killer, 25 + level * 2000);
		else
			upscore (killer, 25);
	} else if (obj [a].shape == ASTSHAPE2) {
		for (i = 0; i < 3; i++) {
			oldb = b;
			newast (&a, &b);
			obj [b].shape = ASTSHAPE3;
			obj [b].mass = M_SMALL;
			if (i == 1) blastpair (oldb, b);
		}
		obj [a].shape = ASTSHAPE3;
		obj [a].mass = M_SMALL;
		blastpair (b, a);
		boom (a, 20, 10);
		if (numasts == 1) upscore (killer, 500 * level);
		numasts = numasts + 3;
		upscore (killer, 50);
	} else if (obj [a].shape == ASTSHAPE3) {
		boom (a, 10, 8);
		obj [a].alive = 0;
		numasts--;
		upscore (killer, 100);
	} else { /* enemy (ship or bullet) */
		boom (a, 9, 7);
		obj [a].alive = 0;
		upscore (killer, 500);
	}
}
 
void moveobjs ()
{	
	int i, j;
	double *temp;
	struct boomtype *prevb, *b;
 
	prevb = blist;
	for (b = blist; b; )
		if (--b->dur) { /* move */
			for (i = 0; i < b->part; i++) 
				for (j = 0; j < 2; j++)
					b->bcoord [i] [j] += 0.2 * 
						b->bvec [i] [j];
			prevb = b;
			b = b->next;
		} else if (b != blist) { /* delete */
			prevb->next = b->next;
			free (b);
			b = prevb->next;
		} else if (b->next) {
			blist = b->next;
			free (b);
			prevb = blist;
			b = prevb->next;
		} else {
			blist = NULL;
			free (b);
			b = NULL;
		}
	for (i = 0; i < LASTOBJ + 1; i++) {
		if (!obj [i].alive) continue;
		temp = &obj [i].x;
		*temp = *temp + obj [i].xvel * speedscale;
		while (*temp < 0) *temp = *temp + width;
		while (*temp > width) *temp = *temp - width;
		temp = &obj [i].y;
		*temp = *temp + obj [i].yvel * speedscale;
		while (*temp < 0) *temp = *temp + height;
		while (*temp > height) *temp = *temp - height;
		obj [i].rot = obj [i].rot + obj [i].rotvel;
	}
	for (i = 0; i < FBUL; i++) {
		if (!obj [i].alive) continue;
		if (obj [SHIP].alive && collide (i, SHIP)) {
			if (shield)
				bounce (SHIP, i);
			else {
				crashed = 1;
				ships--;
				obj [SHIP].alive = 0;
				killasteroid (SHIP, i);
				continue;
			}
		}
		for (j = ENEMYBUL; j < LASTBUL + 1; j++)
			if (obj [j].alive && collide (i, j) &&
				(j != ENEMYBUL ||
				(i != ENEMYBUL && i != ENEMY))) {
				obj [j].alive = 0; /* kill bullet */
				if (obj [i].alive) killasteroid (j, i);
			} /* not 2 bullets for one asteroid */
	}
}
 
void drawframe (GtkWidget *da, PangoLayout *text)
{
	int shape, i, j;
	char buf [FILENAME_MAX];
	double angle, length, rot;
	struct boomtype *b;
 
	cairo_set_line_width (cr, 0.5);
	cairo_set_source_rgb (cr, 1, 1, 1);
	cairo_paint (cr);
	cairo_set_source_rgb (cr, 0, 0, 0.57);
	cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
	for (b = blist; b; b = b->next)
		for (i = 0; i < b->part; i++) {
			cairo_move_to (cr, b->bcoord [i] [0],
				b->bcoord [i] [1]);
			cairo_rel_line_to (cr, 1, 1);
		}
	cairo_stroke (cr);
	cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
	if (shield && obj [SHIP].alive)
		cairo_arc (cr, obj [SHIP].x, obj [SHIP].y, 
			abs (dscale * SHIPSIZE), 0, pi * 2);
	for (i = 0; i <= LASTOBJ; i++) {
		if (!obj [i].alive) continue;
		shape = obj [i].shape;
		rot = obj [i].rot;
		cairo_move_to (cr, obj [i].x, obj [i].y);
		for (j = 1; shapes [shape] [j].length; j++) {
			angle = shapes [shape] [j].angle + rot;
			length = shapes [shape] [j].length * dscale;
			cairo_rel_line_to (cr, length * cos (angle),
				length * sin (angle));
		}
		cairo_stroke (cr);
	}
	pango_layout_set_text (text, "       GTK version\n\n"
		"by TkTkorrovi in DaniWeb\n\n      of Xasteroids", -1);
	cairo_move_to (cr, width / 2 - 120, height / 2 - 3 * LH);
	if (!ships) pango_cairo_show_layout (cr, text);
	cairo_set_source_rgb (cr, 1, 1, 1);
	cairo_rectangle (cr, 0, height - LH, width, height);
	cairo_fill (cr);
	cairo_set_source_rgb (cr, 0, 0, 0.57);
	cairo_rectangle (cr, 340, height - LH + 6, energy >> 1, 10);
	cairo_fill (cr);
	sprintf (buf, "Ships:%2d   Score:%6d   Shield:        High:%6d",
		ships, score, highscore);
	pango_layout_set_text (text, buf, -1);
	cairo_move_to (cr, 0, height - LH);
	pango_cairo_show_layout (cr, text);
	gtk_widget_queue_draw_area (da, 0, 0, width, height);
} /* finally order expose event, only there we can draw to window */
 
void makeasts ()
{
	int i, n;
 
	for (i = 0; i < SHIP; i++) obj [i].alive = 0; /* except ship */
	for (i = ENEMYBUL; i < LASTBUL + 1; i++) obj [i].time = 0;
	for (i = 0; i < (level > 8 ? 8 : level + 4); i++) {
		n = rand () % 128;
		obj [i].x = n > 63 ? n : width - n;
		n = rand () % 128;
		obj [i].y = n > 63 ? n : height - n;
		obj [i].rot = rand () % 8; /* ??? */
		obj [i].rotvel = (rand () % 256) / 2048.;
		n = rand () % 256;
		obj [i].xvel = cos (n);
		obj [i].yvel = sin (n);
		obj [i].shape = ASTSHAPE1;
		obj [i].mass = M_BIG;
		obj [i].alive = 1;
	}
	numasts = i;
}
 
void fire ()
{	
	double *shiprot, cosrot, sinrot;
	static int nextbul = FBUL;
 
	obj [nextbul].alive++;
	shiprot = &obj [SHIP].rot;
	cosrot = cos (*shiprot);
	sinrot = sin (*shiprot);
	obj [nextbul].x = obj [SHIP].x + 20 * cosrot * dscale;
	obj [nextbul].y = obj [SHIP].y + 20 * sinrot * dscale;
	obj [nextbul].xvel = obj [SHIP].xvel + 10 * cosrot;
	obj [nextbul].yvel = obj [SHIP].yvel + 10 * sinrot;
	obj [nextbul].rot = *shiprot;
	obj [nextbul].time = width / (speedscale * 11) / 2;
	if (++nextbul == LASTBUL + 1) nextbul = FBUL;
}
 
void loop (GtkWidget *da, PangoLayout *text)
{
	double dist, dx, dy;
	int enemycount = 20, counter = 0, i;
 
	while (numasts && !newship && !newgame && !quit) {
		for (i = FBUL; i < LASTBUL + 1; i++)
			if (obj [i].alive)
				if (!--obj [i].time)
					obj [i].alive = 0;
		if (pausg)
			for (g_usleep (delay);
				g_main_context_iteration (NULL, 0); );
		if (newship || newgame || pausg || quit) continue;
		moveobjs ();
		if (shield) energy--;
		if (!energy) shield = 0;
		if (!ships) obj [SHIP].alive = 0;
		if (score > highscore) highscore = score;
		if (!counter) {
			counter = cntstart;
			if (crashed) {
				crashed--;
				boom (SHIP, BMAX, 70);
			}
			drawframe (da, text);
		}
		counter--;
		i = rand () % 256;
		if (!obj [ENEMY].alive) {
			if (i < level && rand () % 256 < level * 10) {
				obj [ENEMY].alive = 1;
				obj [ENEMY].x = 0;
				obj [ENEMY].y = height / 4 + rand () % 256;
				obj [ENEMY].xvel = 0.4 + level / 3.;
				obj [ENEMY].yvel = 0;
				obj [ENEMY].rot = 0;
				obj [ENEMY].rotvel = 0;
			}
		} else {
			i += obj [SHIP].y > obj [ENEMY].y ?
				level >> 1 : -(level >> 1);
			obj [ENEMY].yvel += i > 128 + 6 * obj [ENEMY].yvel ?
				.5 : -.5;
		}
		if (!--enemycount) {
			enemycount = 100;
			if (obj [ENEMY].alive) {
				obj [ENEMYBUL].alive++;
				obj [ENEMYBUL].x = obj [ENEMY].x;
				obj [ENEMYBUL].y = obj [ENEMY].y;
				dx = obj [SHIP].x - obj [ENEMY].x;
				dy = obj [SHIP].y - obj [ENEMY].y;
				dist = sqrt (dx * dx + dy * dy);
				obj [ENEMYBUL].xvel = 3 * dx / dist;
				obj [ENEMYBUL].yvel = 3 * dy / dist;
			} else
				obj [ENEMYBUL].alive = 0;
		}
		for (g_usleep (delay); g_main_context_iteration (NULL, 0); );
	}
}
 
gint key_press_event (GtkWidget *widget, GdkEventKey *event)
{
	if (shield) return 1;
	switch (event->keyval) {
		case GDK_Left: /* rotate counterclockwise */
		case GDK_e:
			obj [SHIP].rotvel = -.1;
			break;
		case GDK_Right: /* rotate clockwise */
		case GDK_r:
			obj [SHIP].rotvel = .1;
			break;
		case GDK_w: /* rotate 45 degrees counterclockwise */
			obj [SHIP].rot -= pi / 4;
			break;
		case GDK_t: /* rotate 45 degrees clockwise */
			obj [SHIP].rot += pi / 4;
			break;
		case GDK_d: /* increase ccwise rotational velocity */
			obj [SHIP].rotvel = obj [SHIP].rotvel - .02;
			break;
		case GDK_f: /* increase clockwise rotational velocity */
			obj [SHIP].rotvel = obj [SHIP].rotvel + .02;
			break;
		case GDK_Up: /* thrust */
		case GDK_o:
			obj [SHIP].xvel += cos (obj [SHIP].rot);
			obj [SHIP].yvel += sin (obj [SHIP].rot);
			obj [SHIP].shape = SHIPTHRSHAPE;
			break;
		case GDK_Control_L: /* fire */
		case GDK_Control_R:
		case GDK_p:
			if (obj [SHIP].alive) fire ();
			break;
		case GDK_space: /* hyperspace */
			obj [SHIP].x = rand () % width;
			obj [SHIP].y = rand () % height;
			break;
		case GDK_Down: /* shield */
		case GDK_grave:
			if (energy) shield = 1;
			break;
		case GDK_period: /* decrease delay -- speed game up */
			if (delay > 1) delay >>= 1;
			break;
		case GDK_comma: /* increase delay */
			if (delay < 500000l) delay <<= 1;
			break;
		case GDK_m: /* decrease drawscale -- may go negative */
			dscale -= .1;
			break;
		case GDK_n: /* increase drawscale */
			dscale += .1;
			break;
		case GDK_2: /* increase speedscale */
			speedscale += .1;
			break;
		case GDK_1: /* decrease speedscale */
			speedscale -= .1;
			break;
		case GDK_b: /* increase moves/update */
			if (cntstart < 10000) cntstart++;
			break;
		case GDK_v: /* decrease moves/update */
			if (cntstart > 1) cntstart--;
			break;
		case GDK_Escape: /* pause */
			pausg = 1 - pausg;
			break;
		case GDK_plus: /* cheat */
			ships++;
			break;
		case GDK_q: /* quit */
			quit = 1;
			break;
		case GDK_s: /* new ship */
			if (obj [SHIP].alive) break;
			if (!ships) newgame = 1;
			if (ships) newship = 1;
	}
	return 1;
}
 
gint key_release_event (GtkWidget *widget, GdkEventKey *event)
{
	switch (event->keyval) {
		case GDK_Left:
		case GDK_e:
		case GDK_Right:
		case GDK_r:
			obj [SHIP].rotvel = 0;
			break;
		case GDK_Up:
		case GDK_o:
			obj [SHIP].shape = SHIPSHAPE;
			break;
		case GDK_Down:
		case GDK_grave:
			shield = 0;
	}
	return 1;
}
 
static gboolean expose_event (GtkWidget *widget, GdkEventExpose *event)
{
	gdk_draw_drawable (widget->window, widget->style->white_gc, pixmap,
		event->area.x, event->area.y, event->area.x, event->area.y,
		event->area.width, event->area.height);
	return 0;
}
 
static gboolean configure_event (GtkWidget *widget, GdkEventConfigure *event)
{
	width = widget->allocation.width;
	height = widget->allocation.height;
	if (pixmap) g_object_unref (pixmap);
	if (cr) cairo_destroy (cr);
	pixmap = gdk_pixmap_new (widget->window, width, height, -1);
	cr = gdk_cairo_create (pixmap);
	return 1;
}
 
gint shut ()
{
	quit = 1;
	return 0;
}
 
int main (int argc, char **argv)
{
	int i;
	struct boomtype *b;
	GtkWidget *window, *da;
	PangoFontDescription *font;
	PangoLayout *text;
 
	gtk_init (&argc, &argv);
	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_widget_set_size_request (window, width, height);
	gtk_window_set_title (GTK_WINDOW (window), "GTK Asteroids");
	gtk_window_set_resizable (GTK_WINDOW (window), 1);
	da = gtk_drawing_area_new ();
	gtk_container_add (GTK_CONTAINER (window), da);
	gtk_widget_set_events (da, GDK_EXPOSURE_MASK);
	gtk_widget_set_events (window, GDK_KEY_PRESS_MASK | 
		GDK_KEY_RELEASE_MASK);
	g_signal_connect (window, "delete_event", G_CALLBACK (shut), NULL);
	g_signal_connect (G_OBJECT (window), "key_press_event",
		G_CALLBACK (key_press_event), NULL);
	g_signal_connect (G_OBJECT (window), "key_release_event",
		G_CALLBACK (key_release_event), NULL);
	g_signal_connect (G_OBJECT (da), "expose_event",
		G_CALLBACK (expose_event), NULL);
	g_signal_connect (G_OBJECT (da),"configure_event",
		G_CALLBACK (configure_event), NULL);
	gtk_widget_show_all (window); /* before creating pixmap, gc etc */
	text = gtk_widget_create_pango_layout (da, "");
	font = pango_font_description_from_string ("monospace bold");
	pango_font_description_set_absolute_size (font, 16 * PANGO_SCALE);
	pango_layout_set_font_description (text, font);
	srand (time (0));
	obj [SHIP].shape = SHIPSHAPE;
	obj [SHIP].mass = M_SHIP;
	obj [ENEMY].shape = ENEMYSHAPE;
	obj [ENEMY].mass = M_ENEMY;
	obj [ENEMYBUL].shape = ENBULSH;
	obj [ENEMYBUL].mass = M_BULLET;
	for (i = FBUL; i < LASTBUL + 1; i++) {
		obj [i].shape = BULSHAPE;
		obj [i].mass = M_BULLET;
	}
	while (!quit) {
		newgame = 0; /* newgame */
		ships = 3;
		score = 0;
		for (level = 0; ;) {
			if (!newship) {
				if (level < 15) level++;
				makeasts ();
			}
			newship = 0; /* newship */
			if (!obj [SHIP].alive) {
				obj [SHIP].x = width / 2;
				obj [SHIP].y = (height - LH) / 2;
				obj [SHIP].xvel = 0;
				obj [SHIP].yvel = 0;
				obj [SHIP].rot = 3 * pi / 2;
				obj [SHIP].rotvel = 0;
				energy = 80;
				shield = 0;
			}
		 	obj [SHIP].alive = (ships > 0);
			crashed = 0;
			loop (da, text);
			if (newgame || quit) break;
		}
	}
	for (b = blist; b; b = b->next) b->dur = 1;
	moveobjs ();
	printf ("Your high score was %d\n", highscore);
	pango_font_description_free (font);
	cairo_destroy (cr);
	g_object_unref (pixmap);
	g_object_unref (text);
	return 0;
}

Thanks to Samreid, in #DaniWeb irc channel, for testing this code in Debian. Based on that, you should have GTK development package installed in Linux, to compile GTK applications. If something is wrong, or you are not sure, run "apt-get install libgtk2.0-dev" in Debian, guess this should also work in Ubuntu. It also appeared useful to change the font in the code, from "dejavu sans mono bold 12" to "mono bold 12", because not all have dejavu font installed, or for some reason that font doesn't work, and the result is the default size, which is usually smaller and would cause incorrect placement of text.

I'm glad that you like it :) There is a new version up now, but i don't think that i would change it again, as i don't know any more what to make better... This once very popular vector asteroids game is likely not good as a game any more, but now it can serve as a programming example. Because GTK is a cross-platform library, this code compiles exactly as it is in linux, mingw (windows), and likely also in mac and other platforms.

Who argues that the code in this game is not that good, it's right, it isn't. But then one may look at the debian source package, how much more horrible it was... What would help here is writing the whole thing from scratch, which is not that difficult and likely would also result in much shorter code. But this is not what i'm going to do because my aim was only to port that game from xlib to gtk, which is simple at least because the graphics functions of the two are almost the same, i mean the standard gdk functions. At least i who almost never have to use such graphics functions, would never need any more advanced functions from cairo or such.

I have a new version which uses cairo, the graphics is antialiased, and no graphics contexts nowhere... It would be up when i can put it up. I honestly don't see now anything to make better, if someone thinks that anythyng is wrong, then please say... The new version is somewhat slow in windows, i think antialiasing is badly implemented there, if it's slow for you also, take the antialiasing off, no such problems in linux.