2 Copyright 2009, Ifcaro & volca
3 Licenced under Academic Free License version 3.0
4 Review OpenUsbLd README & LICENSE files for further details.
7 #include "include/menusys.h"
8 #include "include/iosupport.h"
9 #include "include/usbld.h"
10 #include "include/renderman.h"
11 #include "include/fntsys.h"
12 #include "include/lang.h"
13 #include "include/themes.h"
14 #include "include/pad.h"
15 #include "include/gui.h"
16 #include "include/system.h"
17 #include "include/ioman.h"
20 #define MENU_SETTINGS 0
21 #define MENU_GFX_SETTINGS 1
22 #define MENU_IP_CONFIG 2
23 #define MENU_SAVE_CHANGES 3
24 #define MENU_START_HDL 4
27 #define MENU_POWER_OFF 7
29 // global menu variables
30 static menu_list_t
* menu
;
31 static menu_list_t
* selected_item
;
33 static int itemIdConfig
;
34 static config_set_t
* itemConfig
;
36 // "main menu submenu"
37 static submenu_list_t
* mainMenu
;
38 // active item in the main menu
39 static submenu_list_t
* mainMenuCurrent
;
41 // io call to handle the loading of config
42 #define IO_MENU_LOAD_CONFIG 6
44 static s32 menuSemaId
;
45 static ee_sema_t menuSema
;
50 } load_config_request_t
;
52 // Io handled action...
53 static void menuLoadConfig(void* data
) {
54 load_config_request_t
* req
= data
;
57 if (!itemConfig
&& (req
->itemId
== itemIdConfig
))
58 itemConfig
= req
->list
->itemGetConfig(itemIdConfig
);
59 SignalSema(menuSemaId
);
64 static void menuRequestConfig() {
66 if (itemIdConfig
!= selected_item
->item
->current
->item
.id
) {
68 configFree(itemConfig
);
72 item_list_t
* list
= selected_item
->item
->userdata
;
73 if (guiInactiveFrames
>= list
->delay
) {
74 itemIdConfig
= selected_item
->item
->current
->item
.id
;
76 load_config_request_t
* req
= malloc(sizeof(load_config_request_t
));
77 req
->itemId
= itemIdConfig
;
79 ioPutRequest(IO_MENU_LOAD_CONFIG
, req
);
82 SignalSema(menuSemaId
);
85 static void menuInitMainMenu() {
87 submenuDestroy(&mainMenu
);
89 // initialize the menu
91 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_SETTINGS
, _STR_SETTINGS
);
92 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_GFX_SETTINGS
, _STR_GFX_SETTINGS
);
93 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_IP_CONFIG
, _STR_IPCONFIG
);
94 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_SAVE_CHANGES
, _STR_SAVE_CHANGES
);
95 if (gHDDStartMode
) // enabled at all?
96 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_START_HDL
, _STR_STARTHDL
);
98 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_ABOUT
, _STR_ABOUT
);
99 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_EXIT
, _STR_EXIT
);
100 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_POWER_OFF
, _STR_POWEROFF
);
102 mainMenuCurrent
= mainMenu
;
105 // -------------------------------------------------------------------------------------------
106 // ---------------------------------------- Menu manipulation --------------------------------
107 // -------------------------------------------------------------------------------------------
110 selected_item
= NULL
;
114 mainMenuCurrent
= NULL
;
117 menuSema
.init_count
= 1;
118 menuSema
.max_count
= 1;
120 menuSemaId
= CreateSema(&menuSema
);
122 ioRegisterHandler(IO_MENU_LOAD_CONFIG
, &menuLoadConfig
);
127 menu_list_t
*cur
= menu
;
130 menu_list_t
*td
= cur
;
134 submenuDestroy(&td
->item
->submenu
);
136 menuRemoveHints(td
->item
);
141 submenuDestroy(&mainMenu
);
144 configFree(itemConfig
);
148 DeleteSema(menuSemaId
);
151 static menu_list_t
* AllocMenuItem(menu_item_t
* item
) {
154 it
= malloc(sizeof(menu_list_t
));
163 void menuAppendItem(menu_item_t
* item
) {
167 menu
= AllocMenuItem(item
);
168 selected_item
= menu
;
172 menu_list_t
*cur
= menu
;
174 // traverse till the end
179 menu_list_t
*newitem
= AllocMenuItem(item
);
186 void submenuRebuildCache(submenu_list_t
* submenu
) {
188 if (submenu
->item
.cache_id
)
189 free(submenu
->item
.cache_id
);
190 if(submenu
->item
.cache_uid
)
191 free(submenu
->item
.cache_uid
);
193 int size
= gTheme
->gameCacheCount
* sizeof(int);
194 submenu
->item
.cache_id
= malloc(size
);
195 memset(submenu
->item
.cache_id
, -1, size
);
196 submenu
->item
.cache_uid
= malloc(size
);
197 memset(submenu
->item
.cache_uid
, -1, size
);
199 submenu
= submenu
->next
;
203 static submenu_list_t
* submenuAllocItem(int icon_id
, char *text
, int id
, int text_id
) {
204 submenu_list_t
* it
= (submenu_list_t
*) malloc(sizeof(submenu_list_t
));
208 it
->item
.icon_id
= icon_id
;
209 it
->item
.text
= text
;
210 it
->item
.text_id
= text_id
;
212 it
->item
.cache_id
= NULL
;
213 it
->item
.cache_uid
= NULL
;
214 submenuRebuildCache(it
);
219 submenu_list_t
* submenuAppendItem(submenu_list_t
** submenu
, int icon_id
, char *text
, int id
, int text_id
) {
220 if (*submenu
== NULL
) {
221 *submenu
= submenuAllocItem(icon_id
, text
, id
, text_id
);
225 submenu_list_t
*cur
= *submenu
;
227 // traverse till the end
232 submenu_list_t
*newitem
= submenuAllocItem(icon_id
, text
, id
, text_id
);
241 static void submenuDestroyItem(submenu_list_t
* submenu
) {
242 free(submenu
->item
.cache_id
);
243 free(submenu
->item
.cache_uid
);
248 void submenuRemoveItem(submenu_list_t
** submenu
, int id
) {
249 submenu_list_t
* cur
= *submenu
;
250 submenu_list_t
* prev
= NULL
;
253 if (cur
->item
.id
== id
) {
254 submenu_list_t
* next
= cur
->next
;
257 prev
->next
= cur
->next
;
262 submenuDestroyItem(cur
);
272 void submenuDestroy(submenu_list_t
** submenu
) {
274 submenu_list_t
*cur
= *submenu
;
277 submenu_list_t
*td
= cur
;
280 submenuDestroyItem(td
);
286 void menuAddHint(menu_item_t
*menu
, int text_id
, int icon_id
) {
287 // allocate a new hint item
288 menu_hint_item_t
* hint
= malloc(sizeof(menu_hint_item_t
));
290 hint
->text_id
= text_id
;
291 hint
->icon_id
= icon_id
;
295 menu_hint_item_t
* top
= menu
->hints
;
298 for (; top
->next
; top
= top
->next
);
306 void menuRemoveHints(menu_item_t
*menu
) {
307 while (menu
->hints
) {
308 menu_hint_item_t
* hint
= menu
->hints
;
309 menu
->hints
= hint
->next
;
314 char *menuItemGetText(menu_item_t
* it
) {
315 if (it
->text_id
>= 0)
316 return _l(it
->text_id
);
321 char *submenuItemGetText(submenu_item_t
* it
) {
322 if (it
->text_id
>= 0)
323 return _l(it
->text_id
);
328 static void swap(submenu_list_t
* a
, submenu_list_t
* b
) {
329 submenu_list_t
*pa
, *nb
;
345 // Sorts the given submenu by comparing the on-screen titles
346 void submenuSort(submenu_list_t
** submenu
) {
347 // a simple bubblesort
348 // *submenu = mergeSort(*submenu);
349 submenu_list_t
*head
= *submenu
;
352 if ((submenu
== NULL
) || (*submenu
== NULL
) || ((*submenu
)->next
== NULL
))
358 submenu_list_t
*tip
= head
;
361 submenu_list_t
*nxt
= tip
->next
;
363 char *txt1
= submenuItemGetText(&tip
->item
);
364 char *txt2
= submenuItemGetText(&nxt
->item
);
366 int cmp
= stricmp(txt1
, txt2
);
384 static void menuNextH() {
385 if(selected_item
->next
)
386 selected_item
= selected_item
->next
;
389 static void menuPrevH() {
390 if(selected_item
->prev
)
391 selected_item
= selected_item
->prev
;
394 static void menuNextV() {
395 submenu_list_t
*cur
= selected_item
->item
->current
;
397 if(cur
&& cur
->next
) {
398 selected_item
->item
->current
= cur
->next
;
400 // if the current item is beyond the page start, move the page start one page down
401 cur
= selected_item
->item
->pagestart
;
402 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
+ 1;
403 while (--itms
&& cur
)
404 if (selected_item
->item
->current
== cur
)
409 selected_item
->item
->pagestart
= selected_item
->item
->current
;
413 static void menuPrevV() {
414 submenu_list_t
*cur
= selected_item
->item
->current
;
416 if(cur
&& cur
->prev
) {
417 selected_item
->item
->current
= cur
->prev
;
419 // if the current item is on the page start, move the page start one page up
420 if (selected_item
->item
->pagestart
== cur
) {
421 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
+ 1; // +1 because the selection will move as well
422 while (--itms
&& selected_item
->item
->pagestart
->prev
)
423 selected_item
->item
->pagestart
= selected_item
->item
->pagestart
->prev
;
428 static void menuNextPage() {
429 submenu_list_t
*cur
= selected_item
->item
->pagestart
;
432 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
+ 1;
433 while (--itms
&& cur
->next
)
436 selected_item
->item
->current
= cur
;
437 selected_item
->item
->pagestart
= selected_item
->item
->current
;
441 static void menuPrevPage() {
442 submenu_list_t
*cur
= selected_item
->item
->pagestart
;
445 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
+ 1;
446 while (--itms
&& cur
->prev
)
449 selected_item
->item
->current
= cur
;
450 selected_item
->item
->pagestart
= selected_item
->item
->current
;
454 static void menuFirstPage() {
455 selected_item
->item
->current
= selected_item
->item
->submenu
;
456 selected_item
->item
->pagestart
= selected_item
->item
->current
;
459 static void menuLastPage() {
460 submenu_list_t
*cur
= selected_item
->item
->current
;
463 cur
= cur
->next
; // go to end
465 selected_item
->item
->current
= cur
;
467 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
;
468 while (--itms
&& cur
->prev
) // and move back to have a full page
471 selected_item
->item
->pagestart
= cur
;
475 void menuSetSelectedItem(menu_item_t
* item
) {
476 menu_list_t
* itm
= menu
;
479 if (itm
->item
== item
) {
488 void menuRenderMenu() {
494 // draw the animated menu
495 if (!mainMenuCurrent
)
496 mainMenuCurrent
= mainMenu
;
498 submenu_list_t
* it
= mainMenu
;
500 // calculate the number of items
501 int count
= 0; int sitem
= 0;
502 for (; it
; count
++, it
=it
->next
) {
503 if (it
== mainMenuCurrent
)
508 int y
= (gTheme
->usedHeight
>> 1) - (spacing
* (count
>> 1));
509 int cp
= 0; // current position
510 for (it
= mainMenu
; it
; it
= it
->next
, cp
++) {
512 // TODO: Theme support for main menu (font)
513 fntRenderString(FNT_DEFAULT
, 320, y
, ALIGN_CENTER
, submenuItemGetText(&it
->item
), (cp
== sitem
) ? gTheme
->selTextColor
: gTheme
->textColor
);
518 void menuHandleInputMenu() {
522 if (!mainMenuCurrent
)
523 mainMenuCurrent
= mainMenu
;
525 if (getKey(KEY_UP
)) {
526 if (mainMenuCurrent
->prev
)
527 mainMenuCurrent
= mainMenuCurrent
->prev
;
528 else // rewind to the last item
529 while (mainMenuCurrent
->next
)
530 mainMenuCurrent
= mainMenuCurrent
->next
;
533 if (getKey(KEY_DOWN
)) {
534 if (mainMenuCurrent
->next
)
535 mainMenuCurrent
= mainMenuCurrent
->next
;
537 mainMenuCurrent
= mainMenu
;
540 if (getKeyOn(KEY_CROSS
)) {
541 // execute the item via looking at the id of it
542 int id
= mainMenuCurrent
->item
.id
;
544 if (id
== MENU_SETTINGS
) {
546 } else if (id
== MENU_GFX_SETTINGS
) {
548 } else if (id
== MENU_IP_CONFIG
) {
550 } else if (id
== MENU_SAVE_CHANGES
) {
551 saveConfig(CONFIG_OPL
| CONFIG_VMODE
, 1);
552 } else if (id
== MENU_START_HDL
) {
554 } else if (id
== MENU_ABOUT
) {
556 } else if (id
== MENU_EXIT
) {
558 } else if (id
== MENU_POWER_OFF
) {
562 // so the exit press wont propagate twice
566 if(getKeyOn(KEY_START
) || getKeyOn(KEY_CIRCLE
)) {
567 if (gAPPStartMode
|| gETHStartMode
|| gUSBStartMode
|| gHDDStartMode
)
568 guiSwitchScreen(GUI_SCREEN_MAIN
, TRANSITION_LEFT
);
572 void menuRenderMain() {
573 // selected_item can't be NULL here as we only allow to switch to "Main" rendering when there is at least one device activated
574 theme_element_t
* elem
= gTheme
->mainElems
.first
;
577 elem
->drawElem(selected_item
, selected_item
->item
->current
, NULL
, elem
);
583 void menuHandleInputMain() {
584 if(getKey(KEY_LEFT
)) {
586 } else if(getKey(KEY_RIGHT
)) {
588 } else if(getKey(KEY_UP
)) {
590 } else if(getKey(KEY_DOWN
)){
592 } else if(getKeyOn(KEY_CROSS
)) {
593 if (selected_item
->item
->current
&& gUseInfoScreen
&& gTheme
->infoElems
.first
)
594 guiSwitchScreen(GUI_SCREEN_INFO
, TRANSITION_DOWN
);
596 selected_item
->item
->execCross(selected_item
->item
);
597 } else if(getKeyOn(KEY_TRIANGLE
)) {
598 selected_item
->item
->execTriangle(selected_item
->item
);
599 } else if(getKeyOn(KEY_CIRCLE
)) {
600 selected_item
->item
->execCircle(selected_item
->item
);
601 } else if(getKeyOn(KEY_SQUARE
)) {
602 selected_item
->item
->execSquare(selected_item
->item
);
603 } else if(getKeyOn(KEY_START
)) {
604 // reinit main menu - show/hide items valid in the active context
606 guiSwitchScreen(GUI_SCREEN_MENU
, TRANSITION_RIGHT
);
607 } else if(getKeyOn(KEY_SELECT
)) {
608 selected_item
->item
->refresh(selected_item
->item
);
609 } else if(getKey(KEY_L1
)) {
611 } else if(getKey(KEY_R1
)) {
613 } else if (getKeyOn(KEY_L2
)) { // home
615 } else if (getKeyOn(KEY_R2
)) { // end
620 void menuRenderInfo() {
621 // selected_item->item->current can't be NULL here as we only allow to switch to "Info" rendering when there is at least one item
624 theme_element_t
* elem
= gTheme
->infoElems
.first
;
627 elem
->drawElem(selected_item
, selected_item
->item
->current
, itemConfig
, elem
);
633 void menuHandleInputInfo() {
634 if(getKeyOn(KEY_CROSS
)) {
635 selected_item
->item
->execCross(selected_item
->item
);
636 } else if(getKey(KEY_UP
)) {
638 } else if(getKey(KEY_DOWN
)){
640 } else if(getKeyOn(KEY_CIRCLE
)) {
641 guiSwitchScreen(GUI_SCREEN_MAIN
, TRANSITION_UP
);
642 } else if(getKey(KEY_L1
)) {
644 } else if(getKey(KEY_R1
)) {
646 } else if (getKeyOn(KEY_L2
)) {
648 } else if (getKeyOn(KEY_R2
)) {