Implement Qt6 GUI with direct kernel interface, remove Unix user system

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-02 01:26:19 +00:00
parent 67ba162bde
commit 37e973883d
9 changed files with 389 additions and 171 deletions

View File

@@ -2,121 +2,88 @@
## Overview ## Overview
SparkOS is designed as a minimal Linux distribution with a custom init system and a modular architecture that allows for future expansion. SparkOS is a revolutionary operating system that uses the Linux kernel for hardware abstraction but ditches traditional Unix conventions. Instead of shells, users, and Unix utilities, SparkOS boots directly into a Qt6 GUI that interfaces with the kernel through standard Linux APIs.
## System Components ## Core Philosophy
### 1. Init System (`/sbin/init`) 1. **No Unix Baggage**: No user/group system, no shells, no Unix utilities by default
2. **Direct Kernel Interface**: GUI communicates directly with Linux kernel
3. **Network-First**: Networking is a primary interface, not an afterthought
4. **GUI-Only**: No CLI unless explicitly needed for debugging
5. **Linux for Drivers**: Leverage Linux's excellent hardware support
The init system is the first process started by the kernel (PID 1). It is responsible for: ## System Architecture
- **Mounting filesystems**: proc, sys, dev, tmp
- **Network initialization**: Bringing up wired networking via DHCP
- **Process management**: Spawning and respawning the shell
- **Signal handling**: Reaping zombie processes
- **System initialization**: Setting up the initial environment
**Implementation**: `src/init.c`
- Written in C for minimal overhead
- Statically linked for independence
- ~100 lines of clean, well-documented code
### 2. Root Filesystem
Follows the Filesystem Hierarchy Standard (FHS):
``` ```
/ Boot Sequence:
├── bin/ Essential user binaries (busybox with symlinks) Hardware → UEFI/BIOS → GRUB → Linux Kernel → init (PID 1) → Qt6 GUI
├── sbin/ System binaries (init, mount, etc.)
├── etc/ System configuration files
├── proc/ Kernel process information (virtual)
├── sys/ Kernel system information (virtual)
├── dev/ Device files (virtual)
├── tmp/ Temporary files
├── usr/
│ ├── bin/ Non-essential user binaries
│ ├── sbin/ Non-essential system binaries
│ ├── lib/ Libraries for /usr/bin and /usr/sbin
│ └── lib64/ 64-bit libraries
├── var/ Variable data (logs, caches)
├── root/ Root user home directory
└── home/ User home directories
```
### 3. Build System Stack Layers:
┌──────────────────────────────────────────┐
**Makefile**: Main build orchestration │ Qt6 GUI Application │ ← User Interface
- `make init`: Compile init system │ (sparkos-gui executable) │
- `make install`: Install init to rootfs ├──────────────────────────────────────────┤
- `make image`: Create bootable image (requires root) │ Custom Init System (PID 1) │ ← Process Manager
- `make clean`: Clean build artifacts │ • Mounts filesystems │
│ • Spawns/respawns GUI │
**Scripts**: │ • Reaps zombie processes │
- `scripts/build.sh`: Quick build for development ├──────────────────────────────────────────┤
- `scripts/setup_rootfs.sh`: Create rootfs structure │ Linux Kernel │ ← Hardware Abstraction
- `scripts/create_image.sh`: Create dd-able disk image │ • All device drivers │
│ • Framebuffer driver │
### 4. Boot Process │ • Input device drivers │
│ • Network stack & drivers │
``` │ • File system support │
Hardware Power-On ├──────────────────────────────────────────┤
│ Hardware │
BIOS/UEFI │ • Display, GPU, Input │
• Network adapters │
Bootloader (syslinux) │ • Storage devices │
└──────────────────────────────────────────┘
Linux Kernel (vmlinuz)
Init System (/sbin/init) [PID 1]
Mount filesystems
Initialize network (/sbin/init-network)
Spawn busybox sh shell
User interaction
``` ```
## Design Decisions ## Design Decisions
### Why Custom Init? ### Why Ditch Unix Conventions?
- **Simplicity**: No dependencies, easy to understand Traditional Unix systems were designed for multi-user, time-sharing mainframes in the 1970s. Modern personal computing and embedded systems have different needs:
- **Control**: Full control over boot process
- **Size**: Minimal footprint (<1MB statically linked)
- **Learning**: Educational value for OS development
### Why Static Linking? - **Single User**: Most devices have one user - authentication overhead is unnecessary
- **GUI Primary**: Modern users expect graphical interfaces, not command lines
- **Network Central**: Modern computing is network-centric, not file-centric
- **Direct Access**: Applications should talk directly to kernel, not through layers of abstraction
- **Independence**: No library dependencies ### Why Keep Linux Kernel?
- **Portability**: Works on any Linux system
- **Reliability**: No missing library issues
### Why Busybox? - **Hardware Support**: Linux has exceptional driver support for modern hardware
- **Driver Abstraction**: Well-tested, stable hardware abstraction layer
- **Network Stack**: Robust, high-performance networking
- **File Systems**: Mature support for various filesystems
- **Security**: SELinux, namespaces, cgroups for isolation
- **Community**: Active development and security updates
- **Minimal**: Single binary provides dozens of utilities ### Why Qt6 GUI?
- **Small footprint**: Typically <1MB for full feature set
- **Efficient**: Less memory and storage overhead than full GNU coreutils
- **Standard**: De facto standard for embedded Linux systems
- **Networking**: Includes DHCP client (udhcpc), ping, wget, and network tools
### Networking Strategy - **Cross-Platform**: Qt works on many platforms (future portability)
- **Framebuffer Support**: Can render directly to Linux framebuffer without X11/Wayland
- **Modern**: Native look and feel, hardware acceleration support
- **Complete**: Rich widget set, networking APIs, file I/O
- **Performant**: Efficient rendering and event handling
SparkOS uses a two-phase networking approach: ### Why No X11/Wayland?
**Phase 1: Bootstrap (Wired Only)** - **Direct Rendering**: Qt can render directly to framebuffer (/dev/fb0)
- Wired networking configured via DHCP - **Less Overhead**: No display server running in between
- Automatic interface detection (eth0, enp0s3, etc.) - **Simpler**: Fewer processes, less memory usage
- DNS fallback to public servers (8.8.8.8, 1.1.1.1) - **Embedded-Friendly**: Same approach used in embedded systems
- Enables git clone to install spark CLI
**Phase 2: Full Configuration (via spark CLI)** ### Why Network-First?
- WiFi configuration
- Advanced networking features Modern computing is inherently networked. Instead of treating networking as an add-on:
- Custom DNS settings - Network APIs exposed directly to GUI
- Network profiles - Cloud storage as primary storage paradigm
- Web technologies integrated (future: embedded browser)
- Real-time updates and communication built-in
## Future Architecture ## Future Architecture

