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 actionStatus
;
34 static int itemConfigId
;
35 static config_set_t
* itemConfig
;
37 // "main menu submenu"
38 static submenu_list_t
* mainMenu
;
39 // active item in the main menu
40 static submenu_list_t
* mainMenuCurrent
;
42 static s32 menuSemaId
;
43 static ee_sema_t menuSema
;
45 static void _menuLoadConfig() {
48 item_list_t
* list
= selected_item
->item
->userdata
;
49 itemConfig
= list
->itemGetConfig(itemConfigId
);
52 SignalSema(menuSemaId
);
55 static void _menuSaveConfig() {
57 configWrite(itemConfig
);
58 itemConfigId
= -1; // to invalidate cache and force reload
60 SignalSema(menuSemaId
);
63 static void _menuRequestConfig() {
65 if (itemConfigId
!= selected_item
->item
->current
->item
.id
) {
67 configFree(itemConfig
);
70 item_list_t
* list
= selected_item
->item
->userdata
;
71 if (itemConfigId
== -1 || guiInactiveFrames
>= list
->delay
) {
72 itemConfigId
= selected_item
->item
->current
->item
.id
;
73 ioPutRequest(IO_CUSTOM_SIMPLEACTION
, &_menuLoadConfig
);
75 } else if (itemConfig
)
77 SignalSema(menuSemaId
);
80 config_set_t
* menuLoadConfig() {
83 guiHandleDeferedIO(&actionStatus
, _l(_STR_LOADING_SETTINGS
), IO_CUSTOM_SIMPLEACTION
, &_menuRequestConfig
);
87 void menuSaveConfig() {
89 guiHandleDeferedIO(&actionStatus
, _l(_STR_SAVING_SETTINGS
), IO_CUSTOM_SIMPLEACTION
, &_menuSaveConfig
);
92 static void menuInitMainMenu() {
94 submenuDestroy(&mainMenu
);
96 // initialize the menu
98 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_SETTINGS
, _STR_SETTINGS
);
99 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_GFX_SETTINGS
, _STR_GFX_SETTINGS
);
100 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_IP_CONFIG
, _STR_IPCONFIG
);
101 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_SAVE_CHANGES
, _STR_SAVE_CHANGES
);
102 if (gHDDStartMode
) // enabled at all?
103 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_START_HDL
, _STR_STARTHDL
);
105 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_ABOUT
, _STR_ABOUT
);
106 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_EXIT
, _STR_EXIT
);
107 submenuAppendItem(&mainMenu
, -1, NULL
, MENU_POWER_OFF
, _STR_POWEROFF
);
109 mainMenuCurrent
= mainMenu
;
112 // -------------------------------------------------------------------------------------------
113 // ---------------------------------------- Menu manipulation --------------------------------
114 // -------------------------------------------------------------------------------------------
117 selected_item
= NULL
;
121 mainMenuCurrent
= NULL
;
124 menuSema
.init_count
= 1;
125 menuSema
.max_count
= 1;
127 menuSemaId
= CreateSema(&menuSema
);
132 menu_list_t
*cur
= menu
;
135 menu_list_t
*td
= cur
;
139 submenuDestroy(&td
->item
->submenu
);
141 menuRemoveHints(td
->item
);
146 submenuDestroy(&mainMenu
);
149 configFree(itemConfig
);
153 DeleteSema(menuSemaId
);
156 static menu_list_t
* AllocMenuItem(menu_item_t
* item
) {
159 it
= malloc(sizeof(menu_list_t
));
168 void menuAppendItem(menu_item_t
* item
) {
172 menu
= AllocMenuItem(item
);
173 selected_item
= menu
;
177 menu_list_t
*cur
= menu
;
179 // traverse till the end
184 menu_list_t
*newitem
= AllocMenuItem(item
);
191 void submenuRebuildCache(submenu_list_t
* submenu
) {
193 if (submenu
->item
.cache_id
)
194 free(submenu
->item
.cache_id
);
195 if(submenu
->item
.cache_uid
)
196 free(submenu
->item
.cache_uid
);
198 int size
= gTheme
->gameCacheCount
* sizeof(int);
199 submenu
->item
.cache_id
= malloc(size
);
200 memset(submenu
->item
.cache_id
, -1, size
);
201 submenu
->item
.cache_uid
= malloc(size
);
202 memset(submenu
->item
.cache_uid
, -1, size
);
204 submenu
= submenu
->next
;
208 static submenu_list_t
* submenuAllocItem(int icon_id
, char *text
, int id
, int text_id
) {
209 submenu_list_t
* it
= (submenu_list_t
*) malloc(sizeof(submenu_list_t
));
213 it
->item
.icon_id
= icon_id
;
214 it
->item
.text
= text
;
215 it
->item
.text_id
= text_id
;
217 it
->item
.cache_id
= NULL
;
218 it
->item
.cache_uid
= NULL
;
219 submenuRebuildCache(it
);
224 submenu_list_t
* submenuAppendItem(submenu_list_t
** submenu
, int icon_id
, char *text
, int id
, int text_id
) {
225 if (*submenu
== NULL
) {
226 *submenu
= submenuAllocItem(icon_id
, text
, id
, text_id
);
230 submenu_list_t
*cur
= *submenu
;
232 // traverse till the end
237 submenu_list_t
*newitem
= submenuAllocItem(icon_id
, text
, id
, text_id
);
246 static void submenuDestroyItem(submenu_list_t
* submenu
) {
247 free(submenu
->item
.cache_id
);
248 free(submenu
->item
.cache_uid
);
253 void submenuRemoveItem(submenu_list_t
** submenu
, int id
) {
254 submenu_list_t
* cur
= *submenu
;
255 submenu_list_t
* prev
= NULL
;
258 if (cur
->item
.id
== id
) {
259 submenu_list_t
* next
= cur
->next
;
262 prev
->next
= cur
->next
;
267 submenuDestroyItem(cur
);
277 void submenuDestroy(submenu_list_t
** submenu
) {
279 submenu_list_t
*cur
= *submenu
;
282 submenu_list_t
*td
= cur
;
285 submenuDestroyItem(td
);
291 void menuAddHint(menu_item_t
*menu
, int text_id
, int icon_id
) {
292 // allocate a new hint item
293 menu_hint_item_t
* hint
= malloc(sizeof(menu_hint_item_t
));
295 hint
->text_id
= text_id
;
296 hint
->icon_id
= icon_id
;
300 menu_hint_item_t
* top
= menu
->hints
;
303 for (; top
->next
; top
= top
->next
);
311 void menuRemoveHints(menu_item_t
*menu
) {
312 while (menu
->hints
) {
313 menu_hint_item_t
* hint
= menu
->hints
;
314 menu
->hints
= hint
->next
;
319 char *menuItemGetText(menu_item_t
* it
) {
320 if (it
->text_id
>= 0)
321 return _l(it
->text_id
);
326 char *submenuItemGetText(submenu_item_t
* it
) {
327 if (it
->text_id
>= 0)
328 return _l(it
->text_id
);
333 static void swap(submenu_list_t
* a
, submenu_list_t
* b
) {
334 submenu_list_t
*pa
, *nb
;
350 // Sorts the given submenu by comparing the on-screen titles
351 void submenuSort(submenu_list_t
** submenu
) {
352 // a simple bubblesort
353 // *submenu = mergeSort(*submenu);
354 submenu_list_t
*head
= *submenu
;
357 if ((submenu
== NULL
) || (*submenu
== NULL
) || ((*submenu
)->next
== NULL
))
363 submenu_list_t
*tip
= head
;
366 submenu_list_t
*nxt
= tip
->next
;
368 char *txt1
= submenuItemGetText(&tip
->item
);
369 char *txt2
= submenuItemGetText(&nxt
->item
);
371 int cmp
= stricmp(txt1
, txt2
);
389 static void menuNextH() {
390 if(selected_item
->next
) {
391 selected_item
= selected_item
->next
;
396 static void menuPrevH() {
397 if(selected_item
->prev
) {
398 selected_item
= selected_item
->prev
;
403 static void menuNextV() {
404 submenu_list_t
*cur
= selected_item
->item
->current
;
406 if(cur
&& cur
->next
) {
407 selected_item
->item
->current
= cur
->next
;
409 // if the current item is beyond the page start, move the page start one page down
410 cur
= selected_item
->item
->pagestart
;
411 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
+ 1;
412 while (--itms
&& cur
)
413 if (selected_item
->item
->current
== cur
)
418 selected_item
->item
->pagestart
= selected_item
->item
->current
;
422 static void menuPrevV() {
423 submenu_list_t
*cur
= selected_item
->item
->current
;
425 if(cur
&& cur
->prev
) {
426 selected_item
->item
->current
= cur
->prev
;
428 // if the current item is on the page start, move the page start one page up
429 if (selected_item
->item
->pagestart
== cur
) {
430 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
+ 1; // +1 because the selection will move as well
431 while (--itms
&& selected_item
->item
->pagestart
->prev
)
432 selected_item
->item
->pagestart
= selected_item
->item
->pagestart
->prev
;
437 static void menuNextPage() {
438 submenu_list_t
*cur
= selected_item
->item
->pagestart
;
441 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
+ 1;
442 while (--itms
&& cur
->next
)
445 selected_item
->item
->current
= cur
;
446 selected_item
->item
->pagestart
= selected_item
->item
->current
;
450 static void menuPrevPage() {
451 submenu_list_t
*cur
= selected_item
->item
->pagestart
;
454 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
+ 1;
455 while (--itms
&& cur
->prev
)
458 selected_item
->item
->current
= cur
;
459 selected_item
->item
->pagestart
= selected_item
->item
->current
;
463 static void menuFirstPage() {
464 selected_item
->item
->current
= selected_item
->item
->submenu
;
465 selected_item
->item
->pagestart
= selected_item
->item
->current
;
468 static void menuLastPage() {
469 submenu_list_t
*cur
= selected_item
->item
->current
;
472 cur
= cur
->next
; // go to end
474 selected_item
->item
->current
= cur
;
476 int itms
= ((items_list_t
*) gTheme
->itemsList
->extended
)->displayedItems
;
477 while (--itms
&& cur
->prev
) // and move back to have a full page
480 selected_item
->item
->pagestart
= cur
;
484 void menuSetSelectedItem(menu_item_t
* item
) {
485 menu_list_t
* itm
= menu
;
488 if (itm
->item
== item
) {
497 void menuRenderMenu() {
503 // draw the animated menu
504 if (!mainMenuCurrent
)
505 mainMenuCurrent
= mainMenu
;
507 submenu_list_t
* it
= mainMenu
;
509 // calculate the number of items
510 int count
= 0; int sitem
= 0;
511 for (; it
; count
++, it
=it
->next
) {
512 if (it
== mainMenuCurrent
)
517 int y
= (gTheme
->usedHeight
>> 1) - (spacing
* (count
>> 1));
518 int cp
= 0; // current position
519 for (it
= mainMenu
; it
; it
= it
->next
, cp
++) {
521 fntRenderString(gTheme
->fonts
[0], 320, y
, ALIGN_CENTER
, 0, 0, submenuItemGetText(&it
->item
), (cp
== sitem
) ? gTheme
->selTextColor
: gTheme
->textColor
);
526 void menuHandleInputMenu() {
530 if (!mainMenuCurrent
)
531 mainMenuCurrent
= mainMenu
;
533 if (getKey(KEY_UP
)) {
534 if (mainMenuCurrent
->prev
)
535 mainMenuCurrent
= mainMenuCurrent
->prev
;
536 else // rewind to the last item
537 while (mainMenuCurrent
->next
)
538 mainMenuCurrent
= mainMenuCurrent
->next
;
541 if (getKey(KEY_DOWN
)) {
542 if (mainMenuCurrent
->next
)
543 mainMenuCurrent
= mainMenuCurrent
->next
;
545 mainMenuCurrent
= mainMenu
;
548 if (getKeyOn(KEY_CROSS
)) {
549 // execute the item via looking at the id of it
550 int id
= mainMenuCurrent
->item
.id
;
552 if (id
== MENU_SETTINGS
) {
554 } else if (id
== MENU_GFX_SETTINGS
) {
556 } else if (id
== MENU_IP_CONFIG
) {
558 } else if (id
== MENU_SAVE_CHANGES
) {
559 saveConfig(CONFIG_OPL
, 1);
560 } else if (id
== MENU_START_HDL
) {
562 } else if (id
== MENU_ABOUT
) {
564 } else if (id
== MENU_EXIT
) {
566 } else if (id
== MENU_POWER_OFF
) {
570 // so the exit press wont propagate twice
574 if(getKeyOn(KEY_START
) || getKeyOn(KEY_CIRCLE
)) {
575 if (gAPPStartMode
|| gETHStartMode
|| gUSBStartMode
|| gHDDStartMode
)
576 guiSwitchScreen(GUI_SCREEN_MAIN
, TRANSITION_LEFT
);
580 void menuRenderMain() {
581 // selected_item can't be NULL here as we only allow to switch to "Main" rendering when there is at least one device activated
582 theme_element_t
* elem
= gTheme
->mainElems
.first
;
585 elem
->drawElem(selected_item
, selected_item
->item
->current
, NULL
, elem
);
591 void menuHandleInputMain() {
592 if(getKey(KEY_LEFT
)) {
594 } else if(getKey(KEY_RIGHT
)) {
596 } else if(getKey(KEY_UP
)) {
598 } else if(getKey(KEY_DOWN
)){
600 } else if(getKeyOn(KEY_CROSS
)) {
601 if (selected_item
->item
->current
&& gUseInfoScreen
&& gTheme
->infoElems
.first
)
602 guiSwitchScreen(GUI_SCREEN_INFO
, TRANSITION_DOWN
);
604 selected_item
->item
->execCross(selected_item
->item
);
605 } else if(getKeyOn(KEY_TRIANGLE
)) {
606 selected_item
->item
->execTriangle(selected_item
->item
);
607 } else if(getKeyOn(KEY_CIRCLE
)) {
608 selected_item
->item
->execCircle(selected_item
->item
);
609 } else if(getKeyOn(KEY_SQUARE
)) {
610 selected_item
->item
->execSquare(selected_item
->item
);
611 } else if(getKeyOn(KEY_START
)) {
612 // reinit main menu - show/hide items valid in the active context
614 guiSwitchScreen(GUI_SCREEN_MENU
, TRANSITION_RIGHT
);
615 } else if(getKeyOn(KEY_SELECT
)) {
616 selected_item
->item
->refresh(selected_item
->item
);
617 } else if(getKey(KEY_L1
)) {
619 } else if(getKey(KEY_R1
)) {
621 } else if (getKeyOn(KEY_L2
)) { // home
623 } else if (getKeyOn(KEY_R2
)) { // end
628 void menuRenderInfo() {
629 // 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
630 _menuRequestConfig();
632 //WaitSema(menuSemaId); If I'm not mistaking (assignment of itemConfig pointer is atomic), not needed
633 theme_element_t
* elem
= gTheme
->infoElems
.first
;
636 elem
->drawElem(selected_item
, selected_item
->item
->current
, itemConfig
, elem
);
640 //SignalSema(menuSemaId);
643 void menuHandleInputInfo() {
644 if(getKeyOn(KEY_CROSS
)) {
645 selected_item
->item
->execCross(selected_item
->item
);
646 } else if(getKey(KEY_UP
)) {
648 } else if(getKey(KEY_DOWN
)){
650 } else if(getKeyOn(KEY_CIRCLE
)) {
651 guiSwitchScreen(GUI_SCREEN_MAIN
, TRANSITION_UP
);
652 } else if(getKey(KEY_L1
)) {
654 } else if(getKey(KEY_R1
)) {
656 } else if (getKeyOn(KEY_L2
)) {
658 } else if (getKeyOn(KEY_R2
)) {