├── LICENSE ├── README.md └── rain.c /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Niklas Kleemann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, 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 in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ascii-rain 2 | 3 | Comfy rain for your console written in C with Ncurses. 4 | 5 |

6 | 7 |

8 | 9 | 10 | ## Dependencies 11 | 12 | You'll need a ncurses library. For Ubuntu packages are: 'ncurses-dev' or 'libncurses-dev'. 13 | For OSX try `$ brew install ncurses`. 14 | 15 | ## Installation 16 | 17 | ### Manual 18 | 19 | First, download this repo: 20 | - `$ git clone https://github.com/nkleemann/ascii-rain.git` 21 | - `$ cd ascii-rain` 22 | 23 | To compile and link 'rain': 24 | 25 | - `$ gcc rain.c -o rain -lncurses` 26 | 27 | Now you can run 'rain' in your current working directory by just executing: ` ./rain`. 28 | 29 | I you want to be able to run this program from every directory you have to copy the executable to `/usr/local/bin` or `/usr/bin`: 30 | - `$ cp rain /usr/local/bin/rain` 31 | 32 | ### AUR 33 | 34 | You can get it from the AUR right [here](https://aur.archlinux.org/packages/ascii-rain-git/). 35 | 36 | ## Notes & Troubleshooting 37 | 38 | You'll (Or better, ncurses) need/s a somewhat modern terminal emulator with color support and the ability to hide the 39 | cursor. Working examples are: 40 | 41 | - iTerm 2 (OSX) 42 | - OSX Terminal 43 | - xfce4-terminal 44 | - terminator 45 | - kitty 46 | - ghostty 47 | 48 | -------------------------------------------------------------------------------- /rain.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | 000 00 4 | 0000000 0000 5 | 0 00 00000000000000000 6 | 0000 0 000000000000000000000000 0 7 | 000000000000000000000000000000000000000 000 8 | 0000000000000000000000000000000000000000000000 9 | 000000000000000000000000000000000000000000000000 10 | 00000000000000000000000000000000000000000000000000000000 11 | C 12 | O M | 13 | F | 14 | Y | | 15 | | R A 16 | I N 17 | I N | 18 | | | 19 | | Y O 20 | | U R 21 | | T E 22 | 23 | R | | 24 | | M 25 | I N 26 | AL 27 | 28 | 29 | Although this was a purely fun-motivated project I 30 | challenged myself to write this code clean & leak-free. 31 | 32 | If you find bugs or leaks feel free to contact me or fork 33 | this. That would be awesome. 34 | 35 | @ Nik, 07.2017 36 | */ 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | 47 | // 48 | // GLOBALS 49 | // 50 | 51 | int userResized = 0; 52 | int slowerDrops = 0; 53 | 54 | typedef struct 55 | { 56 | int w; 57 | int h; 58 | int speed; 59 | int color; 60 | char shape; 61 | } Drop; 62 | 63 | /** 64 | * Since we want the total number of drops to 65 | * be terminal-size dependent we need a dynamic/ 66 | * resizeable data structure containing an array of 67 | * drops. 68 | */ 69 | 70 | typedef struct 71 | { 72 | Drop *drops; 73 | int size; 74 | int capacity; 75 | 76 | } d_Vector; 77 | 78 | 79 | // 80 | // PROTOTYPES 81 | // 82 | 83 | Drop d_create(); 84 | void d_fall(Drop *d); 85 | void d_show(Drop *d); 86 | 87 | void v_init(d_Vector *v, int cap); 88 | void v_free(d_Vector *v); 89 | void v_delete(d_Vector *v); 90 | void v_resize(d_Vector *v, int newCap); 91 | void v_add(d_Vector *v, Drop d); 92 | Drop *v_getAt(d_Vector *v, int pos); 93 | 94 | void initCurses(); 95 | void exitCurses(); 96 | 97 | int pRand(int min, int max); 98 | int getNumOfDrops(); 99 | void handleResize(int sig); 100 | void exitErr(const char *err); 101 | void usage(); 102 | 103 | 104 | // 105 | // FUNCTIONS - DROP 106 | // 107 | 108 | Drop d_create() 109 | { 110 | Drop d; 111 | 112 | d.w = pRand(0, COLS); 113 | d.h = pRand(0, LINES); 114 | 115 | if (slowerDrops) 116 | { 117 | d.speed = pRand(1, 3); 118 | (d.speed < 2) ? (d.shape = '|') : (d.shape = ':'); 119 | } 120 | 121 | else 122 | { 123 | d.speed = pRand(1, 6); 124 | (d.speed < 3) ? (d.shape = '|') : (d.shape = ':'); 125 | } 126 | 127 | int x = d.speed; 128 | 129 | // patented 130 | d.color = (int) ((0.0416 * (x - 4) 131 | * (x - 3) 132 | * (x - 2) - 4) 133 | * (x - 1) + 255); 134 | return d; 135 | } 136 | 137 | void d_fall(Drop *d) 138 | { 139 | d->h += d->speed; 140 | 141 | if (d->h >= LINES-1) 142 | d->h = pRand(0, 10); 143 | } 144 | 145 | void d_show(Drop *d) 146 | { 147 | attron(COLOR_PAIR(d->color)); 148 | mvaddch(d->h, d->w, d->shape); 149 | } 150 | 151 | 152 | // 153 | // FUNCTIONS - VECTOR 154 | // 155 | 156 | void v_init(d_Vector *v, int cap) 157 | { 158 | if (cap > 0 && v != 0) 159 | { 160 | v->drops = (Drop *) malloc(sizeof(Drop) * cap); 161 | 162 | if (v->drops != 0) 163 | { 164 | v->size = 0; 165 | v->capacity = cap; 166 | } 167 | else 168 | exitErr("\n*DROP ARRAY IS >NULL<*\n"); 169 | } 170 | else 171 | exitErr("\n*VECTOR INIT FAILED*\n"); 172 | } 173 | 174 | void v_free(d_Vector *v) 175 | { 176 | if(v->drops != 0) 177 | { 178 | free(v->drops); 179 | v->drops = 0; 180 | } 181 | 182 | v->size = 0; 183 | v->capacity = 0; 184 | } 185 | 186 | void v_delete(d_Vector *v) 187 | { 188 | v_free(v); 189 | } 190 | 191 | void v_resize(d_Vector *v, int newCap) 192 | { 193 | d_Vector newVec; 194 | v_init(&newVec, newCap); 195 | 196 | for (int i = 0; i < newCap; i++) 197 | v_add(&newVec, d_create()); 198 | 199 | v_free(v); 200 | *v = newVec; 201 | } 202 | 203 | void v_add(d_Vector *v, Drop d) 204 | { 205 | if(v->size >= v->capacity) 206 | v_resize(v, 2 * v->capacity); 207 | 208 | v->drops[v->size] = d; 209 | v->size++; 210 | } 211 | 212 | Drop *v_getAt(d_Vector *v, int pos) 213 | { 214 | if ((pos < v->size) && (pos >= 0)) 215 | return &(v->drops[pos]); 216 | 217 | exitErr("\n*BAD ACCESS*\n"); 218 | } 219 | 220 | 221 | // 222 | // FUNCTIONS - CURSES 223 | // 224 | 225 | void initCurses() 226 | { 227 | initscr(); 228 | noecho(); 229 | cbreak(); 230 | keypad(stdscr, 1); 231 | curs_set(0); 232 | 233 | if ((curs_set(0)) == 1) 234 | exitErr("\n*Terminal emulator lacks capabilities.\n(Can't hide Cursor).*\n"); 235 | 236 | timeout(0); 237 | signal(SIGWINCH, handleResize); 238 | 239 | int hazColors = has_colors() && can_change_color(); 240 | if (hazColors) 241 | { 242 | use_default_colors(); 243 | start_color(); 244 | 245 | for (short i = 0; i < COLORS; i++) 246 | init_pair(i + 1, i, -1); 247 | } 248 | else 249 | exitErr("\n*Terminal emulator lacks capabilities.\n(Can't have colors).\n*"); 250 | 251 | } 252 | 253 | void exitCurses() 254 | { 255 | curs_set(1); 256 | clear(); 257 | refresh(); 258 | resetty(); 259 | endwin(); 260 | } 261 | 262 | 263 | // 264 | // UTILS 265 | // 266 | 267 | int pRand(int min, int max) 268 | { 269 | max -= 1; 270 | return min + rand() / (RAND_MAX / (max - min + 1) + 1); 271 | } 272 | 273 | void handleResize(int sig) 274 | { 275 | endwin(); 276 | 277 | userResized = 1; 278 | 279 | refresh(); 280 | erase(); 281 | } 282 | 283 | void exitErr(const char *err) 284 | { 285 | exitCurses(); 286 | printf("%s", err); 287 | exit(0); 288 | } 289 | 290 | int getNumOfDrops() 291 | { 292 | int nDrops = 0; 293 | 294 | if ((LINES < 20 && COLS > 100) || (COLS < 100 && LINES < 40)) 295 | { 296 | nDrops = (int) (COLS * 0.75); 297 | 298 | // Watch that state.. 299 | slowerDrops = 1; 300 | } 301 | else 302 | { 303 | nDrops = (int) (COLS * 1.5); 304 | slowerDrops = 0; 305 | } 306 | 307 | return nDrops; 308 | } 309 | 310 | void usage() 311 | { 312 | printf(" Usage: rain\n"); 313 | printf("No arguments supported yet. It's just rain, after all.\n"); 314 | printf("Hit 'q' to exit.\n"); 315 | } 316 | 317 | // wrapper around nanosleep, replacing deprecated usleep func 318 | int mssleep(long msec) 319 | { 320 | struct timespec ts; 321 | int res; 322 | 323 | if (msec < 0) 324 | { 325 | errno = EINVAL; 326 | return -1; 327 | } 328 | 329 | ts.tv_sec = msec / 1000; 330 | ts.tv_nsec = (msec % 1000) * 1000000; 331 | 332 | do { 333 | res = nanosleep(&ts, &ts); 334 | } while (res && errno == EINTR); 335 | 336 | return res; 337 | } 338 | 339 | 340 | int main(int argc, char **argv) 341 | { 342 | if (argc != 1) 343 | { 344 | usage(); 345 | exit(0); 346 | } 347 | 348 | // User KeyEvent 349 | int key; 350 | 351 | srand((unsigned int) getpid()); 352 | initCurses(); 353 | 354 | int dropsTotal = getNumOfDrops(); 355 | d_Vector drops; 356 | v_init(&drops, dropsTotal); 357 | 358 | for (int i = 0; i < dropsTotal; i++) 359 | v_add(&drops, d_create()); 360 | 361 | 362 | // 363 | // DRAW-LOOP 364 | // 365 | 366 | while (1) 367 | { 368 | 369 | if (userResized) 370 | { 371 | // Stabilizing hectic user.. 372 | mssleep(90); 373 | 374 | dropsTotal = getNumOfDrops(); 375 | v_resize(&drops, dropsTotal); 376 | 377 | userResized = 0; 378 | } 379 | 380 | dropsTotal = getNumOfDrops(); 381 | 382 | for (int i = 0; i < dropsTotal; i++) 383 | { 384 | d_fall(v_getAt(&drops, i)); 385 | d_show(v_getAt(&drops, i)); 386 | } 387 | 388 | // Frame Delay 389 | mssleep(30); 390 | 391 | if ((key = wgetch(stdscr)) == 'q') 392 | break; 393 | 394 | erase(); 395 | } 396 | 397 | // Free pointers & exit gracefully 398 | v_delete(&drops); 399 | exitCurses(); 400 | 401 | } 402 | --------------------------------------------------------------------------------