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