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 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}