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