A Wayland compositor and mobile-oriented application shell for Linux
Marathon Shell is a Wayland compositor and mobile-oriented application shell for Linux, designed for touch-first interaction. The shell provides gesture-based navigation, application management, and system service integration, with support for both QML-based Marathon apps and native Linux desktop applications.
| Home Screen | App Grid |
| Task Switcher | Notification Hub |
| Quick Settings | Lock Screen |
| Browser |
Marathon Shell consists of four main components:
- Wayland Compositor - Qt6-based compositor for embedding native Linux applications
- QML Shell Interface - Touch-optimized UI with gesture navigation inspired by BlackBerry 10
- Application Framework - QML-based app development platform with optional C++ plugins
- System Integration - D-Bus services for network, power, Bluetooth, telephony, and other system functions
Shell Interface:
- Gesture-based navigation (swipe up for app grid, down for quick settings)
- Hub workflow for unified notifications and messaging
- Active Frames for live app previews in task switcher
- Peek gesture for quick notification preview
- Physics-based scrolling and transitions
Native Application Support:
- Wayland protocol implementation for embedding Linux apps
- D-Bus session integration for desktop services
- Flatpak and Snap container support with automatic permission handling
- gapplication command conversion for GNOME app compatibility
Application Development:
- QML-based app framework with MarathonUI design system
.marathonpackage format with GPG code signing- Runtime permission system via D-Bus portal
- Lifecycle management (background/foreground states)
System Services:
- Network management (WiFi, cellular via NetworkManager)
- Power management (battery, profiles via UPower)
- Bluetooth (device pairing via BlueZ)
- Telephony (calls, SMS via ModemManager)
- Display, audio, and notification services
Fedora/RHEL:
sudo dnf install cmake ninja-build gcc-c++ \ qt6-qtbase-devel \ qt6-qtdeclarative-devel \ qt6-qtwayland-devel \ qt6-qtmultimedia-devel \ qt6-qtsvg-devel \ qt6-qtlocation-devel \ qt6-qtpositioning-devel \ qt6-qtsensors-devel \ qt6-qtwebengine-devel \ pam-devel \ hunspell-devel \ hunspell-en-US \ pulseaudio-libs-devel
Ubuntu/Debian:
sudo apt install cmake ninja-build g++ pkg-config \ qt6-base-dev \ qt6-declarative-dev \ qt6-wayland-dev \ qt6-multimedia-dev \ qt6-svg-dev \ qt6-sensors-dev \ qdbus-qt6 \ libpam0g-dev \ dbus-daemon \ libhunspell-dev \ hunspell-en-us \ qt6-svg-plugins \ qt6-location-dev \ qt6-positioning-dev \ qt6-base-private-dev \ qt6-webengine-dev \ qml-module-qtwebengine \ libpulse-dev
Alpine Linux / postmarketOS:
apk add cmake samurai g++ pkgconf git \ qt6-qtbase-dev \ qt6-qtbase-private-dev \ qt6-qtdeclarative-dev \ qt6-qtwayland-dev \ qt6-qtmultimedia-dev \ qt6-qtsvg-dev \ qt6-qtlocation-dev \ qt6-qtpositioning-dev \ qt6-qtsensors-dev \ qt6-qtwebengine-dev \ linux-pam-dev \ hunspell-dev \ hunspell-en \ wayland-dev \ wayland-protocols \ mesa-dev \ dbus-dev \ eudev-dev \ libinput-dev \ pulseaudio-dev
- Qt 6.5 or later (tested with Qt 6.9.x)
- Wayland display server support
- D-Bus session bus
- Linux kernel 5.10 or later
- Standard preemptible kernel (
CONFIG_PREEMPT=y) -- what postmarketOS, Plasma Mobile, Phosh and Android all ship. The compositor usesSCHED_FIFOfor its render thread, which only needsCAP_SYS_NICE(orrtprioinlimits.conf) -- NOT aPREEMPT_RTkernel. PREEMPT_RTkernel (optional) -- useful primarily if you need bounded worst-case latency for hard-real-time audio paths. For a 60–120 Hz touch UI on a 8–16 ms frame budget the difference is below the noise floor; the shell will not warn if it's missing.
- NetworkManager - WiFi and cellular network management
- UPower - Battery and power profile management
- BlueZ - Bluetooth device management
- ModemManager - Cellular modem and telephony support
- qt6-qtvirtualkeyboard-devel - On-screen keyboard support
- qt6-qtwebengine-devel (Fedora) / qml-module-qtwebengine (Ubuntu/Debian)
- Required for the Browser app to function.
- If missing, the Browser app will fail to launch with
module "QtWebEngine" is not installed.
Important: If you encounter the error
module "MarathonUI.Theme" is not installed, see TROUBLESHOOTING.md for a quick fix.
One wrapper per target. Each calls the shared
scripts/build-image.sh with the right device argument and any
device-specific overlay aport.
| Device | Entry script | Status |
|---|---|---|
| QEMU virt aarch64 | scripts/build-qemu-image.sh |
Ready, verified — --boot lights up the shell on your dev box via virgl + VNC. |
| OnePlus 6 (enchilada) | scripts/build-oneplus6-image.sh |
Build-ready, flash unverified — duranium GPT .raw + EFI-aware boot.img extracted from the ESP, both consumed by scripts/flash/flash-oneplus6.sh. |
| Librem 5 (purism-librem5) | scripts/build-librem5-image.sh |
Build-ready, flash unverified — duranium GPT .raw; orchestrator also extracts phone-boot.img for flash-librem5.sh emmc. SD path is a single dd of the .raw. |
| HackberryPi CM5 | scripts/build-hackberry-cm5-image.sh |
Build-ready, flash unverified — parallel raspios-style pipeline (not duranium). Customizes stock RaspiOS Lite arm64 with marathon-shell + ZitaoTech hackberrypi.dtbo overlay + LightDM autologin. Verified Marathon QML renders responsively at ×ばつ720 locally. Flash with scripts/flash/flash-hackberry-cm5.sh /dev/sdX. |
Two pipelines, three devices:
- duranium (mkosi + systemd-boot + erofs+verity): QEMU, OnePlus 6, Librem 5
- raspios-style (RPi firmware + config.txt + LightDM + Wayland session): HackberryPi CM5
See docs/IMAGE_BUILD_ARCHITECTURE.md for which boot chain runs where, what the per-device extraction does, and which paths are verified vs. documented-but-untested-on-hardware.
Fastest visible result:
git clone https://github.com/patrickjquinn/Marathon-Shell.git
cd Marathon-Shell
./scripts/build-qemu-image.sh --verifyWhat that does, in order:
- Checks host tools (podman, qemu-system-aarch64, sshpass, EFI firmware) and prints install hints if anything's missing.
- Reads the in-tree aports from
packaging/, and auto-clonespostmarketos-duranium(mkosi skeleton) plus a pinnedmkosirelease into~/.cache/marathon-build/. - Overlays Marathon's
mkosi.conf+ image-extras scripts onto the duranium tree. - Builds the four local apks in dependency order (
qmf,marathon-base-config,marathon-mail-oauth,marathon-shell). Each one runs in a rootless podman container; subsequent invocations skip apks already in the cache. - Bakes a
qemu-aarch64_marathon_edgeimage via mkosi. - Boots it under QEMU and runs
verify-mail.sh(12 checks across the Mail backend).
Total first-run time on a modern machine: ~15 minutes (mostly QMF + marathon-shell C++ compile). Subsequent runs that only change shell sources rebuild in ~3 minutes.
Other modes:
./scripts/build-qemu-image.sh— build only, leave the image on disk, exit. Boot later with--boot-only../scripts/build-qemu-image.sh --boot— build then launch QEMU interactively. GL-accelerated (virtio-gpu-gl-pci+ virgl), VNC on127.0.0.1:5905by default so it works on any host. SetMARATHON_QEMU_DISPLAY=gtkto pop a native window instead../scripts/build-qemu-image.sh --boot-only— skip rebuild and just launch QEMU against the existing baked image. Fast iteration../scripts/build-qemu-image.sh --verify— headless boot + the 12-check Mail verification harness, exit with its rc../scripts/build-qemu-image.sh --help— env-var overrides for forks/branches/cache path/OAuth client IDs.
After --boot / --boot-only, connect with any VNC viewer to
127.0.0.1:5905 (Remmina, vinagre, vncviewer, macOS Screen
Sharing, ...). SSH into the guest with
sshpass -p marathon ssh -p 2233 root@127.0.0.1.
For local dev (running the shell directly on your X11/Wayland host instead of inside QEMU), clone the repository:
git clone https://github.com/patrickjquinn/Marathon-Shell.git
cd Marathon-Shell# Build shell, UI library, and apps (first time or after pulling updates)
./scripts/build-all.sh installThis builds:
- MarathonUI design system library (QML modules)
- Marathon Core library (app management)
- Marathon Shell executable
- All bundled applications
- Developer tools
And installs:
- MarathonUI to
~/.local/share/marathon-ui(required for shell to run) - Marathon apps to
~/.local/share/marathon-apps(required for bundled apps to appear)
IMPORTANT: The
installargument is required on first build. Without it, bundled apps (Settings, Browser, Calculator, etc.) won't appear in the shell. After the initial install, you can use./run.shfor incremental builds.
# Quick rebuild and run (incremental) ./run.sh # Clean rebuild CLEAN=1 ./run.sh # Rebuild apps only ./scripts/build-apps.sh # Rebuild shell only cd build && cmake --build .
The provided build scripts are recommended, but you can also use CMake directly:
# CRITICAL: Build and install MarathonUI FIRST (required for shell to run) cmake -B build -S . -DCMAKE_BUILD_TYPE=Release cmake --build build -j$(nproc) cmake --install build # Installs MarathonUI to ~/.local/share/marathon-ui # Now run the shell ./build/shell/marathon-shell-bin # Apps (optional, installs to ~/.local/share/marathon-apps by default) cmake -B build-apps -S apps -DCMAKE_BUILD_TYPE=Release cmake --build build-apps -j$(nproc) cmake --install build-apps
For system-wide installation (requires root):
# System-wide MarathonUI (installs to /usr/lib/qt6/qml/MarathonUI) cmake -B build -S . -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release sudo cmake --install build # System-wide apps (installs to /usr/share/marathon-apps) cmake -B build-apps -S apps -DMARATHON_APPS_DIR=/usr/share/marathon-apps sudo cmake --install build-apps
CRITICAL: Marathon Shell requires MarathonUI to be installed before it can run. If you see
module "MarathonUI.Theme" is not installed, runcmake --install build.
Note: Apps default to
~/.local/share/marathon-appsto avoid permission issues during development. This ensures the build works out of the box without sudo, making it IDE-friendly.
Linux (Primary Target):
- Full Wayland compositor support
- Native app embedding functional
- All system services available
CRITICAL: Marathon Shell requires system permissions and services for full mobile functionality. Run the setup script once before first use:
# One-time system setup (requires sudo)
./scripts/setup-system.shThis configures:
- Brightness control permissions (udev rule for
/sys/class/backlight) - Bluetooth service (installs and enables BlueZ)
- PAM authentication (copies config to
/etc/pam.d/marathon-shell)
# From project directory ./run.sh # Or directly from build directory ./build/shell/marathon-shell-bin # With debug logging MARATHON_DEBUG=1 ./run.sh
CRITICAL: Marathon Shell requires a PAM configuration file to authenticate users. Without this file, password authentication will fail with "Authentication failure" errors.
The shell uses PAM (Pluggable Authentication Modules) for system password authentication. Install the PAM configuration file:
sudo cp pam.d/marathon-shell /etc/pam.d/marathon-shell
This file configures:
- System password authentication via
pam_unix.so - Rate limiting (5 failed attempts = 5 minute lockout) via
pam_faillock.so - Optional fingerprint authentication via
pam_fprintd.so - Session integration with systemd-logind
Note: Without this file, you won't be able to unlock the shell with your password. If you see PAM authentication failures in the logs, this is the fix.
Marathon Shell implements opportunistic suspend with kernel wakelocks and RTC wake alarms. These features require specific permissions to function optimally.
For kernel wakelock support (/sys/power/wake_lock), the shell process needs CAP_BLOCK_SUSPEND capability:
# Option 1: Grant capability to the binary (recommended for production) sudo setcap cap_block_suspend+ep /path/to/marathon-shell-bin # Option 2: Use udev rules for development (easier for testing) # Create /etc/udev/rules.d/99-marathon-power.rules: SUBSYSTEM=="power", ACTION=="add", RUN+="/bin/chmod 666 /sys/power/wake_lock /sys/power/wake_unlock" SUBSYSTEM=="rtc", KERNEL=="rtc0", MODE="0664", GROUP="users" # Reload udev rules: sudo udevadm control --reload-rules sudo udevadm trigger
Note: If wakelock permissions are not available, Marathon Shell automatically falls back to systemd-logind inhibitor locks, which provide similar functionality without requiring special permissions.
For RTC alarm support (/sys/class/rtc/rtc0/wakealarm), the shell needs write access to the RTC device:
# Add your user to the appropriate group (usually dialout or users) sudo usermod -a -G dialout $USER # Or use udev rule (included in the snippet above) SUBSYSTEM=="rtc", KERNEL=="rtc0", MODE="0664", GROUP="dialout"
Check if power management features are available:
# Check wakelock support ls -la /sys/power/wake_lock /sys/power/wake_unlock # Check RTC alarm support ls -la /sys/class/rtc/rtc0/wakealarm # Test wakelock (should not error if permissions are correct) echo "test_lock" | sudo tee /sys/power/wake_lock cat /sys/power/wake_lock echo "test_lock" | sudo tee /sys/power/wake_unlock
Marathon Shell uses XDG Desktop Portals for secure permission management (Camera, Location, Microphone).
Droidian / Linux Mobile:
Ensure xdg-desktop-portal and a backend (e.g., xdg-desktop-portal-phosh or xdg-desktop-portal-gtk) are installed.
sudo apt install xdg-desktop-portal xdg-desktop-portal-phosh
If portals are not available, Marathon Shell automatically falls back to a custom permission dialog.
On first launch, Marathon Shell scans for applications in:
~/.local/share/marathon-apps/- Marathon apps/usr/share/applications/- System applications/var/lib/flatpak/exports/share/applications/- Flatpak apps~/.local/share/flatpak/exports/share/applications/- User Flatpak apps
Default Marathon apps include: Browser, Calculator, Calendar, Camera, Clock, Gallery, Maps, Messages, Music, Notes, Phone, Settings, Store, and Terminal.
- Swipe up from bottom - Open app grid
- Swipe down from top - Quick settings panel
- Swipe right from left edge - Hub (notifications and messages)
- Swipe up (short) - Peek at notifications
- Swipe left/right in app grid - Navigate between Hub/Switcher/Grid
- Long press app icon - Open task switcher
Marathon-Shell/
├── shell/ # Main shell executable
│ ├── main.cpp # Entry point, app scanning, D-Bus setup
│ ├── qml/ # QML UI implementation
│ │ ├── MarathonShell.qml # Main shell orchestration
│ │ ├── components/ # Shell UI components
│ │ ├── stores/ # Global state management
│ │ ├── services/ # System service integrations
│ │ └── core/ # Core utilities
│ ├── src/ # C++ backend implementation
│ │ ├── waylandcompositor* # Wayland compositor + D-Bus
│ │ ├── desktopfileparser* # .desktop file parser
│ │ ├── appmodel* # App registry
│ │ ├── networkmanagercpp* # NetworkManager integration
│ │ ├── powermanagercpp* # UPower integration
│ │ └── securitymanager* # PAM authentication
│ └── resources/ # Embedded assets (icons, fonts, sounds)
├── marathon-ui/ # MarathonUI Design System
│ ├── Theme/ # Colors, typography, spacing, motion
│ ├── Core/ # Buttons, inputs, labels, icons
│ ├── Controls/ # Toggles, sliders, radio buttons
│ ├── Containers/ # Pages, cards, sections, scroll views
│ ├── Lists/ # List items, dividers
│ ├── Navigation/ # Top bar, bottom bar, action bar
│ ├── Feedback/ # Badges, progress bars, activity indicators
│ ├── Modals/ # Dialogs, confirmation sheets, overlays
│ └── Effects/ # Ripple, inset/outset effects
├── marathon-core/ # Shared C++ library
│ └── src/ # App management infrastructure
│ ├── marathonapppackager* # .marathon package creation
│ ├── marathonappverifier* # GPG signature verification
│ ├── marathonappinstaller* # App installation logic
│ ├── marathonappregistry* # App catalog
│ └── marathonappscanner* # App discovery
├── apps/ # Bundled Marathon apps
│ ├── browser/ # Web browser
│ ├── calculator/ # Calculator
│ ├── settings/ # System settings
│ ├── store/ # App store
│ ├── terminal/ # Terminal emulator (C++ plugin)
│ └── ... # Other bundled apps
├── tools/ # Developer tools
│ └── marathon-dev/ # CLI for app development
# NOTE: LunaSVG is auto-fetched via CMake FetchContent for SVG rendering
├── scripts/ # Build and utility scripts
├── docs/ # Documentation
├── systemd/ # Service files
├── udev/ # Hardware access rules
├── polkit/ # Privilege elevation policies
├── pam.d/ # PAM authentication config
└── CMakeLists.txt # Root build configuration
Marathon apps use the .marathon package format:
- ZIP-based archive containing app files
manifest.json- App metadata (id, name, version, permissions)SIGNATURE.txt- GPG detached signature (optional but recommended)- Structured layout with QML files, assets, and optional C++ plugins
# Generate GPG key gpg --full-generate-key # Sign app marathon-dev sign apps/myapp # Verify signature marathon-dev verify myapp.marathon
Apps can be signed with GPG for authenticity verification. The shell verifies signatures during installation and displays trust status. See docs/CODE_SIGNING_GUIDE.md for details.
Apps request permissions in manifest.json:
{
"permissions": [
"network",
"location",
"camera",
"microphone",
"contacts",
"calendar",
"storage"
]
}Permissions are enforced via D-Bus Permission Portal (org.marathonos.shell.PermissionPortal). Users can review and revoke permissions in Settings.
See docs/PERMISSION_GUIDE.md for implementation details.
Marathon includes an integrated App Store for browsing, installing, and updating Marathon apps. The store uses the same marathon-core library as the marathon-dev CLI tool, ensuring consistent behavior.
The marathon-dev tool covers app development end-to-end:
# Create new app from template ./build/tools/marathon-dev/marathon-dev init myapp # Package app ./build/tools/marathon-dev/marathon-dev package apps/myapp myapp.marathon # Sign app ./build/tools/marathon-dev/marathon-dev sign apps/myapp [key-id] # Verify signature ./build/tools/marathon-dev/marathon-dev verify myapp.marathon # Install app ./build/tools/marathon-dev/marathon-dev install myapp.marathon # List installed apps ./build/tools/marathon-dev/marathon-dev list # Show app details ./build/tools/marathon-dev/marathon-dev info myapp # Validate app structure ./build/tools/marathon-dev/marathon-dev validate apps/myapp
See docs/DEV_CLI.md for complete CLI documentation.
Marathon apps are QML-based with optional C++ plugins.
Minimal app structure:
apps/myapp/
├── CMakeLists.txt # Build configuration
├── manifest.json # App metadata
├── MyApp.qml # Entry point
├── qmldir # QML module definition
└── assets/ # Icons and images
└── icon.svg
Example manifest.json:
{
"id": "myapp",
"name": "My App",
"version": "1.0.0",
"author": "Your Name",
"description": "Application description",
"icon": "assets/icon.svg",
"entryPoint": "MyApp.qml",
"permissions": [],
"minShellVersion": "1.0.0"
}Example MyApp.qml:
import QtQuick import MarathonOS.Shell import MarathonUI.Containers import MarathonUI.Core MApp { appId: "myapp" appName: "My App" content: MPage { title: "My App" MLabel { anchors.centerIn: parent text: "Hello Marathon!" } } }
Build and install:
./scripts/build-apps.sh
See docs/APP_DEVELOPMENT.md for complete app development guide.
# Enable debug logging MARATHON_DEBUG=1 ./run.sh # Qt logging rules export QT_LOGGING_RULES="marathon.*.debug=true" ./run.sh # GDB debugging gdb --args ./build/shell/marathon-shell-bin # Valgrind memory check valgrind --leak-check=full ./build/shell/marathon-shell-bin
# Validate all QML files ./scripts/validate-qml.sh # Validate specific file qmllint apps/myapp/MyApp.qml
Marathon Shell implements a Wayland compositor that embeds native Linux applications as first-class citizens in the shell environment.
Application Discovery:
- Desktop files scanned from standard freedesktop.org locations
- Flatpak and Snap apps detected automatically
- gapplication commands converted to direct binary execution
Launch Process:
- Marathon creates isolated Wayland + D-Bus environment
- Application connects to
marathon-wayland-0compositor socket - Wayland surface embedded in shell UI via
WaylandShellSurfaceItem - D-Bus session provides desktop service integration
Flatpak Support:
- Automatic
--socket=waylandflag addition - Environment variables passed for compositor connection
- Permission handling for sandboxed apps
Snap Support:
- Interface detection and logging
- Manual interface connection may be required:
snap connect APP:wayland :wayland
Fully Supported:
- Native Wayland applications (GTK4, Qt6)
- Flatpak applications with Wayland support
- GNOME applications (with gapplication conversion)
- Electron applications (with Wayland flags)
Partially Supported:
- Snap applications (requires manual interface connection)
- X11 applications via XWayland (not yet implemented)
Not Supported:
- Applications requiring systemd user services
- Applications requiring system D-Bus services
- Root/privileged applications
Qt6WebEngineQuick not found- Browser uses mockup UI (expected if QtWebEngine not installed)
Note: Marathon OS uses a fully custom keyboard implementation (not Qt VirtualKeyboard). The custom keyboard is BlackBerry 10-inspired with Marathon design system integration and includes:
- Hunspell spell-checking for word prediction and auto-correction
- Content-aware layouts (email, URL, number, phone)
- Word Fling gesture (swipe up on a key to accept prediction)
- Predictive Spacing (BB10-style automatic spacing)
- Qt logging verbosity in debug mode (can be filtered with
QT_LOGGING_RULES) - EGL display warnings on some systems (hardware acceleration fallback, benign)
- NetworkManager/UPower warnings if services not running (expected on non-systemd systems)
- Applications requiring system D-Bus may not function correctly
- Some Flatpak applications need additional permission configuration
- Snap applications require manual Wayland interface connection
Marathon Shell uses Landlock for application sandboxing on Linux kernel 5.13+. On kernel 6.12+ (Landlock ABI 6), abstract UNIX socket scoping is intentionally disabled to allow sandboxed apps to communicate with the parent Wayland compositor. Signal scoping remains enabled for security.
-
App Development Guide - Creating Marathon apps
-
UI Design System - MarathonUI component reference
-
Development Workflow - Development process and conventions
-
Developer CLI Guide - marathon CLI reference and recipes
-
Code Signing Guide - GPG signing for apps
-
Permission Guide - Permission system implementation
-
Publishing Guide - App distribution process
-
RT Scheduling - Real-time scheduling configuration
- Edit source files in
apps/,shell/, ormarathon-ui/ - Never edit files in
~/.local/share/marathon-apps/(build artifacts) - Run
./scripts/build-all.shto rebuild after changes - Test thoroughly before committing
- Follow existing code style and conventions
See docs/DEVELOPMENT_WORKFLOW.md for detailed contribution workflow.
Apache License 2.0. See LICENSE file for details.
Marathon Shell is inspired by BlackBerry 10's gesture navigation and Hub workflow. Built with Qt6/QML and implementing the Wayland compositor protocol for native Linux application support. System integration follows freedesktop.org standards.