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
);
171 menu_list_t
*cur
= menu
;
173 // traverse till the end
178 menu_list_t
*newitem
= AllocMenuItem(item
);
185 void submenuRebuildCache(submenu_list_t
* submenu
) {
187 if (submenu
->item
.cache_id
)
188 free(submenu
->item
.cache_id
);
189 if(submenu
->item
.cache_uid
)
190 free(submenu
->item
.cache_uid
);
192 int size
= gTheme
->gameCacheCount
* sizeof(int);
193 submenu
->item
.cache_id
= malloc(size
);
194 memset(submenu
->item
.cache_id
, -1, size
);
195 submenu
->item
.cache_uid
= malloc(size
);
196 memset(submenu
->item
.cache_uid
, -1, size
);
198 submenu
= submenu
->next
;
202 static submenu_list_t
* submenuAllocItem(int icon_id
, char *text
, int id
, int text_id
) {
203 submenu_list_t
* it
= (submenu_list_t
*) malloc(sizeof(submenu_list_t
));
207 it
->item
.icon_id
= icon_id
;
208 it
->item
.text
= text
;
209 it
->item
.text_id
= text_id
;
211 it
->item
.cache_id
= NULL
;
212 it
->item
.cache_uid
= NULL
;
213 submenuRebuildCache(it
);
218 submenu_list_t
* submenuAppendItem(submenu_list_t
** submenu
, int icon_id
, char *text
, int id
, int text_id
) {
219 if (*submenu
== NULL
) {
220 *submenu
= submenuAllocItem(icon_id
, text
, id
, text_id
);
224 submenu_list_t
*cur
= *submenu
;
226 // traverse till the end
231 submenu_list_t
*newitem
= submenuAllocItem(icon_id
, text
, id
, text_id
);
240 static void submenuDestroyItem(submenu_list_t
* submenu
) {
241 free(submenu
->item
.cache_id
);
242 free(submenu
->item
.cache_uid
);
247 void submenuRemoveItem(submenu_list_t
** submenu
, int id
) {
248 submenu_list_t
* cur
= *submenu
;
249 submenu_list_t
* prev
= NULL
;
252 if (cur
->item
.id
== id
) {
253 submenu_list_t
* next
= cur
->next
;
256 prev
->next
= cur
->next
;
261 submenuDestroyItem(cur
);
271 void submenuDestroy(submenu_list_t
** submenu
) {
273 submenu_list_t
*cur
= *submenu
;
276 submenu_list_t
*td
= cur
;
279 submenuDestroyItem(td
);
285 void menuAddHint(menu_item_t
*menu
, int text_id
, int icon_id
) {
286 // allocate a new hint item
287 menu_hint_item_t
* hint
= malloc(sizeof(menu_hint_item_t
));
289 hint
->text_id
= text_id
;
290 hint
->icon_id
= icon_id
;
294 menu_hint_item_t
* top
= menu
->hints
;
297 for (; top
->next
; top
= top
->next
);
305 void menuRemoveHints(menu_item_t
*menu
) {
306 while (menu
->hints
) {
307 menu_hint_item_t
* hint
= menu
->hints
;
308 menu
->hints
= hint
->next
;
313 char *menuItemGetText(menu_item_t
* it
) {
314 if (it
->text_id
>= 0)
315 return _l(it
->text_id
);
320 char *submenuItemGetText(submenu_item_t
* it
) {
321 if (it
->text_id
>= 0)
322 return _l(it
->text_id
);
327 static void swap(submenu_list_t
* a
, submenu_list_t
* b
) {
328 submenu_list_t
*pa
, *nb
;
344 // Sorts the given submenu by comparing the on-screen titles
345 void submenuSort(submenu_list_t
** submenu
) {
346 // a simple bubblesort
347 // *submenu = mergeSort(*submenu);
348 submenu_list_t
*head
= *submenu
;
351 if ((submenu
== NULL
) || (*submenu
== NULL
) || ((*submenu
)->next
== NULL
))
357 submenu_list_t
*tip
= head
;
360 submenu_list_t
*nxt
= tip
->next
;
362 char *txt1
= submenuItemGetText(&tip
->item
);
363 char *txt2
= submenuItemGetText(&nxt
->item
);
365 int cmp
= stricmp(txt1
, txt2
);
383 static void menuNextH() {
384 if (!selected_item
) {
385 selected_item
= menu
;
388 if(selected_item
->next
) {
389 selected_item
= selected_item
->next
;
391 if (!selected_item
->item
->current
)
392 selected_item
->item
->current
= selected_item
->item
->submenu
;
396 static void menuPrevH() {
397 if (!selected_item
) {
398 selected_item
= menu
;
401 if(selected_item
->prev
) {
402 selected_item
= selected_item
->prev
;
404 if (!selected_item
->item
->current
)
405 selected_item
->item
->current
= selected_item
->item
->submenu
;
409 static void menuNextV() {
413 submenu_list_t
*cur
= selected_item
->item
->current
;
416 selected_item
->item
->current
= cur
->next
;
419 static void menuPrevV() {
423 submenu_list_t
*cur
= selected_item
->item
->current
;
425 // if the current item is on the page start, move the page start one page up before moving the item
426 if (selected_item
->item
->pagestart
) {
427 if (selected_item
->item
->pagestart
== cur
) {
428 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
+ 1; // +1 because the selection will move as well
430 while (--itms
&& selected_item
->item
->pagestart
->prev
)
431 selected_item
->item
->pagestart
= selected_item
->item
->pagestart
->prev
;
434 selected_item
->item
->pagestart
= cur
;
436 if(cur
&& cur
->prev
) {
437 selected_item
->item
->current
= cur
->prev
;
441 static void menuNextPage() {
445 submenu_list_t
*cur
= selected_item
->item
->pagestart
;
448 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
+ 1;
449 while (--itms
&& cur
->next
)
452 selected_item
->item
->current
= cur
;
456 static void menuPrevPage() {
460 submenu_list_t
*cur
= selected_item
->item
->pagestart
;
463 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
+ 1;
464 while (--itms
&& cur
->prev
)
467 selected_item
->item
->current
= cur
;
468 selected_item
->item
->pagestart
= cur
;
472 static void menuFirstPage() {
476 selected_item
->item
->current
= selected_item
->item
->submenu
;
479 static void menuLastPage() {
483 submenu_list_t
*cur
= selected_item
->item
->current
;
487 cur
= cur
->next
; // go to end
489 selected_item
->item
->current
= cur
;
491 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
;
492 while (--itms
&& cur
->prev
) // and move back to have a full page
495 selected_item
->item
->pagestart
= cur
;
499 void menuSetSelectedItem(menu_item_t
* item
) {
500 menu_list_t
* itm
= menu
;
503 if (itm
->item
== item
) {
512 void menuRenderMenu() {
518 // draw the animated menu
519 if (!mainMenuCurrent
)
520 mainMenuCurrent
= mainMenu
;
522 submenu_list_t
* it
= mainMenu
;
524 // calculate the number of items
525 int count
= 0; int sitem
= 0;
526 for (; it
; count
++, it
=it
->next
) {
527 if (it
== mainMenuCurrent
)
532 int y
= (gTheme
->usedHeight
>> 1) - (spacing
* (count
>> 1));
533 int cp
= 0; // current position
534 for (it
= mainMenu
; it
; it
= it
->next
, cp
++) {
536 // TODO: Theme support for main menu (font)
537 fntRenderString(FNT_DEFAULT
, 320, y
, ALIGN_CENTER
, submenuItemGetText(&it
->item
), (cp
== sitem
) ? gTheme
->selTextColor
: gTheme
->textColor
);
542 void menuHandleInputMenu() {
546 if (!mainMenuCurrent
)
547 mainMenuCurrent
= mainMenu
;
549 if (getKey(KEY_UP
)) {
550 if (mainMenuCurrent
->prev
)
551 mainMenuCurrent
= mainMenuCurrent
->prev
;
552 else // rewind to the last item
553 while (mainMenuCurrent
->next
)
554 mainMenuCurrent
= mainMenuCurrent
->next
;
557 if (getKey(KEY_DOWN
)) {
558 if (mainMenuCurrent
->next
)
559 mainMenuCurrent
= mainMenuCurrent
->next
;
561 mainMenuCurrent
= mainMenu
;
564 if (getKeyOn(KEY_CROSS
)) {
565 // execute the item via looking at the id of it
566 int id
= mainMenuCurrent
->item
.id
;
568 if (id
== MENU_SETTINGS
) {
570 } else if (id
== MENU_GFX_SETTINGS
) {
572 } else if (id
== MENU_IP_CONFIG
) {
574 } else if (id
== MENU_SAVE_CHANGES
) {
575 saveConfig(CONFIG_OPL
| CONFIG_VMODE
, 1);
576 } else if (id
== MENU_START_HDL
) {
578 } else if (id
== MENU_ABOUT
) {
580 } else if (id
== MENU_EXIT
) {
582 } else if (id
== MENU_POWER_OFF
) {
586 // so the exit press wont propagate twice
590 if(getKeyOn(KEY_START
) || getKeyOn(KEY_CIRCLE
)) {
591 if (gAPPStartMode
|| gETHStartMode
|| gUSBStartMode
|| gHDDStartMode
)
592 guiSwitchScreen(GUI_SCREEN_MAIN
, TRANSITION_LEFT
);
596 void menuRenderMain() {
601 selected_item
= menu
;
603 if (!selected_item
->item
->current
)
604 selected_item
->item
->current
= selected_item
->item
->submenu
;
606 submenu_list_t
* cur
= selected_item
->item
->current
;
608 theme_element_t
* elem
= gTheme
->mainElems
.first
;
611 elem
->drawElem(selected_item
, cur
, NULL
, elem
);
617 void menuHandleInputMain() {
621 if(getKey(KEY_LEFT
)) {
623 } else if(getKey(KEY_RIGHT
)) {
625 } else if(getKey(KEY_UP
)) {
627 } else if(getKey(KEY_DOWN
)){
629 } else if(getKeyOn(KEY_CROSS
)) {
630 if (selected_item
->item
->current
&& gUseInfoScreen
&& gTheme
->infoElems
.first
)
631 guiSwitchScreen(GUI_SCREEN_INFO
, TRANSITION_DOWN
);
633 selected_item
->item
->execCross(selected_item
->item
);
634 } else if(getKeyOn(KEY_TRIANGLE
)) {
635 selected_item
->item
->execTriangle(selected_item
->item
);
636 } else if(getKeyOn(KEY_CIRCLE
)) {
637 selected_item
->item
->execCircle(selected_item
->item
);
638 } else if(getKeyOn(KEY_SQUARE
)) {
639 selected_item
->item
->execSquare(selected_item
->item
);
640 } else if(getKeyOn(KEY_START
)) {
641 // reinit main menu - show/hide items valid in the active context
643 guiSwitchScreen(GUI_SCREEN_MENU
, TRANSITION_RIGHT
);
644 } else if(getKeyOn(KEY_SELECT
)) {
645 selected_item
->item
->refresh(selected_item
->item
);
646 } else if(getKey(KEY_L1
)) {
648 } else if(getKey(KEY_R1
)) {
650 } else if (getKeyOn(KEY_L2
)) { // home
652 } else if (getKeyOn(KEY_R2
)) { // end
657 void menuRenderInfo() {
658 submenu_list_t
* cur
= selected_item
->item
->current
;
662 theme_element_t
* elem
= gTheme
->infoElems
.first
;
665 elem
->drawElem(selected_item
, cur
, itemConfig
, elem
);
671 void menuHandleInputInfo() {
672 if(getKeyOn(KEY_CROSS
)) {
673 selected_item
->item
->execCross(selected_item
->item
);
674 } else if(getKey(KEY_UP
)) {
676 } else if(getKey(KEY_DOWN
)){
678 } else if(getKeyOn(KEY_CIRCLE
)) {
679 guiSwitchScreen(GUI_SCREEN_MAIN
, TRANSITION_UP
);
680 } else if(getKey(KEY_L1
)) {
682 } else if(getKey(KEY_R1
)) {
684 } else if (getKeyOn(KEY_L2
)) {
686 } else if (getKeyOn(KEY_R2
)) {