View File

@@ -7,16 +7,18 @@ DESTDIR = rootfs
IMAGE = sparkos.img IMAGE = sparkos.img
IMAGE_SIZE = 512M IMAGE_SIZE = 512M
.PHONY: all clean init image image-docker help install docker-release .PHONY: all clean init gui image image-docker help install docker-release
all: init all: init gui
help: help:
@echo "SparkOS Build System" @echo "SparkOS Build System"
@echo "====================" @echo "===================="
@echo "Targets:" @echo "Targets:"
@echo " make init - Build the init system" @echo " make init - Build the init system"
@echo " make install - Install init to rootfs" @echo " make gui - Build the Qt6 GUI application"
@echo " make all - Build both init and GUI (default)"
@echo " make install - Install init and GUI to rootfs"
@echo " make image - Create bootable dd-able image (requires root)" @echo " make image - Create bootable dd-able image (requires root)"
@echo " make image-docker - Create bootable image using Docker (no root required)" @echo " make image-docker - Create bootable image using Docker (no root required)"
@echo " make docker-release - Build release package using Docker (no root required)" @echo " make docker-release - Build release package using Docker (no root required)"
@@ -34,10 +36,20 @@ init: src/init.c
$(CC) $(CFLAGS) -o init src/init.c $(CC) $(CFLAGS) -o init src/init.c
@echo "Init system built successfully: ./init" @echo "Init system built successfully: ./init"
install: init gui:
@echo "Building SparkOS Qt6 GUI application..."
@mkdir -p build/gui
@cd build/gui && cmake ../../src/qt6-app -DCMAKE_INSTALL_PREFIX=$(DESTDIR)/usr
@cd build/gui && $(MAKE)
@echo "Qt6 GUI application built successfully: build/gui/sparkos-gui"
install: init gui
@echo "Installing init to rootfs..." @echo "Installing init to rootfs..."
install -D -m 755 init $(DESTDIR)/sbin/init install -D -m 755 init $(DESTDIR)/sbin/init
@echo "Init installed to $(DESTDIR)/sbin/init" @echo "Init installed to $(DESTDIR)/sbin/init"
@echo "Installing Qt6 GUI application to rootfs..."
@cd build/gui && $(MAKE) install
@echo "GUI application installed to $(DESTDIR)/usr/bin/sparkos-gui"
image: install image: install
@echo "Creating bootable image..." @echo "Creating bootable image..."

