1 /*
2  * Copyright (C) 1998 Janne Löf <jlof@mail.student.oulu.fi>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 
20 /* Zktor is a word that does not mean anything and is difficult to pronounce. */
21 /* I apologize for horrible coding. */
22 
23 
24 #include <math.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <glib.h>
28 #include <gtk/gtk.h>
29 #include <gdk/gdk.h>
30 #include <gdk/gdkkeysyms.h>
31 #include <gtkgl/gtkglarea.h>
32 #include <GL/gl.h>
33 #include <GL/glu.h>
34 
35 #ifndef M_PI
36 #define M_PI 3.14
37 #endif
38 
39 
40 #if 0
41 #define FULLSCREEN_MESA_3DFX /* uncomment this to get 3DFX acceleration */
42 #endif
43 
44 #ifdef FULLSCREEN_MESA_3DFX
45 #include <GL/xmesa.h>
46 #endif
47 
48 
49 
50 typedef struct {
51   int   state; /* zero state means dead */
52   float timer;
53   float pos_x,pos_y;
54   float vel_x,vel_y;
55   float dir;
56   float radius;
57 } Entity;
58 
59 /* game state */
60 
61 GTimer *gtimer = NULL;
62 
63 double game_time;
64 double game_tick;
65 int draw_fast = 0;
66 
67 int wave_cnt;
68 double wave_time;
69 double vortex_time;
70 
71 int control_speed;
72 int control_spin;
73 int control_fire;
74 
75 int score = 0;
76 int highscore = 0;
77 
78 Entity player;
79 Entity enemy[10];
80 Entity vortex[10];
81 Entity p_bullet[20];
82 Entity e_bullet[20];
83 Entity particle[60];
84 
85 GLuint fontbase = 0;
86 
87 
rnd()88 float rnd()
89 {
90   return (1.0*rand()/(RAND_MAX+1.0));
91 }
collision(const Entity * a,const Entity * b)92 int collision(const Entity *a, const Entity *b)
93 {
94   if (a->state && b->state) {
95     float dx = a->pos_x  - b->pos_x;
96     float dy = a->pos_y  - b->pos_y;
97     float r  = a->radius + b->radius;
98     if (dx*dx+dy*dy < r*r)
99       return TRUE;
100   }
101   return FALSE;
102 }
103 
gCircle(float radius,int points)104 void gCircle(float radius, int points)
105 {
106   float a,step = 360.0/points;
107   for (a = 0; a < 360.0; a += step) {
108     float dx = -sin(a*M_PI/180);
109     float dy =  cos(a*M_PI/180);
110     glVertex2f(dx*radius,dy*radius);
111   }
112 }
113 
114 
game_init()115 void game_init()
116 {
117   int i;
118 
119   if (!gtimer)
120     gtimer = g_timer_new();
121   g_timer_reset(gtimer);
122 
123   game_time = g_timer_elapsed(gtimer, NULL);
124   game_tick  = 1.0 / 60;
125 
126   wave_cnt = 0;
127   wave_time = game_time + 5; /* give 5 secs before start of waves */
128   vortex_time = game_time + 3; /* give 3 secs before 1st vortex */
129 
130 
131   control_speed = 0;
132   control_spin  = 0;
133   control_fire  = 0;
134 
135   score = 0;
136 
137   player.state = 1;
138   player.timer = game_time;
139   player.pos_x = 0;
140   player.pos_y = 0;
141   player.vel_x = 0;
142   player.vel_y = 0;
143   player.dir   = 0;
144   player.radius= 5;
145 
146 
147   for (i=0; i<sizeof(enemy)/sizeof(Entity); i++)
148     enemy[i].state = 0;
149 
150   for (i=0; i<sizeof(vortex)/sizeof(Entity); i++)
151     vortex[i].state = 0;
152 
153   for (i=0; i<sizeof(p_bullet)/sizeof(Entity); i++)
154     p_bullet[i].state = 0;
155 
156   for (i=0; i<sizeof(e_bullet)/sizeof(Entity); i++)
157     e_bullet[i].state = 0;
158 }
game_play()159 void game_play()
160 {
161   int i;
162   double time_now,tick_now;
163 
164   /* timing */
165   time_now = g_timer_elapsed(gtimer, NULL);
166   tick_now = time_now - game_time;
167   if (tick_now < 0.001) tick_now = 0.001;
168   if (tick_now > 0.2  ) tick_now = 0.2;
169   game_tick = (tick_now + 4*game_tick)/5; /* average */
170   game_time = time_now;
171 
172 
173   /* is it time for next wave? */
174   if (player.state && wave_time <= game_time) {
175     wave_time = game_time + 20; /* 20 second waves */
176     wave_cnt++;
177     for (i=0; i<wave_cnt; i++) {
178       int j;
179       for (j=0; j<sizeof(enemy)/sizeof(Entity); j++) {
180 	if (!enemy[j].state) {
181 	  enemy[j].radius = 50;
182 	  do {
183 	    enemy[j].pos_x = rnd()*200 - 100;
184 	    enemy[j].pos_y = rnd()*200 - 100;
185 	  } while (collision(&enemy[j], &player));
186 	  enemy[j].state = 1;
187 	  enemy[j].timer = game_time;
188 	  enemy[j].vel_x = 0;
189 	  enemy[j].vel_y = 0;
190 	  enemy[j].dir = 360*rnd()-180;
191 	  enemy[j].radius = 5;
192 	  break;
193 	}
194       }
195     }
196   }
197 
198 
199 
200   /* player */
201   if (player.state) {
202     float dx,dy;
203 
204     /* turn to direction given by spin control */
205     player.dir   += 180 * control_spin  * game_tick;
206     while (player.dir > 180) player.dir -= 360;
207     while (player.dir <-180) player.dir += 360;
208     /* unit direction vector */
209     dx = -sin(player.dir*(M_PI/180));
210     dy =  cos(player.dir*(M_PI/180));
211     /* accelerate if speed control is pressed */
212     player.vel_x += 50 * control_speed * dx * game_tick;
213     player.vel_y += 50 * control_speed * dy * game_tick;
214     /* move */
215     player.pos_x += player.vel_x * game_tick;
216     player.pos_y += player.vel_y * game_tick;
217     /* collision to border */
218     if (player.pos_x < -100) {
219       player.pos_x = -100;
220       player.vel_x = 0;
221     }
222     if (player.pos_x > 100) {
223       player.pos_x = 100;
224       player.vel_x = 0;
225     }
226     if (player.pos_y < -100) {
227       player.pos_y = -100;
228       player.vel_y = 0;
229     }
230     if (player.pos_y > 100) {
231       player.pos_y = 100;
232       player.vel_y = 0;
233     }
234     /* if fire is pressed and 0.2 secs has elapsed since last bullet fired */
235     if (control_fire && (game_time - player.timer) > 0.2  ) {
236       for (i=0; i<sizeof(p_bullet)/sizeof(Entity); i++) {
237 	if (!p_bullet[i].state) {
238 	  player.timer = game_time;
239 	  p_bullet[i].state = 1;
240 	  p_bullet[i].timer = game_time;
241 	  p_bullet[i].pos_x = player.pos_x + player.radius*dx;
242 	  p_bullet[i].pos_y = player.pos_y + player.radius*dy;
243 	  p_bullet[i].vel_x = player.vel_x + dx*100;
244 	  p_bullet[i].vel_y = player.vel_y + dy*100;
245 	  p_bullet[i].dir   = player.dir;
246 	  p_bullet[i].radius= .5;
247 	  break;
248 	}
249       }
250     }
251 
252     /* if speed control is pressed create more particles */
253     if (control_speed) {
254       for (i=0; i<sizeof(particle)/sizeof(Entity); i++) {
255 	if (!particle[i].state) {
256 	  float spread = rnd()*15 - 7.5;
257 	  particle[i].state = 1;
258 	  particle[i].timer = game_time + rnd()*.8 + .2;
259 	  particle[i].pos_x = player.pos_x - player.radius*dx;
260 	  particle[i].pos_y = player.pos_y - player.radius*dy;
261 	  particle[i].vel_x = player.vel_x - dx*25 - dy * spread;
262 	  particle[i].vel_y = player.vel_y - dy*25 + dx * spread;
263 	  particle[i].dir   = player.dir;
264 	  particle[i].radius= .1;
265 	  break;
266 	}
267       }
268     }
269 
270 
271   } else {
272     /* enemies continue to chase player position even if player is dead
273        - make it center of screen if player is dead */
274     player.pos_x = 0;
275     player.pos_y = 0;
276   }
277 
278 
279   /* enemy */
280   for (i=0; i<sizeof(enemy)/sizeof(Entity); i++) {
281     if (enemy[i].state) {
282       float x,y,dx,dy,a;
283       int j;
284 
285       /* distance to player */
286       x = player.pos_x - enemy[i].pos_x;
287       y = player.pos_y - enemy[i].pos_y;
288       /* calculate signed angle to player */
289       a = enemy[i].dir + atan2(x,y) * 180/M_PI;
290       while (a > 180) a-=360;
291       while (a <-180) a+=360;
292       /* turn towards player */
293       if (a < -3) {
294 	enemy[i].dir += 90 * game_tick;
295 	while (enemy[i].dir > 180) enemy[i].dir -= 360;
296       }
297       if (a > 3) {
298 	enemy[i].dir -= 90 * game_tick;
299 	while (enemy[i].dir <-180) enemy[i].dir += 360;
300       }
301       /* unit direction vector */
302       dx = -sin(enemy[i].dir*(M_PI/180));
303       dy =  cos(enemy[i].dir*(M_PI/180));
304       /* accelerate if player is ahead */
305       if (fabs(a) < 10) {
306 	enemy[i].vel_x += 20*dx * game_tick;
307 	enemy[i].vel_y += 20*dy * game_tick;
308       }
309       /* move */
310       enemy[i].pos_x += enemy[i].vel_x * game_tick;
311       enemy[i].pos_y += enemy[i].vel_y * game_tick;
312       /* collision to border */
313       if (enemy[i].pos_x < -100) {
314 	enemy[i].pos_x = -100;
315 	enemy[i].vel_x = 0;
316       }
317       if (enemy[i].pos_x > 100) {
318 	enemy[i].pos_x = 100;
319 	enemy[i].vel_x = 0;
320       }
321       if (enemy[i].pos_y < -100) {
322 	enemy[i].pos_y = -100;
323 	enemy[i].vel_y = 0;
324       }
325       if (enemy[i].pos_y > 100) {
326 	enemy[i].pos_y = 100;
327 	enemy[i].vel_y = 0;
328       }
329       /* fire if player is alive and ahead */
330       if (player.state && (fabs(a) < 20) && (game_time - enemy[i].timer) > .6) {
331 	for (j=0; j<sizeof(e_bullet)/sizeof(Entity); j++) {
332 	  if (!e_bullet[j].state) {
333 	    enemy[i].timer = game_time;
334 	    e_bullet[j].state = 1;
335 	    e_bullet[j].timer = game_time;
336 	    e_bullet[j].pos_x = enemy[i].pos_x + enemy[i].radius*dx;
337 	    e_bullet[j].pos_y = enemy[i].pos_y + enemy[i].radius*dy;
338 	    e_bullet[j].vel_x = enemy[i].vel_x + dx*60;
339 	    e_bullet[j].vel_y = enemy[i].vel_y + dy*60;
340 	    e_bullet[j].dir   = enemy[i].dir;
341 	    e_bullet[j].radius= .5;
342 	    break;
343 	  }
344 	}
345       }
346     }
347   }
348 
349 
350   /* vortex */
351   for (i=0; i<sizeof(vortex)/sizeof(Entity); i++) {
352     if (vortex[i].state) {
353       int j;
354       /* kill this vortex, time is up */
355       if (vortex[i].timer < game_time) {
356 	vortex[i].state = 0;
357 	continue;
358       }
359       /* move */
360       vortex[i].dir   += 60  * game_tick;
361       vortex[i].pos_x += vortex[i].vel_x * game_tick;
362       vortex[i].pos_y += vortex[i].vel_y * game_tick;
363       /* collision to border */
364       if (vortex[i].pos_x < -100) {
365 	vortex[i].pos_x = -200 - vortex[i].pos_x;
366 	vortex[i].vel_x = -vortex[i].vel_x;
367       }
368       if (vortex[i].pos_x > 100) {
369 	vortex[i].pos_x = 200 - vortex[i].pos_x;
370 	vortex[i].vel_x = -vortex[i].vel_x;
371       }
372       if (vortex[i].pos_y < -100) {
373 	vortex[i].pos_y = -200 - vortex[i].pos_y;
374 	vortex[i].vel_y = -vortex[i].vel_y;
375       }
376       if (vortex[i].pos_y > 100) {
377 	vortex[i].pos_y =  200 - vortex[i].pos_y;
378 	vortex[i].vel_y = -vortex[i].vel_y;
379       }
380 
381       /* shake player */
382       if (collision(&vortex[i], &player)) {
383 	player.vel_x += (rnd()*500 - 250) * game_tick;
384 	player.vel_y += (rnd()*500 - 250) * game_tick;
385 	player.dir   += (rnd()*180 -  90) * game_tick;
386       }
387       /* shake enemy */
388       for (j=0; j<sizeof(enemy)/sizeof(Entity); j++) {
389 	if (collision(&vortex[i], &enemy[j])) {
390 	  enemy[j].vel_x += (rnd()*500 - 250) * game_tick;
391 	  enemy[j].vel_y += (rnd()*500 - 250) * game_tick;
392 	  enemy[i].dir   += (rnd()*180 -  90) * game_tick;
393 	}
394       }
395 
396     } else if (vortex_time < game_time) {
397       vortex_time = game_time + rnd()*10 + 5;
398       do {
399 	vortex[i].pos_x  = 200*rnd()-100;
400 	vortex[i].pos_y  = 200*rnd()-100;
401 	vortex[i].radius = rnd()*10 + 10;
402       } while (collision(&vortex[i], &player));
403       vortex[i].state = 1;
404       vortex[i].timer = game_time + rnd()*15 + 5;
405       do vortex[i].vel_x = rnd()*30 - 15; while (fabs(vortex[i].vel_x) < 10);
406       do vortex[i].vel_y = rnd()*30 - 15; while (fabs(vortex[i].vel_y) < 10);
407       vortex[i].dir = 0;
408     }
409   }
410 
411 
412   /* p_bullet */
413   for (i=0; i<sizeof(p_bullet)/sizeof(Entity); i++) {
414     if (p_bullet[i].state) {
415       int j;
416       /* move */
417       p_bullet[i].pos_x += p_bullet[i].vel_x * game_tick;
418       p_bullet[i].pos_y += p_bullet[i].vel_y * game_tick;
419       /* collision to border kills bullet */
420       if (p_bullet[i].pos_x < -115 ||
421 	  p_bullet[i].pos_x >  115 ||
422 	  p_bullet[i].pos_y < -115 ||
423 	  p_bullet[i].pos_y >  115) {
424 	p_bullet[i].state = 0;
425       }
426       /* collision to enemy. kills bullet and kills enemy */
427       /* and adds 100 to score */
428       for (j=0; j<sizeof(enemy)/sizeof(Entity); j++) {
429 	if (collision(&p_bullet[i],&enemy[j])) {
430 	  score += 100;
431 	  if (score > highscore) highscore = score;
432 	  p_bullet[i].state = 0;
433 	  enemy[j].state = 0;
434 	}
435       }
436       /* collision to vortex shakes bullet */
437       for (j=0; j<sizeof(vortex)/sizeof(Entity); j++) {
438 	if (collision(&p_bullet[i], &vortex[j])) {
439 	  p_bullet[i].vel_x += (rnd()*600 - 300) * game_tick;
440 	  p_bullet[i].vel_y += (rnd()*600 - 300) * game_tick;
441 	  p_bullet[i].dir = rnd()*360-180;
442 	}
443       }
444     }
445   }
446 
447   /* e_bullet */
448   for (i=0; i<sizeof(e_bullet)/sizeof(Entity); i++) {
449     if (e_bullet[i].state) {
450       int j;
451       /* move */
452       e_bullet[i].pos_x += e_bullet[i].vel_x * game_tick;
453       e_bullet[i].pos_y += e_bullet[i].vel_y * game_tick;
454       /* collision to border kills bullet */
455       if (e_bullet[i].pos_x < -115 ||
456 	  e_bullet[i].pos_x >  115 ||
457 	  e_bullet[i].pos_y < -115 ||
458 	  e_bullet[i].pos_y >  115)
459 	e_bullet[i].state = 0;
460       /* collision to player kills bullet and kills player */
461       if (collision(&e_bullet[i], &player)) {
462 	e_bullet[i].state = 0;
463 	player.state = 0;
464       }
465       /* collision to vortex shakes bullet */
466       for (j=0; j<sizeof(vortex)/sizeof(Entity); j++) {
467 	if (collision(&p_bullet[i], &vortex[j])) {
468 	  e_bullet[i].vel_x += (rnd()*600 - 300) * game_tick;
469 	  e_bullet[i].vel_y += (rnd()*600 - 300) * game_tick;
470 	  e_bullet[i].dir = rnd()*360-180;
471 	}
472       }
473     }
474   }
475 
476   /* particle */
477   for (i=0; i<sizeof(particle)/sizeof(Entity); i++) {
478     if (particle[i].state) {
479       /* time elapsed, kill particle */
480       if (particle[i].timer < game_time) {
481 	particle[i].state = 0;
482 	continue;
483       }
484       /* move */
485       particle[i].pos_x += particle[i].vel_x * game_tick;
486       particle[i].pos_y += particle[i].vel_y * game_tick;
487     }
488   }
489 
490 }
491 
game_render()492 void game_render()
493 {
494   int i;
495 
496   /* drawmode */
497   if (draw_fast) {
498     glDisable(GL_LINE_SMOOTH);
499     glDisable(GL_POINT_SMOOTH);
500     glDisable(GL_BLEND);
501   } else {
502     glEnable(GL_LINE_SMOOTH);
503     glEnable(GL_POINT_SMOOTH);
504     glEnable(GL_BLEND);
505     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
506   }
507 
508 
509   /* view */
510   glMatrixMode(GL_PROJECTION);
511   glLoadIdentity();
512   gluOrtho2D(-115,115,-115,115);
513   glMatrixMode(GL_MODELVIEW);
514   glLoadIdentity();
515 
516   /* clear background */
517   glClearColor(0,0,0,1);
518   glClear(GL_COLOR_BUFFER_BIT);
519 
520   /* frame around image */
521   glColor3f(1,1,0);
522   glBegin(GL_LINE_LOOP);
523   glVertex2f(-110, 110);
524   glVertex2f( 110, 110);
525   glVertex2f( 110,-110);
526   glVertex2f(-110,-110);
527   glEnd();
528 
529   glColor3f(1,0,0);
530   glBegin(GL_LINE_LOOP);
531   glVertex2f(-105, 105);
532   glVertex2f( 105, 105);
533   glVertex2f( 105,-105);
534   glVertex2f(-105,-105);
535   glEnd();
536 
537   glColor3f(0,0,1);
538   glBegin(GL_LINE_LOOP);
539   glVertex2f(-100, 100);
540   glVertex2f( 100, 100);
541   glVertex2f( 100,-100);
542   glVertex2f(-100,-100);
543   glEnd();
544 
545 
546   /* player */
547   if (player.state) {
548     glPushMatrix();
549     glTranslatef(player.pos_x, player.pos_y, 0);
550     glRotatef(player.dir, 0,0,1);
551 
552     glColor3f(.5,.5,1);
553     glBegin(GL_LINE_LOOP);
554     glVertex2f(-4,-4);
555     glVertex2f( 0, 5);
556     glVertex2f( 4,-4);
557     glEnd();
558 
559     glColor3f(1,1,1);
560     glBegin(GL_LINE_STRIP);
561     glVertex2f(-2,-5);
562     glVertex2f(-4,-2);
563     glVertex2f(-2, 2);
564     glVertex2f( 2, 2);
565     glVertex2f( 4,-2);
566     glVertex2f( 2,-5);
567     glEnd();
568 
569     glPopMatrix();
570   }
571 
572   /* enemy */
573   for (i=0; i<sizeof(enemy)/sizeof(Entity); i++) {
574     if (enemy[i].state) {
575       glPushMatrix();
576       glTranslatef(enemy[i].pos_x, enemy[i].pos_y, 0);
577 
578       glRotatef(enemy[i].dir, 0,0,1);
579 
580       glColor3f(1,0,0);
581       glBegin(GL_LINE_STRIP);
582       glVertex2f(-3,-4);
583       glVertex2f(-5, 0);
584       glVertex2f( 0, 5);
585       glVertex2f( 5, 0);
586       glVertex2f( 3,-4);
587       glEnd();
588       glColor3f(1,1,0);
589       glBegin(GL_LINE_LOOP);
590       glVertex2f( 0, 5);
591       glVertex2f( 3,-4);
592       glVertex2f(-3,-4);
593       glEnd();
594 
595       glPopMatrix();
596     }
597   }
598 
599   /* vortex */
600   for (i=0; i<sizeof(vortex)/sizeof(Entity); i++) {
601     if (vortex[i].state) {
602 
603       glPushMatrix();
604       glTranslatef(vortex[i].pos_x, vortex[i].pos_y, 0);
605       glRotatef(vortex[i].dir, 0,0,1);
606 
607       glColor3f(0,.5,1);
608       glBegin(GL_LINE_LOOP);
609       gCircle(vortex[i].radius,6);
610       glEnd();
611 
612       glColor3f(0,0,1);
613       glBegin(GL_LINE_LOOP);
614       gCircle(vortex[i].radius*.7, 6);
615       glEnd();
616 
617       glColor3f(0,0,.5);
618       glBegin(GL_LINE_LOOP);
619       gCircle(vortex[i].radius*.4, 6);
620       glEnd();
621 
622       glPopMatrix();
623     }
624   }
625 
626   /* p_bullet */
627   for (i=0; i<sizeof(p_bullet)/sizeof(Entity); i++) {
628     if (p_bullet[i].state) {
629       glPushMatrix();
630       glTranslatef(p_bullet[i].pos_x, p_bullet[i].pos_y, 0);
631       glRotatef(p_bullet[i].dir, 0,0,1);
632 
633       glColor3f(1,1,1);
634       glBegin(GL_LINES);
635       glVertex2f(0, 1);
636       glVertex2f(0,-1);
637       glEnd();
638 
639       glPopMatrix();
640     }
641   }
642 
643   /* e_bullet */
644   for (i=0; i<sizeof(e_bullet)/sizeof(Entity); i++) {
645     if (e_bullet[i].state) {
646       glPushMatrix();
647       glTranslatef(e_bullet[i].pos_x, e_bullet[i].pos_y, 0);
648       glRotatef(e_bullet[i].dir, 0,0,1);
649 
650       glColor3f(1,1,0);
651       glBegin(GL_LINES);
652       glVertex2f(0, 1);
653       glVertex2f(0,-1);
654       glEnd();
655 
656       glPopMatrix();
657     }
658   }
659 
660   /* particles */
661   glColor3f(.5,.7,1);
662   glBegin(GL_POINTS);
663   for (i=0; i<sizeof(particle)/sizeof(Entity); i++) {
664     if (particle[i].state)
665       glVertex2f(particle[i].pos_x, particle[i].pos_y);
666   }
667   glEnd();
668 
669   /* textual info */
670   if (fontbase) {
671     char s[200];
672     g_snprintf(s, sizeof(s), "wave %d score %d highscore %d", wave_cnt, score, highscore);
673 
674     glColor3f(.8,.8,.8);
675     glRasterPos2f(-90, 90);
676     glListBase(fontbase);
677     glCallLists(strlen(s), GL_UNSIGNED_BYTE, s);
678 
679   }
680 }
681 
682 
683 
684 /* --------------------------------------- */
685 
686 
687 #ifdef FULLSCREEN_MESA_3DFX
688 
switch_fullscreen(GtkWidget * gl_area)689 gint switch_fullscreen(GtkWidget *gl_area)
690 {
691   static GtkWidget *fullscreenwidget = NULL;
692 
693   if (!fullscreenwidget)
694     {
695       /* Grab keyboard and pointer so that user does not wander off the game
696 	 window while in fullscreen mode.
697       */
698       if (gdk_keyboard_grab(gl_area->window, FALSE, GDK_CURRENT_TIME) == 0)
699 	{
700 	  if (gdk_pointer_grab(gl_area->window, FALSE, 0, NULL, NULL, GDK_CURRENT_TIME) == 0)
701 	    {
702 	      gtk_widget_grab_focus(gl_area);
703 	      if (gtk_gl_area_make_current(GTK_GL_AREA(gl_area)))
704 		{
705 		  if (XMesaSetFXmode((XMESA_FX_FULLSCREEN)))
706 		    {
707 		      fullscreenwidget = gl_area;
708 		      return TRUE;
709 		    }
710 		}
711 	      gdk_pointer_ungrab(GDK_CURRENT_TIME);
712 	    }
713 	  gdk_keyboard_ungrab(GDK_CURRENT_TIME);
714 	}
715       return FALSE;
716     }
717 
718   if (fullscreenwidget == gl_area)
719     {
720       if (gtk_gl_area_make_current(GTK_GL_AREA(gl_area)))
721 	XMesaSetFXmode(XMESA_FX_WINDOW);
722 
723       gdk_keyboard_ungrab(GDK_CURRENT_TIME);
724       gdk_pointer_ungrab(GDK_CURRENT_TIME);
725       fullscreenwidget = NULL;
726       return TRUE;
727     }
728 
729   return FALSE;
730 }
731 
732 #endif
733 
734 
735 
736 
init(GtkWidget * widget)737 gint init(GtkWidget *widget)
738 {
739   /* OpenGL functions can be called only if makecurrent returns true */
740   if (gtk_gl_area_make_current(GTK_GL_AREA(widget))) {
741 #if !defined(WIN32)
742     GdkFont *font;
743 #endif
744 
745     /* set viewport */
746     glViewport(0,0, widget->allocation.width, widget->allocation.height);
747 
748 #if !defined(WIN32)
749     /* generate font display lists */
750     font = gdk_font_load("-adobe-helvetica-medium-r-normal--*-120-*-*-*-*-*-*");
751     if (font) {
752       fontbase = glGenLists( 128 );
753       gdk_gl_use_gdk_font(font, 0, 128, fontbase);
754       gdk_font_unref(font);
755     }
756 #endif
757   }
758   return TRUE;
759 }
760 
761 
762 /* When widget is exposed it's contents are redrawn. */
draw(GtkWidget * widget,GdkEventExpose * event)763 gint draw(GtkWidget *widget, GdkEventExpose *event)
764 {
765   /* Draw only last expose. */
766   if (event->count > 0)
767     return TRUE;
768 
769   if (gtk_gl_area_make_current(GTK_GL_AREA(widget)))
770     game_render();
771 
772   /* Swap backbuffer to front */
773   gtk_gl_area_swapbuffers(GTK_GL_AREA(widget));
774 
775   return TRUE;
776 }
777 
778 /* When glarea widget size changes, viewport size is set to match the new size */
reshape(GtkWidget * widget,GdkEventConfigure * event)779 gint reshape(GtkWidget *widget, GdkEventConfigure *event)
780 {
781   /* OpenGL functions can be called only if make_current returns true */
782   if (gtk_gl_area_make_current(GTK_GL_AREA(widget)))
783     {
784       glViewport(0,0, widget->allocation.width, widget->allocation.height);
785     }
786   return TRUE;
787 }
788 
789 
key_press_event(GtkWidget * widget,GdkEventKey * event)790 gint key_press_event(GtkWidget *widget, GdkEventKey *event)
791 {
792   switch (event->keyval) {
793   case GDK_Left:
794     control_spin = 1;
795     break;
796   case GDK_Right:
797     control_spin = -1;
798     break;
799   case GDK_Up:
800   case GDK_space:
801     control_fire = 1;
802     break;
803   case GDK_Down:
804     control_speed = 1;
805     break;
806   case GDK_r:
807     game_init();
808     break;
809   case GDK_q:
810     gtk_main_quit();
811     break;
812 
813 #ifdef FULLSCREEN_MESA_3DFX
814   case GDK_f:
815     switch_fullscreen(widget);
816     break;
817 #endif
818 
819   case GDK_d:
820     draw_fast = (!draw_fast);
821     break;
822   }
823   /* prevent the default handler from being run */
824   gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),"key_press_event");
825   return TRUE;
826 }
827 
key_release_event(GtkWidget * widget,GdkEventKey * event)828 gint key_release_event(GtkWidget *widget, GdkEventKey *event)
829 {
830   switch (event->keyval) {
831   case GDK_Left:
832     if (control_spin == 1) control_spin = 0;
833     break;
834   case GDK_Right:
835     if (control_spin == -1) control_spin = 0;
836     break;
837   case GDK_Up:
838   case GDK_space:
839     control_fire = 0;
840     break;
841   case GDK_Down:
842     control_speed = 0;
843     break;
844   }
845   /* prevent the default handler from being run */
846   gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),"key_release_event");
847   return TRUE;
848 }
849 
animate(GtkWidget * glarea)850 gint animate(GtkWidget *glarea)
851 {
852   game_play();
853   gtk_widget_draw(GTK_WIDGET(glarea), NULL);
854   return TRUE;
855 }
856 
857 
main(int argc,char ** argv)858 int main(int argc, char **argv)
859 {
860   GtkWidget *window,*vbox,*logo,*glarea;
861 
862   /* Attribute list for gtkglarea widget. Specifies a
863      list of Boolean attributes and enum/integer
864      attribute/value pairs. The last attribute must be
865      GDK_GL_NONE. See glXChooseVisual manpage for further
866      explanation.
867   */
868   int attrlist[] = {
869     GDK_GL_RGBA,
870     GDK_GL_RED_SIZE,1,
871     GDK_GL_GREEN_SIZE,1,
872     GDK_GL_BLUE_SIZE,1,
873     GDK_GL_DOUBLEBUFFER,
874     GDK_GL_NONE
875   };
876 
877 #ifdef FULLSCREEN_MESA_3DFX
878   setenv("MESA_GLX_FX", "", 1);
879   setenv("FX_GLIDE_NO_SPLASH", "", 1);
880 #endif
881 
882   /* initialize gtk */
883   gtk_init(&argc, &argv);
884 
885   /* Check if OpenGL (GLX extension) is supported. */
886   if (gdk_gl_query() == FALSE) {
887     g_print("OpenGL not supported\n");
888     return 0;
889   }
890 
891   /* Create new top level window. */
892   window = gtk_window_new( GTK_WINDOW_TOPLEVEL);
893   gtk_window_set_title(GTK_WINDOW(window), "Zktor");
894 
895   /* Quit form main if got delete event */
896   gtk_signal_connect(GTK_OBJECT(window), "delete_event",
897 		     GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
898 
899 
900   /* You should always delete gtk_gl_area widgets before exit or else
901      GLX contexts are left undeleted, this may cause problems (=core dump)
902      in some systems.
903      Destroy method of objects is not automatically called on exit.
904      You need to manually enable this feature. Do gtk_quit_add_destroy()
905      for all your top level windows unless you are certain that they get
906      destroy signal by other means.
907   */
908   gtk_quit_add_destroy(1, GTK_OBJECT(window));
909 
910 
911   vbox = GTK_WIDGET(gtk_vbox_new(FALSE, 0));
912   gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
913 
914 
915   logo = gtk_label_new("Zktor");
916 
917 
918   /* Create new OpenGL widget. */
919   glarea = GTK_WIDGET(gtk_gl_area_new(attrlist));
920   /* Events for widget must be set before X Window is created */
921   gtk_widget_set_events(GTK_WIDGET(glarea),
922 			GDK_EXPOSURE_MASK|
923 			GDK_KEY_PRESS_MASK|
924 			GDK_KEY_RELEASE_MASK);
925   /* set minimum size */
926   /*  gtk_widget_set_usize(GTK_WIDGET(glarea), 200,200); */
927   /* set default size */
928   gtk_gl_area_size(GTK_GL_AREA(glarea), 640,400);
929 
930 
931   /* Connect signal handlers */
932   /* Redraw image when exposed. */
933   gtk_signal_connect(GTK_OBJECT(glarea), "expose_event",
934 		     GTK_SIGNAL_FUNC(draw), NULL);
935   /* When window is resized viewport needs to be resized also. */
936   gtk_signal_connect(GTK_OBJECT(glarea), "configure_event",
937 		     GTK_SIGNAL_FUNC(reshape), NULL);
938   /* Do initialization when widget has been realized. */
939   gtk_signal_connect(GTK_OBJECT(glarea), "realize",
940 		     GTK_SIGNAL_FUNC(init), NULL);
941   /* Capture keypress events */
942   gtk_signal_connect(GTK_OBJECT(glarea), "key_press_event",
943 		     GTK_SIGNAL_FUNC(key_press_event), NULL);
944   gtk_signal_connect(GTK_OBJECT(glarea), "key_release_event",
945 		     GTK_SIGNAL_FUNC(key_release_event), NULL);
946 
947   /* construct widget hierarchy  */
948   gtk_container_add(GTK_CONTAINER(window),GTK_WIDGET(vbox));
949   gtk_box_pack_start(GTK_BOX(vbox),   logo, FALSE, FALSE, 0);
950   gtk_box_pack_start(GTK_BOX(vbox), glarea,  TRUE,  TRUE, 0);
951 
952 
953 
954   /* show all widgets */
955   gtk_widget_show(GTK_WIDGET(glarea));
956   gtk_widget_show(GTK_WIDGET(logo));
957   gtk_widget_show(GTK_WIDGET(vbox));
958   gtk_widget_show(window);
959 
960   /* set focus to glarea widget */
961   GTK_WIDGET_SET_FLAGS(glarea, GTK_CAN_FOCUS);
962   gtk_widget_grab_focus(GTK_WIDGET(glarea));
963 
964   /* animating */
965   gtk_idle_add((GtkFunction)animate, glarea);
966 
967   game_init();
968   gtk_main();
969 
970 
971   return 0;
972 }
973