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.34 2008/05/24 23:34:34 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 39WANT_USE_WINDOW(); 40 41#define MAX_THREADS 10 42#define MAX_DROP 5 43 44struct DATA; 45 46typedef void (*DrawPart) (struct DATA *); 47 48typedef 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 57pthread_cond_t cond_next_drop; 58pthread_mutex_t mutex_next_drop; 59static int used_threads; 60 61typedef struct { 62 pthread_t myself; 63 long counter; 64} STATS; 65 66static STATS drop_threads[MAX_THREADS]; 67#endif 68 69static void 70onsig(int n GCC_UNUSED) 71{ 72 curs_set(1); 73 endwin(); 74 ExitProgram(EXIT_FAILURE); 75} 76 77static float 78ranf(void) 79{ 80 long r = (rand() & 077777); 81 return ((float) r / 32768.); 82} 83 84static int 85random_x(void) 86{ 87 return (((float) (COLS - 4) * ranf()) + 2); 88} 89 90static int 91random_y(void) 92{ 93 return (((float) (LINES - 4) * ranf()) + 2); 94} 95 96static int 97next_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 113static void 114part1(DATA * drop) 115{ 116 mvaddch(drop->y, drop->x, '.'); 117} 118 119static void 120part2(DATA * drop) 121{ 122 mvaddch(drop->y, drop->x, 'o'); 123} 124 125static void 126part3(DATA * drop) 127{ 128 mvaddch(drop->y, drop->x, 'O'); 129} 130 131static void 132part4(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 139static void 140part5(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 149static void 150part6(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 160static void 161napsome(void) 162{ 163 napms(60); 164} 165 166/* 167 * This runs inside the use_window() mutex. 168 */ 169static int 170really_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 181static void 182draw_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 */ 194static int 195put_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 */ 206static int 207get_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 215static void * 216draw_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 */ 260static int 261start_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 284static int 285get_input(void) 286{ 287 return USING_WINDOW(stdscr, wgetch); 288} 289 290int 291main(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 j = 0; 327#endif 328 329 while (!done) { 330 drop.x = random_x(); 331 drop.y = random_y(); 332 333#ifdef USE_PTHREADS 334 if (start_drop(&drop) != 0) { 335 beep(); 336 } 337#else 338 /* 339 * The non-threaded code draws parts of each drop on each loop. 340 */ 341 part1(&drop); 342 343 part2(&last[j]); 344 345 j = next_j(j); 346 part3(&last[j]); 347 348 j = next_j(j); 349 part4(&last[j]); 350 351 j = next_j(j); 352 part5(&last[j]); 353 354 j = next_j(j); 355 part6(&last[j]); 356 357 last[j] = drop; 358#endif 359 360 switch (get_input()) { 361 case ('q'): 362 case ('Q'): 363 done = TRUE; 364 break; 365 case 's': 366 nodelay(stdscr, FALSE); 367 break; 368 case ' ': 369 nodelay(stdscr, TRUE); 370 break; 371#ifdef KEY_RESIZE 372 case (KEY_RESIZE): 373 break; 374#endif 375 } 376 napms(50); 377 } 378 curs_set(1); 379 endwin(); 380#ifdef USE_PTHREADS 381 printf("Counts per thread:\n"); 382 for (j = 0; j < MAX_THREADS; ++j) 383 printf(" %d:%ld\n", j, drop_threads[j].counter); 384#endif 385 ExitProgram(EXIT_SUCCESS); 386} 387