Removes the hardcoded `output * mode 1920x1080` from the development Sway config and updates related docs to match. Dev sessions now rely on each output’s preferred mode at scale `1`, which better follows current display size (including VMware autofit behavior), while still documenting how to set a mode manually when needed.
7.4 KiB
Nebula Desktop
Production shell for the mouse-and-keyboard NebulaOS desktop. This is Nebula Desktop 0.1: a usable session foundation, not only a visual prototype.
The user can enter the development session, use the full output, open the Nebula launcher, and start real installed applications (including Foot) without Sway keyboard shortcuts.
Architecture
Nebula Desktop is a Qt Quick / QML Wayland shell. It draws system chrome only. It does not host application or game windows.
Ubuntu
↓
Wayland
↓
Sway
temporary compositor
↓
Nebula Shell
C++20 + Qt 6 + Qt Quick/QML + LayerShellQt
Eventual target:
Ubuntu/Linux
↓
Wayland
↓
Nebula compositor
↓
Nebula Shell
Qt Quick/QML
Normal applications remain ordinary Wayland / XWayland clients of the compositor:
Nebula Launcher
↓
ApplicationService
↓
application process
↓
Wayland / XWayland
↓
Sway
Games, browsers, terminals, and other apps never pass through Qt Quick.
Production stack
| Piece | Technology |
|---|---|
| Language | C++20 |
| UI | Qt 6, Qt Quick, QML |
| Layer surfaces | LayerShellQt |
| Installed apps | core/applications (import Nebula.Applications) |
| Build | CMake |
| Shared design system | packages/nebula-ui (import Nebula.UI) |
The executable is nebula-shell.
QML module URI: Nebula.Shell.
Surfaces
Independent Wayland layer-shell windows:
| Surface | Layer | Role |
|---|---|---|
| Desktop | BACKGROUND |
Fills the current output, no exclusive zone, no keyboard focus |
| Top Bar | TOP |
40 logical pixels, exclusive zone, spans the output width |
| Launcher | OVERLAY |
Covers the output below the top bar while open; hidden otherwise |
Stacking:
OVERLAY
Nebula Launcher
TOP
Nebula Top Bar
NORMAL WAYLAND WINDOWS
Foot, Firefox, applications, games, …
BACKGROUND
Nebula Desktop
Surfaces follow the live Wayland output geometry from Qt (OutputTracker). They do not assume 1920×1080 or other fixed desktop sizes. If VMware resizes the display, the shell updates.
Applications
core/applications discovers freedesktop .desktop entries from XDG data directories:
$XDG_DATA_HOME/applications(default~/.local/share/applications)$XDG_DATA_DIRS/applications(default/usr/local/share:/usr/share)
Launcher-visible apps respect Hidden=true, NoDisplay=true, OnlyShowIn / NotShowIn, and TryExec when present. Localized Name values are used when available.
Launching uses QProcess::startDetached after QProcess::splitCommand. Exec field codes (%f, %F, %u, %U, %i, %c, %k, %%, …) are handled for launches without file arguments. Desktop-entry strings are never passed to a shell.
Search matches name, generic name, comment, and categories. Searching term should find Foot (GenericName=Terminal). Searching firefox should find Firefox if it is installed.
Application icons use the system icon theme through Qt (QIcon::fromTheme). Unresolved icons use a Nebula fallback glyph, not a broken-image placeholder.
The top-bar context text stays Desktop in this milestone. Launching an app is not treated as focus; real window tracking comes next.
Mock launcher data remains only as a fallback when no installed applications are discovered.
Layout
shells/desktop/
├── README.md this file
├── shell/ production Qt/QML shell
│ ├── CMakeLists.txt
│ ├── src/
│ └── qml/
└── prototype-react/ archived design reference only
Shared visual language lives in packages/nebula-ui. Application discovery lives in core/applications.
Linux build
This project will not compile on Windows. LayerShellQt is a Wayland API and is not stubbed.
On the Ubuntu NebulaOS VM:
sudo apt install \
qt6-base-dev \
qt6-declarative-dev \
qt6-wayland \
liblayershellqtinterface-dev \
qml6-module-org-kde-layershell \
qml6-module-qtquick \
qml6-module-qtquick-window \
adwaita-icon-theme \
cmake \
build-essential
Qt 6.4 or newer is required. loadFromModule is used on Qt 6.5+.
cd shells/desktop/shell
rm -rf build
cmake -S . -B build
cmake --build build -j$(nproc)
The production binary is shells/desktop/shell/build/nebula-shell. It does not force QT_QUICK_BACKEND=software. GPU-accelerated Qt Quick is the production default.
QML lint
If the installed Qt provides qmllint:
qmllint \
shells/desktop/shell/qml/*.qml \
shells/desktop/shell/qml/surfaces/*.qml \
shells/desktop/shell/qml/components/*.qml \
shells/desktop/shell/qml/mock/*.qml \
packages/nebula-ui/qml/Nebula/UI/*.qml
Do not use ESLint or Oxlint on the production Qt shell.
Development session (VMware)
From the repository root, the intended flow is:
sway -c compositor/sway/nebula-sway.conf
or, recommended on the VM (sets cursor env and starts Sway from the repo root):
sh compositor/sway/dev/run-sway-vm.sh
That should:
- start Sway without a Sway bar
- autostart
nebula-shellthroughscripts/run-nebula-vm.sh - fill the current output
- show a normal Adwaita 24px cursor
- let the Nebula launcher start Foot and other installed apps
Skip autostart while debugging the compositor:
NEBULA_SHELL_AUTOSTART=0 sway -c compositor/sway/nebula-sway.conf
VMware software rendering
The VMware virtual GPU currently cannot run the Qt Quick accelerated path correctly. The development wrapper therefore sets:
QT_QUICK_BACKEND=software
That variable is not a production default. scripts/run-nebula-vm.sh is the only place that sets it automatically. Production nebula-session / nebula-shell must run GPU-accelerated.
Manual equivalent, if you start the shell yourself:
QT_QUICK_BACKEND=software ./build/nebula-shell
Output / resolution
The development Sway config uses scale 1 and does not pin a resolution. Sway uses the output's preferred mode so the session follows the current display size.
Inspect outputs and modes:
swaymsg -t get_outputs
Set a mode explicitly if needed:
swaymsg output <name> mode 1600x900
swaymsg output <name> scale 1
In VMware, also set the guest display (or Autofit Guest) so the advertised modes match the host window.
Cursor
Sway owns the cursor:
XCURSOR_THEME=Adwaita
XCURSOR_SIZE=24
seat * xcursor_theme Adwaita 24
Install adwaita-icon-theme if the theme is missing. A branded Nebula cursor is a later task. WLR_NO_HARDWARE_CURSORS=1 is set only by the VMware Sway wrapper.
Emergency shortcuts
These remain for development fallback. They are not the normal UX once the GUI launcher works.
| Shortcut | Action |
|---|---|
| Super+Enter | open Foot |
| Super+Shift+E | exit Sway |
| Super+Shift+C | reload Sway config |
Design reference
prototype-react/ is an archived React/Vite UI. It is not the production shell. Keep it until the Qt shell reaches visual and behavioural parity, then remove it in a later cleanup.
Out of scope
Not implemented in this milestone:
- Sway IPC / focused-window tracking
- workspaces, window overview, dock
- PipeWire, NetworkManager, BlueZ, UPower
- notifications, XDG portals, Polkit
- login/lock screen, greetd
- a custom Nebula compositor
- Nebula Bigscreen
- Gamescope
Those come later. Bigscreen is expected to reuse Nebula.UI and Nebula.Applications.