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) 2004-2007 Antoine Cellerier <dionoea @t videolan d.t org>
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#include "plugin.h"
23#include "lib/playback_control.h"
24#include "lib/configfile.h"
25#include "lib/display_text.h"
26#include "button.h"
27#include "lcd.h"
28
29#define min(a,b) (a<b?a:b)
30
31/**
32 * Key definitions
33 */
34
35#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
36 (CONFIG_KEYPAD == IRIVER_H300_PAD)
37# define SOL_QUIT BUTTON_OFF
38# define SOL_UP BUTTON_UP
39# define SOL_DOWN BUTTON_DOWN
40# define SOL_LEFT BUTTON_LEFT
41# define SOL_RIGHT BUTTON_RIGHT
42# define SOL_MOVE_PRE BUTTON_SELECT
43# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
44# define SOL_DRAW BUTTON_MODE
45# define SOL_REM2CUR (BUTTON_LEFT | BUTTON_ON)
46# define SOL_CUR2STACK_PRE BUTTON_SELECT
47# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
48# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_ON)
49# define SOL_REM BUTTON_REC
50# define SOL_RC_QUIT BUTTON_RC_STOP
51# define HK_MOVE "NAVI"
52# define HK_DRAW "A-B"
53# define HK_REM2CUR "PLAY+LEFT"
54# define HK_CUR2STACK "NAVI.."
55# define HK_REM2STACK "PLAY+RIGHT"
56
57#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
58 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
59# define SOL_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
60# define SOL_UP BUTTON_SCROLL_BACK
61# define SOL_DOWN BUTTON_SCROLL_FWD
62# define SOL_LEFT_PRE BUTTON_LEFT
63# define SOL_LEFT (BUTTON_LEFT | BUTTON_REL)
64# define SOL_RIGHT_PRE BUTTON_RIGHT
65# define SOL_RIGHT (BUTTON_RIGHT | BUTTON_REL)
66# define SOL_MOVE_PRE BUTTON_SELECT
67# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
68# define SOL_DRAW_PRE BUTTON_MENU
69# define SOL_DRAW (BUTTON_MENU | BUTTON_REL)
70# define SOL_REM2CUR BUTTON_PLAY
71# define SOL_CUR2STACK_PRE BUTTON_MENU
72# define SOL_CUR2STACK (BUTTON_MENU | BUTTON_REPEAT)
73# define SOL_REM2STACK_PRE BUTTON_RIGHT
74# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_REPEAT)
75# define HK_UD "SCROLL U/D"
76# define HK_MOVE "SELECT"
77# define HK_DRAW "MENU"
78# define HK_REM2CUR "PLAY"
79# define HK_CUR2STACK "MENU.."
80# define HK_REM2STACK "RIGHT.."
81
82#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
83# define SOL_QUIT BUTTON_POWER
84# define SOL_UP BUTTON_UP
85# define SOL_DOWN BUTTON_DOWN
86# define SOL_LEFT BUTTON_LEFT
87# define SOL_RIGHT BUTTON_RIGHT
88# define SOL_MOVE_PRE BUTTON_SELECT
89# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
90# define SOL_DRAW_PRE BUTTON_PLAY
91# define SOL_DRAW (BUTTON_PLAY | BUTTON_REL)
92# define SOL_REM2CUR_PRE BUTTON_PLAY
93# define SOL_REM2CUR (BUTTON_PLAY | BUTTON_REPEAT)
94# define SOL_CUR2STACK_PRE BUTTON_SELECT
95# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
96# define SOL_REM2STACK BUTTON_REC
97# define HK_MOVE "SELECT"
98# define HK_DRAW "PLAY"
99# define HK_REM2CUR "PLAY.."
100# define HK_CUR2STACK "SELECT.."
101# define HK_REM2STACK "REC"
102
103#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
104# define SOL_QUIT BUTTON_POWER
105# define SOL_UP BUTTON_UP
106# define SOL_DOWN BUTTON_DOWN
107# define SOL_LEFT BUTTON_LEFT
108# define SOL_RIGHT BUTTON_RIGHT
109# define SOL_MOVE_PRE BUTTON_SELECT
110# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
111# define SOL_DRAW BUTTON_MENU
112# define SOL_REM2CUR (BUTTON_LEFT | BUTTON_A)
113# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
114# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_A)
115# define HK_MOVE "SELECT"
116# define HK_DRAW "MENU"
117# define HK_REM2CUR "A+LEFT"
118# define HK_CUR2STACK "SELECT.."
119# define HK_REM2STACK "A+RIGHT"
120
121#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
122# define SOL_QUIT BUTTON_POWER
123# define SOL_UP BUTTON_UP
124# define SOL_DOWN BUTTON_DOWN
125# define SOL_LEFT BUTTON_SCROLL_BACK
126# define SOL_RIGHT BUTTON_SCROLL_FWD
127# define SOL_MOVE BUTTON_SELECT
128# define SOL_DRAW BUTTON_REC
129# define SOL_REM2CUR BUTTON_LEFT
130# define SOL_CUR2STACK_PRE BUTTON_REC
131# define SOL_CUR2STACK (BUTTON_REC | BUTTON_RIGHT)
132# define SOL_REM2STACK BUTTON_RIGHT
133# define HK_MOVE "SELECT"
134# define HK_DRAW "REC"
135# define HK_REM2CUR "LEFT"
136# define HK_CUR2STACK "DOUBLE SELECT"
137# define HK_REM2STACK "RIGHT"
138
139#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
140# define SOL_QUIT (BUTTON_HOME|BUTTON_REPEAT)
141# define SOL_UP BUTTON_UP
142# define SOL_DOWN BUTTON_DOWN
143# define SOL_LEFT BUTTON_SCROLL_BACK
144# define SOL_RIGHT BUTTON_SCROLL_FWD
145# define SOL_MOVE (BUTTON_SELECT|BUTTON_REL)
146# define SOL_DRAW (BUTTON_HOME|BUTTON_REL)
147# define SOL_REM2CUR BUTTON_LEFT
148# define SOL_CUR2STACK_PRE (BUTTON_RIGHT | BUTTON_REPEAT)
149# define SOL_CUR2STACK BUTTON_RIGHT
150# define SOL_REM2STACK (BUTTON_LEFT|BUTTON_REPEAT)
151# define SOL_REM2STACK_PRE BUTTON_LEFT
152# define HK_MOVE "SHORT SELECT"
153# define HK_DRAW "SHORT HOME"
154# define HK_REM2CUR "LEFT"
155# define HK_CUR2STACK "DOUBLE SELECT"
156# define HK_REM2STACK "LEFT"
157
158#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
159# define SOL_QUIT BUTTON_POWER
160# define SOL_UP BUTTON_UP
161# define SOL_DOWN BUTTON_DOWN
162# define SOL_LEFT BUTTON_LEFT
163# define SOL_RIGHT BUTTON_RIGHT
164# define SOL_MOVE_PRE BUTTON_SELECT
165# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
166# define SOL_DRAW BUTTON_VOL_DOWN
167# define SOL_REM2CUR BUTTON_REC
168# define SOL_CUR2STACK_PRE BUTTON_SELECT
169# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
170# define SOL_REM2STACK BUTTON_VOL_UP
171# define HK_MOVE "SELECT"
172# define HK_DRAW "REC"
173# define HK_REM2CUR "LEFT"
174# define HK_CUR2STACK "DOUBLE SELECT"
175# define HK_REM2STACK "RIGHT"
176
177#elif CONFIG_KEYPAD == SANSA_CLIP_PAD
178# define SOL_QUIT BUTTON_POWER
179# define SOL_UP BUTTON_UP
180# define SOL_DOWN BUTTON_DOWN
181# define SOL_LEFT BUTTON_LEFT
182# define SOL_RIGHT BUTTON_RIGHT
183# define SOL_MOVE_PRE BUTTON_SELECT
184# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
185# define SOL_DRAW BUTTON_HOME
186# define SOL_REM2CUR BUTTON_VOL_DOWN
187# define SOL_CUR2STACK_PRE BUTTON_SELECT
188# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
189# define SOL_REM2STACK BUTTON_VOL_UP
190# define HK_MOVE "SELECT"
191# define HK_DRAW "HOME"
192# define HK_REM2CUR "LEFT"
193# define HK_CUR2STACK "DOUBLE SELECT"
194# define HK_REM2STACK "RIGHT"
195
196#elif CONFIG_KEYPAD == SANSA_M200_PAD
197# define SOL_QUIT BUTTON_POWER
198# define SOL_UP BUTTON_UP
199# define SOL_DOWN BUTTON_DOWN
200# define SOL_LEFT BUTTON_LEFT
201# define SOL_RIGHT BUTTON_RIGHT
202# define SOL_MOVE_PRE BUTTON_SELECT
203# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
204# define SOL_DRAW (BUTTON_SELECT | BUTTON_UP)
205# define SOL_REM2CUR BUTTON_VOL_DOWN
206# define SOL_CUR2STACK_PRE BUTTON_SELECT
207# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
208# define SOL_REM2STACK BUTTON_VOL_UP
209# define HK_MOVE "SELECT"
210# define HK_DRAW "SELECT + UP"
211# define HK_REM2CUR "LEFT"
212# define HK_CUR2STACK "DOUBLE SELECT"
213# define HK_REM2STACK "RIGHT"
214
215#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
216# define SOL_QUIT BUTTON_POWER
217# define SOL_UP BUTTON_SCROLL_UP
218# define SOL_DOWN BUTTON_SCROLL_DOWN
219# define SOL_LEFT_PRE BUTTON_LEFT
220# define SOL_LEFT (BUTTON_LEFT | BUTTON_REL)
221# define SOL_RIGHT_PRE BUTTON_RIGHT
222# define SOL_RIGHT (BUTTON_RIGHT | BUTTON_REL)
223# define SOL_MOVE BUTTON_PLAY
224# define SOL_DRAW_PRE BUTTON_LEFT
225# define SOL_DRAW (BUTTON_LEFT | BUTTON_REPEAT)
226# define SOL_REM2CUR BUTTON_FF
227# define SOL_CUR2STACK BUTTON_REW
228# define SOL_REM2STACK_PRE BUTTON_RIGHT
229# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_REPEAT)
230# define HK_MOVE "PLAY"
231# define HK_DRAW "LEFT.."
232# define HK_REM2CUR "FF"
233# define HK_CUR2STACK "REW"
234# define HK_REM2STACK "RIGHT.."
235
236#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
237# define SOL_QUIT BUTTON_BACK
238# define SOL_UP BUTTON_UP
239# define SOL_DOWN BUTTON_DOWN
240# define SOL_LEFT BUTTON_LEFT
241# define SOL_RIGHT BUTTON_RIGHT
242# define SOL_MOVE_PRE BUTTON_SELECT
243# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
244# define SOL_DRAW BUTTON_MENU
245# define SOL_REM2CUR (BUTTON_LEFT | BUTTON_SELECT)
246# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
247# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_SELECT)
248# define HK_MOVE "SELECT"
249# define HK_DRAW "MENU"
250# define HK_REM2CUR "SELECT+LEFT"
251# define HK_CUR2STACK "SELECT.."
252# define HK_REM2STACK "SELECT+RIGHT"
253
254#elif (CONFIG_KEYPAD == MROBE100_PAD)
255# define SOL_QUIT BUTTON_POWER
256# define SOL_UP BUTTON_UP
257# define SOL_DOWN BUTTON_DOWN
258# define SOL_LEFT BUTTON_LEFT
259# define SOL_RIGHT BUTTON_RIGHT
260# define SOL_MOVE_PRE BUTTON_SELECT
261# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
262# define SOL_DRAW BUTTON_MENU
263# define SOL_REM2CUR (BUTTON_LEFT | BUTTON_DISPLAY)
264# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
265# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_DISPLAY)
266# define HK_MOVE "SELECT"
267# define HK_DRAW "MENU"
268# define HK_REM2CUR "DISPLAY+LEFT"
269# define HK_CUR2STACK "SELECT.."
270# define HK_REM2STACK "DISPLAY+RIGHT"
271
272#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
273# define SOL_QUIT BUTTON_RC_REC
274# define SOL_UP BUTTON_RC_VOL_UP
275# define SOL_DOWN BUTTON_RC_VOL_DOWN
276# define SOL_LEFT BUTTON_RC_REW
277# define SOL_RIGHT BUTTON_RC_FF
278# define SOL_MOVE BUTTON_RC_PLAY
279# define SOL_DRAW_PRE BUTTON_RC_MENU
280# define SOL_DRAW (BUTTON_RC_MENU | BUTTON_REL)
281# define SOL_REM2CUR_PRE BUTTON_RC_MENU
282# define SOL_REM2CUR (BUTTON_RC_MENU | BUTTON_REPEAT)
283# define SOL_CUR2STACK_PRE BUTTON_RC_MODE
284# define SOL_CUR2STACK (BUTTON_RC_MODE | BUTTON_REL)
285# define SOL_REM2STACK_PRE BUTTON_RC_MODE
286# define SOL_REM2STACK (BUTTON_RC_MODE | BUTTON_REPEAT)
287# define HK_LR "REW/FF"
288# define HK_UD "VOL UP/DOWN"
289# define HK_MOVE "PLAY"
290# define HK_DRAW "MENU"
291# define HK_REM2CUR "MENU.."
292# define HK_CUR2STACK "MODE"
293# define HK_REM2STACK "MODE.."
294
295#elif (CONFIG_KEYPAD == COWON_D2_PAD)
296# define SOL_QUIT BUTTON_POWER
297
298#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
299# define SOL_QUIT BUTTON_BACK
300# define SOL_UP BUTTON_UP
301# define SOL_DOWN BUTTON_DOWN
302# define SOL_LEFT BUTTON_LEFT
303# define SOL_RIGHT BUTTON_RIGHT
304# define SOL_MOVE_PRE BUTTON_SELECT
305# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
306# define SOL_DRAW BUTTON_MENU
307# define SOL_REM2CUR (BUTTON_LEFT | BUTTON_SELECT)
308# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
309# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_SELECT)
310# define HK_MOVE "MIDDLE"
311# define HK_DRAW "MENU"
312# define HK_REM2CUR "PLAY+LEFT"
313# define HK_CUR2STACK "PLAY.."
314# define HK_REM2STACK "PLAY+RIGHT"
315
316#elif (CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD)
317# define SOL_QUIT BUTTON_POWER
318# define SOL_UP BUTTON_UP
319# define SOL_DOWN BUTTON_DOWN
320# define SOL_LEFT BUTTON_BACK
321# define SOL_RIGHT BUTTON_MENU
322# define SOL_MOVE (BUTTON_PLAY|BUTTON_REL)
323# define SOL_DRAW (BUTTON_PLAY|BUTTON_REPEAT)
324# define SOL_REM2CUR BUTTON_VOL_DOWN
325# define SOL_CUR2STACK_PRE (BUTTON_VOL_UP | BUTTON_REPEAT)
326# define SOL_CUR2STACK BUTTON_VOL_UP
327# define SOL_REM2STACK (BUTTON_VOL_DOWN|BUTTON_REPEAT)
328# define SOL_REM2STACK_PRE BUTTON_VOL_DOWN
329
330# define HK_MOVE "SHORT PLAY"
331# define HK_DRAW "LONG PLAY"
332# define HK_REM2CUR "VOLUME-"
333# define HK_CUR2STACK "VOLUME+"
334# define HK_REM2STACK "LONG VOLUME-"
335
336#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
337# define SOL_QUIT BUTTON_POWER
338# define SOL_UP BUTTON_UP
339# define SOL_DOWN BUTTON_DOWN
340# define SOL_LEFT BUTTON_LEFT
341# define SOL_RIGHT BUTTON_RIGHT
342# define SOL_MOVE_PRE BUTTON_SELECT
343# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
344# define SOL_DRAW BUTTON_MENU
345# define SOL_REM2CUR (BUTTON_LEFT | BUTTON_VIEW)
346# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
347# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_VIEW)
348# define HK_MOVE "SELECT"
349# define HK_DRAW "MENU"
350# define HK_REM2CUR "VIEW+LEFT"
351# define HK_CUR2STACK "SELECT.."
352# define HK_REM2STACK "VIEW+RIGHT"
353
354#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
355# define SOL_QUIT BUTTON_POWER
356# define SOL_UP BUTTON_UP
357# define SOL_DOWN BUTTON_DOWN
358# define SOL_LEFT BUTTON_PREV
359# define SOL_RIGHT BUTTON_NEXT
360# define SOL_MOVE_PRE BUTTON_PLAY
361# define SOL_MOVE (BUTTON_PLAY | BUTTON_REL)
362# define SOL_DRAW BUTTON_MENU
363# define SOL_REM2CUR BUTTON_LEFT
364# define SOL_CUR2STACK (BUTTON_PLAY | BUTTON_REPEAT)
365# define SOL_REM2STACK BUTTON_RIGHT
366# define HK_MOVE "PLAY"
367# define HK_DRAW "MENU"
368# define HK_REM2CUR "LEFT"
369# define HK_CUR2STACK "PLAY.."
370# define HK_REM2STACK "RIGHT"
371
372#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
373# define SOL_QUIT BUTTON_POWER
374# define SOL_UP BUTTON_UP
375# define SOL_DOWN BUTTON_DOWN
376# define SOL_LEFT BUTTON_PREV
377# define SOL_RIGHT BUTTON_NEXT
378# define SOL_MOVE_PRE BUTTON_PLAY
379# define SOL_MOVE (BUTTON_PLAY | BUTTON_REL)
380# define SOL_DRAW BUTTON_MENU
381# define SOL_REM2CUR BUTTON_LEFT
382# define SOL_CUR2STACK (BUTTON_PLAY | BUTTON_REPEAT)
383# define SOL_REM2STACK BUTTON_RIGHT
384# define HK_MOVE "PLAY"
385# define HK_DRAW "MENU"
386# define HK_REM2CUR "LEFT"
387# define HK_CUR2STACK "PLAY..."
388# define HK_REM2STACK "RIGHT"
389
390#elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
391(CONFIG_KEYPAD == ONDAVX777_PAD) || \
392CONFIG_KEYPAD == MROBE500_PAD
393# define SOL_QUIT BUTTON_POWER
394
395#elif CONFIG_KEYPAD == SAMSUNG_YH820_PAD
396# define SOL_QUIT BUTTON_REW
397# define SOL_UP BUTTON_UP
398# define SOL_DOWN BUTTON_DOWN
399# define SOL_LEFT BUTTON_LEFT
400# define SOL_RIGHT BUTTON_RIGHT
401# define SOL_MOVE BUTTON_PLAY
402# define SOL_DRAW BUTTON_FFWD
403# define SOL_REM2CUR (BUTTON_REC | BUTTON_DOWN)
404# define SOL_CUR2STACK (BUTTON_REC | BUTTON_UP)
405# define SOL_REM2STACK (BUTTON_REC | BUTTON_RIGHT)
406# define HK_MOVE "PLAY"
407# define HK_DRAW "FFWD"
408# define HK_REM2CUR "REC+DOWN"
409# define HK_CUR2STACK "REC+UP"
410# define HK_REM2STACK "REC+RIGHT"
411
412#elif CONFIG_KEYPAD == SAMSUNG_YH92X_PAD
413# define SOL_QUIT BUTTON_REW
414# define SOL_UP BUTTON_UP
415# define SOL_DOWN BUTTON_DOWN
416# define SOL_LEFT BUTTON_LEFT
417# define SOL_RIGHT BUTTON_RIGHT
418# define SOL_MOVE_PRE BUTTON_PLAY
419# define SOL_MOVE (BUTTON_PLAY | BUTTON_REL)
420# define SOL_DRAW BUTTON_FFWD
421# define SOL_REM2CUR (BUTTON_PLAY | BUTTON_DOWN)
422# define SOL_CUR2STACK (BUTTON_PLAY | BUTTON_UP)
423# define SOL_REM2STACK (BUTTON_PLAY | BUTTON_RIGHT)
424# define HK_MOVE "PLAY"
425# define HK_DRAW "FFWD"
426# define HK_REM2CUR "PLAY+DOWN"
427# define HK_CUR2STACK "PLAY+UP"
428# define HK_REM2STACK "PLAY+RIGHT"
429
430#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
431# define SOL_QUIT BUTTON_REC
432# define SOL_UP BUTTON_UP
433# define SOL_DOWN BUTTON_DOWN
434# define SOL_LEFT BUTTON_PREV
435# define SOL_RIGHT BUTTON_NEXT
436# define SOL_MOVE_PRE BUTTON_OK
437# define SOL_MOVE (BUTTON_OK | BUTTON_REL)
438# define SOL_DRAW BUTTON_MENU
439# define SOL_REM2CUR BUTTON_CANCEL
440# define SOL_CUR2STACK BUTTON_PLAY
441# define SOL_REM2STACK (BUTTON_PLAY | BUTTON_REPEAT)
442# define HK_MOVE "OK"
443# define HK_DRAW "MENU"
444# define HK_REM2CUR "CANCEL"
445# define HK_CUR2STACK "PLAY"
446# define HK_REM2STACK "PLAY..."
447
448#elif CONFIG_KEYPAD == MPIO_HD200_PAD
449# define SOL_QUIT (BUTTON_REC | BUTTON_PLAY)
450# define SOL_UP BUTTON_REW
451# define SOL_DOWN BUTTON_FF
452# define SOL_LEFT BUTTON_VOL_DOWN
453# define SOL_RIGHT BUTTON_VOL_UP
454# define SOL_MOVE_PRE BUTTON_FUNC
455# define SOL_MOVE (BUTTON_FUNC | BUTTON_REL)
456# define SOL_DRAW BUTTON_REC
457# define SOL_REM2CUR (BUTTON_REC | BUTTON_REPEAT)
458# define SOL_CUR2STACK BUTTON_PLAY
459# define SOL_REM2STACK (BUTTON_PLAY | BUTTON_REPEAT)
460# define HK_MOVE "FUNC"
461# define HK_DRAW "REC"
462# define HK_REM2CUR "REC.."
463# define HK_CUR2STACK "PLAY"
464# define HK_REM2STACK "PLAY...."
465
466#elif CONFIG_KEYPAD == MPIO_HD300_PAD
467# define SOL_QUIT (BUTTON_MENU | BUTTON_REPEAT)
468# define SOL_UP BUTTON_UP
469# define SOL_DOWN BUTTON_DOWN
470# define SOL_LEFT BUTTON_REW
471# define SOL_RIGHT BUTTON_FF
472# define SOL_MOVE_PRE BUTTON_ENTER
473# define SOL_MOVE (BUTTON_ENTER | BUTTON_REL)
474# define SOL_DRAW BUTTON_MENU
475# define SOL_REM2CUR (BUTTON_PLAY | BUTTON_REL)
476# define SOL_CUR2STACK BUTTON_REC
477# define SOL_REM2STACK (BUTTON_PLAY | BUTTON_REPEAT)
478# define HK_MOVE "ENTER"
479# define HK_DRAW "MENU"
480# define HK_REM2CUR "PLAY"
481# define HK_CUR2STACK "ENTER..."
482# define HK_REM2STACK "PLAY...."
483
484#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
485# define SOL_QUIT BUTTON_POWER
486# define SOL_UP BUTTON_UP
487# define SOL_DOWN BUTTON_DOWN
488# define SOL_LEFT BUTTON_LEFT
489# define SOL_RIGHT BUTTON_RIGHT
490# define SOL_MOVE BUTTON_SELECT
491# define SOL_DRAW BUTTON_BACK
492# define SOL_REM2CUR BUTTON_BOTTOMLEFT
493# define SOL_CUR2STACK BUTTON_PLAYPAUSE|BUTTON_REL
494# define SOL_REM2STACK BUTTON_PLAYPAUSE|BUTTON_REPEAT
495# define HK_MOVE "SELECT"
496# define HK_DRAW "BACK"
497# define HK_REM2CUR "BOTTOM-LEFT"
498# define HK_CUR2STACK "PLAY-PAUSE"
499# define HK_REM2STACK "BOTTOM-RIGHT"
500
501#elif (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
502# define SOL_QUIT BUTTON_POWER
503# define SOL_UP BUTTON_UP
504# define SOL_DOWN BUTTON_DOWN
505# define SOL_LEFT BUTTON_SCROLL_BACK
506# define SOL_RIGHT BUTTON_SCROLL_FWD
507# define SOL_MOVE BUTTON_SELECT
508# define SOL_DRAW BUTTON_VOL_UP
509# define SOL_REM2CUR BUTTON_LEFT
510# define SOL_CUR2STACK_PRE BUTTON_VOL_DOWN
511# define SOL_CUR2STACK BUTTON_NEXT
512# define SOL_REM2STACK BUTTON_PREV
513# define HK_MOVE "SELECT"
514# define HK_DRAW "Vol+"
515# define HK_REM2CUR "LEFT"
516# define HK_CUR2STACK "NEXT"
517# define HK_REM2STACK "PREV"
518
519#elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
520# define SOL_QUIT BUTTON_BACK
521# define SOL_UP BUTTON_UP
522# define SOL_DOWN BUTTON_DOWN
523# define SOL_LEFT BUTTON_LEFT
524# define SOL_RIGHT BUTTON_RIGHT
525# define SOL_MOVE_PRE BUTTON_SELECT
526# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
527# define SOL_DRAW BUTTON_MENU
528# define SOL_REM2CUR (BUTTON_USER | BUTTON_REPEAT)
529# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
530# define SOL_REM2STACK BUTTON_POWER
531# define HK_MOVE "Select"
532# define HK_DRAW "Menu"
533# define HK_REM2CUR "Long User"
534# define HK_CUR2STACK "Long Select.."
535# define HK_REM2STACK "Power"
536
537#elif (CONFIG_KEYPAD == HM60X_PAD)
538# define SOL_QUIT BUTTON_POWER
539# define SOL_UP BUTTON_UP
540# define SOL_DOWN BUTTON_DOWN
541# define SOL_LEFT BUTTON_LEFT
542# define SOL_RIGHT BUTTON_RIGHT
543# define SOL_MOVE_PRE BUTTON_SELECT
544# define SOL_MOVE (BUTTON_POWER | BUTTON_SELECT)
545# define SOL_DRAW (BUTTON_POWER | BUTTON_UP)
546# define SOL_REM2CUR (BUTTON_POWER | BUTTON_DOWN)
547# define SOL_CUR2STACK (BUTTON_POWER | BUTTON_LEFT)
548# define SOL_REM2STACK (BUTTON_POWER | BUTTON_RIGHT)
549# define HK_MOVE "SELECT + POWER"
550# define HK_DRAW "UP + POWER"
551# define HK_REM2CUR "DOWN + POWER"
552# define HK_CUR2STACK "LEFT + POWER"
553# define HK_REM2STACK "RIGHT + POWER"
554
555#elif (CONFIG_KEYPAD == HM801_PAD)
556# define SOL_QUIT BUTTON_POWER
557# define SOL_UP BUTTON_UP
558# define SOL_DOWN BUTTON_DOWN
559# define SOL_LEFT BUTTON_LEFT
560# define SOL_RIGHT BUTTON_RIGHT
561# define SOL_MOVE_PRE BUTTON_PREV
562# define SOL_MOVE BUTTON_NEXT
563# define SOL_DRAW BUTTON_PLAY
564# define SOL_REM2CUR BUTTON_SELECT
565# define SOL_CUR2STACK (BUTTON_POWER | BUTTON_LEFT)
566# define SOL_REM2STACK (BUTTON_POWER | BUTTON_RIGHT)
567# define HK_MOVE "PREV"
568# define HK_DRAW "PLAY"
569# define HK_REM2CUR "SELECT"
570# define HK_CUR2STACK "POWER + LEFT"
571# define HK_REM2STACK "POWER + RIGHT"
572
573#elif (CONFIG_KEYPAD == SONY_NWZ_PAD)
574#define SOL_QUIT BUTTON_BACK
575#define SOL_UP BUTTON_UP
576#define SOL_DOWN BUTTON_DOWN
577#define SOL_LEFT BUTTON_LEFT
578#define SOL_RIGHT BUTTON_RIGHT
579#define SOL_MOVE BUTTON_PLAY
580#define SOL_DRAW (BUTTON_POWER|BUTTON_UP)
581#define SOL_REM2CUR (BUTTON_POWER|BUTTON_DOWN)
582#define SOL_CUR2STACK (BUTTON_POWER|BUTTON_LEFT)
583#define SOL_REM2STACK (BUTTON_POWER|BUTTON_RIGHT)
584#define HK_MOVE "Play"
585#define HK_DRAW "Option+Up"
586#define HK_REM2CUR "Option+Down"
587#define HK_CUR2STACK "Option+Left"
588#define HK_REM2STACK "Option+Right"
589
590#elif (CONFIG_KEYPAD == AGPTEK_ROCKER_PAD)
591#define SOL_QUIT BUTTON_POWER
592#define SOL_UP BUTTON_UP
593#define SOL_DOWN BUTTON_DOWN
594#define SOL_LEFT BUTTON_LEFT
595#define SOL_RIGHT BUTTON_RIGHT
596#define SOL_MOVE BUTTON_SELECT
597#define SOL_DRAW (BUTTON_VOLUP|BUTTON_UP)
598#define SOL_REM2CUR (BUTTON_VOLUP|BUTTON_DOWN)
599#define SOL_CUR2STACK (BUTTON_VOLUP|BUTTON_LEFT)
600#define SOL_REM2STACK (BUTTON_VOLUP|BUTTON_RIGHT)
601#define HK_MOVE "Select"
602#define HK_DRAW "Option+Up"
603#define HK_REM2CUR "Option+Down"
604#define HK_CUR2STACK "Option+Left"
605#define HK_REM2STACK "Option+Right"
606
607#elif (CONFIG_KEYPAD == CREATIVE_ZEN_PAD)
608#define SOL_QUIT BUTTON_POWER
609#define SOL_UP BUTTON_UP
610#define SOL_DOWN BUTTON_DOWN
611#define SOL_LEFT BUTTON_LEFT
612#define SOL_RIGHT BUTTON_RIGHT
613#define SOL_MOVE BUTTON_SELECT
614#define SOL_DRAW BUTTON_PLAYPAUSE
615#define SOL_REM2CUR BUTTON_BACK
616#define SOL_CUR2STACK BUTTON_MENU
617#define SOL_REM2STACK BUTTON_SHORTCUT
618#define HK_MOVE "Select"
619#define HK_DRAW "Play/pause"
620#define HK_REM2CUR "Back"
621#define HK_CUR2STACK "Menu"
622#define HK_REM2STACK "Shortcut"
623
624#elif (CONFIG_KEYPAD == DX50_PAD)
625# define SOL_QUIT (BUTTON_POWER | BUTTON_REL)
626
627#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
628# define SOL_QUIT BUTTON_POWER
629
630#elif CONFIG_KEYPAD == XDUOO_X3_PAD
631# define SOL_QUIT BUTTON_POWER
632# define SOL_UP BUTTON_HOME
633# define SOL_DOWN BUTTON_OPTION
634# define SOL_LEFT BUTTON_PREV
635# define SOL_RIGHT BUTTON_NEXT
636# define SOL_MOVE_PRE BUTTON_PLAY
637# define SOL_MOVE (BUTTON_PLAY | BUTTON_REL)
638# define SOL_DRAW (BUTTON_POWER | BUTTON_REPEAT)
639# define SOL_REM2CUR BUTTON_VOL_DOWN
640# define SOL_CUR2STACK_PRE BUTTON_PLAY
641# define SOL_CUR2STACK (BUTTON_PLAY | BUTTON_REPEAT)
642# define SOL_REM2STACK BUTTON_VOL_UP
643# define HK_MOVE "PLAY"
644# define HK_DRAW "DBL HOME"
645# define HK_REM2CUR "PREV"
646# define HK_CUR2STACK "DBL PLAY"
647# define HK_REM2STACK "NEXT"
648
649#elif CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD
650# define SOL_QUIT BUTTON_POWER
651# define SOL_UP BUTTON_HOME
652# define SOL_DOWN BUTTON_OPTION
653# define SOL_LEFT BUTTON_PREV
654# define SOL_RIGHT BUTTON_NEXT
655# define SOL_MOVE_PRE BUTTON_PLAY
656# define SOL_MOVE (BUTTON_PLAY | BUTTON_REL)
657# define SOL_DRAW (BUTTON_POWER | BUTTON_REPEAT)
658# define SOL_REM2CUR BUTTON_VOL_DOWN
659# define SOL_CUR2STACK_PRE BUTTON_PLAY
660# define SOL_CUR2STACK (BUTTON_PLAY | BUTTON_REPEAT)
661# define SOL_REM2STACK BUTTON_VOL_UP
662# define HK_MOVE "PLAY"
663# define HK_DRAW "DBL HOME"
664# define HK_REM2CUR "PREV"
665# define HK_CUR2STACK "DBL PLAY"
666# define HK_REM2STACK "NEXT"
667
668#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD
669# define SOL_QUIT BUTTON_POWER
670# define SOL_UP BUTTON_HOME
671# define SOL_DOWN BUTTON_OPTION
672# define SOL_LEFT BUTTON_PREV
673# define SOL_RIGHT BUTTON_NEXT
674# define SOL_MOVE_PRE BUTTON_PLAY
675# define SOL_MOVE (BUTTON_PLAY | BUTTON_REL)
676# define SOL_DRAW (BUTTON_POWER | BUTTON_REPEAT)
677# define SOL_REM2CUR BUTTON_VOL_DOWN
678# define SOL_CUR2STACK_PRE BUTTON_PLAY
679# define SOL_CUR2STACK (BUTTON_PLAY | BUTTON_REPEAT)
680# define SOL_REM2STACK BUTTON_VOL_UP
681# define HK_MOVE "PLAY"
682# define HK_DRAW "DBL HOME"
683# define HK_REM2CUR "PREV"
684# define HK_CUR2STACK "DBL PLAY"
685# define HK_REM2STACK "NEXT"
686
687#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD
688# define SOL_QUIT BUTTON_POWER
689# define SOL_UP BUTTON_PREV
690# define SOL_DOWN BUTTON_NEXT
691# define SOL_LEFT BUTTON_HOME
692# define SOL_RIGHT BUTTON_VOL_DOWN
693# define SOL_MOVE_PRE BUTTON_VOL_UP
694# define SOL_MOVE (BUTTON_PLAY | BUTTON_REL)
695# define SOL_DRAW (BUTTON_POWER | BUTTON_REPEAT)
696# define SOL_REM2CUR (BUTTON_POWER | BUTTON_VOL_DOWN)
697# define SOL_CUR2STACK_PRE BUTTON_PLAY
698# define SOL_CUR2STACK (BUTTON_PLAY | BUTTON_REPEAT)
699# define SOL_REM2STACK (BUTTON_POWER | BUTTON_VOL_UP)
700# define HK_MOVE "PLAY"
701# define HK_DRAW "DBL POWER"
702# define HK_REM2CUR "POWER"
703# define HK_CUR2STACK "DBL PLAY"
704# define HK_REM2STACK "POWER+"
705
706#elif CONFIG_KEYPAD == EROSQ_PAD
707# define SOL_QUIT BUTTON_POWER
708# define SOL_UP BUTTON_PREV
709# define SOL_DOWN BUTTON_NEXT
710# define SOL_LEFT BUTTON_SCROLL_BACK
711# define SOL_RIGHT BUTTON_SCROLL_FWD
712# define SOL_MOVE_PRE BUTTON_PLAY
713# define SOL_MOVE (BUTTON_PLAY | BUTTON_REL)
714# define SOL_DRAW (BUTTON_POWER | BUTTON_REPEAT)
715# define SOL_REM2CUR BUTTON_VOL_DOWN
716# define SOL_CUR2STACK_PRE BUTTON_PLAY
717# define SOL_CUR2STACK (BUTTON_PLAY | BUTTON_REPEAT)
718# define SOL_REM2STACK BUTTON_VOL_UP
719# define HK_MOVE "PLAY"
720# define HK_DRAW "DBL HOME"
721# define HK_REM2CUR "PREV"
722# define HK_CUR2STACK "DBL PLAY"
723# define HK_REM2STACK "NEXT"
724
725#elif CONFIG_KEYPAD == FIIO_M3K_PAD
726# define SOL_QUIT BUTTON_POWER
727# define SOL_UP BUTTON_UP
728# define SOL_DOWN BUTTON_DOWN
729# define SOL_LEFT BUTTON_LEFT
730# define SOL_RIGHT BUTTON_RIGHT
731# define SOL_MOVE_PRE BUTTON_SELECT
732# define SOL_MOVE (BUTTON_SELECT|BUTTON_REL)
733# define SOL_DRAW BUTTON_PLAY
734# define SOL_REM2CUR BUTTON_VOL_DOWN
735# define SOL_CUR2STACK_PRE BUTTON_SELECT
736# define SOL_CUR2STACK (BUTTON_SELECT|BUTTON_REPEAT)
737# define SOL_REM2STACK BUTTON_VOL_UP
738# define HK_MOVE "SELECT"
739# define HK_DRAW "PLAY"
740# define HK_REM2CUR "VOL-"
741# define HK_CUR2STACK "HOLD SELECT"
742# define HK_REM2STACK "VOL+"
743
744#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
745# define SOL_QUIT BUTTON_POWER
746
747#elif CONFIG_PAD == SDL_APP
748# define SOL_QUIT (BUTTON_SELECT | BUTTON_MENU)
749# define SOL_UP BUTTON_UP
750# define SOL_DOWN BUTTON_DOWN
751# define SOL_LEFT_PRE BUTTON_LEFT
752# define SOL_LEFT (BUTTON_LEFT | BUTTON_SELECT)
753# define SOL_RIGHT_PRE BUTTON_RIGHT
754# define SOL_RIGHT (BUTTON_RIGHT | BUTTON_SELECT)
755# define SOL_MOVE_PRE BUTTON_SELECT
756# define SOL_MOVE (BUTTON_SELECT | BUTTON_UP)
757# define SOL_DRAW_PRE BUTTON_MENU
758# define HK_UD "SCROLL U/D"
759# define HK_MOVE "SELECT"
760# define HK_DRAW "MENU"
761# define HK_REM2CUR "PLAY"
762# define HK_CUR2STACK "MENU.."
763# define HK_REM2STACK "RIGHT.."
764#elif CONFIG_KEYPAD == RG_NANO_PAD
765# define SOL_QUIT BUTTON_START
766# define SOL_UP BUTTON_UP
767# define SOL_DOWN BUTTON_DOWN
768# define SOL_LEFT BUTTON_LEFT
769# define SOL_RIGHT BUTTON_RIGHT
770# define SOL_MOVE BUTTON_A
771# define SOL_DRAW BUTTON_X
772# define SOL_REM2CUR BUTTON_L
773# define SOL_CUR2STACK BUTTON_B
774# define SOL_REM2STACK BUTTON_R
775# define HK_MOVE "A"
776# define HK_DRAW "X"
777# define HK_REM2CUR "L"
778# define HK_CUR2STACK "B"
779# define HK_REM2STACK "R"
780
781#elif CONFIG_KEYPAD == MA_PAD
782# define SOL_QUIT (BUTTON_LEFT|BUTTON_REPEAT)
783# define SOL_UP BUTTON_UP
784# define SOL_DOWN BUTTON_DOWN
785# define SOL_LEFT BUTTON_LEFT
786# define SOL_RIGHT BUTTON_RIGHT
787# define SOL_MOVE_PRE BUTTON_MENU
788# define SOL_MOVE (BUTTON_MENU|BUTTON_REPEAT)
789# define SOL_DRAW BUTTON_PLAY
790# define SOL_REM2CUR (BUTTON_MENU|BUTTON_DOWN)
791# define SOL_CUR2STACK_PRE BUTTON_BACK
792# define SOL_CUR2STACK (BUTTON_BACK|BUTTON_REPEAT)
793# define SOL_REM2STACK (BUTTON_MENU|BUTTON_UP)
794# define HK_MOVE "MENU"
795# define HK_DRAW "PLAY"
796# define HK_REM2CUR "MENU+DOWN"
797# define HK_CUR2STACK "BACK"
798# define HK_REM2STACK "MENU+UP"
799
800
801#else
802#error No keymap defined!
803#endif
804
805#ifdef HAVE_TOUCHSCREEN
806//#ifndef SOL_QUIT
807//# define SOL_QUIT BUTTON_TOPLEFT
808//endif
809#ifndef SOL_UP
810# define SOL_UP BUTTON_TOPMIDDLE
811#endif
812#ifndef SOL_DOWN
813# define SOL_DOWN BUTTON_BOTTOMMIDDLE
814#endif
815#ifndef SOL_LEFT
816# define SOL_LEFT BUTTON_MIDLEFT
817#endif
818#ifndef SOL_RIGHT
819# define SOL_RIGHT BUTTON_MIDRIGHT
820#endif
821#ifndef SOL_MOVE
822# define SOL_MOVE BUTTON_CENTER
823# define HK_MOVE "CENTRE"
824#endif
825#ifndef SOL_DRAW
826# define SOL_DRAW BUTTON_TOPLEFT
827# define HK_DRAW "TOPLEFT"
828#endif
829#ifndef SOL_REM2CUR
830# define SOL_REM2CUR BUTTON_TOPRIGHT
831# define HK_REM2CUR "TOPRIGHT"
832#endif
833#ifndef SOL_CUR2STACK
834# define SOL_CUR2STACK BUTTON_BOTTOMLEFT
835# define HK_CUR2STACK "BOTTOMLEFT"
836#endif
837#ifndef SOL_REM2STACK
838# define SOL_REM2STACK BUTTON_BOTTOMRIGHT
839# define HK_REM2STACK "BOTTOMRIGHT"
840#endif
841
842#endif
843
844#ifndef HK_LR
845# define HK_LR "LEFT/RIGHT"
846#endif
847#ifndef HK_UD
848# define HK_UD "UP/DOWN"
849#endif
850
851/**
852 * Misc constants, graphics and other defines
853 */
854
855#include "pluginbitmaps/card_back.h"
856#include "pluginbitmaps/card_deck.h"
857#include "pluginbitmaps/solitaire_suitsi.h"
858
859#define CARD_GFX_WIDTH BMPWIDTH_card_back
860#define CARD_GFX_HEIGHT BMPHEIGHT_card_back
861#define CARD_WIDTH (BMPWIDTH_card_back+2)
862#define CARD_HEIGHT (BMPHEIGHT_card_back+2)
863
864#if LCD_WIDTH >= 640
865# define MARGIN 4
866# define LARGE_CARD
867# define SYMBOL_HEIGHT 24
868
869#elif LCD_WIDTH >= 320
870# define MARGIN 4
871# define LARGE_CARD
872# define SYMBOL_HEIGHT 12
873#elif LCD_WIDTH >= 220
874# define MARGIN 3
875# define LARGE_CARD
876# define SYMBOL_HEIGHT 12
877#elif LCD_WIDTH >= 160
878# define MARGIN 2
879# define SYMBOL_HEIGHT 11
880#elif LCD_WIDTH >= 128
881# define MARGIN 1
882# define SYMBOL_HEIGHT 10
883#else
884# define MARGIN 0
885# define SYMBOL_HEIGHT 8
886#endif
887
888#define CARD_START (CARD_HEIGHT+2*MARGIN+1)
889
890/* background color */
891#ifdef HAVE_LCD_COLOR
892# define BACKGROUND_COLOR LCD_RGBPACK(0,157,0)
893# define FRAME_COLOR LCD_RGBPACK(23,119,218)
894#elif LCD_DEPTH > 1
895# define BACKGROUND_COLOR LCD_WHITE
896# define FRAME_COLOR LCD_BLACK
897#endif
898
899
900#define CONFIG_FILENAME "sol.cfg"
901
902#define NOT_A_CARD -1
903
904/* number of cards per suit */
905#define CARDS_PER_SUIT 13
906
907/* number of suits */
908#define SUITS 4
909
910#define NUM_CARDS ( CARDS_PER_SUIT * SUITS )
911
912/* number of columns */
913#define COL_NUM 7
914
915/* pseudo column numbers to be used for cursor coordinates */
916/* columns COL_NUM to COL_NUM + SUITS - 1 correspond to the final stacks */
917#define STACKS_COL COL_NUM
918/* column COL_NUM + SUITS corresponds to the remains' stack */
919#define REM_COL (STACKS_COL + SUITS)
920
921#define NOT_A_COL -1
922
923#if defined(SOL_LEFT_PRE) || defined(SOL_RIGHT_PRE) || \
924 defined(SOL_DOWN_PRE) || defined(SOL_UP_PRE) || \
925 defined(SOL_CUR2STACK_PRE) || defined(SOL_MOVE_PRE) || \
926 defined(SOL_REM2CUR_PRE) || defined(SOL_REM2STACK_PRE) || \
927 defined(SOL_DRAW_PRE)
928# define NEED_LASTBUTTON_VAR
929#endif
930
931typedef struct
932{
933 signed char suit;
934 signed char num;
935 bool known : 1;
936 bool used : 1; /* this is what is used when dealing cards */
937 signed char next;
938} card_t;
939
940
941/**
942 * LCD card drawing routines
943 */
944
945static void draw_cursor( int x, int y )
946{
947 rb->lcd_set_drawmode( DRMODE_COMPLEMENT );
948 rb->lcd_fillrect( x+1, y+1, CARD_GFX_WIDTH, CARD_GFX_HEIGHT );
949#ifdef LARGE_CARD
950 rb->lcd_drawpixel( x+1, y+1 );
951 rb->lcd_drawpixel( x+1, y+CARD_HEIGHT-2 );
952 rb->lcd_drawpixel( x+CARD_WIDTH-2, y+1 );
953 rb->lcd_drawpixel( x+CARD_WIDTH-2, y+CARD_HEIGHT-2 );
954#endif
955 rb->lcd_set_drawmode( DRMODE_SOLID );
956}
957
958/* Draw a card's border, select it if it's selected and draw the cursor
959 * if the cursor is currently over the card */
960static void draw_card_ext( int x, int y, bool selected, bool cursor )
961{
962#if LCD_DEPTH > 1
963 int oldfg = rb->lcd_get_foreground();
964
965 rb->lcd_set_foreground( LCD_BLACK );
966#endif
967#ifdef LARGE_CARD
968 rb->lcd_hline( x+2, x+CARD_WIDTH-3, y );
969 rb->lcd_hline( x+2, x+CARD_WIDTH-3, y+CARD_HEIGHT-1 );
970 rb->lcd_vline( x, y+2, y+CARD_HEIGHT-3 );
971 rb->lcd_vline( x+CARD_WIDTH-1, y+2, y+CARD_HEIGHT-3 );
972 rb->lcd_drawpixel( x+1, y+1 );
973 rb->lcd_drawpixel( x+1, y+CARD_HEIGHT-2 );
974 rb->lcd_drawpixel( x+CARD_WIDTH-2, y+1 );
975 rb->lcd_drawpixel( x+CARD_WIDTH-2, y+CARD_HEIGHT-2 );
976#else
977 rb->lcd_hline( x+1, x+CARD_WIDTH-2, y );
978 rb->lcd_hline( x+1, x+CARD_WIDTH-2, y+CARD_HEIGHT-1 );
979 rb->lcd_vline( x, y+1, y+CARD_HEIGHT-2 );
980 rb->lcd_vline( x+CARD_WIDTH-1, y+1, y+CARD_HEIGHT-2 );
981#endif
982
983 if( selected )
984 {
985#if LCD_DEPTH > 1
986 rb->lcd_set_foreground( FRAME_COLOR );
987#endif
988 rb->lcd_drawrect( x+1, y+1, CARD_WIDTH-2, CARD_HEIGHT-2 );
989#ifdef LARGE_CARD
990 rb->lcd_drawrect( x+2, y+2, CARD_WIDTH-4, CARD_HEIGHT-4 );
991#endif
992 }
993#if LCD_DEPTH > 1
994 rb->lcd_set_foreground( oldfg );
995#endif
996
997 if( cursor )
998 {
999 draw_cursor( x, y );
1000 }
1001}
1002
1003/* Draw a card's inner graphics */
1004static void draw_card( card_t *card, int x, int y,
1005 bool selected, bool cursor )
1006{
1007 if( card->known )
1008 {
1009 rb->lcd_bitmap_part( card_deck, CARD_GFX_WIDTH * card->num,
1010 CARD_GFX_HEIGHT * card->suit,
1011 STRIDE(SCREEN_MAIN,
1012 BMPWIDTH_card_deck, BMPHEIGHT_card_deck),
1013 x+1, y+1, CARD_GFX_WIDTH, CARD_GFX_HEIGHT );
1014 }
1015 else
1016 {
1017 rb->lcd_bitmap( card_back, x+1, y+1,
1018 CARD_GFX_WIDTH, CARD_GFX_HEIGHT );
1019 }
1020 draw_card_ext( x, y, selected, cursor );
1021}
1022
1023/* Draw an empty stack */
1024static void draw_empty_stack( int s, int x, int y, bool cursor )
1025{
1026 rb->lcd_bitmap_part( solitaire_suitsi, 0,
1027 CARD_GFX_HEIGHT * s,
1028 STRIDE( SCREEN_MAIN,
1029 BMPWIDTH_solitaire_suitsi, BMPHEIGHT_solitaire_suitsi),
1030 x+1, y+1, CARD_GFX_WIDTH, CARD_GFX_HEIGHT );
1031
1032 draw_card_ext( x, y, false, cursor );
1033}
1034
1035/* Help */
1036static bool solitaire_help( void )
1037{
1038 static char* help_text[] = {
1039 "Solitaire", "", "Controls", "",
1040 HK_LR ":", "Move", "the", "cursor", "to", "the",
1041 "previous/", "next", "column.", "",
1042 HK_UD ":", "Move", "the", "cursor", "up/", "down", "in", "the",
1043 "column.", "",
1044 HK_MOVE ":", "Select", "cards,", "move", "cards...", "",
1045 HK_DRAW ":", "Deselect", "a", "card", "if", "it", "was", "selected.",
1046 "Else", "draw", "new", "card(s)", "from", "the", "remains",
1047 "stack.", "", "",
1048 "Shortcuts", "",
1049 HK_REM2CUR ":", "Put", "the", "card", "on", "top", "of", "the",
1050 "remains", "stack", "on", "top", "of", "the", "cursor.", "",
1051 HK_CUR2STACK ":", "Put", "the", "card", "under", "the", "cursor",
1052 "on", "one", "of", "the", "4", "final", "stacks.", "",
1053 HK_REM2STACK ":", "Put", "the", "card", "on", "top", "of", "the",
1054 "remains", "stack", "on", "one", "of", "the", "4", "final",
1055 "stacks."
1056 };
1057 static struct style_text formation[]={
1058 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1059 { 2, C_RED },
1060 { 48, C_RED },
1061 LAST_STYLE_ITEM
1062 };
1063 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
1064 return true;
1065 return false;
1066}
1067
1068/**
1069 * Custom menu / options
1070 */
1071
1072#define CFGFILE_VERSION 0
1073
1074struct sol_config {
1075 int draw_type;
1076};
1077
1078struct sol_config sol_disk = {0};
1079struct sol_config sol;
1080
1081static struct configdata config[] = {
1082 { TYPE_INT, 0, 1, { .int_p = &sol_disk.draw_type }, "draw_type", NULL }
1083};
1084
1085static const struct opt_items drawcards[2] = {
1086 { "Draw Three Cards", -1 },
1087 { "Draw One Card", -1 },
1088};
1089
1090void solitaire_init(void);
1091
1092/* menu return codes */
1093enum { MENU_RESUME, MENU_SAVE_AND_QUIT, MENU_QUIT, MENU_USB };
1094
1095static bool _ingame;
1096static int solitaire_menu_cb(int action,
1097 const struct menu_item_ex *this_item,
1098 struct gui_synclist *this_list)
1099{
1100 (void)this_list;
1101 int i = (intptr_t)this_item;
1102 if( action == ACTION_REQUEST_MENUITEM )
1103 {
1104 if((!_ingame && (i==0 || i==5)) || ( _ingame && i==2 ))
1105 return ACTION_EXIT_MENUITEM;
1106 }
1107 return action;
1108}
1109
1110static int solitaire_menu(bool in_game)
1111{
1112 int selected = 0;
1113 int result = -1;
1114
1115 MENUITEM_STRINGLIST(menu, "Solitaire Menu", solitaire_menu_cb,
1116 "Resume Game", "Start New Game",
1117 "Draw Cards Option",
1118 "Help", "Playback Control",
1119 "Quit without Saving", "Quit");
1120 _ingame = in_game;
1121
1122 while (result < 0)
1123 {
1124 switch (rb->do_menu(&menu, &selected, NULL, false))
1125 {
1126 default:
1127 result = MENU_RESUME;
1128 break;
1129
1130 case MENU_ATTACHED_USB:
1131 result = MENU_USB;
1132 break;
1133
1134 case 0:
1135 result = MENU_RESUME;
1136 break;
1137
1138 case 1:
1139 solitaire_init();
1140 result = MENU_RESUME;
1141 break;
1142
1143 case 2:
1144 if (rb->set_option("Draw Cards Option", &sol.draw_type,
1145 RB_INT, drawcards, 2, NULL))
1146 result = MENU_USB;
1147 break;
1148
1149 case 3:
1150 if (solitaire_help())
1151 result = MENU_USB;
1152 break;
1153
1154 case 4:
1155 playback_control(NULL);
1156 break;
1157
1158 case 5:
1159 result = MENU_QUIT;
1160 break;
1161
1162 case 6:
1163 result = MENU_SAVE_AND_QUIT;
1164 break;
1165 }
1166 }
1167 return result;
1168}
1169
1170/**
1171 * Global variables
1172 */
1173
1174/* player's cursor */
1175int cur_card;
1176/* player's cursor column num */
1177int cur_col;
1178
1179/* selected card */
1180int sel_card;
1181
1182/* the deck */
1183card_t deck[ NUM_CARDS ];
1184
1185/* the remaining cards */
1186/* first card of the remains' stack */
1187int rem;
1188/* upper visible card from the remains' stack */
1189int cur_rem;
1190/* number of cards drawn from the remains stack - 1 */
1191int count_rem;
1192/* number of cards per draw of the remains' stack */
1193int cards_per_draw;
1194
1195/* the 7 game columns */
1196int cols[COL_NUM];
1197/* the 4 final stacks */
1198int stacks[SUITS];
1199
1200/**
1201 * Card handling routines
1202 */
1203
1204static int next_random_card( card_t *deck )
1205{
1206 int i,r;
1207
1208 r = rb->rand()%(NUM_CARDS)+1;
1209 i = 0;
1210
1211 while( r>0 )
1212 {
1213 i = (i + 1)%(NUM_CARDS);
1214 if( !deck[i].used ) r--;
1215 }
1216
1217 deck[i].used = true;
1218
1219 return i;
1220}
1221
1222
1223/* initialize the game */
1224void solitaire_init( void )
1225{
1226
1227 int c;
1228 int i, j;
1229
1230 /* number of cards that are drawn on the remains' stack (by pressing F2) */
1231 if( sol.draw_type == 0 )
1232 {
1233 cards_per_draw = 3;
1234 }
1235 else
1236 {
1237 cards_per_draw = 1;
1238 }
1239
1240 /* init deck */
1241 for( i=0; i<SUITS; i++ )
1242 {
1243 for( j=0; j<CARDS_PER_SUIT; j++ )
1244 {
1245#define card deck[i*CARDS_PER_SUIT+j]
1246 card.suit = i;
1247 card.num = j;
1248 card.known = true;
1249 card.used = false;
1250 card.next = NOT_A_CARD;
1251#undef card
1252 }
1253 }
1254
1255 /* deal the cards ... */
1256 /* ... in the columns */
1257 for( i=0; i<COL_NUM; i++ )
1258 {
1259 c = NOT_A_CARD;
1260 for( j=0; j<=i; j++ )
1261 {
1262 if( c == NOT_A_CARD )
1263 {
1264 cols[i] = next_random_card( deck );
1265 c = cols[i];
1266 }
1267 else
1268 {
1269 deck[c].next = next_random_card( deck );
1270 c = deck[c].next;
1271 }
1272 if( j < i )
1273 deck[c].known = false;
1274 }
1275 }
1276
1277 /* ... shuffle what's left of the deck */
1278 rem = next_random_card(deck);
1279 c = rem;
1280
1281 for( i=1; i < NUM_CARDS - COL_NUM * (COL_NUM + 1)/2; i++ )
1282 {
1283 deck[c].next = next_random_card( deck );
1284 c = deck[c].next;
1285 }
1286
1287 /* we now finished dealing the cards. The game can start ! (at last) */
1288
1289 /* init the stack */
1290 for( i = 0; i<SUITS; i++ )
1291 {
1292 stacks[i] = NOT_A_CARD;
1293 }
1294
1295 /* the cursor starts on upper left card */
1296 cur_card = cols[0];
1297 cur_col = 0;
1298
1299 /* no card is selected */
1300 sel_card = NOT_A_CARD;
1301
1302 /* init the remainder */
1303 cur_rem = NOT_A_CARD;
1304
1305 count_rem = -1;
1306}
1307
1308/* find the column number in which 'card' can be found */
1309static int find_card_col( int card )
1310{
1311 int i;
1312 int c;
1313
1314 if( card == NOT_A_CARD ) return NOT_A_COL;
1315
1316 for( i=0; i<COL_NUM; i++ )
1317 {
1318 c = cols[i];
1319 while( c != NOT_A_CARD )
1320 {
1321 if( c == card ) return i;
1322 c = deck[c].next;
1323 }
1324 }
1325
1326 for( i=0; i<SUITS; i++ )
1327 {
1328 c = stacks[i];
1329 while( c != NOT_A_CARD )
1330 {
1331 if( c == card ) return STACKS_COL + i;
1332 c = deck[c].next;
1333 }
1334 }
1335
1336 return REM_COL;
1337}
1338
1339/* find the card preceding 'card' */
1340/* if it doesn't exist, return NOT_A_CARD */
1341static int find_prev_card( int card ){
1342 int i;
1343
1344 for( i=0; i < NUM_CARDS; i++ )
1345 {
1346 if( deck[i].next == card ) return i;
1347 }
1348
1349 return NOT_A_CARD;
1350}
1351
1352/* find the last card of a given column */
1353static int find_last_card( int col )
1354{
1355 int c;
1356
1357 if( col < COL_NUM )
1358 {
1359 c = cols[col];
1360 }
1361 else if( col < REM_COL )
1362 {
1363 c = stacks[col - STACKS_COL];
1364 }
1365 else
1366 {
1367 c = cur_rem;
1368 }
1369
1370 if(c == NOT_A_CARD)
1371 return c;
1372 else
1373 {
1374 while(deck[c].next != NOT_A_CARD)
1375 c = deck[c].next;
1376 return c;
1377 }
1378}
1379
1380enum move { MOVE_OK, MOVE_NOT_OK };
1381
1382static enum move move_card( int dest_col, int src_card )
1383{
1384 /* the column on which to take src_card */
1385 int src_col;
1386
1387 /* the last card of dest_col */
1388 int dest_card;
1389
1390 /* the card under src_card */
1391 int src_card_prev;
1392
1393 /* you can't move no card (at least, it doesn't have any consequence) */
1394 if( src_card == NOT_A_CARD ) return MOVE_NOT_OK;
1395 /* you can't put a card back on the remains' stack */
1396 if( dest_col == REM_COL ) return MOVE_NOT_OK;
1397 /* you can't move an unknown card */
1398 if( !deck[src_card].known ) return MOVE_NOT_OK;
1399
1400 src_col = find_card_col( src_card );
1401 dest_card = find_last_card( dest_col );
1402 src_card_prev = find_prev_card( src_card );
1403
1404 /* you can't move more than one card at a time from the final stack */
1405 /* to the rest of the game */
1406 if( src_col >= COL_NUM && src_col < REM_COL
1407 && deck[src_card].next != NOT_A_CARD )
1408 {
1409 return MOVE_NOT_OK;
1410 }
1411
1412 /* if we (that means dest) are on one of the 7 columns ... */
1413 if( dest_col < COL_NUM )
1414 {
1415 /* ... check is we are on an empty color and that the src is a king */
1416 if( dest_card == NOT_A_CARD
1417 && deck[src_card].num == CARDS_PER_SUIT - 1 )
1418 {
1419 /* this is a winning combination */
1420 cols[dest_col] = src_card;
1421 }
1422 /* ... or check if the cards follow one another and have
1423 * different colorsuit */
1424 else if(( deck[dest_card].suit + deck[src_card].suit)%2==1
1425 && deck[dest_card].num == deck[src_card].num + 1 )
1426 {
1427 /* this is a winning combination */
1428 deck[dest_card].next = src_card;
1429 }
1430 /* ... or, humpf, well that's not good news */
1431 else
1432 {
1433 /* this is not a winning combination */
1434 return MOVE_NOT_OK;
1435 }
1436 }
1437 /* if we are on one of the 4 final stacks ... */
1438 else if( dest_col < REM_COL )
1439 {
1440 /* ... check if we are on an empty stack... */
1441 if( dest_card == NOT_A_CARD )
1442 {
1443 /* ... and the src is an ace and this is the correct final stack */
1444 if( deck[src_card].num == 0
1445 && deck[src_card].suit == dest_col - STACKS_COL )
1446 {
1447 /* this is a winning combination */
1448 stacks[dest_col - STACKS_COL] = src_card;
1449 }
1450 else
1451 {
1452 /* this is not a winning combination */
1453 return MOVE_NOT_OK;
1454 }
1455 }
1456 else /* non-empty stack */
1457 {
1458 /* ...check if the cards follow one another, have the same suit and
1459 * {that src has no .next element or is from the remains' stack} */
1460 if( deck[dest_card].suit == deck[src_card].suit
1461 && deck[dest_card].num + 1 == deck[src_card].num
1462 && (deck[src_card].next == NOT_A_CARD || src_col == REM_COL) )
1463 {
1464 /* this is a winning combination */
1465 deck[dest_card].next = src_card;
1466 }
1467 /* ... or, well that's not good news */
1468 else
1469 {
1470 /* this is not a winning combination */
1471 return MOVE_NOT_OK;
1472 }
1473 }
1474 }
1475 /* if we are on the remains' stack */
1476 else
1477 {
1478 /* you can't move a card back to the remains' stack */
1479 return MOVE_NOT_OK;
1480 }
1481
1482 /* if the src card is from the remains' stack, we don't want to take
1483 * the following cards */
1484 if( src_col == REM_COL )
1485 {
1486 /* if src card is the first card from the stack */
1487 if( src_card_prev == NOT_A_CARD )
1488 {
1489 rem = deck[src_card].next;
1490 }
1491 /* if src card is not the first card from the stack */
1492 else
1493 {
1494 deck[src_card_prev].next = deck[src_card].next;
1495 }
1496 deck[src_card].next = NOT_A_CARD;
1497 cur_rem = src_card_prev;
1498 count_rem--;
1499 }
1500 /* if the src card is from somewhere else, just take everything */
1501 else
1502 {
1503 if( src_card_prev == NOT_A_CARD )
1504 {
1505 if( src_col < COL_NUM )
1506 {
1507 cols[src_col] = NOT_A_CARD;
1508 }
1509 else
1510 {
1511 stacks[src_col - STACKS_COL] = NOT_A_CARD;
1512 }
1513 }
1514 else
1515 {
1516 deck[src_card_prev].next = NOT_A_CARD;
1517 deck[src_card_prev].known = true;
1518 }
1519 }
1520 sel_card = NOT_A_CARD;
1521 /* tada ! */
1522 return MOVE_OK;
1523}
1524
1525enum { SOLITAIRE_WIN, SOLITAIRE_SAVE_AND_QUIT, SOLITAIRE_QUIT, SOLITAIRE_USB };
1526
1527/**
1528 * Bouncing cards at the end of the game
1529 */
1530
1531#define BC_ACCEL ((1<<16)*LCD_HEIGHT/128)
1532#define BC_MYSPEED (6*BC_ACCEL)
1533#define BC_MXSPEED (6*LCD_HEIGHT/128)
1534
1535static int bouncing_cards( void )
1536{
1537 int i, j, x, vx, y, fp_y, fp_vy, button;
1538
1539 /* flush the button queue */
1540 while( ( button = rb->button_get( false ) ) != BUTTON_NONE )
1541 {
1542 if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
1543 return SOLITAIRE_USB;
1544 }
1545
1546 /* fun stuff :) */
1547 for( i = CARDS_PER_SUIT-1; i>=0; i-- )
1548 {
1549 for( j = 0; j < SUITS; j++ )
1550 {
1551 x = LCD_WIDTH-(CARD_WIDTH*4+4+MARGIN)+CARD_WIDTH*j+j+1;
1552 fp_y = MARGIN<<16;
1553
1554#if LCD_WIDTH > 200
1555 vx = rb->rand() % (4*BC_MXSPEED/3-2) - BC_MXSPEED;
1556 if( vx >= -1 )
1557 vx += 3;
1558#else
1559 vx = rb->rand() % (4*BC_MXSPEED/3) - BC_MXSPEED;
1560 if( vx >= 0 )
1561 vx++;
1562#endif
1563
1564 fp_vy = -rb->rand() % BC_MYSPEED;
1565
1566 while( x < LCD_WIDTH && x + CARD_WIDTH > 0 )
1567 {
1568 fp_vy += BC_ACCEL;
1569 x += vx;
1570 fp_y += fp_vy;
1571 if( fp_y >= (LCD_HEIGHT-CARD_HEIGHT) << 16 )
1572 {
1573 fp_vy = -fp_vy*4/5;
1574 fp_y = (LCD_HEIGHT-CARD_HEIGHT) << 16;
1575 }
1576 y = fp_y >> 16;
1577 draw_card( &deck[j*CARDS_PER_SUIT+i], x, y,
1578 false, false );
1579 rb->lcd_update_rect( x<0?0:x, y<0?0:y,
1580 CARD_WIDTH, CARD_HEIGHT );
1581
1582 button = rb->button_get_w_tmo( 2 );
1583 if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
1584 return SOLITAIRE_USB;
1585 if( button == SOL_QUIT || button == SOL_MOVE )
1586 return SOLITAIRE_WIN;
1587 }
1588 }
1589 }
1590 return SOLITAIRE_WIN;
1591}
1592
1593/**
1594 * Game save/load routines
1595 */
1596static void get_save_filename( char *buf )
1597{
1598#ifdef APPLICATION
1599 rb->snprintf(buf, sizeof(buf), PLUGIN_DATA_DIR "/sol.save");
1600#else
1601 char *s;
1602 rb->strcpy( buf, rb->plugin_get_current_filename() );
1603 s = rb->strrchr( buf, '/' ) + 1;
1604 *s = '\0';
1605 rb->strcat( s, "sol.save" );
1606#endif
1607}
1608
1609static int open_save_file( int flags )
1610{
1611 char buf[MAX_PATH];
1612 get_save_filename( buf );
1613 return rb->open( buf, flags, 0666);
1614}
1615
1616static void delete_save_file( void )
1617{
1618 char buf[MAX_PATH];
1619 get_save_filename( buf );
1620 rb->remove( buf );
1621}
1622
1623#ifdef write
1624# undef write
1625#endif
1626static int save_write( int fd, const void *buf, size_t count, int *checksum )
1627{
1628 size_t i;
1629 if( rb->write( fd, buf, count ) < (ssize_t)count )
1630 return 1;
1631 for( i = 0; i < count; i++ )
1632 *checksum += (int)(((const char *)buf)[i]);
1633 return 0;
1634}
1635
1636#ifdef read
1637# undef read
1638#endif
1639static int save_read( int fd, void *buf, size_t count, int *checksum )
1640{
1641 size_t i;
1642 if( rb->read( fd, buf, count ) < (ssize_t)count )
1643 return 1;
1644 for( i = 0; i < count; i++ )
1645 *checksum -= (int)(((const char *)buf)[i]);
1646 return 0;
1647}
1648
1649static int save_game( void )
1650{
1651 int fd = open_save_file( O_CREAT|O_WRONLY|O_TRUNC );
1652 int checksum = 42;
1653 if( fd < 0 )
1654 return -1;
1655 if( save_write( fd, &cur_card, sizeof( int ), &checksum )
1656 || save_write( fd, &cur_col, sizeof( int ), &checksum )
1657 || save_write( fd, &sel_card, sizeof( int ), &checksum )
1658 || save_write( fd, deck, NUM_CARDS * sizeof( card_t ), &checksum )
1659 || save_write( fd, &rem, sizeof( int ), &checksum )
1660 || save_write( fd, &cur_rem, sizeof( int ), &checksum )
1661 || save_write( fd, &count_rem, sizeof( int ), &checksum )
1662 || save_write( fd, &cards_per_draw, sizeof( int ), &checksum )
1663 || save_write( fd, cols, COL_NUM * sizeof( int ), &checksum )
1664 || save_write( fd, stacks, SUITS * sizeof( int ), &checksum )
1665 || ( rb->write( fd, &checksum, sizeof( int ) ) < (ssize_t)(sizeof( int ) ) ) )
1666 {
1667 rb->close( fd );
1668 rb->splash( 2*HZ, "Error while saving game. Aborting." );
1669 return -2;
1670 }
1671 rb->close( fd );
1672 return 0;
1673}
1674
1675static int load_game( void )
1676{
1677 int checksum, retval;
1678
1679 int fd = open_save_file( O_RDONLY );
1680 if( fd < 0 )
1681 return -1;
1682
1683 retval = 0; /* Assume good case */
1684 if( ( rb->lseek( fd, -(off_t)sizeof( int ), SEEK_END ) == -((ssize_t)sizeof( int ))-1 )
1685 || ( rb->read( fd, &checksum, sizeof( int ) ) < ((ssize_t)sizeof( int )) )
1686 || ( rb->lseek( fd, 0, SEEK_SET ) == -1 )
1687 || save_read( fd, &cur_card, sizeof( int ), &checksum )
1688 || save_read( fd, &cur_col, sizeof( int ), &checksum )
1689 || save_read( fd, &sel_card, sizeof( int ), &checksum )
1690 || save_read( fd, deck, NUM_CARDS * sizeof( card_t ), &checksum )
1691 || save_read( fd, &rem, sizeof( int ), &checksum )
1692 || save_read( fd, &cur_rem, sizeof( int ), &checksum )
1693 || save_read( fd, &count_rem, sizeof( int ), &checksum )
1694 || save_read( fd, &cards_per_draw, sizeof( int ), &checksum )
1695 || save_read( fd, cols, COL_NUM * sizeof( int ), &checksum )
1696 || save_read( fd, stacks, SUITS * sizeof( int ), &checksum ) )
1697 {
1698 rb->splash( 2*HZ, "Error while loading saved game. Aborting." );
1699 retval = -2;
1700 }
1701 else if( checksum != 42 )
1702 {
1703 rb->splash( 2*HZ, "Save file was corrupted. Aborting." );
1704 retval = -3;
1705 }
1706
1707 rb->close( fd );
1708 delete_save_file();
1709 return retval;
1710}
1711
1712/**
1713 * The main game loop
1714 *
1715 * If skipmenu is defined to SOLITAIRE_QUIT, the menu will be skipped and
1716 * game will resume.
1717 */
1718
1719static int solitaire( int skipmenu )
1720{
1721
1722 int i,j;
1723 int button;
1724#ifdef NEED_LASTBUTTON_VAR
1725 int lastbutton = 0;
1726#endif
1727 int c,h,prevcard;
1728 int biggest_col_length;
1729
1730 rb->srand( *rb->current_tick );
1731 if( skipmenu != SOLITAIRE_QUIT )
1732 {
1733 switch( solitaire_menu(false) )
1734 {
1735 case MENU_SAVE_AND_QUIT:
1736 return SOLITAIRE_QUIT;
1737
1738 case MENU_USB:
1739 return SOLITAIRE_USB;
1740 }
1741 solitaire_init();
1742 }
1743
1744 while( true )
1745 {
1746 rb->lcd_clear_display();
1747
1748 /* get the biggest column length so that display can be "optimized" */
1749 biggest_col_length = 0;
1750
1751 for(i=0;i<COL_NUM;i++)
1752 {
1753 j = 0;
1754 c = cols[i];
1755
1756 if( c != NOT_A_CARD )
1757 {
1758 while( true )
1759 {
1760 /* don't include the last card in the column length. */
1761 if( deck[c].next == NOT_A_CARD )
1762 {
1763 break; /* no successor: get outta here. */
1764 }
1765 else
1766 {
1767 if( deck[c].known )
1768 j += 2;
1769 else
1770 j++;
1771 }
1772 c = deck[c].next;
1773 }
1774 /* make column distinguishable from an empty column,
1775 * and avoid division by zero while displaying */
1776 if( j == 0 )
1777 j = 1;
1778 }
1779 if( j > biggest_col_length )
1780 biggest_col_length = j;
1781 }
1782
1783 /* check if there are cards remaining in the game. */
1784 /* if there aren't any, that means you won :) */
1785 if( biggest_col_length == 0 && rem == NOT_A_CARD )
1786 {
1787 rb->lcd_update();
1788 rb->splash( HZ, "You Won :)" );
1789 return bouncing_cards();
1790 }
1791
1792 /* draw the columns */
1793 for( i = 0; i < COL_NUM; i++ )
1794 {
1795 c = cols[i];
1796 j = CARD_START;
1797 while( true )
1798 {
1799 if( c == NOT_A_CARD )
1800 {
1801 /* draw the cursor on empty columns */
1802 if( cur_col == i )
1803 {
1804 draw_cursor( MARGIN + i * (CARD_WIDTH
1805 +(LCD_WIDTH-COL_NUM*CARD_WIDTH-2*MARGIN)/(COL_NUM-1)),
1806 j );
1807 }
1808 break;
1809 }
1810
1811 draw_card( &deck[c], MARGIN + i * (CARD_WIDTH
1812 +(LCD_WIDTH-COL_NUM*CARD_WIDTH-2*MARGIN)/(COL_NUM-1)),
1813 j, c == sel_card, c == cur_card );
1814
1815 h = c;
1816 c = deck[c].next;
1817 if( c == NOT_A_CARD ) break;
1818
1819 /* This is where we change the spacing between cards so that
1820 * they don't overflow out of the LCD */
1821 if( h == cur_card )
1822 j += SYMBOL_HEIGHT;
1823 else if( deck[h].known )
1824 j += min( SYMBOL_HEIGHT,
1825 2 * (LCD_HEIGHT - CARD_START - CARD_HEIGHT - MARGIN)
1826 / biggest_col_length );
1827 else
1828 j += min( SYMBOL_HEIGHT / 2,
1829 (LCD_HEIGHT - CARD_START - CARD_HEIGHT - MARGIN)
1830 / biggest_col_length );
1831 }
1832 }
1833
1834 /* draw the stacks */
1835 for( i=0; i<SUITS; i++ )
1836 {
1837 c = find_last_card( STACKS_COL + i );
1838
1839 if( c != NOT_A_CARD )
1840 {
1841 draw_card( &deck[c],
1842 LCD_WIDTH-(CARD_WIDTH*4+4+MARGIN)+CARD_WIDTH*i+i+1,
1843 MARGIN,
1844 c == sel_card, cur_col == STACKS_COL + i );
1845 }
1846 else
1847 {
1848 draw_empty_stack( i,
1849 LCD_WIDTH-(CARD_WIDTH*4+4+MARGIN)+CARD_WIDTH*i+i+1,
1850 MARGIN, cur_col == STACKS_COL + i );
1851 }
1852 }
1853
1854 /* draw the remains */
1855 if( rem != NOT_A_CARD &&
1856 ( cur_rem == NOT_A_CARD || deck[cur_rem].next != NOT_A_CARD ) )
1857 {
1858 /* gruik ! (we want to display a card back) */
1859 deck[rem].known = false;
1860 draw_card( &deck[rem], MARGIN, MARGIN, false, false );
1861 deck[rem].known = true;
1862 }
1863
1864 if( rem != NOT_A_CARD && cur_rem != NOT_A_CARD )
1865 {
1866 if( count_rem < 0 )
1867 {
1868 prevcard = rem;
1869 count_rem = 0;
1870 while( prevcard != cur_rem && count_rem < cards_per_draw-1 )
1871 {
1872 prevcard = deck[prevcard].next;
1873 count_rem++;
1874 }
1875 }
1876 prevcard = cur_rem;
1877 j = CARD_WIDTH+2*MARGIN+1;
1878 for( i = 0; i < count_rem; i++ )
1879 prevcard = find_prev_card(prevcard);
1880 for( i = 0; i <= count_rem; i++ )
1881 {
1882 draw_card( &deck[prevcard], j,
1883 MARGIN, sel_card == prevcard,
1884 cur_card == prevcard );
1885 prevcard = deck[prevcard].next;
1886 j += CARD_WIDTH/2;
1887 }
1888 }
1889 if( ( cur_rem == NOT_A_CARD || rem == NOT_A_CARD )
1890 && cur_col == REM_COL )
1891 {
1892 draw_cursor( CARD_WIDTH+2*MARGIN+1, MARGIN );
1893 }
1894
1895 rb->lcd_update();
1896
1897 /* what to do when a key is pressed ... */
1898 button = rb->button_get( true );
1899#if (CONFIG_KEYPAD == SANSA_E200_PAD) || (CONFIG_KEYPAD == SANSA_FUZE_PAD)
1900 if (button&(BUTTON_SCROLL_BACK|BUTTON_SCROLL_FWD))
1901 button = button & (~BUTTON_REPEAT);
1902#endif
1903 switch( button )
1904 {
1905 /* move cursor to the last card of the previous column
1906 * or to the previous final stack
1907 * or to the remains stack */
1908 case SOL_RIGHT:
1909#ifdef SOL_RIGHT_PRE
1910 if( lastbutton != SOL_RIGHT_PRE )
1911 break;
1912#endif
1913 if( cur_col >= COL_NUM )
1914 {
1915 cur_col = 0;
1916 }
1917 else if( cur_col == COL_NUM - 1 )
1918 {
1919 cur_col = REM_COL;
1920 }
1921 else
1922 {
1923 cur_col = (cur_col+1)%(REM_COL+1);
1924 }
1925 if(cur_col == REM_COL)
1926 {
1927 cur_card = cur_rem;
1928 break;
1929 }
1930 cur_card = find_last_card( cur_col );
1931 break;
1932
1933 /* move cursor to the last card of the next column
1934 * or to the next final stack
1935 * or to the remains stack */
1936 case SOL_LEFT:
1937#ifdef SOL_LEFT_PRE
1938 if( lastbutton != SOL_LEFT_PRE )
1939 break;
1940#endif
1941 if( cur_col == 0 )
1942 {
1943 cur_col = REM_COL;
1944 }
1945 else if( cur_col >= COL_NUM )
1946 {
1947 cur_col = COL_NUM - 1;
1948 }
1949 else
1950 {
1951 cur_col = (cur_col + REM_COL)%(REM_COL+1);
1952 }
1953 if( cur_col == REM_COL )
1954 {
1955 cur_card = cur_rem;
1956 break;
1957 }
1958 cur_card = find_last_card( cur_col );
1959 break;
1960
1961 /* move cursor to card that's bellow */
1962 case SOL_DOWN:
1963#ifdef SOL_DOWN_PRE
1964 if( lastbutton != SOL_DOWN_PRE )
1965 break;
1966#else
1967 case SOL_DOWN|BUTTON_REPEAT:
1968#endif
1969 if( cur_col >= COL_NUM )
1970 {
1971 cur_col = (cur_col - COL_NUM + 1)%(SUITS + 1) + COL_NUM;
1972 if( cur_col == REM_COL )
1973 {
1974 cur_card = cur_rem;
1975 }
1976 else
1977 {
1978 cur_card = find_last_card( cur_col );
1979 }
1980 break;
1981 }
1982 if( cur_card == NOT_A_CARD ) break;
1983 if( deck[cur_card].next != NOT_A_CARD )
1984 {
1985 cur_card = deck[cur_card].next;
1986 }
1987 else
1988 {
1989 cur_card = cols[cur_col];
1990 while( !deck[ cur_card].known
1991 && deck[cur_card].next != NOT_A_CARD )
1992 {
1993 cur_card = deck[cur_card].next;
1994 }
1995 }
1996 break;
1997
1998 /* move cursor to card that's above */
1999 case SOL_UP:
2000#ifdef SOL_UP_PRE
2001 if( lastbutton != SOL_UP_PRE )
2002 break;
2003#else
2004 case SOL_UP|BUTTON_REPEAT:
2005#endif
2006 if( cur_col >= COL_NUM )
2007 {
2008 cur_col = (cur_col - COL_NUM + SUITS)%(SUITS + 1) + COL_NUM;
2009 if( cur_col == REM_COL )
2010 {
2011 cur_card = cur_rem;
2012 }
2013 else
2014 {
2015 cur_card = find_last_card( cur_col );
2016 }
2017 break;
2018 }
2019 if( cur_card == NOT_A_CARD ) break;
2020 do {
2021 cur_card = find_prev_card( cur_card );
2022 if( cur_card == NOT_A_CARD )
2023 {
2024 cur_card = find_last_card( cur_col );
2025 }
2026 } while( deck[cur_card].next != NOT_A_CARD
2027 && !deck[cur_card].known );
2028 break;
2029
2030 /* Try to put card under cursor on one of the stacks */
2031 case SOL_CUR2STACK:
2032#ifdef SOL_CUR2STACK_PRE
2033 if( lastbutton != SOL_CUR2STACK_PRE )
2034 break;
2035#endif
2036 move_card( deck[cur_card].suit + STACKS_COL, cur_card );
2037 break;
2038
2039 /* Move cards arround, Uncover cards, ... */
2040 case SOL_MOVE:
2041#ifdef SOL_MOVE_PRE
2042 if( lastbutton != SOL_MOVE_PRE )
2043 break;
2044#endif
2045
2046 if( sel_card == NOT_A_CARD )
2047 {
2048 if( cur_card != NOT_A_CARD )
2049 {
2050 if( deck[cur_card].next == NOT_A_CARD
2051 && !deck[cur_card].known )
2052 {
2053 /* reveal a hidden card */
2054 deck[cur_card].known = true;
2055 }
2056 else if( cur_col == REM_COL && cur_rem == NOT_A_CARD )
2057 {
2058 break;
2059 }
2060 else
2061 {
2062 /* select a card */
2063 sel_card = cur_card;
2064 }
2065 }
2066 }
2067 else if( sel_card == cur_card )
2068 {
2069 /* unselect card or try putting card on
2070 * one of the 4 stacks */
2071 if( move_card( deck[sel_card].suit + COL_NUM, sel_card )
2072 == MOVE_OK && cur_col == REM_COL )
2073 {
2074 cur_card = cur_rem;
2075 }
2076 sel_card = NOT_A_CARD;
2077 }
2078 else
2079 {
2080 /* try moving cards */
2081 /* The flexible move must not be used from the remains stack. */
2082 if (find_card_col(sel_card) == REM_COL)
2083 {
2084 if (move_card( cur_col, sel_card ) == MOVE_NOT_OK)
2085 sel_card = NOT_A_CARD;
2086 }
2087 else
2088 {
2089 do {
2090 if (move_card( cur_col, sel_card) == MOVE_OK)
2091 break;
2092 sel_card = find_prev_card(sel_card);
2093 } while (sel_card != NOT_A_CARD);
2094 }
2095 }
2096 break;
2097
2098 /* If the card on the top of the remains can be put where
2099 * the cursor is, go ahead */
2100 case SOL_REM2CUR:
2101#ifdef SOL_REM2CUR_PRE
2102 if( lastbutton != SOL_REM2CUR_PRE )
2103 break;
2104#endif
2105 move_card( cur_col, cur_rem );
2106 break;
2107
2108 /* If the card on top of the remains can be put on one
2109 * of the stacks, do so */
2110 case SOL_REM2STACK:
2111#ifdef SOL_REM2STACK_PRE
2112 if( lastbutton != SOL_REM2STACK_PRE )
2113 break;
2114#endif
2115 move_card( deck[cur_rem].suit + COL_NUM, cur_rem );
2116 break;
2117
2118#ifdef SOL_REM
2119 case SOL_REM:
2120 if( sel_card != NOT_A_CARD )
2121 {
2122 /* unselect selected card */
2123 sel_card = NOT_A_CARD;
2124 break;
2125 }
2126 if( rem != NOT_A_CARD && cur_rem != NOT_A_CARD )
2127 {
2128 sel_card = cur_rem;
2129 break;
2130 }
2131 break;
2132#endif
2133
2134 /* unselect selected card or ...
2135 * draw new cards from the remains of the deck */
2136 case SOL_DRAW:
2137#ifdef SOL_DRAW_PRE
2138 if( lastbutton != SOL_DRAW_PRE )
2139 break;
2140#endif
2141 if( sel_card != NOT_A_CARD )
2142 {
2143 /* unselect selected card */
2144 sel_card = NOT_A_CARD;
2145 break;
2146 }
2147 if( rem != NOT_A_CARD )
2148 {
2149 int cur_rem_old = cur_rem;
2150 count_rem = -1;
2151 /* draw new cards form the remains of the deck */
2152 if( cur_rem == NOT_A_CARD )
2153 {
2154 /*if the cursor card is null*/
2155 cur_rem = rem;
2156 i = cards_per_draw - 1;
2157 count_rem++;
2158 }
2159 else
2160 {
2161 i = cards_per_draw;
2162 }
2163
2164 while( i > 0 && deck[cur_rem].next != NOT_A_CARD )
2165 {
2166 cur_rem = deck[cur_rem].next;
2167 i--;
2168 count_rem++;
2169 }
2170 /* test if any cards are really left on
2171 * the remains' stack */
2172 if( i == cards_per_draw )
2173 {
2174 cur_rem = NOT_A_CARD;
2175 count_rem = -1;
2176 }
2177 /* if cursor was on remains' stack when new cards were
2178 * drawn, put cursor on top of remains' stack */
2179 if( cur_col == REM_COL && cur_card == cur_rem_old )
2180 {
2181 cur_card = cur_rem;
2182 sel_card = NOT_A_CARD;
2183 }
2184 }
2185 break;
2186
2187 /* Show the menu */
2188#ifdef SOL_RC_QUIT
2189 case SOL_RC_QUIT:
2190#endif
2191 case SOL_QUIT:
2192 switch( solitaire_menu(true) )
2193 {
2194 case MENU_SAVE_AND_QUIT:
2195 return SOLITAIRE_SAVE_AND_QUIT;
2196
2197 case MENU_QUIT:
2198 return SOLITAIRE_QUIT;
2199
2200 case MENU_USB:
2201 return SOLITAIRE_USB;
2202 }
2203 break;
2204
2205 case SYS_POWEROFF:
2206 case SYS_REBOOT:
2207 return SOLITAIRE_SAVE_AND_QUIT;
2208
2209 default:
2210 if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
2211 return SOLITAIRE_USB;
2212 break;
2213 }
2214
2215#ifdef NEED_LASTBUTTON_VAR
2216 if( button != BUTTON_NONE )
2217 lastbutton = button;
2218#endif
2219
2220 /* fix incoherences concerning cur_col and cur_card */
2221 c = find_card_col( cur_card );
2222 if( c != NOT_A_COL && c != cur_col )
2223 cur_card = find_last_card( cur_col );
2224
2225 if( cur_card == NOT_A_CARD
2226 && find_last_card( cur_col ) != NOT_A_CARD )
2227 cur_card = find_last_card( cur_col );
2228
2229 rb->yield();
2230 }
2231}
2232
2233/**
2234 * Plugin entry point
2235 */
2236
2237enum plugin_status plugin_start(const void* parameter )
2238{
2239 int result;
2240
2241 /* plugin init */
2242 (void)parameter;
2243
2244 configfile_load(CONFIG_FILENAME, config,
2245 sizeof(config) / sizeof(config[0]), CFGFILE_VERSION);
2246 rb->memcpy(&sol, &sol_disk, sizeof(sol)); /* copy to running config */
2247
2248 if( load_game() == 0 )
2249 {
2250 rb->splash( HZ, "Resuming saved game." );
2251 result = SOLITAIRE_QUIT;
2252 }
2253 else
2254 result = SOLITAIRE_WIN;
2255
2256 /* play the game :)
2257 * Keep playing if a game was won (that means display the menu after
2258 * winning instead of quiting) */
2259 while( ( result = solitaire( result ) ) == SOLITAIRE_WIN );
2260
2261 if( result != SOLITAIRE_QUIT )
2262 /* result == SOLITAIRE_USB || result == SOLITAIRE_SAVE_AND_QUIT */
2263 save_game();
2264
2265 if (rb->memcmp(&sol, &sol_disk, sizeof(sol))) /* save settings if changed */
2266 {
2267 rb->memcpy(&sol_disk, &sol, sizeof(sol));
2268 configfile_save(CONFIG_FILENAME, config,
2269 sizeof(config) / sizeof(config[0]), CFGFILE_VERSION);
2270 }
2271
2272 /* Exit the plugin */
2273 return ( result == SOLITAIRE_USB ) ? PLUGIN_USB_CONNECTED : PLUGIN_OK;
2274}