OreBolt-OS/main.c

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;
}