View File

@@ -1,16 +1,46 @@
# SparkOS # SparkOS
A minimal Linux distribution designed for simplicity and portability. SparkOS features: A revolutionary operating system that ditches Unix conventions for a modern, network-first approach. SparkOS features:
- **Minimal footprint**: Barebones Linux system with busybox shell - **Direct Kernel Interface**: Qt6 GUI communicates directly with Linux kernel, bypassing Unix layers
- **Linux Driver Layer**: All hardware abstraction handled by Linux kernel drivers
- **No Unix User System**: No users, groups, passwords, or authentication - direct boot to GUI
- **Network-First Architecture**: Built around networking as the primary paradigm
- **Qt6 Full-Screen GUI**: Modern graphical interface from boot
- **Minimal footprint**: Lean system with only essential components
- **Portable**: dd-able disk image for USB flash drives - **Portable**: dd-able disk image for USB flash drives
- **Custom init**: Lightweight C init system - **Custom init**: Lightweight C init system that launches GUI directly
- **Future-ready**: Designed to support Qt6/QML GUI and Wayland - **Immutable base**: Read-only root filesystem with overlay for runtime data
- **Root elevation**: Uses sudo for privilege management
- **Bootstrap networking**: Wired networking with DHCP for initial setup ## Architecture
- Minimal installation by default: kernel, init system, busybox, git, and sudo
- DNS configured with fallback to public DNS servers (8.8.8.8, 1.1.1.1) ```
- WiFi and advanced networking configured later via spark CLI ┌─────────────────────────────────────┐
│ Qt6 GUI Application │
│ (Direct Framebuffer Rendering) │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Linux Kernel │
│ • Framebuffer (/dev/fb0) │
│ • Input devices (/dev/input/*) │
│ • Network stack │
│ • All hardware drivers │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Hardware │
│ • GPU, Display, Input devices │
│ • Network adapters │
│ • Storage, etc. │
└─────────────────────────────────────┘
```
**Key Design Principles:**
- Linux kernel provides complete hardware abstraction and driver support
- Qt6 interfaces directly with kernel through /dev, /proc, /sys interfaces
- No intermediate Unix layers (no systemd, no user management, no shells by default)
- Network-first: networking capabilities exposed directly to GUI
## MVP Status ## MVP Status

View File

@@ -1,3 +0,0 @@
root:x:0:
spark:x:1000:
sudo:x:27:spark

View File

@@ -1,2 +0,0 @@
root:x:0:0:root:/root:/bin/sh
spark:x:1000:1000:SparkOS User:/home/spark:/bin/sh

View File

@@ -1,15 +0,0 @@
# SparkOS Sudoers Configuration
# Allow spark user to run any command without password
# Default settings
Defaults env_reset
Defaults secure_path="/bin:/sbin:/usr/bin:/usr/sbin"
# Root can run anything
root ALL=(ALL:ALL) ALL
# Spark user can run anything without password
spark ALL=(ALL:ALL) NOPASSWD: ALL
# Include sudoers.d directory
@includedir /etc/sudoers.d

View File

