├── 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 |
--------------------------------------------------------------------------------