ncurses 6.0 - patch 20170930
[ncurses.git] / test / rain.c
1 /****************************************************************************
2  * Copyright (c) 1998-2014,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: rain.c,v 1.47 2017/09/30 18:10:05 tom Exp $
30  */
31 #include <test.priv.h>
32 #include <popup_msg.h>
33
34 /* rain 11/3/1980 EPS/CITHEP */
35
36 #ifdef USE_PTHREADS
37 #include <pthread.h>
38 #endif
39
40 WANT_USE_WINDOW();
41
42 #define MAX_THREADS     10
43 #define MAX_DROP        5
44
45 struct DATA;
46
47 typedef void (*DrawPart) (struct DATA *);
48
49 typedef struct DATA {
50     int y, x;
51 #ifdef USE_PTHREADS
52     DrawPart func;
53     int state;
54 #endif
55 } DATA;
56
57 #ifdef USE_PTHREADS
58 pthread_cond_t cond_next_drop;
59 pthread_mutex_t mutex_next_drop;
60 static int used_threads;
61
62 typedef struct {
63     pthread_t myself;
64     long counter;
65 } STATS;
66
67 static STATS drop_threads[MAX_THREADS];
68 #endif
69
70 static void
71 onsig(int n GCC_UNUSED)
72 {
73     exit_curses();
74     ExitProgram(EXIT_FAILURE);
75 }
76
77 static double
78 ranf(void)
79 {
80     long r = (rand() & 077777);
81     return ((double) r / 32768.);
82 }
83
84 static int
85 random_x(void)
86 {
87     return (int) (((double) (COLS - 4) * ranf()) + 2);
88 }
89
90 static int
91 random_y(void)
92 {
93     return (int) (((double) (LINES - 4) * ranf()) + 2);
94 }
95
96 static int
97 next_j(int j)
98 {
99     if (j == 0)
100         j = MAX_DROP - 1;
101     else
102         --j;
103     if (has_colors()) {
104         int z = (int) (3 * ranf());
105         (void) attrset(AttrArg(COLOR_PAIR(z), (z ? A_BOLD : A_NORMAL)));
106     }
107     return j;
108 }
109
110 static void
111 part1(DATA * drop)
112 {
113     MvAddCh(drop->y, drop->x, '.');
114 }
115
116 static void
117 part2(DATA * drop)
118 {
119     MvAddCh(drop->y, drop->x, 'o');
120 }
121
122 static void
123 part3(DATA * drop)
124 {
125     MvAddCh(drop->y, drop->x, 'O');
126 }
127
128 static void
129 part4(DATA * drop)
130 {
131     MvAddCh(drop->y - 1, drop->x, '-');
132     MvAddStr(drop->y, drop->x - 1, "|.|");
133     MvAddCh(drop->y + 1, drop->x, '-');
134 }
135
136 static void
137 part5(DATA * drop)
138 {
139     MvAddCh(drop->y - 2, drop->x, '-');
140     MvAddStr(drop->y - 1, drop->x - 1, "/ \\");
141     MvAddStr(drop->y, drop->x - 2, "| O |");
142     MvAddStr(drop->y + 1, drop->x - 1, "\\ /");
143     MvAddCh(drop->y + 2, drop->x, '-');
144 }
145
146 static void
147 part6(DATA * drop)
148 {
149     MvAddCh(drop->y - 2, drop->x, ' ');
150     MvAddStr(drop->y - 1, drop->x - 1, "   ");
151     MvAddStr(drop->y, drop->x - 2, "     ");
152     MvAddStr(drop->y + 1, drop->x - 1, "   ");
153     MvAddCh(drop->y + 2, drop->x, ' ');
154 }
155
156 #ifdef USE_PTHREADS
157 static void
158 napsome(void)
159 {
160     napms(60);
161 }
162
163 /*
164  * This runs inside the use_window() mutex.
165  */
166 static int
167 really_draw(WINDOW *win, void *arg)
168 {
169     DATA *data = (DATA *) arg;
170
171     (void) win;
172     next_j(data->state);
173     data->func(data);
174     refresh();
175     return OK;
176 }
177
178 static void
179 draw_part(void (*func) (DATA *), int state, DATA * data)
180 {
181     data->func = func;
182     data->state = state;
183     use_window(stdscr, really_draw, (void *) data);
184     napsome();
185 }
186
187 /*
188  * Tell the threads that one of them can start work on a new raindrop.
189  * They may all be busy if we're sending requests too rapidly.
190  */
191 static int
192 put_next_drop(void)
193 {
194     pthread_cond_signal(&cond_next_drop);
195     pthread_mutex_unlock(&mutex_next_drop);
196
197     return 0;
198 }
199
200 /*
201  * Wait until we're assigned the task of drawing a new raindrop.
202  */
203 static int
204 get_next_drop(void)
205 {
206     pthread_mutex_lock(&mutex_next_drop);
207     pthread_cond_wait(&cond_next_drop, &mutex_next_drop);
208
209     return TRUE;
210 }
211
212 static void *
213 draw_drop(void *arg)
214 {
215     DATA mydata;
216     int mystats;
217
218     /*
219      * Find myself in the list of threads so we can count the number of loops.
220      */
221     for (mystats = 0; mystats < MAX_THREADS; ++mystats) {
222 #if defined(__MINGW32__) && !defined(__WINPTHREADS_VERSION)
223         if (drop_threads[mystats].myself.p == pthread_self().p)
224 #else
225         if (drop_threads[mystats].myself == pthread_self())
226 #endif
227             break;
228     }
229
230     do {
231         if (mystats < MAX_THREADS)
232             drop_threads[mystats].counter++;
233
234         /*
235          * Make a copy of caller's data.  We're cheating for the cases after
236          * the first loop since we still have a pointer into the main thread
237          * to the data which it uses for setting up this thread (but it has
238          * been modified to use different coordinates).
239          */
240         mydata = *(DATA *) arg;
241
242         draw_part(part1, 0, &mydata);
243         draw_part(part2, 1, &mydata);
244         draw_part(part3, 2, &mydata);
245         draw_part(part4, 3, &mydata);
246         draw_part(part5, 4, &mydata);
247         draw_part(part6, 0, &mydata);
248     } while (get_next_drop());
249
250     return NULL;
251 }
252
253 /*
254  * The description of pthread_create() is misleading, since it implies that
255  * threads will exit cleanly after their function returns.
256  *
257  * Since they do not (and the number of threads is limited by system
258  * resources), make a limited number of threads, and signal any that are
259  * waiting when we want a thread past that limit.
260  */
261 static int
262 start_drop(DATA * data)
263 {
264     int rc;
265
266     if (!used_threads) {
267         /* mutex and condition for signalling thread */
268         pthread_mutex_init(&mutex_next_drop, NULL);
269         pthread_cond_init(&cond_next_drop, NULL);
270     }
271
272     if (used_threads < MAX_THREADS) {
273         rc = pthread_create(&(drop_threads[used_threads].myself),
274                             NULL,
275                             draw_drop,
276                             data);
277         ++used_threads;
278     } else {
279         rc = put_next_drop();
280     }
281     return rc;
282 }
283 #endif
284
285 static int
286 get_input(void)
287 {
288     return USING_WINDOW(stdscr, wgetch);
289 }
290
291 static void
292 usage(void)
293 {
294     static const char *msg[] =
295     {
296         "Usage: rain [options]"
297         ,""
298         ,"Options:"
299 #if HAVE_USE_DEFAULT_COLORS
300         ," -d       invoke use_default_colors"
301 #endif
302     };
303     size_t n;
304
305     for (n = 0; n < SIZEOF(msg); n++)
306         fprintf(stderr, "%s\n", msg[n]);
307
308     ExitProgram(EXIT_FAILURE);
309 }
310
311 int
312 main(int argc, char *argv[])
313 {
314     static const char *help[] =
315     {
316         "Commands:",
317         " q/Q        exit the program",
318         " s          do single-step",
319         " <space>    undo single-step",
320         "",
321         0
322     };
323
324     bool done = FALSE;
325     DATA drop;
326 #ifndef USE_PTHREADS
327     DATA last[MAX_DROP];
328 #endif
329     int j = 0;
330     int ch;
331 #if HAVE_USE_DEFAULT_COLORS
332     bool d_option = FALSE;
333 #endif
334
335     while ((ch = getopt(argc, argv, "d")) != -1) {
336         switch (ch) {
337 #if HAVE_USE_DEFAULT_COLORS
338         case 'd':
339             d_option = TRUE;
340             break;
341 #endif
342         default:
343             usage();
344             /* NOTREACHED */
345         }
346     }
347     if (optind < argc)
348         usage();
349
350     setlocale(LC_ALL, "");
351
352     InitAndCatch(initscr(), onsig);
353     if (has_colors()) {
354         int bg = COLOR_BLACK;
355         start_color();
356 #if HAVE_USE_DEFAULT_COLORS
357         if (d_option && (use_default_colors() == OK))
358             bg = -1;
359 #endif
360         init_pair(1, COLOR_BLUE, (short) bg);
361         init_pair(2, COLOR_CYAN, (short) bg);
362     }
363     nl();
364     noecho();
365     curs_set(0);
366     timeout(0);
367
368 #ifndef USE_PTHREADS
369     for (j = MAX_DROP; --j >= 0;) {
370         last[j].x = random_x();
371         last[j].y = random_y();
372     }
373     j = 0;
374 #endif
375
376     while (!done) {
377         drop.x = random_x();
378         drop.y = random_y();
379
380 #ifdef USE_PTHREADS
381         if (start_drop(&drop) != 0) {
382             beep();
383         }
384 #else
385         /*
386          * The non-threaded code draws parts of each drop on each loop.
387          */
388         part1(&drop);
389
390         part2(&last[j]);
391
392         j = next_j(j);
393         part3(&last[j]);
394
395         j = next_j(j);
396         part4(&last[j]);
397
398         j = next_j(j);
399         part5(&last[j]);
400
401         j = next_j(j);
402         part6(&last[j]);
403
404         last[j] = drop;
405 #endif
406
407         switch (get_input()) {
408         case ('q'):
409         case ('Q'):
410             done = TRUE;
411             break;
412         case 's':
413             nodelay(stdscr, FALSE);
414             break;
415         case ' ':
416             nodelay(stdscr, TRUE);
417             break;
418 #ifdef KEY_RESIZE
419         case (KEY_RESIZE):
420             break;
421 #endif
422         case HELP_KEY_1:
423             popup_msg(stdscr, help);
424             break;
425         case ERR:
426             break;
427         default:
428             beep();
429         }
430         napms(50);
431     }
432     exit_curses();
433 #ifdef USE_PTHREADS
434     printf("Counts per thread:\n");
435     for (j = 0; j < MAX_THREADS; ++j)
436         printf("  %d:%ld\n", j, drop_threads[j].counter);
437 #endif
438     ExitProgram(EXIT_SUCCESS);
439 }