411306a71f9dbcdf55690b415cf1621be44ca6e3
[ncurses.git] / test / rain.c
1 /****************************************************************************
2  * Copyright (c) 1998-2006,2008 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.33 2008/03/22 18:12:01 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 float
78 ranf(void)
79 {
80     long r = (rand() & 077777);
81     return ((float) r / 32768.);
82 }
83
84 static int
85 random_x(void)
86 {
87     return (((float) (COLS - 4) * ranf()) + 2);
88 }
89
90 static int
91 random_y(void)
92 {
93     return (((float) (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 = COLOR_PAIR(z);
106         if (z)
107             color |= A_BOLD;
108         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         if (drop_threads[mystats].myself == pthread_self())
226             break;
227     }
228
229     do {
230         if (mystats < MAX_THREADS)
231             drop_threads[mystats].counter++;
232
233         /*
234          * Make a copy of caller's data.  We're cheating for the cases after
235          * the first loop since we still have a pointer into the main thread
236          * to the data which it uses for setting up this thread (but it has
237          * been modified to use different coordinates).
238          */
239         mydata = *(DATA *) arg;
240
241         draw_part(part1, 0, &mydata);
242         draw_part(part2, 1, &mydata);
243         draw_part(part3, 2, &mydata);
244         draw_part(part4, 3, &mydata);
245         draw_part(part5, 4, &mydata);
246         draw_part(part6, 0, &mydata);
247     } while (get_next_drop());
248
249     return NULL;
250 }
251
252 /*
253  * The description of pthread_create() is misleading, since it implies that
254  * threads will exit cleanly after their function returns.
255  * 
256  * Since they do not (and the number of threads is limited by system
257  * resources), make a limited number of threads, and signal any that are
258  * waiting when we want a thread past that limit.
259  */
260 static int
261 start_drop(DATA * data)
262 {
263     int rc;
264
265     if (!used_threads) {
266         /* mutex and condition for signalling thread */
267         pthread_mutex_init(&mutex_next_drop, NULL);
268         pthread_cond_init(&cond_next_drop, NULL);
269     }
270
271     if (used_threads < MAX_THREADS) {
272         rc = pthread_create(&(drop_threads[used_threads].myself),
273                             NULL,
274                             draw_drop,
275                             data);
276         ++used_threads;
277     } else {
278         rc = put_next_drop();
279     }
280     return rc;
281 }
282 #endif
283
284 static int
285 get_input(void)
286 {
287     return USING_WINDOW(stdscr, wgetch);
288 }
289
290 int
291 main(int argc GCC_UNUSED,
292      char *argv[]GCC_UNUSED)
293 {
294     bool done = FALSE;
295     DATA drop;
296 #ifndef USE_PTHREADS
297     DATA last[MAX_DROP];
298 #endif
299     int j = 0;
300
301     setlocale(LC_ALL, "");
302
303     CATCHALL(onsig);
304
305     initscr();
306     if (has_colors()) {
307         int bg = COLOR_BLACK;
308         start_color();
309 #if HAVE_USE_DEFAULT_COLORS
310         if (use_default_colors() == OK)
311             bg = -1;
312 #endif
313         init_pair(1, COLOR_BLUE, bg);
314         init_pair(2, COLOR_CYAN, bg);
315     }
316     nl();
317     noecho();
318     curs_set(0);
319     timeout(0);
320
321 #ifndef USE_PTHREADS
322     for (j = MAX_DROP; --j >= 0;) {
323         last[j].x = random_x();
324         last[j].y = random_y();
325     }
326 #endif
327
328     while (!done) {
329         drop.x = random_x();
330         drop.y = random_y();
331
332 #ifdef USE_PTHREADS
333         if (start_drop(&drop) != 0) {
334             beep();
335         }
336 #else
337         /*
338          * The non-threaded code draws parts of each drop on each loop.
339          */
340         part1(&drop);
341
342         part2(&last[j]);
343
344         j = next_j(j);
345         part3(&last[j]);
346
347         j = next_j(j);
348         part4(&last[j]);
349
350         j = next_j(j);
351         part5(&last[j]);
352
353         j = next_j(j);
354         part6(&last[j]);
355
356         last[j] = drop;
357 #endif
358
359         switch (get_input()) {
360         case ('q'):
361         case ('Q'):
362             done = TRUE;
363             break;
364         case 's':
365             nodelay(stdscr, FALSE);
366             break;
367         case ' ':
368             nodelay(stdscr, TRUE);
369             break;
370 #ifdef KEY_RESIZE
371         case (KEY_RESIZE):
372             break;
373 #endif
374         }
375         napms(50);
376     }
377     curs_set(1);
378     endwin();
379 #ifdef USE_PTHREADS
380     printf("Counts per thread:\n");
381     for (j = 0; j < MAX_THREADS; ++j)
382         printf("  %d:%ld\n", j, drop_threads[j].counter);
383 #endif
384     ExitProgram(EXIT_SUCCESS);
385 }