ncurses 6.0 - patch 20171021
[ncurses.git] / test / savescreen.c
1 /****************************************************************************
2  * Copyright (c) 2007-2015,2017 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: savescreen.c,v 1.36 2017/10/20 21:20:34 tom Exp $
30  *
31  * Demonstrate save/restore functions from the curses library.
32  * Thomas Dickey - 2007/7/14
33  */
34
35 #include <test.priv.h>
36 #include <popup_msg.h>
37
38 #if HAVE_SCR_DUMP
39
40 #include <sys/types.h>
41 #include <sys/stat.h>
42
43 #if TIME_WITH_SYS_TIME
44 # include <sys/time.h>
45 # include <time.h>
46 #else
47 # if HAVE_SYS_TIME_H
48 #  include <sys/time.h>
49 # else
50 #  include <time.h>
51 # endif
52 #endif
53
54 #if defined(__hpux)
55 #define MyMarker 'X'
56 #else
57 #define MyMarker ACS_DIAMOND
58 #endif
59
60 static bool use_init = FALSE;
61 static bool keep_dumps = FALSE;
62
63 #if USE_WIDEC_SUPPORT
64 /* In HPUX curses, cchar_t is opaque; other implementations are not */
65 static wchar_t
66 BaseChar(cchar_t data)
67 {
68     wchar_t my_wchar[CCHARW_MAX];
69     attr_t my_attr;
70     short my_pair;
71     getcchar(&data, my_wchar, &my_attr, &my_pair, NULL);
72     return my_wchar[0];
73 }
74 #endif
75
76 static int
77 fexists(const char *name)
78 {
79     struct stat sb;
80     return (stat(name, &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFREG);
81 }
82
83 static void
84 setup_next(void)
85 {
86     curs_set(1);
87     reset_shell_mode();
88 }
89
90 static void
91 cleanup(char *files[])
92 {
93     int n;
94
95     if (!keep_dumps) {
96         for (n = 0; files[n] != 0; ++n) {
97             unlink(files[n]);
98         }
99     }
100 }
101
102 static int
103 load_screen(char *filename)
104 {
105     int result;
106
107     if (use_init) {
108         if ((result = scr_init(filename)) != ERR)
109             result = scr_restore(filename);
110     } else {
111         result = scr_set(filename);
112     }
113     return result;
114 }
115
116 /*
117  * scr_restore() or scr_set() operates on curscr.  If we read a character using
118  * getch() that will refresh stdscr, wiping out the result.  To avoid that,
119  * copy the data back from curscr to stdscr.
120  */
121 static void
122 after_load(void)
123 {
124     overwrite(curscr, stdscr);
125     doupdate();
126 }
127
128 static void
129 show_what(int which, int last)
130 {
131     int y, x, n;
132     time_t now;
133     char *mytime;
134
135     getyx(stdscr, y, x);
136
137     move(0, 0);
138     printw("Saved %d of %d (? for help)", which, last + 1);
139
140     now = time((time_t *) 0);
141     mytime = ctime(&now);
142     for (n = (int) strlen(mytime) - 1; n >= 0; --n) {
143         if (isspace(UChar(mytime[n]))) {
144             mytime[n] = '\0';
145         } else {
146             break;
147         }
148     }
149     mvprintw(0, (COLS - n - 2), " %s", mytime);
150
151     move(y, x);
152
153     refresh();
154 }
155
156 static int
157 get_command(int which, int last)
158 {
159     int ch;
160
161     timeout(50);
162
163     do {
164         show_what(which, last);
165         ch = getch();
166     } while (ch == ERR);
167
168     return ch;
169 }
170
171 static void
172 editor_help(void)
173 {
174     static const char *msgs[] =
175     {
176         "You are now in the screen-editor, which allows you to make some",
177         "lines on the screen, as well as save copies of the screen to a",
178         "temporary file",
179         "",
180         "Keys:",
181         "   q           quit",
182         "   n           run the screen-loader to show the saved screens",
183         "   <space>     dump a screen",
184         "",
185         "   a           toggle between '#' and graphic symbol for drawing",
186         "   c           change color drawn by line to next in palette",
187         "   h,j,k,l or arrows to move around the screen, drawing",
188         0
189     };
190     popup_msg(stdscr, msgs);
191 }
192
193 static void
194 replay_help(void)
195 {
196     static const char *msgs[] =
197     {
198         "You are now in the screen-loader, which allows you to view",
199         "the dumped/restored screens.",
200         "",
201         "Keys:",
202         "   q           quit",
203         "   <space>     load the next screen",
204         "   <backspace> load the previous screen",
205         0
206     };
207     popup_msg(stdscr, msgs);
208 }
209
210 static void
211 usage(void)
212 {
213     static const char *msg[] =
214     {
215         "Usage: savescreen [-r] files",
216         "",
217         "Options:",
218         " -f file  fill/initialize screen using text from this file",
219         " -i       use scr_init/scr_restore rather than scr_set",
220         " -k       keep the restored dump-files rather than removing them",
221         " -r       replay the screen-dump files"
222     };
223     unsigned n;
224     for (n = 0; n < SIZEOF(msg); ++n) {
225         fprintf(stderr, "%s\n", msg[n]);
226     }
227     ExitProgram(EXIT_FAILURE);
228 }
229
230 int
231 main(int argc, char *argv[])
232 {
233     int ch;
234     int which = 0;
235     int last;
236     bool replaying = FALSE;
237     bool done = FALSE;
238     char **files;
239     char *fill_by = 0;
240 #if USE_WIDEC_SUPPORT
241     cchar_t mycc;
242     int myxx;
243 #endif
244
245     setlocale(LC_ALL, "");
246
247     while ((ch = getopt(argc, argv, "f:ikr")) != -1) {
248         switch (ch) {
249         case 'f':
250             fill_by = optarg;
251             break;
252         case 'i':
253             use_init = TRUE;
254             break;
255         case 'k':
256             keep_dumps = TRUE;
257             break;
258         case 'r':
259             replaying = TRUE;
260             break;
261         default:
262             usage();
263             break;
264         }
265     }
266
267     files = argv + optind;
268     last = argc - optind - 1;
269
270     if (replaying) {
271         while (last >= 0 && !fexists(files[last]))
272             --last;
273     }
274
275     initscr();
276     cbreak();
277     noecho();
278     keypad(stdscr, TRUE);
279     curs_set(0);
280     if (has_colors()) {
281         short pair;
282         short color;
283
284         start_color();
285         /*
286          * Assume pairs is the square of colors, and assign pairs going down
287          * so that there is minimal conflict with the background color (which
288          * counts up).  The intent is just to show how color pair values are
289          * saved and restored.
290          */
291         for (pair = 0; pair < COLOR_PAIRS; ++pair) {
292             color = (short) (pair % (COLORS - 1));
293             init_pair(pair, (short) (COLOR_WHITE - color), color);
294         }
295     }
296
297     if (fill_by != 0) {
298         FILE *fp = fopen(fill_by, "r");
299         if (fp != 0) {
300             bool filled = FALSE;
301             move(1, 0);
302             while ((ch = fgetc(fp)) != EOF) {
303                 if (addch(UChar(ch)) == ERR) {
304                     filled = TRUE;
305                     break;
306                 }
307             }
308             fclose(fp);
309             if (!filled) {
310                 while (addch(' ') != ERR) {
311                     ;
312                 }
313             }
314             move(0, 0);
315         } else {
316             exit_curses();
317             fprintf(stderr, "Cannot open \"%s\"\n", fill_by);
318             ExitProgram(EXIT_FAILURE);
319         }
320     }
321
322     if (replaying) {
323
324         /*
325          * Use the last file as the initial/current screen.
326          */
327         if (last < 0) {
328             exit_curses();
329             printf("No screen-dumps given\n");
330             ExitProgram(EXIT_FAILURE);
331         }
332
333         which = last;
334         if (load_screen(files[which]) == ERR) {
335             exit_curses();
336             printf("Cannot load screen-dump %s\n", files[which]);
337             ExitProgram(EXIT_FAILURE);
338         }
339         after_load();
340
341         while (!done && (ch = getch()) != ERR) {
342             switch (ch) {
343             case 'n':
344                 /*
345                  * If we got a "next" here, skip to the final screen before
346                  * moving to the next process.
347                  */
348                 setup_next();
349                 which = last;
350                 done = TRUE;
351                 break;
352             case 'q':
353                 cleanup(files);
354                 done = TRUE;
355                 break;
356             case KEY_BACKSPACE:
357             case '\b':
358                 if (--which < 0)
359                     which = last;
360                 break;
361             case ' ':
362                 if (++which > last)
363                     which = 0;
364                 break;
365             case HELP_KEY_1:
366                 replay_help();
367                 break;
368             default:
369                 beep();
370                 continue;
371             }
372
373             if (ch == 'q') {
374                 ;
375             } else if (scr_restore(files[which]) == ERR) {
376                 endwin();
377                 printf("Cannot load screen-dump %s\n", files[which]);
378                 cleanup(files);
379                 ExitProgram(EXIT_FAILURE);
380             } else {
381                 wrefresh(curscr);
382             }
383         }
384         endwin();
385     } else {
386         int y = 0;
387         int x = 0;
388         int color = 0;
389         int altchars = 0;
390
391         while (!done) {
392             switch (get_command(which, last)) {
393             case 'n':
394                 setup_next();
395                 done = TRUE;
396                 break;
397             case 'q':
398                 cleanup(files);
399                 done = TRUE;
400                 break;
401             case ' ':
402                 if (files[which] != 0) {
403                     show_what(which + 1, last);
404                     if (scr_dump(files[which]) == ERR) {
405                         endwin();
406                         printf("Cannot write screen-dump %s\n", files[which]);
407                         cleanup(files);
408                         done = TRUE;
409                         break;
410                     }
411                     ++which;
412                     if (has_colors()) {
413                         int cx, cy;
414                         short pair = (short) (which % COLOR_PAIRS);
415                         /*
416                          * Change the background color, to make it more
417                          * obvious.  But that changes the existing text-color. 
418                          * Copy the old values from the currently displayed
419                          * screen.
420                          */
421                         bkgd((chtype) COLOR_PAIR(pair));
422                         for (cy = 1; cy < LINES; ++cy) {
423                             for (cx = 0; cx < COLS; ++cx) {
424                                 wmove(curscr, cy, cx);
425                                 wmove(stdscr, cy, cx);
426 #if USE_WIDEC_SUPPORT
427                                 if (win_wch(curscr, &mycc) != ERR) {
428                                     myxx = wcwidth(BaseChar(mycc));
429                                     if (myxx > 0) {
430                                         wadd_wchnstr(stdscr, &mycc, 1);
431                                         cx += (myxx - 1);
432                                     }
433                                 }
434 #else
435                                 waddch(stdscr, winch(curscr));
436 #endif
437                             }
438                         }
439                     }
440                 } else {
441                     beep();
442                 }
443                 break;
444             case KEY_LEFT:
445             case 'h':
446                 if (--x < 0)
447                     x = COLS - 1;
448                 break;
449             case KEY_DOWN:
450             case 'j':
451                 if (++y >= LINES)
452                     y = 1;
453                 break;
454             case KEY_UP:
455             case 'k':
456                 if (--y < 1)
457                     y = LINES - 1;
458                 break;
459             case KEY_RIGHT:
460             case 'l':
461                 if (++x >= COLS)
462                     x = 0;
463                 break;
464             case 'a':
465                 altchars = !altchars;
466                 break;
467             case 'c':
468                 color = (color + 1) % COLORS;
469                 break;
470             case HELP_KEY_1:
471                 editor_help();
472                 break;
473             default:
474                 beep();
475                 continue;
476             }
477             if (!done) {
478                 chtype attr = (A_REVERSE | (chtype) COLOR_PAIR(color * COLORS));
479                 chtype ch2 = (altchars ? MyMarker : '#');
480                 move(y, x);
481                 AddCh(ch2 | attr);
482                 move(y, x);
483             }
484         }
485         endwin();
486     }
487     ExitProgram(EXIT_SUCCESS);
488 }
489
490 #else
491 int
492 main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
493 {
494     printf("This program requires the screen-dump functions\n");
495     ExitProgram(EXIT_FAILURE);
496 }
497 #endif