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