Initial Commit

This commit is contained in:
Ryzee119
2022-04-03 11:04:28 +09:30
commit 179f4ec129
68 changed files with 9773 additions and 0 deletions
+94
View File
@@ -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
View File
@@ -0,0 +1,11 @@
.vscode
bin
build
linux_build
CMakeFiles
CMakeCache.txt
dash.xml
*.d
*.obj
*.exe
*.iso
+15
View File
@@ -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
+57
View File
@@ -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)
+21
View File
@@ -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.
+48
View File
@@ -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
+113
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+105
View File
@@ -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
View File
@@ -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;
}
+22
View File
@@ -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

+128
View File
@@ -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);
}
+32
View File
@@ -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
+242
View File
@@ -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