Put manpage in right place - really, this time
[cluster-algebra-visualize.git] / main.c
blobb62c682fd8a9ab4968b5c124799916eb0ba9367d
1 /*
2 * Copyright (c) 2016, S. Gilles <sgilles@math.umd.edu>
4 * Permission to use, copy, modify, and/or distribute this software
5 * for any purpose with or without fee is hereby granted, provided
6 * that the above copyright notice and this permission notice appear
7 * in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <errno.h>
20 #include <locale.h>
21 #include <math.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
27 #include <curses.h>
29 uint_fast8_t enough_colors = 0;
31 /* what the program is doing */
32 typedef enum show {
33 /* */
34 SH_NORMAL, SH_NAMES
35 } show;
37 /* strings for arrow bodies */
38 char *dirstr[] = {
39 /* */
40 [(-1 + 1) * 3 + (-1 + 1)] = "/", /* */
41 [(-1 + 1) * 3 + (0 + 1)] = "|", /* */
42 [(-1 + 1) * 3 + (1 + 1)] = "\\", /* */
43 [(0 + 1) * 3 + (-1 + 1)] = "-", /* */
44 [(0 + 1) * 3 + (0 + 1)] = "o", /* */
45 [(0 + 1) * 3 + (1 + 1)] = "-", /* */
46 [(1 + 1) * 3 + (-1 + 1)] = "\\", /* */
47 [(1 + 1) * 3 + (0 + 1)] = "|", /* */
48 [(1 + 1) * 3 + (1 + 1)] = "/", /* */
51 /* strings for arrow heads */
52 char *arrstr[] = {
53 /* */
54 [(-1 + 1) * 3 + (-1 + 1)] = "\u2b69", /* */
55 [(-1 + 1) * 3 + (0 + 1)] = "v", /* */
56 [(-1 + 1) * 3 + (1 + 1)] = "\u2b68", /* */
57 [(0 + 1) * 3 + (-1 + 1)] = "<", /* */
58 [(0 + 1) * 3 + (0 + 1)] = "\u2b59", /* */
59 [(0 + 1) * 3 + (1 + 1)] = ">", /* */
60 [(1 + 1) * 3 + (-1 + 1)] = "\u2b66", /* */
61 [(1 + 1) * 3 + (0 + 1)] = "^", /* */
62 [(1 + 1) * 3 + (1 + 1)] = "\u2b67", /* */
65 /* an arrow */
66 typedef struct arrow {
67 /* index of the point from */
68 size_t from;
70 /* index of the point to */
71 size_t to;
73 /* multiplicity of the arrow */
74 int16_t multiplicity;
75 } arrow;
77 /* x-y coords */
78 typedef struct coord {
79 /* */
80 int x;
82 /* */
83 int y;
84 } coord;
86 /* A cluster algebra */
87 typedef struct ca {
88 /* contains leftmost and bottommost */
89 coord bl;
91 /* contains rightmost and topmost */
92 coord ur;
94 /* how many points */
95 size_t point_num;
97 /* x-coords of each point */
98 coord *point_coord;
100 /* how many arrows */
101 size_t arrow_num;
103 /* arrow data */
104 arrow *arrow;
105 } ca;
107 /* given x and y, return the index of the point (possibly created) */
108 static int
109 find_make_point(int x,
110 int y,
111 size_t *out_idx,
112 ca *data)
114 size_t i;
115 coord *c;
116 void *newmem;
118 for (i = 0; i < data->point_num; ++i) {
119 c = &data->point_coord[i];
121 if (c->x == x &&
122 c->y == y) {
123 if (out_idx) {
124 *out_idx = i;
127 return 0;
131 data->point_num++;
133 if (!(newmem = realloc(data->point_coord, data->point_num *
134 sizeof *data->point_coord))) {
135 data->point_num--;
137 return ENOMEM;
140 data->point_coord = newmem;
141 data->point_coord[i] = (coord) { .x = x, .y = y };
143 if (out_idx) {
144 *out_idx = i;
147 return 0;
150 /* add an arrow p1 -> p2 in data */
151 static int
152 add_arrow(size_t p1,
153 size_t p2,
154 int mult,
155 ca *data)
157 size_t i;
158 arrow *a;
159 void *newmem;
161 if (p1 >= data->point_num ||
162 p2 >= data->point_num ||
163 p1 == p2) {
164 return EINVAL;
167 for (i = 0; i < data->arrow_num; ++i) {
168 a = &data->arrow[i];
170 if (a->from == p1 &&
171 a->to == p2) {
172 a->multiplicity += mult;
174 return 0;
175 } else if (a->from == p2 &&
176 a->to == p1) {
177 a->multiplicity -= mult;
179 return 0;
183 data->arrow_num++;
185 if (!(newmem = realloc(data->arrow, data->arrow_num *
186 sizeof *data->arrow))) {
187 data->arrow_num--;
189 return ENOMEM;
192 data->arrow = newmem;
193 data->arrow[data->arrow_num - 1] = (arrow) { .from = p1, .to = p2,
194 .multiplicity = mult };
196 return 0;
199 /* fix up the bl and ur data */
200 static void
201 recalc_extents(ca *data)
203 size_t i;
205 data->bl = data->point_coord[0];
206 data->ur = data->point_coord[0];
208 for (i = 0; i < data->point_num; ++i) {
209 coord *c = &data->point_coord[i];
211 if (data->bl.x > c->x) {
212 data->bl.x = c->x;
215 if (data->bl.y > c->y) {
216 data->bl.y = c->y;
219 if (data->ur.x < c->x) {
220 data->ur.x = c->x;
223 if (data->ur.y < c->y) {
224 data->ur.y = c->y;
229 /* read a file descriptor, initialize from it */
230 static int
231 init_from_file(FILE *f,
232 ca *data)
235 * each line should be in form
237 * (x,y)
238 * or
239 * (x,y) -> (x,y)
241 * all referenced points exist, referenced arrows affect
242 * multiplicity.
244 char *line = 0;
245 size_t n = 0;
246 ssize_t err = 0;
248 while (getline(&line, &n, f) >= 0) {
249 long x1 = 0;
250 long y1 = 0;
251 size_t p1 = 0;
252 long x2 = 0;
253 long y2 = 0;
254 size_t p2 = 0;
255 char *s;
257 if (!(s = strchr(line, '('))) {
258 continue;
261 errno = 0;
262 x1 = strtol(++s, 0, 10);
264 if (errno) {
265 continue;
268 if (!(s = strchr(s, ','))) {
269 continue;
272 y1 = strtol(++s, 0, 10);
274 if (errno) {
275 continue;
278 if ((err = find_make_point(x1, y1, &p1, data))) {
279 goto done;
282 if (!(s = strstr(s, "->"))) {
283 continue;
286 if (!(s = strchr(s, '('))) {
287 continue;
290 errno = 0;
291 x2 = strtol(++s, 0, 10);
293 if (errno) {
294 continue;
297 if (!(s = strchr(s, ','))) {
298 continue;
301 y2 = strtol(++s, 0, 10);
303 if (errno) {
304 continue;
307 if ((err = find_make_point(x2, y2, &p2, data))) {
308 goto done;
311 if ((err = add_arrow(p1, p2, 1, data))) {
312 goto done;
316 if (errno != EINVAL &&
317 !err) {
318 err = errno;
321 if (data->point_num == 0) {
322 goto done;
325 recalc_extents(data);
326 done:
327 free(line);
329 return (int) err;
332 /* draw the algebra */
333 static void
334 draw_ca(ca *data,
335 show s,
336 int width,
337 int height,
338 int cx,
339 int cy)
341 size_t i;
343 /* draw the lines */
344 for (i = 0; i < data->arrow_num; ++i) {
345 arrow *a = &data->arrow[i];
346 coord cur = { 0 };
347 coord target = { 0 };
348 int xdelta_orig;
349 int ydelta_orig;
350 int cpair = 0;
352 if (!a->multiplicity ||
353 a->from == a->to) {
354 continue;
357 cur = data->point_coord[(a->multiplicity > 0) ? a->from :
358 a->to];
359 target = data->point_coord[(a->multiplicity > 0) ? a->to :
360 a->from];
361 xdelta_orig = target.x - cur.x;
362 ydelta_orig = target.y - cur.y;
363 cpair = (int) (M_PI / 8.0 + 4.0 * M_1_PI * atan2(
364 (double) ydelta_orig,
365 (double) xdelta_orig) + 8);
366 cpair = ((cpair + 8) % 8) + 1;
368 while (cur.x != target.x ||
369 cur.y != target.y) {
370 int xdelta_cur = target.x - cur.x;
371 int ydelta_cur = target.y - cur.y;
372 int xdir = (xdelta_cur > 0) - (xdelta_cur < 0);
373 int ydir = (ydelta_cur > 0) - (ydelta_cur < 0);
375 if (xdelta_orig == 0 ||
376 xdelta_cur == 0) {
377 xdir = 0;
378 } else if (ydelta_orig == 0 ||
379 ydelta_cur == 0) {
380 ydir = 0;
381 } else if ((xdelta_orig / ydelta_orig != xdelta_cur /
382 ydelta_cur) ||
383 (ydelta_orig / xdelta_orig != ydelta_cur /
384 xdelta_cur)) {
385 xdir *= !!(xdelta_cur / ydelta_cur);
386 ydir *= !!(ydelta_cur / xdelta_cur);
389 if (height - cur.y + cy - (height / 2) >= 0 &&
390 -cur.y + cy - (height / 2) < -1 &&
391 cur.x - cx + (width / 2) < width &&
392 cur.x - cx + (width / 2) >= 0) {
393 move(height - cur.y + cy - (height / 2), cur.x -
394 cx + (width / 2));
396 if (a->multiplicity > 1) {
397 attron(A_STANDOUT);
400 if (enough_colors) {
401 attron(COLOR_PAIR(cpair));
404 if (cur.x + xdir == target.x &&
405 cur.y + ydir == target.y) {
406 addstr(arrstr[(ydir + 1) * 3 + (xdir +
407 1)]);
408 } else {
409 addstr(dirstr[(ydir + 1) * 3 + (xdir +
410 1)]);
413 standend();
416 cur.x += xdir;
417 cur.y += ydir;
421 /* now draw the points */
422 for (i = 0; i < data->point_num; ++i) {
423 coord *c = &data->point_coord[i];
424 int x = c->x - cx + (width / 2);
425 int y = c->y - cy + (height / 2);
427 if (x < 0 ||
428 x >= width ||
429 y <= 0 ||
430 y >= height) {
431 continue;
434 move(height - y, x);
436 if (s == SH_NORMAL) {
437 addstr("\u2022");
438 } else {
439 printw("%x", i);
444 /* prompt for a point to mutate at, mutate there */
446 mutate_at(ca *data,
447 unsigned width,
448 unsigned height)
450 char input[80];
451 char *s = input;
452 char *t;
453 size_t i;
454 size_t j;
455 long l;
456 size_t p;
457 int err;
459 memset(&input, 0, sizeof input);
460 move(height - 1, 0);
462 for (i = 0; i < width; ++i) {
463 addch(' ');
466 move(height - 1, 0);
467 addstr("Mutate at (lists ok): ");
468 curs_set(1);
469 echo();
470 getnstr(input, (sizeof input) - 1);
471 noecho();
472 curs_set(0);
474 while (*s) {
475 errno = 0;
476 l = strtoll(s, &t, 16);
478 if (errno ||
479 s == t ||
480 l < 0 ||
481 (size_t) l >= data->point_num) {
482 goto advance;
485 p = (size_t) l;
487 /* add all needed new arrows */
488 for (i = 0; i < data->arrow_num; ++i) {
489 arrow *a = &data->arrow[i];
491 for (j = i + 1; j < data->arrow_num; ++j) {
492 arrow *b = &data->arrow[j];
494 if (a->to == p &&
495 b->from == p) {
496 if ((err = add_arrow(a->from, b->to,
497 a->multiplicity *
498 b->multiplicity,
499 data))) {
500 return err;
502 } else if (a->from == p &&
503 b->to == p) {
504 if ((err = add_arrow(b->from, a->to,
505 a->multiplicity *
506 b->multiplicity,
507 data))) {
508 return err;
514 /* reverse all arrows */
515 for (i = 0; i < data->arrow_num; ++i) {
516 arrow *a = &data->arrow[i];
518 if (a->from == p) {
519 a->from = a->to;
520 a->to = p;
521 } else if (a->to == p) {
522 a->to = a->from;
523 a->from = p;
527 advance:
528 s = (s == t) ? (s + 1) : t;
531 return 0;
534 /* prompt for a point to delete, delete it */
536 del_point(ca *data,
537 unsigned width,
538 unsigned height)
540 char input[80];
541 char *s = input;
542 char *t;
543 size_t i;
544 long l;
545 size_t p;
547 memset(&input, 0, sizeof input);
548 move(height - 1, 0);
550 for (i = 0; i < width; ++i) {
551 addch(' ');
554 move(height - 1, 0);
555 addstr("Delete point(s) (lists ok): ");
556 curs_set(1);
557 echo();
558 getnstr(input, (sizeof input) - 1);
559 noecho();
560 curs_set(0);
562 while (*s) {
563 errno = 0;
564 l = strtoll(s, &t, 16);
566 if (errno ||
567 s == t ||
568 l < 0 ||
569 (size_t) l >= data->point_num) {
570 goto advance;
573 p = (size_t) l;
575 /* kill arrows and swap with other */
576 for (i = 0; i < data->arrow_num; ++i) {
577 arrow *a = &data->arrow[i];
579 if (a->from == p ||
580 a->to == p) {
581 *a = (arrow) { 0 };
582 } else if (a->from == data->point_num - 1) {
583 a->from = p;
584 } else if (a->to == data->point_num - 1) {
585 a->to = p;
589 data->point_coord[p] = data->point_coord[data->point_num - 1];
590 data->point_num--;
591 advance:
592 s = (s == t) ? (s + 1) : t;
595 return 0;
598 /* prompt for a point to insert, insert it */
600 inp_point(ca *data,
601 int width,
602 int height,
603 int cx,
604 int cy)
606 int i;
607 int orig_x = (width) / 2;
608 int orig_y = (height) / 2;
609 int x = orig_x;
610 int y = orig_y;
611 int c;
613 move(height - 1, 0);
615 for (i = 0; i < width; ++i) {
616 addch(' ');
619 move(height - 1, 0);
620 addstr("h/j/k/l to move, Return to add");
621 curs_set(1);
623 do {
624 if (x < 0) {
625 x = 0;
626 } else if (x >= width) {
627 x = width - 1;
630 if (y < 0) {
631 y = 0;
632 } else if (y >= height) {
633 y = height - 1;
636 move(height - y, x);
637 refresh();
639 switch (c = getch()) {
640 case 'h':
641 case KEY_LEFT:
642 x--;
643 break;
644 case 'l':
645 case KEY_RIGHT:
646 x++;
647 break;
648 case 'j':
649 case KEY_DOWN:
650 y--;
651 break;
652 case 'k':
653 case KEY_UP:
654 y++;
655 break;
656 case 'y':
657 case KEY_A1:
658 x--;
659 y++;
660 break;
661 case 'u':
662 case KEY_A3:
663 x++;
664 y++;
665 break;
666 case 'b':
667 case KEY_C1:
668 x--;
669 y--;
670 break;
671 case 'n':
672 case KEY_C3:
673 x++;
674 y--;
675 break;
676 case 'H':
677 x -= 8;
678 break;
679 case 'L':
680 x += 8;
681 break;
682 case 'J':
683 y -= 8;
684 break;
685 case 'K':
686 y += 8;
687 break;
688 case 'Y':
689 x -= 8;
690 y += 8;
691 break;
692 case 'U':
693 x += 8;
694 y += 8;
695 break;
696 case 'B':
697 x -= 8;
698 y -= 8;
699 break;
700 case 'N':
701 x += 8;
702 y -= 8;
703 break;
705 } while (c != ' ' &&
706 c != '\n' &&
707 c != '\r' &&
708 c != KEY_ENTER);
710 curs_set(0);
712 return find_make_point(x - orig_x + cx, y - orig_y + cy, 0, data);
715 /* prompt for two points to add an arrow between, add it */
717 inp_arrow(ca *data,
718 int width,
719 int height)
721 char input[80];
722 char *s = input;
723 char *t;
724 int i;
725 long l;
726 size_t p1;
727 size_t p2;
729 /* from */
730 memset(&input, 0, sizeof input);
731 move(height - 1, 0);
733 for (i = 0; i < width; ++i) {
734 addch(' ');
737 move(height - 1, 0);
738 addstr("From: ");
739 curs_set(1);
740 echo();
741 getnstr(input, (sizeof input) - 1);
742 noecho();
743 curs_set(0);
744 errno = 0;
745 l = strtoll(s, &t, 16);
747 if (errno ||
748 s == t ||
749 l < 0 ||
750 (size_t) l >= data->point_num) {
751 return 0;
754 p1 = (size_t) l;
756 /* to */
757 memset(&input, 0, sizeof input);
758 move(height - 1, 0);
760 for (i = 0; i < width; ++i) {
761 addch(' ');
764 move(height - 1, 0);
765 addstr("To: ");
766 curs_set(1);
767 echo();
768 getnstr(input, (sizeof input) - 1);
769 noecho();
770 curs_set(0);
771 errno = 0;
772 l = strtoll(s, &t, 16);
774 if (errno ||
775 s == t ||
776 l < 0 ||
777 (size_t) l >= data->point_num) {
778 return 0;
781 p2 = (size_t) l;
783 /* mult */
784 memset(&input, 0, sizeof input);
785 move(height - 1, 0);
787 for (i = 0; i < width; ++i) {
788 addch(' ');
791 move(height - 1, 0);
792 addstr("Multiplicity: ");
793 curs_set(1);
794 echo();
795 getnstr(input, (sizeof input) - 1);
796 noecho();
797 curs_set(0);
798 errno = 0;
800 if (!*input) {
801 l = 1;
802 } else {
803 l = strtoll(s, &t, 10);
805 if (errno ||
806 s == t) {
807 return 0;
811 return add_arrow(p1, p2, l, data);
814 /* prompt for a file to save state to, save it */
816 save_state(ca *data,
817 unsigned width,
818 unsigned height)
820 char input[4096];
821 size_t i;
822 int m;
823 FILE *f;
825 memset(&input, 0, sizeof input);
826 move(height - 1, 0);
828 for (i = 0; i < width; ++i) {
829 addch(' ');
832 move(height - 1, 0);
833 addstr("Save to file: ");
834 curs_set(1);
835 echo();
836 getnstr(input, (sizeof input) - 1);
837 noecho();
838 curs_set(0);
840 if (!*input) {
841 return 0;
844 if (!(f = fopen(input, "w"))) {
845 move(height - 1, 0);
847 for (i = 0; i < width; ++i) {
848 addch(' ');
851 move(height - 1, 0);
852 addstr("Could not write to file!");
853 attron(A_STANDOUT);
854 addstr("[OK]");
855 standend();
856 refresh();
857 getch();
859 return 0;
862 for (i = 0; i < data->point_num; ++i) {
863 coord *c = &data->point_coord[i];
864 fprintf(f, "(%d,%d)\n", c->x, c->y);
867 fprintf(f, "\n");
869 for (i = 0; i < data->arrow_num; ++i) {
870 arrow *a = &data->arrow[i];
871 coord *p1 = &data->point_coord[a->from];
872 coord *p2 = &data->point_coord[a->to];
874 for (m = 0; m < a->multiplicity; ++m) {
875 fprintf(f, "(%d,%d) -> (%d,%d)\n", p1->x, p1->y, p2->x,
876 p2->y);
879 for (m = 0; m > a->multiplicity; --m) {
880 fprintf(f, "(%d,%d) -> (%d,%d)\n", p2->x, p2->y, p1->x,
881 p1->y);
885 fclose(f);
886 move(height - 1, 0);
888 for (i = 0; i < width; ++i) {
889 addch(' ');
892 move(height - 1, 0);
893 addstr("Written ");
894 attron(A_STANDOUT);
895 addstr("[OK]");
896 standend();
897 refresh();
898 getch();
900 return 0;
903 /* read keys, do stuff */
904 static int
905 displayloop(ca *data)
907 int width = -1;
908 int height = -1;
909 uint_fast8_t should_quit = 0;
910 int c = 0;
911 int cx = data->bl.x + (data->ur.x - data->bl.x + 1) / 2;
912 int cy = data->bl.y + (data->ur.y - data->bl.y + 1) / 2;
913 int i;
914 int err;
916 do {
917 getmaxyx(stdscr, height, width);
918 height = (height >= 0) ? height : 0;
919 width = (width >= 0) ? width : 0;
920 clear();
921 draw_ca(data, SH_NORMAL, width, height, cx, cy);
922 move(height - 1, 0);
923 refresh();
924 addch('M' | A_STANDOUT);
925 addstr("utate at ");
926 addstr("New ");
927 addch('p' | A_STANDOUT);
928 addstr("oint ");
929 addstr("New ");
930 addch('a' | A_STANDOUT);
931 addstr("rrow ");
932 addch('D' | A_STANDOUT);
933 addstr("elete point ");
934 addch('S' | A_STANDOUT);
935 addstr("ave state ");
936 addch('Q' | A_STANDOUT);
937 addstr("uit");
939 for (i = 74; i < width; ++i) {
940 addch(' ');
943 switch ((c = getch())) {
944 case KEY_EXIT:
945 case 'Q':
946 case 'q':
947 should_quit = 1;
948 break;
949 case 'M':
950 case 'm':
951 draw_ca(data, SH_NAMES, width, height, cx, cy);
952 refresh();
954 if ((err = mutate_at(data, width, height))) {
955 return err;
958 break;
959 case 'D':
960 case 'd':
961 draw_ca(data, SH_NAMES, width, height, cx, cy);
962 refresh();
964 if ((err = del_point(data, width, height))) {
965 return err;
968 break;
969 case 'P':
970 case 'p':
972 if ((err = inp_point(data, width, height, cx, cy))) {
973 return err;
976 break;
977 case 'A':
978 case 'a':
979 draw_ca(data, SH_NAMES, width, height, cx, cy);
980 refresh();
982 if ((err = inp_arrow(data, width, height))) {
983 return err;
986 break;
987 case 'S':
988 case 's':
989 save_state(data, width, height);
990 break;
991 case 'h':
992 cx--;
993 break;
994 case 'l':
995 cx++;
996 break;
997 case 'j':
998 cy--;
999 break;
1000 case 'k':
1001 cy++;
1002 break;
1003 case KEY_RESIZE:
1004 default:
1005 break;
1007 } while (!should_quit);
1009 return 0;
1012 /* main */
1014 main(int argc,
1015 const char **argv)
1017 FILE *template = 0;
1018 ca data = { 0 };
1019 int err = 0;
1021 setlocale(LC_ALL, "");
1023 if (argc == 2) {
1024 if (!(template = fopen(argv[1], "r"))) {
1025 perror("Could not open template file");
1027 return ENOENT;
1030 err = init_from_file(template, &data);
1031 fclose(template);
1033 if (err) {
1034 goto done;
1036 } else if (argc != 1) {
1037 fprintf(stderr, "Usage: %s [path-to-input-file]\n", argv[0]);
1039 return E2BIG;
1042 if (!initscr()) {
1043 return EINVAL;
1046 if (has_colors()) {
1047 start_color();
1050 if (COLORS >= 256) {
1051 enough_colors = 1;
1052 use_default_colors();
1054 /* no special reasons for these, I don't know color */
1055 init_pair(1, 1, -1);
1056 init_pair(2, 3, -1);
1057 init_pair(3, 2, -1);
1058 init_pair(4, 5, -1);
1059 init_pair(5, 4, -1);
1060 init_pair(6, 58, -1);
1061 init_pair(7, 202, -1);
1062 init_pair(8, 6, -1);
1065 cbreak();
1066 noecho();
1067 curs_set(0);
1068 clear();
1069 nonl();
1070 intrflush(stdscr, FALSE);
1071 keypad(stdscr, TRUE);
1072 err = displayloop(&data);
1073 endwin();
1074 done:
1076 if (err) {
1077 errno = err;
1078 perror(0);
1081 return err;