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