A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 591 lines 17 kB view raw
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}