comparison packages/services/gfx/mw/current/src/demos/nanox/ntetris.c @ 208:e0c0827131d1 ecos

Merge from eCos master repository on 2002-05-20-20:11:54-BST
author jlarmour
date Mon, 20 May 2002 22:19:26 +0000
parents
children 120561ca1209
comparison
equal deleted inserted replaced
207:74c807ddde34 208:e0c0827131d1
1 /*
2 * The contents of this file are subject to the Mozilla Public License
3 * Version 1.1 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS IS"
8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
9 * License for the specific language governing rights and limitations
10 * under the License.
11 *
12 * The Original Code is NanoTetris.
13 *
14 * The Initial Developer of the Original Code is Alex Holden.
15 * Portions created by Alex Holden are Copyright (C) 2000
16 * Alex Holden <alex@linuxhacker.org>. All Rights Reserved.
17 *
18 * Contributor(s):
19 *
20 * Alternatively, the contents of this file may be used under the terms
21 * of the GNU General Public license (the "[GNU] License"), in which case the
22 * provisions of [GNU] License are applicable instead of those
23 * above. If you wish to allow use of your version of this file only
24 * under the terms of the [GNU] License and not to allow others to use
25 * your version of this file under the MPL, indicate your decision by
26 * deleting the provisions above and replace them with the notice and
27 * other provisions required by the [GNU] License. If you do not delete
28 * the provisions above, a recipient may use your version of this file
29 * under either the MPL or the [GNU] License.
30 */
31
32 /*
33 * A Nano-X Tetris clone by Alex Holden.
34 *
35 * The objective is to keep placing new pieces for as long as possible. When a
36 * horizontal line is filled with blocks, it will vanish, and everything above
37 * it will drop down a line. It quickly gets difficult because the speed
38 * increases with the score. Unlike with some Tetris clones, no bonus points
39 * are awarded for matching colours, completing more than one line at a time,
40 * or for using the "drop shape to bottom" function.
41 *
42 * The box in the top left of the game window is the score box. The top score
43 * is the highest score you have achieved since last resetting the high score
44 * counter. The counter is stored when the game exits in the file specified by
45 * the HISCORE_FILE parameter ("/usr/games/nanotetris.hiscore" by default).
46 * Note that no attempt is made to encrypt the file, so anybody with write
47 * access to the file can alter the contents of it using a text editor.
48 *
49 * The box below the score box is the next shape box. This contains a "preview"
50 * of the next shape to appear, so that you can plan ahead as you are building
51 * up the blocks.
52 *
53 * The game functions can be controlled using either the mouse (or a touch pad,
54 * touch screen, trackball, etc.) and the buttons below the next shape box, or
55 * with the following keyboard keys:
56 *
57 * Q = quit game
58 * N = new game
59 * P = pause game
60 * C = continue game
61 * D = rotate shape anticlockwise
62 * F = rotate shape clockwise
63 * J = move shape left
64 * K = move shape right
65 * Space Bar = drop shape to bottom.
66 *
67 * The reason for the unconventional use of D, F, J, and K keys is that they
68 * are all in the "home" position of a QWERTY keyboard, which makes them very
69 * easy to press if you are used to touch typing.
70 *
71 * I'll leave it to you to figure out which mouse operated movement button does
72 * what (it's pretty obvious).
73 */
74
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <fcntl.h>
78 #include <unistd.h>
79 #include <time.h>
80 #include <ctype.h>
81 #include <errno.h>
82 #include <sys/time.h>
83
84 #ifdef __ECOS
85 #define random rand
86 #define srandom srand
87 #endif
88
89 #define MWINCLUDECOLORS
90 #include <nano-X.h>
91
92 #include "ntetris.h"
93
94 void *my_malloc(size_t size)
95 {
96 void *ret;
97
98 if(!(ret = malloc(size))) {
99 fprintf(stderr, "Out of memory\n");
100 exit(1);
101 }
102
103 return ret;
104 }
105
106 #ifdef HAVE_USLEEP
107 void msleep(long ms)
108 {
109 usleep(ms * 1000);
110 }
111 #else
112 void msleep(long ms)
113 {
114 struct timespec req, rem;
115
116 req.tv_sec = ms / 1000000;
117 req.tv_nsec = (ms % 1000000) * 1000000;
118
119 while(nanosleep(&req, &rem) == -1) {
120 if(errno == EINTR) {
121 req.tv_sec = rem.tv_sec;
122 req.tv_nsec = rem.tv_nsec;
123 continue;
124 } else {
125 perror("nanosleep() failed");
126 return;
127 }
128 }
129 }
130 #endif
131
132 #ifdef USE_HISCORE_FILE
133 void read_hiscore(nstate *state)
134 {
135 FILE *f;
136 int i, n;
137
138 if(!(f = fopen(HISCORE_FILE, "r"))) {
139 if(errno != ENOENT)
140 perror("Couldn't open high score file for reading");
141 state->hiscore = state->fhiscore = 0;
142 return;
143 }
144
145 i = fscanf(f, "%d", &n);
146 fclose(f);
147
148 if(i != 1) {
149 fprintf(stderr, "Couldn't read high score file\n");
150 n = 0;
151 }
152
153 state->hiscore = state->fhiscore = n;
154 }
155
156 void write_hiscore(nstate *state)
157 {
158 FILE *f;
159
160 if(state->score > state->hiscore) state->hiscore = state->score;
161 if(state->hiscore <= state->fhiscore) return;
162
163 if(!(f = fopen(HISCORE_FILE, "w"))) {
164 perror("Couldn't open high score file for writing");
165 return;
166 }
167
168 if((fprintf(f, "%d", state->hiscore)) == -1) {
169 perror("Couldn't write to high score file");
170 }
171
172 fclose(f);
173 }
174 #else
175 void read_hiscore(nstate *state)
176 {
177 state->hiscore = 0;
178 }
179
180 void write_hiscore(nstate *state) {}
181 #endif
182
183 int will_collide(nstate *state, int x, int y, int orientation)
184 {
185 int r, c, xx, yy;
186 char ch = 0;
187
188 draw_shape(state, state->current_shape.x, state->current_shape.y, 1);
189 for(r = 0; ch < 3; r++) {
190 ch = 0;
191 for(c = 0; ch < 2; c++) {
192 ch = shapes[state->current_shape.type]
193 [orientation][r][c];
194 if(ch == 1) {
195 yy = y + r;
196 xx = x + c;
197 if((yy == WELL_HEIGHT) || (xx == WELL_WIDTH) ||
198 (state->blocks[0][yy][xx])) {
199 draw_shape(state,
200 state->current_shape.x,
201 state->current_shape.y, 0);
202 return 1;
203 }
204 }
205 }
206 }
207 draw_shape(state, state->current_shape.x, state->current_shape.y, 0);
208
209 return 0;
210 }
211
212 void draw_shape(nstate *state, GR_COORD x, GR_COORD y, int erase)
213 {
214 int r, c, yy, xx;
215 GR_COLOR col;
216 char ch = 0;
217
218 if(erase) col = 0;
219 else col = state->current_shape.colour;
220
221 for(r = 0; ch < 3; r++) {
222 ch = 0;
223 for(c = 0; ch < 2; c++) {
224 ch = shapes[state->current_shape.type]
225 [state->current_shape.orientation][r][c];
226 if(ch == 1) {
227 yy = y + r;
228 xx = x + c;
229 state->blocks[0][yy][xx] = col;
230 }
231 }
232 }
233 }
234
235 void draw_well(nstate *state, int forcedraw)
236 {
237 int x, y;
238
239 for(y = WELL_NOTVISIBLE; y < WELL_HEIGHT; y++) {
240 for(x = 0; x < WELL_WIDTH; x++) {
241 if(forcedraw || (state->blocks[0][y][x] !=
242 state->blocks[1][y][x])) {
243 state->blocks[1][y][x] = state->blocks[0][y][x];
244 GrSetGCForeground(state->wellgc,
245 state->blocks[0][y][x]);
246 GrFillRect(state->well_window, state->wellgc,
247 (BLOCK_SIZE * x),
248 (BLOCK_SIZE * (y - WELL_NOTVISIBLE)),
249 BLOCK_SIZE, BLOCK_SIZE);
250 }
251 }
252 }
253
254 GrFlush();
255 }
256
257 void draw_score(nstate *state)
258 {
259 char buf[32];
260
261 GrFillRect(state->score_window, state->scoregcb, 0, 0,
262 SCORE_WINDOW_WIDTH, SCORE_WINDOW_HEIGHT);
263
264 sprintf(buf, "%d", state->score);
265 GrText(state->score_window, state->scoregcf, TEXT_X_POSITION,
266 TEXT2_Y_POSITION, buf, strlen(buf), 0);
267 sprintf(buf, "%d", state->hiscore);
268 GrText(state->score_window, state->scoregcf, TEXT_X_POSITION,
269 TEXT_Y_POSITION, buf, strlen(buf), 0);
270 }
271
272 void draw_next_shape(nstate *state)
273 {
274 int r, c, startx, starty, x, y;
275 char ch = 0;
276
277 GrFillRect(state->next_shape_window, state->nextshapegcb, 0, 0,
278 NEXT_SHAPE_WINDOW_WIDTH, NEXT_SHAPE_WINDOW_HEIGHT);
279
280 GrSetGCForeground(state->nextshapegcf, state->next_shape.colour);
281
282 startx = (BLOCK_SIZE * ((NEXT_SHAPE_WINDOW_SIZE / 2) -
283 (shape_sizes[state->next_shape.type]
284 [state->next_shape.orientation][0] / 2)));
285 starty = (BLOCK_SIZE * ((NEXT_SHAPE_WINDOW_SIZE / 2) -
286 (shape_sizes[state->next_shape.type]
287 [state->next_shape.orientation][1] / 2)));
288
289 for(r = 0; ch < 3; r++) {
290 ch = 0;
291 for(c = 0; ch < 2; c++) {
292 ch = shapes[state->next_shape.type]
293 [state->next_shape.orientation][r][c];
294 if(ch == 1) {
295 x = startx + (c * BLOCK_SIZE);
296 y = starty + (r * BLOCK_SIZE);
297 GrFillRect(state->next_shape_window,
298 state->nextshapegcf, x, y,
299 BLOCK_SIZE, BLOCK_SIZE);
300 }
301 }
302 }
303 }
304
305 void draw_new_game_button(nstate *state)
306 {
307 GrFillRect(state->new_game_button, state->buttongcb, 0, 0,
308 NEW_GAME_BUTTON_WIDTH, NEW_GAME_BUTTON_HEIGHT);
309 GrText(state->new_game_button, state->buttongcf, TEXT_X_POSITION,
310 TEXT_Y_POSITION, "New Game", 8, 0);
311 }
312
313 void draw_anticlockwise_button(nstate *state)
314 {
315 if(!state->running_buttons_mapped) return;
316 GrFillRect(state->anticlockwise_button, state->buttongcb, 0, 0,
317 ANTICLOCKWISE_BUTTON_WIDTH, ANTICLOCKWISE_BUTTON_HEIGHT);
318 GrText(state->anticlockwise_button, state->buttongcf, TEXT_X_POSITION,
319 TEXT_Y_POSITION, " /", 4, 0);
320 }
321
322 void draw_clockwise_button(nstate *state)
323 {
324 if(!state->running_buttons_mapped) return;
325 GrFillRect(state->clockwise_button, state->buttongcb, 0, 0,
326 CLOCKWISE_BUTTON_WIDTH, CLOCKWISE_BUTTON_HEIGHT);
327 GrText(state->clockwise_button, state->buttongcf, TEXT_X_POSITION,
328 TEXT_Y_POSITION, " \\", 4, 0);
329 }
330
331 void draw_left_button(nstate *state)
332 {
333 if(!state->running_buttons_mapped) return;
334 GrFillRect(state->left_button, state->buttongcb, 0, 0,
335 LEFT_BUTTON_WIDTH, LEFT_BUTTON_HEIGHT);
336 GrText(state->left_button, state->buttongcf, TEXT_X_POSITION,
337 TEXT_Y_POSITION, " <", 3, 0);
338 }
339
340 void draw_right_button(nstate *state)
341 {
342 if(!state->running_buttons_mapped) return;
343 GrFillRect(state->right_button, state->buttongcb, 0, 0,
344 RIGHT_BUTTON_WIDTH, RIGHT_BUTTON_HEIGHT);
345 GrText(state->right_button, state->buttongcf, TEXT_X_POSITION,
346 TEXT_Y_POSITION, " >", 4, 0);
347 }
348
349 void draw_drop_button(nstate *state)
350 {
351 if(!state->running_buttons_mapped) return;
352 GrFillRect(state->drop_button, state->buttongcb, 0, 0,
353 DROP_BUTTON_WIDTH, DROP_BUTTON_HEIGHT);
354 GrText(state->drop_button, state->buttongcf, TEXT_X_POSITION,
355 TEXT_Y_POSITION, " Drop", 8, 0);
356 }
357
358 void draw_pause_continue_button(nstate *state)
359 {
360 if((state->running_buttons_mapped) && (state->state == STATE_STOPPED)) {
361 GrUnmapWindow(state->pause_continue_button);
362 GrUnmapWindow(state->anticlockwise_button);
363 GrUnmapWindow(state->clockwise_button);
364 GrUnmapWindow(state->left_button);
365 GrUnmapWindow(state->right_button);
366 GrUnmapWindow(state->drop_button);
367 state->running_buttons_mapped = 0;
368 return;
369 }
370 if((!state->running_buttons_mapped) && (state->state == STATE_RUNNING)){
371 GrMapWindow(state->pause_continue_button);
372 GrMapWindow(state->anticlockwise_button);
373 GrMapWindow(state->clockwise_button);
374 GrMapWindow(state->left_button);
375 GrMapWindow(state->right_button);
376 GrMapWindow(state->drop_button);
377 state->running_buttons_mapped = 1;
378 return;
379 }
380 if(!state->running_buttons_mapped) return;
381 GrFillRect(state->pause_continue_button, state->buttongcb, 0, 0,
382 PAUSE_CONTINUE_BUTTON_WIDTH, PAUSE_CONTINUE_BUTTON_HEIGHT);
383 if(state->state == STATE_PAUSED) {
384 GrText(state->pause_continue_button, state->buttongcf,
385 TEXT_X_POSITION, TEXT_Y_POSITION, " Continue", 9, 0);
386 } else {
387 GrText(state->pause_continue_button, state->buttongcf,
388 TEXT_X_POSITION, TEXT_Y_POSITION, " Pause", 8, 0);
389 }
390 }
391
392 int block_is_all_in_well(nstate *state)
393 {
394 if(state->current_shape.y >= WELL_NOTVISIBLE)
395 return 1;
396
397 return 0;
398 }
399
400 void delete_line(nstate *state, int line)
401 {
402 int x, y;
403
404 if(line < WELL_NOTVISIBLE) return;
405
406 for(y = line - 1; y; y--)
407 for(x = WELL_WIDTH; x; x--)
408 state->blocks[0][y + 1][x] = state->blocks[0][y][x];
409
410 draw_well(state, 0);
411 }
412
413 void block_reached_bottom(nstate *state)
414 {
415 int x, y;
416
417 if(!block_is_all_in_well(state)) {
418 state->state = STATE_STOPPED;
419 return;
420 }
421
422 for(y = WELL_HEIGHT - 1; y; y--) {
423 for(x = 0; x < WELL_WIDTH; x++)
424 if(!state->blocks[0][y][x]) goto nr;
425 msleep(DELETE_LINE_DELAY);
426 delete_line(state, y);
427 state->score += SCORE_INCREMENT;
428 if((LEVELS > (state->level + 1)) && (((state->level + 1) *
429 LEVEL_DIVISOR) <= state->score))
430 state->level++;
431 draw_score(state);
432 y++;
433 nr:
434 }
435
436 choose_new_shape(state);
437 draw_next_shape(state);
438 }
439
440 void move_block(nstate *state, int direction)
441 {
442 if(direction == 0) {
443 if(!state->current_shape.x) return;
444 else {
445 if(!will_collide(state, (state->current_shape.x - 1),
446 state->current_shape.y,
447 state->current_shape.orientation)) {
448 draw_shape(state, state->current_shape.x,
449 state->current_shape.y, 1);
450 state->current_shape.x--;
451 draw_shape(state, state->current_shape.x,
452 state->current_shape.y, 0);
453 draw_well(state, 0);
454 }
455 }
456 } else {
457 if(!will_collide(state, (state->current_shape.x + 1),
458 state->current_shape.y,
459 state->current_shape.orientation)) {
460 draw_shape(state, state->current_shape.x,
461 state->current_shape.y, 1);
462 state->current_shape.x++;
463 draw_shape(state, state->current_shape.x,
464 state->current_shape.y, 0);
465 draw_well(state, 0);
466 }
467 }
468 }
469
470 void rotate_block(nstate *state, int direction)
471 {
472 int neworientation = 0;
473
474 if(direction == 0) {
475 if(!state->current_shape.orientation)
476 neworientation = MAXORIENTATIONS - 1;
477 else neworientation = state->current_shape.orientation - 1;
478 } else {
479 neworientation = state->current_shape.orientation + 1;
480 if(neworientation == MAXORIENTATIONS) neworientation = 0;
481 }
482
483 if(!will_collide(state, state->current_shape.x, state->current_shape.y,
484 neworientation)) {
485 draw_shape(state, state->current_shape.x,
486 state->current_shape.y, 1);
487 state->current_shape.orientation = neworientation;
488 draw_shape(state, state->current_shape.x,
489 state->current_shape.y, 0);
490 draw_well(state, 0);
491 }
492 }
493
494 int drop_block_1(nstate *state)
495 {
496 if(will_collide(state, state->current_shape.x,
497 (state->current_shape.y + 1),
498 state->current_shape.orientation)) {
499 block_reached_bottom(state);
500 return 1;
501 }
502
503 draw_shape(state, state->current_shape.x, state->current_shape.y, 1);
504 state->current_shape.y++;
505 draw_shape(state, state->current_shape.x, state->current_shape.y, 0);
506
507 draw_well(state, 0);
508
509 return 0;
510 }
511
512 void drop_block(nstate *state)
513 {
514 while(!drop_block_1(state)) msleep(DROP_BLOCK_DELAY);
515 }
516
517 void handle_exposure_event(nstate *state)
518 {
519 GR_EVENT_EXPOSURE *event = &state->event.exposure;
520
521 if(event->wid == state->score_window) {
522 draw_score(state);
523 return;
524 }
525 if(event->wid == state->next_shape_window) {
526 draw_next_shape(state);
527 return;
528 }
529 if(event->wid == state->new_game_button) {
530 draw_new_game_button(state);
531 return;
532 }
533 if(event->wid == state->pause_continue_button) {
534 draw_pause_continue_button(state);
535 return;
536 }
537 if(event->wid == state->anticlockwise_button) {
538 draw_anticlockwise_button(state);
539 return;
540 }
541 if(event->wid == state->clockwise_button) {
542 draw_clockwise_button(state);
543 return;
544 }
545 if(event->wid == state->left_button) {
546 draw_left_button(state);
547 return;
548 }
549 if(event->wid == state->right_button) {
550 draw_right_button(state);
551 return;
552 }
553 if(event->wid == state->drop_button) {
554 draw_drop_button(state);
555 return;
556 }
557 if(event->wid == state->well_window) {
558 draw_well(state, 1);
559 return;
560 }
561 }
562
563 void handle_mouse_event(nstate *state)
564 {
565 GR_EVENT_MOUSE *event = &state->event.mouse;
566
567 if(event->wid == state->new_game_button) {
568 state->state = STATE_NEWGAME;
569 return;
570 }
571 if(event->wid == state->pause_continue_button) {
572 if(state->state == STATE_PAUSED) state->state = STATE_RUNNING;
573 else state->state = STATE_PAUSED;
574 return;
575 }
576 if(event->wid == state->anticlockwise_button) {
577 if(state->state == STATE_PAUSED) state->state = STATE_RUNNING;
578 rotate_block(state, 0);
579 return;
580 }
581 if(event->wid == state->clockwise_button) {
582 if(state->state == STATE_PAUSED) state->state = STATE_RUNNING;
583 rotate_block(state, 1);
584 return;
585 }
586 if(event->wid == state->left_button) {
587 if(state->state == STATE_PAUSED) state->state = STATE_RUNNING;
588 move_block(state, 0);
589 return;
590 }
591 if(event->wid == state->right_button) {
592 if(state->state == STATE_PAUSED) state->state = STATE_RUNNING;
593 move_block(state, 1);
594 return;
595 }
596 if(event->wid == state->drop_button) {
597 if(state->state == STATE_PAUSED) state->state = STATE_RUNNING;
598 drop_block(state);
599 return;
600 }
601 }
602
603 void handle_keyboard_event(nstate *state)
604 {
605 GR_EVENT_KEYSTROKE *event = &state->event.keystroke;
606
607 switch(event->ch) {
608 case 'q':
609 case 'Q':
610 case MWKEY_CANCEL:
611 state->state = STATE_EXIT;
612 return;
613 case 'n':
614 case 'N':
615 case MWKEY_APP1:
616 state->state = STATE_NEWGAME;
617 return;
618 }
619
620 if(state->state == STATE_STOPPED) return;
621
622 state->state = STATE_RUNNING;
623
624 switch(event->ch) {
625 case 'p':
626 case 'P':
627 state->state = STATE_PAUSED;
628 break;
629 case 'j':
630 case 'J':
631 case MWKEY_LEFT:
632 move_block(state, 0);
633 break;
634 case 'k':
635 case 'K':
636 case MWKEY_RIGHT:
637 move_block(state, 1);
638 break;
639 case 'd':
640 case 'D':
641 case MWKEY_UP:
642 rotate_block(state, 0);
643 break;
644 case 'f':
645 case 'F':
646 case MWKEY_DOWN:
647 rotate_block(state, 1);
648 break;
649 case ' ':
650 case MWKEY_MENU:
651 drop_block(state);
652 break;
653 }
654 }
655
656 void handle_event(nstate *state)
657 {
658 switch(state->event.type) {
659 case GR_EVENT_TYPE_EXPOSURE:
660 handle_exposure_event(state);
661 break;
662 case GR_EVENT_TYPE_BUTTON_DOWN:
663 handle_mouse_event(state);
664 break;
665 case GR_EVENT_TYPE_KEY_DOWN:
666 handle_keyboard_event(state);
667 break;
668 case GR_EVENT_TYPE_CLOSE_REQ:
669 state->state = STATE_EXIT;
670 break;
671 case GR_EVENT_TYPE_TIMEOUT:
672 break;
673 default:
674 fprintf(stderr, "Unhandled event type %d\n",
675 state->event.type);
676 break;
677 }
678 }
679
680 void clear_well(nstate *state)
681 {
682 int x, y;
683
684 for(y = 0; y < WELL_HEIGHT; y++)
685 for(x = 0; x < WELL_WIDTH; x++) {
686 state->blocks[0][y][x] = 0;
687 state->blocks[1][y][x] = 0;
688 }
689 }
690
691 /* Dirty hack alert- this is to avoid using any floating point math */
692 int random8(int limit)
693 {
694 int ret;
695
696 do { ret = random() & 7; } while(ret > limit);
697
698 return ret;
699 }
700
701 void choose_new_shape(nstate *state)
702 {
703 state->current_shape.type = state->next_shape.type;
704 state->current_shape.orientation = state->next_shape.orientation;
705 state->current_shape.colour = state->next_shape.colour;
706 state->current_shape.x = (WELL_WIDTH / 2) - 2;
707 state->current_shape.y = WELL_NOTVISIBLE -
708 shape_sizes[state->next_shape.type]
709 [state->next_shape.orientation][1] - 1;
710 state->next_shape.type = random8(MAXSHAPES - 1);
711 state->next_shape.orientation = random8(MAXORIENTATIONS - 1);
712 state->next_shape.colour = block_colours[random8(MAX_BLOCK_COLOUR)];
713 }
714
715 void new_game(nstate *state)
716 {
717 clear_well(state);
718 if(state->score > state->hiscore) state->hiscore = state->score;
719 state->score = 0;
720 state->level = 0;
721 draw_score(state);
722 choose_new_shape(state);
723 draw_next_shape(state);
724 draw_well(state, 1);
725 if(state->state == STATE_NEWGAME) state->state = STATE_RUNNING;
726 }
727
728 void init_game(nstate *state)
729 {
730 GR_WM_PROPERTIES props;
731
732 if(GrOpen() < 0) {
733 fprintf(stderr, "Couldn't connect to Nano-X server\n");
734 exit(1);
735 }
736
737 state->main_window = GrNewWindow(GR_ROOT_WINDOW_ID,
738 MAIN_WINDOW_X_POSITION,
739 MAIN_WINDOW_Y_POSITION,
740 MAIN_WINDOW_WIDTH,
741 MAIN_WINDOW_HEIGHT, 0,
742 MAIN_WINDOW_BACKGROUND_COLOUR, 0);
743 /* set title */
744 props.flags = GR_WM_FLAGS_TITLE | GR_WM_FLAGS_PROPS;
745 props.props = GR_WM_PROPS_BORDER | GR_WM_PROPS_CAPTION;
746 props.title = "Nano-Tetris";
747 GrSetWMProperties(state->main_window, &props);
748 GrSelectEvents(state->main_window, GR_EVENT_MASK_EXPOSURE |
749 GR_EVENT_MASK_CLOSE_REQ |
750 GR_EVENT_MASK_KEY_DOWN |
751 GR_EVENT_MASK_TIMEOUT);
752
753 state->score_window = GrNewWindow(state->main_window,
754 SCORE_WINDOW_X_POSITION,
755 SCORE_WINDOW_Y_POSITION,
756 SCORE_WINDOW_WIDTH,
757 SCORE_WINDOW_HEIGHT, 0,
758 SCORE_WINDOW_BACKGROUND_COLOUR, 0);
759 GrSelectEvents(state->score_window, GR_EVENT_MASK_EXPOSURE);
760 GrMapWindow(state->score_window);
761 state->scoregcf = GrNewGC();
762 GrSetGCForeground(state->scoregcf, SCORE_WINDOW_FOREGROUND_COLOUR);
763 GrSetGCBackground(state->scoregcf, SCORE_WINDOW_BACKGROUND_COLOUR);
764 state->scoregcb = GrNewGC();
765 GrSetGCForeground(state->scoregcb, SCORE_WINDOW_BACKGROUND_COLOUR);
766
767 state->next_shape_window = GrNewWindow(state->main_window,
768 NEXT_SHAPE_WINDOW_X_POSITION,
769 NEXT_SHAPE_WINDOW_Y_POSITION,
770 NEXT_SHAPE_WINDOW_WIDTH,
771 NEXT_SHAPE_WINDOW_HEIGHT, 0,
772 NEXT_SHAPE_WINDOW_BACKGROUND_COLOUR, 0);
773 GrSelectEvents(state->next_shape_window, GR_EVENT_MASK_EXPOSURE);
774 GrMapWindow(state->next_shape_window);
775 state->nextshapegcf = GrNewGC();
776 state->nextshapegcb = GrNewGC();
777 GrSetGCForeground(state->nextshapegcb,
778 NEXT_SHAPE_WINDOW_BACKGROUND_COLOUR);
779
780 state->new_game_button = GrNewWindow(state->main_window,
781 NEW_GAME_BUTTON_X_POSITION,
782 NEW_GAME_BUTTON_Y_POSITION,
783 NEW_GAME_BUTTON_WIDTH,
784 NEW_GAME_BUTTON_HEIGHT, 0,
785 BUTTON_BACKGROUND_COLOUR, 0);
786 GrSelectEvents(state->new_game_button, GR_EVENT_MASK_EXPOSURE |
787 GR_EVENT_MASK_BUTTON_DOWN);
788 GrMapWindow(state->new_game_button);
789 state->buttongcf = GrNewGC();
790 GrSetGCForeground(state->buttongcf, BUTTON_FOREGROUND_COLOUR);
791 GrSetGCBackground(state->buttongcf, BUTTON_BACKGROUND_COLOUR);
792 state->buttongcb = GrNewGC();
793 GrSetGCForeground(state->buttongcb, BUTTON_BACKGROUND_COLOUR);
794
795 state->pause_continue_button = GrNewWindow(state->main_window,
796 PAUSE_CONTINUE_BUTTON_X_POSITION,
797 PAUSE_CONTINUE_BUTTON_Y_POSITION,
798 PAUSE_CONTINUE_BUTTON_WIDTH,
799 PAUSE_CONTINUE_BUTTON_HEIGHT, 0,
800 BUTTON_BACKGROUND_COLOUR, 0);
801 GrSelectEvents(state->pause_continue_button, GR_EVENT_MASK_EXPOSURE |
802 GR_EVENT_MASK_BUTTON_DOWN);
803
804 state->anticlockwise_button = GrNewWindow(state->main_window,
805 ANTICLOCKWISE_BUTTON_X_POSITION,
806 ANTICLOCKWISE_BUTTON_Y_POSITION,
807 ANTICLOCKWISE_BUTTON_WIDTH,
808 ANTICLOCKWISE_BUTTON_HEIGHT, 0,
809 BUTTON_BACKGROUND_COLOUR,
810 0);
811 GrSelectEvents(state->anticlockwise_button, GR_EVENT_MASK_EXPOSURE |
812 GR_EVENT_MASK_BUTTON_DOWN);
813
814 state->clockwise_button = GrNewWindow(state->main_window,
815 CLOCKWISE_BUTTON_X_POSITION,
816 CLOCKWISE_BUTTON_Y_POSITION,
817 CLOCKWISE_BUTTON_WIDTH,
818 CLOCKWISE_BUTTON_HEIGHT, 0,
819 BUTTON_BACKGROUND_COLOUR,
820 0);
821 GrSelectEvents(state->clockwise_button, GR_EVENT_MASK_EXPOSURE |
822 GR_EVENT_MASK_BUTTON_DOWN);
823
824 state->left_button = GrNewWindow(state->main_window,
825 LEFT_BUTTON_X_POSITION,
826 LEFT_BUTTON_Y_POSITION,
827 LEFT_BUTTON_WIDTH,
828 LEFT_BUTTON_HEIGHT, 0,
829 BUTTON_BACKGROUND_COLOUR,
830 0);
831 GrSelectEvents(state->left_button, GR_EVENT_MASK_EXPOSURE |
832 GR_EVENT_MASK_BUTTON_DOWN);
833
834 state->right_button = GrNewWindow(state->main_window,
835 RIGHT_BUTTON_X_POSITION,
836 RIGHT_BUTTON_Y_POSITION,
837 RIGHT_BUTTON_WIDTH,
838 RIGHT_BUTTON_HEIGHT, 0,
839 BUTTON_BACKGROUND_COLOUR,
840 0);
841 GrSelectEvents(state->right_button, GR_EVENT_MASK_EXPOSURE |
842 GR_EVENT_MASK_BUTTON_DOWN);
843
844 state->drop_button = GrNewWindow(state->main_window,
845 DROP_BUTTON_X_POSITION,
846 DROP_BUTTON_Y_POSITION,
847 DROP_BUTTON_WIDTH,
848 DROP_BUTTON_HEIGHT, 0,
849 BUTTON_BACKGROUND_COLOUR,
850 0);
851 GrSelectEvents(state->drop_button, GR_EVENT_MASK_EXPOSURE |
852 GR_EVENT_MASK_BUTTON_DOWN);
853
854 state->well_window = GrNewWindow(state->main_window,
855 WELL_WINDOW_X_POSITION,
856 WELL_WINDOW_Y_POSITION,
857 WELL_WINDOW_WIDTH,
858 WELL_WINDOW_HEIGHT, 0,
859 WELL_WINDOW_BACKGROUND_COLOUR, 0);
860 GrSelectEvents(state->well_window, GR_EVENT_MASK_EXPOSURE);
861 GrMapWindow(state->well_window);
862 state->wellgc = GrNewGC();
863
864 GrMapWindow(state->main_window);
865
866 state->state = STATE_STOPPED;
867 state->score = 0;
868 read_hiscore(state);
869 state->level = 0;
870 state->running_buttons_mapped = 0;
871
872 srandom(time(0));
873
874 choose_new_shape(state);
875 new_game(state);
876 }
877
878 void calculate_timeout(nstate *state)
879 {
880 struct timeval t;
881 long u;
882
883 gettimeofday(&t, NULL);
884 u = t.tv_usec + (delays[state->level] * 1000);
885 state->timeout.tv_sec = t.tv_sec + (u / 1000000);
886 state->timeout.tv_usec = u % 1000000;
887 }
888
889 unsigned long timeout_delay(nstate *state)
890 {
891 struct timeval t;
892 signed long s, m, ret;
893
894 gettimeofday(&t, NULL);
895
896 if((t.tv_sec > state->timeout.tv_sec) ||
897 ((t.tv_sec == state->timeout.tv_sec) &&
898 t.tv_usec >= state->timeout.tv_usec)) return 1;
899
900 s = state->timeout.tv_sec - t.tv_sec;
901 m = ((state->timeout.tv_usec - t.tv_usec) / 1000);
902 ret = (unsigned long)((1000 * s) + m);
903 /*
904 fprintf(stderr, "t.tv_sec = %ld, t.tv_usec = %ld, timeout.tv_sec = "
905 "%ld, timeout.tv_usec = %ld, s = %ld, m = %ld, ret = %ld\n",
906 t.tv_sec, t.tv_usec, state->timeout.tv_sec,
907 state->timeout.tv_usec, s, m, ret);
908 */
909 if(ret <= 0) return 1;
910 else return ret;
911 }
912
913 void do_update(nstate *state)
914 {
915 struct timeval t;
916
917 gettimeofday(&t, NULL);
918
919 if((t.tv_sec > state->timeout.tv_sec) ||
920 ((t.tv_sec == state->timeout.tv_sec) &&
921 (t.tv_usec >= state->timeout.tv_usec))) {
922 drop_block_1(state);
923 calculate_timeout(state);
924 }
925 }
926
927 void do_pause(nstate *state)
928 {
929 draw_pause_continue_button(state);
930 while(state->state == STATE_PAUSED) {
931 GrGetNextEvent(&state->event);
932 handle_event(state);
933 }
934 draw_pause_continue_button(state);
935 }
936
937 void wait_for_start(nstate *state)
938 {
939 draw_pause_continue_button(state);
940 while(state->state == STATE_STOPPED) {
941 GrGetNextEvent(&state->event);
942 handle_event(state);
943 }
944 if(state->state == STATE_NEWGAME) state->state = STATE_RUNNING;
945 draw_pause_continue_button(state);
946 calculate_timeout(state);
947 }
948
949 void run_game(nstate *state)
950 {
951 while(state->state == STATE_RUNNING) {
952 GrGetNextEventTimeout(&state->event, timeout_delay(state));
953 handle_event(state);
954 if(state->state == STATE_PAUSED) do_pause(state);
955 if(state->state == STATE_RUNNING) do_update(state);
956 }
957 }
958
959 void main_game_loop(nstate *state)
960 {
961 wait_for_start(state);
962 while(state->state != STATE_EXIT) {
963 if(state->state == STATE_RUNNING) run_game(state);
964 if(state->state == STATE_STOPPED) wait_for_start(state);
965 if(state->state != STATE_EXIT) new_game(state);
966 }
967 }
968
969 int main(int argc, char *argv[])
970 {
971 nstate *state = my_malloc(sizeof(nstate));
972
973 init_game(state);
974 main_game_loop(state);
975
976 write_hiscore(state);
977
978 GrClose();
979
980 return 0;
981 }