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