Add native Wayland shell host scaffolding
Introduce a Linux-only C++20 `nebula-shell` host built with CMake, GTK4, gtk4-layer-shell, and WebKitGTK 6.0. Add a shared `Surface` abstraction and concrete Desktop/TopBar/Launcher surfaces with layer placement, sizing, transparency, and per-surface `?surface=` URL loading. Add `ShellApplication` startup flow that validates `NEBULA_SHELL_DEV_URL`, checks layer-shell support, creates/presents desktop and top bar, and keeps launcher created but hidden for future bridge-driven toggling. Update the native README to document architecture, development setup, dependencies, and current milestone scope.
This commit is contained in:
@@ -1,34 +1,55 @@
|
||||
# Nebula Desktop native host
|
||||
|
||||
This directory will contain the Linux native shell host for Nebula Desktop.
|
||||
`nebula-shell` hosts the React-based Nebula Desktop chrome as real Wayland Layer Shell surfaces.
|
||||
|
||||
The host is a C++ process that creates **several** GTK4 windows, each backed by WebKitGTK 6.0 and placed with **gtk4-layer-shell**. It does not composite application windows. Those remain ordinary Wayland / XWayland clients of the compositor.
|
||||
It is a C++ process that creates **several** GTK4 windows, each backed by WebKitGTK 6.0 and placed with **gtk4-layer-shell**. It does not composite application windows. Those remain ordinary Wayland / XWayland clients of the compositor.
|
||||
|
||||
Planned stack:
|
||||
Stack:
|
||||
|
||||
- C++
|
||||
- C++20
|
||||
- GTK4
|
||||
- gtk4-layer-shell
|
||||
- WebKitGTK 6.0
|
||||
- CMake
|
||||
- Wayland
|
||||
|
||||
## Surfaces
|
||||
This host is Linux-only. It will not compile on Windows.
|
||||
|
||||
The React UI in `shells/desktop/ui` can render each shell layer independently via a query parameter. The native host will create one WebKitGTK view per surface and append that parameter to the load URL.
|
||||
## Purpose
|
||||
|
||||
Nebula Desktop's React UI is shell chrome only: wallpaper/desktop, top bar, and launcher. `nebula-shell` maps each of those to a Wayland layer surface so they sit in the compositor like a real desktop environment, not like a browser window.
|
||||
|
||||
Normal applications and games never render inside WebKit.
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
Nebula Shell
|
||||
├── DesktopSurface
|
||||
│ ├── WebKitGTK
|
||||
│ ├── ?surface=desktop
|
||||
│ └── BACKGROUND
|
||||
│
|
||||
├── TopBarSurface
|
||||
│ ├── WebKitGTK
|
||||
│ ├── ?surface=topbar
|
||||
│ └── TOP
|
||||
│
|
||||
└── LauncherSurface
|
||||
├── WebKitGTK
|
||||
├── ?surface=launcher
|
||||
└── OVERLAY
|
||||
```
|
||||
|
||||
| Surface | Query | Layer Shell layer | Placement |
|
||||
|-----------|-----------------------|-------------------|-----------|
|
||||
| Desktop | `?surface=desktop` | `BACKGROUND` | Anchored to all monitor edges |
|
||||
| Top Bar | `?surface=topbar` | `TOP` | Anchored top/left/right, fixed shell-defined height, exclusive zone enabled |
|
||||
| Launcher | `?surface=launcher` | `OVERLAY` | Hidden/unmapped when closed |
|
||||
|
||||
The host must not hardcode a Vite port. It loads a base URL and appends the surface parameter.
|
||||
|
||||
## Stacking
|
||||
| Top Bar | `?surface=topbar` | `TOP` | Anchored top/left/right, 40px (matches the React top bar), exclusive zone enabled |
|
||||
| Launcher | `?surface=launcher` | `OVERLAY` | Created at startup, hidden/unmapped until later toggle work |
|
||||
|
||||
```text
|
||||
OVERLAY
|
||||
Nebula Launcher
|
||||
Nebula Launcher ← created, currently unmapped
|
||||
notifications
|
||||
OSDs
|
||||
|
||||
@@ -43,26 +64,36 @@ BACKGROUND
|
||||
wallpaper
|
||||
```
|
||||
|
||||
React draws **shell chrome only**. Applications and games render through the compositor, not through WebKit.
|
||||
React draws **shell chrome only**. Applications and games render through the compositor, not through WebKit:
|
||||
|
||||
```text
|
||||
Application / game
|
||||
→ Wayland / XWayland
|
||||
→ compositor
|
||||
→ GPU
|
||||
App
|
||||
↓
|
||||
Wayland / XWayland
|
||||
↓
|
||||
Compositor
|
||||
↓
|
||||
GPU
|
||||
```
|
||||
|
||||
## Loading modes
|
||||
## Development
|
||||
|
||||
### Development
|
||||
The host must not hardcode a Vite host or port. It reads a base origin from the environment and appends the surface query parameter.
|
||||
|
||||
The host will load a configurable Vite origin, conceptually:
|
||||
Start the React UI (the port is an example, not part of the native architecture):
|
||||
|
||||
```text
|
||||
NEBULA_SHELL_DEV_URL=http://127.0.0.1:5174
|
||||
```bash
|
||||
cd shells/desktop/ui
|
||||
npm run dev -- --host 127.0.0.1 --port 5174 --strictPort
|
||||
```
|
||||
|
||||
and request:
|
||||
Point the shell at that origin:
|
||||
|
||||
```bash
|
||||
export NEBULA_SHELL_DEV_URL=http://127.0.0.1:5174
|
||||
```
|
||||
|
||||
That loads:
|
||||
|
||||
```text
|
||||
{NEBULA_SHELL_DEV_URL}/?surface=desktop
|
||||
@@ -70,22 +101,46 @@ and request:
|
||||
{NEBULA_SHELL_DEV_URL}/?surface=launcher
|
||||
```
|
||||
|
||||
The port is environment configuration, not architecture. A combined browser preview (`/?surface=preview`, also the default) is for Windows UI work only; the native host should load the three surfaces separately.
|
||||
Build:
|
||||
|
||||
### Production
|
||||
```bash
|
||||
cd shells/desktop/native
|
||||
|
||||
The host will load the compiled assets from `npm run build` in `shells/desktop/ui/dist`, still using the same `?surface=` parameters. It will not depend on a Vite server.
|
||||
cmake -S . -B build
|
||||
cmake --build build -j$(nproc)
|
||||
```
|
||||
|
||||
Run inside a Wayland compositor such as Sway:
|
||||
|
||||
```bash
|
||||
./build/nebula-shell
|
||||
```
|
||||
|
||||
On startup the desktop and top bar are presented. The launcher is created but left hidden.
|
||||
|
||||
A combined browser preview (`/?surface=preview`, also the default) is for Windows UI work only. The native host loads the three surfaces separately.
|
||||
|
||||
### Linux packages
|
||||
|
||||
Build dependencies (install on the Ubuntu machine, not from this tree):
|
||||
|
||||
```text
|
||||
libgtk-4-dev
|
||||
libgtk4-layer-shell-dev
|
||||
libwebkitgtk-6.0-dev
|
||||
pkg-config
|
||||
cmake
|
||||
build-essential
|
||||
```
|
||||
|
||||
## Production
|
||||
|
||||
Production loading from bundled `shells/desktop/ui/dist` files is not implemented yet. This milestone is development-URL loading only.
|
||||
|
||||
## Shell bridge
|
||||
|
||||
Each WebKit view talks to the host through a JavaScript bridge (`shellBridge` in the React UI). The development build uses an in-page mock. The native host will inject the real implementation so the top bar can toggle the launcher, and so launching an app can unmap the overlay and update the top-bar context.
|
||||
|
||||
`nebula.*` remains the API boundary for system services (apps, audio, network, power). Those stay mocked until Linux services land.
|
||||
Not implemented in this milestone. Each WebKit view will later talk to the host through a JavaScript bridge (`shellBridge` in the React UI) so the top bar can map and unmap `LauncherSurface`.
|
||||
|
||||
## Current development compositor
|
||||
|
||||
Sway is the temporary compositor. See `compositor/sway/nebula-sway.conf`.
|
||||
|
||||
## Build notes
|
||||
|
||||
This host is Linux-only. Do not expect it to compile on Windows. Implementation is the next Linux-side task.
|
||||
|
||||
Reference in New Issue
Block a user