A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 602 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 the implementation of a maze generation algorithm. 24 * The generated mazes are "perfect", i.e. there is one and only 25 * one path from any point in the maze to any other point. 26 * 27 * 28 * The implemented algorithm is called "Depth-First search", the 29 * solving is done by a dead-end filler routine. 30 * 31 */ 32 33#include "plugin.h" 34#include "lib/helper.h" 35 36 37 38/* key assignments */ 39 40#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \ 41 || (CONFIG_KEYPAD == IPOD_3G_PAD) \ 42 || (CONFIG_KEYPAD == IPOD_4G_PAD) 43# define MAZE_NEW (BUTTON_SELECT | BUTTON_REPEAT) 44# define MAZE_NEW_PRE BUTTON_SELECT 45# define MAZE_QUIT BUTTON_MENU 46# define MAZE_SOLVE (BUTTON_SELECT | BUTTON_REL) 47# define MAZE_SOLVE_PRE BUTTON_SELECT 48# define MAZE_RIGHT BUTTON_RIGHT 49# define MAZE_RIGHT_REPEAT BUTTON_RIGHT|BUTTON_REPEAT 50# define MAZE_LEFT BUTTON_LEFT 51# define MAZE_LEFT_REPEAT BUTTON_LEFT|BUTTON_REPEAT 52# define MAZE_UP BUTTON_SCROLL_BACK 53# define MAZE_UP_REPEAT BUTTON_SCROLL_BACK|BUTTON_REPEAT 54# define MAZE_DOWN BUTTON_SCROLL_FWD 55# define MAZE_DOWN_REPEAT BUTTON_SCROLL_FWD|BUTTON_REPEAT 56 57#else 58# include "lib/pluginlib_actions.h" 59# define MAZE_NEW PLA_SELECT_REPEAT 60# define MAZE_QUIT PLA_CANCEL 61# define MAZE_SOLVE PLA_SELECT_REL 62# define MAZE_RIGHT PLA_RIGHT 63# define MAZE_RIGHT_REPEAT PLA_RIGHT_REPEAT 64# define MAZE_LEFT PLA_LEFT 65# define MAZE_LEFT_REPEAT PLA_LEFT_REPEAT 66# define MAZE_UP PLA_UP 67# define MAZE_UP_REPEAT PLA_UP_REPEAT 68# define MAZE_DOWN PLA_DOWN 69# define MAZE_DOWN_REPEAT PLA_DOWN_REPEAT 70static const struct button_mapping *plugin_contexts[] 71= {pla_main_ctx}; 72 73#endif 74 75/* cell property bits */ 76#define WALL_N 0x0001 77#define WALL_E 0x0002 78#define WALL_S 0x0004 79#define WALL_W 0x0008 80#define WALL_ALL (WALL_N | WALL_E | WALL_S | WALL_W) 81#define PATH 0x0010 82 83/* border tests */ 84#define BORDER_N(y) ((y) == 0) 85#define BORDER_E(x) ((x) == MAZE_WIDTH-1) 86#define BORDER_S(y) ((y) == MAZE_HEIGHT-1) 87#define BORDER_W(x) ((x) == 0) 88 89// we can and should change this to make square boxes 90#if ( LCD_WIDTH == 112 ) 91#define MAZE_WIDTH 16 92#define MAZE_HEIGHT 12 93#elif( LCD_WIDTH == 132 ) 94#define MAZE_WIDTH 26 95#define MAZE_HEIGHT 16 96#else 97#define MAZE_WIDTH 32 98#define MAZE_HEIGHT 24 99#endif 100 101struct maze 102{ 103 int show_path; 104 int solved; 105 int player_x; 106 int player_y; 107 uint8_t maze[MAZE_WIDTH][MAZE_HEIGHT]; 108}; 109 110static void maze_init(struct maze* maze) 111{ 112 int x, y; 113 114 /* initialize the properties */ 115 maze->show_path = false; 116 maze->solved = false; 117 maze->player_x = 0; 118 maze->player_y = 0; 119 120 /* all walls are up */ 121 for(y=0; y<MAZE_HEIGHT; y++){ 122 for(x=0; x<MAZE_WIDTH; x++){ 123 maze->maze[x][y] = WALL_ALL; 124 } 125 } 126} 127 128static void maze_draw(struct maze* maze, struct screen* display) 129{ 130 int x, y; 131 int wx, wy; 132 int point_width, point_height, point_offset_x, point_offset_y; 133 uint8_t cell; 134 135 /* calculate the size variables */ 136 137 wx = (int) display->lcdwidth / MAZE_WIDTH; 138 wy = (int) display->lcdheight / MAZE_HEIGHT; 139 140 if(wx>3){ 141 point_width=wx-3; 142 point_offset_x=2; 143 }else{ 144 point_width=1; 145 point_offset_x=1; 146 } 147 148 if(wy>3){ 149 point_height=wy-3; 150 point_offset_y=2; 151 }else{ 152 point_height=1; 153 point_offset_y=1; 154 } 155 156 /* start drawing */ 157 158 display->clear_display(); 159 160 /* draw the walls */ 161 for(y=0; y<MAZE_HEIGHT; y++){ 162 for(x=0; x<MAZE_WIDTH; x++){ 163 cell = maze->maze[x][y]; 164 if(cell & WALL_N) 165 display->hline(x*wx, x*wx+wx, y*wy); 166 if(cell & WALL_E) 167 display->vline(x*wx+wx, y*wy, y*wy+wy); 168 if(cell & WALL_S) 169 display->hline(x*wx, x*wx+wx, y*wy+wy); 170 if(cell & WALL_W) 171 display->vline(x*wx, y*wy, y*wy+wy); 172 } 173 } 174 175 /* draw the path */ 176 if(maze->show_path){ 177#if LCD_DEPTH >= 16 178 if(display->depth>=16) 179 display->set_foreground(LCD_RGBPACK(127,127,127)); 180#endif 181#if LCD_DEPTH >= 2 182 if(display->depth==2) 183 display->set_foreground(1); 184#endif 185 186 /* highlight the path */ 187 for(y=0; y<MAZE_HEIGHT; y++){ 188 for(x=0; x<MAZE_WIDTH; x++){ 189 cell = maze->maze[x][y]; 190 if(cell & PATH) 191 display->fillrect(x*wx+point_offset_x, 192 y*wy+point_offset_y, 193 point_width, point_height); 194 } 195 } 196 197 /* link the cells in the path together */ 198 for(y=0; y<MAZE_HEIGHT; y++){ 199 for(x=0; x<MAZE_WIDTH; x++){ 200 cell = maze->maze[x][y]; 201 if(cell & PATH){ 202 if(!(cell & WALL_N) && (maze->maze[x][y-1] & PATH)) 203 display->fillrect(x*wx+point_offset_x, 204 y*wy, 205 point_width, wy-point_height); 206 if(!(cell & WALL_E) && (maze->maze[x+1][y] & PATH)) 207 display->fillrect(x*wx+wx-point_offset_x, 208 y*wy+point_offset_y, 209 wx-point_width, point_height); 210 if(!(cell & WALL_S) && (maze->maze[x][y+1] & PATH)) 211 display->fillrect(x*wx+point_offset_x, 212 y*wy+wy-point_offset_y, 213 point_width, wy-point_height); 214 if(!(cell & WALL_W) && (maze->maze[x-1][y] & PATH)) 215 display->fillrect(x*wx, 216 y*wy+point_offset_y, 217 wx-point_width, point_height); 218 } 219 } 220 } 221 222#if LCD_DEPTH >= 16 223 if(display->depth>=16) 224 display->set_foreground(LCD_RGBPACK(0,0,0)); 225#endif 226#if LCD_DEPTH >= 2 227 if(display->depth==2) 228 display->set_foreground(0); 229#endif 230 } 231 232 /* mark start and end */ 233 display->drawline(0, 0, wx, wy); 234 display->drawline(0, wy, wx, 0); 235 display->drawline((MAZE_WIDTH-1)*wx,(MAZE_HEIGHT-1)*wy, 236 (MAZE_WIDTH-1)*wx+wx, (MAZE_HEIGHT-1)*wy+wy); 237 display->drawline((MAZE_WIDTH-1)*wx,(MAZE_HEIGHT-1)*wy+wy, 238 (MAZE_WIDTH-1)*wx+wx, (MAZE_HEIGHT-1)*wy); 239 240 /* draw current position */ 241 display->fillrect(maze->player_x*wx+point_offset_x, 242 maze->player_y*wy+point_offset_y, 243 point_width, point_height); 244 245 /* update the display */ 246 display->update(); 247} 248 249 250struct coord_stack 251{ 252 uint8_t x[MAZE_WIDTH*MAZE_HEIGHT]; 253 uint8_t y[MAZE_WIDTH*MAZE_HEIGHT]; 254 int stp; 255}; 256 257static void coord_stack_init(struct coord_stack* stack) 258{ 259 rb->memset(stack->x, 0, sizeof(stack->x)); 260 rb->memset(stack->y, 0, sizeof(stack->y)); 261 stack->stp = 0; 262} 263 264static void coord_stack_push(struct coord_stack* stack, int x, int y) 265{ 266 stack->x[stack->stp] = x; 267 stack->y[stack->stp] = y; 268 stack->stp++; 269} 270 271static void coord_stack_pop(struct coord_stack* stack, int* x, int* y) 272{ 273 stack->stp--; 274 *x = stack->x[stack->stp]; 275 *y = stack->y[stack->stp]; 276} 277 278static int maze_pick_random_neighbour_cell_with_walls(struct maze* maze, 279 int x, int y, int *pnx, int *pny) 280{ 281 int n, i; 282 int px[4], py[4]; 283 284 n = 0; 285 286 /* look for neighbours with all walls set up */ 287 288 if(!BORDER_N(y) && ((maze->maze[x][y-1] & WALL_ALL) == WALL_ALL)){ 289 px[n] = x; 290 py[n] = y-1; 291 n++; 292 } 293 294 if(!BORDER_E(x) && ((maze->maze[x+1][y] & WALL_ALL) == WALL_ALL)){ 295 px[n] = x+1; 296 py[n] = y; 297 n++; 298 } 299 300 if(!BORDER_S(y) && ((maze->maze[x][y+1] & WALL_ALL) == WALL_ALL)){ 301 px[n] = x; 302 py[n] = y+1; 303 n++; 304 } 305 306 if(!BORDER_W(x) && ((maze->maze[x-1][y] & WALL_ALL) == WALL_ALL)){ 307 px[n] = x-1; 308 py[n] = y; 309 n++; 310 } 311 312 /* then choose one */ 313 if (n > 0){ 314 i = rb->rand() % n; 315 *pnx = px[i]; 316 *pny = py[i]; 317 } 318 319 return n; 320} 321 322/* Removes the wall between the cell (x,y) and the cell (nx,ny) */ 323static void maze_remove_wall(struct maze* maze, int x, int y, int nx, int ny) 324{ 325 /* where is our neighbour? */ 326 327 /* north or south */ 328 if(x==nx){ 329 if(y<ny){ 330 /*south*/ 331 maze->maze[x][y] &= ~WALL_S; 332 maze->maze[nx][ny] &= ~WALL_N; 333 } else { 334 /*north*/ 335 maze->maze[x][y] &= ~WALL_N; 336 maze->maze[nx][ny] &= ~WALL_S; 337 } 338 } else { 339 /* east or west */ 340 if(y==ny){ 341 if(x<nx){ 342 /* east */ 343 maze->maze[x][y] &= ~WALL_E; 344 maze->maze[nx][ny] &= ~WALL_W; 345 } else { 346 /*west*/ 347 maze->maze[x][y] &= ~WALL_W; 348 maze->maze[nx][ny] &= ~WALL_E; 349 } 350 } 351 } 352} 353 354static void maze_generate(struct maze* maze) 355{ 356 int total_cells = MAZE_WIDTH * MAZE_HEIGHT; 357 int visited_cells; 358 int available_neighbours; 359 int x, y; 360 int nx = 0; 361 int ny = 0; 362 struct coord_stack done_cells; 363 364 coord_stack_init(&done_cells); 365 366 x = rb->rand()%MAZE_WIDTH; 367 y = rb->rand()%MAZE_HEIGHT; 368 369 visited_cells = 1; 370 while (visited_cells < total_cells){ 371 available_neighbours = 372 maze_pick_random_neighbour_cell_with_walls(maze, x, y, &nx, &ny); 373 if(available_neighbours == 0){ 374 /* pop from stack */ 375 coord_stack_pop(&done_cells, &x, &y); 376 } else { 377 /* remove the wall */ 378 maze_remove_wall(maze, x, y, nx, ny); 379 /* save our position on the stack */ 380 coord_stack_push(&done_cells, x, y); 381 /* move to the next cell */ 382 x=nx; 383 y=ny; 384 /* keep track of visited cells count */ 385 visited_cells++; 386 } 387 } 388} 389 390 391static void maze_solve(struct maze* maze) 392{ 393 int x, y; 394 int dead_ends = 1; 395 uint8_t cell; 396 uint8_t wall; 397 uint8_t solved_maze[MAZE_WIDTH][MAZE_HEIGHT]; 398 399 /* toggle the visibility of the path */ 400 maze->show_path = ~(maze->show_path); 401 402 /* no need to solve the maze if already solved */ 403 if (maze->solved) 404 return; 405 406 /* work on a copy of the maze */ 407 rb->memcpy(solved_maze, maze->maze, sizeof(maze->maze)); 408 409 /* remove walls on start and end point */ 410 solved_maze[0][0] &= ~WALL_N; 411 solved_maze[MAZE_WIDTH-1][MAZE_HEIGHT-1] &= ~WALL_S; 412 413 /* first, mark all the cells as reachable */ 414 for(y=0; y<MAZE_HEIGHT; y++){ 415 for(x=0; x<MAZE_WIDTH; x++){ 416 solved_maze[x][y] |= PATH; 417 } 418 } 419 420 /* start solving */ 421 while(dead_ends){ 422 /* solve by blocking off dead ends -- backward approach */ 423 dead_ends = 0; 424 /* scan for dead ends */ 425 for(y=0; y<MAZE_HEIGHT; y++){ 426 rb->yield(); 427 for(x=0; x<MAZE_WIDTH; x++){ 428 cell = solved_maze[x][y]; 429 wall = cell & WALL_ALL; 430 if((wall == (WALL_E | WALL_S | WALL_W)) || 431 (wall == (WALL_N | WALL_S | WALL_W)) || 432 (wall == (WALL_N | WALL_E | WALL_W)) || 433 (wall == (WALL_N | WALL_E | WALL_S))){ 434 /* found dead end, clear path bit and set all its walls */ 435 solved_maze[x][y] &= ~PATH; 436 solved_maze[x][y] |= WALL_ALL; 437 /* don't forget the neighbours */ 438 if(!BORDER_S(y)) 439 solved_maze[x][y+1] |= WALL_N; 440 if(!BORDER_W(x)) 441 solved_maze[x-1][y] |= WALL_E; 442 if(!BORDER_N(y)) 443 solved_maze[x][y-1] |= WALL_S; 444 if(!BORDER_E(x)) 445 solved_maze[x+1][y] |= WALL_W; 446 dead_ends++; 447 } 448 } 449 } 450 } 451 452 /* copy all the path bits to the maze */ 453 for(y=0; y<MAZE_HEIGHT; y++){ 454 for(x=0; x<MAZE_WIDTH; x++){ 455 maze->maze[x][y] |= solved_maze[x][y] & PATH; 456 } 457 } 458 459 /* mark the maze as solved */ 460 maze->solved = true; 461} 462 463static void maze_move_player_up(struct maze* maze) 464{ 465 uint8_t cell = maze->maze[maze->player_x][maze->player_y]; 466 if(!BORDER_N(maze->player_y) && !(cell & WALL_N)) 467 maze->player_y--; 468} 469 470static void maze_move_player_right(struct maze* maze) 471{ 472 uint8_t cell = maze->maze[maze->player_x][maze->player_y]; 473 if(!BORDER_E(maze->player_x) && !(cell & WALL_E)) 474 maze->player_x++; 475} 476 477static void maze_move_player_down(struct maze* maze) 478{ 479 uint8_t cell = maze->maze[maze->player_x][maze->player_y]; 480 if(!BORDER_S(maze->player_y) && !(cell & WALL_S)) 481 maze->player_y++; 482} 483 484static void maze_move_player_left(struct maze* maze) 485{ 486 uint8_t cell = maze->maze[maze->player_x][maze->player_y]; 487 if(!BORDER_W(maze->player_x) && !(cell & WALL_W)) 488 maze->player_x--; 489} 490 491/**********************************/ 492/* this is the plugin entry point */ 493/**********************************/ 494enum plugin_status plugin_start(const void* parameter) 495{ 496 int button; 497#if defined(MAZE_NEW_PRE) || defined(MAZE_SOLVE_PRE) 498 int lastbutton = BUTTON_NONE; 499#endif 500 int quit = 0; 501 struct maze maze; 502 (void)parameter; 503 504 /* Turn off backlight timeout */ 505 backlight_ignore_timeout(); 506 507 /* Seed the RNG */ 508 rb->srand(*rb->current_tick); 509 510 FOR_NB_SCREENS(i) 511 rb->screens[i]->set_viewport(NULL); 512 513 /* Draw the background */ 514#if LCD_DEPTH > 1 515 rb->lcd_set_backdrop(NULL); 516#if LCD_DEPTH >= 16 517 rb->lcd_set_foreground(LCD_RGBPACK( 0, 0, 0)); 518 rb->lcd_set_background(LCD_RGBPACK(182, 198, 229)); /* rockbox blue */ 519#elif LCD_DEPTH == 2 520 rb->lcd_set_foreground(0); 521 rb->lcd_set_background(LCD_DEFAULT_BG); 522#endif 523#endif 524 525 /* Initialize and draw the maze */ 526 maze_init(&maze); 527 maze_generate(&maze); 528 FOR_NB_SCREENS(i) 529 maze_draw(&maze, rb->screens[i]); 530 531 while(!quit) { 532#ifdef __PLUGINLIB_ACTIONS_H__ 533 button = pluginlib_getaction(TIMEOUT_BLOCK, plugin_contexts, 534 ARRAYLEN(plugin_contexts)); 535#else 536 button = rb->button_get(true); 537#endif 538 switch(button) { 539 case MAZE_NEW: 540#ifdef MAZE_NEW_PRE 541 if(lastbutton != MAZE_NEW_PRE) 542 break; 543#endif 544 maze_init(&maze); 545 maze_generate(&maze); 546 FOR_NB_SCREENS(i) 547 maze_draw(&maze, rb->screens[i]); 548 break; 549 case MAZE_SOLVE: 550#ifdef MAZE_SOLVE_PRE 551 if(lastbutton != MAZE_SOLVE_PRE) 552 break; 553#endif 554 maze_solve(&maze); 555 FOR_NB_SCREENS(i) 556 maze_draw(&maze, rb->screens[i]); 557 break; 558 case MAZE_UP: 559 case MAZE_UP_REPEAT: 560 maze_move_player_up(&maze); 561 FOR_NB_SCREENS(i) 562 maze_draw(&maze, rb->screens[i]); 563 break; 564 case MAZE_RIGHT: 565 case MAZE_RIGHT_REPEAT: 566 maze_move_player_right(&maze); 567 FOR_NB_SCREENS(i) 568 maze_draw(&maze, rb->screens[i]); 569 break; 570 case MAZE_DOWN: 571 case MAZE_DOWN_REPEAT: 572 maze_move_player_down(&maze); 573 FOR_NB_SCREENS(i) 574 maze_draw(&maze, rb->screens[i]); 575 break; 576 case MAZE_LEFT: 577 case MAZE_LEFT_REPEAT: 578 maze_move_player_left(&maze); 579 FOR_NB_SCREENS(i) 580 maze_draw(&maze, rb->screens[i]); 581 break; 582 case MAZE_QUIT: 583 /* quit plugin */ 584 quit=1; 585 break; 586 default: 587 if (rb->default_event_handler(button) == SYS_USB_CONNECTED) { 588 /* quit plugin */ 589 quit=2; 590 } 591 break; 592 } 593#if defined(MAZE_NEW_PRE) || defined(MAZE_SOLVE_PRE) 594 if( button != BUTTON_NONE ) 595 lastbutton = button; 596#endif 597 } 598 /* Turn on backlight timeout (revert to settings) */ 599 backlight_use_settings(); 600 601 return ((quit == 1) ? PLUGIN_OK : PLUGIN_USB_CONNECTED); 602}