Merge "res_pjsip_send_to_voicemail.c: Fix off-nominal double channel unref."
[asterisk/asterisk.git] / menuselect / menuselect_curses.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005 - 2006, Russell Bryant
5  *
6  * Russell Bryant <russell@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*
20  * \file
21  *
22  * \author Russell Bryant <russell@digium.com>
23  * 
24  * \brief curses frontend for selection maintenance
25  */
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <signal.h>
33 #include <time.h>
34 #include <curses.h>
35
36 #include "menuselect.h"
37
38 #define MENU_HELP       "Press 'h' for help."
39
40 #define TITLE_HEIGHT    7
41
42 #define MIN_X           80
43 #define MIN_Y           27
44
45 #define PAGE_OFFSET     10
46
47 #define SCROLL_NONE     0
48 #define SCROLL_DOWN     1
49
50 #define SCROLL_DOWN_INDICATOR "... More ..."
51
52 #define MIN(a, b) ({ typeof(a) __a = (a); typeof(b) __b = (b); ((__a > __b) ? __b : __a);})
53 #define MAX(a, b) ({ typeof(a) __a = (a); typeof(b) __b = (b); ((__a < __b) ? __b : __a);})
54
55 extern int changes_made;
56
57 /*! Maximum number of characters horizontally */
58 static int max_x = 0;
59 /*! Maximum number of characters vertically */
60 static int max_y = 0;
61
62 static const char * const help_info[] = {
63         "scroll              => up/down arrows",
64         "toggle selection    => Enter",
65         "select              => y",
66         "deselect            => n",
67         "select all          => F8",
68         "deselect all        => F7",
69         "back                => left arrow",
70         "quit                => q",
71         "save and quit       => x",
72         "",
73         "XXX means dependencies have not been met",
74         "    or a conflict exists",
75         "",
76         "< > means a dependency has been deselected",
77         "    and will be automatically re-selected",
78         "    if this item is selected",
79         "",
80         "( ) means a conflicting item has been",
81         "    selected",
82 };
83
84 /*! \brief Handle a window resize in xterm */
85 static void _winch_handler(int sig)
86 {
87         getmaxyx(stdscr, max_y, max_x);
88
89         if (max_x < MIN_X || max_y < MIN_Y) {
90                 fprintf(stderr, "Terminal must be at least %d x %d.\n", MIN_X, MIN_Y);
91                 max_x = MIN_X - 1;
92                 max_y = MIN_Y - 1;
93         }
94 }
95
96 static struct sigaction winch_handler = {
97         .sa_handler = _winch_handler,
98 };
99
100 /*! \brief Handle a SIGQUIT */
101 static void _sigint_handler(int sig)
102 {
103
104 }
105
106 static struct sigaction sigint_handler = {
107         .sa_handler = _sigint_handler,
108 };
109
110 /*! \brief Display help information */
111 static void show_help(WINDOW *win)
112 {
113         int i;
114
115         wclear(win);
116         for (i = 0; i < (sizeof(help_info) / sizeof(help_info[0])); i++) {
117                 wmove(win, i, max_x / 2 - 15);
118                 waddstr(win, (char *) help_info[i]);
119         }
120         wrefresh(win);
121         getch(); /* display the help until the user hits a key */
122 }
123
124 static int really_quit(WINDOW *win)
125 {
126         int c;
127         wclear(win);
128         wmove(win, 2, max_x / 2 - 15);
129         waddstr(win, "ARE YOU SURE?");
130         wmove(win, 3, max_x / 2 - 12);
131         waddstr(win, "--- It appears you have made some changes, and");
132         wmove(win, 4, max_x / 2 - 12);
133         waddstr(win, "you have opted to Quit without saving these changes!");
134         wmove(win, 6, max_x / 2 - 12);
135         waddstr(win, "  Please Enter Y to exit without saving;");
136         wmove(win, 7, max_x / 2 - 12);
137         waddstr(win, "  Enter N to cancel your decision to quit,");
138         wmove(win, 8, max_x / 2 - 12);
139         waddstr(win, "     and keep working in menuselect, or");
140         wmove(win, 9, max_x / 2 - 12);
141         waddstr(win, "  Enter S to save your changes, and exit");
142         wmove(win, 10, max_x / 2 - 12);
143         wrefresh(win);
144         while ((c=getch())) {
145                 if (c == 'Y' || c == 'y') {
146                         c = 'q';
147                         break;
148                 }
149                 if (c == 'S' || c == 's') {
150                         c = 'S';
151                         break;
152                 }
153                 if (c == 'N' || c == 'n') {
154                         c = '%';
155                         break;
156                 }
157         }
158         return c;
159 }
160
161 static void draw_main_menu(WINDOW *menu, int curopt)
162 {
163         struct category *cat;
164         char buf[64];
165         int i = 0;
166
167         wclear(menu);
168
169         AST_LIST_TRAVERSE(&categories, cat, list) {
170                 wmove(menu, i++, max_x / 2 - 10);
171                 snprintf(buf, sizeof(buf), " %s", strlen_zero(cat->displayname) ? cat->name : cat->displayname);
172                 waddstr(menu, buf);
173         }
174
175         wmove(menu, curopt, (max_x / 2) - 15);
176         waddstr(menu, "--->");
177         wmove(menu, 0, 0);
178
179         wrefresh(menu);
180 }
181
182 static void display_mem_info(WINDOW *menu, struct member *mem, int start, int end)
183 {
184         char buf[64];
185         struct reference *dep;
186         struct reference *con;
187         struct reference *use;
188
189         wmove(menu, end - start + 2, max_x / 2 - 16);
190         wclrtoeol(menu);
191         wmove(menu, end - start + 3, max_x / 2 - 16);
192         wclrtoeol(menu);
193         wmove(menu, end - start + 4, max_x / 2 - 16);
194         wclrtoeol(menu);
195         wmove(menu, end - start + 5, max_x / 2 - 16);
196         wclrtoeol(menu);
197         wmove(menu, end - start + 6, max_x / 2 - 16);
198         wclrtoeol(menu);
199
200         if (mem->displayname) {
201                 wmove(menu, end - start + 2, max_x / 2 - 16);
202                 waddstr(menu, (char *) mem->displayname);
203         }
204         if (!AST_LIST_EMPTY(&mem->deps)) {
205                 wmove(menu, end - start + 3, max_x / 2 - 16);
206                 strcpy(buf, "Depends on: ");
207                 AST_LIST_TRAVERSE(&mem->deps, dep, list) {
208                         strncat(buf, dep->displayname, sizeof(buf) - strlen(buf) - 1);
209                         strncat(buf, dep->member ? "(M)" : "(E)", sizeof(buf) - strlen(buf) - 1);
210                         if (AST_LIST_NEXT(dep, list))
211                                 strncat(buf, ", ", sizeof(buf) - strlen(buf) - 1);
212                 }
213                 waddstr(menu, buf);
214         }
215         if (!AST_LIST_EMPTY(&mem->uses)) {
216                 wmove(menu, end - start + 4, max_x / 2 - 16);
217                 strcpy(buf, "Can use: ");
218                 AST_LIST_TRAVERSE(&mem->uses, use, list) {
219                         strncat(buf, use->displayname, sizeof(buf) - strlen(buf) - 1);
220                         strncat(buf, use->member ? "(M)" : "(E)", sizeof(buf) - strlen(buf) - 1);
221                         if (AST_LIST_NEXT(use, list))
222                                 strncat(buf, ", ", sizeof(buf) - strlen(buf) - 1);
223                 }
224                 waddstr(menu, buf);
225         }
226         if (!AST_LIST_EMPTY(&mem->conflicts)) {
227                 wmove(menu, end - start + 5, max_x / 2 - 16);
228                 strcpy(buf, "Conflicts with: ");
229                 AST_LIST_TRAVERSE(&mem->conflicts, con, list) {
230                         strncat(buf, con->displayname, sizeof(buf) - strlen(buf) - 1);
231                         strncat(buf, con->member ? "(M)" : "(E)", sizeof(buf) - strlen(buf) - 1);
232                         if (AST_LIST_NEXT(con, list))
233                                 strncat(buf, ", ", sizeof(buf) - strlen(buf) - 1);
234                 }
235                 waddstr(menu, buf);
236         }
237
238         if (!mem->is_separator) { /* Separators lack support levels */
239                 { /* support level */
240                         wmove(menu, end - start + 6, max_x / 2 - 16);
241                         snprintf(buf, sizeof(buf), "Support Level: %s", mem->support_level);
242                         if (mem->replacement && *mem->replacement) {
243                                 char buf2[64];
244                                 snprintf(buf2, sizeof(buf2), ", Replaced by: %s", mem->replacement);
245                                 strncat(buf, buf2, sizeof(buf) - strlen(buf) - 1);
246                         }
247                         waddstr(menu, buf);
248                 }
249         }
250 }
251
252 static void draw_category_menu(WINDOW *menu, struct category *cat, int start, int end, int curopt, int changed, int flags)
253 {
254         int i = 0;
255         int j = 0;
256         struct member *mem;
257         char buf[64];
258
259         if (!changed) {
260                 /* If all we have to do is move the cursor, 
261                  * then don't clear the screen and start over */
262                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
263                         i++;
264                         if (curopt + 1 == i) {
265                                 display_mem_info(menu, mem, start, end);
266                                 break;
267                         }
268                 }
269                 wmove(menu, curopt - start, max_x / 2 - 9);
270                 wrefresh(menu);
271                 return;
272         }
273
274         wclear(menu);
275
276         i = 0;
277         AST_LIST_TRAVERSE(&cat->members, mem, list) {
278                 if (i < start) {
279                         i++;
280                         continue;
281                 }
282                 wmove(menu, j++, max_x / 2 - 10);
283                 i++;
284                 if ((mem->depsfailed == HARD_FAILURE) || (mem->conflictsfailed == HARD_FAILURE)) {
285                         snprintf(buf, sizeof(buf), "XXX %s", mem->name);
286                 } else if (mem->is_separator) {
287                         snprintf(buf, sizeof(buf), "    --- %s ---", mem->name);
288                 } else if (mem->depsfailed == SOFT_FAILURE) {
289                         snprintf(buf, sizeof(buf), "<%s> %s", mem->enabled ? "*" : " ", mem->name);
290                 } else if (mem->conflictsfailed == SOFT_FAILURE) {
291                         snprintf(buf, sizeof(buf), "(%s) %s", mem->enabled ? "*" : " ", mem->name);
292                 } else {
293                         snprintf(buf, sizeof(buf), "[%s] %s", mem->enabled ? "*" : " ", mem->name);
294                 }
295                 waddstr(menu, buf);
296                 
297                 if (curopt + 1 == i)
298                         display_mem_info(menu, mem, start, end);
299
300                 if (i == end - (flags & SCROLL_DOWN ? 1 : 0))
301                         break;
302         }
303
304         if (flags & SCROLL_DOWN) {
305                 wmove(menu, j, max_x / 2 - sizeof(SCROLL_DOWN_INDICATOR) / 2);
306                 waddstr(menu, SCROLL_DOWN_INDICATOR);
307         }
308
309         wmove(menu, curopt - start, max_x / 2 - 9);
310         wrefresh(menu);
311 }
312
313 static void play_space(void);
314
315 static int move_up(int *current, int itemcount, int delta, int *start, int *end, int scroll)
316 {
317         if (*current > 0) {
318                 *current = MAX(*current - delta, 0);
319                 if (*current < *start) {
320                         int diff = *start - MAX(*start - delta, 0);
321                         *start  -= diff;
322                         *end    -= diff;
323                         return 1;
324                 }
325         }
326         return 0;
327 }
328
329 static int move_down(int *current, int itemcount, int delta, int *start, int *end, int scroll)
330 {
331         if (*current < itemcount) {
332                 *current = MIN(*current + delta, itemcount);
333                 if (*current > *end - 1 - (scroll & SCROLL_DOWN ? 1 : 0)) {
334                         int diff = MIN(*end + delta - 1, itemcount) - *end + 1;
335                         *start  += diff;
336                         *end    += diff;
337                         return 1;
338                 }
339         }
340         return 0;
341 }
342
343 static int run_category_menu(WINDOW *menu, int cat_num)
344 {
345         struct category *cat;
346         int i = 0;
347         int start = 0;
348         int end = max_y - TITLE_HEIGHT - 8;
349         int c;
350         int curopt = 0;
351         int maxopt;
352         int changed = 1;
353         int scroll = SCROLL_NONE;
354
355         AST_LIST_TRAVERSE(&categories, cat, list) {
356                 if (i++ == cat_num)
357                         break;
358         }
359         if (!cat)
360                 return -1;
361
362         maxopt = count_members(cat) - 1;
363
364         if (maxopt > end) {
365                 scroll = SCROLL_DOWN;
366         }
367
368         draw_category_menu(menu, cat, start, end, curopt, changed, scroll);
369
370         while ((c = getch())) {
371                 changed = 0;
372                 switch (c) {
373                 case KEY_UP:
374                         changed = move_up(&curopt, maxopt, 1, &start, &end, scroll);
375                         break;
376                 case KEY_DOWN:
377                         changed = move_down(&curopt, maxopt, 1, &start, &end, scroll);
378                         break;
379                 case KEY_PPAGE:
380                         changed = move_up(
381                                 &curopt,
382                                 maxopt,
383                                 MIN(PAGE_OFFSET, max_y - TITLE_HEIGHT - 6 - (scroll & SCROLL_DOWN ? 1 : 0)),
384                                 &start,
385                                 &end,
386                                 scroll);
387                         break;
388                 case KEY_NPAGE:
389                         changed = move_down(
390                                 &curopt,
391                                 maxopt,
392                                 MIN(PAGE_OFFSET, max_y - TITLE_HEIGHT - 6 - (scroll & SCROLL_DOWN ? 1 : 0)),
393                                 &start,
394                                 &end,
395                                 scroll);
396                         break;
397                 case KEY_HOME:
398                         changed = move_up(&curopt, maxopt, curopt, &start, &end, scroll);
399                         break;
400                 case KEY_END:
401                         changed = move_down(&curopt, maxopt, maxopt - curopt, &start, &end, scroll);
402                         break;
403                 case KEY_LEFT:
404                 case 27:        /* Esc key */
405                         return 0;
406                 case KEY_RIGHT:
407                 case KEY_ENTER:
408                 case '\n':
409                 case ' ':
410                         toggle_enabled_index(cat, curopt);
411                         changed = 1;
412                         break;
413                 case 'y':
414                 case 'Y':
415                         set_enabled(cat, curopt);
416                         changed = 1;
417                         break;
418                 case 'n':
419                 case 'N':
420                         clear_enabled(cat, curopt);
421                         changed = 1;
422                         break;
423                 case 'h':
424                 case 'H':
425                         show_help(menu);
426                         changed = 1;
427                         break;
428                 case KEY_F(7):
429                         set_all(cat, 0);
430                         changed = 1;
431                         break;
432                 case KEY_F(8):
433                         set_all(cat, 1);
434                         changed = 1;
435                 default:
436                         break;  
437                 }
438                 if (c == 'x' || c == 'X' || c == 'Q' || c == 'q')
439                         break;
440
441                 if (end <= maxopt) {
442                         scroll |= SCROLL_DOWN;
443                 } else {
444                         scroll &= ~SCROLL_DOWN;
445                 }
446
447                 draw_category_menu(menu, cat, start, end, curopt, changed, scroll);
448         }
449
450         wrefresh(menu);
451
452         return c;
453 }
454
455 static void draw_title_window(WINDOW *title)
456 {
457         char titlebar[strlen(menu_name) + 9];
458
459         memset(titlebar, '*', sizeof(titlebar) - 1);
460         titlebar[sizeof(titlebar) - 1] = '\0';
461         wclear(title);
462         wmove(title, 1, (max_x / 2) - (strlen(titlebar) / 2));
463         waddstr(title, titlebar);
464         wmove(title, 2, (max_x / 2) - (strlen(menu_name) / 2));
465         waddstr(title, (char *) menu_name);
466         wmove(title, 3, (max_x / 2) - (strlen(titlebar) / 2));
467         waddstr(title, titlebar);
468         wmove(title, 5, (max_x / 2) - (strlen(MENU_HELP) / 2));
469         waddstr(title, MENU_HELP);
470         wrefresh(title);
471 }
472
473 int run_menu(void)
474 {
475         WINDOW *title;
476         WINDOW *menu;
477         int maxopt;
478         int curopt = 0;
479         int c;
480         int res = 0;
481
482         setenv("ESCDELAY", "0", 1); /* So that ESC is processed immediately */
483
484         initscr();
485         getmaxyx(stdscr, max_y, max_x);
486         sigaction(SIGWINCH, &winch_handler, NULL); /* handle window resizing in xterm */
487         sigaction(SIGINT, &sigint_handler, NULL); /* handle window resizing in xterm */
488
489         if (max_x < MIN_X || max_y < MIN_Y) {
490                 fprintf(stderr, "Terminal must be at least %d x %d.\n", MIN_X, MIN_Y);
491                 endwin();
492                 return -1;
493         }
494
495         cbreak(); /* don't buffer input until the enter key is pressed */
496         noecho(); /* don't echo user input to the screen */
497         keypad(stdscr, TRUE); /* allow the use of arrow keys */
498         clear();
499         refresh();
500
501         maxopt = count_categories() - 1;
502         
503         /* We have two windows - the title window at the top, and the menu window gets the rest */
504         title = newwin(TITLE_HEIGHT, max_x, 0, 0);
505         menu = newwin(max_y - TITLE_HEIGHT, max_x, TITLE_HEIGHT, 0);
506         draw_title_window(title);       
507         draw_main_menu(menu, curopt);
508         
509         while ((c = getch())) {
510                 switch (c) {
511                 case KEY_UP:
512                         if (curopt > 0)
513                                 curopt--;
514                         break;
515                 case KEY_DOWN:
516                         if (curopt < maxopt)
517                                 curopt++;
518                         break;
519                 case KEY_HOME:
520                         curopt = 0;
521                         break;
522                 case KEY_END:
523                         curopt = maxopt;
524                         break;
525                 case KEY_RIGHT:
526                 case KEY_ENTER:
527                 case '\n':
528                 case ' ':
529                         c = run_category_menu(menu, curopt);
530                         break;
531                 case 'h':
532                 case 'H':
533                         show_help(menu);
534                         break;
535                 case 'i':
536                 case 'I':
537                         play_space();
538                         draw_title_window(title);
539                 default:
540                         break;  
541                 }
542                 if (c == 'q' || c == 'Q' || c == 27 || c == 3) {
543                         if (changes_made) {
544                                 c = really_quit(menu);
545                                 if (c == 'q') {
546                                         res = -1;
547                                         break;
548                                 }
549                         } else {
550                                 res = -1;
551                                 break;
552                         }
553                 }
554                 if (c == 'x' || c == 'X' || c == 's' || c == 'S')
555                         break;  
556                 draw_main_menu(menu, curopt);
557         }
558
559         endwin();
560
561         return res;
562 }
563
564 enum blip_type {
565         BLIP_TANK = 0,
566         BLIP_SHOT,
567         BLIP_BOMB,
568         BLIP_ALIEN,
569         BLIP_BARRIER,
570         BLIP_UFO
571 };
572
573 struct blip {
574         enum blip_type type;
575         int x;
576         int y;
577         int ox;
578         int oy;
579         int goingleft;
580         int health;
581         AST_LIST_ENTRY(blip) entry;
582 };
583
584 static AST_LIST_HEAD_NOLOCK(, blip) blips;
585
586 static int respawn = 0;
587 static int score = 0;
588 static int num_aliens = 0;
589 static int alien_sleeptime = 0;
590 struct blip *ufo = NULL;
591 struct blip *tank = NULL;
592
593 /*! Probability of a bomb, out of 100 */
594 #define BOMB_PROB   1
595
596 static int add_barrier(int x, int y)
597 {
598         struct blip *cur = NULL;
599
600         cur = calloc(1,sizeof(struct blip));
601         if(!cur) {
602                 return -1;
603         }
604         cur->type=BLIP_BARRIER;
605         cur->x = x;
606         cur->y=max_y - y;
607         cur->health = 1;
608         AST_LIST_INSERT_HEAD(&blips, cur,entry);
609         return 0;
610 }
611
612 static int init_blips(void)
613 {
614         int i, j;
615         struct blip *cur;
616         int offset = 4;
617
618         srandom(time(NULL) + getpid());
619
620         /* make tank */
621         cur = calloc(1, sizeof(struct blip));
622         if (!cur)
623                 return -1;
624         cur->type = BLIP_TANK;
625         cur->x = max_x / 2;
626         cur->y = max_y - 1;
627         AST_LIST_INSERT_HEAD(&blips, cur, entry);
628         tank = cur;
629
630         /* 3 rows of 10 aliens */
631         num_aliens = 0;
632         for (i = 0; i < 3; i++) {
633                 for (j = 0; j < 10; j++) {
634                         cur = calloc(1, sizeof(struct blip));
635                         if (!cur)
636                                 return -1;
637                         cur->type = BLIP_ALIEN;
638                         cur->x = (j * 2) + 1;
639                         cur->y = (i * 2) + 2;
640                         AST_LIST_INSERT_HEAD(&blips, cur, entry);
641                         num_aliens++;
642                 }
643         }
644         for(i=0; i < 4; i++) {
645                 if (i > 0)
646                         offset += 5 + ((max_x) -28) / 3;
647                 add_barrier(offset + 1, 6);
648                 add_barrier(offset + 2, 6);
649                 add_barrier(offset + 3, 6);
650
651                 add_barrier(offset, 5);
652                 add_barrier(offset + 1, 5);
653                 add_barrier(offset + 2, 5);
654                 add_barrier(offset + 3, 5);
655                 add_barrier(offset + 4, 5);
656
657                 add_barrier(offset, 4);
658                 add_barrier(offset + 1, 4);
659                 add_barrier(offset + 3, 4);
660                 add_barrier(offset + 4, 4);
661         }
662         return 0;
663 }
664
665 static inline chtype type2chtype(enum blip_type type)
666 {
667         switch (type) {
668         case BLIP_TANK:
669                 return 'A';
670         case BLIP_ALIEN:
671                 return 'X';
672         case BLIP_SHOT:
673                 return '|';
674         case BLIP_BOMB:
675                 return 'o';
676         case BLIP_BARRIER:
677                 return '*';
678         case BLIP_UFO:
679                 return '@';
680         default:
681                 break;
682         }
683         return '?';
684 }
685
686 static int repaint_screen(void)
687 {
688         struct blip *cur;
689
690         wmove(stdscr, 0, 0);
691         wprintw(stdscr, "Score: %d", score);
692
693         AST_LIST_TRAVERSE(&blips, cur, entry) {
694                 if (cur->x != cur->ox || cur->y != cur->oy) {
695                         wmove(stdscr, cur->oy, cur->ox);
696                         waddch(stdscr, ' ');
697                         wmove(stdscr, cur->y, cur->x);
698                         waddch(stdscr, type2chtype(cur->type)); 
699                         cur->ox = cur->x;
700                         cur->oy = cur->y;
701                 }
702         }
703
704         wmove(stdscr, 0, max_x - 1);
705
706         wrefresh(stdscr);
707
708         return 0;
709 }
710
711 static int tank_move_left(void)
712 {
713         if (tank->x > 0)
714                 tank->x--;
715         
716         return 0;
717 }
718
719 static int tank_move_right(void)
720 {
721         if (tank->x < (max_x - 1))
722                 tank->x++;
723
724         return 0;
725 }
726
727 static int count_shots(void)
728 {
729         struct blip *cur;
730         int count = 0;
731
732         AST_LIST_TRAVERSE(&blips, cur, entry) {
733                 if (cur->type == BLIP_SHOT)
734                         count++;
735         }
736
737         return count;
738 }
739
740 static int tank_shoot(void)
741 {
742         struct blip *shot;
743
744         if (count_shots() == 3)
745                 return 0;
746
747         score--;
748
749         shot = calloc(1, sizeof(struct blip));
750         if (!shot)
751                 return -1;
752         shot->type = BLIP_SHOT;
753         shot->x = tank->x;
754         shot->y = max_y - 2;
755         AST_LIST_INSERT_HEAD(&blips, shot, entry);
756
757         return 0;
758 }
759
760 static int remove_blip(struct blip *blip)
761 {
762         if (!blip) {
763                 return -1;
764         }
765
766         AST_LIST_REMOVE(&blips, blip, entry);
767
768         if (blip->type == BLIP_ALIEN) {
769                 num_aliens--;
770         }
771         wmove(stdscr, blip->oy, blip->ox);
772         waddch(stdscr, ' ');
773         free(blip);
774
775         return 0;
776 }
777
778 static int move_aliens(void)
779 {
780         struct blip *cur;
781         struct blip *current_barrier;
782
783         AST_LIST_TRAVERSE(&blips, cur, entry) {
784                 if (cur->type != BLIP_ALIEN) {
785                         /* do nothing if it's not an alien */
786                         continue;
787                 }
788                 if (cur->goingleft && (cur->x == 0)) {
789                         cur->y++;
790                         cur->goingleft = 0;
791                 } else if (!cur->goingleft && cur->x == (max_x - 1)) {
792                         cur->y++;
793                         cur->goingleft = 1;
794                 } else if (cur->goingleft) {
795                         cur->x--;
796                 } else {
797                         cur->x++;
798                 }
799                 /* Alien into the tank == game over */
800                 if (cur->x == tank->x && cur->y == tank->y)
801                         return 1;
802                 AST_LIST_TRAVERSE(&blips, current_barrier, entry){
803                         if(current_barrier->type!=BLIP_BARRIER)
804                                 continue;
805                         if(cur->y == current_barrier->y && cur->x == current_barrier -> x)
806                                 remove_blip(current_barrier);
807                 }
808                 if (random() % 100 < BOMB_PROB && cur->y != max_y) {
809                         struct blip *bomb = calloc(1, sizeof(struct blip));
810                         if (!bomb)
811                                 continue;
812                         bomb->type = BLIP_BOMB;
813                         bomb->x = cur->x;
814                         bomb->y = cur->y + 1;
815                         AST_LIST_INSERT_HEAD(&blips, bomb, entry);
816                 }
817         }
818
819         return 0;
820 }
821
822 static int move_bombs(void)
823 {
824         struct blip *cur;
825         struct blip *current_barrier;
826
827         AST_LIST_TRAVERSE(&blips, cur, entry) {
828                 int mark = 0;
829                 if (cur->type != BLIP_BOMB)
830                         continue;
831                 cur->y++;
832                 if (cur->x == tank->x && cur->y == tank->y) {
833                         return 1;
834                 }
835
836                 AST_LIST_TRAVERSE(&blips, current_barrier, entry) {
837                         if (current_barrier->type != BLIP_BARRIER)
838                                 continue;
839                         if (cur->x == current_barrier->x && cur->y == current_barrier->y) {
840                                 mark = 1;
841                                 current_barrier->health--;
842                                 if (current_barrier->health == 0)
843                                         remove_blip(current_barrier);
844                         }
845                 }
846                 if (mark){
847                         remove_blip(cur);}
848         }
849
850         return 0;
851 }
852
853 static void move_shots(void)
854 {
855         struct blip *cur;
856
857         AST_LIST_TRAVERSE(&blips, cur, entry) {
858                 if (cur->type != BLIP_SHOT)
859                         continue;
860                 cur->y--;
861         }
862 }
863
864
865 static int ufo_action()
866 {
867         struct blip *cur;
868
869         AST_LIST_TRAVERSE(&blips, cur, entry) {
870                 if (cur->type != BLIP_UFO) {
871                         continue;
872                 }
873
874                 cur->x--;
875
876                 if (cur->x < 0) {
877                         remove_blip(cur);
878                         respawn += 1;
879                 }
880
881         }
882
883         if (respawn == 7) {
884                 respawn = 0;
885                 /* make new mothership*/
886                 cur = calloc(1, sizeof(struct blip));
887                 if(!cur)
888                         return -1;
889                 cur->type = BLIP_UFO;
890                 cur->x = max_x - 1;
891                 cur->y = 1;
892                 AST_LIST_INSERT_HEAD(&blips, cur, entry);
893         }
894
895         return 0;
896 }
897
898 static void game_over(int win)
899 {
900         clear();
901
902         wmove(stdscr, max_y / 2, max_x / 2 - 10);
903         wprintw(stdscr, "Game over!  You %s!", win ? "win" : "lose");
904
905         wmove(stdscr, 0, max_x - 1);
906
907         wrefresh(stdscr);
908
909         sleep(1);
910
911         while (getch() != ' ');
912
913         return;
914 }
915
916 static int check_shot(struct blip *shot)
917 {
918         struct blip *cur;
919
920         AST_LIST_TRAVERSE(&blips, cur, entry) {
921                 if ((cur->type == BLIP_ALIEN || cur->type == BLIP_UFO) && cur->x == shot->x && cur->y == shot->y){
922                         if (cur->type == BLIP_UFO) {
923                                 score += 80;
924                         }
925                         score += 20;
926                         remove_blip(cur);
927                         remove_blip(shot);
928                         respawn += 1;
929                         if (!num_aliens) {
930                                 if(alien_sleeptime < 101) {
931                                         game_over(1);
932                                         return 1;
933                                 } else {
934                                         alien_sleeptime = alien_sleeptime - 100;
935                                         return 1;
936                                 }
937                         }
938                         break;
939                 }
940                 if (cur->type == BLIP_BARRIER) {
941                         if (shot->x == cur->x && shot->y == cur->y) {
942                                 remove_blip(cur);
943                                 remove_blip(shot);
944                                 break;
945                         }
946                 }
947         }
948
949         return 0;
950 }
951
952 static int check_placement(void)
953 {
954         struct blip *cur;
955
956         AST_LIST_TRAVERSE_SAFE_BEGIN(&blips, cur, entry) {
957                 if (cur->y <= 0 || cur->y >= max_y) {
958                         AST_LIST_REMOVE_CURRENT(&blips, entry);
959                         remove_blip(cur);
960                 } else if (cur->type == BLIP_SHOT && check_shot(cur))
961                         return 1;
962         }
963         AST_LIST_TRAVERSE_SAFE_END
964
965         return 0;
966 }
967
968 static void play_space(void)
969 {
970         int c;
971         unsigned int jiffies = 1;
972         int quit = 0;
973         struct blip *blip;
974         alien_sleeptime = 1000;
975         score = 0;
976
977         while(alien_sleeptime > 100) {
978
979                 jiffies = 1;
980                 clear();
981                 nodelay(stdscr, TRUE);
982                 init_blips();
983                 repaint_screen();
984
985                 for (;;) {
986                         c = getch();
987                         switch (c) {
988                         case ' ':
989                                 tank_shoot();
990                                 break;
991                         case KEY_LEFT:
992                                 tank_move_left();
993                                 break;
994                         case KEY_RIGHT:
995                                 tank_move_right();
996                                 break;
997                         case 'x':
998                         case 'X':
999                         case 'q':
1000                         case 'Q':
1001                                 quit = 1;
1002                         default:
1003                                 /* ignore unknown input */
1004                                 break;
1005                         }
1006                         if (quit) {
1007                                 alien_sleeptime = 1;
1008                                 break;
1009                         }
1010                         if (!(jiffies % 25)) {
1011                                 if (move_aliens() || move_bombs() || ufo_action()) {
1012                                         alien_sleeptime = 1;
1013                                         game_over(0);
1014                                         break;
1015                                 }
1016                                 if (check_placement())
1017                                         break;
1018                         }
1019                         if (!(jiffies % 10)) {
1020                                 move_shots();
1021                                 if (check_placement())
1022                                         break;
1023                         }
1024                         repaint_screen();
1025                         jiffies++;
1026                         usleep(alien_sleeptime);
1027                 }
1028
1029                 while ((blip = AST_LIST_REMOVE_HEAD(&blips, entry)))
1030                         free(blip);
1031         }
1032
1033         nodelay(stdscr, FALSE);
1034 }