extracted code branching from ItemsList drawing and main drawing method
[open-ps2-loader.git] / src / menusys.c
blobd4c9178bbfe212c9247532e2caf6b4d1e9c3f85f
1 /*
2 Copyright 2009, Ifcaro & volca
3 Licenced under Academic Free License version 3.0
4 Review OpenUsbLd README & LICENSE files for further details.
5 */
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"
18 #include "assert.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
25 #define MENU_ABOUT 5
26 #define MENU_EXIT 6
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;
47 typedef struct {
48 int itemId;
49 item_list_t* list;
50 } load_config_request_t;
52 // Io handled action...
53 static void menuLoadConfig(void* data) {
54 load_config_request_t* req = data;
56 WaitSema(menuSemaId);
57 if (!itemConfig && (req->itemId == itemIdConfig))
58 itemConfig = req->list->itemGetConfig(itemIdConfig);
59 SignalSema(menuSemaId);
61 free(req);
64 static void menuRequestConfig() {
65 WaitSema(menuSemaId);
66 if (itemIdConfig != selected_item->item->current->item.id) {
67 if (itemConfig) {
68 configFree(itemConfig);
69 itemConfig = NULL;
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;
78 req->list = list;
79 ioPutRequest(IO_MENU_LOAD_CONFIG, req);
82 SignalSema(menuSemaId);
85 static void menuInitMainMenu() {
86 if (mainMenu)
87 submenuDestroy(&mainMenu);
89 // initialize the menu
90 #ifndef __CHILDPROOF
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);
97 #endif
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 // -------------------------------------------------------------------------------------------
108 void menuInit() {
109 menu = NULL;
110 selected_item = NULL;
111 itemIdConfig = -1;
112 itemConfig = NULL;
113 mainMenu = NULL;
114 mainMenuCurrent = NULL;
115 menuInitMainMenu();
117 menuSema.init_count = 1;
118 menuSema.max_count = 1;
119 menuSema.option = 0;
120 menuSemaId = CreateSema(&menuSema);
122 ioRegisterHandler(IO_MENU_LOAD_CONFIG, &menuLoadConfig);
125 void menuEnd() {
126 // destroy menu
127 menu_list_t *cur = menu;
129 while (cur) {
130 menu_list_t *td = cur;
131 cur = cur->next;
133 if (&td->item)
134 submenuDestroy(&td->item->submenu);
136 menuRemoveHints(td->item);
138 free(td);
141 submenuDestroy(&mainMenu);
143 if (itemConfig) {
144 configFree(itemConfig);
145 itemConfig = NULL;
148 DeleteSema(menuSemaId);
151 static menu_list_t* AllocMenuItem(menu_item_t* item) {
152 menu_list_t* it;
154 it = malloc(sizeof(menu_list_t));
156 it->prev = NULL;
157 it->next = NULL;
158 it->item = item;
160 return it;
163 void menuAppendItem(menu_item_t* item) {
164 assert(item);
166 if (menu == NULL) {
167 menu = AllocMenuItem(item);
168 selected_item = menu;
169 return;
172 menu_list_t *cur = menu;
174 // traverse till the end
175 while (cur->next)
176 cur = cur->next;
178 // create new item
179 menu_list_t *newitem = AllocMenuItem(item);
181 // link
182 cur->next = newitem;
183 newitem->prev = cur;
186 void submenuRebuildCache(submenu_list_t* submenu) {
187 while (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));
206 it->prev = NULL;
207 it->next = NULL;
208 it->item.icon_id = icon_id;
209 it->item.text = text;
210 it->item.text_id = text_id;
211 it->item.id = id;
212 it->item.cache_id = NULL;
213 it->item.cache_uid = NULL;
214 submenuRebuildCache(it);
216 return 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);
222 return *submenu;
225 submenu_list_t *cur = *submenu;
227 // traverse till the end
228 while (cur->next)
229 cur = cur->next;
231 // create new item
232 submenu_list_t *newitem = submenuAllocItem(icon_id, text, id, text_id);
234 // link
235 cur->next = newitem;
236 newitem->prev = cur;
238 return newitem;
241 static void submenuDestroyItem(submenu_list_t* submenu) {
242 free(submenu->item.cache_id);
243 free(submenu->item.cache_uid);
245 free(submenu);
248 void submenuRemoveItem(submenu_list_t** submenu, int id) {
249 submenu_list_t* cur = *submenu;
250 submenu_list_t* prev = NULL;
252 while (cur) {
253 if (cur->item.id == id) {
254 submenu_list_t* next = cur->next;
256 if (prev)
257 prev->next = cur->next;
259 if (*submenu == cur)
260 *submenu = next;
262 submenuDestroyItem(cur);
264 cur = next;
265 } else {
266 prev = cur;
267 cur = cur->next;
272 void submenuDestroy(submenu_list_t** submenu) {
273 // destroy sub menu
274 submenu_list_t *cur = *submenu;
276 while (cur) {
277 submenu_list_t *td = cur;
278 cur = cur->next;
280 submenuDestroyItem(td);
283 *submenu = NULL;
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;
292 hint->next = NULL;
294 if (menu->hints) {
295 menu_hint_item_t* top = menu->hints;
297 // rewind to end
298 for (; top->next; top = top->next);
300 top->next = hint;
301 } else {
302 menu->hints = hint;
306 void menuRemoveHints(menu_item_t *menu) {
307 while (menu->hints) {
308 menu_hint_item_t* hint = menu->hints;
309 menu->hints = hint->next;
310 free(hint);
314 char *menuItemGetText(menu_item_t* it) {
315 if (it->text_id >= 0)
316 return _l(it->text_id);
317 else
318 return it->text;
321 char *submenuItemGetText(submenu_item_t* it) {
322 if (it->text_id >= 0)
323 return _l(it->text_id);
324 else
325 return it->text;
328 static void swap(submenu_list_t* a, submenu_list_t* b) {
329 submenu_list_t *pa, *nb;
330 pa = a->prev;
331 nb = b->next;
333 a->next = nb;
334 b->prev = pa;
335 b->next = a;
336 a->prev = b;
338 if (pa)
339 pa->next = b;
341 if (nb)
342 nb->prev = a;
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;
350 int sorted = 0;
352 if ((submenu == NULL) || (*submenu == NULL) || ((*submenu)->next == NULL))
353 return;
355 while (!sorted) {
356 sorted = 1;
358 submenu_list_t *tip = head;
360 while (tip->next) {
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);
368 if (cmp > 0) {
369 swap(tip, nxt);
371 if (tip == head)
372 head = nxt;
374 sorted = 0;
375 } else {
376 tip = tip->next;
381 *submenu = head;
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)
405 return;
406 else
407 cur = cur->next;
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;
431 if (cur) {
432 int itms = ((items_list_t*) gTheme->itemsList->extended)->displayedItems + 1;
433 while (--itms && cur->next)
434 cur = 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;
444 if (cur) {
445 int itms = ((items_list_t*) gTheme->itemsList->extended)->displayedItems + 1;
446 while (--itms && cur->prev)
447 cur = 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;
461 if (cur) {
462 while (cur->next)
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
469 cur = cur->prev;
471 selected_item->item->pagestart = cur;
475 void menuSetSelectedItem(menu_item_t* item) {
476 menu_list_t* itm = menu;
478 while (itm) {
479 if (itm->item == item) {
480 selected_item = itm;
481 return;
484 itm = itm->next;
488 void menuRenderMenu() {
489 guiDrawBGPlasma();
491 if (!mainMenu)
492 return;
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)
504 sitem = count;
507 int spacing = 25;
508 int y = (gTheme->usedHeight >> 1) - (spacing * (count >> 1));
509 int cp = 0; // current position
510 for (it = mainMenu; it; it = it->next, cp++) {
511 // render, advance
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);
514 y += spacing;
518 void menuHandleInputMenu() {
519 if (!mainMenu)
520 return;
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;
536 else
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) {
545 guiShowConfig();
546 } else if (id == MENU_GFX_SETTINGS) {
547 guiShowUIConfig();
548 } else if (id == MENU_IP_CONFIG) {
549 guiShowIPConfig();
550 } else if (id == MENU_SAVE_CHANGES) {
551 saveConfig(CONFIG_OPL | CONFIG_VMODE, 1);
552 } else if (id == MENU_START_HDL) {
553 handleHdlSrv();
554 } else if (id == MENU_ABOUT) {
555 guiShowAbout();
556 } else if (id == MENU_EXIT) {
557 sysExecExit();
558 } else if (id == MENU_POWER_OFF) {
559 sysPowerOff();
562 // so the exit press wont propagate twice
563 readPads();
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;
575 while (elem) {
576 if (elem->drawElem)
577 elem->drawElem(selected_item, selected_item->item->current, NULL, elem);
579 elem = elem->next;
583 void menuHandleInputMain() {
584 if(getKey(KEY_LEFT)) {
585 menuPrevH();
586 } else if(getKey(KEY_RIGHT)) {
587 menuNextH();
588 } else if(getKey(KEY_UP)) {
589 menuPrevV();
590 } else if(getKey(KEY_DOWN)){
591 menuNextV();
592 } else if(getKeyOn(KEY_CROSS)) {
593 if (selected_item->item->current && gUseInfoScreen && gTheme->infoElems.first)
594 guiSwitchScreen(GUI_SCREEN_INFO, TRANSITION_DOWN);
595 else
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
605 menuInitMainMenu();
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)) {
610 menuPrevPage();
611 } else if(getKey(KEY_R1)) {
612 menuNextPage();
613 } else if (getKeyOn(KEY_L2)) { // home
614 menuFirstPage();
615 } else if (getKeyOn(KEY_R2)) { // end
616 menuLastPage();
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
622 menuRequestConfig();
624 theme_element_t* elem = gTheme->infoElems.first;
625 while (elem) {
626 if (elem->drawElem)
627 elem->drawElem(selected_item, selected_item->item->current, itemConfig, elem);
629 elem = elem->next;
633 void menuHandleInputInfo() {
634 if(getKeyOn(KEY_CROSS)) {
635 selected_item->item->execCross(selected_item->item);
636 } else if(getKey(KEY_UP)) {
637 menuPrevV();
638 } else if(getKey(KEY_DOWN)){
639 menuNextV();
640 } else if(getKeyOn(KEY_CIRCLE)) {
641 guiSwitchScreen(GUI_SCREEN_MAIN, TRANSITION_UP);
642 } else if(getKey(KEY_L1)) {
643 menuPrevPage();
644 } else if(getKey(KEY_R1)) {
645 menuNextPage();
646 } else if (getKeyOn(KEY_L2)) {
647 menuFirstPage();
648 } else if (getKeyOn(KEY_R2)) {
649 menuLastPage();