236 lines
8.2 KiB
C
Executable File
236 lines
8.2 KiB
C
Executable File
/*
|
|
* main.c -- OreBolt OS master launcher
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*
|
|
* Boots the LVGL UI, displays the module menu, and fork+execs the
|
|
* selected module binary from /apps/<name>.mod.
|
|
*
|
|
* Each module is a standalone binary with its own LVGL init and
|
|
* event loop. The retro_input_mapper is a standalone uinput daemon
|
|
* (no LVGL dependency) integrated into the core build with hardened
|
|
* flags.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <dirent.h>
|
|
|
|
#include "h2_ui.h"
|
|
#include "log_manager.h"
|
|
|
|
#define MAX_MODULES 32
|
|
#define APPS_DIR "/apps"
|
|
#define MAX_LABEL_LEN 48
|
|
|
|
/* Module metadata table -- drives the menu */
|
|
static const h2_module_t modules[] = {
|
|
{ H2_MOD_VAULT, "vault", "1.7", "GPL-2.0-or-later", "vault.mod" },
|
|
{ H2_MOD_NETTAP, "nettaps", "1.7", "GPL-2.0-or-later", "nettaps.mod" },
|
|
{ H2_MOD_SCALPEL, "scalpel", "1.7", "GPL-2.0-or-later", "scalpel.mod" },
|
|
{ H2_MOD_DEPLOY, "deploy", "1.7", "GPL-2.0-or-later", "deploy.mod" },
|
|
{ H2_MOD_STUDIO, "studio", "1.7", "GPL-2.0-or-later", "studio.mod" },
|
|
{ H2_MOD_PROBE, "probe", "1.7", "GPL-2.0-or-later", "probe.mod" },
|
|
{ H2_MOD_VTERM, "vterm", "1.7", "GPL-2.0-or-later", "vterm.mod" },
|
|
{ H2_MOD_PAUTO, "pauto", "1.7", "GPL-2.0-or-later", "pauto.mod" },
|
|
{ H2_MOD_EXTRACT, "extract", "1.7", "GPL-2.0-or-later", "extract.mod" },
|
|
{ H2_MOD_NOISE, "noise", "1.7", "GPL-2.0-or-later", "noise.mod" },
|
|
{ H2_MOD_RESET, "system", "1.7", "GPL-2.0-or-later", "reset.mod" },
|
|
{ H2_MOD_RETRO, "retro", "1.7", "GPL-2.0-or-later", "retro.mod" },
|
|
{ H2_MOD_RFID, "rfid", "1.7", "GPL-2.0-or-later", "rfid.mod" },
|
|
{ H2_MOD_GLITCH, "glitch", "1.7", "GPL-2.0-or-later", "glitch.mod" },
|
|
{ H2_MOD_PWDB, "pwdb", "1.7", "GPL-2.0-or-later", "pwdb.mod" },
|
|
{ H2_MOD_PROXALARM, "proxalarm", "1.7", "GPL-2.0-or-later", "proxalarm.mod" },
|
|
{ H2_MOD_BITCHAT, "bitchat", "1.7", "AGPL-3.0-only", "bitchat.mod" },
|
|
};
|
|
#define NUM_MODULES (sizeof(modules) / sizeof(modules[0]))
|
|
|
|
static volatile sig_atomic_t running = 1;
|
|
static void on_signal(int sig) { (void)sig; running = 0; }
|
|
|
|
/* Menu UI objects */
|
|
static lv_obj_t *menu_list = NULL;
|
|
static lv_obj_t *info_label = NULL;
|
|
static int selection = 0;
|
|
static int total_modules = 0;
|
|
|
|
/* Track which .mod files actually exist on disk */
|
|
static h2_module_t available[MAX_MODULES];
|
|
static int available_count = 0;
|
|
|
|
static void scan_apps(void) {
|
|
available_count = 0;
|
|
for (size_t i = 0; i < NUM_MODULES && available_count < MAX_MODULES; i++) {
|
|
char path[128];
|
|
snprintf(path, sizeof(path), "%s/%s", APPS_DIR, modules[i].mod_binary);
|
|
if (access(path, X_OK) == 0) {
|
|
available[available_count++] = modules[i];
|
|
}
|
|
}
|
|
total_modules = available_count;
|
|
}
|
|
|
|
static void build_menu(void) {
|
|
if (menu_list) lv_obj_clean(menu_list);
|
|
|
|
for (int i = 0; i < total_modules; i++) {
|
|
lv_obj_t *btn = lv_list_add_btn(menu_list, LV_SYMBOL_LIST,
|
|
available[i].name);
|
|
|
|
/* Color AGPL modules differently */
|
|
if (strcmp(available[i].license, "AGPL-3.0-only") == 0) {
|
|
lv_obj_set_style_text_color(btn,
|
|
lv_color_make(255, 180, 50), LV_PART_MAIN);
|
|
}
|
|
}
|
|
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "%d modules available", total_modules);
|
|
lv_label_set_text(info_label, buf);
|
|
}
|
|
|
|
static void scroll_to_selection(int delta) {
|
|
selection += delta;
|
|
if (selection < 0) selection = 0;
|
|
if (selection >= total_modules) selection = total_modules - 1;
|
|
|
|
/* Get the button list children and scroll to selection */
|
|
uint32_t child_cnt = lv_obj_get_child_cnt(menu_list);
|
|
if (selection < (int)child_cnt) {
|
|
lv_obj_t *child = lv_obj_get_child(menu_list, (uint32_t)selection);
|
|
if (child) lv_obj_scroll_to_view(child, LV_ANIM_ON);
|
|
}
|
|
}
|
|
|
|
static void launch_selected(void) {
|
|
if (selection < 0 || selection >= total_modules) return;
|
|
|
|
const char *binary = available[selection].mod_binary;
|
|
char path[128];
|
|
snprintf(path, sizeof(path), "%s/%s", APPS_DIR, binary);
|
|
|
|
LOG_INF("launching module: %s (%s)", available[selection].name, binary);
|
|
|
|
pid_t pid = fork();
|
|
if (pid == 0) {
|
|
/* Child: exec the module binary */
|
|
execl(path, binary, NULL);
|
|
_exit(127);
|
|
}
|
|
|
|
if (pid < 0) {
|
|
LOG_ERR("fork failed for %s", binary);
|
|
return;
|
|
}
|
|
|
|
/* Parent: wait for child to exit */
|
|
int wstatus;
|
|
waitpid(pid, &wstatus, 0);
|
|
LOG_INF("module exited: %s", available[selection].name);
|
|
|
|
/* Re-initialize LVGL display after child returns (framebuffer may be dirty) */
|
|
init_h2_graphics_runtime("OREBOLT OS v1.7 LAUNCHER");
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
(void)argc; (void)argv;
|
|
signal(SIGINT, on_signal);
|
|
signal(SIGTERM, on_signal);
|
|
|
|
if (log_init("/data/vault/syslog.log") < 0) {
|
|
fprintf(stderr, "[-] log_init failed\n");
|
|
return 1;
|
|
}
|
|
LOG_INF("OreBolt OS v1.7 starting (target: Ingenic X1000E)");
|
|
|
|
/* Initialize graphics */
|
|
init_h2_graphics_runtime("OREBOLT OS v1.7 LAUNCHER");
|
|
lv_obj_t *scr = lv_scr_act();
|
|
|
|
/* Title */
|
|
lv_obj_t *title = lv_label_create(scr);
|
|
lv_label_set_text(title, "OREBOLT OS v1.7");
|
|
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 8);
|
|
lv_obj_set_style_text_color(title, COLOR_PRIMARY, LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(title, &lv_font_montserrat_14, LV_PART_MAIN);
|
|
|
|
/* Subtitle */
|
|
lv_obj_t *sub = lv_label_create(scr);
|
|
lv_label_set_text(sub, "HiFiWalker H2 | X1000E | MIPS32r2");
|
|
lv_obj_align(sub, LV_ALIGN_TOP_MID, 0, 26);
|
|
lv_obj_set_style_text_color(sub, COLOR_MUTED, LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(sub, &lv_font_montserrat_10, LV_PART_MAIN);
|
|
|
|
/* Module list */
|
|
menu_list = lv_list_create(scr);
|
|
lv_obj_set_size(menu_list, 296, 170);
|
|
lv_obj_align(menu_list, LV_ALIGN_TOP_MID, 0, 42);
|
|
lv_obj_set_style_bg_color(menu_list,
|
|
lv_color_make(18, 22, 32), LV_PART_MAIN);
|
|
lv_obj_set_style_border_width(menu_list, 1, LV_PART_MAIN);
|
|
lv_obj_set_style_border_color(menu_list,
|
|
lv_color_make(50, 55, 70), LV_PART_MAIN);
|
|
|
|
/* Info bar */
|
|
info_label = lv_label_create(scr);
|
|
lv_obj_align(info_label, LV_ALIGN_BOTTOM_LEFT, 8, -16);
|
|
lv_obj_set_style_text_color(info_label, COLOR_MUTED, LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(info_label, &lv_font_montserrat_10, LV_PART_MAIN);
|
|
|
|
/* Status bar */
|
|
lv_obj_t *status = lv_label_create(scr);
|
|
lv_label_set_text(status, "ROTATE=scroll PLAY=launch BACK=exit");
|
|
lv_obj_align(status, LV_ALIGN_BOTTOM_MID, 0, -4);
|
|
lv_obj_set_style_text_color(status,
|
|
lv_color_make(80, 85, 100), LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(status, &lv_font_montserrat_10, LV_PART_MAIN);
|
|
|
|
/* Scan for available modules and build menu */
|
|
scan_apps();
|
|
build_menu();
|
|
LOG_INF("%d modules available out of %zu registered",
|
|
total_modules, NUM_MODULES);
|
|
|
|
/* Input loop */
|
|
int input_fd = open("/dev/input/event0", O_RDONLY | O_NONBLOCK);
|
|
struct input_event ev;
|
|
|
|
while (running) {
|
|
lv_timer_handler();
|
|
|
|
if (input_fd >= 0 &&
|
|
read(input_fd, &ev, sizeof(struct input_event)) > 0) {
|
|
|
|
if (ev.type == EV_REL && ev.code == H2_ROTARY_REL && ev.value != 0) {
|
|
scroll_to_selection(ev.value > 0 ? 1 : -1);
|
|
}
|
|
|
|
if (ev.type == EV_KEY && ev.value == 1) {
|
|
if (ev.code == H2_KEY_PLAY && total_modules > 0) {
|
|
launch_selected();
|
|
/* Rebuild menu after module returns */
|
|
scan_apps();
|
|
build_menu();
|
|
if (selection >= total_modules)
|
|
selection = (total_modules > 0) ? total_modules - 1 : 0;
|
|
}
|
|
if (ev.code == H2_KEY_BACK) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
usleep(5000);
|
|
}
|
|
|
|
if (input_fd >= 0) close(input_fd);
|
|
log_close();
|
|
LOG_INF("OreBolt OS v1.7 shutting down");
|
|
return 0;
|
|
} |