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) 2008-2009 Rene Peinthor
11 * Contribution from Johannes Schwarz (new menu system, use of highscore lib)
12 *
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20#include "plugin.h"
21#include "lib/highscore.h"
22#include "lib/playback_control.h"
23#include "lib/display_text.h"
24
25
26
27#if (CONFIG_KEYPAD == SANSA_E200_PAD) || \
28 (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
29#define CLIX_BUTTON_QUIT BUTTON_POWER
30#define CLIX_BUTTON_UP BUTTON_UP
31#define CLIX_BUTTON_DOWN BUTTON_DOWN
32#define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
33#define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
34#define CLIX_BUTTON_LEFT BUTTON_LEFT
35#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
36#define CLIX_BUTTON_CLICK BUTTON_SELECT
37
38#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
39#define CLIX_BUTTON_QUIT BUTTON_POWER
40#define CLIX_BUTTON_UP BUTTON_UP
41#define CLIX_BUTTON_DOWN BUTTON_DOWN
42#define CLIX_BUTTON_LEFT BUTTON_LEFT
43#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
44#define CLIX_BUTTON_CLICK BUTTON_SELECT
45
46#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
47#define CLIX_BUTTON_QUIT BUTTON_HOME
48#define CLIX_BUTTON_UP BUTTON_UP
49#define CLIX_BUTTON_DOWN BUTTON_DOWN
50#define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
51#define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
52#define CLIX_BUTTON_LEFT BUTTON_LEFT
53#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
54#define CLIX_BUTTON_CLICK BUTTON_SELECT
55
56#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
57#define CLIX_BUTTON_QUIT BUTTON_POWER
58#define CLIX_BUTTON_UP BUTTON_UP
59#define CLIX_BUTTON_DOWN BUTTON_DOWN
60#define CLIX_BUTTON_SCROLL_FWD BUTTON_VOL_UP
61#define CLIX_BUTTON_SCROLL_BACK BUTTON_VOL_DOWN
62#define CLIX_BUTTON_LEFT BUTTON_LEFT
63#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
64#define CLIX_BUTTON_CLICK BUTTON_SELECT
65
66#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
67 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
68 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
69#define CLIX_BUTTON_QUIT BUTTON_MENU
70#define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
71#define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
72#define CLIX_BUTTON_CLICK BUTTON_SELECT
73#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
74#define CLIX_BUTTON_LEFT BUTTON_LEFT
75
76#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
77#define CLIX_BUTTON_QUIT BUTTON_POWER
78#define CLIX_BUTTON_LEFT BUTTON_LEFT
79#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
80#define CLIX_BUTTON_CLICK BUTTON_SELECT
81#define CLIX_BUTTON_UP BUTTON_UP
82#define CLIX_BUTTON_DOWN BUTTON_DOWN
83
84#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
85 (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
86#define CLIX_BUTTON_QUIT BUTTON_BACK
87#define CLIX_BUTTON_LEFT BUTTON_LEFT
88#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
89#define CLIX_BUTTON_CLICK BUTTON_SELECT
90#define CLIX_BUTTON_UP BUTTON_UP
91#define CLIX_BUTTON_DOWN BUTTON_DOWN
92
93#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
94#define CLIX_BUTTON_QUIT BUTTON_POWER
95#define CLIX_BUTTON_LEFT BUTTON_LEFT
96#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
97#define CLIX_BUTTON_CLICK BUTTON_PLAY
98#define CLIX_BUTTON_UP BUTTON_SCROLL_UP
99#define CLIX_BUTTON_DOWN BUTTON_SCROLL_DOWN
100
101#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
102#define CLIX_BUTTON_QUIT BUTTON_POWER
103#define CLIX_BUTTON_LEFT BUTTON_LEFT
104#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
105#define CLIX_BUTTON_CLICK BUTTON_SELECT
106#define CLIX_BUTTON_UP BUTTON_UP
107#define CLIX_BUTTON_DOWN BUTTON_DOWN
108
109#elif (CONFIG_KEYPAD == IRIVER_H300_PAD)
110#define CLIX_BUTTON_QUIT BUTTON_OFF
111#define CLIX_BUTTON_LEFT BUTTON_LEFT
112#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
113#define CLIX_BUTTON_CLICK BUTTON_SELECT
114#define CLIX_BUTTON_UP BUTTON_UP
115#define CLIX_BUTTON_DOWN BUTTON_DOWN
116
117#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
118#define CLIX_BUTTON_QUIT BUTTON_BACK
119#define CLIX_BUTTON_LEFT BUTTON_LEFT
120#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
121#define CLIX_BUTTON_CLICK BUTTON_SELECT
122#define CLIX_BUTTON_UP BUTTON_UP
123#define CLIX_BUTTON_DOWN BUTTON_DOWN
124
125#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
126#define CLIX_BUTTON_QUIT BUTTON_POWER
127#define CLIX_BUTTON_LEFT BUTTON_BACK
128#define CLIX_BUTTON_RIGHT BUTTON_MENU
129#define CLIX_BUTTON_CLICK BUTTON_PLAY
130#define CLIX_BUTTON_UP BUTTON_UP
131#define CLIX_BUTTON_DOWN BUTTON_DOWN
132
133#elif (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD)
134#define CLIX_BUTTON_QUIT BUTTON_POWER
135#define CLIX_BUTTON_LEFT BUTTON_LEFT
136#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
137#define CLIX_BUTTON_CLICK BUTTON_SELECT
138#define CLIX_BUTTON_UP BUTTON_UP
139#define CLIX_BUTTON_DOWN BUTTON_DOWN
140
141#elif (CONFIG_KEYPAD == PHILIPS_HDD6330_PAD)
142#define CLIX_BUTTON_QUIT BUTTON_POWER
143#define CLIX_BUTTON_LEFT BUTTON_PREV
144#define CLIX_BUTTON_RIGHT BUTTON_NEXT
145#define CLIX_BUTTON_CLICK BUTTON_PLAY
146#define CLIX_BUTTON_UP BUTTON_UP
147#define CLIX_BUTTON_DOWN BUTTON_DOWN
148
149#elif (CONFIG_KEYPAD == PHILIPS_SA9200_PAD)
150#define CLIX_BUTTON_QUIT BUTTON_POWER
151#define CLIX_BUTTON_LEFT BUTTON_PREV
152#define CLIX_BUTTON_RIGHT BUTTON_NEXT
153#define CLIX_BUTTON_CLICK BUTTON_PLAY
154#define CLIX_BUTTON_UP BUTTON_UP
155#define CLIX_BUTTON_DOWN BUTTON_DOWN
156
157#elif CONFIG_KEYPAD == COWON_D2_PAD
158#define CLIX_BUTTON_QUIT BUTTON_POWER
159
160#elif (CONFIG_KEYPAD == ONDAVX747_PAD)
161#define CLIX_BUTTON_QUIT BUTTON_POWER
162#define CLIX_BUTTON_CLICK BUTTON_MENU
163
164#elif (CONFIG_KEYPAD == ONDAVX777_PAD)
165#define CLIX_BUTTON_QUIT BUTTON_POWER
166
167#elif (CONFIG_KEYPAD == ANDROID_PAD) \
168 || (CONFIG_KEYPAD == SDL_PAD)
169#define CLIX_BUTTON_QUIT BUTTON_BACK
170
171#elif (CONFIG_KEYPAD == MROBE500_PAD)
172#define CLIX_BUTTON_QUIT BUTTON_POWER
173
174#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
175 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
176#define CLIX_BUTTON_QUIT BUTTON_REW
177#define CLIX_BUTTON_LEFT BUTTON_LEFT
178#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
179#define CLIX_BUTTON_CLICK BUTTON_PLAY
180#define CLIX_BUTTON_UP BUTTON_UP
181#define CLIX_BUTTON_DOWN BUTTON_DOWN
182
183#elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
184#define CLIX_BUTTON_QUIT BUTTON_REC
185#define CLIX_BUTTON_UP BUTTON_UP
186#define CLIX_BUTTON_DOWN BUTTON_DOWN
187#define CLIX_BUTTON_SCROLL_FWD BUTTON_PLAY
188#define CLIX_BUTTON_SCROLL_BACK BUTTON_MENU
189#define CLIX_BUTTON_LEFT BUTTON_PREV
190#define CLIX_BUTTON_RIGHT BUTTON_NEXT
191#define CLIX_BUTTON_CLICK BUTTON_OK
192
193#elif (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
194#define CLIX_BUTTON_QUIT BUTTON_POWER
195#define CLIX_BUTTON_UP BUTTON_UP
196#define CLIX_BUTTON_DOWN BUTTON_DOWN
197#define CLIX_BUTTON_LEFT BUTTON_LEFT
198#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
199#define CLIX_BUTTON_SCROLL_FWD BUTTON_BOTTOMRIGHT
200#define CLIX_BUTTON_SCROLL_BACK BUTTON_BOTTOMLEFT
201#define CLIX_BUTTON_CLICK BUTTON_SELECT
202
203#elif (CONFIG_KEYPAD == HM60X_PAD)
204#define CLIX_BUTTON_QUIT BUTTON_POWER
205#define CLIX_BUTTON_UP BUTTON_UP
206#define CLIX_BUTTON_DOWN BUTTON_DOWN
207#define CLIX_BUTTON_SCROLL_FWD (BUTTON_POWER | BUTTON_UP)
208#define CLIX_BUTTON_SCROLL_BACK (BUTTON_POWER | BUTTON_DOWN)
209#define CLIX_BUTTON_LEFT BUTTON_LEFT
210#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
211#define CLIX_BUTTON_CLICK BUTTON_SELECT
212
213#elif (CONFIG_KEYPAD == HM801_PAD)
214#define CLIX_BUTTON_QUIT BUTTON_POWER
215#define CLIX_BUTTON_UP BUTTON_UP
216#define CLIX_BUTTON_DOWN BUTTON_DOWN
217#define CLIX_BUTTON_SCROLL_FWD BUTTON_NEXT
218#define CLIX_BUTTON_SCROLL_BACK BUTTON_PREV
219#define CLIX_BUTTON_LEFT BUTTON_LEFT
220#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
221#define CLIX_BUTTON_CLICK BUTTON_SELECT
222
223#elif CONFIG_KEYPAD == SONY_NWZ_PAD
224#define CLIX_BUTTON_QUIT BUTTON_BACK
225#define CLIX_BUTTON_UP BUTTON_UP
226#define CLIX_BUTTON_DOWN BUTTON_DOWN
227#define CLIX_BUTTON_SCROLL_FWD (BUTTON_POWER|BUTTON_RIGHT)
228#define CLIX_BUTTON_SCROLL_BACK (BUTTON_POWER|BUTTON_LEFT)
229#define CLIX_BUTTON_LEFT BUTTON_LEFT
230#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
231#define CLIX_BUTTON_CLICK BUTTON_PLAY
232
233#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
234#define CLIX_BUTTON_QUIT BUTTON_BACK
235#define CLIX_BUTTON_UP BUTTON_UP
236#define CLIX_BUTTON_DOWN BUTTON_DOWN
237#define CLIX_BUTTON_SCROLL_FWD BUTTON_PLAYPAUSE
238#define CLIX_BUTTON_SCROLL_BACK BUTTON_MENU
239#define CLIX_BUTTON_LEFT BUTTON_LEFT
240#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
241#define CLIX_BUTTON_CLICK BUTTON_SELECT
242
243#elif (CONFIG_KEYPAD == SAMSUNG_YPR1_PAD)
244#define CLIX_BUTTON_QUIT BUTTON_POWER
245
246#elif (CONFIG_KEYPAD == DX50_PAD)
247#define CLIX_BUTTON_QUIT BUTTON_POWER
248
249#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
250#define CLIX_BUTTON_QUIT BUTTON_POWER
251#define CLIX_BUTTON_UP BUTTON_TOPMIDDLE
252#define CLIX_BUTTON_DOWN BUTTON_BOTTOMMIDDLE
253#define CLIX_BUTTON_SCROLL_FWD BUTTON_TOPRIGHT
254#define CLIX_BUTTON_SCROLL_BACK BUTTON_TOPLEFT
255#define CLIX_BUTTON_LEFT BUTTON_MIDLEFT
256#define CLIX_BUTTON_RIGHT BUTTON_MIDRIGHT
257#define CLIX_BUTTON_CLICK BUTTON_CENTER
258
259#elif (CONFIG_KEYPAD == AGPTEK_ROCKER_PAD)
260#define CLIX_BUTTON_QUIT BUTTON_POWER
261#define CLIX_BUTTON_UP BUTTON_UP
262#define CLIX_BUTTON_DOWN BUTTON_DOWN
263#define CLIX_BUTTON_SCROLL_FWD (BUTTON_VOLUP)
264#define CLIX_BUTTON_SCROLL_BACK (BUTTON_VOLDOWN)
265#define CLIX_BUTTON_LEFT BUTTON_LEFT
266#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
267#define CLIX_BUTTON_CLICK BUTTON_SELECT
268
269#elif CONFIG_KEYPAD == XDUOO_X3_PAD
270#define CLIX_BUTTON_QUIT BUTTON_POWER
271#define CLIX_BUTTON_UP BUTTON_HOME
272#define CLIX_BUTTON_DOWN BUTTON_OPTION
273#define CLIX_BUTTON_LEFT BUTTON_PREV
274#define CLIX_BUTTON_RIGHT BUTTON_NEXT
275#define CLIX_BUTTON_CLICK BUTTON_PLAY
276
277#elif CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD
278#define CLIX_BUTTON_QUIT BUTTON_POWER
279#define CLIX_BUTTON_UP BUTTON_HOME
280#define CLIX_BUTTON_DOWN BUTTON_OPTION
281#define CLIX_BUTTON_LEFT BUTTON_PREV
282#define CLIX_BUTTON_RIGHT BUTTON_NEXT
283#define CLIX_BUTTON_CLICK BUTTON_PLAY
284
285#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD
286#define CLIX_BUTTON_QUIT BUTTON_POWER
287#define CLIX_BUTTON_UP BUTTON_HOME
288#define CLIX_BUTTON_DOWN BUTTON_OPTION
289#define CLIX_BUTTON_LEFT BUTTON_PREV
290#define CLIX_BUTTON_RIGHT BUTTON_NEXT
291#define CLIX_BUTTON_CLICK BUTTON_PLAY
292
293#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD
294#define CLIX_BUTTON_QUIT BUTTON_POWER
295#define CLIX_BUTTON_UP BUTTON_PREV
296#define CLIX_BUTTON_DOWN BUTTON_NEXT
297#define CLIX_BUTTON_LEFT BUTTON_HOME
298#define CLIX_BUTTON_RIGHT BUTTON_VOL_DOWN
299#define CLIX_BUTTON_CLICK BUTTON_VOL_UP
300
301#elif CONFIG_KEYPAD == EROSQ_PAD
302#define CLIX_BUTTON_QUIT BUTTON_POWER
303#define CLIX_BUTTON_UP BUTTON_PREV
304#define CLIX_BUTTON_DOWN BUTTON_NEXT
305#define CLIX_BUTTON_LEFT BUTTON_SCROLL_BACK
306#define CLIX_BUTTON_RIGHT BUTTON_SCROLL_FWD
307#define CLIX_BUTTON_CLICK BUTTON_PLAY
308
309#elif CONFIG_KEYPAD == FIIO_M3K_PAD
310#define CLIX_BUTTON_QUIT BUTTON_POWER
311#define CLIX_BUTTON_UP BUTTON_UP
312#define CLIX_BUTTON_DOWN BUTTON_DOWN
313#define CLIX_BUTTON_LEFT BUTTON_LEFT
314#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
315#define CLIX_BUTTON_CLICK BUTTON_SELECT
316
317#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
318#define CLIX_BUTTON_QUIT BUTTON_POWER
319
320#elif CONFIG_KEYPAD == MA_PAD
321#define CLIX_BUTTON_QUIT BUTTON_BACK
322#define CLIX_BUTTON_UP BUTTON_UP
323#define CLIX_BUTTON_DOWN BUTTON_DOWN
324#define CLIX_BUTTON_LEFT BUTTON_LEFT
325#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
326#define CLIX_BUTTON_CLICK BUTTON_PLAY
327
328#elif (CONFIG_KEYPAD == RG_NANO_PAD)
329#define CLIX_BUTTON_QUIT BUTTON_START
330#define CLIX_BUTTON_LEFT BUTTON_LEFT
331#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
332#define CLIX_BUTTON_CLICK BUTTON_A
333#define CLIX_BUTTON_UP BUTTON_UP
334#define CLIX_BUTTON_DOWN BUTTON_DOWN
335
336#else
337#error "no keymap"
338#endif
339
340#ifndef CLIX_BUTTON_CLICK
341#define CLIX_BUTTON_CLICK BUTTON_CENTER
342#endif
343
344#define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/clix.score"
345#define NUM_SCORES 5
346struct highscore highscores[NUM_SCORES];
347
348#define NUM_LEVELS 9
349#define BLINK_TICKCOUNT 25
350#define MARGIN 5
351
352#if (LCD_WIDTH >= LCD_HEIGHT)
353#define BOARD_WIDTH 18
354#define BOARD_HEIGHT 12
355#else
356#define BOARD_WIDTH 12
357#define BOARD_HEIGHT 18
358#endif
359
360#if (LCD_WIDTH>=480)
361#if (LCD_WIDTH/BOARD_WIDTH) > (LCD_HEIGHT/BOARD_HEIGHT)
362#define CELL_SIZE (LCD_HEIGHT/BOARD_HEIGHT)
363#else
364#define CELL_SIZE (LCD_WIDTH/BOARD_WIDTH)
365#endif
366
367#elif (LCD_WIDTH >= 312 && LCD_HEIGHT >= 468)
368#define CELL_SIZE 24
369
370#elif (LCD_WIDTH >= 306 && LCD_HEIGHT>= 204)
371#define CELL_SIZE 16
372
373#elif (LCD_WIDTH >= 270 && LCD_HEIGHT>= 180)
374#define CELL_SIZE 14
375
376#elif (LCD_WIDTH >= 234 && LCD_HEIGHT>= 156)
377#define CELL_SIZE 12
378
379#elif (LCD_WIDTH >= 198 && LCD_HEIGHT>= 132)
380#define CELL_SIZE 10
381
382#elif (LCD_WIDTH >= 162 && LCD_HEIGHT>= 108)
383#define CELL_SIZE 8
384
385#elif (LCD_WIDTH >= 126 && LCD_HEIGHT>= 84)
386#define CELL_SIZE 6
387
388#elif (LCD_WIDTH >= 60)
389#define CELL_SIZE 4
390#endif
391
392#define XYPOS(x,y) ((y) * BOARD_WIDTH + x)
393#define XOFS LCD_WIDTH/2-(BOARD_WIDTH * (CELL_SIZE + 1)/2)
394#define YOFS (LCD_HEIGHT+10)/2-(BOARD_HEIGHT * (CELL_SIZE + 1)/2)
395
396
397struct clix_game_state_t {
398 int level; /* current level */
399 int score; /* current game score */
400 int x,y; /* current positions of the cursor */
401 int board[BOARD_WIDTH * BOARD_HEIGHT]; /* play board*/
402 /* state of selected fields,maybe we can store this in the play board too */
403 bool board_selected[ BOARD_WIDTH * BOARD_HEIGHT];
404 int selected_count;
405 int status;
406 bool blink; /* true if selected CELLS are currently white */
407};
408
409/* game state enum */
410enum {
411 CLIX_GAMEOVER = -1,
412 CLIX_CONTINUE,
413 CLIX_CLEARED
414};
415
416/* cell color enum */
417enum {
418 CC_BLACK = -1,
419 CC_BLUE,
420 CC_GREEN,
421 CC_RED,
422 CC_YELLOW,
423 CC_ORANGE,
424 CC_CYAN,
425 CC_BROWN,
426 CC_PINK,
427 CC_DARK_BLUE,
428 CC_DARK_GREEN
429};
430
431/* recursive function to check if a neighbour cell is of the same color
432 if so call the function with the neighbours position
433*/
434static void clix_set_selected(struct clix_game_state_t* state,
435 const int x, const int y)
436{
437 state->selected_count++;
438 state->board_selected[ XYPOS( x, y)] = true;
439 int current_color = state->board[ XYPOS( x, y)];
440
441 if( (x - 1) >= 0 &&
442 state->board[ XYPOS( x - 1, y)] == current_color &&
443 state->board_selected[ XYPOS(x - 1, y)] == false)
444 clix_set_selected( state, x - 1, y);
445
446 if( (y + 1) < BOARD_HEIGHT &&
447 state->board[ XYPOS( x, y + 1)] == current_color &&
448 state->board_selected[ XYPOS(x, y + 1)] == false)
449 clix_set_selected( state, x, y + 1);
450
451 if( (x + 1) < BOARD_WIDTH &&
452 state->board[ XYPOS( x + 1, y)] == current_color &&
453 state->board_selected[ XYPOS(x + 1, y)] == false)
454 clix_set_selected( state, x + 1, y);
455
456 if( (y - 1) >= 0 &&
457 state->board[ XYPOS( x, y - 1)] == current_color &&
458 state->board_selected[ XYPOS(x, y - 1)] == false)
459 clix_set_selected( state, x, y - 1);
460}
461
462/* updates "blinking" cells by finding out which one is a valid neighbours */
463static void clix_update_selected(struct clix_game_state_t* state)
464{
465 int i;
466
467 for( i = 0; i < BOARD_WIDTH * BOARD_HEIGHT; ++i)
468 {
469 state->board_selected[i] = false;
470 }
471 state->selected_count = 0;
472
473 /* recursion starts here */
474 clix_set_selected( state, state->x, state->y);
475}
476
477/* inits the board with new random colors according to the level */
478static void clix_init_new_level(struct clix_game_state_t* state)
479{
480 int i;
481 int r;
482
483 state->y = BOARD_HEIGHT / 2;
484 state->x = BOARD_WIDTH / 2;
485
486 rb->srand( *rb->current_tick);
487 /* create a random colored board, according to the current level */
488 for(i = 0; i < BOARD_HEIGHT * BOARD_WIDTH; ++i)
489 {
490 r = rb->rand() % (state->level + 1);
491 state->board[i] = r;
492 }
493}
494
495/* this inits the game state structure */
496static void clix_init(struct clix_game_state_t* state)
497{
498 state->level = 1;
499 state->score = 0;
500 state->blink = false;
501 state->status = CLIX_CONTINUE;
502
503 clix_init_new_level(state);
504
505 clix_update_selected(state);
506}
507
508/* Function for drawing a cell */
509static void clix_draw_cell(struct clix_game_state_t* state, const int x, const int y)
510{
511 int realx = XOFS;
512 int realy = YOFS;
513
514 realx += x * (CELL_SIZE + 1);
515 realy += y * (CELL_SIZE + 1);
516
517 if (state->blink && state->board_selected[ XYPOS( x, y)]) {
518 rb->lcd_set_foreground(LCD_WHITE);
519 } else {
520 switch (state->board[ XYPOS( x, y)])
521 {
522 case CC_BLUE:
523 rb->lcd_set_foreground( LCD_RGBPACK( 25, 25, 255));
524 break;
525 case CC_GREEN:
526 rb->lcd_set_foreground( LCD_RGBPACK( 25, 255, 25));
527 break;
528 case CC_RED:
529 rb->lcd_set_foreground( LCD_RGBPACK( 255, 25, 25));
530 break;
531 case CC_YELLOW:
532 rb->lcd_set_foreground( LCD_RGBPACK( 225, 225, 25));
533 break;
534 case CC_ORANGE:
535 rb->lcd_set_foreground( LCD_RGBPACK( 230, 140, 15));
536 break;
537 case CC_CYAN:
538 rb->lcd_set_foreground( LCD_RGBPACK( 25, 245, 230));
539 break;
540 case CC_BROWN:
541 rb->lcd_set_foreground( LCD_RGBPACK(139, 69, 19));
542 break;
543 case CC_PINK:
544 rb->lcd_set_foreground( LCD_RGBPACK(255, 105, 180));
545 break;
546 case CC_DARK_GREEN:
547 rb->lcd_set_foreground( LCD_RGBPACK( 0, 100, 0));
548 break;
549 case CC_DARK_BLUE:
550 rb->lcd_set_foreground( LCD_RGBPACK( 280, 32, 144));
551 break;
552 default:
553 rb->lcd_set_foreground( LCD_BLACK);
554 break;
555 }
556 }
557
558 rb->lcd_fillrect( realx, realy, CELL_SIZE, CELL_SIZE);
559
560 /* draw cursor */
561 if ( x == state->x && y == state->y) {
562 rb->lcd_set_foreground( LCD_WHITE);
563 rb->lcd_drawrect( realx - 1, realy - 1, CELL_SIZE + 2, CELL_SIZE + 2);
564 }
565}
566
567/* main function of drawing the whole board and score... */
568static void clix_draw(struct clix_game_state_t* state)
569{
570 int i,j;
571
572 /* Clear screen */
573 rb->lcd_clear_display();
574 rb->lcd_set_foreground( LCD_WHITE);
575
576 rb->lcd_putsxy( MARGIN, MARGIN, "Score:");
577 rb->lcd_putsxyf( 43, MARGIN, "%d", state->score);
578#if LCD_WIDTH <= 100
579 rb->lcd_putsxy( 75, MARGIN, "L:");
580 rb->lcd_putsxyf( 90, MARGIN, "%d", state->level);
581#else
582 rb->lcd_putsxy( 75, MARGIN, "Level:");
583 rb->lcd_putsxyf( 113, MARGIN, "%d", state->level);
584#endif
585 for( i = 0; i < BOARD_WIDTH; ++i)
586 {
587 for( j = 0; j < BOARD_HEIGHT; ++j)
588 {
589 clix_draw_cell( state, i, j);
590 }
591 }
592
593 rb->lcd_update();
594 rb->lcd_set_foreground(LCD_WHITE);
595}
596
597static void clix_move_cursor(struct clix_game_state_t* state, const bool left)
598{
599 int x, y;
600
601 x = state->x;
602 do
603 {
604 y = state->y;
605 while(state->board[ XYPOS( x, y)] == CC_BLACK && y < BOARD_HEIGHT) y++;
606 if (y < BOARD_HEIGHT) {
607 state->y = y;
608 state->x = x;
609 }
610 else
611 {
612 if (left) {
613 if( x >= 0)
614 x--;
615 else
616 y = state->y;
617 }
618 else
619 {
620 if( x < BOARD_WIDTH - 1)
621 x++;
622 else
623 x = 0;
624 }
625 }
626 } while ( y != state->y);
627}
628
629/* returns the color of the given position, if out of bounds return CC_BLACK */
630static int clix_get_color(struct clix_game_state_t* state, const int x, const int y)
631{
632 if( x >= 0 && x < BOARD_WIDTH && y >= 0 && y < BOARD_HEIGHT)
633 return state->board[XYPOS( x, y)];
634 else
635 return CC_BLACK;
636}
637
638static int clix_clear_selected(struct clix_game_state_t* state)
639{
640 int i, j, x, y;
641 bool found_move;
642
643 state->status = CLIX_CLEARED;
644
645 /* clear the selected blocks */
646 for( i = 0; i < BOARD_WIDTH; ++i)
647 {
648 for( j = 0; j < BOARD_HEIGHT; ++j)
649 {
650 if( state->board_selected[ XYPOS( i, j)] )
651 {
652 state->board_selected[ XYPOS( i, j)] = false;
653 state->board[ XYPOS( i, j)] = CC_BLACK;
654 }
655 }
656 }
657
658 /* count score */
659 state->score += state->selected_count * state->level;
660 state->selected_count = 0;
661
662 /* let blocks falling down */
663 for( i = BOARD_WIDTH - 1; i >= 0; --i)
664 {
665 for( j = BOARD_HEIGHT - 1; j >= 0; --j)
666 {
667 y = j;
668 while( (y + 1) < BOARD_HEIGHT &&
669 state->board[ XYPOS( i, y + 1)] == CC_BLACK
670 )
671 y++;
672
673 if (y != j) {
674 state->board[ XYPOS( i, y)] = state->board[ XYPOS( i, j)];
675 state->board[ XYPOS( i, j)] = CC_BLACK;
676 }
677 }
678 }
679
680 /* move columns to left side */
681 for( i = 0; i < BOARD_WIDTH; ++i)
682 {
683 x = i;
684 while( (x - 1) >= 0 &&
685 state->board[ XYPOS( x - 1, BOARD_HEIGHT - 1)] == CC_BLACK
686 )
687 x--;
688 if (x != i)
689 {
690 for( j = 0; j < BOARD_HEIGHT; ++j)
691 {
692 state->board[ XYPOS( x, j)] = state->board[ XYPOS( i, j)];
693 state->board[ XYPOS( i, j)] = CC_BLACK;
694 }
695 }
696 }
697
698 if(state->board[ XYPOS( 0, BOARD_HEIGHT - 1)] != CC_BLACK)
699 state->status = CLIX_CONTINUE;
700
701 if (state->status != CLIX_CLEARED) {
702 /* check if a move is still possible, otherwise the game is over.
703 tart from the left bottom, because there are the last fields
704 at the end of the game.
705 */
706 found_move = false;
707 for( i = 0; !found_move && i < BOARD_WIDTH; ++i)
708 {
709 for( j = BOARD_HEIGHT - 1; !found_move && j >= 0; --j)
710 {
711 int color = state->board[ XYPOS( i, j)];
712 if (color != CC_BLACK) {
713 if (color == clix_get_color( state, i - 1, j) ||
714 color == clix_get_color( state, i + 1, j) ||
715 color == clix_get_color( state, i, j - 1) ||
716 color == clix_get_color( state, i, j + 1)
717 )
718 {
719 found_move = true;
720 }
721 }
722 }
723 }
724 /* if the loops ended without a possible move, the game is over */
725 if( !found_move)
726 state->status = CLIX_GAMEOVER;
727
728 /* set cursor to the right position */
729 if (state->status == CLIX_CONTINUE) {
730 clix_move_cursor( state, true);
731 clix_update_selected( state);
732 }
733 }
734
735 return state->status;
736}
737
738static bool clix_help(void)
739{
740 static char *help_text[] = {
741 "Clix", "", "Aim", "",
742 "Remove", "all", "blocks", "from", "the", "board", "to", "achieve",
743 "the", "next", "level.", "You", "can", "only", "remove", "blocks,",
744 "if", "at", "least", "two", "blocks", "with", "the", "same", "color",
745 "have", "a", "direct", "connection.", "The", "more", "blocks", "you",
746 "remove", "per", "turn,", "the", "more", "points", "you", "get."
747 };
748 static struct style_text formation[]={
749 { 0, TEXT_CENTER|TEXT_UNDERLINE },
750 { 2, C_RED },
751 LAST_STYLE_ITEM
752 };
753
754 rb->lcd_setfont(FONT_UI);
755 rb->lcd_set_foreground(LCD_WHITE);
756 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
757 return true;
758 rb->lcd_setfont(FONT_SYSFIXED);
759
760 return false;
761}
762
763static bool _ingame;
764static int clix_menu_cb(int action,
765 const struct menu_item_ex *this_item,
766 struct gui_synclist *this_list)
767{
768 (void)this_list;
769 if(action == ACTION_REQUEST_MENUITEM
770 && !_ingame && ((intptr_t)this_item)==0)
771 return ACTION_EXIT_MENUITEM;
772 return action;
773}
774
775static int clix_menu(struct clix_game_state_t* state, bool ingame)
776{
777 rb->button_clear_queue();
778 int choice = 0;
779 bool leave_menu=false;
780 int ret=0;
781
782 _ingame = ingame;
783
784 MENUITEM_STRINGLIST (main_menu, "Clix Menu", clix_menu_cb,
785 "Resume Game",
786 "Start New Game",
787 "Help",
788 "High Scores",
789 "Playback Control",
790 "Quit");
791
792 while (!leave_menu) {
793
794 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
795 case 0:
796 leave_menu=true;
797 ret = 0;
798 break;
799 case 1:
800 clix_init(state);
801 leave_menu=true;
802 ret = 0;
803 break;
804 case 2:
805 if (clix_help()) {
806 leave_menu=true;
807 ret = 1;
808 }
809 break;
810 case 3:
811 highscore_show(-1, highscores, NUM_SCORES, true);
812 break;
813 case 4:
814 playback_control(NULL);
815 break;
816 case 5:
817 case MENU_ATTACHED_USB:
818 leave_menu=true;
819 ret = 1;
820 break;
821 default:
822 break;
823 }
824 }
825
826 return ret;
827}
828
829static int clix_click(struct clix_game_state_t* state)
830{
831 int position;
832 if (state->selected_count <= 1) {
833 return 0;
834 }
835 switch( clix_clear_selected( state))
836 {
837 case CLIX_CLEARED:
838 state->score += state->level * 100;
839 clix_draw( state);
840 if (state->level < NUM_LEVELS) {
841 rb->splash(HZ*2, "Great! Next Level!");
842 state->level++;
843 clix_init_new_level( state);
844 clix_update_selected( state);
845 }
846 else {
847 rb->splash(HZ*2, "Congratulation!!!");
848 rb->splash(HZ*2, "You have finished the game.");
849 if(clix_menu(state, 0))
850 return 1;
851 }
852 break;
853 case CLIX_GAMEOVER:
854 clix_draw( state);
855 rb->splash(HZ*2, "Game Over!");
856 rb->lcd_clear_display();
857 position = highscore_update(state->score, state->level, "",
858 highscores, NUM_SCORES);
859 if (position != -1)
860 {
861 if (position == 0)
862 rb->splash(HZ*2, "New High Score");
863 highscore_show(position, highscores, NUM_SCORES, true);
864 }
865 if(clix_menu(state, 0))
866 return 1;
867 break;
868 default:
869 rb->sleep(10); /* prevent repeating clicks */
870 break;
871 }
872 return 0;
873}
874
875static int clix_handle_game(struct clix_game_state_t* state)
876{
877 int button;
878 int blink_tick = *rb->current_tick + BLINK_TICKCOUNT;
879
880 int time;
881 int start;
882 int end;
883 int oldx, oldy;
884
885 if (clix_menu(state, 0))
886 return 1;
887
888 while(true)
889 {
890 if (TIME_AFTER(*rb->current_tick, blink_tick)) {
891 state->blink = !state->blink;
892 blink_tick = *rb->current_tick + BLINK_TICKCOUNT;
893 }
894
895 time = 6; /* number of ticks this function will loop reading keys */
896 start = *rb->current_tick;
897 end = start + time;
898 while(TIME_BEFORE(*rb->current_tick, end))
899 {
900 oldx = state->x;
901 oldy = state->y;
902
903 button = rb->button_get_w_tmo(end - *rb->current_tick);
904#ifdef HAVE_TOUCHSCREEN
905 if(button & BUTTON_TOUCHSCREEN)
906 {
907 int x = rb->button_get_data() >> 16;
908 int y = rb->button_get_data() & 0xffff;
909
910 x -= XOFS;
911 y -= YOFS;
912 if(x >= 0 && y >= 0)
913 {
914 x /= CELL_SIZE + 1;
915 y /= CELL_SIZE + 1;
916
917 if(x < BOARD_WIDTH && y < BOARD_HEIGHT
918 && state->board[XYPOS(x, y)] != CC_BLACK)
919 {
920 if(state->x == x && state->y == y && button & BUTTON_REL)
921 button = CLIX_BUTTON_CLICK;
922 else
923 {
924 state->x = x;
925 state->y = y;
926 }
927 }
928 }
929 }
930#endif
931 switch( button)
932 {
933#ifndef HAVE_TOUCHSCREEN
934#ifdef CLIX_BUTTON_SCROLL_BACK
935 case CLIX_BUTTON_SCROLL_BACK:
936 case CLIX_BUTTON_SCROLL_BACK|BUTTON_REPEAT:
937#endif
938#ifdef CLIX_BUTTON_UP
939 case CLIX_BUTTON_UP:
940 case CLIX_BUTTON_UP|BUTTON_REPEAT:
941#endif
942 if( state->y == 0 ||
943 state->board[ XYPOS( state->x, state->y - 1)] ==
944 CC_BLACK
945 )
946 state->y = BOARD_HEIGHT - 1;
947 else
948 state->y--;
949
950 clix_move_cursor(state, true);
951 break;
952
953#ifdef CLIX_BUTTON_SCROLL_FWD
954 case CLIX_BUTTON_SCROLL_FWD:
955 case CLIX_BUTTON_SCROLL_FWD|BUTTON_REPEAT:
956#endif
957#ifdef CLIX_BUTTON_DOWN
958 case CLIX_BUTTON_DOWN:
959 case CLIX_BUTTON_DOWN|BUTTON_REPEAT:
960#endif
961 if( state->y == (BOARD_HEIGHT - 1))
962 state->y = 0;
963 else
964 state->y++;
965
966 clix_move_cursor( state, true);
967 break;
968
969 case CLIX_BUTTON_RIGHT:
970 case CLIX_BUTTON_RIGHT|BUTTON_REPEAT:
971 if( state->x == (BOARD_WIDTH - 1))
972 state->x = 0;
973 else
974 state->x++;
975
976 clix_move_cursor(state, false);
977 break;
978
979 case CLIX_BUTTON_LEFT:
980 case CLIX_BUTTON_LEFT|BUTTON_REPEAT:
981 if( state->x == 0)
982 state->x = BOARD_WIDTH - 1;
983 else
984 state->x--;
985
986 clix_move_cursor(state, true);
987 break;
988#endif
989 case CLIX_BUTTON_CLICK:
990 if (clix_click(state) == 1)
991 return 1;
992 break;
993
994 case CLIX_BUTTON_QUIT:
995 if (clix_menu(state, 1) != 0) {
996 rb->button_clear_queue();
997 return 1;
998 }
999 break;
1000 default:
1001
1002 break;
1003 }
1004
1005 if( (oldx != state->x || oldy != state->y) &&
1006 state->board_selected[ XYPOS( oldx, oldy)] !=
1007 state->board_selected[ XYPOS( state->x, state->y)]
1008 )
1009 {
1010 clix_update_selected(state);
1011 }
1012 clix_draw(state);
1013 rb->sleep(time);
1014 }
1015 }
1016}
1017
1018/* this is the plugin entry point */
1019enum plugin_status plugin_start(const void* parameter)
1020{
1021 (void)parameter;
1022
1023 rb->lcd_set_backdrop(NULL);
1024 rb->lcd_set_foreground(LCD_WHITE);
1025 rb->lcd_set_background(LCD_BLACK);
1026 rb->lcd_setfont(FONT_SYSFIXED);
1027#ifdef HAVE_TOUCHSCREEN
1028 rb->touchscreen_set_mode(TOUCHSCREEN_POINT);
1029#endif
1030
1031 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
1032
1033 struct clix_game_state_t state;
1034 clix_handle_game( &state);
1035
1036 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
1037
1038 return PLUGIN_OK;
1039}