A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Matthias Wientapper
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22/*
23 * This is an implementatino of Conway's Game of Life
24 *
25 * from http://en.wikipedia.org/wiki/Conway's_Game_of_Life:
26 *
27 * Rules
28 *
29 * The universe of the Game of Life is an infinite two-dimensional
30 * orthogonal grid of square cells, each of which is in one of two
31 * possible states, live or dead. Every cell interacts with its eight
32 * neighbours, which are the cells that are directly horizontally,
33 * vertically, or diagonally adjacent. At each step in time, the
34 * following transitions occur:
35 *
36 * 1. Any live cell with fewer than two live neighbours dies, as if by
37 * loneliness.
38 *
39 * 2. Any live cell with more than three live neighbours dies, as if
40 * by overcrowding.
41 *
42 * 3. Any live cell with two or three live neighbours lives,
43 * unchanged, to the next generation.
44 *
45 * 4. Any dead cell with exactly three live neighbours comes to life.
46 *
47 * The initial pattern constitutes the first generation of the
48 * system. The second generation is created by applying the above
49 * rules simultaneously to every cell in the first generation --
50 * births and deaths happen simultaneously, and the discrete moment at
51 * which this happens is sometimes called a tick. (In other words,
52 * each generation is based entirely on the one before.) The rules
53 * continue to be applied repeatedly to create further generations.
54 *
55 *
56 *
57 * TODO:
58 * - nicer colours for pixels with respect to age
59 * - editor for start patterns
60 * - probably tons of speed-up opportunities
61 */
62
63#include "plugin.h"
64#include "lib/pluginlib_actions.h"
65#include "lib/helper.h"
66
67
68
69#define ROCKLIFE_PLAY_PAUSE PLA_SELECT
70#define ROCKLIFE_INIT PLA_DOWN
71#define ROCKLIFE_NEXT PLA_RIGHT
72#define ROCKLIFE_NEXT_REP PLA_RIGHT_REPEAT
73#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
74 || (CONFIG_KEYPAD == IPOD_3G_PAD) \
75 || (CONFIG_KEYPAD == IPOD_4G_PAD)
76#define ROCKLIFE_QUIT PLA_UP
77#else
78#define ROCKLIFE_QUIT PLA_CANCEL
79#endif
80#define ROCKLIFE_STATUS PLA_LEFT
81
82#define PATTERN_RANDOM 0
83#define PATTERN_GROWTH_1 1
84#define PATTERN_GROWTH_2 2
85#define PATTERN_ACORN 3
86#define PATTERN_GLIDER_GUN 4
87
88const struct button_mapping *plugin_contexts[]
89= {pla_main_ctx};
90
91#define GRID_W LCD_WIDTH
92#define GRID_H LCD_HEIGHT
93
94unsigned char grid_a[GRID_W][GRID_H];
95unsigned char grid_b[GRID_W][GRID_H];
96int generation = 0;
97int population = 0;
98int status_line = 0;
99
100
101static inline bool is_valid_cell(int x, int y) {
102 return (x >= 0 && x < GRID_W
103 && y >= 0 && y < GRID_H);
104}
105
106static inline void set_cell_age(int x, int y, unsigned char age, char *pgrid) {
107 pgrid[x+y*GRID_W] = age;
108}
109
110static inline void set_cell(int x, int y, char *pgrid) {
111 set_cell_age(x, y, 1, pgrid);
112}
113
114static inline unsigned char get_cell(int x, int y, char *pgrid) {
115 if (x < 0)
116 x += GRID_W;
117 else if (x >= GRID_W)
118 x -= GRID_W;
119
120 if (y < 0)
121 y += GRID_H;
122 else if (y >= GRID_H)
123 y -= GRID_H;
124
125 return pgrid[x+y*GRID_W];
126}
127
128/* clear grid */
129static void init_grid(char *pgrid){
130 memset(pgrid, 0, GRID_W * GRID_H);
131}
132
133/*fill grid with pattern from file (viewer mode)*/
134static bool load_cellfile(const char *file, char *pgrid){
135 int fd;
136 fd = rb->open(file, O_RDONLY);
137 if (fd<0)
138 return false;
139
140 init_grid(pgrid);
141
142 char c;
143 int nc, x, y, xmid, ymid;
144 bool comment;
145 x=0;
146 y=0;
147 xmid = (GRID_W>>1) - 2;
148 ymid = (GRID_H>>1) - 2;
149 comment = false;
150
151 while (true) {
152 nc = rb->read(fd, &c, 1);
153 if (nc <= 0)
154 break;
155
156 switch(c) {
157 case '!':
158 comment = true;
159 break;
160 case '.':
161 if (!comment)
162 x++;
163 break;
164 case 'O':
165 if (!comment) {
166 if (is_valid_cell(xmid + x, ymid + y))
167 set_cell(xmid + x, ymid + y, pgrid);
168 x++;
169 }
170 break;
171 case '\n':
172 y++;
173 x=0;
174 comment = false;
175 break;
176 default:
177 break;
178 }
179 }
180 rb->close(fd);
181 return true;
182}
183
184/* fill grid with initial pattern */
185static void setup_grid(char *pgrid, int pattern){
186 int n, max;
187 int xmid, ymid;
188
189 max = GRID_W * GRID_H;
190
191 switch(pattern){
192 case PATTERN_RANDOM:
193 rb->splash(HZ, "Random");
194#if 0 /* two oscilators, debug pattern */
195 set_cell( 0, 1 , pgrid);
196 set_cell( 1, 1 , pgrid);
197 set_cell( 2, 1 , pgrid);
198
199 set_cell( 6, 7 , pgrid);
200 set_cell( 7, 7 , pgrid);
201 set_cell( 8, 7 , pgrid);
202#endif
203
204 /* fill screen randomly */
205 for(n=0; n<(max>>2); n++)
206 pgrid[rb->rand()%max] = 1;
207
208 break;
209
210 case PATTERN_GROWTH_1:
211 rb->splash(HZ, "Growth");
212 xmid = (GRID_W>>1) - 2;
213 ymid = (GRID_H>>1) - 2;
214 set_cell(xmid + 6, ymid + 0 , pgrid);
215 set_cell(xmid + 4, ymid + 1 , pgrid);
216 set_cell(xmid + 6, ymid + 1 , pgrid);
217 set_cell(xmid + 7, ymid + 1 , pgrid);
218 set_cell(xmid + 4, ymid + 2 , pgrid);
219 set_cell(xmid + 6, ymid + 2 , pgrid);
220 set_cell(xmid + 4, ymid + 3 , pgrid);
221 set_cell(xmid + 2, ymid + 4 , pgrid);
222 set_cell(xmid + 0, ymid + 5 , pgrid);
223 set_cell(xmid + 2, ymid + 5 , pgrid);
224 break;
225 case PATTERN_ACORN:
226 rb->splash(HZ, "Acorn");
227 xmid = (GRID_W>>1) - 3;
228 ymid = (GRID_H>>1) - 1;
229 set_cell(xmid + 1, ymid + 0 , pgrid);
230 set_cell(xmid + 3, ymid + 1 , pgrid);
231 set_cell(xmid + 0, ymid + 2 , pgrid);
232 set_cell(xmid + 1, ymid + 2 , pgrid);
233 set_cell(xmid + 4, ymid + 2 , pgrid);
234 set_cell(xmid + 5, ymid + 2 , pgrid);
235 set_cell(xmid + 6, ymid + 2 , pgrid);
236 break;
237 case PATTERN_GROWTH_2:
238 rb->splash(HZ, "Growth 2");
239 xmid = (GRID_W>>1) - 4;
240 ymid = (GRID_H>>1) - 1;
241 set_cell(xmid + 0, ymid + 0 , pgrid);
242 set_cell(xmid + 1, ymid + 0 , pgrid);
243 set_cell(xmid + 2, ymid + 0 , pgrid);
244 set_cell(xmid + 4, ymid + 0 , pgrid);
245 set_cell(xmid + 0, ymid + 1 , pgrid);
246 set_cell(xmid + 3, ymid + 2 , pgrid);
247 set_cell(xmid + 4, ymid + 2 , pgrid);
248 set_cell(xmid + 1, ymid + 3 , pgrid);
249 set_cell(xmid + 2, ymid + 3 , pgrid);
250 set_cell(xmid + 4, ymid + 3 , pgrid);
251 set_cell(xmid + 0, ymid + 4 , pgrid);
252 set_cell(xmid + 2, ymid + 4 , pgrid);
253 set_cell(xmid + 4, ymid + 4 , pgrid);
254 break;
255 case PATTERN_GLIDER_GUN:
256 rb->splash(HZ, "Glider Gun");
257 set_cell( 24, 0, pgrid);
258 set_cell( 22, 1, pgrid);
259 set_cell( 24, 1, pgrid);
260 set_cell( 12, 2, pgrid);
261 set_cell( 13, 2, pgrid);
262 set_cell( 20, 2, pgrid);
263 set_cell( 21, 2, pgrid);
264 set_cell( 34, 2, pgrid);
265 set_cell( 35, 2, pgrid);
266 set_cell( 11, 3, pgrid);
267 set_cell( 15, 3, pgrid);
268 set_cell( 20, 3, pgrid);
269 set_cell( 21, 3, pgrid);
270 set_cell( 34, 3, pgrid);
271 set_cell( 35, 3, pgrid);
272 set_cell( 0, 4, pgrid);
273 set_cell( 1, 4, pgrid);
274 set_cell( 10, 4, pgrid);
275 set_cell( 16, 4, pgrid);
276 set_cell( 20, 4, pgrid);
277 set_cell( 21, 4, pgrid);
278 set_cell( 0, 5, pgrid);
279 set_cell( 1, 5, pgrid);
280 set_cell( 10, 5, pgrid);
281 set_cell( 14, 5, pgrid);
282 set_cell( 16, 5, pgrid);
283 set_cell( 17, 5, pgrid);
284 set_cell( 22, 5, pgrid);
285 set_cell( 24, 5, pgrid);
286 set_cell( 10, 6, pgrid);
287 set_cell( 16, 6, pgrid);
288 set_cell( 24, 6, pgrid);
289 set_cell( 11, 7, pgrid);
290 set_cell( 15, 7, pgrid);
291 set_cell( 12, 8, pgrid);
292 set_cell( 13, 8, pgrid);
293 break;
294 }
295}
296
297/* display grid */
298static void show_grid(char *pgrid){
299 int x, y;
300 unsigned char age;
301
302 rb->lcd_clear_display();
303 for(y=0; y<GRID_H; y++){
304 for(x=0; x<GRID_W; x++){
305 age = get_cell(x, y, pgrid);
306 if(age){
307#if LCD_DEPTH >= 16
308 rb->lcd_set_foreground( LCD_RGBPACK( age, age, age ));
309#elif LCD_DEPTH == 2
310 rb->lcd_set_foreground(age>>7);
311#endif
312 rb->lcd_drawpixel(x, y);
313 }
314 }
315 }
316 if(status_line){
317#if LCD_DEPTH > 1
318 rb->lcd_set_foreground( LCD_BLACK );
319#endif
320 rb->lcd_putsf(0, 0, "g:%d p:%d", generation, population);
321 }
322 rb->lcd_update();
323}
324
325
326/* Calculates whether the cell will be alive in the next generation.
327 n is the array with 9 elements that represent the cell itself and its
328 neighborhood like this (the cell itself is n[4]):
329 0 1 2
330 3 4 5
331 6 7 8
332*/
333static inline bool check_cell(unsigned char *n)
334{
335 int empty_cells = 0;
336 int alive_cells;
337 bool result;
338
339 /* count empty neighbour cells */
340 if(n[0]==0) empty_cells++;
341 if(n[1]==0) empty_cells++;
342 if(n[2]==0) empty_cells++;
343 if(n[3]==0) empty_cells++;
344 if(n[5]==0) empty_cells++;
345 if(n[6]==0) empty_cells++;
346 if(n[7]==0) empty_cells++;
347 if(n[8]==0) empty_cells++;
348
349 /* now we build the number of non-zero neighbours :-P */
350 alive_cells = 8 - empty_cells;
351
352 if (n[4]) {
353 /* If the cell is alive, it stays alive iff it has 2 or 3 alive neighbours */
354 result = (alive_cells==2 || alive_cells==3);
355 }
356 else {
357 /* If the cell is dead, it gets alive iff it has 3 alive neighbours */
358 result = (alive_cells==3);
359 }
360
361 return result;
362}
363
364/* Calculate the next generation of cells
365 *
366 * The borders of the grid are connected to their opposite sides.
367 *
368 * To avoid multiplications while accessing data in the 2-d grid
369 * (pgrid) we try to re-use previously accessed neighbourhood
370 * information which is stored in an 3x3 array.
371 */
372static void next_generation(char *pgrid, char *pnext_grid){
373 int x, y;
374 bool cell;
375 unsigned char age;
376 unsigned char n[9];
377
378 rb->memset(n, 0, sizeof(n));
379
380 /*
381 * cell is (4) with 8 neighbours
382 *
383 * 0|1|2
384 * -----
385 * 3|4|5
386 * -----
387 * 6|7|8
388 */
389
390 population = 0;
391
392 /* go through the grid */
393 for(y=0; y<GRID_H; y++){
394 for(x=0; x<GRID_W; x++){
395 if(y==0 && x==0){
396 /* first cell in first row, we have to load all neighbours */
397 n[0] = get_cell(x-1, y-1, pgrid);
398 n[1] = get_cell(x, y-1, pgrid);
399 n[2] = get_cell(x+1, y-1, pgrid);
400 n[3] = get_cell(x-1, y, pgrid);
401 n[4] = get_cell(x, y, pgrid);
402 n[5] = get_cell(x+1, y, pgrid);
403 n[6] = get_cell(x-1, y+1, pgrid);
404 n[7] = get_cell(x, y+1, pgrid);
405 n[8] = get_cell(x+1, y+1, pgrid);
406 } else {
407 if(x==0){
408 /* beginning of a row, copy what we know about our predecessor,
409 0, 1, 3, 4 are known, 2, 5, 6, 7, 8 have to be loaded
410 */
411 n[0] = n[4];
412 n[1] = n[5];
413 n[2] = get_cell(x+1, y-1, pgrid);
414 n[3] = n[7];
415 n[4] = n[8];
416 n[5] = get_cell(x+1, y, pgrid);
417 n[6] = get_cell(x-1, y+1, pgrid);
418 n[7] = get_cell(x, y+1, pgrid);
419 n[8] = get_cell(x+1, y+1, pgrid);
420 } else {
421 /* we are moving right in a row,
422 * copy what we know about the neighbours on our left side,
423 * 2, 5, 8 have to be loaded
424 */
425 n[0] = n[1];
426 n[1] = n[2];
427 n[2] = get_cell(x+1, y-1, pgrid);
428 n[3] = n[4];
429 n[4] = n[5];
430 n[5] = get_cell(x+1, y, pgrid);
431 n[6] = n[7];
432 n[7] = n[8];
433 n[8] = get_cell(x+1, y+1, pgrid);
434 }
435 }
436
437 /* how old is our cell? */
438 age = n[4];
439
440 /* calculate the cell based on given neighbour information */
441 cell = check_cell(n);
442
443 /* is the actual cell alive? */
444 if(cell){
445 population++;
446 /* prevent overflow */
447 if(age<252)
448 age++;
449 set_cell_age(x, y, age, pnext_grid);
450 }
451 else
452 set_cell_age(x, y, 0, pnext_grid);
453#if 0
454 DEBUGF("x=%d,y=%d\n", x, y);
455 DEBUGF("cell: %d\n", cell);
456 DEBUGF("%d %d %d\n", n[0],n[1],n[2]);
457 DEBUGF("%d %d %d\n", n[3],n[4],n[5]);
458 DEBUGF("%d %d %d\n", n[6],n[7],n[8]);
459 DEBUGF("----------------\n");
460#endif
461 }
462 }
463 generation++;
464}
465
466
467
468/**********************************/
469/* this is the plugin entry point */
470/**********************************/
471enum plugin_status plugin_start(const void* parameter)
472{
473 int button = 0;
474 int quit = 0;
475 int stop = 0;
476 int usb = 0;
477 int pattern = 0;
478 char *pgrid;
479 char *pnext_grid;
480 char *ptemp;
481 (void)(parameter);
482
483 backlight_ignore_timeout();
484
485#if LCD_DEPTH > 1
486 rb->lcd_set_backdrop(NULL);
487#ifdef HAVE_LCD_COLOR
488 rb->lcd_set_background(LCD_RGBPACK(182, 198, 229)); /* rockbox blue */
489#else
490 rb->lcd_set_background(LCD_DEFAULT_BG);
491#endif /* HAVE_LCD_COLOR */
492#endif /* LCD_DEPTH > 1 */
493
494 /* link pointers to grids */
495 pgrid = (char *)grid_a;
496 pnext_grid = (char *)grid_b;
497
498 init_grid(pgrid);
499
500 if( parameter == NULL )
501 {
502 setup_grid(pgrid, pattern++);
503 }
504 else
505 {
506 if( load_cellfile(parameter, pgrid) )
507 {
508 rb->splashf( 1*HZ, "Cells loaded (%s)", (char *)parameter );
509 }
510 else
511 {
512 rb->splash( 1*HZ, "File Open Error");
513 setup_grid(pgrid, pattern++); /* fall back to stored patterns */
514 }
515 }
516
517
518 show_grid(pgrid);
519
520 while(!quit) {
521 button = pluginlib_getaction(TIMEOUT_BLOCK, plugin_contexts, ARRAYLEN(plugin_contexts));
522 switch(button) {
523 case ROCKLIFE_NEXT:
524 case ROCKLIFE_NEXT_REP:
525 /* calculate next generation */
526 next_generation(pgrid, pnext_grid);
527 /* swap buffers, grid is the new generation */
528 ptemp = pgrid;
529 pgrid = pnext_grid;
530 pnext_grid = ptemp;
531 /* show new generation */
532 show_grid(pgrid);
533 break;
534 case ROCKLIFE_PLAY_PAUSE:
535 stop = 0;
536 while(!stop){
537 /* calculate next generation */
538 next_generation(pgrid, pnext_grid);
539 /* swap buffers, grid is the new generation */
540 ptemp = pgrid;
541 pgrid = pnext_grid;
542 pnext_grid = ptemp;
543 /* show new generation */
544 rb->yield();
545 show_grid(pgrid);
546 button = pluginlib_getaction(0, plugin_contexts, ARRAYLEN(plugin_contexts));
547 switch(button) {
548 case ROCKLIFE_PLAY_PAUSE:
549 case ROCKLIFE_QUIT:
550 stop = 1;
551 break;
552 default:
553 if (rb->default_event_handler(button) == SYS_USB_CONNECTED) {
554 stop = 1;
555 quit = 1;
556 usb = 1;
557 }
558 break;
559 }
560 rb->yield();
561 }
562 break;
563 case ROCKLIFE_INIT:
564 init_grid(pgrid);
565 setup_grid(pgrid, pattern);
566 show_grid(pgrid);
567 pattern++;
568 pattern%=5;
569 break;
570 case ROCKLIFE_STATUS:
571 status_line = !status_line;
572 show_grid(pgrid);
573 break;
574 case ROCKLIFE_QUIT:
575 /* quit plugin */
576 quit = 1;
577 break;
578 default:
579 if (rb->default_event_handler(button) == SYS_USB_CONNECTED) {
580 quit = 1;
581 usb = 1;
582 }
583 break;
584 }
585 rb->yield();
586 }
587
588 backlight_use_settings();
589
590 return usb? PLUGIN_USB_CONNECTED: PLUGIN_OK;
591}