Add desktop app service and VM dev session tooling

Introduces a new `core/applications` QML/C++ module (`Nebula.Applications`) to discover `.desktop` entries, expose filterable application models, and launch installed apps safely from the shell. The desktop launcher now uses real installed apps with themed icon loading and fallback glyphs, adds output-aware surface sizing via `OutputTracker`, and wires in image/icon providers. It also adds VMware-focused Sway/shell wrapper scripts, updates Sway config/session env defaults, and refreshes README docs to document the new development flow and Desktop 0.1 behavior.
This commit is contained in:
2026-08-26 17:19:30 +12:00
parent a7d204f241
commit b0c95b9e78
31 changed files with 1673 additions and 72 deletions
+133 -34
View File
@@ -1,6 +1,8 @@
# Nebula Desktop
Production shell for the mouse-and-keyboard NebulaOS 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
@@ -34,25 +36,15 @@ Qt Quick/QML
Normal applications remain ordinary Wayland / XWayland clients of the compositor:
```text
Normal Application
Nebula Launcher
ApplicationService
application process
Wayland / XWayland
Compositor
GPU
```
Nebula chrome:
```text
QML
Qt Quick
Wayland Layer Shell
Compositor
Sway
```
Games, browsers, terminals, and other apps never pass through Qt Quick.
@@ -64,6 +56,7 @@ Games, browsers, terminals, and other apps never pass through Qt Quick.
| 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`) |
@@ -77,9 +70,9 @@ Independent Wayland layer-shell windows:
| Surface | Layer | Role |
|---------|-------|------|
| Desktop | `BACKGROUND` | Wallpaper / desktop, no exclusive zone, no keyboard focus |
| Top Bar | `TOP` | 40px bar, exclusive zone, no keyboard focus by default |
| Launcher | `OVERLAY` | Hidden until the Nebula button opens it |
| 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:
@@ -91,12 +84,33 @@ TOP
Nebula Top Bar
NORMAL WAYLAND WINDOWS
Firefox, Steam, Blender, games, terminals, …
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:
1. `$XDG_DATA_HOME/applications` (default `~/.local/share/applications`)
2. `$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
```text
@@ -104,12 +118,12 @@ shells/desktop/
├── README.md this file
├── shell/ production Qt/QML shell
│ ├── CMakeLists.txt
│ ├── src/main.cpp
│ ├── src/
│ └── qml/
└── prototype-react/ archived design reference only
```
Shared visual language lives in `packages/nebula-ui`, not in Desktop-specific chrome.
Shared visual language lives in `packages/nebula-ui`. Application discovery lives in `core/applications`.
## Linux build
@@ -126,6 +140,7 @@ sudo apt install \
qml6-module-org-kde-layershell \
qml6-module-qtquick \
qml6-module-qtquick-window \
adwaita-icon-theme \
cmake \
build-essential
```
@@ -135,14 +150,12 @@ Qt 6.4 or newer is required. `loadFromModule` is used on Qt 6.5+.
```bash
cd shells/desktop/shell
rm -rf build
cmake -S . -B build
cmake --build build -j$(nproc)
./build/nebula-shell
```
Run inside a Wayland compositor such as Sway (`compositor/sway/nebula-sway.conf`). Sway must not show its own bar.
There is no npm server, Vite server, `NEBULA_SHELL_DEV_URL`, WebKitGTK, GTK4, or React involved in running the production shell.
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
@@ -159,6 +172,91 @@ qmllint \
Do not use ESLint or Oxlint on the production Qt shell.
## Development session (VMware)
From the repository root, the intended flow is:
```bash
sway -c compositor/sway/nebula-sway.conf
```
or, recommended on the VM (sets cursor env and starts Sway from the repo root):
```bash
./compositor/sway/dev/run-sway-vm.sh
```
That should:
1. start Sway without a Sway bar
2. autostart `nebula-shell` through `scripts/run-nebula-vm.sh`
3. fill the current output
4. show a normal Adwaita 24px cursor
5. let the Nebula launcher start Foot and other installed apps
Skip autostart while debugging the compositor:
```bash
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:
```bash
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:
```bash
QT_QUICK_BACKEND=software ./build/nebula-shell
```
### Output / resolution
The development Sway config requests `1920x1080` at scale `1` when that mode exists. If the VM does not advertise it, Sway keeps the preferred mode. This is a development default, not a production requirement.
Inspect outputs and modes:
```bash
swaymsg -t get_outputs
```
Set a mode explicitly if needed:
```bash
swaymsg output <name> mode 1920x1080
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:
```text
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.
@@ -167,12 +265,13 @@ Do not use ESLint or Oxlint on the production Qt shell.
Not implemented in this milestone:
- Sway IPC / focused-window tracking
- workspaces, window overview, dock
- PipeWire, NetworkManager, BlueZ, UPower
- D-Bus services
- real `.desktop` discovery or app launching
- Sway IPC, workspaces, notifications
- authentication, greetd, Gamescope
- 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 shared C++ services with a different interaction model.
Those come later. Bigscreen is expected to reuse `Nebula.UI` and `Nebula.Applications`.