Make sure that reversi doesn't enter an infinite loop when using 1 or 2 AIs.
[maemo-rb.git] / apps / plugins / reversi / reversi-gui.c
blobdcf4dd8ccba1a1cce8383e040206ae2e8f85ba61
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (c) 2006 Alexander Levin
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
21 GUI part of reversi. Code is inspired by sudoku code by Dave Chapman
22 which is copyright (c) 2005 Dave Chapman and is released under the
23 GNU General Public License.
26 User instructions
27 -----------------
29 Use the arrow keys to move cursor, and press TOGGLE to place a stone.
31 At any time during the game, press MENU to bring up the game menu with
32 further options:
34 - Save
35 - Reload
36 - Clear
40 #include "plugin.h"
42 #ifdef HAVE_LCD_BITMAP
44 #include "reversi-game.h"
45 #include "reversi-strategy.h"
46 #include "reversi-gui.h"
48 #include "../lib/oldmenuapi.h"
50 PLUGIN_HEADER
52 /* The global api struct pointer. While not strictly necessary,
53 it's nice not to have to pass the api pointer in all function
54 calls in the plugin */
55 static struct plugin_api* rb;
57 /* Thickness of the grid lines */
58 #define LINE_THCK 1
60 #if LCD_HEIGHT <= LCD_WIDTH /* Horizontal layout */
62 #if (LCD_HEIGHT==64) && (LCD_WIDTH==112)
63 /* Archos Recorders and Ondios - 112x64, 8 cells @ 8x6 with 9 border lines */
65 /* Internal dimensions of a cell */
66 #define CELL_WIDTH 8
67 #define CELL_HEIGHT 6
68 #define SMALL_BOARD
70 #elif (LCD_HEIGHT==110) && (LCD_WIDTH==138)
71 /* iPod Mini - 138x110, 8 cells @ 10x10 with 9 border lines */
73 /* Internal dimensions of a cell */
74 #define CELL_WIDTH 10
75 #define CELL_HEIGHT 10
77 #elif (LCD_HEIGHT==128) && (LCD_WIDTH==128)
78 /* iriver H10 5-6GB - 128x128, 8 cells @ 10x10 with 9 border lines */
80 /* Internal dimensions of a cell */
81 #define CELL_WIDTH 10
82 #define CELL_HEIGHT 10
84 #elif ((LCD_HEIGHT==128) && (LCD_WIDTH==160)) || \
85 ((LCD_HEIGHT==132) && (LCD_WIDTH==176))
86 /* iAudio X5, Iriver H1x0, iPod G3, G4 - 160x128; */
87 /* iPod Nano - 176x132, 8 cells @ 12x12 with 9 border lines */
89 /* Internal dimensions of a cell */
90 #define CELL_WIDTH 12
91 #define CELL_HEIGHT 12
93 #elif ((LCD_HEIGHT==176) && (LCD_WIDTH==220)) || \
94 ((LCD_HEIGHT==220) && (LCD_WIDTH==176))
95 /* Iriver h300, iPod Color/Photo - 220x176, 8 cells @ 16x16 with 9 border lines */
97 /* Internal dimensions of a cell */
98 #define CELL_WIDTH 16
99 #define CELL_HEIGHT 16
101 #elif (LCD_HEIGHT>=240) && (LCD_WIDTH>=320)
102 /* iPod Video - 320x240, 8 cells @ 24x24 with 9 border lines */
104 /* Internal dimensions of a cell */
105 #define CELL_WIDTH 24
106 #define CELL_HEIGHT 24
108 #else
109 #error REVERSI: Unsupported LCD size
110 #endif
112 #else /* Vertical layout */
113 #define VERTICAL_LAYOUT
115 #if (LCD_HEIGHT>=320) && (LCD_WIDTH>=240)
116 /* Gigabeat - 240x320, 8 cells @ 24x24 with 9 border lines */
118 /* Internal dimensions of a cell */
119 #define CELL_WIDTH 24
120 #define CELL_HEIGHT 24
122 #elif (LCD_HEIGHT>=220) && (LCD_WIDTH>=176)
123 /* e200 - 176x220, 8 cells @ 12x12 with 9 border lines */
125 /* Internal dimensions of a cell */
126 #define CELL_WIDTH 18
127 #define CELL_HEIGHT 18
129 #else
130 #error REVERSI: Unsupported LCD size
131 #endif
133 #endif /* Layout */
136 /* Where the board begins */
137 #define XOFS 4
138 #define YOFS 4
140 /* Total width and height of the board without enclosing box */
141 #define BOARD_WIDTH (CELL_WIDTH*BOARD_SIZE + LINE_THCK*(BOARD_SIZE+1))
142 #define BOARD_HEIGHT (CELL_HEIGHT*BOARD_SIZE + LINE_THCK*(BOARD_SIZE+1))
144 /* Thickness of the white cells' lines */
145 #if (CELL_WIDTH >= 15) && (CELL_HEIGHT >= 15)
146 #define CELL_LINE_THICKNESS 2
147 #else
148 #define CELL_LINE_THICKNESS 1
149 #endif
151 /* Margins within a cell */
152 #if (CELL_WIDTH >= 10) && (CELL_HEIGHT >= 10)
153 #define STONE_MARGIN 2
154 #else
155 #define STONE_MARGIN 1
156 #endif
158 #define CURSOR_MARGIN (STONE_MARGIN + CELL_LINE_THICKNESS)
160 /* Upper left corner of a cell */
161 #define CELL_X(c) (XOFS + (c)*CELL_WIDTH + ((c)+1)*LINE_THCK)
162 #define CELL_Y(r) (YOFS + (r)*CELL_HEIGHT + ((r)+1)*LINE_THCK)
165 #ifdef VERTICAL_LAYOUT
166 #define LEGEND_X(lc) (CELL_X(lc))
167 #define LEGEND_Y(lr) (CELL_Y(BOARD_SIZE+(lr)) + CELL_HEIGHT/2)
168 #else
169 #define LEGEND_X(lc) (CELL_X(BOARD_SIZE+(lc)) + CELL_WIDTH/2)
170 #define LEGEND_Y(lr) (CELL_Y(lr))
171 #endif
174 /* Board state */
175 static reversi_board_t game;
177 /* --- Setting values --- */
179 /* Playing strategies used by white and black players */
180 const game_strategy_t *white_strategy;
181 const game_strategy_t *black_strategy;
183 /* Cursor position */
184 static int cur_row, cur_col;
186 /* Color for the next move (BLACK/WHITE) */
187 static int cur_player;
189 /* Active cursor wrapping mode */
190 static cursor_wrap_mode_t cursor_wrap_mode;
192 static bool quit_plugin;
193 static bool game_finished;
196 /* Initialises the state of the game (starts a new game) */
197 static void reversi_gui_init(void) {
198 reversi_init_game(&game);
199 game_finished = false;
200 cur_player = BLACK;
202 /* Place the cursor so that WHITE can make a move */
203 cur_row = 2;
204 cur_col = 3;
208 /* Draws the cursor in the specified cell. Cursor is drawn in the complement
209 * mode, i.e. drawing it twice will result in no changes on the screen.
211 static void reversi_gui_display_cursor(int row, int col) {
212 int old_mode, x, y;
213 old_mode = rb->lcd_get_drawmode();
214 x = CELL_X(col);
215 y = CELL_Y(row);
217 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
218 rb->lcd_drawline(x+CURSOR_MARGIN, y+CURSOR_MARGIN,
219 x+CELL_WIDTH-CURSOR_MARGIN-1, y+CELL_HEIGHT-CURSOR_MARGIN-1);
220 rb->lcd_drawline(x+CURSOR_MARGIN, y+CELL_HEIGHT-CURSOR_MARGIN-1,
221 x+CELL_WIDTH-CURSOR_MARGIN-1, y+CURSOR_MARGIN);
223 /* Draw the shadows */
224 rb->lcd_hline(x, x+CELL_WIDTH-1, YOFS-3);
225 rb->lcd_hline(x, x+CELL_WIDTH-1, YOFS+BOARD_HEIGHT+2);
226 rb->lcd_vline(XOFS-3, y, y+CELL_HEIGHT-1);
227 rb->lcd_vline(XOFS+BOARD_WIDTH+2, y, y+CELL_HEIGHT-1);
229 rb->lcd_set_drawmode(old_mode);
230 rb->lcd_update();
234 /* Draws the cell of the specified color (WHITE/BLACK) assuming that
235 * the upper left corner of the cell is at (x, y) */
236 static void reversi_gui_draw_cell(int x, int y, int color) {
237 int i;
238 if (color == WHITE) {
239 for (i = 0; i < CELL_LINE_THICKNESS; i++) {
240 rb->lcd_drawrect(x+STONE_MARGIN+i, y+STONE_MARGIN+i,
241 CELL_WIDTH-2*(STONE_MARGIN+i), CELL_HEIGHT-2*(STONE_MARGIN+i));
243 } else if (color == BLACK) {
244 rb->lcd_fillrect(x+STONE_MARGIN, y+STONE_MARGIN,
245 CELL_WIDTH-2*STONE_MARGIN, CELL_HEIGHT-2*STONE_MARGIN);
246 } else {
247 /* Cell is free -> nothing to do */
252 /* Draws the complete screen */
253 static void reversi_gui_display_board(void) {
254 int x, y, r, c, x_width, x_height;
255 char buf[8];
257 /* Clear the display buffer */
258 rb->lcd_clear_display();
259 rb->lcd_set_drawmode(DRMODE_FG);
261 /* Thicker board box */
262 rb->lcd_drawrect(XOFS-1, YOFS-1, BOARD_WIDTH+2, BOARD_HEIGHT+2);
264 /* Draw the gridlines */
265 for (r=0, x=XOFS, y=YOFS; r<=BOARD_SIZE;
266 r++, x+=CELL_WIDTH+LINE_THCK, y+=CELL_HEIGHT+LINE_THCK) {
267 rb->lcd_hline(XOFS, XOFS+BOARD_WIDTH-1, y);
268 rb->lcd_vline(x, YOFS, YOFS+BOARD_HEIGHT-1);
271 /* Draw the stones. This is not the most efficient way but more readable */
272 for (r=0; r<BOARD_SIZE; r++) {
273 y = CELL_Y(r);
274 for (c=0; c<BOARD_SIZE; c++) {
275 x = CELL_X(c);
276 reversi_gui_draw_cell(x, y, game.board[r][c]);
280 /* Draw the cursor */
281 reversi_gui_display_cursor(cur_row, cur_col);
283 /* Draw the current score */
284 reversi_count_occupied_cells(&game, &r, &c);
285 rb->lcd_getstringsize("x", &x_width, &x_height);
287 x = LEGEND_X(0);
288 y = LEGEND_Y(0);
289 reversi_gui_draw_cell(x, y, BLACK);
290 rb->snprintf(buf, sizeof(buf), "%d", c);
291 y += (CELL_HEIGHT-x_height) / 2;
292 rb->lcd_putsxy(x + CELL_WIDTH + CELL_WIDTH/2, y, buf);
294 y = LEGEND_Y(1);
295 reversi_gui_draw_cell(x, y, WHITE);
296 rb->snprintf(buf, sizeof(buf), "%d", r);
297 y += (CELL_HEIGHT-x_height) / 2;
298 rb->lcd_putsxy(x + CELL_WIDTH + CELL_WIDTH/2, y, buf);
300 /* Draw the box around the current player */
301 r = (cur_player == BLACK ? 0 : 1);
302 y = LEGEND_Y(r);
303 rb->lcd_drawrect(x-1, y-1, CELL_WIDTH+2, CELL_HEIGHT+2);
305 /* Update the screen */
306 rb->lcd_update();
311 * Menu related stuff
314 /* Menu entries and the corresponding values for cursor wrap mode */
315 #define MENU_TEXT_WRAP_MODE "Cursor wrap mode"
316 static const struct opt_items cursor_wrap_mode_settings[] = {
317 { "Flat board", NULL },
318 { "Sphere", NULL },
319 { "Torus", NULL },
321 static const cursor_wrap_mode_t cursor_wrap_mode_values[3] = {
322 WRAP_FLAT, WRAP_SPHERE, WRAP_TORUS };
325 /* Menu entries and the corresponding values for available strategies */
326 #define MENU_TEXT_STRAT_WHITE "Strategy for white"
327 #define MENU_TEXT_STRAT_BLACK "Strategy for black"
329 static struct opt_items strategy_settings[] = {
330 { "Human", NULL },
331 { "Naive robot", NULL },
332 { "Simple robot", NULL },
333 //{ "AB robot", NULL },
335 static const game_strategy_t * const strategy_values[] = {
336 &strategy_human, &strategy_naive, &strategy_simple, /*&strategy_ab*/ };
339 /* Sets the strategy for the specified player. 'player' is the
340 pointer to the player to set the strategy for (actually,
341 either white_strategy or black_strategy). propmpt is the
342 text to show as the prompt in the menu */
343 static bool reversi_gui_choose_strategy(
344 const game_strategy_t **player, const char *prompt) {
345 int index = 0, i;
346 int num_items = sizeof(strategy_settings)/sizeof(strategy_settings[0]);
347 bool result;
349 for (i = 0; i < num_items; i++) {
350 if ((*player) == strategy_values[i]) {
351 index = i;
352 break;
355 result = rb->set_option(prompt, &index, INT, strategy_settings, num_items, NULL);
356 (*player) = strategy_values[index];
358 if((*player)->init_func)
359 (*player)->init_func(&game);
361 return result;
365 /* Returns true iff USB ws connected while in the menu */
366 static bool reversi_gui_menu(void) {
367 int m, index, num_items, i;
368 int result;
370 static const struct menu_item items[] = {
371 { "Start new game", NULL },
372 { "Pass the move", NULL },
373 { MENU_TEXT_STRAT_BLACK, NULL },
374 { MENU_TEXT_STRAT_WHITE, NULL },
375 { MENU_TEXT_WRAP_MODE, NULL },
376 { "Quit", NULL },
379 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
380 NULL, NULL, NULL, NULL);
382 result = menu_show(m);
384 switch (result) {
385 case 0: /* Start a new game */
386 reversi_gui_init();
387 break;
389 case 1: /* Pass the move to the partner */
390 cur_player = reversi_flipped_color(cur_player);
391 break;
393 case 2: /* Strategy for black */
394 reversi_gui_choose_strategy(&black_strategy, MENU_TEXT_STRAT_BLACK);
395 break;
397 case 3: /* Strategy for white */
398 reversi_gui_choose_strategy(&white_strategy, MENU_TEXT_STRAT_WHITE);
399 break;
401 case 4: /* Cursor wrap mode */
402 num_items = sizeof(cursor_wrap_mode_values)/sizeof(cursor_wrap_mode_values[0]);
403 index = 0;
404 for (i = 0; i < num_items; i++) {
405 if (cursor_wrap_mode == cursor_wrap_mode_values[i]) {
406 index = i;
407 break;
410 rb->set_option(MENU_TEXT_WRAP_MODE, &index, INT,
411 cursor_wrap_mode_settings, 3, NULL);
412 cursor_wrap_mode = cursor_wrap_mode_values[index];
413 break;
415 case 5: /* Quit */
416 quit_plugin = true;
417 break;
420 menu_exit(m);
422 return (result == MENU_ATTACHED_USB);
426 /* Calculates the new cursor position if the user wants to move it
427 * vertically as specified by delta. Current wrap mode is respected.
428 * The cursor is not actually moved.
430 * Returns true iff the cursor would be really moved. In any case, the
431 * new cursor position is stored in (new_row, new_col).
433 static bool reversi_gui_cursor_pos_vmove(int row_delta, int *new_row, int *new_col) {
434 *new_row = cur_row + row_delta;
435 *new_col = cur_col;
437 if (*new_row < 0) {
438 switch (cursor_wrap_mode) {
439 case WRAP_FLAT:
440 *new_row = cur_row;
441 break;
442 case WRAP_SPHERE:
443 *new_row = BOARD_SIZE - 1;
444 break;
445 case WRAP_TORUS:
446 *new_row = BOARD_SIZE - 1;
447 (*new_col)--;
448 if (*new_col < 0) {
449 *new_col = BOARD_SIZE - 1;
451 break;
453 } else if (*new_row >= BOARD_SIZE) {
454 switch (cursor_wrap_mode) {
455 case WRAP_FLAT:
456 *new_row = cur_row;
457 break;
458 case WRAP_SPHERE:
459 *new_row = 0;
460 break;
461 case WRAP_TORUS:
462 *new_row = 0;
463 (*new_col)++;
464 if (*new_col >= BOARD_SIZE) {
465 *new_col = 0;
467 break;
471 return (cur_row != (*new_row)) || (cur_col != (*new_col));
475 /* Calculates the new cursor position if the user wants to move it
476 * horisontally as specified by delta. Current wrap mode is respected.
477 * The cursor is not actually moved.
479 * Returns true iff the cursor would be really moved. In any case, the
480 * new cursor position is stored in (new_row, new_col).
482 static bool reversi_gui_cursor_pos_hmove(int col_delta, int *new_row, int *new_col) {
483 *new_row = cur_row;
484 *new_col = cur_col + col_delta;
486 if (*new_col < 0) {
487 switch (cursor_wrap_mode) {
488 case WRAP_FLAT:
489 *new_col = cur_col;
490 break;
491 case WRAP_SPHERE:
492 *new_col = BOARD_SIZE - 1;
493 break;
494 case WRAP_TORUS:
495 *new_col = BOARD_SIZE - 1;
496 (*new_row)--;
497 if (*new_row < 0) {
498 *new_row = BOARD_SIZE - 1;
500 break;
502 } else if (*new_col >= BOARD_SIZE) {
503 switch (cursor_wrap_mode) {
504 case WRAP_FLAT:
505 *new_col = cur_col;
506 break;
507 case WRAP_SPHERE:
508 *new_col = 0;
509 break;
510 case WRAP_TORUS:
511 *new_col = 0;
512 (*new_row)++;
513 if (*new_row >= BOARD_SIZE) {
514 *new_row = 0;
516 break;
520 return (cur_row != (*new_row)) || (cur_col != (*new_col));
524 /* Actually moves the cursor to the new position and updates the screen */
525 static void reversi_gui_move_cursor(int new_row, int new_col) {
526 int old_row, old_col;
528 old_row = cur_row;
529 old_col = cur_col;
531 cur_row = new_row;
532 cur_col = new_col;
534 /* Only update the changed cells since there are no global changes */
535 reversi_gui_display_cursor(old_row, old_col);
536 reversi_gui_display_cursor(new_row, new_col);
540 /* plugin entry point */
541 enum plugin_status plugin_start(struct plugin_api *api, void *parameter) {
542 bool exit, draw_screen;
543 int button;
544 int lastbutton = BUTTON_NONE;
545 int row, col;
546 int w_cnt, b_cnt;
547 char msg_buf[30];
549 /* plugin init */
550 rb = api;
551 /* end of plugin init */
553 #if LCD_DEPTH > 1
554 rb->lcd_set_backdrop(NULL);
555 rb->lcd_set_foreground(LCD_BLACK);
556 rb->lcd_set_background(LCD_WHITE);
557 #endif
559 /* Avoid compiler warnings */
560 (void)parameter;
562 game.rb = rb;
563 rb->srand(*rb->current_tick); /* Some AIs use rand() */
564 white_strategy = &strategy_human;
565 black_strategy = &strategy_human;
567 reversi_gui_init();
568 cursor_wrap_mode = WRAP_FLAT;
570 /* The main game loop */
571 exit = false;
572 quit_plugin = false;
573 draw_screen = true;
574 while (!exit && !quit_plugin) {
575 const game_strategy_t *cur_strategy = NULL;
576 if (draw_screen) {
577 reversi_gui_display_board();
578 draw_screen = false;
580 switch(cur_player) {
581 case BLACK:
582 cur_strategy = black_strategy;
583 break;
584 case WHITE:
585 cur_strategy = white_strategy;
586 break;
589 if(cur_strategy->is_robot && !game_finished) {
590 move_t m = cur_strategy->move_func(&game, cur_player);
591 reversi_make_move(&game, MOVE_ROW(m), MOVE_COL(m), cur_player);
592 cur_player = reversi_flipped_color(cur_player);
593 draw_screen = true;
594 /* TODO: Add some delay to prevent it from being too fast ? */
595 /* TODO: Don't duplicate end of game check */
596 if (reversi_game_is_finished(&game, cur_player)) {
597 reversi_count_occupied_cells(&game, &w_cnt, &b_cnt);
598 rb->snprintf(msg_buf, sizeof(msg_buf),
599 "Game over. %s have won.",
600 (w_cnt>b_cnt?"WHITE":"BLACK"));
601 rb->splash(HZ*2, msg_buf);
602 draw_screen = true; /* Must update screen after splash */
603 game_finished = true;
605 continue;
608 button = rb->button_get(true);
610 switch (button) {
611 #ifdef REVERSI_BUTTON_QUIT
612 /* Exit game */
613 case REVERSI_BUTTON_QUIT:
614 exit = true;
615 break;
616 #endif
618 #ifdef REVERSI_BUTTON_ALT_MAKE_MOVE
619 case REVERSI_BUTTON_ALT_MAKE_MOVE:
620 #endif
621 case REVERSI_BUTTON_MAKE_MOVE:
622 #ifdef REVERSI_BUTTON_MAKE_MOVE_PRE
623 if ((button == REVERSI_BUTTON_MAKE_MOVE)
624 && (lastbutton != REVERSI_BUTTON_MAKE_MOVE_PRE))
625 break;
626 #endif
627 if (game_finished) break;
628 if (reversi_make_move(&game, cur_row, cur_col, cur_player) > 0) {
629 /* Move was made. Global changes on the board are possible */
630 draw_screen = true; /* Redraw the screen next time */
631 cur_player = reversi_flipped_color(cur_player);
632 if (reversi_game_is_finished(&game, cur_player)) {
633 reversi_count_occupied_cells(&game, &w_cnt, &b_cnt);
634 rb->snprintf(msg_buf, sizeof(msg_buf),
635 "Game over. %s have won.",
636 (w_cnt>b_cnt?"WHITE":"BLACK"));
637 rb->splash(HZ*2, msg_buf);
638 draw_screen = true; /* Must update screen after splash */
639 game_finished = true;
641 } else {
642 /* An attempt to make an invalid move */
643 rb->splash(HZ/2, "Illegal move!");
644 draw_screen = true;
645 /* Ignore any button presses during the splash */
646 rb->button_clear_queue();
648 break;
650 /* Move cursor left */
651 case REVERSI_BUTTON_LEFT:
652 case (REVERSI_BUTTON_LEFT | BUTTON_REPEAT):
653 if (reversi_gui_cursor_pos_hmove(-1, &row, &col)) {
654 reversi_gui_move_cursor(row, col);
656 break;
658 /* Move cursor right */
659 case REVERSI_BUTTON_RIGHT:
660 case (REVERSI_BUTTON_RIGHT | BUTTON_REPEAT):
661 if (reversi_gui_cursor_pos_hmove(1, &row, &col)) {
662 reversi_gui_move_cursor(row, col);
664 break;
666 /* Move cursor up */
667 case REVERSI_BUTTON_UP:
668 case (REVERSI_BUTTON_UP | BUTTON_REPEAT):
669 if (reversi_gui_cursor_pos_vmove(-1, &row, &col)) {
670 reversi_gui_move_cursor(row, col);
672 break;
674 /* Move cursor down */
675 case REVERSI_BUTTON_DOWN:
676 case (REVERSI_BUTTON_DOWN | BUTTON_REPEAT):
677 if (reversi_gui_cursor_pos_vmove(1, &row, &col)) {
678 reversi_gui_move_cursor(row, col);
680 break;
682 case REVERSI_BUTTON_MENU:
683 #ifdef REVERSI_BUTTON_MENU_PRE
684 if (lastbutton != REVERSI_BUTTON_MENU_PRE) {
685 break;
687 #endif
688 if (reversi_gui_menu()) {
689 return PLUGIN_USB_CONNECTED;
691 draw_screen = true;
692 break;
694 default:
695 if (rb->default_event_handler(button) == SYS_USB_CONNECTED) {
696 /* Quit if USB has been connected */
697 return PLUGIN_USB_CONNECTED;
699 break;
701 if (button != BUTTON_NONE) {
702 lastbutton = button;
706 return PLUGIN_OK;
709 #endif