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