mirror of
https://github.com/Team-Resurgent/LithiumX.git
synced 2026-08-15 11:18:19 -07:00
Initial Commit
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.images/*'
|
||||
- 'LICENSE'
|
||||
- '.gitignore'
|
||||
- '*.md'
|
||||
branches: [ master ]
|
||||
push:
|
||||
paths-ignore:
|
||||
- '.images/*'
|
||||
- 'LICENSE'
|
||||
- '.gitignore'
|
||||
- '*.md'
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
Xbox:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install and Setup Dependencies
|
||||
run: |
|
||||
sudo apt-get update -y && sudo apt-get install -y flex bison clang lld llvm
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Compile
|
||||
run: |
|
||||
eval $(src/lib/nxdk/bin/activate -s)
|
||||
make -f Makefile.nxdk -j$(nproc)
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: dash_xbox
|
||||
path: |
|
||||
LithiumX.iso
|
||||
bin
|
||||
|
||||
Linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install and Setup Dependencies
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt install build-essential libsdl2-dev libturbojpeg-dev -y
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Compile
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
cmake --build .
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: dash_linux
|
||||
path: |
|
||||
build/LithiumX
|
||||
|
||||
Windows:
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: Setup MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
- name: Install and Setup Dependencies
|
||||
run: |
|
||||
pacman -S mingw-w64-x86_64-make --noconfirm
|
||||
pacman -S mingw-w64-x86_64-cmake --noconfirm
|
||||
pacman -S mingw-w64-x86_64-gcc --noconfirm
|
||||
pacman -S mingw-w64-x86_64-SDL2 --noconfirm
|
||||
pacman -S mingw-w64-x86_64-libjpeg-turbo --noconfirm
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Compile
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake .. -G "MinGW Makefiles"
|
||||
cmake --build .
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: dash_windows
|
||||
path: |
|
||||
build/LithiumX.exe
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
.vscode
|
||||
bin
|
||||
build
|
||||
linux_build
|
||||
CMakeFiles
|
||||
CMakeCache.txt
|
||||
dash.xml
|
||||
*.d
|
||||
*.obj
|
||||
*.exe
|
||||
*.iso
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
[submodule "src/nanoprintf"]
|
||||
path = src/lib/nanoprintf
|
||||
url = https://github.com/charlesnicholson/nanoprintf.git
|
||||
[submodule "src/lib/lvgl"]
|
||||
path = src/lib/lvgl
|
||||
url = https://github.com/Ryzee119/lvgl.git
|
||||
[submodule "src/xml_c"]
|
||||
path = src/lib/xml
|
||||
url = https://github.com/Ryzee119/xml_c.git
|
||||
[submodule "src/lib/xml"]
|
||||
path = src/lib/xml
|
||||
url = https://github.com/Ryzee119/xml_c.git
|
||||
[submodule "src/lib/nxdk"]
|
||||
path = src/lib/nxdk
|
||||
url = https://github.com/Ryzee119/nxdk.git
|
||||
@@ -0,0 +1,57 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
project(LithiumX LANGUAGES C)
|
||||
|
||||
execute_process(
|
||||
COMMAND git log -1 --format=%h
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_COMMIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
add_definitions("-DBUILD_VERSION=\"${GIT_COMMIT_HASH}\"")
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(SDL2 REQUIRED sdl2)
|
||||
|
||||
set(PROJECT_DIR "${CMAKE_SOURCE_DIR}/src")
|
||||
set(LVGL_DIR "${PROJECT_DIR}/lib/lvgl")
|
||||
set(LV_CONF_PATH ${PROJECT_DIR}/lv_conf.h CACHE STRING "" FORCE)
|
||||
|
||||
add_definitions(-DLVGL_USE_CUSTOM_CONTROLLER_MAP)
|
||||
add_definitions(-DLV_LVGL_H_INCLUDE_SIMPLE)
|
||||
add_definitions(-DLV_CONF_PATH=${LV_CONF_PATH})
|
||||
add_definitions(-DJPEG_DECODER_QUEUE_SIZE=1024)
|
||||
add_definitions(-DQRURL=\"https://github.com/Ryzee119/LithiumX\")
|
||||
#add_definitions(-DDBG) #Show fps and memory usage info
|
||||
|
||||
add_compile_options(-Wall -O3)
|
||||
file(GLOB PROJECT_FILES
|
||||
"${PROJECT_DIR}/dash.c"
|
||||
"${PROJECT_DIR}/dash_styles.c"
|
||||
"${PROJECT_DIR}/dash_menu.c"
|
||||
"${PROJECT_DIR}/dash_synop.c"
|
||||
"${PROJECT_DIR}/dash_titlelist.c"
|
||||
"${PROJECT_DIR}/main.c"
|
||||
"${PROJECT_DIR}/helpers/nano_debug.c"
|
||||
"${PROJECT_DIR}/helpers/fileio.c"
|
||||
"${PROJECT_DIR}/platform/platform_generic.c"
|
||||
"${PROJECT_DIR}/dash_resources/default_tbn.c"
|
||||
"${PROJECT_DIR}/lvgl_drivers/lv_sdl_indev.c"
|
||||
"${PROJECT_DIR}/lvgl_drivers/lv_sdl_disp.c"
|
||||
)
|
||||
add_executable(LithiumX ${PROJECT_FILES})
|
||||
|
||||
include_directories(${LVGL_DIR})
|
||||
include_directories(${PROJECT_DIR})
|
||||
include_directories(${PROJECT_DIR}/lib)
|
||||
include_directories(${PROJECT_DIR}/lvgl_drivers)
|
||||
include_directories(${LIBJPEG_TURBO_DIR})
|
||||
|
||||
add_subdirectory(${LVGL_DIR})
|
||||
add_subdirectory(${PROJECT_DIR}/lib/jpg_decoder/)
|
||||
add_subdirectory(${PROJECT_DIR}/lib/xml/)
|
||||
|
||||
target_include_directories(LithiumX PUBLIC ${SDL2_INCLUDE_DIRS} ${TURBOJPEG_INCLUDE_DIRS})
|
||||
target_compile_options(LithiumX PUBLIC ${SDL2_CFLAGS_OTHER})
|
||||
target_link_libraries(LithiumX ${SDL2_LIBRARIES} ${TURBOJPEG_LIBRARIES}
|
||||
${LIBJPEG_LIBRARIES} lvgl::lvgl xml jpg_decoder SDL2)
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Wendland
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,48 @@
|
||||
XBE_TITLE = LithiumX
|
||||
GEN_XISO = $(XBE_TITLE).iso
|
||||
NXDK_SDL = y
|
||||
|
||||
LVGL_DIR = src/lib/lvgl
|
||||
LV_CONF_PATH = "lv_conf.h"
|
||||
GIT_COMMIT_HASH := "$(shell git describe --abbrev=7 --always)"
|
||||
|
||||
# Include my files
|
||||
SRCS += \
|
||||
$(CURDIR)/src/main.c \
|
||||
$(CURDIR)/src/dash.c \
|
||||
$(CURDIR)/src/dash_menu.c \
|
||||
$(CURDIR)/src/dash_styles.c \
|
||||
$(CURDIR)/src/dash_synop.c \
|
||||
$(CURDIR)/src/dash_titlelist.c \
|
||||
$(CURDIR)/src/helpers/fileio.c \
|
||||
$(CURDIR)/src/helpers/nano_debug.c \
|
||||
$(CURDIR)/src/platform/platform_xbox.c \
|
||||
$(CURDIR)/src/dash_resources/default_tbn.c \
|
||||
$(CURDIR)/src/lib/jpg_decoder/jpg_decoder.c \
|
||||
$(CURDIR)/src/lib/xml/src/xml.c \
|
||||
$(CURDIR)/src/lvgl_drivers/lv_sdl_disp.c) \
|
||||
$(CURDIR)/src/lvgl_drivers/lv_sdl_indev.c)
|
||||
|
||||
CFLAGS += \
|
||||
-I$(CURDIR)/src \
|
||||
-I$(CURDIR)/src/lib \
|
||||
-I$(CURDIR)/src/lib/jpg_decoder \
|
||||
-I$(CURDIR)/src/lvgl_drivers \
|
||||
-O3 \
|
||||
-Wall \
|
||||
-DLV_LVGL_H_INCLUDE_SIMPLE -DLV_CONF_PATH=$(LV_CONF_PATH) \
|
||||
-DWIN32 \
|
||||
-DSDL_DISABLE_JOYSTICK_INIT_DELAY \
|
||||
-DLVGL_USE_CUSTOM_CONTROLLER_MAP \
|
||||
-DDEBUG_CONSOLE \
|
||||
-DBUILD_VERSION=\"$(GIT_COMMIT_HASH)\" \
|
||||
-DQRURL=\"https://github.com/Ryzee119/LithiumX\"
|
||||
#-DDBG_I2C
|
||||
#-DDBG
|
||||
|
||||
# Include lvgl main library
|
||||
include $(LVGL_DIR)/lvgl.mk
|
||||
SRCS += $(CSRCS)
|
||||
CFLAGS += -I$(LVGL_DIR)/
|
||||
|
||||
include $(NXDK_DIR)/Makefile
|
||||
@@ -0,0 +1,113 @@
|
||||
# LithiumX
|
||||
|
||||
A simple dashboard, mainly developed for the Original Xbox console, but it can be compiled for Windows and Linux for rapid development and testing.
|
||||
|
||||
<img src="./images/dash_main.jpg" alt="main" width="50%"/>
|
||||
|
||||
## Features
|
||||
* Customisable search paths and pages.
|
||||
* Supports game synopsis information and boxart using the [XBMC4Gamers artwork format](https://github.com/Rocky5/XBMC4Gamers/blob/master/README.md).
|
||||
* Keeps track of recently launched titles to quickly get back into your games.
|
||||
* Will run at 720p if available, otherwise it will automatically fallback to 480p.
|
||||
|
||||
## Controls
|
||||
* Black/White - Change page
|
||||
* LT/RT - Scroll page
|
||||
* D-PAD - Select title
|
||||
* Back - Show Synopsis Screen
|
||||
* Start - Show main menu
|
||||
|
||||
## Settings
|
||||
* On the first launch, a `dash.xml` will be created alongside the default.xbe with a starting template. Edit this to modify search paths for titles.
|
||||
* The settings file only supports ASCII characters.
|
||||
* The settings file does not support comments or other text.
|
||||
* If the template is invalid, the program will reset it back to the inbuilt default.
|
||||
* A summary of the file is at the end of this readme.
|
||||
|
||||
## Todo
|
||||
- [ ] FTP/SMB or some network interface to access files.
|
||||
- [ ] Rework rendering backend for Xbox. It uses the GPU but is not very optimised and still has lots of SW routines.
|
||||
- [ ] Take advantage of 128MB Xbox RAM mods.
|
||||
- [ ] Some basic audio.
|
||||
- [ ] File browser.
|
||||
- [ ] Lots more testing.
|
||||
|
||||
## Images
|
||||
<img src="./images/dash_main.jpg" alt="main" width="75%"/>
|
||||
<img src="./images/dash_menu.jpg" alt="menu" width="75%"/>
|
||||
<img src="./images/dash_synop.jpg" alt="recent" width="75%"/>
|
||||
<img src="./images/dash_recent.jpg" alt="synopsis" width="75%"/>
|
||||
|
||||
## Build (Original Xbox)
|
||||
Setup and install nxdk, then:
|
||||
```
|
||||
sudo apt-get update -y && sudo apt-get install -y flex bison clang lld llvm
|
||||
git clone --recursive https://github.com/Ryzee119/LithiumX.git
|
||||
cd LithiumX
|
||||
./src/lib/nxdk/bin/activate
|
||||
make -f Makefile.nxdk -j (Add -B if editing lv_conf.h or other header files to ensure its built correctly)
|
||||
```
|
||||
|
||||
## Build (Windows Xbox)
|
||||
Install MYSYS2, then from a mingw64 environment:
|
||||
```
|
||||
pacman -Syu
|
||||
pacman -S mingw-w64-x86_64-make \
|
||||
mingw-w64-x86_64-cmake \
|
||||
mingw-w64-x86_64-gcc \
|
||||
mingw-w64-x86_64-SDL2 \
|
||||
mingw-w64-x86_64-libjpeg-turbo
|
||||
|
||||
git clone --recursive https://github.com/Ryzee119/LithiumX.git
|
||||
cd LithiumX/
|
||||
mkdir build && cd build
|
||||
cmake .. -G "MinGW Makefiles"
|
||||
cmake --build .
|
||||
```
|
||||
|
||||
## Build (Linux)
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt install build-essential libsdl2-dev libturbojpeg-dev
|
||||
git clone --recursive https://github.com/Ryzee119/LithiumX.git
|
||||
cd LithiumX/
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
cmake --build .
|
||||
```
|
||||
|
||||
## XML settings summary
|
||||
```diff
|
||||
<Root>
|
||||
<dash_pages>
|
||||
# Create a page node called "Recent"
|
||||
<Recent>
|
||||
- # Use the keyword "Recent" as a path to populate with recently launched titles. This must only appear once
|
||||
<1>Recent</1>
|
||||
</Recent>
|
||||
- # Create a page node called "My Sweet Games"
|
||||
<My Sweet Games>
|
||||
- # Search in my_path1 and add titles to this page"
|
||||
<1>my_path1</1>
|
||||
- # Search in my_path2 and add titles to this page"
|
||||
<2>my_path2</2>
|
||||
</My Sweet Games>
|
||||
...rest of pages
|
||||
</dash_pages>
|
||||
<dash_settings>
|
||||
- # The temperature info in the mainmenu will be in Fahrenheit, Celcius otherwise
|
||||
<use_fahrenheit>0</use_fahrenheit>
|
||||
- # What page to show on bootup. 0 = the first item under <dash_pages>"
|
||||
<default_screen_index>0</default_screen_index>
|
||||
- # Autolaunch a DVD when it is inserted and successfully read"
|
||||
<auto_launch_dvd>0</auto_launch_dvd>
|
||||
</dash_settings>
|
||||
</Root>
|
||||
```
|
||||
|
||||
## Licence and Attribution
|
||||
This project is shared under the [MIT license](https://github.com/Ryzee119/LithiumX/blob/master/LICENSE), however this project includes code by others. Refer to the list below.
|
||||
* [lvgl](https://github.com/lvgl)/**[lvgl](https://github.com/lvgl/lvgl)** shared under the [MIT License](https://github.com/lvgl/lvgl/blob/master/LICENCE.txt).
|
||||
* [charlesnicholson](https://github.com/charlesnicholson)/**[nanoprintf](https://github.com/charlesnicholson/nanoprintf)** shared under the [MIT License](https://github.com/charlesnicholson/nanoprintf/blob/main/LICENSE).
|
||||
* [ooxi](https://github.com/ooxi/)/**[ooxi](https://github.com/ooxi/xml.c)** shared under the [MIT License](https://github.com/ooxi/xml.c/blob/master/LICENSE).
|
||||
* [XboxDev](https://github.com/XboxDev)/**[nxdk](https://github.com/XboxDev/nxdk)** shared under the [Various Licenses](https://github.com/XboxDev/nxdk/tree/master/LICENSES).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 158 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 132 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 149 KiB |
+819
File diff suppressed because it is too large
Load Diff
+105
@@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2022 Ryzee119
|
||||
|
||||
#ifndef _DASH_H
|
||||
#define _DASH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define DASH_PATH_SEPARATOR '\\'
|
||||
#else
|
||||
#define DASH_PATH_SEPARATOR '/'
|
||||
#endif
|
||||
|
||||
#ifndef DASH_XMARGIN
|
||||
#define DASH_XMARGIN 0
|
||||
#endif
|
||||
#ifndef DASH_YMARGIN
|
||||
#define DASH_YMARGIN 20
|
||||
#endif
|
||||
|
||||
|
||||
//All lvgl directories are prefixed with A:
|
||||
//nxdk local directory is also mounting to A: so we get A:A:..
|
||||
#ifndef DASH_XML
|
||||
#ifdef NXDK
|
||||
#define DASH_XML "A:A:\\dash.xml"
|
||||
#else
|
||||
#define DASH_XML "A:dash.xml"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef RECENT_TITLES
|
||||
#ifdef NXDK
|
||||
#define RECENT_TITLES "A:E:\\UDATA\\LithiumX\\recent.dat"
|
||||
#else
|
||||
#define RECENT_TITLES "A:recent.dat"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef RECENT_TITLES_MAX
|
||||
#define RECENT_TITLES_MAX 16
|
||||
#endif
|
||||
|
||||
#ifndef DASH_LAUNCH_EXE
|
||||
#define DASH_LAUNCH_EXE "default.xbe"
|
||||
#endif
|
||||
|
||||
#ifndef DASH_MAX_PAGES
|
||||
#define DASH_MAX_PAGES 8
|
||||
#endif
|
||||
|
||||
#ifndef DASH_MAX_PATHS_PER_PAGE
|
||||
#define DASH_MAX_PATHS_PER_PAGE 16
|
||||
#endif
|
||||
|
||||
#ifndef DASH_MAX_GAMES
|
||||
#define DASH_MAX_GAMES 1024 //Per page
|
||||
#endif
|
||||
|
||||
#ifndef DASH_MAX_PATHLEN
|
||||
#define DASH_MAX_PATHLEN 256 //Per page
|
||||
#endif
|
||||
|
||||
#ifndef DASH_THUMBNAIL_WIDTH
|
||||
#define DASH_THUMBNAIL_WIDTH 200
|
||||
#endif
|
||||
|
||||
#ifndef DASH_THUMBNAIL_HEIGHT
|
||||
#define DASH_THUMBNAIL_HEIGHT 283
|
||||
#endif
|
||||
|
||||
#ifndef DASH_DEFAULT_THUMBNAIL
|
||||
#define DASH_DEFAULT_THUMBNAIL "default_tbn.jpg" //Root directory if not found in game directory
|
||||
#endif
|
||||
|
||||
#ifndef DASH_GAME_THUMBNAIL
|
||||
#define DASH_GAME_THUMBNAIL "default.tbn"
|
||||
#endif
|
||||
|
||||
#define DASH_NEXT_PAGE '>'
|
||||
#define DASH_PREV_PAGE '<'
|
||||
#define DASH_SETTINGS_PAGE 'S'
|
||||
#define DASH_INFO_PAGE 'I'
|
||||
|
||||
extern unsigned int settings_use_fahrenheit;
|
||||
extern unsigned int settings_default_screen_index;
|
||||
extern unsigned int settings_auto_launch_dvd;
|
||||
|
||||
void dash_init(void);
|
||||
void dash_deinit(void);
|
||||
void dash_set_launch_folder(const char *path);
|
||||
void dash_clear_recent_list(void);
|
||||
const char *dash_get_launch_folder();
|
||||
|
||||
void lvgl_getlock();
|
||||
void lvgl_removelock();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+356
@@ -0,0 +1,356 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2022 Ryzee119
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "lv_port_indev.h"
|
||||
#include "dash.h"
|
||||
#include "dash_styles.h"
|
||||
#include "dash_menu.h"
|
||||
#include "platform/platform.h"
|
||||
#include "helpers/nano_debug.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAINMENU_WIDTH 600
|
||||
#define MAINMENU_HEIGHT 440
|
||||
|
||||
static lv_obj_t *main_menu;
|
||||
static lv_obj_t *rt_info;
|
||||
static lv_timer_t *rt_info_timer;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
lv_obj_t *old_focus_parent;
|
||||
lv_obj_t *old_focus;
|
||||
confirm_cb_t cb;
|
||||
} menu_data_t;
|
||||
|
||||
enum
|
||||
{
|
||||
MENU_SYSTEM_INFO,
|
||||
MENU_CLEAR_RECENT,
|
||||
#ifdef NXDK
|
||||
MENU_LAUNCH_MS_DASH,
|
||||
MENU_LAUNCH_DVD,
|
||||
MENU_FLUSH_CACHE_PARTITION,
|
||||
#endif
|
||||
MENU_ABOUT,
|
||||
MENU_REBOOT,
|
||||
MENU_SHUTDOWN,
|
||||
MENU_MAX,
|
||||
};
|
||||
|
||||
static const char *menu_items[] =
|
||||
{
|
||||
"System Information",
|
||||
"Clear Recent Titles",
|
||||
#ifdef NXDK
|
||||
"Launch MS Dashboard",
|
||||
"Launch DVD",
|
||||
"Flush Cache Partitions",
|
||||
#endif
|
||||
"About",
|
||||
"Reboot",
|
||||
"Shutdown",
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SUBMENU_CANCEL,
|
||||
SUBMENU_ACCEPT,
|
||||
};
|
||||
|
||||
// The follow callbacks are called after the confirmation box has been accepted
|
||||
static void dash_shutdown()
|
||||
{
|
||||
lv_set_quit(LV_SHUTDOWN);
|
||||
}
|
||||
|
||||
static void dash_reboot()
|
||||
{
|
||||
lv_set_quit(LV_REBOOT);
|
||||
}
|
||||
|
||||
static void dash_clear_recent()
|
||||
{
|
||||
dash_clear_recent_list();
|
||||
}
|
||||
|
||||
#ifdef NXDK
|
||||
static void dash_launch_msdash()
|
||||
{
|
||||
dash_set_launch_folder("MSDASH");
|
||||
lv_set_quit(LV_QUIT_OTHER);
|
||||
}
|
||||
|
||||
static void dash_launch_dvd()
|
||||
{
|
||||
platform_launch_dvd();
|
||||
}
|
||||
|
||||
static void dash_flush_cache()
|
||||
{
|
||||
platform_flush_cache_cb();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void realtime_info_cb(lv_timer_t *event)
|
||||
{
|
||||
lv_obj_t *rt_info = event->user_data;
|
||||
if (lv_obj_is_valid(rt_info) == false)
|
||||
{
|
||||
lv_timer_del(event);
|
||||
return;
|
||||
}
|
||||
const char *rt_info_str = platform_realtime_info_cb();
|
||||
lv_label_set_text(rt_info, rt_info_str);
|
||||
}
|
||||
|
||||
// Menu or submenu close callback on ESC or DASH_SETTINGS_PAGE (B or BACK) to close
|
||||
static void close_callback(lv_event_t *e)
|
||||
{
|
||||
lv_obj_t *obj = lv_event_get_target(e);
|
||||
lv_group_t *gp = lv_group_get_default();
|
||||
lv_key_t key = lv_indev_get_key(lv_indev_get_act());
|
||||
menu_data_t *menu_data = (menu_data_t *)obj->user_data;
|
||||
if (key == LV_KEY_ESC || key == DASH_SETTINGS_PAGE)
|
||||
{
|
||||
if (obj == main_menu)
|
||||
{
|
||||
lv_obj_del(rt_info);
|
||||
lv_timer_del(rt_info_timer);
|
||||
}
|
||||
lv_group_focus_freeze(gp, false);
|
||||
// If the object was on the recent items page, it may have been cleared. Ensure its valid still
|
||||
// before refocusing on it
|
||||
if (lv_obj_is_valid(menu_data->old_focus))
|
||||
{
|
||||
lv_group_focus_obj(menu_data->old_focus);
|
||||
}
|
||||
else if (lv_obj_is_valid(menu_data->old_focus_parent))
|
||||
{
|
||||
lv_group_focus_obj(menu_data->old_focus_parent);
|
||||
}
|
||||
lv_mem_free(menu_data);
|
||||
lv_obj_del(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// Callback when submenu/confirmbox has an item selected (Either cancel or accept action)
|
||||
static void confirmbox_callback(lv_event_t *e)
|
||||
{
|
||||
uint16_t row, col;
|
||||
lv_obj_t *obj = lv_event_get_target(e);
|
||||
lv_group_t *gp = lv_group_get_default();
|
||||
menu_data_t *menu_data = (menu_data_t *)obj->user_data;
|
||||
lv_table_get_selected_cell(obj, &row, &col);
|
||||
if (row == SUBMENU_ACCEPT)
|
||||
{
|
||||
if (menu_data->cb)
|
||||
{
|
||||
menu_data->cb();
|
||||
}
|
||||
}
|
||||
// Reselect the item that was previously selected before the menu was opened
|
||||
lv_group_focus_freeze(gp, false);
|
||||
lv_group_focus_obj(menu_data->old_focus);
|
||||
lv_mem_free(menu_data);
|
||||
lv_obj_del(obj);
|
||||
}
|
||||
|
||||
// Helper function to apply generic style and focus rules to menu and submenu containers
|
||||
static void menu_set_style(lv_obj_t *obj)
|
||||
{
|
||||
lv_group_t *gp = lv_group_get_default();
|
||||
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_obj_add_style(obj, &menu_table_style, LV_PART_MAIN);
|
||||
lv_group_add_obj(gp, obj);
|
||||
lv_group_focus_freeze(gp, false);
|
||||
lv_group_focus_obj(obj);
|
||||
lv_group_focus_freeze(gp, true);
|
||||
}
|
||||
|
||||
// Create a generic submenu container
|
||||
static lv_obj_t *menu_create_submenu_box()
|
||||
{
|
||||
lv_obj_t *obj = lv_obj_create(lv_scr_act());
|
||||
lv_group_t *gp = lv_group_get_default();
|
||||
lv_obj_add_style(obj, &menu_table_style, LV_PART_MAIN);
|
||||
lv_obj_add_style(obj, &menu_table_cell_style, LV_PART_ITEMS);
|
||||
lv_obj_set_width(obj, MAINMENU_WIDTH);
|
||||
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_add_event_cb(obj, confirmbox_callback, LV_EVENT_PRESSED, NULL);
|
||||
|
||||
menu_data_t *user_data = lv_mem_alloc(sizeof(menu_data_t));
|
||||
user_data->old_focus = lv_group_get_focused(gp);
|
||||
user_data->old_focus_parent = lv_obj_get_parent(user_data->old_focus);
|
||||
user_data->cb = NULL;
|
||||
obj->user_data = user_data;
|
||||
|
||||
menu_set_style(obj);
|
||||
lv_obj_add_event_cb(obj, close_callback, LV_EVENT_KEY, NULL);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Callback if a button is actived on the main menu
|
||||
static void mainmenu_callback(lv_event_t *e)
|
||||
{
|
||||
char temp[DASH_MAX_PATHLEN];
|
||||
uint16_t row, col;
|
||||
lv_obj_t *submenu;
|
||||
lv_obj_t *obj = lv_event_get_target(e);
|
||||
lv_table_get_selected_cell(obj, &row, &col);
|
||||
|
||||
if (row == LV_TABLE_CELL_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (row == MENU_SYSTEM_INFO)
|
||||
{
|
||||
submenu = menu_create_submenu_box();
|
||||
lv_obj_set_layout(submenu, LV_LAYOUT_FLEX);
|
||||
lv_obj_set_flex_flow(submenu, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_height(submenu, MAINMENU_HEIGHT);
|
||||
lv_obj_t *label;
|
||||
label = lv_label_create(submenu);
|
||||
lv_label_set_text(label, menu_items[row]);
|
||||
platform_show_info_cb(submenu);
|
||||
}
|
||||
else if (row == MENU_CLEAR_RECENT)
|
||||
{
|
||||
lv_snprintf(temp, sizeof(temp), "%s \"%s\"", "Confirm", menu_items[row]);
|
||||
submenu = menu_create_confirm_box(temp, dash_clear_recent);
|
||||
}
|
||||
#ifdef NXDK
|
||||
else if (row == MENU_LAUNCH_MS_DASH)
|
||||
{
|
||||
lv_snprintf(temp, sizeof(temp), "%s \"%s\"", "Confirm", menu_items[row]);
|
||||
submenu = menu_create_confirm_box(temp, dash_launch_msdash);
|
||||
}
|
||||
else if (row == MENU_LAUNCH_DVD)
|
||||
{
|
||||
lv_snprintf(temp, sizeof(temp), "%s \"%s\"", "Confirm", menu_items[row]);
|
||||
submenu = menu_create_confirm_box(temp, dash_launch_dvd);
|
||||
}
|
||||
else if (row == MENU_FLUSH_CACHE_PARTITION)
|
||||
{
|
||||
lv_snprintf(temp, sizeof(temp), "%s \"%s\"", "Confirm", menu_items[row]);
|
||||
submenu = menu_create_confirm_box(temp, dash_flush_cache);
|
||||
}
|
||||
#endif
|
||||
// About
|
||||
else if (row == MENU_ABOUT)
|
||||
{
|
||||
const char *url = QRURL;
|
||||
submenu = menu_create_submenu_box();
|
||||
lv_obj_align(submenu, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_obj_set_width(submenu, lv_obj_get_width(lv_scr_act()));
|
||||
lv_obj_set_height(submenu, lv_obj_get_height(lv_scr_act()));
|
||||
|
||||
lv_obj_t *qrcode = lv_qrcode_create(submenu, LV_MIN((MAINMENU_WIDTH * 3) / 4, (MAINMENU_HEIGHT * 3) / 4),
|
||||
lv_color_make(0x00, 0x00, 0x00),
|
||||
lv_color_make(0xFF, 0xFF, 0xFF));
|
||||
lv_obj_align(qrcode, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_qrcode_update(qrcode, url, strlen(url));
|
||||
lv_obj_update_layout(qrcode);
|
||||
lv_obj_t *label = lv_label_create(submenu);
|
||||
lv_label_set_text_fmt(label, "Visit the Github Repo for info\n%s", url);
|
||||
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
|
||||
lv_obj_update_layout(label);
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, -(lv_obj_get_height(qrcode) + lv_obj_get_height(label)) / 2 - 10);
|
||||
}
|
||||
else if (row == MENU_REBOOT)
|
||||
{
|
||||
lv_snprintf(temp, sizeof(temp), "%s \"%s\"", "Confirm", menu_items[row]);
|
||||
submenu = menu_create_confirm_box(temp, dash_reboot);
|
||||
}
|
||||
else if (row == MENU_SHUTDOWN)
|
||||
{
|
||||
lv_snprintf(temp, sizeof(temp), "%s \"%s\"", "Confirm", menu_items[row]);
|
||||
submenu = menu_create_confirm_box(temp, dash_shutdown);
|
||||
}
|
||||
}
|
||||
|
||||
void main_menu_init()
|
||||
{
|
||||
}
|
||||
|
||||
void main_menu_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
// Create and open the main menu
|
||||
void main_menu_open()
|
||||
{
|
||||
nano_debug(LEVEL_TRACE, "TRACE: Opening main menu\n");
|
||||
lv_group_t *gp;
|
||||
|
||||
gp = lv_group_get_default();
|
||||
main_menu = lv_table_create(lv_scr_act());
|
||||
menu_data_t *user_data = lv_mem_alloc(sizeof(menu_data_t));
|
||||
user_data->old_focus = lv_group_get_focused(gp);
|
||||
user_data->old_focus_parent = lv_obj_get_parent(user_data->old_focus);
|
||||
user_data->cb = NULL;
|
||||
main_menu->user_data = user_data;
|
||||
lv_obj_add_style(main_menu, &menu_table_style, LV_PART_MAIN);
|
||||
lv_obj_add_style(main_menu, &menu_table_style, LV_PART_MAIN | LV_STATE_FOCUS_KEY);
|
||||
lv_obj_add_style(main_menu, &menu_table_cell_style, LV_PART_ITEMS);
|
||||
lv_obj_add_style(main_menu, &menu_table_highlight_style, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
|
||||
lv_obj_set_scrollbar_mode(main_menu, LV_SCROLLBAR_MODE_OFF);
|
||||
lv_obj_set_width(main_menu, MAINMENU_WIDTH);
|
||||
lv_obj_align(main_menu, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_obj_update_layout(main_menu);
|
||||
|
||||
int row_cnt = sizeof(menu_items) / sizeof(menu_items[0]);
|
||||
lv_table_set_col_cnt(main_menu, 1);
|
||||
lv_table_set_row_cnt(main_menu, row_cnt);
|
||||
lv_table_set_col_width(main_menu, 0, lv_obj_get_width(main_menu));
|
||||
for (int i = 0; i < row_cnt; i++)
|
||||
{
|
||||
lv_table_set_cell_value(main_menu, i, 0, menu_items[i]);
|
||||
}
|
||||
lv_obj_update_layout(main_menu);
|
||||
|
||||
lv_obj_add_event_cb(main_menu, mainmenu_callback, LV_EVENT_PRESSED, NULL);
|
||||
lv_obj_add_event_cb(main_menu, close_callback, LV_EVENT_KEY, NULL);
|
||||
|
||||
lv_group_focus_obj(main_menu);
|
||||
lv_group_focus_freeze(gp, true);
|
||||
|
||||
rt_info = lv_label_create(lv_scr_act());
|
||||
lv_obj_add_style(rt_info, &rtinfo_style, LV_PART_MAIN);
|
||||
lv_obj_set_width(rt_info, MAINMENU_WIDTH);
|
||||
lv_obj_align(rt_info, LV_ALIGN_CENTER, 0,
|
||||
(-lv_obj_get_height(main_menu) - lv_font_get_line_height(&lv_font_montserrat_22)) / 2);
|
||||
rt_info_timer = lv_timer_create(realtime_info_cb, 2000, rt_info);
|
||||
lv_timer_ready(rt_info_timer);
|
||||
}
|
||||
|
||||
// Create a generic confirmation box. Options will be "Cancel" or Confirm "msg".
|
||||
// If confirmed the confirm_cb will be called
|
||||
lv_obj_t *menu_create_confirm_box(const char *msg, confirm_cb_t confirm_cb)
|
||||
{
|
||||
lv_obj_t *obj = lv_table_create(lv_scr_act());
|
||||
lv_table_set_cell_value(obj, 0, 0, "Cancel");
|
||||
lv_table_set_cell_value(obj, 1, 0, msg);
|
||||
lv_table_set_col_width(obj, 0, MAINMENU_WIDTH);
|
||||
|
||||
lv_group_t *gp = lv_group_get_default();
|
||||
lv_obj_add_style(obj, &menu_table_style, LV_PART_MAIN);
|
||||
lv_obj_add_style(obj, &menu_table_cell_style, LV_PART_ITEMS);
|
||||
lv_obj_set_width(obj, MAINMENU_WIDTH);
|
||||
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_add_event_cb(obj, confirmbox_callback, LV_EVENT_PRESSED, NULL);
|
||||
|
||||
// We need to manually control the focus here so lvgl doesnt just to random places
|
||||
menu_data_t *user_data = lv_mem_alloc(sizeof(menu_data_t));
|
||||
user_data->old_focus = lv_group_get_focused(gp);
|
||||
user_data->old_focus_parent = lv_obj_get_parent(user_data->old_focus);
|
||||
user_data->cb = confirm_cb;
|
||||
obj->user_data = user_data;
|
||||
|
||||
menu_set_style(obj);
|
||||
lv_obj_add_event_cb(obj, close_callback, LV_EVENT_KEY, NULL);
|
||||
return obj;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2022 Ryzee119
|
||||
|
||||
#ifndef _MENU_H
|
||||
#define _MENU_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*confirm_cb_t)(void);
|
||||
|
||||
void main_menu_init(void);
|
||||
void main_menu_deinit(void);
|
||||
void main_menu_open(void);
|
||||
lv_obj_t *menu_create_confirm_box(const char *msg, confirm_cb_t confirm_cb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 7.0 KiB |
@@ -0,0 +1,128 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2022 Ryzee119
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "dash.h"
|
||||
#include "dash_styles.h"
|
||||
|
||||
lv_style_t dash_background_style;
|
||||
lv_style_t menu_table_style;
|
||||
lv_style_t menu_table_highlight_style;
|
||||
lv_style_t menu_table_cell_style;
|
||||
lv_style_t rtinfo_style;
|
||||
lv_style_t synop_container_style;
|
||||
lv_style_t synop_text_style;
|
||||
lv_style_t titleview_style;
|
||||
lv_style_t titleview_image_container_style;
|
||||
lv_style_t titleview_image_text_style;
|
||||
lv_style_t titleview_header_footer_style;
|
||||
|
||||
void dash_styles_init()
|
||||
{
|
||||
// Set the style for the background
|
||||
lv_style_init(&dash_background_style);
|
||||
lv_style_set_border_width(&dash_background_style, 0);
|
||||
lv_style_set_radius(&dash_background_style, 0);
|
||||
lv_style_set_bg_color(&dash_background_style, lv_color_make(48, 48, 48));
|
||||
lv_style_set_bg_grad_color(&dash_background_style, lv_color_make(22, 111, 15));
|
||||
lv_style_set_bg_grad_dir(&dash_background_style, LV_GRAD_DIR_VER);
|
||||
|
||||
// Set the style for the main menu container
|
||||
lv_style_init(&menu_table_style);
|
||||
lv_style_set_bg_color(&menu_table_style, lv_color_make(48, 48, 48));
|
||||
lv_style_set_border_width(&menu_table_style, 1);
|
||||
lv_style_set_border_color(&menu_table_style, lv_color_make(255, 255, 255));
|
||||
lv_style_set_pad_all(&menu_table_style, 0);
|
||||
lv_style_set_radius(&menu_table_style, 0);
|
||||
lv_style_set_text_color(&menu_table_style, lv_color_white());
|
||||
lv_style_set_text_font(&menu_table_style, &lv_font_montserrat_26);
|
||||
lv_style_set_outline_width(&menu_table_style, 0);
|
||||
|
||||
// Set the style for the main menu item cells.
|
||||
lv_style_init(&menu_table_cell_style);
|
||||
lv_style_set_border_width(&menu_table_cell_style, 1);
|
||||
lv_style_set_border_color(&menu_table_cell_style, lv_color_make(255, 255, 255));
|
||||
lv_style_set_bg_opa(&menu_table_cell_style, 0);
|
||||
lv_style_set_text_color(&menu_table_cell_style, lv_color_white());
|
||||
lv_style_set_text_font(&menu_table_cell_style, &lv_font_montserrat_26);
|
||||
lv_style_set_pad_top(&menu_table_cell_style, 10);
|
||||
lv_style_set_pad_bottom(&menu_table_cell_style, 10);
|
||||
lv_style_set_radius(&menu_table_cell_style, 0);
|
||||
|
||||
// Set the style for the main menu item cells when they are selected
|
||||
lv_style_init(&menu_table_highlight_style);
|
||||
lv_style_set_bg_color(&menu_table_highlight_style, lv_color_make(0x0A, 0x0A, 0x0A));
|
||||
|
||||
// This is the small text area about the main menu that shows temperature/tray state or other
|
||||
// real-time info.
|
||||
lv_style_init(&rtinfo_style);
|
||||
lv_style_set_text_font(&rtinfo_style, &lv_font_montserrat_22);
|
||||
lv_style_set_bg_color(&rtinfo_style, lv_color_make(48, 48, 48));
|
||||
lv_style_set_bg_opa(&rtinfo_style, 255);
|
||||
lv_style_set_text_align(&rtinfo_style, LV_TEXT_ALIGN_CENTER);
|
||||
lv_style_set_border_width(&rtinfo_style, 1);
|
||||
lv_style_set_border_color(&rtinfo_style, lv_color_make(255, 255, 255));
|
||||
lv_style_set_pad_all(&rtinfo_style, 0);
|
||||
lv_style_set_radius(&rtinfo_style, 0);
|
||||
lv_style_set_text_color(&rtinfo_style, lv_color_white());
|
||||
lv_style_set_outline_width(&rtinfo_style, 0);
|
||||
|
||||
// Set the style for the synopsis screen container
|
||||
lv_style_init(&synop_container_style);
|
||||
lv_style_set_bg_color(&synop_container_style, lv_color_make(48, 48, 48));
|
||||
lv_style_set_pad_left(&synop_container_style, 10);
|
||||
lv_style_set_border_width(&synop_container_style, 1);
|
||||
lv_style_set_radius(&synop_container_style, 0);
|
||||
|
||||
// Set the style for the synopsis screen text.
|
||||
// To change the colour of the synop title text, edit DASH_MENU_COLOR in dash_styles.h
|
||||
lv_style_init(&synop_text_style);
|
||||
lv_style_set_text_color(&synop_text_style, lv_color_white());
|
||||
lv_style_set_text_font(&synop_text_style, &lv_font_montserrat_22);
|
||||
|
||||
// Create a style for the container that holds all the images
|
||||
// Set padding between images, background colour behind the images etc
|
||||
lv_style_init(&titleview_style);
|
||||
lv_style_set_radius(&titleview_style, 0);
|
||||
lv_style_set_border_width(&titleview_style, 0);
|
||||
lv_style_set_bg_color(&titleview_style, lv_color_make(34, 34, 34));
|
||||
lv_style_set_pad_all(&titleview_style, 0);
|
||||
lv_style_set_pad_row(&titleview_style, 0);
|
||||
lv_style_set_pad_column(&titleview_style, 0);
|
||||
|
||||
// Create a style for the image containers that contain an image. We basically want not borders or padding
|
||||
// as we want this contaier to be invisible
|
||||
lv_style_init(&titleview_image_container_style);
|
||||
lv_style_set_radius(&titleview_image_container_style, 0);
|
||||
lv_style_set_border_width(&titleview_image_container_style, 0);
|
||||
lv_style_set_pad_all(&titleview_image_container_style, 0);
|
||||
lv_style_set_border_width(&titleview_image_container_style, 0);
|
||||
|
||||
// Create a style for the text that appears on the thumbnail art when no artwork is found
|
||||
lv_style_init(&titleview_image_text_style);
|
||||
lv_style_set_align(&titleview_image_text_style, LV_ALIGN_CENTER);
|
||||
lv_style_set_text_font(&titleview_image_text_style, &lv_font_montserrat_24);
|
||||
lv_style_set_text_color(&titleview_image_text_style, lv_color_white());
|
||||
lv_style_set_text_align(&titleview_image_text_style, LV_TEXT_ALIGN_CENTER);
|
||||
|
||||
// Create a style for the page header and footer text
|
||||
lv_style_init(&titleview_header_footer_style);
|
||||
lv_style_set_bg_color(&titleview_header_footer_style, lv_color_make(0, 72, 16));
|
||||
lv_style_set_text_color(&titleview_header_footer_style, lv_color_white());
|
||||
lv_style_set_text_font(&titleview_header_footer_style, &lv_font_montserrat_26);
|
||||
}
|
||||
|
||||
void dash_styles_deinit()
|
||||
{
|
||||
lv_style_reset(&dash_background_style);
|
||||
lv_style_reset(&menu_table_style);
|
||||
lv_style_reset(&menu_table_highlight_style);
|
||||
lv_style_reset(&menu_table_cell_style);
|
||||
lv_style_reset(&rtinfo_style);
|
||||
lv_style_reset(&synop_container_style);
|
||||
lv_style_reset(&synop_text_style);
|
||||
lv_style_reset(&titleview_style);
|
||||
lv_style_reset(&titleview_image_container_style);
|
||||
lv_style_reset(&titleview_image_text_style);
|
||||
lv_style_reset(&titleview_header_footer_style);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2022 Ryzee119
|
||||
|
||||
#ifndef _STYLES_H
|
||||
#define _STYLES_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DASH_MENU_COLOR "#999999"
|
||||
|
||||
extern lv_style_t dash_background_style;
|
||||
extern lv_style_t menu_table_style;
|
||||
extern lv_style_t menu_table_highlight_style;
|
||||
extern lv_style_t menu_table_cell_style;
|
||||
extern lv_style_t rtinfo_style;
|
||||
extern lv_style_t synop_container_style;
|
||||
extern lv_style_t synop_text_style;
|
||||
extern lv_style_t titleview_style;
|
||||
extern lv_style_t titleview_image_container_style;
|
||||
extern lv_style_t titleview_image_text_style;
|
||||
extern lv_style_t titleview_header_footer_style;
|
||||
|
||||
void dash_styles_init(void);
|
||||
void dash_styles_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,242 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2022 Ryzee119
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "dash.h"
|
||||
#include "dash_styles.h"
|
||||
#include "dash_titlelist.h"
|
||||
#include "xml/src/xml.h"
|
||||
#include "helpers/fileio.h"
|
||||
#include "helpers/nano_debug.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define SYNOP_WIDTH 600
|
||||
#define SYNOP_HEIGHT 440
|
||||
#define SYNOP_CACHE_SIZE 6
|
||||
|
||||
typedef struct
|
||||
{
|
||||
title_t *title;
|
||||
lv_obj_t *synop_menu;
|
||||
} synop_cache_t;
|
||||
|
||||
static char temp[4096];
|
||||
synop_cache_t cache[SYNOP_CACHE_SIZE];
|
||||
static int cache_head = 0;
|
||||
|
||||
static void close_callback(lv_event_t *event)
|
||||
{
|
||||
lv_obj_t *synop_menu = (lv_obj_t *)event->user_data;
|
||||
lv_key_t key = lv_indev_get_key(lv_indev_get_act());
|
||||
if (key == LV_KEY_ESC || key == LV_KEY_ENTER || key == DASH_INFO_PAGE)
|
||||
{
|
||||
lv_group_t *gp = lv_group_get_default();
|
||||
lv_group_focus_freeze(gp, false);
|
||||
lv_group_focus_obj(synop_menu->user_data);
|
||||
// We only hide it and keep is cached for now
|
||||
lv_obj_add_flag(synop_menu, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to apply a consisten stype to the text
|
||||
static void apply_label_style(lv_obj_t *label)
|
||||
{
|
||||
lv_label_set_recolor(label, true);
|
||||
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
|
||||
lv_style_value_t value;
|
||||
lv_style_get_prop(&synop_container_style, LV_STYLE_PAD_LEFT, &value);
|
||||
lv_obj_set_width(label, lv_obj_get_width(lv_obj_get_parent(label)) - 2 * value.num);
|
||||
lv_obj_add_style(label, &synop_text_style, 0);
|
||||
lv_obj_update_layout(label);
|
||||
}
|
||||
|
||||
// Helper function to extract and xml string, and create a new label with the formatted text
|
||||
static lv_obj_t *new_label(lv_obj_t *parent, struct xml_string *t, const char *prefix)
|
||||
{
|
||||
lv_obj_t *label;
|
||||
size_t slen;
|
||||
|
||||
label = lv_label_create(parent);
|
||||
slen = LV_MIN(xml_string_length(t), sizeof(temp));
|
||||
xml_string_copy(t, (uint8_t *)temp, slen);
|
||||
temp[slen] = '\0';
|
||||
lv_label_set_text_fmt(label, "%s %s# %s", DASH_MENU_COLOR, prefix, temp);
|
||||
apply_label_style(label);
|
||||
lv_obj_add_flag(label, LV_OBJ_FLAG_EVENT_BUBBLE);
|
||||
return label;
|
||||
}
|
||||
|
||||
// Basic containers dont scroll. We registered a custom scroll callback. This is handled here.
|
||||
static void synop_scroll(lv_event_t *e)
|
||||
{
|
||||
// objects normally scroll automatically however core object containers dont have any animations.
|
||||
// I replace the scrolling with my own here with animation enabled.
|
||||
lv_obj_t *obj = lv_event_get_target(e);
|
||||
lv_key_t key = lv_indev_get_key(lv_indev_get_act());
|
||||
if (key == LV_KEY_UP)
|
||||
{
|
||||
lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, LV_ANIM_ON);
|
||||
}
|
||||
if (key == LV_KEY_DOWN)
|
||||
{
|
||||
lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, LV_ANIM_ON);
|
||||
}
|
||||
}
|
||||
|
||||
void synop_menu_init()
|
||||
{
|
||||
lv_memset(cache, 0x00, sizeof(cache));
|
||||
}
|
||||
|
||||
void synop_menu_deinit()
|
||||
{
|
||||
for (int i = 0; i < SYNOP_CACHE_SIZE; i++)
|
||||
{
|
||||
if (cache[i].title != NULL)
|
||||
{
|
||||
lv_obj_del(cache[i].synop_menu);
|
||||
}
|
||||
}
|
||||
lv_memset(cache, 0x00, sizeof(cache));
|
||||
}
|
||||
|
||||
// Opens the synopsis menu for the given title
|
||||
void synop_menu_open(title_t *title)
|
||||
{
|
||||
char xml_path[DASH_MAX_PATHLEN];
|
||||
bool has_synop;
|
||||
lv_obj_t *label;
|
||||
lv_obj_t *synop_menu;
|
||||
struct xml_document *xml_handle;
|
||||
struct xml_string *t;
|
||||
uint32_t br;
|
||||
|
||||
nano_debug(LEVEL_TRACE, "TRACE: Opening synopsis menu for %s\n", title->title);
|
||||
|
||||
lv_group_t *gp = lv_group_get_default();
|
||||
|
||||
// Instead of reading the xml file and creating the synopsis page repeatly
|
||||
// the last few calls are cached so if you reopen the synopsis screen is just shows the previously
|
||||
// cached one
|
||||
for (int i = 0; i < SYNOP_CACHE_SIZE; i++)
|
||||
{
|
||||
if (cache[i].title == title)
|
||||
{
|
||||
synop_menu = cache[i].synop_menu;
|
||||
lv_obj_clear_flag(synop_menu, LV_OBJ_FLAG_HIDDEN);
|
||||
nano_debug(LEVEL_TRACE, "TRACE: Found synopsis menu in cache slot %d\n", i);
|
||||
goto cache_leave;
|
||||
}
|
||||
}
|
||||
|
||||
xml_handle = NULL;
|
||||
has_synop = false;
|
||||
|
||||
// Create a basic container, make it vertically scrollable, fixed size, center aligned
|
||||
// and apply flex layout so we can add text easily.
|
||||
synop_menu = lv_obj_create(lv_scr_act());
|
||||
lv_obj_add_style(synop_menu, &synop_container_style, LV_PART_MAIN);
|
||||
lv_obj_set_scrollbar_mode(synop_menu, LV_SCROLLBAR_MODE_OFF);
|
||||
lv_obj_clear_flag(synop_menu, LV_OBJ_FLAG_SCROLLABLE); // Use my own scroll callback
|
||||
lv_obj_add_event_cb(synop_menu, synop_scroll, LV_EVENT_KEY, NULL); // Setup scroll callback
|
||||
lv_obj_set_scroll_dir(synop_menu, LV_DIR_TOP); // Only scroll up and down
|
||||
lv_obj_set_size(synop_menu, SYNOP_WIDTH, SYNOP_HEIGHT);
|
||||
lv_obj_align(synop_menu, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_obj_set_layout(synop_menu, LV_LAYOUT_FLEX);
|
||||
lv_obj_set_flex_flow(synop_menu, LV_FLEX_FLOW_COLUMN); // When each label is added, it is added below the previous
|
||||
lv_obj_update_layout(synop_menu);
|
||||
|
||||
label = lv_label_create(synop_menu);
|
||||
lv_label_set_text_fmt(label, "%s Title:# %s", DASH_MENU_COLOR, title->title);
|
||||
apply_label_style(label);
|
||||
|
||||
if (title->has_xml == false)
|
||||
{
|
||||
nano_debug(LEVEL_WARN, "WARN: %s does not have a synopsis menu.\n", title->title);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
lv_snprintf(xml_path, sizeof(xml_path), "%s%c_resources%cdefault.xml", title->title_folder, DASH_PATH_SEPARATOR, DASH_PATH_SEPARATOR);
|
||||
|
||||
uint8_t *xml_raw = lv_fs_orc(xml_path, &br);
|
||||
if (xml_raw == NULL)
|
||||
{
|
||||
// We shouldnt get here if the file doesnt exist or is invalid so we log an error
|
||||
nano_debug(LEVEL_ERROR, "ERROR: Could not open %s\n", xml_path);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
xml_handle = xml_parse_document(xml_raw, strlen((char *)xml_raw));
|
||||
if (xml_handle == NULL)
|
||||
{
|
||||
lv_mem_free(xml_raw);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
nano_debug(LEVEL_TRACE, "TRACE: Synopsis for %s seems ok. Parsing it\n", title->title);
|
||||
has_synop = true;
|
||||
|
||||
t = title_get_synopsis(xml_handle, "developer");
|
||||
if (t != NULL)
|
||||
{
|
||||
label = new_label(synop_menu, t, "Developer: ");
|
||||
}
|
||||
|
||||
t = title_get_synopsis(xml_handle, "publisher");
|
||||
if (t != NULL)
|
||||
{
|
||||
label = new_label(synop_menu, t, "Publisher: ");
|
||||
}
|
||||
|
||||
t = title_get_synopsis(xml_handle, "release_date");
|
||||
if (t != NULL)
|
||||
{
|
||||
label = new_label(synop_menu, t, "Release Date: ");
|
||||
}
|
||||
|
||||
t = title_get_synopsis(xml_handle, "rating");
|
||||
if (t != NULL)
|
||||
{
|
||||
label = new_label(synop_menu, t, "Rating: ");
|
||||
lv_label_ins_text(label, LV_LABEL_POS_LAST, "/10");
|
||||
}
|
||||
|
||||
t = title_get_synopsis(xml_handle, "overview");
|
||||
if (t != NULL)
|
||||
{
|
||||
label = new_label(synop_menu, t, "Overview:");
|
||||
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_update_layout(label);
|
||||
}
|
||||
|
||||
xml_document_free(xml_handle, false);
|
||||
lv_mem_free(xml_raw);
|
||||
leave: ;
|
||||
synop_cache_t *c = &cache[cache_head];
|
||||
nano_debug(LEVEL_TRACE, "TRACE: Cached synopsis menu in cache slot %d\n", cache_head);
|
||||
if (c->title != NULL)
|
||||
{
|
||||
nano_debug(LEVEL_TRACE, "TRACE: Cleared old slot %d\n", cache_head);
|
||||
lv_obj_del(c->synop_menu);
|
||||
}
|
||||
cache_head = (cache_head + 1) % SYNOP_CACHE_SIZE;
|
||||
c->title = title;
|
||||
c->synop_menu = synop_menu;
|
||||
if (has_synop == false)
|
||||
{
|
||||
label = lv_label_create(synop_menu);
|
||||
lv_label_set_text(label, "No synopsis found for this title");
|
||||
apply_label_style(label);
|
||||
}
|
||||
// We need to add the obj to the input group so we register button events.
|
||||
// Freeze focus on the synop box as we dont want lvgl change focus for us
|
||||
// Remember the previously focused object so we jump back to it when we close the synopsis menu
|
||||
lv_group_add_obj(gp, synop_menu);
|
||||
lv_obj_add_event_cb(synop_menu, close_callback, LV_EVENT_KEY, synop_menu);
|
||||
cache_leave: ;
|
||||
lv_obj_t *old_focus = lv_group_get_focused(gp);
|
||||
lv_group_focus_obj(synop_menu);
|
||||
lv_group_focus_freeze(gp, true);
|
||||
synop_menu->user_data = old_focus;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user