]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/services/gfx/mw/v2_0/src/demos/nanox/ntetris.c
Initial revision
[karo-tx-redboot.git] / packages / services / gfx / mw / v2_0 / src / demos / nanox / ntetris.c
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 static 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 static void msleep(long ms)
108 {
109         usleep(ms * 1000);
110 }
111 #else
112 static 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 static 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 static 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 static void read_hiscore(nstate *state)
176 {
177         state->hiscore = 0;
178 }
179
180 static void write_hiscore(nstate *state) {}
181 #endif
182
183 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static void drop_block(nstate *state)
513 {
514         while(!drop_block_1(state)) msleep(DROP_BLOCK_DELAY);
515 }
516
517 static 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 static 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 static 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 static 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 static 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 static int random8(int limit)
693 {
694         int ret;
695
696         do { ret = random() & 7; } while(ret > limit);
697
698         return ret;
699 }
700
701 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 #ifdef __ECOS
970 #define main ntetris_main
971 #endif
972
973 int main(int argc, char *argv[])
974 {
975         nstate *state = my_malloc(sizeof(nstate));
976
977         bzero(state, sizeof(*state));
978         init_game(state);
979         main_game_loop(state);
980
981         write_hiscore(state);
982
983         GrClose();
984
985         return 0;
986 }