1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2011-2017 - Daniel De Matteis
3 * Copyright (C) 2014-2017 - Jean-André Santoni
4 * Copyright (C) 2016-2019 - Brad Parker
5 * Copyright (C) 2018 - Alfredo Monclús
6 * Copyright (C) 2018-2020 - natinusala
7 * Copyright (C) 2019 - Patrick Scheurenbrand
8 *
9 * RetroArch is free software: you can redistribute it and/or modify it under the terms
10 * of the GNU General Public License as published by the Free Software Found-
11 * ation, either version 3 of the License, or (at your option) any later version.
12 *
13 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
14 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along with RetroArch.
18 * If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "ozone.h"
22 #include "ozone_display.h"
23 #include "ozone_theme.h"
24 #include "ozone_texture.h"
25 #include "ozone_sidebar.h"
26
27 #if 0
28 #include "discord/discord.h"
29 #endif
30
31 #include <file/file_path.h>
32 #include <string/stdstring.h>
33 #include <encodings/utf.h>
34 #include <features/features_cpu.h>
35 #include <formats/image.h>
36 #include <math/float_minmax.h>
37
38 #include "../../../gfx/gfx_animation.h"
39 #include "../../../gfx/gfx_display.h"
40 #include "../../runtime_file.h"
41
42 #include "../../input/input_osk.h"
43
44 #include "../../../configuration.h"
45 #include "../../../content.h"
46 #include "../../../core_info.h"
47 #include "../../../verbosity.h"
48
49 static const char *OZONE_TEXTURES_FILES[OZONE_TEXTURE_LAST] = {
50 "retroarch",
51 "cursor_border"
52 };
53
54 static const char *OZONE_TAB_TEXTURES_FILES[OZONE_TAB_TEXTURE_LAST] = {
55 "retroarch",
56 "settings",
57 "history",
58 "favorites",
59 "music",
60 "video",
61 "image",
62 "netplay",
63 "add"
64 };
65
ozone_set_thumbnail_content(void * data,const char * s)66 static void ozone_set_thumbnail_content(void *data, const char *s)
67 {
68 ozone_handle_t *ozone = (ozone_handle_t*)data;
69
70 if (!ozone)
71 return;
72
73 if (ozone->is_playlist)
74 {
75 /* Playlist content */
76 if (string_is_empty(s))
77 {
78 size_t selection = menu_navigation_get_selection();
79 size_t list_size = menu_entries_get_size();
80 file_list_t *list = menu_entries_get_selection_buf_ptr(0);
81
82 /* Get playlist index corresponding
83 * to the selected entry */
84 if (list &&
85 (selection < list_size) &&
86 (list->list[selection].type == FILE_TYPE_RPL_ENTRY))
87 {
88 size_t playlist_index = list->list[selection].entry_idx;
89 gfx_thumbnail_set_content_playlist(ozone->thumbnail_path_data,
90 playlist_get_cached(), playlist_index);
91 }
92 else
93 gfx_thumbnail_set_content_playlist(ozone->thumbnail_path_data,
94 NULL, selection);
95 }
96 }
97 else if (ozone->is_db_manager_list)
98 {
99 /* Database list content */
100 if (string_is_empty(s))
101 {
102 menu_entry_t entry;
103 size_t selection = menu_navigation_get_selection();
104
105 MENU_ENTRY_INIT(entry);
106 entry.label_enabled = false;
107 entry.rich_label_enabled = false;
108 entry.value_enabled = false;
109 entry.sublabel_enabled = false;
110 menu_entry_get(&entry, 0, selection, NULL, true);
111
112 if (!string_is_empty(entry.path))
113 gfx_thumbnail_set_content(ozone->thumbnail_path_data, entry.path);
114 }
115 }
116 else if (string_is_equal(s, "imageviewer"))
117 {
118 /* Filebrowser image updates */
119 menu_entry_t entry;
120 size_t selection = menu_navigation_get_selection();
121 file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
122 ozone_node_t *node = (ozone_node_t*)selection_buf->list[selection].userdata;
123
124 if (node)
125 {
126 MENU_ENTRY_INIT(entry);
127 entry.label_enabled = false;
128 entry.rich_label_enabled = false;
129 entry.value_enabled = false;
130 entry.sublabel_enabled = false;
131 menu_entry_get(&entry, 0, selection, NULL, true);
132 if (!string_is_empty(entry.path) && !string_is_empty(node->fullpath))
133 gfx_thumbnail_set_content_image(ozone->thumbnail_path_data, node->fullpath, entry.path);
134 }
135 }
136 else if (!string_is_empty(s))
137 {
138 /* Annoying leftovers...
139 * This is required to ensure that thumbnails are
140 * updated correctly when navigating deeply through
141 * the sublevels of database manager lists.
142 * Showing thumbnails on database entries is a
143 * pointless nuisance and a waste of CPU cycles, IMHO... */
144 gfx_thumbnail_set_content(ozone->thumbnail_path_data, s);
145 }
146
147 ozone_update_content_metadata(ozone);
148 }
149
150 /* Returns true if specified category is currently
151 * displayed on screen */
ozone_category_onscreen(ozone_handle_t * ozone,size_t idx)152 static bool INLINE ozone_category_onscreen(
153 ozone_handle_t *ozone, size_t idx)
154 {
155 return (idx >= ozone->first_onscreen_category) &&
156 (idx <= ozone->last_onscreen_category);
157 }
158
159 /* If current category is on screen, returns its
160 * index. If current category is off screen, returns
161 * index of centremost on screen category. */
ozone_get_onscreen_category_selection(ozone_handle_t * ozone)162 static size_t ozone_get_onscreen_category_selection(
163 ozone_handle_t *ozone)
164 {
165 /* Check whether selected category is already on screen */
166 if (ozone_category_onscreen(ozone, ozone->categories_selection_ptr))
167 return ozone->categories_selection_ptr;
168
169 /* Return index of centremost category */
170 return (ozone->first_onscreen_category >> 1) +
171 (ozone->last_onscreen_category >> 1);
172 }
173
174 /* Returns true if specified entry is currently
175 * displayed on screen */
176 /* Check whether selected item is already on screen */
177 #define OZONE_ENTRY_ONSCREEN(ozone, idx) (((idx) >= (ozone)->first_onscreen_entry) && ((idx) <= (ozone)->last_onscreen_entry))
178
179 /* If currently selected entry is off screen,
180 * moves selection to specified on screen target
181 * > Does nothing if currently selected item is
182 * already on screen */
ozone_auto_select_onscreen_entry(ozone_handle_t * ozone,enum ozone_onscreen_entry_position_type target_entry)183 static void ozone_auto_select_onscreen_entry(
184 ozone_handle_t *ozone,
185 enum ozone_onscreen_entry_position_type target_entry)
186 {
187 size_t selection = 0;
188
189 /* Update selection index */
190 switch (target_entry)
191 {
192 case OZONE_ONSCREEN_ENTRY_FIRST:
193 selection = ozone->first_onscreen_entry;
194 break;
195 case OZONE_ONSCREEN_ENTRY_LAST:
196 selection = ozone->last_onscreen_entry;
197 break;
198 case OZONE_ONSCREEN_ENTRY_CENTRE:
199 default:
200 selection = (ozone->first_onscreen_entry >> 1) +
201 (ozone->last_onscreen_entry >> 1);
202 break;
203 }
204
205 /* Apply new selection */
206 menu_navigation_set_selection(selection);
207 }
208
ozone_metadata_override_available(ozone_handle_t * ozone)209 static bool INLINE ozone_metadata_override_available(ozone_handle_t *ozone)
210 {
211 /* Ugly construct...
212 * Content metadata display override may be
213 * toggled if the following are true:
214 * - We are viewing playlist thumbnails
215 * - This is *not* an image viewer playlist
216 * - Both right and left thumbnails are
217 * enabled/available
218 * Short circuiting means that in most cases
219 * only 'ozone->is_playlist' will be evaluated,
220 * so this isn't too much of a performance hog... */
221 return ozone->is_playlist
222 && ozone->show_thumbnail_bar
223 && !ozone->selection_core_is_viewer
224 && (ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_MISSING)
225 && gfx_thumbnail_is_enabled(ozone->thumbnail_path_data,
226 GFX_THUMBNAIL_LEFT)
227 && (ozone->thumbnails.right.status != GFX_THUMBNAIL_STATUS_MISSING)
228 && gfx_thumbnail_is_enabled(ozone->thumbnail_path_data,
229 GFX_THUMBNAIL_RIGHT);
230 }
231
ozone_is_current_entry_settings(size_t current_selection)232 static bool ozone_is_current_entry_settings(size_t current_selection)
233 {
234 menu_entry_t last_entry;
235 const char *entry_value;
236
237 unsigned entry_type = 0;
238 enum msg_file_type entry_file_type = FILE_TYPE_NONE;
239
240 MENU_ENTRY_INIT(last_entry);
241 last_entry.path_enabled = false;
242 last_entry.label_enabled = false;
243 last_entry.rich_label_enabled = false;
244 last_entry.sublabel_enabled = false;
245
246 menu_entry_get(&last_entry, 0, current_selection, NULL, true);
247
248 if (last_entry.enum_idx == MENU_ENUM_LABEL_CHEEVOS_PASSWORD)
249 entry_value = last_entry.password_value;
250 else
251 entry_value = last_entry.value;
252
253 entry_file_type = msg_hash_to_file_type(msg_hash_calculate(entry_value));
254 entry_type = last_entry.type;
255
256 /* Logic below taken from materialui_pointer_up_swipe_horz_default */
257 if (!string_is_empty(entry_value))
258 {
259 /* Toggle switch off */
260 if (string_is_equal(entry_value, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)) ||
261 string_is_equal(entry_value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)))
262 {
263 return true;
264 }
265 /* Toggle switch on */
266 else if (string_is_equal(entry_value, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) ||
267 string_is_equal(entry_value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)))
268 {
269 return true;
270 }
271 /* Normal value text */
272 else
273 {
274 switch (entry_file_type)
275 {
276 case FILE_TYPE_IN_CARCHIVE:
277 case FILE_TYPE_MORE:
278 case FILE_TYPE_CORE:
279 case FILE_TYPE_DIRECT_LOAD:
280 case FILE_TYPE_RDB:
281 case FILE_TYPE_CURSOR:
282 case FILE_TYPE_PLAIN:
283 case FILE_TYPE_DIRECTORY:
284 case FILE_TYPE_MUSIC:
285 case FILE_TYPE_IMAGE:
286 case FILE_TYPE_MOVIE:
287 break;
288 case FILE_TYPE_COMPRESSED:
289 /* Note that we have to perform a backup check here,
290 * since the 'manual content scan - file extensions'
291 * setting may have a value of 'zip' or '7z' etc, which
292 * means it would otherwise get incorreclty identified as
293 * an archive file... */
294 if (entry_type != FILE_TYPE_CARCHIVE)
295 return true;
296 break;
297 default:
298 return true;
299 break;
300 }
301 }
302 }
303
304 return false;
305 }
306
ozone_parse_menu_entry_action(ozone_handle_t * ozone,settings_t * settings,enum menu_action action)307 static enum menu_action ozone_parse_menu_entry_action(
308 ozone_handle_t *ozone, settings_t *settings,
309 enum menu_action action)
310 {
311 uintptr_t tag;
312 int new_selection;
313 enum menu_action new_action = action;
314 file_list_t *selection_buf = NULL;
315 unsigned horizontal_list_size = 0;
316 bool menu_navigation_wraparound_enable;
317 bool is_current_entry_settings;
318 size_t selection;
319 size_t selection_total;
320
321 /* If fullscreen thumbnail view is active, any
322 * valid menu action will disable it... */
323 if (ozone->show_fullscreen_thumbnails)
324 {
325 if (action != MENU_ACTION_NOOP)
326 {
327 ozone_hide_fullscreen_thumbnails(ozone, true);
328
329 /* ...and any action other than Select/OK
330 * is ignored
331 * > We allow pass-through of Select/OK since
332 * users may want to run content directly
333 * after viewing fullscreen thumbnails,
334 * and having to press RetroPad A or the Return
335 * key twice is navigationally confusing
336 * > Note that we can only do this for non-pointer
337 * input
338 * > Note that we don't do this when viewing a
339 * file list, since there is no quick menu
340 * in this case - i.e. content loads directly,
341 * and a sudden transition from fullscreen
342 * thumbnail to content is jarring...
343 * > We also don't do this when viewing a database
344 * manager list, because the menu transition
345 * detection becomes too cumbersome... */
346 if (ozone->is_file_list ||
347 ozone->is_db_manager_list ||
348 ((action != MENU_ACTION_SELECT) &&
349 (action != MENU_ACTION_OK)))
350 return MENU_ACTION_NOOP;
351 }
352 }
353
354 horizontal_list_size = (unsigned)ozone->horizontal_list.size;
355
356 ozone->messagebox_state = menu_input_dialog_get_display_kb();
357 selection_buf = menu_entries_get_selection_buf_ptr(0);
358 tag = (uintptr_t)selection_buf;
359 selection = menu_navigation_get_selection();
360 selection_total = menu_entries_get_size();
361
362 menu_navigation_wraparound_enable = settings->bools.menu_navigation_wraparound_enable;
363
364 /* Don't wiggle left or right if the current entry is a setting. This is
365 partially wrong because some settings don't use left and right to change their value, such as
366 free input fields (passwords...). This is good enough. */
367 is_current_entry_settings = ozone_is_current_entry_settings(selection);
368
369 /* Scan user inputs */
370 switch (action)
371 {
372 case MENU_ACTION_START:
373 ozone->cursor_mode = false;
374 /* If this is a menu with thumbnails and cursor
375 * is not in the sidebar, attempt to show
376 * fullscreen thumbnail view */
377 if (ozone->fullscreen_thumbnails_available &&
378 !ozone->cursor_in_sidebar)
379 {
380 ozone_show_fullscreen_thumbnails(ozone);
381 new_action = MENU_ACTION_NOOP;
382 }
383 break;
384 case MENU_ACTION_DOWN:
385 if (ozone->cursor_in_sidebar)
386 {
387 /* If cursor is active, ensure we target
388 * an on screen category */
389 size_t selection = (ozone->cursor_mode)
390 ? ozone_get_onscreen_category_selection(ozone)
391 : ozone->categories_selection_ptr;
392
393 new_selection = (int)(selection + 1);
394
395 if (new_selection >= (int)(ozone->system_tab_end
396 + horizontal_list_size + 1))
397 new_selection = 0;
398
399 ozone_sidebar_goto(ozone, new_selection);
400
401 new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
402 ozone->cursor_mode = false;
403 break;
404 }
405 else if (!menu_navigation_wraparound_enable && selection == selection_total - 1)
406 {
407 ozone_start_cursor_wiggle(ozone, MENU_ACTION_DOWN);
408 }
409
410 /* If pointer is active and current selection
411 * is off screen, auto select *centre* item */
412 if (ozone->cursor_mode)
413 if (!OZONE_ENTRY_ONSCREEN(ozone, selection))
414 ozone_auto_select_onscreen_entry(ozone,
415 OZONE_ONSCREEN_ENTRY_CENTRE);
416 ozone->cursor_mode = false;
417 break;
418 case MENU_ACTION_UP:
419 if (ozone->cursor_in_sidebar)
420 {
421 /* If cursor is active, ensure we target
422 * an on screen category */
423 size_t selection = (ozone->cursor_mode)
424 ? ozone_get_onscreen_category_selection(ozone)
425 : ozone->categories_selection_ptr;
426 new_selection = (int)selection - 1;
427
428 if (new_selection < 0)
429 new_selection = horizontal_list_size + ozone->system_tab_end;
430
431 ozone_sidebar_goto(ozone, new_selection);
432
433 new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
434 ozone->cursor_mode = false;
435 break;
436 }
437 else if (!menu_navigation_wraparound_enable && selection == 0)
438 {
439 ozone_start_cursor_wiggle(ozone, MENU_ACTION_UP);
440 }
441
442 /* If pointer is active and current selection
443 * is off screen, auto select *centre* item */
444 if (ozone->cursor_mode)
445 if (!OZONE_ENTRY_ONSCREEN(ozone, selection))
446 ozone_auto_select_onscreen_entry(ozone,
447 OZONE_ONSCREEN_ENTRY_CENTRE);
448 ozone->cursor_mode = false;
449 break;
450 case MENU_ACTION_LEFT:
451 ozone->cursor_mode = false;
452
453 if (ozone->cursor_in_sidebar)
454 {
455 new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
456 ozone_start_cursor_wiggle(ozone, MENU_ACTION_LEFT);
457
458 break;
459 }
460 else if (ozone->depth > 1)
461 {
462 if (!menu_navigation_wraparound_enable && selection == 0 && !is_current_entry_settings)
463 {
464 /* Pressing left goes up but faster, so
465 wiggle up to say that there is nothing more upwards
466 even though the user pressed the left button */
467 ozone_start_cursor_wiggle(ozone, MENU_ACTION_DOWN);
468 }
469
470 break;
471 }
472
473 ozone_go_to_sidebar(ozone, settings, tag);
474
475 new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
476 break;
477 case MENU_ACTION_RIGHT:
478 ozone->cursor_mode = false;
479 if (!ozone->cursor_in_sidebar)
480 {
481 if (ozone->depth == 1)
482 {
483 new_action = MENU_ACTION_NOOP;
484 ozone_start_cursor_wiggle(ozone, MENU_ACTION_RIGHT);
485 }
486 else if (!menu_navigation_wraparound_enable && selection == selection_total - 1 && !is_current_entry_settings)
487 {
488 /* Pressing right goes down but faster, so
489 wiggle down to say that there is nothing more downwards
490 even though the user pressed the right button */
491 ozone_start_cursor_wiggle(ozone, MENU_ACTION_DOWN);
492 }
493
494 break;
495 }
496
497 ozone_leave_sidebar(ozone, settings, tag);
498
499 new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_LABEL;
500 break;
501 case MENU_ACTION_OK:
502 ozone->cursor_mode = false;
503 if (ozone->cursor_in_sidebar)
504 {
505 ozone_leave_sidebar(ozone, settings, tag);
506 new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_LABEL;
507 break;
508 }
509 break;
510 case MENU_ACTION_CANCEL:
511 ozone->cursor_mode = false;
512
513 /* If this is a playlist, handle 'backing out'
514 * of a search, if required */
515 if (ozone->is_playlist)
516 if (menu_entries_search_get_terms())
517 break;
518
519 if (ozone->cursor_in_sidebar)
520 {
521 /* Go back to main menu tab */
522 if (ozone->categories_selection_ptr != 0)
523 ozone_sidebar_goto(ozone, 0);
524
525 new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
526 break;
527 }
528
529 if (menu_entries_get_stack_size(0) == 1)
530 {
531 ozone_go_to_sidebar(ozone, settings, tag);
532 new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
533 }
534 break;
535
536 case MENU_ACTION_SCROLL_UP:
537 /* Descend alphabet (Z towards A) */
538
539 /* Ignore if cursor is in sidebar */
540 if (ozone->cursor_in_sidebar)
541 {
542 new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
543 break;
544 }
545
546 /* If pointer is active and current selection
547 * is off screen, auto select *last* item */
548 if (ozone->cursor_mode)
549 if (!OZONE_ENTRY_ONSCREEN(ozone, selection))
550 ozone_auto_select_onscreen_entry(ozone,
551 OZONE_ONSCREEN_ENTRY_LAST);
552 ozone->cursor_mode = false;
553 break;
554 case MENU_ACTION_SCROLL_DOWN:
555 /* Ascend alphabet (A towards Z) */
556
557 /* > Ignore if cursor is in sidebar */
558 if (ozone->cursor_in_sidebar)
559 {
560 new_action = MENU_ACTION_ACCESSIBILITY_SPEAK_TITLE;
561 break;
562 }
563
564 /* If pointer is active and current selection
565 * is off screen, auto select *first* item */
566 if (ozone->cursor_mode)
567 if (!OZONE_ENTRY_ONSCREEN(ozone, selection))
568 ozone_auto_select_onscreen_entry(ozone,
569 OZONE_ONSCREEN_ENTRY_FIRST);
570 ozone->cursor_mode = false;
571 break;
572
573 case MENU_ACTION_INFO:
574 /* If we currently viewing a playlist with
575 * dual thumbnails, toggle the content metadata
576 * override */
577 if (ozone_metadata_override_available(ozone))
578 {
579 ozone_toggle_metadata_override(ozone);
580 new_action = MENU_ACTION_NOOP;
581 }
582 /* ...and since the user is likely to trigger
583 * 'INFO' actions on invalid playlist entries,
584 * suppress this action entirely when viewing
585 * playlists under all other conditions
586 * > Playlists have no 'INFO' entries - the
587 * user is just greeted with a useless
588 * 'no information available' message
589 * > It is incredibly annoying to inadvertently
590 * trigger this message when you just want to
591 * toggle metadata... */
592 else if (ozone->is_playlist && ozone->show_thumbnail_bar)
593 new_action = MENU_ACTION_NOOP;
594
595 ozone->cursor_mode = false;
596 break;
597
598 default:
599 /* In all other cases, pass through input
600 * menu action without intervention */
601 break;
602 }
603
604 return new_action;
605 }
606
607
608 /* Menu entry action callback */
ozone_menu_entry_action(void * userdata,menu_entry_t * entry,size_t i,enum menu_action action)609 static int ozone_menu_entry_action(
610 void *userdata, menu_entry_t *entry,
611 size_t i, enum menu_action action)
612 {
613 menu_entry_t new_entry;
614 ozone_handle_t *ozone = (ozone_handle_t*)userdata;
615 menu_entry_t *entry_ptr = entry;
616 settings_t *settings = config_get_ptr();
617 size_t selection = i;
618 /* Process input action */
619 enum menu_action new_action = ozone_parse_menu_entry_action(ozone, settings,
620 action);
621 /* Check whether current selection has changed
622 * (due to automatic on screen entry selection...) */
623 size_t new_selection = menu_navigation_get_selection();
624
625 if (new_selection != selection)
626 {
627 /* Selection has changed - must update
628 * entry pointer */
629 MENU_ENTRY_INIT(new_entry);
630 new_entry.path_enabled = false;
631 new_entry.label_enabled = false;
632 new_entry.rich_label_enabled = false;
633 new_entry.value_enabled = false;
634 new_entry.sublabel_enabled = false;
635 menu_entry_get(&new_entry, 0, new_selection, NULL, true);
636 entry_ptr = &new_entry;
637 }
638
639 /* Call standard generic_menu_entry_action() function */
640 return generic_menu_entry_action(userdata, entry_ptr,
641 new_selection, new_action);
642 }
643
ozone_free_node(ozone_node_t * node)644 static void ozone_free_node(ozone_node_t *node)
645 {
646 if (!node)
647 return;
648
649 if (node->console_name)
650 free(node->console_name);
651
652 node->console_name = NULL;
653
654 if (node->fullpath)
655 free(node->fullpath);
656
657 node->fullpath = NULL;
658
659 free(node);
660 }
661
ozone_menu_animation_update_time(float * ticker_pixel_increment,unsigned video_width,unsigned video_height)662 static void ozone_menu_animation_update_time(
663 float *ticker_pixel_increment,
664 unsigned video_width, unsigned video_height)
665 {
666 gfx_display_t *p_disp = disp_get_ptr();
667 settings_t *settings = config_get_ptr();
668 /* Ozone uses DPI scaling
669 * > Smooth ticker scaling multiplier is
670 * gfx_display_get_dpi_scale() multiplied by
671 * a small correction factor to achieve a
672 * default scroll speed equal to that of the
673 * non-smooth ticker */
674 *(ticker_pixel_increment) *= gfx_display_get_dpi_scale(p_disp,
675 settings, video_width, video_height) * 0.5f;
676 }
677
ozone_init(void ** userdata,bool video_is_threaded)678 static void *ozone_init(void **userdata, bool video_is_threaded)
679 {
680 unsigned i;
681 bool fallback_color_theme = false;
682 unsigned width, height, color_theme = 0;
683 ozone_handle_t *ozone = NULL;
684 settings_t *settings = config_get_ptr();
685 gfx_animation_t *p_anim = anim_get_ptr();
686 gfx_display_t *p_disp = disp_get_ptr();
687 menu_handle_t *menu = (menu_handle_t*)calloc(1, sizeof(*menu));
688 const char *directory_assets = settings->paths.directory_assets;
689
690 if (!menu)
691 return NULL;
692
693 video_driver_get_size(&width, &height);
694
695 ozone = (ozone_handle_t*)calloc(1, sizeof(ozone_handle_t));
696
697 if (!ozone)
698 goto error;
699
700 *userdata = ozone;
701
702 for (i = 0; i < 15; i++)
703 ozone->pure_white[i] = 1.00f;
704
705 ozone->last_width = width;
706 ozone->last_height = height;
707 ozone->last_scale_factor = gfx_display_get_dpi_scale(p_disp,
708 settings, width, height);
709
710 file_list_initialize(&ozone->selection_buf_old);
711
712 ozone->draw_sidebar = true;
713 ozone->sidebar_offset = 0;
714 ozone->pending_message = NULL;
715 ozone->is_playlist = false;
716 ozone->categories_selection_ptr = 0;
717 ozone->pending_message = NULL;
718 ozone->show_cursor = false;
719 ozone->show_screensaver = false;
720
721 ozone->first_frame = true;
722 ozone->cursor_mode = false;
723
724 ozone->sidebar_collapsed = false;
725 ozone->animations.sidebar_text_alpha = 1.0f;
726
727 ozone->animations.thumbnail_bar_position = 0.0f;
728 ozone->show_thumbnail_bar = false;
729 ozone->pending_hide_thumbnail_bar = false;
730 ozone->dimensions_sidebar_width = 0.0f;
731
732 ozone->num_search_terms_old = 0;
733
734 ozone->cursor_wiggle_state.wiggling = false;
735
736 ozone->thumbnail_path_data = gfx_thumbnail_path_init();
737 if (!ozone->thumbnail_path_data)
738 goto error;
739
740 ozone->screensaver = menu_screensaver_init();
741 if (!ozone->screensaver)
742 goto error;
743
744 ozone->fullscreen_thumbnails_available = false;
745 ozone->show_fullscreen_thumbnails = false;
746 ozone->animations.fullscreen_thumbnail_alpha = 0.0f;
747 ozone->fullscreen_thumbnail_selection = 0;
748 ozone->fullscreen_thumbnail_label[0] = '\0';
749
750 ozone->animations.left_thumbnail_alpha = 1.0f;
751 ozone->force_metadata_display = false;
752
753 gfx_thumbnail_set_stream_delay(-1.0f);
754 gfx_thumbnail_set_fade_duration(-1.0f);
755 gfx_thumbnail_set_fade_missing(false);
756
757 ozone_sidebar_update_collapse(ozone, settings, false);
758
759 ozone->system_tab_end = 0;
760 ozone->tabs[ozone->system_tab_end] = OZONE_SYSTEM_TAB_MAIN;
761 if ( settings->bools.menu_content_show_settings
762 && !settings->bools.kiosk_mode_enable)
763 ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_SETTINGS;
764 if (settings->bools.menu_content_show_favorites)
765 ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_FAVORITES;
766 if (settings->bools.menu_content_show_history)
767 ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_HISTORY;
768 #ifdef HAVE_IMAGEVIEWER
769 if (settings->bools.menu_content_show_images)
770 ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_IMAGES;
771 #endif
772 if (settings->bools.menu_content_show_music)
773 ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_MUSIC;
774 #if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
775 if (settings->bools.menu_content_show_video)
776 ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_VIDEO;
777 #endif
778 #ifdef HAVE_NETWORKING
779 if (settings->bools.menu_content_show_netplay)
780 ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_NETPLAY;
781 #endif
782
783 if ( settings->bools.menu_content_show_add
784 && !settings->bools.kiosk_mode_enable)
785 ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_ADD;
786
787 #if defined(HAVE_LIBRETRODB)
788 if (settings->bools.menu_content_show_explore)
789 ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_EXPLORE;
790 #endif
791
792 menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL);
793
794 gfx_display_set_width(width);
795 gfx_display_set_height(height);
796
797 gfx_display_init_white_texture(gfx_display_white_texture);
798
799 file_list_initialize(&ozone->horizontal_list);
800
801 ozone_init_horizontal_list(ozone, settings);
802
803 /* Theme */
804 if (settings->bools.menu_use_preferred_system_color_theme)
805 {
806 #ifdef HAVE_LIBNX
807 if (R_SUCCEEDED(setsysInitialize()))
808 {
809 ColorSetId theme;
810 setsysGetColorSetId(&theme);
811 color_theme = (theme == ColorSetId_Dark) ? 1 : 0;
812 ozone_set_color_theme(ozone, color_theme);
813 configuration_set_uint(settings,
814 settings->uints.menu_ozone_color_theme, color_theme);
815 configuration_set_bool(settings,
816 settings->bools.menu_preferred_system_color_theme_set, true);
817 setsysExit();
818 }
819 else
820 #endif
821 fallback_color_theme = true;
822 }
823 else
824 fallback_color_theme = true;
825
826 if (fallback_color_theme)
827 {
828 color_theme = settings->uints.menu_ozone_color_theme;
829 ozone_set_color_theme(ozone, color_theme);
830 }
831
832 ozone->need_compute = false;
833 ozone->animations.scroll_y = 0.0f;
834 ozone->animations.scroll_y_sidebar = 0.0f;
835
836 ozone->first_onscreen_entry = 0;
837 ozone->last_onscreen_entry = 0;
838 ozone->first_onscreen_category = 0;
839 ozone->last_onscreen_category = 0;
840
841 /* Assets path */
842 fill_pathname_join(
843 ozone->assets_path,
844 directory_assets,
845 "ozone",
846 sizeof(ozone->assets_path)
847 );
848
849 /* PNG path */
850 fill_pathname_join(
851 ozone->png_path,
852 ozone->assets_path,
853 "png",
854 sizeof(ozone->png_path)
855 );
856
857 /* Sidebar path */
858 fill_pathname_join(
859 ozone->tab_path,
860 ozone->png_path,
861 "sidebar",
862 sizeof(ozone->tab_path)
863 );
864
865 /* Icons path */
866 fill_pathname_application_special(ozone->icons_path,
867 sizeof(ozone->icons_path),
868 APPLICATION_SPECIAL_DIRECTORY_ASSETS_OZONE_ICONS);
869
870 last_use_preferred_system_color_theme = settings->bools.menu_use_preferred_system_color_theme;
871 p_anim->updatetime_cb = ozone_menu_animation_update_time;
872
873 /* set word_wrap function pointer */
874 ozone->word_wrap = msg_hash_get_wideglyph_str() ? word_wrap_wideglyph : word_wrap;
875
876 return menu;
877
878 error:
879 if (ozone)
880 {
881 ozone_free_list_nodes(&ozone->horizontal_list, false);
882 ozone_free_list_nodes(&ozone->selection_buf_old, false);
883 file_list_deinitialize(&ozone->horizontal_list);
884 file_list_deinitialize(&ozone->selection_buf_old);
885 }
886
887 if (menu)
888 free(menu);
889
890 return NULL;
891 }
892
ozone_free(void * data)893 static void ozone_free(void *data)
894 {
895 ozone_handle_t *ozone = (ozone_handle_t*) data;
896
897 if (ozone)
898 {
899 video_coord_array_free(&ozone->fonts.footer.raster_block.carr);
900 video_coord_array_free(&ozone->fonts.title.raster_block.carr);
901 video_coord_array_free(&ozone->fonts.time.raster_block.carr);
902 video_coord_array_free(&ozone->fonts.entries_label.raster_block.carr);
903 video_coord_array_free(&ozone->fonts.entries_sublabel.raster_block.carr);
904 video_coord_array_free(&ozone->fonts.sidebar.raster_block.carr);
905
906 ozone_free_list_nodes(&ozone->selection_buf_old, false);
907 ozone_free_list_nodes(&ozone->horizontal_list, false);
908 file_list_deinitialize(&ozone->selection_buf_old);
909 file_list_deinitialize(&ozone->horizontal_list);
910
911 if (!string_is_empty(ozone->pending_message))
912 free(ozone->pending_message);
913
914 if (ozone->thumbnail_path_data)
915 free(ozone->thumbnail_path_data);
916
917 menu_screensaver_free(ozone->screensaver);
918 }
919
920 if (gfx_display_white_texture)
921 video_driver_texture_unload(&gfx_display_white_texture);
922
923 font_driver_bind_block(NULL, NULL);
924 }
925
ozone_update_thumbnail_image(void * data)926 static void ozone_update_thumbnail_image(void *data)
927 {
928 ozone_handle_t *ozone = (ozone_handle_t*)data;
929 size_t selection = menu_navigation_get_selection();
930 settings_t *settings = config_get_ptr();
931 playlist_t *playlist = playlist_get_cached();
932 unsigned gfx_thumbnail_upscale_threshold = settings->uints.gfx_thumbnail_upscale_threshold;
933 bool network_on_demand_thumbnails = settings->bools.network_on_demand_thumbnails;
934
935 if (!ozone)
936 return;
937
938 gfx_thumbnail_cancel_pending_requests();
939
940 gfx_thumbnail_request(
941 ozone->thumbnail_path_data,
942 GFX_THUMBNAIL_RIGHT,
943 playlist,
944 selection,
945 &ozone->thumbnails.right,
946 gfx_thumbnail_upscale_threshold,
947 network_on_demand_thumbnails
948 );
949
950 /* Image (and video/music) content requires special
951 * treatment... */
952 if (ozone->selection_core_is_viewer)
953 {
954 /* Left thumbnail is simply reset */
955 gfx_thumbnail_reset(&ozone->thumbnails.left);
956 }
957 else
958 {
959 /* Left thumbnail */
960 gfx_thumbnail_request(
961 ozone->thumbnail_path_data,
962 GFX_THUMBNAIL_LEFT,
963 playlist,
964 selection,
965 &ozone->thumbnails.left,
966 gfx_thumbnail_upscale_threshold,
967 network_on_demand_thumbnails);
968 }
969 }
970
ozone_refresh_thumbnail_image(void * data,unsigned i)971 static void ozone_refresh_thumbnail_image(void *data, unsigned i)
972 {
973 ozone_handle_t *ozone = (ozone_handle_t*)data;
974
975 if (!ozone)
976 return;
977
978 /* Only refresh thumbnails if thumbnails are enabled
979 * and we are currently viewing a playlist */
980 if ((gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) ||
981 gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT)) &&
982 (ozone->is_playlist && ozone->depth == 1))
983 ozone_update_thumbnail_image(ozone);
984 }
985
ozone_init_font(ozone_font_data_t * font_data,bool is_threaded,char * font_path,float font_size)986 static bool ozone_init_font(
987 ozone_font_data_t *font_data,
988 bool is_threaded, char *font_path, float font_size)
989 {
990 int glyph_width = 0;
991 gfx_display_t *p_disp = disp_get_ptr();
992 const char *wideglyph_str = msg_hash_get_wideglyph_str();
993
994 /* Free existing */
995 if (font_data->font)
996 {
997 gfx_display_font_free(font_data->font);
998 font_data->font = NULL;
999 }
1000
1001 /* Cache approximate dimensions */
1002 font_data->line_height = (int)(font_size + 0.5f);
1003 font_data->glyph_width = (int)((font_size * (3.0f / 4.0f)) + 0.5f);
1004
1005 /* Create font */
1006 font_data->font = gfx_display_font_file(p_disp,
1007 font_path, font_size, is_threaded);
1008
1009 if (!font_data->font)
1010 return false;
1011
1012 /* Get font metadata */
1013 glyph_width = font_driver_get_message_width(font_data->font, "a", 1, 1.0f);
1014 if (glyph_width > 0)
1015 font_data->glyph_width = glyph_width;
1016
1017 font_data->wideglyph_width = 100;
1018
1019 if (wideglyph_str)
1020 {
1021 int wideglyph_width =
1022 font_driver_get_message_width(font_data->font, wideglyph_str, strlen(wideglyph_str), 1.0f);
1023
1024 if (wideglyph_width > 0 && glyph_width > 0)
1025 font_data->wideglyph_width = wideglyph_width * 100 / glyph_width;
1026 }
1027
1028 font_data->line_height = font_driver_get_line_height(font_data->font, 1.0f);
1029 font_data->line_ascender = font_driver_get_line_ascender(font_data->font, 1.0f);
1030 font_data->line_centre_offset = font_driver_get_line_centre_offset(font_data->font, 1.0f);
1031
1032 return true;
1033 }
1034
ozone_cache_footer_label(ozone_handle_t * ozone,ozone_footer_label_t * label,enum msg_hash_enums enum_idx)1035 static void ozone_cache_footer_label(ozone_handle_t *ozone,
1036 ozone_footer_label_t *label, enum msg_hash_enums enum_idx)
1037 {
1038 const char *str = msg_hash_to_str(enum_idx);
1039 /* Determine pixel width */
1040 unsigned length = (unsigned)strlen(str);
1041
1042 /* Assign string */
1043 label->str = str;
1044 label->width = font_driver_get_message_width(
1045 ozone->fonts.footer.font,
1046 label->str, length, 1.0f);
1047 /* If font_driver_get_message_width() fails,
1048 * use predetermined glyph_width as a fallback */
1049 if (label->width < 0)
1050 label->width = length * ozone->fonts.footer.glyph_width;
1051 }
1052
1053 /* Assigns footer label strings (based on current
1054 * menu language) and calculates pixel widths */
ozone_cache_footer_labels(ozone_handle_t * ozone)1055 static void ozone_cache_footer_labels(ozone_handle_t *ozone)
1056 {
1057 ozone_cache_footer_label(
1058 ozone, &ozone->footer_labels.ok,
1059 MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_OK);
1060
1061 ozone_cache_footer_label(
1062 ozone, &ozone->footer_labels.back,
1063 MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_BACK);
1064
1065 ozone_cache_footer_label(
1066 ozone, &ozone->footer_labels.search,
1067 MENU_ENUM_LABEL_VALUE_SEARCH);
1068
1069 ozone_cache_footer_label(
1070 ozone, &ozone->footer_labels.fullscreen_thumbs,
1071 MSG_TOGGLE_FULLSCREEN_THUMBNAILS);
1072
1073 ozone_cache_footer_label(
1074 ozone, &ozone->footer_labels.metadata_toggle,
1075 MSG_TOGGLE_CONTENT_METADATA);
1076
1077 /* Record current language setting */
1078 ozone->footer_labels_language = *msg_hash_get_uint(MSG_HASH_USER_LANGUAGE);
1079 }
1080
1081 /* Determines the size of all menu elements */
ozone_set_layout(ozone_handle_t * ozone,settings_t * settings,bool is_threaded)1082 static void ozone_set_layout(
1083 ozone_handle_t *ozone,
1084 settings_t *settings,
1085 bool is_threaded)
1086 {
1087 char s1[PATH_MAX_LENGTH];
1088 char font_path[PATH_MAX_LENGTH];
1089 float scale_factor = 0.0f;
1090 bool font_inited = false;
1091
1092 font_path[0] = s1[0]= '\0';
1093
1094 if (!ozone)
1095 return;
1096
1097 scale_factor = ozone->last_scale_factor;
1098
1099 /* Calculate dimensions */
1100 ozone->dimensions.header_height = HEADER_HEIGHT * scale_factor;
1101 ozone->dimensions.footer_height = FOOTER_HEIGHT * scale_factor;
1102
1103 ozone->dimensions.entry_padding_horizontal_half = ENTRY_PADDING_HORIZONTAL_HALF * scale_factor;
1104 ozone->dimensions.entry_padding_horizontal_full = ENTRY_PADDING_HORIZONTAL_FULL * scale_factor;
1105 ozone->dimensions.entry_padding_vertical = ENTRY_PADDING_VERTICAL * scale_factor;
1106 ozone->dimensions.entry_height = ENTRY_HEIGHT * scale_factor;
1107 ozone->dimensions.entry_spacing = ENTRY_SPACING * scale_factor;
1108 ozone->dimensions.entry_icon_size = ENTRY_ICON_SIZE * scale_factor;
1109 ozone->dimensions.entry_icon_padding = ENTRY_ICON_PADDING * scale_factor;
1110
1111 ozone->dimensions.sidebar_entry_height = SIDEBAR_ENTRY_HEIGHT * scale_factor;
1112 ozone->dimensions.sidebar_padding_horizontal = SIDEBAR_X_PADDING * scale_factor;
1113 ozone->dimensions.sidebar_padding_vertical = SIDEBAR_Y_PADDING * scale_factor;
1114 ozone->dimensions.sidebar_entry_padding_vertical = SIDEBAR_ENTRY_Y_PADDING * scale_factor;
1115 ozone->dimensions.sidebar_entry_icon_size = SIDEBAR_ENTRY_ICON_SIZE * scale_factor;
1116 ozone->dimensions.sidebar_entry_icon_padding = SIDEBAR_ENTRY_ICON_PADDING * scale_factor;
1117 ozone->dimensions.sidebar_gradient_height = SIDEBAR_GRADIENT_HEIGHT * scale_factor;
1118
1119 ozone->dimensions.sidebar_width_normal = SIDEBAR_WIDTH * scale_factor;
1120 ozone->dimensions.sidebar_width_collapsed =
1121 ozone->dimensions.sidebar_entry_icon_size +
1122 ozone->dimensions.sidebar_entry_icon_padding * 2 +
1123 ozone->dimensions.sidebar_padding_horizontal * 2;
1124
1125 if (ozone->dimensions_sidebar_width == 0)
1126 ozone->dimensions_sidebar_width = (float)ozone->dimensions.sidebar_width_normal;
1127
1128 ozone->dimensions.thumbnail_bar_width =
1129 ozone->dimensions.sidebar_width_normal -
1130 ozone->dimensions.sidebar_entry_icon_size -
1131 ozone->dimensions.sidebar_entry_icon_padding;
1132
1133 ozone->dimensions.cursor_size = CURSOR_SIZE * scale_factor;
1134
1135 ozone->dimensions.fullscreen_thumbnail_padding = FULLSCREEN_THUMBNAIL_PADDING * scale_factor;
1136
1137 ozone->dimensions.spacer_1px = (scale_factor > 1.0f) ? (unsigned)(scale_factor + 0.5f) : 1;
1138 ozone->dimensions.spacer_2px = ozone->dimensions.spacer_1px * 2;
1139 ozone->dimensions.spacer_3px = (unsigned)((scale_factor * 3.0f) + 0.5f);
1140 ozone->dimensions.spacer_5px = (unsigned)((scale_factor * 5.0f) + 0.5f);
1141
1142 /* Determine movement delta size for activating
1143 * pointer input (note: not a dimension as such,
1144 * so not included in the 'dimensions' struct) */
1145 ozone->pointer_active_delta = CURSOR_ACTIVE_DELTA * scale_factor;
1146
1147 /* Initialise fonts */
1148 switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE))
1149 {
1150 case RETRO_LANGUAGE_ARABIC:
1151 case RETRO_LANGUAGE_PERSIAN:
1152 fill_pathname_application_special(s1, sizeof(s1),
1153 APPLICATION_SPECIAL_DIRECTORY_ASSETS_PKG);
1154 fill_pathname_join(font_path, s1, "fallback-font.ttf", sizeof(font_path));
1155 break;
1156 case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
1157 case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
1158 fill_pathname_application_special(s1, sizeof(s1),
1159 APPLICATION_SPECIAL_DIRECTORY_ASSETS_PKG);
1160 fill_pathname_join(font_path, s1, "chinese-fallback-font.ttf", sizeof(font_path));
1161 break;
1162 case RETRO_LANGUAGE_KOREAN:
1163 fill_pathname_application_special(s1, sizeof(s1),
1164 APPLICATION_SPECIAL_DIRECTORY_ASSETS_PKG);
1165 fill_pathname_join(font_path, s1, "korean-fallback-font.ttf", sizeof(font_path));
1166 break;
1167 default:
1168 fill_pathname_join(font_path, ozone->assets_path, "bold.ttf", sizeof(font_path));
1169 }
1170
1171 font_inited = ozone_init_font(&ozone->fonts.title,
1172 is_threaded, font_path, FONT_SIZE_TITLE * scale_factor);
1173 ozone->has_all_assets = ozone->has_all_assets && font_inited;
1174
1175 switch (*msg_hash_get_uint(MSG_HASH_USER_LANGUAGE))
1176 {
1177 case RETRO_LANGUAGE_ARABIC:
1178 case RETRO_LANGUAGE_PERSIAN:
1179 fill_pathname_application_special(s1, sizeof(s1),
1180 APPLICATION_SPECIAL_DIRECTORY_ASSETS_PKG);
1181 fill_pathname_join(font_path, s1, "fallback-font.ttf", sizeof(font_path));
1182 break;
1183 case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
1184 case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
1185 fill_pathname_application_special(s1, sizeof(s1),
1186 APPLICATION_SPECIAL_DIRECTORY_ASSETS_PKG);
1187 fill_pathname_join(font_path, s1, "chinese-fallback-font.ttf", sizeof(font_path));
1188 break;
1189 case RETRO_LANGUAGE_KOREAN:
1190 fill_pathname_application_special(s1, sizeof(s1),
1191 APPLICATION_SPECIAL_DIRECTORY_ASSETS_PKG);
1192 fill_pathname_join(font_path, s1, "korean-fallback-font.ttf", sizeof(font_path));
1193 break;
1194 default:
1195 fill_pathname_join(font_path, ozone->assets_path, "regular.ttf", sizeof(font_path));
1196 }
1197
1198 font_inited = ozone_init_font(&ozone->fonts.footer,
1199 is_threaded, font_path, FONT_SIZE_FOOTER * scale_factor);
1200 ozone->has_all_assets = ozone->has_all_assets && font_inited;
1201
1202 font_inited = ozone_init_font(&ozone->fonts.time,
1203 is_threaded, font_path, FONT_SIZE_TIME * scale_factor);
1204 ozone->has_all_assets = ozone->has_all_assets && font_inited;
1205
1206 font_inited = ozone_init_font(&ozone->fonts.entries_label,
1207 is_threaded, font_path, FONT_SIZE_ENTRIES_LABEL * scale_factor);
1208 ozone->has_all_assets = ozone->has_all_assets && font_inited;
1209
1210 font_inited = ozone_init_font(&ozone->fonts.entries_sublabel,
1211 is_threaded, font_path, FONT_SIZE_ENTRIES_SUBLABEL * scale_factor);
1212 ozone->has_all_assets = ozone->has_all_assets && font_inited;
1213
1214 font_inited = ozone_init_font(&ozone->fonts.sidebar,
1215 is_threaded, font_path, FONT_SIZE_SIDEBAR * scale_factor);
1216 ozone->has_all_assets = ozone->has_all_assets && font_inited;
1217
1218 /* Cache footer text labels
1219 * > Fonts have been (re)initialised, so need
1220 * to recalculate label widths */
1221 ozone_cache_footer_labels(ozone);
1222
1223 /* Multiple sidebar parameters are set via animations
1224 * > ozone_refresh_sidebars() cancels any existing
1225 * animations and 'force updates' the affected
1226 * variables with newly scaled values */
1227 ozone_refresh_sidebars(ozone, settings, ozone->last_height);
1228
1229 /* Entry dimensions must be recalculated after
1230 * updating menu layout */
1231 ozone->need_compute = true;
1232 }
1233
ozone_context_reset(void * data,bool is_threaded)1234 static void ozone_context_reset(void *data, bool is_threaded)
1235 {
1236 unsigned i;
1237 ozone_handle_t *ozone = (ozone_handle_t*) data;
1238 settings_t *settings = config_get_ptr();
1239
1240 if (ozone)
1241 {
1242 ozone->has_all_assets = true;
1243
1244 ozone_set_layout(ozone, settings, is_threaded);
1245
1246 /* Textures init */
1247 for (i = 0; i < OZONE_TEXTURE_LAST; i++)
1248 {
1249 char filename[PATH_MAX_LENGTH];
1250 filename[0] = '\0';
1251 #if 0
1252 if (i == OZONE_TEXTURE_DISCORD_OWN_AVATAR && discord_avatar_is_ready())
1253 strlcpy(filename, discord_get_own_avatar(), sizeof(filename));
1254 else
1255 #endif
1256 strlcpy(filename, OZONE_TEXTURES_FILES[i], sizeof(filename));
1257
1258 strlcat(filename, FILE_PATH_PNG_EXTENSION, sizeof(filename));
1259
1260 #if 0
1261 if (i == OZONE_TEXTURE_DISCORD_OWN_AVATAR && discord_avatar_is_ready())
1262 {
1263 char buf[PATH_MAX_LENGTH];
1264 fill_pathname_application_special(buf,
1265 sizeof(buf),
1266 APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_DISCORD_AVATARS);
1267 if (!gfx_display_reset_textures_list(filename, buf, &ozone->textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
1268 RARCH_WARN("[OZONE] Asset missing: %s%s%s\n", ozone->png_path,
1269 PATH_DEFAULT_SLASH(), filename);
1270 }
1271 else
1272 {
1273 #endif
1274 if (!gfx_display_reset_textures_list(filename, ozone->png_path, &ozone->textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
1275 ozone->has_all_assets = false;
1276 #if 0
1277 }
1278 #endif
1279 }
1280
1281 /* Sidebar textures */
1282 for (i = 0; i < OZONE_TAB_TEXTURE_LAST; i++)
1283 {
1284 char filename[PATH_MAX_LENGTH];
1285
1286 filename[0] = '\0';
1287 strlcpy(filename,
1288 OZONE_TAB_TEXTURES_FILES[i], sizeof(filename));
1289 strlcat(filename, FILE_PATH_PNG_EXTENSION, sizeof(filename));
1290
1291 if (!gfx_display_reset_textures_list(filename, ozone->tab_path, &ozone->tab_textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
1292 {
1293 ozone->has_all_assets = false;
1294 RARCH_WARN("[OZONE] Asset missing: %s%s%s\n", ozone->tab_path,
1295 PATH_DEFAULT_SLASH(), filename);
1296 }
1297 }
1298
1299 /* Theme textures */
1300 if (!ozone_reset_theme_textures(ozone))
1301 ozone->has_all_assets = false;
1302
1303 /* Icons textures init */
1304 for (i = 0; i < OZONE_ENTRIES_ICONS_TEXTURE_LAST; i++)
1305 {
1306 if (!gfx_display_reset_textures_list(ozone_entries_icon_texture_path(i), ozone->icons_path, &ozone->icons_textures[i], TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
1307 {
1308 ozone->has_all_assets = false;
1309 RARCH_WARN("[OZONE] Asset missing: %s%s%s\n", ozone->icons_path,
1310 PATH_DEFAULT_SLASH(), ozone_entries_icon_texture_path(i));
1311 }
1312 }
1313
1314 if (gfx_display_white_texture)
1315 video_driver_texture_unload(&gfx_display_white_texture);
1316 gfx_display_init_white_texture(gfx_display_white_texture);
1317
1318 /* Horizontal list */
1319 ozone_context_reset_horizontal_list(ozone);
1320
1321 /* State reset */
1322 ozone->fade_direction = false;
1323 ozone->cursor_in_sidebar = false;
1324 ozone->cursor_in_sidebar_old = false;
1325 ozone->draw_old_list = false;
1326 ozone->messagebox_state = false;
1327 ozone->messagebox_state_old = false;
1328
1329 ozone->cursor_wiggle_state.wiggling = false;
1330
1331 /* Animations */
1332 ozone->animations.cursor_alpha = 1.0f;
1333 ozone->animations.scroll_y = 0.0f;
1334 ozone->animations.list_alpha = 1.0f;
1335
1336 /* Missing assets message */
1337 if (!ozone->has_all_assets)
1338 runloop_msg_queue_push(msg_hash_to_str(MSG_MISSING_ASSETS), 1, 256, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
1339
1340 /* Thumbnails */
1341 ozone_update_thumbnail_image(ozone);
1342
1343 /* TODO: update savestate thumbnail image */
1344
1345 ozone_restart_cursor_animation(ozone);
1346
1347 /* Screensaver */
1348 menu_screensaver_context_destroy(ozone->screensaver);
1349 }
1350 video_driver_monitor_reset();
1351 }
1352
ozone_collapse_end(void * userdata)1353 static void ozone_collapse_end(void *userdata)
1354 {
1355 ozone_handle_t *ozone = (ozone_handle_t*) userdata;
1356 ozone->draw_sidebar = false;
1357 }
1358
ozone_unload_thumbnail_textures(void * data)1359 static void ozone_unload_thumbnail_textures(void *data)
1360 {
1361 ozone_handle_t *ozone = (ozone_handle_t*)data;
1362 if (!ozone)
1363 return;
1364
1365 gfx_thumbnail_cancel_pending_requests();
1366 gfx_thumbnail_reset(&ozone->thumbnails.right);
1367 gfx_thumbnail_reset(&ozone->thumbnails.left);
1368 }
1369
ozone_font_free(ozone_font_data_t * font_data)1370 static void INLINE ozone_font_free(ozone_font_data_t *font_data)
1371 {
1372 if (font_data->font)
1373 gfx_display_font_free(font_data->font);
1374
1375 font_data->font = NULL;
1376 }
1377
ozone_context_destroy(void * data)1378 static void ozone_context_destroy(void *data)
1379 {
1380 unsigned i;
1381 uintptr_t tag;
1382 ozone_handle_t *ozone = (ozone_handle_t*) data;
1383
1384 if (!ozone)
1385 return;
1386
1387 /* Theme */
1388 ozone_unload_theme_textures(ozone);
1389
1390 /* Icons */
1391 for (i = 0; i < OZONE_ENTRIES_ICONS_TEXTURE_LAST; i++)
1392 video_driver_texture_unload(&ozone->icons_textures[i]);
1393
1394 /* Textures */
1395 for (i = 0; i < OZONE_TEXTURE_LAST; i++)
1396 video_driver_texture_unload(&ozone->textures[i]);
1397
1398 /* Icons */
1399 for (i = 0; i < OZONE_TAB_TEXTURE_LAST; i++)
1400 video_driver_texture_unload(&ozone->tab_textures[i]);
1401
1402 /* Thumbnails */
1403 ozone_unload_thumbnail_textures(ozone);
1404
1405 video_driver_texture_unload(&gfx_display_white_texture);
1406
1407 /* Fonts */
1408 ozone_font_free(&ozone->fonts.footer);
1409 ozone_font_free(&ozone->fonts.title);
1410 ozone_font_free(&ozone->fonts.time);
1411 ozone_font_free(&ozone->fonts.entries_label);
1412 ozone_font_free(&ozone->fonts.entries_sublabel);
1413 ozone_font_free(&ozone->fonts.sidebar);
1414
1415 tag = (uintptr_t) &ozone_default_theme;
1416 gfx_animation_kill_by_tag(&tag);
1417
1418 /* Horizontal list */
1419 ozone_context_destroy_horizontal_list(ozone);
1420
1421 /* Screensaver */
1422 menu_screensaver_context_destroy(ozone->screensaver);
1423 }
1424
ozone_list_get_entry(void * data,enum menu_list_type type,unsigned i)1425 static void *ozone_list_get_entry(void *data,
1426 enum menu_list_type type, unsigned i)
1427 {
1428 size_t list_size = 0;
1429 ozone_handle_t* ozone = (ozone_handle_t*) data;
1430
1431 switch (type)
1432 {
1433 case MENU_LIST_PLAIN:
1434 {
1435 file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0);
1436 list_size = menu_entries_get_stack_size(0);
1437 if (i < list_size)
1438 return (void*)&menu_stack->list[i];
1439 }
1440 break;
1441 case MENU_LIST_HORIZONTAL:
1442 list_size = ozone->horizontal_list.size;
1443 if (i < list_size)
1444 return (void*)&ozone->horizontal_list.list[i];
1445 break;
1446 default:
1447 break;
1448 }
1449
1450 return NULL;
1451 }
1452
ozone_list_push(void * data,void * userdata,menu_displaylist_info_t * info,unsigned type)1453 static int ozone_list_push(void *data, void *userdata,
1454 menu_displaylist_info_t *info, unsigned type)
1455 {
1456 int ret = -1;
1457 core_info_list_t *list = NULL;
1458
1459 switch (type)
1460 {
1461 case DISPLAYLIST_LOAD_CONTENT_LIST:
1462 {
1463 settings_t *settings = config_get_ptr();
1464 bool menu_content_show_playlists = settings->bools.menu_content_show_playlists;
1465 bool kiosk_mode_enable = settings->bools.kiosk_mode_enable;
1466
1467 menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
1468
1469 menu_entries_append_enum(info->list,
1470 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES),
1471 msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES),
1472 MENU_ENUM_LABEL_FAVORITES,
1473 MENU_SETTING_ACTION_FAVORITES_DIR, 0, 0);
1474
1475 core_info_get_list(&list);
1476 if (list->info_count > 0)
1477 {
1478 menu_entries_append_enum(info->list,
1479 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DOWNLOADED_FILE_DETECT_CORE_LIST),
1480 msg_hash_to_str(MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST),
1481 MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST,
1482 MENU_SETTING_ACTION, 0, 0);
1483 }
1484
1485 if (menu_content_show_playlists)
1486 menu_entries_append_enum(info->list,
1487 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLISTS_TAB),
1488 msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB),
1489 MENU_ENUM_LABEL_PLAYLISTS_TAB,
1490 MENU_SETTING_ACTION, 0, 0);
1491
1492 if (frontend_driver_parse_drive_list(info->list, true) != 0)
1493 menu_entries_append_enum(info->list, "/",
1494 msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
1495 MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR,
1496 MENU_SETTING_ACTION, 0, 0);
1497
1498 if (!kiosk_mode_enable)
1499 {
1500 menu_entries_append_enum(info->list,
1501 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MENU_FILE_BROWSER_SETTINGS),
1502 msg_hash_to_str(MENU_ENUM_LABEL_MENU_FILE_BROWSER_SETTINGS),
1503 MENU_ENUM_LABEL_MENU_FILE_BROWSER_SETTINGS,
1504 MENU_SETTING_ACTION, 0, 0);
1505 }
1506
1507 info->need_push = true;
1508 info->need_refresh = true;
1509 ret = 0;
1510 }
1511 break;
1512 case DISPLAYLIST_MAIN_MENU:
1513 {
1514 settings_t *settings = config_get_ptr();
1515 rarch_system_info_t *system = runloop_get_system_info();
1516 menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
1517
1518 if (rarch_ctl(RARCH_CTL_CORE_IS_RUNNING, NULL))
1519 {
1520 if (!rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL))
1521 {
1522 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1523 info->list,
1524 MENU_ENUM_LABEL_CONTENT_SETTINGS,
1525 PARSE_ACTION,
1526 false);
1527 }
1528 }
1529 else
1530 {
1531 if (system->load_no_content)
1532 {
1533 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1534 info->list,
1535 MENU_ENUM_LABEL_START_CORE,
1536 PARSE_ACTION,
1537 false);
1538 }
1539
1540 #ifndef HAVE_DYNAMIC
1541 if (frontend_driver_has_fork())
1542 #endif
1543 {
1544 if (settings->bools.menu_show_load_core)
1545 {
1546 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1547 info->list,
1548 MENU_ENUM_LABEL_CORE_LIST,
1549 PARSE_ACTION,
1550 false);
1551 }
1552 }
1553 }
1554
1555 if (settings->bools.menu_show_load_content)
1556 {
1557 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1558 info->list,
1559 MENU_ENUM_LABEL_LOAD_CONTENT_LIST,
1560 PARSE_ACTION,
1561 false);
1562
1563 if (menu_displaylist_has_subsystems())
1564 {
1565 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1566 info->list,
1567 MENU_ENUM_LABEL_SUBSYSTEM_SETTINGS,
1568 PARSE_ACTION,
1569 false);
1570 }
1571 }
1572
1573 if (settings->bools.menu_show_load_disc)
1574 {
1575 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1576 info->list,
1577 MENU_ENUM_LABEL_LOAD_DISC,
1578 PARSE_ACTION,
1579 false);
1580 }
1581
1582 if (settings->bools.menu_show_dump_disc)
1583 {
1584 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1585 info->list,
1586 MENU_ENUM_LABEL_DUMP_DISC,
1587 PARSE_ACTION,
1588 false);
1589 }
1590
1591 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1592 info->list,
1593 MENU_ENUM_LABEL_ADD_CONTENT_LIST,
1594 PARSE_ACTION,
1595 false);
1596 #ifdef HAVE_QT
1597 if (settings->bools.desktop_menu_enable)
1598 {
1599 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1600 info->list,
1601 MENU_ENUM_LABEL_SHOW_WIMP,
1602 PARSE_ACTION,
1603 false);
1604 }
1605 #endif
1606 #if defined(HAVE_NETWORKING)
1607 #if defined(HAVE_ONLINE_UPDATER)
1608 if (settings->bools.menu_show_online_updater && !settings->bools.kiosk_mode_enable)
1609 {
1610 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1611 info->list,
1612 MENU_ENUM_LABEL_ONLINE_UPDATER,
1613 PARSE_ACTION,
1614 false);
1615 }
1616 #endif
1617 #endif
1618 if (!settings->bools.menu_content_show_settings && !string_is_empty(settings->paths.menu_content_show_settings_password))
1619 {
1620 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1621 info->list,
1622 MENU_ENUM_LABEL_XMB_MAIN_MENU_ENABLE_SETTINGS,
1623 PARSE_ACTION,
1624 false);
1625 }
1626
1627 if (settings->bools.kiosk_mode_enable && !string_is_empty(settings->paths.kiosk_mode_password))
1628 {
1629 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1630 info->list,
1631 MENU_ENUM_LABEL_MENU_DISABLE_KIOSK_MODE,
1632 PARSE_ACTION,
1633 false);
1634 }
1635
1636 if (settings->bools.menu_show_information)
1637 {
1638 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1639 info->list,
1640 MENU_ENUM_LABEL_INFORMATION_LIST,
1641 PARSE_ACTION,
1642 false);
1643 }
1644
1645 #if defined(HAVE_LAKKA_SWITCH) || defined(HAVE_LIBNX)
1646 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1647 info->list,
1648 MENU_ENUM_LABEL_SWITCH_CPU_PROFILE,
1649 PARSE_ACTION,
1650 false);
1651 #endif
1652
1653 #ifdef HAVE_LAKKA_SWITCH
1654 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1655 info->list,
1656 MENU_ENUM_LABEL_SWITCH_GPU_PROFILE,
1657 PARSE_ACTION,
1658 false);
1659 #endif
1660
1661 if (settings->bools.menu_show_configurations && !settings->bools.kiosk_mode_enable)
1662 {
1663 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1664 info->list,
1665 MENU_ENUM_LABEL_CONFIGURATIONS_LIST,
1666 PARSE_ACTION,
1667 false);
1668 }
1669
1670 if (settings->bools.menu_show_help)
1671 {
1672 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1673 info->list,
1674 MENU_ENUM_LABEL_HELP_LIST,
1675 PARSE_ACTION,
1676 false);
1677 }
1678
1679 #if !defined(IOS)
1680 if (settings->bools.menu_show_restart_retroarch)
1681 {
1682 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1683 info->list,
1684 MENU_ENUM_LABEL_RESTART_RETROARCH,
1685 PARSE_ACTION,
1686 false);
1687 }
1688
1689 if (settings->bools.menu_show_quit_retroarch)
1690 {
1691 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1692 info->list,
1693 MENU_ENUM_LABEL_QUIT_RETROARCH,
1694 PARSE_ACTION,
1695 false);
1696 }
1697 #endif
1698
1699 if (settings->bools.menu_show_reboot)
1700 {
1701 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1702 info->list,
1703 MENU_ENUM_LABEL_REBOOT,
1704 PARSE_ACTION,
1705 false);
1706 }
1707
1708 if (settings->bools.menu_show_shutdown)
1709 {
1710 MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(
1711 info->list,
1712 MENU_ENUM_LABEL_SHUTDOWN,
1713 PARSE_ACTION,
1714 false);
1715 }
1716
1717 info->need_push = true;
1718 ret = 0;
1719 }
1720 break;
1721 }
1722 return ret;
1723 }
1724
ozone_list_get_selection(void * data)1725 static size_t ozone_list_get_selection(void *data)
1726 {
1727 ozone_handle_t *ozone = (ozone_handle_t*)data;
1728
1729 if (!ozone)
1730 return 0;
1731
1732 return ozone->categories_selection_ptr;
1733 }
1734
ozone_list_clear(file_list_t * list)1735 static void ozone_list_clear(file_list_t *list)
1736 {
1737 uintptr_t tag = (uintptr_t)list;
1738 gfx_animation_kill_by_tag(&tag);
1739
1740 ozone_free_list_nodes(list, false);
1741 }
1742
ozone_list_free(file_list_t * list,size_t a,size_t b)1743 static void ozone_list_free(file_list_t *list, size_t a, size_t b)
1744 {
1745 ozone_list_clear(list);
1746 }
1747
ozone_render(void * data,unsigned width,unsigned height,bool is_idle)1748 static void ozone_render(void *data,
1749 unsigned width, unsigned height,
1750 bool is_idle)
1751 {
1752 size_t i;
1753 /* c.f. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
1754 * On some platforms (e.g. 32-bit x86 without SSE),
1755 * gcc can produce inconsistent floating point results
1756 * depending upon optimisation level. This can break
1757 * floating point variable comparisons. A workaround is
1758 * to declare the affected variable as 'volatile', which
1759 * disables optimisations and removes excess precision
1760 * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323#c87) */
1761 volatile float scale_factor;
1762 unsigned entries_end = (unsigned)menu_entries_get_size();
1763 bool pointer_enabled = false;
1764 unsigned language = *msg_hash_get_uint(MSG_HASH_USER_LANGUAGE);
1765 ozone_handle_t *ozone = (ozone_handle_t*)data;
1766 gfx_display_t *p_disp = disp_get_ptr();
1767 gfx_animation_t *p_anim = anim_get_ptr();
1768 settings_t *settings = config_get_ptr();
1769 if (!ozone)
1770 return;
1771
1772 /* Check whether screen dimensions or menu scale
1773 * factor have changed */
1774 scale_factor = gfx_display_get_dpi_scale(p_disp, settings, width, height);
1775
1776 if ((scale_factor != ozone->last_scale_factor) ||
1777 (width != ozone->last_width) ||
1778 (height != ozone->last_height))
1779 {
1780 ozone->last_scale_factor = scale_factor;
1781 ozone->last_width = width;
1782 ozone->last_height = height;
1783
1784 /* Note: We don't need a full context reset here
1785 * > Just rescale layout, and reset frame time counter */
1786 ozone_set_layout(ozone, settings, video_driver_is_threaded());
1787 video_driver_monitor_reset();
1788 }
1789
1790 if (ozone->need_compute)
1791 {
1792 ozone_compute_entries_position(ozone, settings, entries_end);
1793 ozone->need_compute = false;
1794 }
1795
1796 /* Check whether menu language has changed
1797 * > If so, need to re-cache footer text labels */
1798 if (ozone->footer_labels_language != language)
1799 {
1800 ozone->footer_labels_language = language;
1801 ozone_cache_footer_labels(ozone);
1802 }
1803
1804 ozone->selection = menu_navigation_get_selection();
1805
1806 /* Need to update this each frame, otherwise touchscreen
1807 * input breaks when changing orientation */
1808 p_disp->framebuf_width = width;
1809 p_disp->framebuf_height = height;
1810
1811 /* Read pointer state */
1812 menu_input_get_pointer_state(&ozone->pointer);
1813
1814 /* If menu screensaver is active, update
1815 * screensaver and return */
1816 if (ozone->show_screensaver)
1817 {
1818 menu_screensaver_iterate(
1819 ozone->screensaver,
1820 p_disp, p_anim,
1821 (enum menu_screensaver_effect)settings->uints.menu_screensaver_animation,
1822 settings->floats.menu_screensaver_animation_speed,
1823 ozone->theme->screensaver_tint,
1824 width, height,
1825 settings->paths.directory_assets);
1826 GFX_ANIMATION_CLEAR_ACTIVE(p_anim);
1827 return;
1828 }
1829
1830 /* Check whether pointer is enabled */
1831 if (ozone->pointer.type != MENU_POINTER_DISABLED)
1832 {
1833 /* When using a mouse, entry under pointer is
1834 * automatically selected
1835 * > Must therefore filter out small movements,
1836 * otherwise scrolling with the mouse wheel
1837 * becomes impossible... */
1838 if (ozone->pointer.type == MENU_POINTER_MOUSE)
1839 {
1840 int16_t cursor_x_delta = ozone->pointer.x - ozone->cursor_x_old;
1841 int16_t cursor_y_delta = ozone->pointer.y - ozone->cursor_y_old;
1842
1843 if ((cursor_x_delta > ozone->pointer_active_delta) ||
1844 (cursor_x_delta < -ozone->pointer_active_delta) ||
1845 (cursor_y_delta > ozone->pointer_active_delta) ||
1846 (cursor_y_delta < -ozone->pointer_active_delta))
1847 ozone->cursor_mode = true;
1848 }
1849 /* On touchscreens, just check for any movement */
1850 else
1851 {
1852 if ((ozone->pointer.x != ozone->cursor_x_old) ||
1853 (ozone->pointer.y != ozone->cursor_y_old))
1854 ozone->cursor_mode = true;
1855 }
1856 }
1857
1858 ozone->cursor_x_old = ozone->pointer.x;
1859 ozone->cursor_y_old = ozone->pointer.y;
1860
1861 /* Pointer is disabled when:
1862 * - Showing fullscreen thumbnails
1863 * - On-screen keyboard is active
1864 * - A message box is being displayed */
1865 pointer_enabled = ozone->cursor_mode &&
1866 !ozone->show_fullscreen_thumbnails &&
1867 !menu_input_dialog_get_display_kb() &&
1868 !ozone->should_draw_messagebox;
1869
1870 /* Process pointer input, if required */
1871 if (pointer_enabled)
1872 {
1873 file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
1874 uintptr_t animation_tag = (uintptr_t)selection_buf;
1875
1876 int entry_padding = (ozone->depth == 1) ?
1877 ozone->dimensions.entry_padding_horizontal_half :
1878 ozone->dimensions.entry_padding_horizontal_full;
1879 float entry_x = ozone->dimensions_sidebar_width +
1880 ozone->sidebar_offset + entry_padding;
1881 float entry_width = width - ozone->dimensions_sidebar_width -
1882 ozone->sidebar_offset - entry_padding * 2 -
1883 ozone->animations.thumbnail_bar_position;
1884 bool first_entry_found = false;
1885 bool last_entry_found = false;
1886
1887 unsigned horizontal_list_size = (unsigned)ozone->horizontal_list.size;
1888 float category_height = ozone->dimensions.sidebar_entry_height +
1889 ozone->dimensions.sidebar_entry_padding_vertical;
1890 bool first_category_found = false;
1891 bool last_category_found = false;
1892
1893 /* Check whether pointer is operating on entries
1894 * or sidebar
1895 * > Note 1: Since touchscreens effectively 'lose their
1896 * place' when a touch is released, we can only perform
1897 * this this check if the pointer is currently
1898 * pressed - i.e. we must preserve the values set the
1899 * last time the screen was touched.
1900 * With mouse input we have a permanent cursor, so this
1901 * is not an issue
1902 * > Note 2: Windows seems to report negative pointer
1903 * coordinates when the cursor goes off the left hand
1904 * side of the screen/window, so checking whether
1905 * pointer.x is less than the effective sidebar width
1906 * generates a false positive when ozone->depth > 1.
1907 * We therefore must also check whether the sidebar
1908 * is currently being drawn */
1909 ozone->last_pointer_in_sidebar = ozone->pointer_in_sidebar;
1910 if ((ozone->pointer.type == MENU_POINTER_MOUSE) ||
1911 ozone->pointer.pressed)
1912 ozone->pointer_in_sidebar = ozone->draw_sidebar &&
1913 (ozone->pointer.x < ozone->dimensions_sidebar_width
1914 + ozone->sidebar_offset);
1915
1916 /* If pointer has switched from entries to sidebar
1917 * or vice versa, must reset pointer acceleration */
1918 if (ozone->pointer_in_sidebar != ozone->last_pointer_in_sidebar)
1919 {
1920 menu_input_set_pointer_y_accel(0.0f);
1921 ozone->pointer.y_accel = 0.0f;
1922 }
1923
1924 /* If pointer is a mouse, then automatically follow
1925 * mouse focus from entries to sidebar (and vice versa) */
1926 if (ozone->pointer.type == MENU_POINTER_MOUSE)
1927 {
1928 if (ozone->pointer_in_sidebar &&
1929 !ozone->last_pointer_in_sidebar &&
1930 !ozone->cursor_in_sidebar)
1931 ozone_go_to_sidebar(ozone, settings, animation_tag);
1932 else if (!ozone->pointer_in_sidebar &&
1933 ozone->last_pointer_in_sidebar &&
1934 ozone->cursor_in_sidebar)
1935 ozone_leave_sidebar(ozone, settings, animation_tag);
1936 }
1937
1938 /* Update scrolling - must be done first, otherwise
1939 * cannot determine entry/category positions
1940 * > Entries */
1941 if (!ozone->pointer_in_sidebar)
1942 {
1943 float entry_bottom_boundary = height - ozone->dimensions.header_height -
1944 ozone->dimensions.spacer_1px - ozone->dimensions.footer_height -
1945 ozone->dimensions.entry_padding_vertical * 2;
1946
1947 ozone->animations.scroll_y += ozone->pointer.y_accel;
1948
1949 if (ozone->animations.scroll_y + ozone->entries_height < entry_bottom_boundary)
1950 ozone->animations.scroll_y = entry_bottom_boundary - ozone->entries_height;
1951
1952 if (ozone->animations.scroll_y > 0.0f)
1953 ozone->animations.scroll_y = 0.0f;
1954 }
1955 /* > Sidebar
1956 * Only process sidebar input here if the
1957 * cursor is currently *in* the sidebar */
1958 else if (ozone->cursor_in_sidebar)
1959 {
1960 float sidebar_bottom_boundary = height -
1961 (ozone->dimensions.header_height + ozone->dimensions.spacer_1px) -
1962 ozone->dimensions.footer_height -
1963 ozone->dimensions.sidebar_padding_vertical;
1964 float sidebar_height = ozone_get_sidebar_height(ozone);
1965
1966 ozone->animations.scroll_y_sidebar += ozone->pointer.y_accel;
1967
1968 if (ozone->animations.scroll_y_sidebar + sidebar_height < sidebar_bottom_boundary)
1969 ozone->animations.scroll_y_sidebar = sidebar_bottom_boundary - sidebar_height;
1970
1971 if (ozone->animations.scroll_y_sidebar > 0.0f)
1972 ozone->animations.scroll_y_sidebar = 0.0f;
1973 }
1974
1975 /* Regardless of pointer location, have to process
1976 * all entries/categories in order to determine
1977 * the indices of the first and last entries/categories
1978 * displayed on screen
1979 * > Needed so we can determine proper cursor positions
1980 * when mixing pointer + gamepad/keyboard input */
1981
1982 /* >> Loop over all entries */
1983 ozone->first_onscreen_entry = 0;
1984 ozone->last_onscreen_entry = (entries_end > 0) ? entries_end - 1 : 0;
1985
1986 for (i = 0; i < entries_end; i++)
1987 {
1988 float entry_y;
1989 ozone_node_t *node = (ozone_node_t*)selection_buf->list[i].userdata;
1990
1991 /* Sanity check */
1992 if (!node)
1993 break;
1994
1995 /* Get current entry y position */
1996 entry_y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px +
1997 ozone->dimensions.entry_padding_vertical + ozone->animations.scroll_y +
1998 node->position_y;
1999
2000 /* Check whether this is the first on screen entry */
2001 if (!first_entry_found)
2002 {
2003 if ((entry_y + node->height) > ozone->dimensions.header_height)
2004 {
2005 ozone->first_onscreen_entry = i;
2006 first_entry_found = true;
2007 }
2008 }
2009 /* Check whether this is the last on screen entry */
2010 else if (!last_entry_found)
2011 {
2012 if (entry_y > (height - ozone->dimensions.footer_height))
2013 {
2014 /* Current entry is off screen - get index
2015 * of previous entry */
2016 if (i > 0)
2017 {
2018 ozone->last_onscreen_entry = i - 1;
2019 last_entry_found = true;
2020 }
2021 }
2022 }
2023
2024 /* Track pointer input, if required */
2025 if (!ozone->pointer_in_sidebar &&
2026 first_entry_found &&
2027 !last_entry_found)
2028 {
2029 /* Check whether pointer is within the bounds
2030 * of the current entry */
2031 if ((ozone->pointer.x > entry_x) &&
2032 (ozone->pointer.x < entry_x + entry_width) &&
2033 (ozone->pointer.y > entry_y) &&
2034 (ozone->pointer.y < entry_y + node->height))
2035 {
2036 /* Pointer selection is always updated */
2037 menu_input_set_pointer_selection((unsigned)i);
2038
2039 /* If pointer is a mouse, then automatically
2040 * select entry under cursor */
2041 if (ozone->pointer.type == MENU_POINTER_MOUSE)
2042 {
2043 /* Note the fudge factor - cannot auto select
2044 * items while drag-scrolling the entry list,
2045 * so have to wait until pointer acceleration
2046 * drops below a 'sensible' level... */
2047 if (!ozone->cursor_in_sidebar &&
2048 (i != ozone->selection) &&
2049 (ozone->pointer.y_accel < ozone->last_scale_factor) &&
2050 (ozone->pointer.y_accel > -ozone->last_scale_factor))
2051 {
2052 menu_navigation_set_selection(i);
2053
2054 /* If this is a playlist, must update thumbnails */
2055 if (ozone->is_playlist && (ozone->depth == 1))
2056 {
2057 ozone_set_thumbnail_content(ozone, "");
2058 ozone_update_thumbnail_image(ozone);
2059 }
2060 }
2061 }
2062
2063 /* If pointer is pressed and stationary, and
2064 * if pointer has been held for at least
2065 * MENU_INPUT_PRESS_TIME_SHORT ms, automatically
2066 * select current entry */
2067 if (ozone->pointer.pressed &&
2068 !ozone->pointer.dragged &&
2069 (ozone->pointer.press_duration >= MENU_INPUT_PRESS_TIME_SHORT) &&
2070 (i != ozone->selection))
2071 {
2072 menu_navigation_set_selection(i);
2073
2074 /* If we are currently in the sidebar, leave it */
2075 if (ozone->cursor_in_sidebar)
2076 ozone_leave_sidebar(ozone, settings, animation_tag);
2077 /* If this is a playlist, must update thumbnails */
2078 else if (ozone->is_playlist && (ozone->depth == 1))
2079 {
2080 ozone_set_thumbnail_content(ozone, "");
2081 ozone_update_thumbnail_image(ozone);
2082 }
2083 }
2084 }
2085 }
2086
2087 if (last_entry_found)
2088 break;
2089 }
2090
2091 /* >> Loop over all categories */
2092 ozone->first_onscreen_category = 0;
2093 ozone->last_onscreen_category = ozone->system_tab_end + horizontal_list_size;
2094
2095 for (i = 0; i < ozone->system_tab_end + horizontal_list_size + 1; i++)
2096 {
2097 /* Get current category y position */
2098 float category_y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px +
2099 ozone->dimensions.sidebar_padding_vertical + (category_height * i) +
2100 ((i > ozone->system_tab_end) ?
2101 (ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px) : 0) +
2102 ozone->animations.scroll_y_sidebar;
2103
2104 /* Check whether this is the first on screen category */
2105 if (!first_category_found)
2106 {
2107 if ((category_y + category_height) > ozone->dimensions.header_height)
2108 {
2109 ozone->first_onscreen_category = i;
2110 first_category_found = true;
2111 }
2112 }
2113 /* Check whether this is the last on screen category */
2114 else if (!last_category_found)
2115 {
2116 if (category_y > (height - ozone->dimensions.footer_height))
2117 {
2118 /* Current category is off screen - get index
2119 * of previous category */
2120 if (i > 0)
2121 {
2122 ozone->last_onscreen_category = i - 1;
2123 last_category_found = true;
2124 }
2125 }
2126 }
2127
2128 /* Track pointer input, if required */
2129 if (ozone->pointer_in_sidebar &&
2130 ozone->cursor_in_sidebar &&
2131 first_category_found &&
2132 !last_category_found)
2133 {
2134 /* If pointer is within the bounds of the
2135 * current category, cache category index
2136 * (for use in next 'pointer up' event) */
2137 if ((ozone->pointer.y > category_y) &&
2138 (ozone->pointer.y < category_y + category_height))
2139 ozone->pointer_categories_selection = i;
2140 }
2141
2142 if (last_category_found)
2143 break;
2144 }
2145 }
2146
2147 menu_entries_ctl(MENU_ENTRIES_CTL_START_GET, &i);
2148
2149 if (i >= entries_end)
2150 {
2151 i = 0;
2152 menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &i);
2153 }
2154
2155 GFX_ANIMATION_CLEAR_ACTIVE(p_anim);
2156 }
2157
ozone_draw_header(ozone_handle_t * ozone,gfx_display_t * p_disp,gfx_animation_t * p_anim,settings_t * settings,void * userdata,unsigned video_width,unsigned video_height,bool battery_level_enable,bool timedate_enable)2158 static void ozone_draw_header(
2159 ozone_handle_t *ozone,
2160 gfx_display_t *p_disp,
2161 gfx_animation_t *p_anim,
2162 settings_t *settings,
2163 void *userdata,
2164 unsigned video_width,
2165 unsigned video_height,
2166 bool battery_level_enable,
2167 bool timedate_enable)
2168 {
2169 char title[255];
2170 gfx_animation_ctx_ticker_t ticker;
2171 gfx_animation_ctx_ticker_smooth_t ticker_smooth;
2172 static const char* const ticker_spacer = OZONE_TICKER_SPACER;
2173 unsigned ticker_x_offset = 0;
2174 unsigned timedate_offset = 0;
2175 bool use_smooth_ticker = settings->bools.menu_ticker_smooth;
2176 float scale_factor = ozone->last_scale_factor;
2177 unsigned logo_icon_size = 60 * scale_factor;
2178 unsigned status_icon_size = 92 * scale_factor;
2179 unsigned seperator_margin = 30 * scale_factor;
2180 enum gfx_animation_ticker_type
2181 menu_ticker_type = (enum gfx_animation_ticker_type)settings->uints.menu_ticker_type;
2182 gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
2183 float *col = ozone->theme->entries_icon;
2184
2185 /* Initial ticker configuration */
2186 if (use_smooth_ticker)
2187 {
2188 ticker_smooth.idx = p_anim->ticker_pixel_idx;
2189 ticker_smooth.font_scale = 1.0f;
2190 ticker_smooth.type_enum = menu_ticker_type;
2191 ticker_smooth.spacer = ticker_spacer;
2192 ticker_smooth.x_offset = &ticker_x_offset;
2193 ticker_smooth.dst_str_width = NULL;
2194 }
2195 else
2196 {
2197 ticker.idx = p_anim->ticker_idx;
2198 ticker.type_enum = menu_ticker_type;
2199 ticker.spacer = ticker_spacer;
2200 }
2201
2202 /* Separator */
2203 gfx_display_draw_quad(
2204 p_disp,
2205 userdata,
2206 video_width,
2207 video_height,
2208 seperator_margin,
2209 ozone->dimensions.header_height,
2210 video_width - seperator_margin * 2,
2211 ozone->dimensions.spacer_1px,
2212 video_width,
2213 video_height,
2214 ozone->theme->header_footer_separator);
2215
2216 /* Title */
2217 if (use_smooth_ticker)
2218 {
2219 ticker_smooth.font = ozone->fonts.title.font;
2220 ticker_smooth.selected = true;
2221 ticker_smooth.field_width = (video_width - (128 + 47 + 180) * scale_factor);
2222 ticker_smooth.src_str = ozone->show_fullscreen_thumbnails ? ozone->fullscreen_thumbnail_label : ozone->title;
2223 ticker_smooth.dst_str = title;
2224 ticker_smooth.dst_str_len = sizeof(title);
2225
2226 gfx_animation_ticker_smooth(&ticker_smooth);
2227 }
2228 else
2229 {
2230 ticker.s = title;
2231 ticker.len = (video_width - (128 + 47 + 180) * scale_factor) / ozone->fonts.title.glyph_width;
2232 ticker.str = ozone->show_fullscreen_thumbnails ? ozone->fullscreen_thumbnail_label : ozone->title;
2233 ticker.selected = true;
2234
2235 gfx_animation_ticker(&ticker);
2236 }
2237
2238 gfx_display_draw_text(
2239 ozone->fonts.title.font,
2240 title,
2241 ticker_x_offset + 128 * scale_factor,
2242 ozone->dimensions.header_height / 2
2243 + ozone->fonts.title.line_centre_offset,
2244 video_width,
2245 video_height,
2246 ozone->theme->text_rgba,
2247 TEXT_ALIGN_LEFT,
2248 1.0f,
2249 false,
2250 1.0f,
2251 false);
2252
2253 /* Icon */
2254 if (dispctx)
2255 {
2256 if (dispctx->blend_begin)
2257 dispctx->blend_begin(userdata);
2258 if (dispctx->draw)
2259 {
2260 #if 0
2261 if (discord_avatar_is_ready())
2262 ozone_draw_icon(
2263 p_disp,
2264 userdata,
2265 video_width,
2266 video_height,
2267 logo_icon_size,
2268 logo_icon_size,
2269 ozone->textures[OZONE_TEXTURE_DISCORD_OWN_AVATAR],
2270 47 * scale_factor,
2271 14 * scale_factor, /* Where does this come from...? */
2272 video_width,
2273 video_height,
2274 0, 1, col);
2275 else
2276 #endif
2277 ozone_draw_icon(
2278 p_disp,
2279 userdata,
2280 video_width,
2281 video_height,
2282 logo_icon_size,
2283 logo_icon_size,
2284 ozone->textures[OZONE_TEXTURE_RETROARCH],
2285 47 * scale_factor,
2286 (ozone->dimensions.header_height - logo_icon_size) / 2,
2287 video_width,
2288 video_height,
2289 0, 1, col);
2290 }
2291 if (dispctx->blend_end)
2292 dispctx->blend_end(userdata);
2293 }
2294
2295 /* Battery */
2296 if (battery_level_enable)
2297 {
2298 gfx_display_ctx_powerstate_t powerstate;
2299 char msg[12];
2300
2301 msg[0] = '\0';
2302
2303 powerstate.s = msg;
2304 powerstate.len = sizeof(msg);
2305
2306 menu_display_powerstate(&powerstate);
2307
2308 if (powerstate.battery_enabled)
2309 {
2310 timedate_offset = 95 * scale_factor;
2311
2312 gfx_display_draw_text(
2313 ozone->fonts.time.font,
2314 msg,
2315 video_width - 85 * scale_factor,
2316 ozone->dimensions.header_height / 2
2317 + ozone->fonts.time.line_centre_offset,
2318 video_width,
2319 video_height,
2320 ozone->theme->text_rgba,
2321 TEXT_ALIGN_RIGHT,
2322 1.0f,
2323 false,
2324 1.0f,
2325 false);
2326
2327 if (dispctx)
2328 {
2329 if (dispctx->blend_begin)
2330 dispctx->blend_begin(userdata);
2331 if (dispctx->draw)
2332 ozone_draw_icon(
2333 p_disp,
2334 userdata,
2335 video_width,
2336 video_height,
2337 status_icon_size,
2338 status_icon_size,
2339 ozone->icons_textures[powerstate.charging? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_CHARGING : (powerstate.percent > 80)? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_FULL : (powerstate.percent > 60)? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_80 : (powerstate.percent > 40)? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_60 : (powerstate.percent > 20)? OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_40 : OZONE_ENTRIES_ICONS_TEXTURE_BATTERY_20],
2340 video_width - (60 + 56) * scale_factor,
2341 0,
2342 video_width,
2343 video_height,
2344 0, 1, col);
2345 if (dispctx->blend_end)
2346 dispctx->blend_end(userdata);
2347 }
2348 }
2349 }
2350
2351 /* Timedate */
2352 if (timedate_enable)
2353 {
2354 gfx_display_ctx_datetime_t datetime;
2355 char timedate[255];
2356
2357 timedate[0] = '\0';
2358
2359 datetime.s = timedate;
2360 datetime.time_mode = settings->uints.menu_timedate_style;
2361 datetime.date_separator = settings->uints.menu_timedate_date_separator;
2362 datetime.len = sizeof(timedate);
2363
2364 menu_display_timedate(&datetime);
2365
2366 gfx_display_draw_text(
2367 ozone->fonts.time.font,
2368 timedate,
2369 video_width - (85 * scale_factor) - timedate_offset,
2370 ozone->dimensions.header_height / 2
2371 + ozone->fonts.time.line_centre_offset,
2372 video_width,
2373 video_height,
2374 ozone->theme->text_rgba,
2375 TEXT_ALIGN_RIGHT,
2376 1.0f,
2377 false,
2378 1.0f,
2379 false);
2380
2381 if (dispctx)
2382 {
2383 if (dispctx->blend_begin)
2384 dispctx->blend_begin(userdata);
2385 if (dispctx->draw)
2386 ozone_draw_icon(
2387 p_disp,
2388 userdata,
2389 video_width,
2390 video_height,
2391 status_icon_size,
2392 status_icon_size,
2393 ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CLOCK],
2394 video_width - (60 + 56) * scale_factor - timedate_offset,
2395 0,
2396 video_width,
2397 video_height,
2398 0, 1, col);
2399 if (dispctx->blend_end)
2400 dispctx->blend_end(userdata);
2401 }
2402 }
2403 }
2404
ozone_draw_footer(ozone_handle_t * ozone,gfx_display_t * p_disp,gfx_animation_t * p_anim,void * userdata,unsigned video_width,unsigned video_height,bool input_menu_swap_ok_cancel_buttons,settings_t * settings)2405 static void ozone_draw_footer(
2406 ozone_handle_t *ozone,
2407 gfx_display_t *p_disp,
2408 gfx_animation_t *p_anim,
2409 void *userdata,
2410 unsigned video_width,
2411 unsigned video_height,
2412 bool input_menu_swap_ok_cancel_buttons,
2413 settings_t *settings)
2414 {
2415 bool menu_core_enable = settings->bools.menu_core_enable;
2416 float scale_factor = ozone->last_scale_factor;
2417 unsigned seperator_margin = 30 * scale_factor;
2418 float footer_margin = 59 * scale_factor;
2419 float footer_text_y = (float)video_height -
2420 (ozone->dimensions.footer_height / 2.0f) +
2421 ozone->fonts.footer.line_centre_offset;
2422 float icon_size = 35 * scale_factor;
2423 float icon_padding = 12 * scale_factor;
2424 float icon_y = (float)video_height -
2425 (ozone->dimensions.footer_height / 2.0f) -
2426 (icon_size / 2.0f);
2427 /* Button enable states
2428 * > Note: Only show 'metadata_toggle' if
2429 * 'fullscreen_thumbs' is shown. This condition
2430 * should be guaranteed anyway, but enforce it
2431 * here to prevent 'gaps' in the button list in
2432 * the event of unknown errors */
2433 bool fullscreen_thumbnails_available =
2434 ozone->fullscreen_thumbnails_available &&
2435 !ozone->cursor_in_sidebar &&
2436 ozone->show_thumbnail_bar &&
2437 ((ozone->thumbnails.right.status != GFX_THUMBNAIL_STATUS_MISSING) ||
2438 (ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_MISSING)) &&
2439 (gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) ||
2440 gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT));
2441 bool metadata_override_available =
2442 fullscreen_thumbnails_available &&
2443 ozone_metadata_override_available(ozone);
2444 /* Determine x origin positions of each
2445 * button
2446 * > From right to left, these are ordered:
2447 * - ok
2448 * - back
2449 * - search
2450 * - toggle fullscreen thumbs (playlists only)
2451 * - toggle metadata (playlists only) */
2452 float ok_x = (float)video_width - footer_margin -
2453 ozone->footer_labels.ok.width - icon_size - icon_padding;
2454 float back_x = ok_x -
2455 ozone->footer_labels.back.width - icon_size - (2.0f * icon_padding);
2456 float search_x = back_x -
2457 ozone->footer_labels.search.width - icon_size - (2.0f * icon_padding);
2458 float fullscreen_thumbs_x = search_x -
2459 ozone->footer_labels.fullscreen_thumbs.width - icon_size - (2.0f * icon_padding);
2460 float metadata_toggle_x = fullscreen_thumbs_x -
2461 ozone->footer_labels.metadata_toggle.width - icon_size - (2.0f * icon_padding);
2462 gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
2463 float *col = ozone->theme_dynamic.entries_icon;
2464
2465 /* Separator */
2466 gfx_display_draw_quad(
2467 p_disp,
2468 userdata,
2469 video_width,
2470 video_height,
2471 seperator_margin,
2472 video_height - ozone->dimensions.footer_height,
2473 video_width - seperator_margin * 2,
2474 ozone->dimensions.spacer_1px,
2475 video_width,
2476 video_height,
2477 ozone->theme->header_footer_separator);
2478
2479 /* Buttons */
2480
2481 /* Draw icons */
2482 if (dispctx)
2483 {
2484 if (dispctx->blend_begin)
2485 dispctx->blend_begin(userdata);
2486 gfx_display_set_alpha(ozone->theme_dynamic.entries_icon, 1.0f);
2487
2488 if (dispctx->draw)
2489 {
2490 /* > ok */
2491 ozone_draw_icon(
2492 p_disp,
2493 userdata,
2494 video_width,
2495 video_height,
2496 icon_size,
2497 icon_size,
2498 input_menu_swap_ok_cancel_buttons ?
2499 ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_D] :
2500 ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_R],
2501 ok_x,
2502 icon_y,
2503 video_width,
2504 video_height,
2505 0, 1, col);
2506
2507 /* > back */
2508 ozone_draw_icon(
2509 p_disp,
2510 userdata,
2511 video_width,
2512 video_height,
2513 icon_size,
2514 icon_size,
2515 input_menu_swap_ok_cancel_buttons ?
2516 ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_R] :
2517 ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_D],
2518 back_x,
2519 icon_y,
2520 video_width,video_height,
2521 0, 1, col);
2522
2523 /* > search */
2524 ozone_draw_icon(
2525 p_disp,
2526 userdata,
2527 video_width,
2528 video_height,
2529 icon_size,
2530 icon_size,
2531 ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BTN_U],
2532 search_x,
2533 icon_y,
2534 video_width,video_height,
2535 0, 1, col);
2536
2537 /* > fullscreen_thumbs */
2538 if (fullscreen_thumbnails_available)
2539 ozone_draw_icon(
2540 p_disp,
2541 userdata,
2542 video_width,
2543 video_height,
2544 icon_size,
2545 icon_size,
2546 ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_START],
2547 fullscreen_thumbs_x,
2548 icon_y,
2549 video_width,video_height,
2550 0, 1, col);
2551
2552 /* > metadata_toggle */
2553 if (metadata_override_available)
2554 ozone_draw_icon(
2555 p_disp,
2556 userdata,
2557 video_width,
2558 video_height,
2559 icon_size,
2560 icon_size,
2561 ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SELECT],
2562 metadata_toggle_x,
2563 icon_y,
2564 video_width,video_height,
2565 0, 1, col);
2566 }
2567
2568 if (dispctx->blend_end)
2569 dispctx->blend_end(userdata);
2570 }
2571
2572 /* Draw labels */
2573
2574 /* > ok */
2575 gfx_display_draw_text(
2576 ozone->fonts.footer.font,
2577 ozone->footer_labels.ok.str,
2578 ok_x + icon_size + icon_padding,
2579 footer_text_y,
2580 video_width,
2581 video_height,
2582 ozone->theme->text_rgba,
2583 TEXT_ALIGN_LEFT,
2584 1.0f,
2585 false,
2586 1.0f,
2587 false);
2588
2589 /* > back */
2590 gfx_display_draw_text(
2591 ozone->fonts.footer.font,
2592 ozone->footer_labels.back.str,
2593 back_x + icon_size + icon_padding,
2594 footer_text_y,
2595 video_width,
2596 video_height,
2597 ozone->theme->text_rgba,
2598 TEXT_ALIGN_LEFT,
2599 1.0f,
2600 false,
2601 1.0f,
2602 false);
2603
2604 /* > search */
2605 gfx_display_draw_text(
2606 ozone->fonts.footer.font,
2607 ozone->footer_labels.search.str,
2608 search_x + icon_size + icon_padding,
2609 footer_text_y,
2610 video_width,
2611 video_height,
2612 ozone->theme->text_rgba,
2613 TEXT_ALIGN_LEFT,
2614 1.0f,
2615 false,
2616 1.0f,
2617 false);
2618
2619 /* > fullscreen_thumbs */
2620 if (fullscreen_thumbnails_available)
2621 gfx_display_draw_text(
2622 ozone->fonts.footer.font,
2623 ozone->footer_labels.fullscreen_thumbs.str,
2624 fullscreen_thumbs_x + icon_size + icon_padding,
2625 footer_text_y,
2626 video_width,
2627 video_height,
2628 ozone->theme->text_rgba,
2629 TEXT_ALIGN_LEFT,
2630 1.0f,
2631 false,
2632 1.0f,
2633 false);
2634
2635 /* > metadata_toggle */
2636 if (metadata_override_available)
2637 gfx_display_draw_text(
2638 ozone->fonts.footer.font,
2639 ozone->footer_labels.metadata_toggle.str,
2640 metadata_toggle_x + icon_size + icon_padding,
2641 footer_text_y,
2642 video_width,
2643 video_height,
2644 ozone->theme->text_rgba,
2645 TEXT_ALIGN_LEFT,
2646 1.0f,
2647 false,
2648 1.0f,
2649 false);
2650
2651 /* Core title or Switch icon */
2652 if (menu_core_enable)
2653 {
2654 gfx_animation_ctx_ticker_t ticker;
2655 gfx_animation_ctx_ticker_smooth_t ticker_smooth;
2656 char core_title[255];
2657 char core_title_buf[255];
2658 int usable_width;
2659 bool use_smooth_ticker =
2660 settings->bools.menu_ticker_smooth;
2661 enum gfx_animation_ticker_type menu_ticker_type =
2662 (enum gfx_animation_ticker_type)settings->uints.menu_ticker_type;
2663 static const char* const ticker_spacer = OZONE_TICKER_SPACER;
2664 unsigned ticker_x_offset = 0;
2665
2666 core_title[0] = '\0';
2667 core_title_buf[0] = '\0';
2668
2669 /* Determine available width for core
2670 * title string */
2671 usable_width = metadata_override_available ?
2672 metadata_toggle_x :
2673 fullscreen_thumbnails_available ?
2674 fullscreen_thumbs_x :
2675 search_x;
2676 usable_width -= footer_margin + (icon_padding * 3);
2677
2678 if (usable_width > 0)
2679 {
2680 /* Get core title */
2681 menu_entries_get_core_title(core_title, sizeof(core_title));
2682
2683 /* Configure and run ticker */
2684 if (use_smooth_ticker)
2685 {
2686 ticker_smooth.idx = p_anim->ticker_pixel_idx;
2687 ticker_smooth.font_scale = 1.0f;
2688 ticker_smooth.type_enum = menu_ticker_type;
2689 ticker_smooth.spacer = ticker_spacer;
2690 ticker_smooth.x_offset = &ticker_x_offset;
2691 ticker_smooth.dst_str_width = NULL;
2692
2693 ticker_smooth.font = ozone->fonts.footer.font;
2694 ticker_smooth.selected = true;
2695 ticker_smooth.field_width = usable_width;
2696 ticker_smooth.src_str = core_title;
2697 ticker_smooth.dst_str = core_title_buf;
2698 ticker_smooth.dst_str_len = sizeof(core_title_buf);
2699
2700 gfx_animation_ticker_smooth(&ticker_smooth);
2701 }
2702 else
2703 {
2704 ticker.idx = p_anim->ticker_idx;
2705 ticker.type_enum = menu_ticker_type;
2706 ticker.spacer = ticker_spacer;
2707
2708 ticker.s = core_title_buf;
2709 ticker.len = usable_width / ozone->fonts.footer.glyph_width;
2710 ticker.str = core_title;
2711 ticker.selected = true;
2712
2713 gfx_animation_ticker(&ticker);
2714 }
2715
2716 /* Draw text */
2717 gfx_display_draw_text(
2718 ozone->fonts.footer.font,
2719 core_title_buf,
2720 ticker_x_offset + footer_margin,
2721 footer_text_y,
2722 video_width,
2723 video_height,
2724 ozone->theme->text_rgba,
2725 TEXT_ALIGN_LEFT,
2726 1.0f,
2727 false,
2728 1.0f,
2729 false);
2730 }
2731 }
2732 #ifdef HAVE_LIBNX
2733 else
2734 {
2735 if (dispctx)
2736 {
2737 if (dispctx->blend_begin)
2738 dispctx->blend_begin(userdata);
2739 if (dispctx->draw)
2740 ozone_draw_icon(
2741 p_disp,
2742 userdata,
2743 video_width,
2744 video_height,
2745 69 * scale_factor,
2746 30 * scale_factor,
2747 ozone->theme->textures[OZONE_THEME_TEXTURE_SWITCH],
2748 footer_margin,
2749 video_height - ozone->dimensions.footer_height / 2 - 15 * scale_factor,
2750 video_width,
2751 video_height,
2752 0,
2753 1,
2754 ozone->pure_white);
2755 if (dispctx->blend_end)
2756 dispctx->blend_end(userdata);
2757 }
2758 }
2759 #endif
2760 }
2761
ozone_set_thumbnail_system(void * data,char * s,size_t len)2762 static void ozone_set_thumbnail_system(void *data, char*s, size_t len)
2763 {
2764 ozone_handle_t *ozone = (ozone_handle_t*)data;
2765 if (!ozone)
2766 return;
2767
2768 gfx_thumbnail_set_system(
2769 ozone->thumbnail_path_data, s, playlist_get_cached());
2770 }
2771
ozone_get_thumbnail_system(void * data,char * s,size_t len)2772 static void ozone_get_thumbnail_system(void *data, char*s, size_t len)
2773 {
2774 ozone_handle_t *ozone = (ozone_handle_t*)data;
2775 const char *system = NULL;
2776 if (!ozone)
2777 return;
2778
2779 if (gfx_thumbnail_get_system(ozone->thumbnail_path_data, &system))
2780 strlcpy(s, system, len);
2781 }
2782
ozone_selection_changed(ozone_handle_t * ozone,bool allow_animation)2783 static void ozone_selection_changed(ozone_handle_t *ozone, bool allow_animation)
2784 {
2785 file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
2786 size_t new_selection = menu_navigation_get_selection();
2787 ozone_node_t *node = (ozone_node_t*)selection_buf->list[new_selection].userdata;
2788
2789 if (!node)
2790 return;
2791
2792 if (ozone->selection != new_selection)
2793 {
2794 menu_entry_t entry;
2795 unsigned entry_type;
2796 uintptr_t tag = (uintptr_t)selection_buf;
2797 size_t selection = menu_navigation_get_selection();
2798
2799 MENU_ENTRY_INIT(entry);
2800 entry.path_enabled = false;
2801 entry.label_enabled = false;
2802 entry.rich_label_enabled = false;
2803 entry.value_enabled = false;
2804 entry.sublabel_enabled = false;
2805 menu_entry_get(&entry, 0, selection, NULL, true);
2806
2807 entry_type = entry.type;
2808
2809 ozone->selection_old = ozone->selection;
2810 ozone->selection = new_selection;
2811
2812 ozone->cursor_in_sidebar_old = ozone->cursor_in_sidebar;
2813
2814 gfx_animation_kill_by_tag(&tag);
2815 ozone_update_scroll(ozone, allow_animation, node);
2816
2817 /* Update thumbnail */
2818 if (gfx_thumbnail_is_enabled(
2819 ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) ||
2820 gfx_thumbnail_is_enabled(
2821 ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
2822 {
2823 bool update_thumbnails = false;
2824
2825 /* Playlist updates */
2826 if (ozone->is_playlist && ozone->depth == 1)
2827 {
2828 ozone_set_thumbnail_content(ozone, "");
2829 update_thumbnails = true;
2830 }
2831 /* Database list updates
2832 * (pointless nuisance...) */
2833 else if (ozone->depth == 4 && ozone->is_db_manager_list)
2834 {
2835 ozone_set_thumbnail_content(ozone, "");
2836 update_thumbnails = true;
2837 }
2838 /* Filebrowser image updates */
2839 else if (ozone->is_file_list)
2840 {
2841 if ((entry_type == FILE_TYPE_IMAGEVIEWER) ||
2842 (entry_type == FILE_TYPE_IMAGE))
2843 {
2844 ozone_set_thumbnail_content(ozone, "imageviewer");
2845 update_thumbnails = true;
2846 }
2847 else
2848 {
2849 /* If this is a file list and current
2850 * entry is not an image, have to 'reset'
2851 * content + right/left thumbnails
2852 * (otherwise last loaded thumbnail will
2853 * persist, and be shown on the wrong entry) */
2854 gfx_thumbnail_set_content(ozone->thumbnail_path_data, NULL);
2855 ozone_unload_thumbnail_textures(ozone);
2856 }
2857 }
2858
2859 if (update_thumbnails)
2860 ozone_update_thumbnail_image(ozone);
2861 }
2862
2863 /* TODO: update savestate thumbnail and path */
2864 }
2865 }
2866
ozone_navigation_clear(void * data,bool pending_push)2867 static void ozone_navigation_clear(void *data, bool pending_push)
2868 {
2869 ozone_handle_t *ozone = (ozone_handle_t*)data;
2870 if (!pending_push)
2871 ozone_selection_changed(ozone, true);
2872 }
2873
ozone_navigation_pointer_changed(void * data)2874 static void ozone_navigation_pointer_changed(void *data)
2875 {
2876 ozone_handle_t *ozone = (ozone_handle_t*)data;
2877 ozone_selection_changed(ozone, true);
2878 }
2879
ozone_navigation_set(void * data,bool scroll)2880 static void ozone_navigation_set(void *data, bool scroll)
2881 {
2882 ozone_handle_t *ozone = (ozone_handle_t*)data;
2883 ozone_selection_changed(ozone, true);
2884 }
2885
ozone_navigation_alphabet(void * data,size_t * unused)2886 static void ozone_navigation_alphabet(void *data, size_t *unused)
2887 {
2888 ozone_handle_t *ozone = (ozone_handle_t*)data;
2889 ozone_selection_changed(ozone, true);
2890 }
2891
ozone_messagebox_fadeout_cb(void * userdata)2892 static void ozone_messagebox_fadeout_cb(void *userdata)
2893 {
2894 ozone_handle_t *ozone = (ozone_handle_t*) userdata;
2895
2896 free(ozone->pending_message);
2897 ozone->pending_message = NULL;
2898
2899 ozone->should_draw_messagebox = false;
2900 }
2901
ozone_font_bind(ozone_font_data_t * font_data)2902 static void INLINE ozone_font_bind(ozone_font_data_t *font_data)
2903 {
2904 font_driver_bind_block(font_data->font, &font_data->raster_block);
2905 font_data->raster_block.carr.coords.vertices = 0;
2906 }
2907
ozone_font_unbind(ozone_font_data_t * font_data)2908 static void INLINE ozone_font_unbind(ozone_font_data_t *font_data)
2909 {
2910 font_driver_bind_block(font_data->font, NULL);
2911 }
2912
ozone_frame(void * data,video_frame_info_t * video_info)2913 static void ozone_frame(void *data, video_frame_info_t *video_info)
2914 {
2915 gfx_animation_ctx_entry_t entry;
2916 ozone_handle_t* ozone = (ozone_handle_t*) data;
2917 settings_t *settings = config_get_ptr();
2918 unsigned color_theme = settings->uints.menu_ozone_color_theme;
2919 bool use_preferred_system_color_theme = settings->bools.menu_use_preferred_system_color_theme;
2920 uintptr_t messagebox_tag = (uintptr_t)ozone->pending_message;
2921 bool draw_osk = menu_input_dialog_get_display_kb();
2922 static bool draw_osk_old = false;
2923 float *background_color = NULL;
2924 void *userdata = video_info->userdata;
2925 unsigned video_width = video_info->width;
2926 unsigned video_height = video_info->height;
2927 float menu_framebuffer_opacity = video_info->menu_framebuffer_opacity;
2928 bool libretro_running = video_info->libretro_running;
2929 bool video_fullscreen = video_info->fullscreen;
2930 bool mouse_grabbed = video_info->input_driver_grab_mouse_state;
2931 bool menu_mouse_enable = video_info->menu_mouse_enable;
2932 bool input_menu_swap_ok_cancel_buttons = video_info->input_menu_swap_ok_cancel_buttons;
2933 bool battery_level_enable = video_info->battery_level_enable;
2934 bool timedate_enable = video_info->timedate_enable;
2935 gfx_display_t *p_disp = (gfx_display_t*)video_info->disp_userdata;
2936 gfx_animation_t *p_anim = anim_get_ptr();
2937 gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
2938
2939 #if 0
2940 static bool reset = false;
2941
2942 if (discord_avatar_is_ready() && !reset)
2943 {
2944 ozone_context_reset(data, false);
2945 reset = true;
2946 }
2947 #endif
2948
2949
2950 if (!ozone)
2951 return;
2952
2953 if (ozone->first_frame)
2954 {
2955 menu_input_get_pointer_state(&ozone->pointer);
2956
2957 ozone->cursor_x_old = ozone->pointer.x;
2958 ozone->cursor_y_old = ozone->pointer.y;
2959 ozone->first_frame = false;
2960 }
2961
2962 /* OSK Fade detection */
2963 if (draw_osk != draw_osk_old)
2964 {
2965 draw_osk_old = draw_osk;
2966 if (!draw_osk)
2967 {
2968 ozone->should_draw_messagebox = false;
2969 ozone->messagebox_state = false;
2970 ozone->messagebox_state_old = false;
2971 ozone->animations.messagebox_alpha = 0.0f;
2972 }
2973 }
2974
2975 /* Change theme on the fly */
2976 if ((color_theme != last_color_theme) ||
2977 (last_use_preferred_system_color_theme != use_preferred_system_color_theme))
2978 {
2979 if (use_preferred_system_color_theme)
2980 {
2981 color_theme = ozone_get_system_theme();
2982 configuration_set_uint(settings,
2983 settings->uints.menu_ozone_color_theme, color_theme);
2984 }
2985
2986 ozone_set_color_theme(ozone, color_theme);
2987 ozone_set_background_running_opacity(ozone, menu_framebuffer_opacity);
2988
2989 last_use_preferred_system_color_theme = use_preferred_system_color_theme;
2990 }
2991
2992 /* If menu screensaver is active, draw
2993 * screensaver and return */
2994 if (ozone->show_screensaver)
2995 {
2996 menu_screensaver_frame(ozone->screensaver,
2997 video_info, p_disp);
2998 return;
2999 }
3000
3001 video_driver_set_viewport(video_width, video_height, true, false);
3002
3003 /* Clear text */
3004 ozone_font_bind(&ozone->fonts.footer);
3005 ozone_font_bind(&ozone->fonts.title);
3006 ozone_font_bind(&ozone->fonts.time);
3007 ozone_font_bind(&ozone->fonts.entries_label);
3008 ozone_font_bind(&ozone->fonts.entries_sublabel);
3009 ozone_font_bind(&ozone->fonts.sidebar);
3010
3011 /* Background */
3012 if (libretro_running &&
3013 (menu_framebuffer_opacity < 1.0f))
3014 {
3015 if (menu_framebuffer_opacity != last_framebuffer_opacity)
3016 ozone_set_background_running_opacity(ozone, menu_framebuffer_opacity);
3017
3018 background_color = ozone->theme->background_libretro_running;
3019 }
3020 else
3021 background_color = ozone->theme->background;
3022
3023 gfx_display_draw_quad(
3024 p_disp,
3025 userdata,
3026 video_width,
3027 video_height,
3028 0, 0, video_width, video_height,
3029 video_width, video_height,
3030 background_color
3031 );
3032
3033 /* Header, footer */
3034 ozone_draw_header(
3035 ozone,
3036 p_disp,
3037 p_anim,
3038 settings,
3039 userdata,
3040 video_width,
3041 video_height,
3042 battery_level_enable,
3043 timedate_enable);
3044 ozone_draw_footer(ozone,
3045 p_disp, p_anim,
3046 userdata,
3047 video_width,
3048 video_height,
3049 input_menu_swap_ok_cancel_buttons,
3050 settings);
3051
3052 /* Sidebar */
3053 if (ozone->draw_sidebar)
3054 ozone_draw_sidebar(
3055 ozone,
3056 p_disp,
3057 p_anim,
3058 settings,
3059 userdata,
3060 video_width,
3061 video_height,
3062 libretro_running,
3063 menu_framebuffer_opacity);
3064
3065 /* Menu entries */
3066 gfx_display_scissor_begin(p_disp,
3067 userdata,
3068 video_width,
3069 video_height,
3070 ozone->sidebar_offset + (unsigned) ozone->dimensions_sidebar_width,
3071 ozone->dimensions.header_height + ozone->dimensions.spacer_1px,
3072 video_width - (unsigned) ozone->dimensions_sidebar_width
3073 + (-ozone->sidebar_offset),
3074 video_height - ozone->dimensions.header_height - ozone->dimensions.footer_height - ozone->dimensions.spacer_1px);
3075
3076 /* Current list */
3077 ozone_draw_entries(
3078 ozone,
3079 p_disp,
3080 p_anim,
3081 settings,
3082 userdata,
3083 video_width,
3084 video_height,
3085 (unsigned)ozone->selection,
3086 (unsigned)ozone->selection_old,
3087 menu_entries_get_selection_buf_ptr(0),
3088 ozone->animations.list_alpha,
3089 ozone->animations.scroll_y,
3090 ozone->is_playlist
3091 );
3092
3093 /* Old list */
3094 if (ozone->draw_old_list)
3095 ozone_draw_entries(
3096 ozone,
3097 p_disp,
3098 p_anim,
3099 settings,
3100 userdata,
3101 video_width,
3102 video_height,
3103 (unsigned)ozone->selection_old_list,
3104 (unsigned)ozone->selection_old_list,
3105 &ozone->selection_buf_old,
3106 ozone->animations.list_alpha,
3107 ozone->scroll_old,
3108 ozone->is_playlist_old
3109 );
3110
3111 /* Thumbnail bar */
3112 if (ozone->show_thumbnail_bar)
3113 ozone_draw_thumbnail_bar(ozone,
3114 p_disp,
3115 p_anim,
3116 settings,
3117 userdata,
3118 video_width,
3119 video_height,
3120 libretro_running,
3121 menu_framebuffer_opacity);
3122
3123 if (dispctx && dispctx->scissor_end)
3124 dispctx->scissor_end(userdata,
3125 video_width, video_height);
3126
3127 /* Flush first layer of text */
3128 ozone_font_flush(video_width, video_height, &ozone->fonts.footer);
3129 ozone_font_flush(video_width, video_height, &ozone->fonts.title);
3130 ozone_font_flush(video_width, video_height, &ozone->fonts.time);
3131 ozone_font_flush(video_width, video_height, &ozone->fonts.entries_label);
3132 ozone_font_flush(video_width, video_height, &ozone->fonts.entries_sublabel);
3133 ozone_font_flush(video_width, video_height, &ozone->fonts.sidebar);
3134
3135 /* Draw fullscreen thumbnails, if required */
3136 ozone_draw_fullscreen_thumbnails(ozone,
3137 userdata,
3138 video_info->disp_userdata,
3139 video_width,
3140 video_height);
3141
3142 /* Message box & OSK - second layer of text */
3143 if (ozone->should_draw_messagebox || draw_osk)
3144 {
3145 /* Fade in animation */
3146 if (ozone->messagebox_state_old != ozone->messagebox_state && ozone->messagebox_state)
3147 {
3148 ozone->messagebox_state_old = ozone->messagebox_state;
3149
3150 gfx_animation_kill_by_tag(&messagebox_tag);
3151 ozone->animations.messagebox_alpha = 0.0f;
3152
3153 entry.cb = NULL;
3154 entry.duration = ANIMATION_PUSH_ENTRY_DURATION;
3155 entry.easing_enum = EASING_OUT_QUAD;
3156 entry.subject = &ozone->animations.messagebox_alpha;
3157 entry.tag = messagebox_tag;
3158 entry.target_value = 1.0f;
3159 entry.userdata = NULL;
3160
3161 gfx_animation_push(&entry);
3162 }
3163 /* Fade out animation */
3164 else if (ozone->messagebox_state_old != ozone->messagebox_state && !ozone->messagebox_state)
3165 {
3166 ozone->messagebox_state_old = ozone->messagebox_state;
3167 ozone->messagebox_state = false;
3168
3169 gfx_animation_kill_by_tag(&messagebox_tag);
3170 ozone->animations.messagebox_alpha = 1.0f;
3171
3172 entry.cb = ozone_messagebox_fadeout_cb;
3173 entry.duration = ANIMATION_PUSH_ENTRY_DURATION;
3174 entry.easing_enum = EASING_OUT_QUAD;
3175 entry.subject = &ozone->animations.messagebox_alpha;
3176 entry.tag = messagebox_tag;
3177 entry.target_value = 0.0f;
3178 entry.userdata = ozone;
3179
3180 gfx_animation_push(&entry);
3181 }
3182
3183 ozone_draw_backdrop(
3184 userdata,
3185 video_info->disp_userdata,
3186 video_width,
3187 video_height,
3188 float_min(ozone->animations.messagebox_alpha, 0.75f));
3189
3190 if (draw_osk)
3191 {
3192 const char *label = menu_input_dialog_get_label_buffer();
3193 const char *str = menu_input_dialog_get_buffer();
3194
3195 ozone_draw_osk(ozone,
3196 userdata,
3197 video_info->disp_userdata,
3198 video_width,
3199 video_height,
3200 label, str);
3201 }
3202 else
3203 ozone_draw_messagebox(
3204 ozone,
3205 p_disp,
3206 userdata,
3207 video_width,
3208 video_height,
3209 ozone->pending_message);
3210
3211 /* Flush second layer of text */
3212 ozone_font_flush(video_width, video_height, &ozone->fonts.footer);
3213 ozone_font_flush(video_width, video_height, &ozone->fonts.entries_label);
3214 }
3215
3216 /* Cursor */
3217 if (ozone->show_cursor && (ozone->pointer.type != MENU_POINTER_DISABLED))
3218 {
3219 bool cursor_visible = (video_fullscreen || mouse_grabbed) &&
3220 menu_mouse_enable;
3221
3222 gfx_display_set_alpha(ozone->pure_white, 1.0f);
3223 if (cursor_visible)
3224 gfx_display_draw_cursor(
3225 p_disp,
3226 userdata,
3227 video_width,
3228 video_height,
3229 cursor_visible,
3230 ozone->pure_white,
3231 ozone->dimensions.cursor_size,
3232 ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_POINTER],
3233 ozone->pointer.x,
3234 ozone->pointer.y,
3235 video_width,
3236 video_height
3237 );
3238 }
3239
3240 /* Unbind fonts */
3241 ozone_font_unbind(&ozone->fonts.footer);
3242 ozone_font_unbind(&ozone->fonts.title);
3243 ozone_font_unbind(&ozone->fonts.time);
3244 ozone_font_unbind(&ozone->fonts.entries_label);
3245 ozone_font_unbind(&ozone->fonts.entries_sublabel);
3246 ozone_font_unbind(&ozone->fonts.sidebar);
3247
3248 video_driver_set_viewport(video_width, video_height, false, true);
3249 }
3250
ozone_set_header(ozone_handle_t * ozone)3251 static void ozone_set_header(ozone_handle_t *ozone)
3252 {
3253 if (ozone->categories_selection_ptr <= ozone->system_tab_end)
3254 menu_entries_get_title(ozone->title, sizeof(ozone->title));
3255 else
3256 {
3257 ozone_node_t *node = (ozone_node_t*)file_list_get_userdata_at_offset(&ozone->horizontal_list, ozone->categories_selection_ptr - ozone->system_tab_end-1);
3258
3259 if (node && node->console_name)
3260 {
3261 strlcpy(ozone->title, node->console_name, sizeof(ozone->title));
3262
3263 /* Add current search terms */
3264 menu_entries_search_append_terms_string(
3265 ozone->title, sizeof(ozone->title));
3266 }
3267 }
3268 }
3269
ozone_animation_end(void * userdata)3270 static void ozone_animation_end(void *userdata)
3271 {
3272 ozone_handle_t *ozone = (ozone_handle_t*) userdata;
3273 ozone->draw_old_list = false;
3274 ozone->animations.cursor_alpha = 1.0f;
3275 }
3276
ozone_list_open(ozone_handle_t * ozone,settings_t * settings)3277 static void ozone_list_open(ozone_handle_t *ozone, settings_t *settings)
3278 {
3279 struct gfx_animation_ctx_entry entry;
3280 uintptr_t sidebar_tag = (uintptr_t)&ozone->sidebar_offset;
3281
3282 ozone->draw_old_list = true;
3283
3284 /* Left/right animation */
3285 ozone->animations.list_alpha = 0.0f;
3286
3287 entry.cb = ozone_animation_end;
3288 entry.duration = ANIMATION_PUSH_ENTRY_DURATION;
3289 entry.easing_enum = EASING_OUT_QUAD;
3290 entry.subject = &ozone->animations.list_alpha;
3291 entry.tag = (uintptr_t)NULL;
3292 entry.target_value = 1.0f;
3293 entry.userdata = ozone;
3294
3295 gfx_animation_push(&entry);
3296
3297 /* Sidebar animation */
3298 ozone_sidebar_update_collapse(ozone, settings, true);
3299
3300 /* Kill any existing sidebar slide-in/out animations
3301 * before pushing a new one
3302 * > This is required since the 'ozone_collapse_end'
3303 * callback from an unfinished slide-out animation
3304 * may subsequently override the 'draw_sidebar'
3305 * value set at the beginning of the next slide-in
3306 * animation... */
3307 gfx_animation_kill_by_tag(&sidebar_tag);
3308
3309 if (ozone->depth == 1)
3310 {
3311 ozone->draw_sidebar = true;
3312
3313 entry.cb = NULL;
3314 entry.duration = ANIMATION_PUSH_ENTRY_DURATION;
3315 entry.easing_enum = EASING_OUT_QUAD;
3316 entry.subject = &ozone->sidebar_offset;
3317 entry.tag = sidebar_tag;
3318 entry.target_value = 0.0f;
3319 entry.userdata = NULL;
3320
3321 gfx_animation_push(&entry);
3322 }
3323 else if (ozone->depth > 1)
3324 {
3325 struct gfx_animation_ctx_entry entry;
3326
3327 entry.cb = ozone_collapse_end;
3328 entry.duration = ANIMATION_PUSH_ENTRY_DURATION;
3329 entry.easing_enum = EASING_OUT_QUAD;
3330 entry.subject = &ozone->sidebar_offset;
3331 entry.tag = sidebar_tag;
3332 entry.target_value = -ozone->dimensions_sidebar_width;
3333 entry.userdata = (void*)ozone;
3334
3335 gfx_animation_push(&entry);
3336 }
3337 }
3338
ozone_populate_entries(void * data,const char * path,const char * label,unsigned k)3339 static void ozone_populate_entries(void *data,
3340 const char *path, const char *label, unsigned k)
3341 {
3342 settings_t *settings = NULL;
3343 ozone_handle_t *ozone = (ozone_handle_t*) data;
3344
3345 int new_depth;
3346 bool animate;
3347
3348 if (!ozone)
3349 return;
3350
3351 settings = config_get_ptr();
3352
3353 ozone_set_header(ozone);
3354
3355 if (menu_driver_ctl(RARCH_MENU_CTL_IS_PREVENT_POPULATE, NULL))
3356 {
3357 menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL);
3358 ozone_selection_changed(ozone, false);
3359
3360 /* Handle playlist searches
3361 * (Ozone is a fickle beast...) */
3362 if (ozone->is_playlist)
3363 {
3364 menu_serch_terms_t *menu_search_terms =
3365 menu_entries_search_get_terms();
3366 size_t num_search_terms =
3367 menu_search_terms ? menu_search_terms->size : 0;
3368
3369 if (ozone->num_search_terms_old != num_search_terms)
3370 {
3371 /* Refresh thumbnails */
3372 ozone_unload_thumbnail_textures(ozone);
3373
3374 if (gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) ||
3375 gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
3376 {
3377 ozone_set_thumbnail_content(ozone, "");
3378 ozone_update_thumbnail_image(ozone);
3379 }
3380
3381 /* If we are currently inside an empty
3382 * playlist, return to the sidebar */
3383 if (!ozone->cursor_in_sidebar)
3384 {
3385 file_list_t *list = menu_entries_get_selection_buf_ptr(0);
3386 uintptr_t animation_tag = (uintptr_t)&ozone->animations.cursor_alpha;
3387 bool goto_sidebar = false;
3388
3389 if (!list || (list->size < 1))
3390 goto_sidebar = true;
3391
3392 if (!goto_sidebar &&
3393 (list->list[0].type != FILE_TYPE_RPL_ENTRY))
3394 goto_sidebar = true;
3395
3396 if (goto_sidebar)
3397 {
3398 gfx_animation_kill_by_tag(&animation_tag);
3399 ozone->empty_playlist = true;
3400 ozone_go_to_sidebar(ozone, settings, animation_tag);
3401 }
3402 }
3403
3404 ozone->num_search_terms_old = num_search_terms;
3405 }
3406 }
3407
3408 return;
3409 }
3410
3411 ozone->need_compute = true;
3412
3413 ozone->first_onscreen_entry = 0;
3414 ozone->last_onscreen_entry = 0;
3415
3416 new_depth = (int)ozone_list_get_size(ozone, MENU_LIST_PLAIN);
3417
3418 animate = new_depth != ozone->depth;
3419 ozone->fade_direction = new_depth <= ozone->depth;
3420 ozone->depth = new_depth;
3421 ozone->is_playlist = ozone_is_playlist(ozone, true);
3422 ozone->is_db_manager_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DATABASE_MANAGER_LIST));
3423 ozone->is_file_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES));
3424 ozone->is_quick_menu = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_RPL_ENTRY_ACTIONS)) ||
3425 string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_SETTINGS));
3426
3427 if (animate)
3428 if (ozone->categories_selection_ptr == ozone->categories_active_idx_old)
3429 ozone_list_open(ozone, settings);
3430
3431 /* Thumbnails
3432 * > Note: Leave current thumbnails loaded when
3433 * opening the quick menu - allows proper fade
3434 * out of the fullscreen thumbnail viewer */
3435 if (!ozone->is_quick_menu)
3436 {
3437 ozone_unload_thumbnail_textures(ozone);
3438
3439 if (gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) ||
3440 gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT))
3441 {
3442 /* Only auto-load thumbnails if we are viewing
3443 * a playlist or a database manager list
3444 * > Note that we can ignore file browser lists,
3445 * since the first selected item on such a list
3446 * can never have a thumbnail */
3447 if (ozone->is_playlist ||
3448 (ozone->depth == 4 && ozone->is_db_manager_list))
3449 {
3450 ozone_set_thumbnail_content(ozone, "");
3451 ozone_update_thumbnail_image(ozone);
3452 }
3453 }
3454 }
3455
3456 /* Fullscreen thumbnails are only enabled on
3457 * playlists, database manager lists and file
3458 * lists */
3459 ozone->fullscreen_thumbnails_available =
3460 (ozone->is_playlist && ozone->depth == 1) ||
3461 (ozone->is_db_manager_list && ozone->depth == 4) ||
3462 ozone->is_file_list;
3463 }
3464
3465 /* TODO: Fancy toggle animation */
3466
ozone_toggle(void * userdata,bool menu_on)3467 static void ozone_toggle(void *userdata, bool menu_on)
3468 {
3469 settings_t *settings = NULL;
3470 bool tmp = false;
3471 ozone_handle_t *ozone = (ozone_handle_t*) userdata;
3472
3473 if (!ozone)
3474 return;
3475
3476 settings = config_get_ptr();
3477 tmp = !menu_entries_ctl(
3478 MENU_ENTRIES_CTL_NEEDS_REFRESH, NULL);
3479
3480 if (tmp)
3481 menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
3482 else
3483 menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL);
3484
3485 if (ozone->depth == 1)
3486 {
3487 ozone->draw_sidebar = true;
3488 ozone->sidebar_offset = 0.0f;
3489 }
3490
3491 ozone_sidebar_update_collapse(ozone, settings, false);
3492 }
3493
ozone_menu_init_list(void * data)3494 static bool ozone_menu_init_list(void *data)
3495 {
3496 menu_displaylist_info_t info;
3497
3498 settings_t *settings = config_get_ptr();
3499 file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0);
3500 file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
3501
3502 menu_displaylist_info_init(&info);
3503
3504 info.label = strdup(
3505 msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU));
3506 info.exts = strdup("lpl");
3507 info.type_default = FILE_TYPE_PLAIN;
3508 info.enum_idx = MENU_ENUM_LABEL_MAIN_MENU;
3509
3510 menu_entries_append_enum(menu_stack, info.path,
3511 info.label,
3512 MENU_ENUM_LABEL_MAIN_MENU,
3513 info.type, info.flags, 0);
3514
3515 info.list = selection_buf;
3516
3517 if (!menu_displaylist_ctl(DISPLAYLIST_MAIN_MENU, &info, settings))
3518 goto error;
3519
3520 info.need_push = true;
3521
3522 if (!menu_displaylist_process(&info))
3523 goto error;
3524
3525 menu_displaylist_info_free(&info);
3526 return true;
3527
3528 error:
3529 menu_displaylist_info_free(&info);
3530 return false;
3531 }
3532
ozone_copy_node(const ozone_node_t * old_node)3533 static ozone_node_t *ozone_copy_node(const ozone_node_t *old_node)
3534 {
3535 ozone_node_t *new_node = (ozone_node_t*)malloc(sizeof(*new_node));
3536
3537 *new_node = *old_node;
3538 new_node->fullpath = old_node->fullpath ? strdup(old_node->fullpath) : NULL;
3539
3540 return new_node;
3541 }
3542
ozone_list_insert(void * userdata,file_list_t * list,const char * path,const char * fullpath,const char * label,size_t list_size,unsigned type)3543 static void ozone_list_insert(void *userdata,
3544 file_list_t *list,
3545 const char *path,
3546 const char *fullpath,
3547 const char *label,
3548 size_t list_size,
3549 unsigned type)
3550 {
3551 ozone_handle_t *ozone = (ozone_handle_t*) userdata;
3552 ozone_node_t *node = NULL;
3553 int i = (int)list_size;
3554
3555 if (!ozone || !list)
3556 return;
3557
3558 ozone->need_compute = true;
3559
3560 node = (ozone_node_t*)list->list[i].userdata;
3561
3562 if (!node)
3563 node = ozone_alloc_node();
3564
3565 if (!node)
3566 {
3567 RARCH_ERR("ozone node could not be allocated.\n");
3568 return;
3569 }
3570
3571 if (!string_is_empty(fullpath))
3572 {
3573 if (node->fullpath)
3574 free(node->fullpath);
3575
3576 node->fullpath = strdup(fullpath);
3577 }
3578
3579 list->list[i].userdata = node;
3580 }
3581
ozone_list_deep_copy(const file_list_t * src,file_list_t * dst,size_t first,size_t last)3582 static void ozone_list_deep_copy(const file_list_t *src, file_list_t *dst,
3583 size_t first, size_t last)
3584 {
3585 size_t i, j = 0;
3586 uintptr_t tag = (uintptr_t)dst;
3587
3588 gfx_animation_kill_by_tag(&tag);
3589
3590 ozone_free_list_nodes(dst, true);
3591
3592 file_list_clear(dst);
3593 file_list_reserve(dst, (last + 1) - first);
3594
3595 for (i = first; i <= last; ++i)
3596 {
3597 struct item_file *d = &dst->list[j];
3598 struct item_file *s = &src->list[i];
3599 void *src_udata = s->userdata;
3600 void *src_adata = s->actiondata;
3601
3602 *d = *s;
3603 d->alt = string_is_empty(d->alt) ? NULL : strdup(d->alt);
3604 d->path = string_is_empty(d->path) ? NULL : strdup(d->path);
3605 d->label = string_is_empty(d->label) ? NULL : strdup(d->label);
3606
3607 if (src_udata)
3608 dst->list[j].userdata = (void*)ozone_copy_node((const ozone_node_t*)src_udata);
3609
3610 if (src_adata)
3611 {
3612 void *data = malloc(sizeof(menu_file_list_cbs_t));
3613 memcpy(data, src_adata, sizeof(menu_file_list_cbs_t));
3614 dst->list[j].actiondata = data;
3615 }
3616
3617 ++j;
3618 }
3619
3620 dst->size = j;
3621 }
3622
ozone_list_cache(void * data,enum menu_list_type type,unsigned action)3623 void ozone_list_cache(void *data,
3624 enum menu_list_type type, unsigned action)
3625 {
3626 size_t y, entries_end;
3627 unsigned i;
3628 unsigned video_info_height;
3629 float bottom_boundary;
3630 ozone_node_t *first_node;
3631 float scale_factor;
3632 unsigned first = 0;
3633 unsigned last = 0;
3634 file_list_t *selection_buf = NULL;
3635 ozone_handle_t *ozone = (ozone_handle_t*)data;
3636
3637 if (!ozone)
3638 return;
3639
3640 scale_factor = ozone->last_scale_factor;
3641 ozone->need_compute = true;
3642 ozone->selection_old_list = ozone->selection;
3643 ozone->scroll_old = ozone->animations.scroll_y;
3644 ozone->is_playlist_old = ozone->is_playlist;
3645
3646 /* Deep copy visible elements */
3647 video_driver_get_size(NULL, &video_info_height);
3648 y = ozone->dimensions.header_height + ozone->dimensions.entry_padding_vertical;
3649 entries_end = menu_entries_get_size();
3650 selection_buf = menu_entries_get_selection_buf_ptr(0);
3651 bottom_boundary = video_info_height - ozone->dimensions.header_height - ozone->dimensions.footer_height;
3652
3653 for (i = 0; i < entries_end; i++)
3654 {
3655 ozone_node_t *node = (ozone_node_t*)selection_buf->list[i].userdata;
3656
3657 if (!node)
3658 continue;
3659
3660 if (y + ozone->animations.scroll_y + node->height + 20 * scale_factor < ozone->dimensions.header_height + ozone->dimensions.entry_padding_vertical)
3661 {
3662 first++;
3663 goto text_iterate;
3664 }
3665 else if (y + ozone->animations.scroll_y - node->height - 20 * scale_factor > bottom_boundary)
3666 goto text_iterate;
3667
3668 last++;
3669 text_iterate:
3670 y += node->height;
3671 }
3672
3673 last -= 1;
3674 last += first;
3675
3676 first_node = (ozone_node_t*)selection_buf->list[first].userdata;
3677 ozone->old_list_offset_y = first_node->position_y;
3678
3679 ozone_list_deep_copy(selection_buf,
3680 &ozone->selection_buf_old, first, last);
3681 }
3682
ozone_environ_cb(enum menu_environ_cb type,void * data,void * userdata)3683 static int ozone_environ_cb(enum menu_environ_cb type, void *data, void *userdata)
3684 {
3685 ozone_handle_t *ozone = (ozone_handle_t*) userdata;
3686
3687 if (!ozone)
3688 return -1;
3689
3690 switch (type)
3691 {
3692 case MENU_ENVIRON_ENABLE_MOUSE_CURSOR:
3693 ozone->show_cursor = true;
3694 break;
3695 case MENU_ENVIRON_DISABLE_MOUSE_CURSOR:
3696 ozone->show_cursor = false;
3697 break;
3698 case MENU_ENVIRON_RESET_HORIZONTAL_LIST:
3699 if (!ozone)
3700 return -1;
3701 {
3702 settings_t *settings = config_get_ptr();
3703 ozone_refresh_horizontal_list(ozone, settings);
3704 }
3705 break;
3706 case MENU_ENVIRON_ENABLE_SCREENSAVER:
3707 ozone->show_screensaver = true;
3708 break;
3709 case MENU_ENVIRON_DISABLE_SCREENSAVER:
3710 ozone->show_screensaver = false;
3711 break;
3712 default:
3713 return -1;
3714 }
3715
3716 return 0;
3717 }
3718
ozone_messagebox(void * data,const char * message)3719 static void ozone_messagebox(void *data, const char *message)
3720 {
3721 ozone_handle_t *ozone = (ozone_handle_t*) data;
3722
3723 if (!ozone || string_is_empty(message))
3724 return;
3725
3726 if (ozone->pending_message)
3727 {
3728 free(ozone->pending_message);
3729 ozone->pending_message = NULL;
3730 }
3731
3732 ozone->pending_message = strdup(message);
3733 ozone->messagebox_state = true;
3734 ozone->should_draw_messagebox = true;
3735 }
3736
ozone_deferred_push_content_actions(menu_displaylist_info_t * info)3737 static int ozone_deferred_push_content_actions(menu_displaylist_info_t *info)
3738 {
3739 settings_t *settings = config_get_ptr();
3740 if (!menu_displaylist_ctl(
3741 DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS, info, settings))
3742 return -1;
3743 menu_displaylist_process(info);
3744 menu_displaylist_info_free(info);
3745 return 0;
3746 }
3747
ozone_list_bind_init_compare_label(menu_file_list_cbs_t * cbs)3748 static int ozone_list_bind_init_compare_label(menu_file_list_cbs_t *cbs)
3749 {
3750 if (cbs && cbs->enum_idx != MSG_UNKNOWN)
3751 {
3752 switch (cbs->enum_idx)
3753 {
3754 case MENU_ENUM_LABEL_CONTENT_ACTIONS:
3755 cbs->action_deferred_push = ozone_deferred_push_content_actions;
3756 break;
3757 default:
3758 return -1;
3759 }
3760 }
3761
3762 return 0;
3763 }
3764
ozone_list_bind_init(menu_file_list_cbs_t * cbs,const char * path,const char * label,unsigned type,size_t idx)3765 static int ozone_list_bind_init(menu_file_list_cbs_t *cbs,
3766 const char *path, const char *label, unsigned type, size_t idx)
3767 {
3768 if (ozone_list_bind_init_compare_label(cbs) == 0)
3769 return 0;
3770
3771 return -1;
3772 }
3773
ozone_pointer_up(void * userdata,unsigned x,unsigned y,unsigned ptr,enum menu_input_pointer_gesture gesture,menu_file_list_cbs_t * cbs,menu_entry_t * entry,unsigned action)3774 static int ozone_pointer_up(void *userdata,
3775 unsigned x, unsigned y, unsigned ptr,
3776 enum menu_input_pointer_gesture gesture,
3777 menu_file_list_cbs_t *cbs,
3778 menu_entry_t *entry, unsigned action)
3779 {
3780 unsigned width, height;
3781 ozone_handle_t *ozone = (ozone_handle_t*)userdata;
3782 file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
3783 uintptr_t sidebar_tag = (uintptr_t)selection_buf;
3784 size_t selection = menu_navigation_get_selection();
3785 size_t entries_end = menu_entries_get_size();
3786 settings_t *settings = config_get_ptr();
3787
3788 if (!ozone)
3789 return -1;
3790
3791 /* If fullscreen thumbnail view is enabled,
3792 * all input will disable it and otherwise
3793 * be ignored */
3794 if (ozone->show_fullscreen_thumbnails)
3795 {
3796 /* Must reset scroll acceleration, in case
3797 * user performed a swipe (don't want menu
3798 * list to 'drift' after hiding fullscreen
3799 * thumbnails...) */
3800 menu_input_set_pointer_y_accel(0.0f);
3801
3802 ozone_hide_fullscreen_thumbnails(ozone, true);
3803 return 0;
3804 }
3805
3806 video_driver_get_size(&width, &height);
3807
3808 switch (gesture)
3809 {
3810 case MENU_INPUT_GESTURE_TAP:
3811 case MENU_INPUT_GESTURE_SHORT_PRESS:
3812 /* Tap/press header or footer: Menu back/cancel */
3813 if ((y < ozone->dimensions.header_height) ||
3814 (y > height - ozone->dimensions.footer_height))
3815 return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_CANCEL);
3816 /* Tap/press entries: Activate and/or select item */
3817 else if ((ptr < entries_end) &&
3818 (x > ozone->dimensions_sidebar_width
3819 + ozone->sidebar_offset) &&
3820 (x < width - ozone->animations.thumbnail_bar_position))
3821 {
3822 if (gesture == MENU_INPUT_GESTURE_TAP)
3823 {
3824 /* A 'tap' always produces a menu action */
3825
3826 /* If current 'pointer' item is not active,
3827 * activate it immediately */
3828 if (ptr != selection)
3829 menu_navigation_set_selection(ptr);
3830
3831 /* If we are not currently in the sidebar,
3832 * perform a MENU_ACTION_SELECT on currently
3833 * active item
3834 * > NOTE 1: Cannot perform a 'leave sidebar' operation
3835 * and a MENU_ACTION_SELECT at the same time...
3836 * > NOTE 2: We still use 'selection' (i.e. old selection
3837 * value) here. This ensures that ozone_menu_entry_action()
3838 * registers any change due to the above automatic
3839 * 'pointer item' activation, and thus operates
3840 * on the correct target entry */
3841 if (!ozone->cursor_in_sidebar)
3842 return ozone_menu_entry_action(ozone, entry,
3843 selection, MENU_ACTION_SELECT);
3844
3845 /* If we currently in the sidebar, leave it */
3846 ozone_leave_sidebar(ozone, settings, sidebar_tag);
3847 }
3848 else
3849 {
3850 /* A 'short' press is used only to activate (highlight)
3851 * an item - it does not invoke a MENU_ACTION_SELECT
3852 * action */
3853 menu_input_set_pointer_y_accel(0.0f);
3854
3855 if (ptr != selection)
3856 menu_navigation_set_selection(ptr);
3857
3858 /* If we are currently in the sidebar, leave it */
3859 if (ozone->cursor_in_sidebar)
3860 ozone_leave_sidebar(ozone, settings, sidebar_tag);
3861 /* If this is a playlist and the selection
3862 * has changed, must update thumbnails */
3863 else if (ozone->is_playlist &&
3864 (ozone->depth == 1) &&
3865 (ptr != selection))
3866 {
3867 ozone_set_thumbnail_content(ozone, "");
3868 ozone_update_thumbnail_image(ozone);
3869 }
3870 }
3871 }
3872 /* Tap/press thumbnail bar: toggle content metadata
3873 * override */
3874 else if (x > width - ozone->animations.thumbnail_bar_position)
3875 {
3876 /* Want to capture all input here, but only act
3877 * upon it if the content metadata toggle is
3878 * available (i.e. viewing a playlist with dual
3879 * thumbnails) */
3880 if (ozone_metadata_override_available(ozone))
3881 return ozone_menu_entry_action(ozone, entry, selection, MENU_ACTION_INFO);
3882 }
3883 /* Tap/press sidebar: return to sidebar or select
3884 * category */
3885 else if (ozone->pointer_in_sidebar)
3886 {
3887 /* If cursor is not in sidebar, return to sidebar */
3888 if (!ozone->cursor_in_sidebar)
3889 ozone_go_to_sidebar(ozone, settings, sidebar_tag);
3890 /* Otherwise, select current category */
3891 else if (
3892 ozone->pointer_categories_selection
3893 != ozone->categories_selection_ptr)
3894 {
3895 unsigned horizontal_list_size = (unsigned)ozone->horizontal_list.size;
3896
3897 /* Ensure that current category is valid */
3898 if (ozone->pointer_categories_selection <= ozone->system_tab_end + horizontal_list_size)
3899 ozone_sidebar_goto(ozone, (unsigned)ozone->pointer_categories_selection);
3900 }
3901 }
3902 break;
3903 case MENU_INPUT_GESTURE_LONG_PRESS:
3904 /* 'Reset to default' action */
3905 if ((y > ozone->dimensions.header_height) &&
3906 (y < height - ozone->dimensions.footer_height) &&
3907 (ptr < entries_end) &&
3908 (ptr == selection) &&
3909 (x > ozone->dimensions_sidebar_width + ozone->sidebar_offset) &&
3910 (x < width - ozone->animations.thumbnail_bar_position))
3911 return ozone_menu_entry_action(ozone,
3912 entry, selection, MENU_ACTION_START);
3913 break;
3914 case MENU_INPUT_GESTURE_SWIPE_LEFT:
3915 /* If this is a playlist, descend alphabet
3916 * > Note: Can only do this if we are not using
3917 * a mouse, since it conflicts with auto selection
3918 * of entry under cursor */
3919 if ((ozone->pointer.type != MENU_POINTER_MOUSE) &&
3920 ozone->is_playlist &&
3921 (ozone->depth == 1))
3922 return ozone_menu_entry_action(ozone, entry, (size_t)ptr, MENU_ACTION_SCROLL_UP);
3923 break;
3924 case MENU_INPUT_GESTURE_SWIPE_RIGHT:
3925 /* If this is a playlist, ascend alphabet
3926 * > Note: Can only do this if we are not using
3927 * a mouse, since it conflicts with auto selection
3928 * of entry under cursor */
3929 if ((ozone->pointer.type != MENU_POINTER_MOUSE) &&
3930 ozone->is_playlist &&
3931 (ozone->depth == 1))
3932 return ozone_menu_entry_action(ozone, entry, (size_t)ptr, MENU_ACTION_SCROLL_DOWN);
3933 break;
3934 default:
3935 /* Ignore input */
3936 break;
3937 }
3938
3939 return 0;
3940 }
3941
ozone_alloc_node(void)3942 ozone_node_t *ozone_alloc_node(void)
3943 {
3944 ozone_node_t *node = (ozone_node_t*)malloc(sizeof(*node));
3945
3946 node->height = 0;
3947 node->position_y = 0;
3948 node->console_name = NULL;
3949 node->icon = 0;
3950 node->content_icon = 0;
3951 node->fullpath = NULL;
3952 node->sublabel_lines = 0;
3953 node->wrap = false;
3954
3955 return node;
3956 }
3957
ozone_list_get_size(void * data,enum menu_list_type type)3958 size_t ozone_list_get_size(void *data, enum menu_list_type type)
3959 {
3960 ozone_handle_t *ozone = (ozone_handle_t*) data;
3961
3962 if (!ozone)
3963 return 0;
3964
3965 switch (type)
3966 {
3967 case MENU_LIST_PLAIN:
3968 return menu_entries_get_stack_size(0);
3969 case MENU_LIST_HORIZONTAL:
3970 return ozone->horizontal_list.size;
3971 case MENU_LIST_TABS:
3972 return ozone->system_tab_end;
3973 }
3974
3975 return 0;
3976 }
3977
ozone_free_list_nodes(file_list_t * list,bool actiondata)3978 void ozone_free_list_nodes(file_list_t *list, bool actiondata)
3979 {
3980 unsigned i, size = list ? (unsigned)list->size : 0;
3981
3982 for (i = 0; i < size; ++i)
3983 {
3984 ozone_free_node((ozone_node_t*)file_list_get_userdata_at_offset(list, i));
3985
3986 list->list[i].userdata = NULL;
3987
3988 if (actiondata)
3989 file_list_free_actiondata(list, i);
3990 }
3991 }
3992
ozone_update_content_metadata(ozone_handle_t * ozone)3993 void ozone_update_content_metadata(ozone_handle_t *ozone)
3994 {
3995 const char *core_name = NULL;
3996 size_t selection = menu_navigation_get_selection();
3997 playlist_t *playlist = playlist_get_cached();
3998 settings_t *settings = config_get_ptr();
3999 bool scroll_content_metadata = settings->bools.ozone_scroll_content_metadata;
4000 bool show_entry_idx = settings->bools.playlist_show_entry_idx;
4001 bool content_runtime_log = settings->bools.content_runtime_log;
4002 bool content_runtime_log_aggr = settings->bools.content_runtime_log_aggregate;
4003 const char *directory_runtime_log = settings->paths.directory_runtime_log;
4004 const char *directory_playlist = settings->paths.directory_playlist;
4005 unsigned runtime_type = settings->uints.playlist_sublabel_runtime_type;
4006 enum playlist_sublabel_last_played_style_type
4007 runtime_last_played_style =
4008 (enum playlist_sublabel_last_played_style_type)
4009 settings->uints.playlist_sublabel_last_played_style;
4010 enum playlist_sublabel_last_played_date_separator_type
4011 runtime_date_separator =
4012 (enum playlist_sublabel_last_played_date_separator_type)
4013 settings->uints.menu_timedate_date_separator;
4014
4015 /* Must check whether core corresponds to 'viewer'
4016 * content even when not using a playlist, otherwise
4017 * file browser image updates are mishandled */
4018 if (gfx_thumbnail_get_core_name(ozone->thumbnail_path_data, &core_name))
4019 ozone->selection_core_is_viewer = string_is_equal(core_name, "imageviewer")
4020 || string_is_equal(core_name, "musicplayer")
4021 || string_is_equal(core_name, "movieplayer");
4022 else
4023 ozone->selection_core_is_viewer = false;
4024
4025 if (ozone->is_playlist && playlist)
4026 {
4027 const char *core_label = NULL;
4028 const struct playlist_entry *entry = NULL;
4029 size_t list_size = menu_entries_get_size();
4030 file_list_t *list = menu_entries_get_selection_buf_ptr(0);
4031 bool playlist_valid = false;
4032 size_t playlist_index = selection;
4033
4034 /* Get playlist index corresponding
4035 * to the selected entry */
4036 if (list &&
4037 (selection < list_size) &&
4038 (list->list[selection].type == FILE_TYPE_RPL_ENTRY))
4039 {
4040 playlist_valid = true;
4041 playlist_index = list->list[selection].entry_idx;
4042 }
4043
4044 /* Fill entry enumeration */
4045 if (show_entry_idx)
4046 snprintf(ozone->selection_entry_enumeration, sizeof(ozone->selection_entry_enumeration),
4047 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT_INFO_ENTRY_IDX),
4048 (unsigned long)(playlist_index + 1), (unsigned long)list_size);
4049 else
4050 ozone->selection_entry_enumeration[0] = '\0';
4051
4052 /* Fill core name */
4053 if (!core_name || string_is_equal(core_name, "DETECT"))
4054 core_label = msg_hash_to_str(MSG_AUTODETECT);
4055 else
4056 core_label = core_name;
4057
4058 snprintf(ozone->selection_core_name, sizeof(ozone->selection_core_name),
4059 "%s %s", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_SUBLABEL_CORE), core_label);
4060
4061 /* Word wrap core name string, if required */
4062 if (!scroll_content_metadata)
4063 {
4064 char tmpstr[sizeof(ozone->selection_core_name)];
4065 unsigned metadata_len =
4066 (ozone->dimensions.thumbnail_bar_width - ((ozone->dimensions.sidebar_entry_icon_padding * 2) * 2)) /
4067 ozone->fonts.footer.glyph_width;
4068
4069 strlcpy(tmpstr, ozone->selection_core_name, sizeof(tmpstr));
4070 (ozone->word_wrap)(ozone->selection_core_name, sizeof(ozone->selection_core_name),
4071 tmpstr, metadata_len, ozone->fonts.footer.wideglyph_width, 0);
4072 ozone->selection_core_name_lines = ozone_count_lines(ozone->selection_core_name);
4073 }
4074 else
4075 ozone->selection_core_name_lines = 1;
4076
4077 /* Fill play time if applicable */
4078 if (playlist_valid &&
4079 (content_runtime_log || content_runtime_log_aggr))
4080 playlist_get_index(playlist, playlist_index, &entry);
4081
4082 if (entry)
4083 {
4084 if (entry->runtime_status == PLAYLIST_RUNTIME_UNKNOWN)
4085 runtime_update_playlist(
4086 playlist, playlist_index,
4087 directory_runtime_log,
4088 directory_playlist,
4089 (runtime_type == PLAYLIST_RUNTIME_PER_CORE),
4090 runtime_last_played_style,
4091 runtime_date_separator);
4092
4093 if (!string_is_empty(entry->runtime_str))
4094 strlcpy(ozone->selection_playtime, entry->runtime_str, sizeof(ozone->selection_playtime));
4095 if (!string_is_empty(entry->last_played_str))
4096 strlcpy(ozone->selection_lastplayed, entry->last_played_str, sizeof(ozone->selection_lastplayed));
4097 }
4098 else
4099 {
4100 snprintf(ozone->selection_playtime, sizeof(ozone->selection_playtime), "%s %s",
4101 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_SUBLABEL_RUNTIME),
4102 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISABLED));
4103
4104 snprintf(ozone->selection_lastplayed, sizeof(ozone->selection_lastplayed), "%s %s",
4105 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_SUBLABEL_LAST_PLAYED),
4106 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISABLED));
4107 }
4108
4109 /* Word wrap last played string, if required */
4110 if (!scroll_content_metadata)
4111 {
4112 /* Note: Have to use a fixed length of '30' here, to
4113 * avoid awkward wrapping for certain last played time
4114 * formats. Last played strings are well defined, however
4115 * (unlike core names), so this should never overflow the
4116 * side bar */
4117 char tmpstr[sizeof(ozone->selection_lastplayed)];
4118
4119 strlcpy(tmpstr, ozone->selection_lastplayed, sizeof(tmpstr));
4120 (ozone->word_wrap)(ozone->selection_lastplayed, sizeof(ozone->selection_lastplayed), tmpstr, 30, 100, 0);
4121 ozone->selection_lastplayed_lines = ozone_count_lines(ozone->selection_lastplayed);
4122 }
4123 else
4124 ozone->selection_lastplayed_lines = 1;
4125 }
4126 }
4127
ozone_font_flush(unsigned video_width,unsigned video_height,ozone_font_data_t * font_data)4128 void ozone_font_flush(
4129 unsigned video_width, unsigned video_height,
4130 ozone_font_data_t *font_data)
4131 {
4132 /* Flushing is slow - only do it if font
4133 * has actually been used */
4134 if (!font_data ||
4135 (font_data->raster_block.carr.coords.vertices == 0))
4136 return;
4137
4138 font_driver_flush(video_width, video_height, font_data->font);
4139 font_data->raster_block.carr.coords.vertices = 0;
4140 }
4141
ozone_hide_fullscreen_thumbnails(ozone_handle_t * ozone,bool animate)4142 void ozone_hide_fullscreen_thumbnails(ozone_handle_t *ozone, bool animate)
4143 {
4144 uintptr_t alpha_tag = (uintptr_t)
4145 &ozone->animations.fullscreen_thumbnail_alpha;
4146 gfx_thumbnail_state_t *p_gfx_thumb = gfx_thumb_get_ptr();
4147
4148 /* Kill any existing fade in/out animations */
4149 gfx_animation_kill_by_tag(&alpha_tag);
4150
4151 /* Check whether animations are enabled */
4152 if (animate && (ozone->animations.fullscreen_thumbnail_alpha > 0.0f))
4153 {
4154 gfx_animation_ctx_entry_t animation_entry;
4155
4156 /* Configure fade out animation */
4157 animation_entry.easing_enum = EASING_OUT_QUAD;
4158 animation_entry.tag = alpha_tag;
4159 animation_entry.duration = p_gfx_thumb->fade_duration;
4160 animation_entry.target_value = 0.0f;
4161 animation_entry.subject = &ozone->animations.fullscreen_thumbnail_alpha;
4162 animation_entry.cb = NULL;
4163 animation_entry.userdata = NULL;
4164
4165 /* Push animation */
4166 gfx_animation_push(&animation_entry);
4167 }
4168 /* No animation - just set thumbnail alpha to zero */
4169 else
4170 ozone->animations.fullscreen_thumbnail_alpha = 0.0f;
4171
4172 /* Disable fullscreen thumbnails */
4173 ozone->show_fullscreen_thumbnails = false;
4174 }
4175
ozone_show_fullscreen_thumbnails(ozone_handle_t * ozone)4176 void ozone_show_fullscreen_thumbnails(ozone_handle_t *ozone)
4177 {
4178 menu_entry_t selected_entry;
4179 gfx_animation_ctx_entry_t animation_entry;
4180 const char *thumbnail_label = NULL;
4181 file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
4182 uintptr_t alpha_tag = (uintptr_t)&ozone->animations.fullscreen_thumbnail_alpha;
4183 uintptr_t scroll_tag = (uintptr_t)selection_buf;
4184 gfx_thumbnail_state_t *p_gfx_thumb = gfx_thumb_get_ptr();
4185
4186 /* Before showing fullscreen thumbnails, must
4187 * ensure that any existing fullscreen thumbnail
4188 * view is disabled... */
4189 ozone_hide_fullscreen_thumbnails(ozone, false);
4190
4191 /* Sanity check: Return immediately if this is
4192 * a menu without thumbnail support, or cursor
4193 * is currently in the sidebar */
4194 if (!ozone->fullscreen_thumbnails_available ||
4195 ozone->cursor_in_sidebar)
4196 return;
4197
4198 /* We can only enable fullscreen thumbnails if
4199 * current selection has at least one valid thumbnail
4200 * and all thumbnails for current selection are already
4201 * loaded/available */
4202 if (ozone->selection_core_is_viewer)
4203 {
4204 /* imageviewer content requires special treatment,
4205 * since only the right thumbnail is ever loaded */
4206 if (!gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT))
4207 return;
4208
4209 if (ozone->thumbnails.right.status != GFX_THUMBNAIL_STATUS_AVAILABLE)
4210 return;
4211 }
4212 else
4213 {
4214 bool left_thumbnail_enabled = gfx_thumbnail_is_enabled(
4215 ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT);
4216
4217 if (!left_thumbnail_enabled &&
4218 !gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT))
4219 return;
4220
4221 if ((ozone->thumbnails.right.status == GFX_THUMBNAIL_STATUS_AVAILABLE) &&
4222 (left_thumbnail_enabled &&
4223 ((ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_MISSING) &&
4224 (ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_AVAILABLE))))
4225 return;
4226
4227 if ((ozone->thumbnails.right.status == GFX_THUMBNAIL_STATUS_MISSING) &&
4228 (!left_thumbnail_enabled ||
4229 (ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_AVAILABLE)))
4230 return;
4231 }
4232
4233 /* Menu list must be stationary while fullscreen
4234 * thumbnails are shown
4235 * > Kill any existing scroll animations and
4236 * reset scroll acceleration */
4237 gfx_animation_kill_by_tag(&scroll_tag);
4238 menu_input_set_pointer_y_accel(0.0f);
4239
4240 /* Cache selected entry label
4241 * (used as title when fullscreen thumbnails
4242 * are shown) */
4243 ozone->fullscreen_thumbnail_label[0] = '\0';
4244
4245 /* > Get menu entry */
4246 MENU_ENTRY_INIT(selected_entry);
4247 selected_entry.path_enabled = false;
4248 selected_entry.value_enabled = false;
4249 selected_entry.sublabel_enabled = false;
4250 menu_entry_get(&selected_entry, 0, (size_t)ozone->selection, NULL, true);
4251
4252 /* > Get entry label */
4253 if (!string_is_empty(selected_entry.rich_label))
4254 thumbnail_label = selected_entry.rich_label;
4255 else
4256 thumbnail_label = selected_entry.path;
4257
4258 /* > Sanity check */
4259 if (!string_is_empty(thumbnail_label))
4260 strlcpy(
4261 ozone->fullscreen_thumbnail_label,
4262 thumbnail_label,
4263 sizeof(ozone->fullscreen_thumbnail_label));
4264
4265 /* Configure fade in animation */
4266 animation_entry.easing_enum = EASING_OUT_QUAD;
4267 animation_entry.tag = alpha_tag;
4268 animation_entry.duration = p_gfx_thumb->fade_duration;
4269 animation_entry.target_value = 1.0f;
4270 animation_entry.subject = &ozone->animations.fullscreen_thumbnail_alpha;
4271 animation_entry.cb = NULL;
4272 animation_entry.userdata = NULL;
4273
4274 /* Push animation */
4275 gfx_animation_push(&animation_entry);
4276
4277 /* Enable fullscreen thumbnails */
4278 ozone->fullscreen_thumbnail_selection = (size_t)ozone->selection;
4279 ozone->show_fullscreen_thumbnails = true;
4280 }
4281
ozone_toggle_metadata_override(ozone_handle_t * ozone)4282 void ozone_toggle_metadata_override(ozone_handle_t *ozone)
4283 {
4284 gfx_animation_ctx_entry_t animation_entry;
4285 uintptr_t alpha_tag = (uintptr_t)
4286 &ozone->animations.left_thumbnail_alpha;
4287 gfx_thumbnail_state_t *p_gfx_thumb = gfx_thumb_get_ptr();
4288
4289 /* Kill any existing fade in/out animations */
4290 gfx_animation_kill_by_tag(&alpha_tag);
4291
4292 /* Set common animation parameters */
4293 animation_entry.easing_enum = EASING_OUT_QUAD;
4294 animation_entry.tag = alpha_tag;
4295 animation_entry.duration = p_gfx_thumb->fade_duration;
4296 animation_entry.subject = &ozone->animations.left_thumbnail_alpha;
4297 animation_entry.cb = NULL;
4298 animation_entry.userdata = NULL;
4299
4300 /* Check whether metadata override is
4301 * currently enabled */
4302 if (ozone->force_metadata_display)
4303 {
4304 /* Thumbnail will fade in */
4305 animation_entry.target_value = 1.0f;
4306 ozone->force_metadata_display = false;
4307 }
4308 else
4309 {
4310 /* Thumbnail will fade out */
4311 animation_entry.target_value = 0.0f;
4312 ozone->force_metadata_display = true;
4313 }
4314
4315 /* Push animation */
4316 gfx_animation_push(&animation_entry);
4317 }
4318
ozone_start_cursor_wiggle(ozone_handle_t * ozone,enum menu_action direction)4319 void ozone_start_cursor_wiggle(ozone_handle_t* ozone, enum menu_action direction)
4320 {
4321 /* Don't start another wiggle animation on top of another */
4322 if (!ozone || ozone->cursor_wiggle_state.wiggling)
4323 return;
4324
4325 /* Don't allow wiggling in invalid directions */
4326 if (!(
4327 direction == MENU_ACTION_UP ||
4328 direction == MENU_ACTION_DOWN ||
4329 direction == MENU_ACTION_LEFT ||
4330 direction == MENU_ACTION_RIGHT
4331 ))
4332 return;
4333
4334 /* Start wiggling */
4335 ozone->cursor_wiggle_state.start_time = menu_driver_get_current_time() / 1000;
4336 ozone->cursor_wiggle_state.direction = direction;
4337 ozone->cursor_wiggle_state.amplitude = rand() % 15 + 10;
4338 ozone->cursor_wiggle_state.wiggling = true;
4339 }
4340
ozone_wiggle(ozone_handle_t * ozone,float t)4341 static int ozone_wiggle(ozone_handle_t* ozone, float t)
4342 {
4343 float a = ozone->cursor_wiggle_state.amplitude;
4344
4345 /* Damped sine wave */
4346 float w = 0.8f; /* period */
4347 float c = 0.35f; /* damp factor */
4348 return roundf(a * exp(-(c * t)) * sin(w * t));
4349 }
4350
ozone_apply_cursor_wiggle_offset(ozone_handle_t * ozone,int * x,size_t * y)4351 void ozone_apply_cursor_wiggle_offset(ozone_handle_t* ozone, int* x, size_t* y)
4352 {
4353 retro_time_t cur_time;
4354 retro_time_t t;
4355
4356 /* Don't do anything if we are not wiggling */
4357 if (!ozone || !ozone->cursor_wiggle_state.wiggling)
4358 return;
4359
4360 cur_time = menu_driver_get_current_time() / 1000;
4361 t = (cur_time - ozone->cursor_wiggle_state.start_time) / 10;
4362
4363 /* Has the animation ended? */
4364 if (t >= OZONE_WIGGLE_DURATION)
4365 {
4366 ozone->cursor_wiggle_state.wiggling = false;
4367 return;
4368 }
4369
4370 /* Change cursor position depending on wiggle direction */
4371 switch (ozone->cursor_wiggle_state.direction)
4372 {
4373 case MENU_ACTION_RIGHT:
4374 *x += ozone_wiggle(ozone, t);
4375 break;
4376 case MENU_ACTION_LEFT:
4377 *x -= ozone_wiggle(ozone, t);
4378 break;
4379 case MENU_ACTION_DOWN:
4380 *y += ozone_wiggle(ozone, t);
4381 break;
4382 case MENU_ACTION_UP:
4383 *y -= ozone_wiggle(ozone, t);
4384 break;
4385 default:
4386 break;
4387 }
4388 }
4389
4390 menu_ctx_driver_t menu_ctx_ozone = {
4391 NULL, /* set_texture */
4392 ozone_messagebox,
4393 ozone_render,
4394 ozone_frame,
4395 ozone_init,
4396 ozone_free,
4397 ozone_context_reset,
4398 ozone_context_destroy,
4399 ozone_populate_entries,
4400 ozone_toggle,
4401 ozone_navigation_clear,
4402 NULL,
4403 NULL,
4404 ozone_navigation_set,
4405 ozone_navigation_pointer_changed,
4406 ozone_navigation_alphabet,
4407 ozone_navigation_alphabet,
4408 ozone_menu_init_list,
4409 ozone_list_insert,
4410 NULL, /* list_prepend */
4411 ozone_list_free,
4412 ozone_list_clear,
4413 ozone_list_cache,
4414 ozone_list_push,
4415 ozone_list_get_selection,
4416 ozone_list_get_size,
4417 ozone_list_get_entry,
4418 NULL, /* list_set_selection */
4419 ozone_list_bind_init,
4420 NULL,
4421 "ozone",
4422 ozone_environ_cb,
4423 NULL,
4424 ozone_update_thumbnail_image,
4425 ozone_refresh_thumbnail_image,
4426 ozone_set_thumbnail_system,
4427 ozone_get_thumbnail_system,
4428 ozone_set_thumbnail_content,
4429 gfx_display_osk_ptr_at_pos,
4430 NULL, /* update_savestate_thumbnail_path */
4431 NULL, /* update_savestate_thumbnail_image */
4432 NULL, /* pointer_down */
4433 ozone_pointer_up,
4434 ozone_menu_entry_action
4435 };
4436