ncurses 6.1 - patch 20181124
[ncurses.git] / test / movewindow.c
1 /****************************************************************************
2  * Copyright (c) 2006-2017,2018 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 /*
29  * $Id: movewindow.c,v 1.48 2018/11/24 21:29:46 tom Exp $
30  *
31  * Demonstrate move functions for windows and derived windows from the curses
32  * library.
33  *
34  * Author: Thomas E. Dickey
35  */
36 /*
37 derwin
38 mvderwin
39 subwin
40 mvwin
41
42 TODO:
43     add command to reset subwindow's origin to coincide with parent.
44     add command to delete subwindow (check if it has subwindows though)
45  */
46
47 #include <test.priv.h>
48
49 #if HAVE_MVDERWIN && HAVE_MVWIN
50
51 #include <popup_msg.h>
52
53 #ifdef HAVE_XCURSES
54 #undef derwin
55 #endif
56
57 #if defined(NCURSES_VERSION) || defined(PDCURSES)
58 #define CONST_FMT const
59 #else
60 #define CONST_FMT               /* nothing */
61 #endif
62
63 #undef LINE_MAX
64
65 #define LINE_MIN        2
66 #define LINE_MAX        (LINES - 2)
67 #define COL_MIN         2
68 #define COL_MAX         (COLS - 2)
69
70 typedef struct {
71     int y, x;
72 } PAIR;
73
74 typedef struct {
75     WINDOW *parent;             /* need this since WINDOW->_parent is not portable */
76     WINDOW *child;              /* the actual value */
77 } FRAME;
78
79 static void head_line(CONST_FMT char *fmt,...) GCC_PRINTFLIKE(1, 2);
80 static void tail_line(CONST_FMT char *fmt,...) GCC_PRINTFLIKE(1, 2);
81
82 static unsigned num_windows;
83 static FRAME *all_windows;
84
85 static void
86 failed(const char *s)
87 {
88     perror(s);
89     endwin();
90     ExitProgram(EXIT_FAILURE);
91 }
92
93 static void
94 message(int lineno, CONST_FMT char *fmt, va_list argp)
95 {
96     int y, x;
97
98     getyx(stdscr, y, x);
99     move(lineno, 0);
100     clrtoeol();
101
102 #ifdef HAVE_XCURSES
103     {
104         char buffer[1024];
105         vsprintf(buffer, fmt, argp);
106         addstr(buffer);
107     }
108 #elif defined(HAVE_VW_PRINTW)
109     vw_printw(stdscr, fmt, argp);
110 #else
111     vwprintw(stdscr, fmt, argp);
112 #endif
113
114     move(y, x);
115     refresh();
116 }
117
118 static void
119 head_line(CONST_FMT char *fmt,...)
120 {
121     va_list argp;
122
123     va_start(argp, fmt);
124     message(0, fmt, argp);
125     va_end(argp);
126 }
127
128 static void
129 tail_line(CONST_FMT char *fmt,...)
130 {
131     va_list argp;
132
133     va_start(argp, fmt);
134     message(LINES - 1, fmt, argp);
135     va_end(argp);
136 }
137
138 /*
139  * Arrow keys move cursor, return location at current on non-arrow key.
140  */
141 static PAIR *
142 selectcell(WINDOW *parent,
143            WINDOW *child,
144            int uli, int ulj,
145            int lri, int lrj,
146            bool relative,
147            bool * more)
148 {
149     static PAIR res;            /* result cell */
150     int si = lri - uli + 1;     /* depth of the select area */
151     int sj = lrj - ulj + 1;     /* width of the select area */
152     int i = 0, j = 0;           /* offsets into the select area */
153
154     res.y = uli;
155     res.x = ulj;
156
157     if (child != 0) {
158         if (relative) {
159             getparyx(child, i, j);
160         } else {
161             getbegyx(child, i, j);
162             i -= uli + getbegy(parent);
163             j -= ulj + getbegx(parent);
164         }
165     }
166
167     if (more)
168         *more = FALSE;
169
170     for (;;) {
171         bool moved = FALSE;
172
173         tail_line("Upper left [%2d,%2d] Lower right [%2d,%2d] -> %d,%d -> %d,%d",
174                   uli, ulj,
175                   lri, lrj,
176                   i, j,
177                   uli + i, ulj + j);
178         wmove(parent, uli + i, ulj + j);
179
180         switch (wgetch(parent)) {
181         case KEY_UP:
182             i += si - 1;
183             moved = TRUE;
184             break;
185         case KEY_DOWN:
186             i++;
187             moved = TRUE;
188             break;
189         case KEY_LEFT:
190             j += sj - 1;
191             moved = TRUE;
192             break;
193         case KEY_RIGHT:
194             j++;
195             moved = TRUE;
196             break;
197         case QUIT:
198             /* FALLTHRU */
199         case ESCAPE:
200             return ((PAIR *) 0);
201 #ifdef NCURSES_MOUSE_VERSION
202         case KEY_MOUSE:
203             {
204                 MEVENT event;
205
206                 getmouse(&event);
207                 if (event.y > uli && event.x > ulj) {
208                     if (parent != stdscr) {
209                         i = event.y - getbegy(parent) - uli;
210                         j = event.x - getbegx(parent) - ulj;
211                     } else {
212                         i = event.y - uli;
213                         j = event.x - ulj;
214                     }
215                 } else {
216                     beep();
217                     break;
218                 }
219             }
220 #endif
221             /* FALLTHRU */
222         default:
223             res.y = uli + i;
224             res.x = ulj + j;
225             return (&res);
226         }
227
228         if (si <= 0)
229             i = 0;
230         else
231             i %= si;
232
233         if (sj <= 0)
234             j = 0;
235         else
236             j %= sj;
237
238         /*
239          * If the caller can handle continuous movement, return the result.
240          */
241         if (moved && more) {
242             *more = TRUE;
243             res.y = uli + i;
244             res.x = ulj + j;
245             return (&res);
246         }
247     }
248 }
249
250 /*
251  * Ask user for a window definition.
252  */
253 static bool
254 getwindow(WINDOW *parent, PAIR * ul, PAIR * lr)
255 {
256     int min_col = (parent == stdscr) ? COL_MIN : 0;
257     int max_col = (parent == stdscr) ? COL_MAX : getmaxx(parent);
258     int min_line = (parent == stdscr) ? LINE_MIN : 0;
259     int max_line = (parent == stdscr) ? LINE_MAX : getmaxy(parent);
260     PAIR *tmp;
261     bool result = FALSE;
262
263     head_line("Use arrows to move cursor, anything else to mark corner 1");
264     if ((tmp = selectcell(parent, 0,
265                           min_line, min_col,
266                           max_line, max_col,
267                           FALSE,
268                           (bool *) 0)) != 0) {
269         *ul = *tmp;
270         MvWAddCh(parent, ul->y, ul->x, '*');
271
272         head_line("Use arrows to move cursor, anything else to mark corner 2");
273         if ((tmp = selectcell(parent, 0,
274                               ul->y, ul->x,
275                               max_line, max_col,
276                               FALSE,
277                               (bool *) 0)) != 0) {
278             *lr = *tmp;
279             MvWAddCh(parent, lr->y, lr->x, '*');
280             wmove(parent, lr->y, lr->x);
281             wsyncdown(parent);
282             wrefresh(parent);
283             result = (lr->y != ul->y && lr->x != ul->x);
284         }
285     }
286     head_line("done");
287     return result;
288 }
289
290 /*
291  * Draw a box inside the given window.
292  */
293 static void
294 box_inside(WINDOW *win)
295 {
296     int y0, x0;
297     int y1, x1;
298
299     getyx(win, y0, x0);
300     getmaxyx(win, y1, x1);
301
302     MvWHLine(win, 0, 0, ACS_HLINE, x1);
303     MvWHLine(win, y1 - 1, 0, ACS_HLINE, x1);
304
305     MvWVLine(win, 0, 0, ACS_VLINE, y1);
306     MvWVLine(win, 0, x1 - 1, ACS_VLINE, y1);
307
308     MvWAddCh(win, 0, 0, ACS_ULCORNER);
309     MvWAddCh(win, y1 - 1, 0, ACS_LLCORNER);
310     MvWAddCh(win, 0, x1 - 1, ACS_URCORNER);
311     MvWAddCh(win, y1 - 1, x1 - 1, ACS_LRCORNER);
312
313     wsyncdown(win);
314     wmove(win, y0, x0);
315     wrefresh(win);
316 }
317
318 /*
319  * Add a window to our list.
320  */
321 static void
322 add_window(WINDOW *parent, WINDOW *child)
323 {
324     static unsigned have = 0;
325     unsigned need = ((num_windows + 1) | 31) + 1;
326
327     keypad(child, TRUE);
328     if (need > have) {
329         all_windows = typeRealloc(FRAME, need, all_windows);
330         if (!all_windows)
331             failed("add_window");
332     }
333     all_windows[num_windows].parent = parent;
334     all_windows[num_windows].child = child;
335     num_windows++;
336 }
337
338 static int
339 window2num(WINDOW *win)
340 {
341     int n;
342     int result = -1;
343     for (n = 0; n < (int) num_windows; ++n) {
344         if (win == all_windows[n].child) {
345             result = n;
346             break;
347         }
348     }
349     return result;
350 }
351
352 static WINDOW *
353 parent_of(WINDOW *win)
354 {
355     WINDOW *result = 0;
356     int n = window2num(win);
357     if (n >= 0)
358         result = all_windows[n].parent;
359     return result;
360 }
361
362 static void
363 repaint_one(WINDOW *win)
364 {
365     touchwin(win);
366     wnoutrefresh(win);
367 }
368
369 static void
370 refresh_all(WINDOW *win)
371 {
372     unsigned n;
373
374     for (n = 0; n < num_windows; ++n) {
375         if (all_windows[n].child != win) {
376             repaint_one(all_windows[n].child);
377         }
378     }
379
380     repaint_one(win);
381     doupdate();
382 }
383
384 static WINDOW *
385 next_window(WINDOW *win)
386 {
387     WINDOW *result = win;
388     int n = window2num(win);
389
390     if (n++ >= 0) {
391         result = all_windows[(unsigned) n % num_windows].child;
392         wmove(result, 0, 0);
393         wrefresh(result);
394     }
395     return result;
396 }
397
398 static WINDOW *
399 prev_window(WINDOW *win)
400 {
401     WINDOW *result = win;
402     int n = window2num(win);
403
404     if (n-- >= 0) {
405         if (n < 0)
406             n = (int) (num_windows - 1);
407         result = all_windows[(unsigned) n % num_windows].child;
408         wmove(result, 0, 0);
409         wrefresh(result);
410     }
411     return result;
412 }
413
414 static void
415 recur_move_window(WINDOW *parent, int dy, int dx)
416 {
417     unsigned n;
418
419     for (n = 0; n < num_windows; ++n) {
420         if (all_windows[n].parent == parent) {
421             mvwin(all_windows[n].child, dy, dx);
422             recur_move_window(all_windows[n].child, dy, dx);
423         }
424     }
425 }
426
427 /*
428  * test mvwin().
429  */
430 static bool
431 move_window(WINDOW *win, bool recur)
432 {
433     WINDOW *parent = parent_of(win);
434     bool result = FALSE;
435
436     if (parent != 0) {
437         bool top = (parent == stdscr);
438         int min_col = top ? COL_MIN : 0;
439         int max_col = top ? COL_MAX : getmaxx(parent);
440         int min_line = top ? LINE_MIN : 0;
441         int max_line = top ? LINE_MAX : getmaxy(parent);
442         PAIR *tmp;
443         bool more;
444
445         head_line("Select new position for %swindow", top ? "" : "sub");
446
447         while ((tmp = selectcell(parent,
448                                  win,
449                                  min_line, min_col,
450                                  max_line, max_col,
451                                  FALSE,
452                                  &more)) != 0) {
453             int y0, x0;
454             getbegyx(parent, y0, x0);
455             /*
456              * Moving a subwindow has the effect of moving a viewport around
457              * the screen.  The parent window retains the contents of the
458              * subwindow in the original location, but the viewport will show
459              * the contents (again) at the new location.  So it will look odd
460              * when testing.
461              */
462             if (mvwin(win, y0 + tmp->y, x0 + tmp->x) != ERR) {
463                 if (recur) {
464                     recur_move_window(win, tmp->y, tmp->x);
465                 }
466                 refresh_all(win);
467                 doupdate();
468                 result = TRUE;
469             } else {
470                 result = FALSE;
471             }
472             if (!more)
473                 break;
474         }
475     }
476     head_line("done");
477     return result;
478 }
479
480 static void
481 show_derwin(WINDOW *win)
482 {
483     int pary, parx, maxy, maxx;
484
485     getmaxyx(win, maxy, maxx);
486     getparyx(win, pary, parx);
487
488     head_line("Select new position for derived window at %d,%d (%d,%d)",
489               pary, parx, maxy, maxx);
490 }
491
492 /*
493  * test mvderwin().
494  */
495 static bool
496 move_derwin(WINDOW *win)
497 {
498     WINDOW *parent = parent_of(win);
499     bool result = FALSE;
500
501     if (parent != 0) {
502         bool top = (parent == stdscr);
503         int min_col = top ? COL_MIN : 0;
504         int max_col = top ? COL_MAX : getmaxx(parent);
505         int min_line = top ? LINE_MIN : 0;
506         int max_line = top ? LINE_MAX : getmaxy(parent);
507         PAIR *tmp;
508         bool more;
509
510         show_derwin(win);
511         while ((tmp = selectcell(parent,
512                                  win,
513                                  min_line, min_col,
514                                  max_line, max_col,
515                                  TRUE,
516                                  &more)) != 0) {
517             if (mvderwin(win, tmp->y, tmp->x) != ERR) {
518                 refresh_all(win);
519                 doupdate();
520                 repaint_one(win);
521                 doupdate();
522                 result = TRUE;
523                 show_derwin(win);
524             } else {
525                 flash();
526             }
527             if (!more)
528                 break;
529         }
530     }
531     head_line("done");
532     return result;
533 }
534
535 static void
536 fill_window(WINDOW *win, chtype ch)
537 {
538     int y, x;
539     int y0, x0;
540     int y1, x1;
541
542     getyx(win, y0, x0);
543     getmaxyx(win, y1, x1);
544     for (y = 0; y < y1; ++y) {
545         for (x = 0; x < x1; ++x) {
546             MvWAddCh(win, y, x, ch);
547         }
548     }
549     wsyncdown(win);
550     wmove(win, y0, x0);
551     wrefresh(win);
552 }
553
554 static void
555 fill_with_pattern(WINDOW *win)
556 {
557     int y, x;
558     int y0, x0;
559     int y1, x1;
560     int ch = 'a';
561
562     getyx(win, y0, x0);
563     getmaxyx(win, y1, x1);
564     for (y = 0; y < y1; ++y) {
565         for (x = 0; x < x1; ++x) {
566             MvWAddCh(win, y, x, (chtype) ch);
567             if (++ch > 'z')
568                 ch = 'a';
569         }
570     }
571     wsyncdown(win);
572     wmove(win, y0, x0);
573     wrefresh(win);
574 }
575
576 #define lines_of(ul,lr) (lr.y - ul.y + 1)
577 #define cols_of(ul,lr)  (lr.x - ul.x + 1)
578 #define pair_of(ul)     ul.y, ul.x
579
580 static WINDOW *
581 create_my_window(WINDOW *current)
582 {
583     PAIR ul, lr;
584     WINDOW *result = 0;
585
586     if (getwindow(stdscr, &ul, &lr)) {
587         result = newwin(lines_of(ul, lr), cols_of(ul, lr), pair_of(ul));
588         if (result != 0) {
589             fill_window(result, 'c');
590             add_window(stdscr, result);
591         }
592     }
593     if (result == 0)
594         result = current;
595     return result;
596 }
597
598 static WINDOW *
599 create_my_derwin(WINDOW *parent)
600 {
601     PAIR ul, lr;
602     WINDOW *result = 0;
603
604     if (getwindow(parent, &ul, &lr)) {
605         result = derwin(parent, lines_of(ul, lr), cols_of(ul, lr), pair_of(ul));
606         if (result != 0) {
607             fill_window(result, 'd');
608             add_window(parent, result);
609         }
610     }
611     if (result == 0)
612         result = parent;
613     return result;
614 }
615
616 static WINDOW *
617 create_my_subwin(WINDOW *parent)
618 {
619     PAIR ul, lr;
620     WINDOW *result = 0;
621
622     if (getwindow(parent, &ul, &lr)) {
623         result = subwin(parent,
624                         lines_of(ul, lr),
625                         cols_of(ul, lr),
626                         ul.y + getbegy(parent),
627                         ul.x + getbegx(parent));
628         if (result != 0) {
629             fill_window(result, 's');
630             add_window(parent, result);
631         }
632     }
633     if (result == 0)
634         result = parent;
635     return result;
636 }
637
638 static void
639 show_help(WINDOW *current)
640 {
641     /* *INDENT-OFF* */
642     static struct {
643         int     key;
644         CONST_FMT char * msg;
645     } help[] = {
646         { HELP_KEY_1,   "Show this screen" },
647         { 'b',          "Draw a box inside the current window" },
648         { 'c',          "Create a new window" },
649         { 'd',          "Create a new derived window" },
650         { 'D',          "Move derived window (moves viewport)" },
651         { 'f',          "Fill the current window with the next character" },
652         { 'F',          "Fill the current window with a pattern" },
653         { 'm',          "Move the current window" },
654         { 'M',          "Move the current window (and its children)" },
655         { 'q',          "Quit" },
656         { 's',          "Create a new subwindow" },
657         { CTRL('L'),    "Repaint all windows, doing current one last" },
658         { CTRL('N'),    "Cursor to next window" },
659         { CTRL('P'),    "Cursor to previous window" },
660     };
661     /* *INDENT-ON* */
662
663     char **msgs = typeCalloc(char *, SIZEOF(help) + 1);
664     size_t n;
665
666     for (n = 0; n < SIZEOF(help); ++n) {
667         msgs[n] = typeMalloc(char, 21 + strlen(help[n].msg));
668         sprintf(msgs[n], "%-20s%s", keyname(help[n].key), help[n].msg);
669     }
670     popup_msg2(current, msgs);
671     for (n = 0; n < SIZEOF(help); ++n) {
672         free(msgs[n]);
673     }
674     free(msgs);
675 }
676
677 int
678 main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
679 {
680     WINDOW *current_win;
681     int ch;
682     bool done = FALSE;
683
684     initscr();
685     cbreak();
686     noecho();
687     nonl();
688     intrflush(stdscr, FALSE);
689
690     add_window(0, current_win = stdscr);
691
692 #ifdef NCURSES_MOUSE_VERSION
693     (void) mousemask(BUTTON1_CLICKED, (mmask_t *) NULL);
694 #endif /* NCURSES_MOUSE_VERSION */
695
696     while (!done && (ch = wgetch(current_win)) != ERR) {
697         int y, x;
698
699         getyx(current_win, y, x);
700
701         switch (ch) {
702         case HELP_KEY_1:
703             show_help(current_win);
704             break;
705         case 'b':
706             box_inside(current_win);
707             break;
708         case 'c':
709             current_win = create_my_window(current_win);
710             break;
711         case 'd':
712             current_win = create_my_derwin(current_win);
713             break;
714         case 'D':
715             if (!move_derwin(current_win)) {
716                 tail_line("error");
717                 continue;
718             }
719             break;
720         case 'f':
721             fill_window(current_win, (chtype) wgetch(current_win));
722             break;
723         case 'F':
724             fill_with_pattern(current_win);
725             break;
726         case 'm':
727         case 'M':
728             if (!move_window(current_win, (ch == 'M'))) {
729                 tail_line("error");
730                 continue;
731             }
732             break;
733         case 'q':
734             done = TRUE;
735             break;
736         case 's':
737             current_win = create_my_subwin(current_win);
738             break;
739         case CTRL('L'):
740             refresh_all(current_win);
741             break;
742         case CTRL('N'):
743             current_win = next_window(current_win);
744             break;
745         case CTRL('P'):
746             current_win = prev_window(current_win);
747             break;
748 #if 0
749             /* want to allow cursor to move around the current window too */
750             /* want to test the resizing of windows and subwindows too */
751             /* want to allow deleting a window also */
752 #endif
753         default:
754             wmove(current_win, y, x);
755             tail_line("unrecognized key (use '?' for help)");
756             beep();
757             continue;
758         }
759         tail_line("size [%d,%d] begin [%d,%d] parent [%d,%d]",
760                   getmaxy(current_win),
761                   getmaxx(current_win),
762                   getbegy(current_win),
763                   getbegx(current_win),
764                   getpary(current_win),
765                   getparx(current_win));
766         wmove(current_win, 0, 0);
767     }
768     endwin();
769 #if NO_LEAKS
770     free(all_windows);
771 #endif
772     ExitProgram(EXIT_SUCCESS);
773 }
774 #else
775 int
776 main(void)
777 {
778     printf("This program requires the curses mvderwin and mvwin functions\n");
779     ExitProgram(EXIT_FAILURE);
780 }
781 #endif