ncurses 6.0 - patch 20160625
[ncurses.git] / test / rain.c
1 /****************************************************************************
2  * Copyright (c) 1998-2012,2014 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.41 2014/08/02 17:24:07 tom Exp $
30  */
31 #include <test.priv.h>
32
33 /* rain 11/3/1980 EPS/CITHEP */
34
35 #ifdef USE_PTHREADS
36 #include <pthread.h>
37 #endif
38
39 WANT_USE_WINDOW();
40
41 #define MAX_THREADS     10
42 #define MAX_DROP        5
43
44 struct DATA;
45
46 typedef void (*DrawPart) (struct DATA *);
47
48 typedef struct DATA {
49     int y, x;
50 #ifdef USE_PTHREADS
51     DrawPart func;
52     int state;
53 #endif
54 } DATA;
55
56 #ifdef USE_PTHREADS
57 pthread_cond_t cond_next_drop;
58 pthread_mutex_t mutex_next_drop;
59 static int used_threads;
60
61 typedef struct {
62     pthread_t myself;
63     long counter;
64 } STATS;
65
66 static STATS drop_threads[MAX_THREADS];
67 #endif
68
69 static void
70 onsig(int n GCC_UNUSED)
71 {
72     curs_set(1);
73     endwin();
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 int
292 main(int argc GCC_UNUSED,
293      char *argv[]GCC_UNUSED)
294 {
295     bool done = FALSE;
296     DATA drop;
297 #ifndef USE_PTHREADS
298     DATA last[MAX_DROP];
299 #endif
300     int j = 0;
301
302     setlocale(LC_ALL, "");
303
304     CATCHALL(onsig);
305
306     initscr();
307     if (has_colors()) {
308         int bg = COLOR_BLACK;
309         start_color();
310 #if HAVE_USE_DEFAULT_COLORS
311         if (use_default_colors() == OK)
312             bg = -1;
313 #endif
314         init_pair(1, COLOR_BLUE, (short) bg);
315         init_pair(2, COLOR_CYAN, (short) bg);
316     }
317     nl();
318     noecho();
319     curs_set(0);
320     timeout(0);
321
322 #ifndef USE_PTHREADS
323     for (j = MAX_DROP; --j >= 0;) {
324         last[j].x = random_x();
325         last[j].y = random_y();
326     }
327     j = 0;
328 #endif
329
330     while (!done) {
331         drop.x = random_x();
332         drop.y = random_y();
333
334 #ifdef USE_PTHREADS
335         if (start_drop(&drop) != 0) {
336             beep();
337         }
338 #else
339         /*
340          * The non-threaded code draws parts of each drop on each loop.
341          */
342         part1(&drop);
343
344         part2(&last[j]);
345
346         j = next_j(j);
347         part3(&last[j]);
348
349         j = next_j(j);
350         part4(&last[j]);
351
352         j = next_j(j);
353         part5(&last[j]);
354
355         j = next_j(j);
356         part6(&last[j]);
357
358         last[j] = drop;
359 #endif
360
361         switch (get_input()) {
362         case ('q'):
363         case ('Q'):
364             done = TRUE;
365             break;
366         case 's':
367             nodelay(stdscr, FALSE);
368             break;
369         case ' ':
370             nodelay(stdscr, TRUE);
371             break;
372 #ifdef KEY_RESIZE
373         case (KEY_RESIZE):
374             break;
375 #endif
376         }
377         napms(50);
378     }
379     curs_set(1);
380     endwin();
381 #ifdef USE_PTHREADS
382     printf("Counts per thread:\n");
383     for (j = 0; j < MAX_THREADS; ++j)
384         printf("  %d:%ld\n", j, drop_threads[j].counter);
385 #endif
386     ExitProgram(EXIT_SUCCESS);
387 }