@@ -12,12 +12,6 @@
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
// Default user configuration
#define SPARK_UID 1000
#define SPARK_GID 1000
#define SPARK_HOME "/home/spark"
#define SPARK_USER "spark"
static void signal_handler(int sig) { static void signal_handler(int sig) {
if (sig == SIGCHLD) { if (sig == SIGCHLD) {
// Reap zombie processes // Reap zombie processes
@@ -25,6 +19,38 @@ static void signal_handler(int sig) {
} }
} }
static void spawn_gui() {
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
return;
}
if (pid == 0) {
// Child process - exec Qt6 GUI application as root (no user switching)
char *argv[] = {"/usr/bin/sparkos-gui", NULL};
char *envp[] = {
"HOME=/root",
"PATH=/bin:/sbin:/usr/bin:/usr/sbin",
"QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0",
"QT_QPA_FB_FORCE_FULLSCREEN=1",
"QT_QPA_FONTDIR=/usr/share/fonts",
NULL
};
execve("/usr/bin/sparkos-gui", argv, envp);
perror("failed to exec GUI application");
exit(1);
}
// Parent process - wait for GUI to exit
int status;
waitpid(pid, &status, 0);
}
static void spawn_shell() { static void spawn_shell() {
pid_t pid = fork(); pid_t pid = fork();
@@ -34,34 +60,17 @@ static void spawn_shell() {
} }
if (pid == 0) { if (pid == 0) {
// Child process - exec shell as spark user // Child process - exec shell as root (fallback only)
// Set user and group IDs to spark user
if (setgid(SPARK_GID) != 0) {
perror("setgid failed");
exit(1);
}
if (setuid(SPARK_UID) != 0) {
perror("setuid failed");
exit(1);
}
char *argv[] = {"/bin/sh", "-l", NULL}; char *argv[] = {"/bin/sh", "-l", NULL};
char *envp[] = { char *envp[] = {
"HOME=" SPARK_HOME, "HOME=/root",
"PATH=/bin:/sbin:/usr/bin:/usr/sbin", "PATH=/bin:/sbin:/usr/bin:/usr/sbin",
"TERM=linux", "TERM=linux",
"PS1=SparkOS$ ", "PS1=SparkOS# ",
"USER=" SPARK_USER,
"LOGNAME=" SPARK_USER,
NULL NULL
}; };
// Change to home directory
if (chdir(SPARK_HOME) != 0) {
perror("chdir failed");
}
execve("/bin/sh", argv, envp); execve("/bin/sh", argv, envp);
perror("failed to exec shell"); perror("failed to exec shell");
@@ -107,9 +116,6 @@ int main(int argc, char *argv[]) {
if (system("mkdir -p /tmp/overlay/var-upper /tmp/overlay/var-work 2>/dev/null") != 0) { if (system("mkdir -p /tmp/overlay/var-upper /tmp/overlay/var-work 2>/dev/null") != 0) {
fprintf(stderr, "Warning: Failed to create overlay directories for /var\n"); fprintf(stderr, "Warning: Failed to create overlay directories for /var\n");
} }
if (system("mkdir -p /tmp/overlay/home-upper /tmp/overlay/home-work 2>/dev/null") != 0) {
fprintf(stderr, "Warning: Failed to create overlay directories for /home/spark\n");
}
// Mount overlay on /var for logs and runtime data // Mount overlay on /var for logs and runtime data
if (system("mount -t overlay overlay -o lowerdir=/var,upperdir=/tmp/overlay/var-upper,workdir=/tmp/overlay/var-work /var 2>/dev/null") != 0) { if (system("mount -t overlay overlay -o lowerdir=/var,upperdir=/tmp/overlay/var-upper,workdir=/tmp/overlay/var-work /var 2>/dev/null") != 0) {
@@ -118,13 +124,6 @@ int main(int argc, char *argv[]) {
printf("Overlay filesystem mounted on /var (base OS is immutable)\n"); printf("Overlay filesystem mounted on /var (base OS is immutable)\n");
} }
// Mount overlay on /home/spark for user data
if (system("mount -t overlay overlay -o lowerdir=/home/spark,upperdir=/tmp/overlay/home-upper,workdir=/tmp/overlay/home-work /home/spark 2>/dev/null") != 0) {
fprintf(stderr, "Warning: Failed to mount overlay on /home/spark - home directory may be read-only\n");
} else {
printf("Overlay filesystem mounted on /home/spark (writable user home)\n");
}
// Mount tmpfs on /run for runtime data // Mount tmpfs on /run for runtime data
if (system("mkdir -p /run 2>/dev/null") == 0) { if (system("mkdir -p /run 2>/dev/null") == 0) {
if (system("mount -t tmpfs tmpfs /run 2>/dev/null") != 0) { if (system("mount -t tmpfs tmpfs /run 2>/dev/null") != 0) {
@@ -138,18 +137,20 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Warning: Network initialization failed - check network interface availability\n"); fprintf(stderr, "Warning: Network initialization failed - check network interface availability\n");
} }
printf("Starting shell...\n"); printf("Starting Qt6 GUI application...\n");
printf("Welcome to SparkOS!\n"); printf("Welcome to SparkOS!\n");
printf("===================\n"); printf("===================\n");
printf("Base OS: Read-only (immutable)\n"); printf("Base OS: Read-only (immutable)\n");
printf("Writable: /tmp, /var (overlay), /home/spark (overlay), /run\n\n"); printf("Writable: /tmp, /var (overlay), /run\n");
printf("Mode: Qt6 GUI (Network-First Architecture)\n");
printf("No Users/Authentication - Direct Boot to GUI\n\n");
// Main loop - keep respawning shell // Main loop - keep respawning GUI application
while (1) { while (1) {
spawn_shell(); spawn_gui();
// If shell exits, ask if user wants to reboot // If GUI exits, respawn after a short delay
printf("\nShell exited. Press Ctrl+Alt+Del to reboot or wait for new shell...\n"); printf("\nGUI application exited. Restarting in 2 seconds...\n");
sleep(2); sleep(2);
} }

View File

@@ -0,0 +1,42 @@
cmake_minimum_required(VERSION 3.16)
project(sparkos-gui VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Set build type to Release if not specified
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# Find Qt6 packages
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Gui)
# Enable automatic MOC (Meta-Object Compiler)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# Source files
set(SOURCES
main.cpp
)
# Create executable
add_executable(sparkos-gui ${SOURCES})
# Link Qt6 libraries
target_link_libraries(sparkos-gui PRIVATE
Qt6::Core
Qt6::Widgets
Qt6::Gui
)
# Set properties for static linking if desired
# target_link_options(sparkos-gui PRIVATE -static-libgcc -static-libstdc++)
# Install target
install(TARGETS sparkos-gui
RUNTIME DESTINATION bin
)

186
src/qt6-app/main.cpp Normal file
View File

@@ -0,0 +1,186 @@
/*
* SparkOS Qt6 GUI Application
* Direct kernel interface - bypassing Unix conventions
* Network-first, GUI-only operating system
*/
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QVBoxLayout>
#include <QPushButton>
#include <QFont>
#include <QScreen>
#include <QStyle>
#include <QProcess>
#include <QTextEdit>
class SparkOSMainWindow : public QWidget {
Q_OBJECT
public:
SparkOSMainWindow(QWidget *parent = nullptr) : QWidget(parent) {
setupUI();
}
private slots:
void showSystemInfo() {
// Direct kernel interface through /proc and /sys
QString info = "SparkOS - Direct Kernel Interface\n";
info += "==================================\n\n";
// Read kernel version directly from /proc
QFile kernelFile("/proc/version");
if (kernelFile.open(QIODevice::ReadOnly)) {
info += "Kernel: " + QString(kernelFile.readLine()).trimmed() + "\n\n";
kernelFile.close();
}
// Read memory info directly from /proc
QFile meminfoFile("/proc/meminfo");
if (meminfoFile.open(QIODevice::ReadOnly)) {
info += "Memory Info:\n";
int lineCount = 0;
while (!meminfoFile.atEnd() && lineCount < 3) {
info += " " + QString(meminfoFile.readLine()).trimmed() + "\n";
lineCount++;
}
meminfoFile.close();
}
info += "\n";
info += "Architecture: Network-First OS\n";
info += "No Unix user/group system\n";
info += "Direct Qt6 GUI to Kernel interface\n";
QTextEdit *infoDialog = new QTextEdit();
infoDialog->setReadOnly(true);
infoDialog->setPlainText(info);
infoDialog->setWindowTitle("System Information");
infoDialog->resize(600, 400);
infoDialog->show();
}
private:
void setupUI() {
// Set window properties
setWindowTitle("SparkOS");
// Create main layout
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setAlignment(Qt::AlignCenter);
mainLayout->setSpacing(30);
// Create welcome label
QLabel *titleLabel = new QLabel("Welcome to SparkOS", this);
QFont titleFont;
titleFont.setPointSize(48);
titleFont.setBold(true);
titleLabel->setFont(titleFont);
titleLabel->setAlignment(Qt::AlignCenter);
titleLabel->setStyleSheet("color: #2196F3;");
// Create subtitle label
QLabel *subtitleLabel = new QLabel("Direct Kernel • Network-First • No Unix", this);
QFont subtitleFont;
subtitleFont.setPointSize(24);
subtitleLabel->setFont(subtitleFont);
subtitleLabel->setAlignment(Qt::AlignCenter);
subtitleLabel->setStyleSheet("color: #666;");
// Create status label
QLabel *statusLabel = new QLabel("✓ System Initialized • GUI Active", this);
QFont statusFont;
statusFont.setPointSize(16);
statusLabel->setFont(statusFont);
statusLabel->setAlignment(Qt::AlignCenter);
statusLabel->setStyleSheet("color: #4CAF50;");
// Create info label
QLabel *infoLabel = new QLabel("Qt6 GUI ↔ Linux Kernel (Direct Interface)", this);
QFont infoFont;
infoFont.setPointSize(14);
infoLabel->setFont(infoFont);
infoLabel->setAlignment(Qt::AlignCenter);
infoLabel->setStyleSheet("color: #999;");
// Create system info button
QPushButton *infoButton = new QPushButton("System Info", this);
infoButton->setMinimumSize(200, 60);
QFont buttonFont;
buttonFont.setPointSize(16);
infoButton->setFont(buttonFont);
infoButton->setStyleSheet(
"QPushButton {"
" background-color: #2196F3;"
" color: white;"
" border: none;"
" border-radius: 5px;"
" padding: 10px;"
"}"
"QPushButton:hover {"
" background-color: #1976D2;"
"}"
"QPushButton:pressed {"
" background-color: #0D47A1;"
"}"
);
connect(infoButton, &QPushButton::clicked, this, &SparkOSMainWindow::showSystemInfo);
// Create exit button
QPushButton *exitButton = new QPushButton("Power Off", this);
exitButton->setMinimumSize(200, 60);
exitButton->setFont(buttonFont);
exitButton->setStyleSheet(
"QPushButton {"
" background-color: #f44336;"
" color: white;"
" border: none;"
" border-radius: 5px;"
" padding: 10px;"
"}"
"QPushButton:hover {"
" background-color: #da190b;"
"}"
"QPushButton:pressed {"
" background-color: #a31408;"
"}"
);
// Connect exit button
connect(exitButton, &QPushButton::clicked, this, &QApplication::quit);
// Add widgets to layout
mainLayout->addStretch();
mainLayout->addWidget(titleLabel);
mainLayout->addWidget(subtitleLabel);
mainLayout->addSpacing(40);
mainLayout->addWidget(statusLabel);
mainLayout->addWidget(infoLabel);
mainLayout->addSpacing(40);
mainLayout->addWidget(infoButton, 0, Qt::AlignCenter);
mainLayout->addWidget(exitButton, 0, Qt::AlignCenter);
mainLayout->addStretch();
// Set background color
setStyleSheet("QWidget { background-color: #f5f5f5; }");
// Make fullscreen on Linux
showFullScreen();
}
};
int main(int argc, char *argv[]) {
// Direct framebuffer rendering - no X11/Wayland server needed
// The Qt application interfaces directly with the Linux kernel framebuffer
qputenv("QT_QPA_PLATFORM", "linuxfb");
QApplication app(argc, argv);
SparkOSMainWindow window;
window.show();
return app.exec();
}
#include "main.moc"