Aurora's own desktop
The shell written in Python and GTK 4: top bar, dock, Spotlight, notifications, Quick Look and more.
Language: Python 3 (PyGObject)
- Alternatives: C, Vala, Rust (gtk-rs), JavaScript (GJS, AGS/Astal), Qt/QML.
- Why: Python makes the desktop small (about 10,000 lines), readable and easy to contribute to. Every part is a plain
.pyfile you can change and restart. The expensive work happens in C libraries (GTK, GLib, wlroots), so Python is not the bottleneck. Rust or C would be faster to run but much slower to write and change. JS shell frameworks (AGS/Astal) are elegant but not packaged in Debian.
Toolkit: GTK 4 + libadwaita
- Alternatives: Qt 6/QML, Iced, Flutter, web technology (Electron/Tauri).
- Why: GTK 4 is GPU-accelerated and accessible, and it supports Wayland first. libadwaita adds modern widgets (adaptive layouts, toasts, dialogs), dark mode and accent colors. Most of the preinstalled apps already use it, so the whole system looks like one product. Web technology would cost hundreds of megabytes of RAM for a panel.
Desktop surfaces: gtk4-layer-shell
- Why: it turns ordinary GTK windows into Wayland layer surfaces (panels, docks, overlays, the background) through the standard
wlr-layer-shellprotocol.
Window list: pywayland + wlr-foreign-toplevel-management
- Why: GTK can't see other apps' windows. The dock and the menu bar open a second Wayland connection with pywayland and use this protocol to list, focus, minimize and close windows. Bindings are generated from the protocol XML at build time.
- Lesson learned (tests caught it): keep the registry and every handle referenced, or Python's garbage collector frees them.
One process, many surfaces
- Choice: wallpaper, menu bar, dock, Spotlight/Launchpad, notifications, OSD, tray and Control Center all live in one
aurora-shellprocess. Commands such asaurora-shell launcheroraurora-shell volume upreach it through GApplication's single-instance D-Bus mechanism, which is how keybindings talk to it. - Why: one Python interpreter instead of eight saves memory and keeps state (window list, settings) in one place.
Notifications: Aurora's own server
- Alternatives: mako, dunst, SwayNotificationCenter.
- Why: our own implementation of the freedesktop spec (actions, markup, urgency, replace, history) integrates with the menu bar, the calendar popover and Do Not Disturb. Its markup sanitizer is unit-tested. SwayNotificationCenter isn't in Debian.
System tray: our own StatusNotifierItem host
- Why: modern tray icons (Discord, Slack, Steam, Nextcloud…) use StatusNotifierItem and dbusmenu over D-Bus. Aurora provides the watcher and draws items and menus natively.
- Lesson learned: PyGObject's
bus_watch_namecan report spurious "vanished" events, so we listen toNameOwnerChangeddirectly.
- Lesson learned: PyGObject's
Search: Spotlight-style providers in Python
- Why: apps, settings pages (by keywords), recent files, a safe calculator (an AST walker, never
eval), commands (> …) and web search. They are simple, fast and all covered by tests. - Conversions (
aurora/convert.py): length, mass, volume, time, speed, data (SI and binary), area, energy and temperature, offline. Currencies use the European Central Bank's daily reference rates: a public XML file with no key or account, fetched only when you type a currency conversion and cached for 12 hours. Commercial APIs need keys and track usage. - Emoji (
:prefix): the names come from Python's own Unicode database, so there is no extra data file to ship or update. - Projects: git repositories up to two levels inside
~/Projects,~/src,~/code,~/git,~/dev,~/workand similar, with the current branch. Enter opens the project in VS Code, VSCodium, Zed or Sublime if installed, otherwise a terminal there. - Clipboard history (
clip:prefix, <kbd>Super</kbd>+<kbd>V</kbd>): see below.
Clipboard history: wl-paste --watch + a tiny store
- Alternatives: cliphist, clipman, CopyQ, GPaste.
- Why: Wayland only shows the clipboard to the focused app, but the
wlr-data-controlprotocol lets a helper watch it. The shell runswl-paste --type text --watch aurora-clipboard store, andaurora/clipboard.pykeeps the last 100 text entries (up to 64 KB each) in~/.local/share/aurora/clipboard.json, readable only by you. Password managers mark their copies as sensitive and wl-paste passes that on, so passwords are never stored. cliphist would work too, but it adds a Go binary for what is about 60 lines of Python, and it doesn't skip sensitive entries. CopyQ and GPaste bring their own UIs, and ours is Spotlight. Turn it off or clear it in Settings → Privacy.
Quick Look: GTK widgets per file type
- Alternatives: GNOME Sushi (needs Nautilus and GJS), opening the default app.
- Why: a small GTK 4 window (
aurora/quicklook.py) with the right widget for each type:Gtk.Picturefor images;Gtk.Video(GStreamer,libgtk-4-media-gstreamer) for video and audio;- Poppler for the first pages of a PDF;
- GtkSourceView 5 for text and code, with syntax highlighting in the light or dark scheme;
- a folder summary, and for anything else a card with its thumbnail and details.
- Files opens it with <kbd>Space</kbd>; arrow keys move the selection in Files and the preview follows.
aurora-quicklook FILE…works from anywhere.
Sun position without a location service (aurora/sun.py)
- Alternatives: GeoClue (Wi-Fi-based location through an online service), asking the user for a city, fixed hours.
- Why: the dynamic wallpaper, automatic dark style and Night Light only need sunrise and sunset. The time zone you picked in the installer already says roughly where you are: tzdata's
zone1970.tabgives coordinates for every zone. The NOAA solar formulas then give the sun's elevation within a few minutes. Nothing leaves the computer and nothing asks for permission. Unit tests compare against known sunrise and sunset times.
Dynamic wallpaper and automatic dark style (shell/daycycle.py)
- Alternatives: GNOME's XML slideshows (fixed clock times), HEIC dynamic wallpapers (macOS).
- Why: three series of four pictures of the same landscape (dawn, day, dusk, night) live in
branding/wallpapers/SERIES/; Settings → Appearance picks the series. Once a minute the shell checks the sun's elevation: night below −6°, dawn and dusk up to 8°, day above. On a change, the new picture fades in over 2.5 seconds. Following the sun rather than the clock means winter evenings get dark when it's actually dark. The shell keeps~/.cache/aurora/wallpaperpointing at the current picture for the lock screen, and the login screen computes the same phase. "Auto" dark style flips the system color scheme at sunset and sunrise. Choosing Dark Style by hand in the Control Center turns Auto off.
Overview and hot corners
- Alternatives: labwc's built-in window switcher only, a GNOME-style overview with live thumbnails.
- Why: the overview (
shell/overview.py) is a full-screen layer with a card per window from the foreign-toplevel list, over a blurred screenshot of the desktop (grim at half scale, shrunk and scaled back up: a cheap blur with no GPU code). labwc 0.8 doesn't let other programs capture single windows yet, so cards show the app icon and title instead of live thumbnails. Hot corners (shell/hotcorners.py) are 2×2-pixel transparent layer surfaces in the corners. The pointer must rest there for 120 ms, which avoids triggers on fast passes, and there is a short cooldown afterwards. - Quarters and thirds are labwc snap regions defined in
rc.xml, so they work with keyboard shortcuts and by holding a modifier while dragging.
Weather in the calendar: Open-Meteo
- Alternatives: libgweather/GNOME Weather's providers (MET Norway), OpenWeatherMap (needs an API key).
- Why: Open-Meteo is free, open data and needs no key or account. The shell asks for the weather at your time zone's main city (never an exact position), at most every 30 minutes, only when you open the calendar. Fahrenheit is used where it is the local convention. Settings → Privacy turns it off.
Desktop widgets (shell/widgets.py, shell/devwidgets.py)
- Alternatives: separate layer-shell windows per widget, a web view (as KDE's or macOS's widgets are closer to), conky.
- Why: each widget is a GTK widget on the wallpaper surface, under the windows, so twenty-three of them cost one surface. Positions are saved as fractions of the screen and sizes follow its height, so a resolution change keeps the layout. Dragging is handled by the desktop surface, not by the widget: a gesture on a moving widget shifts its own coordinates at every step and makes it jump. A widget is a small slot that measures itself (a
Gtk.Boxcan't: its layout manager ignoresmeasure), holding the visible card. Anything that runs a program (git, podman, nvidia-smi, ss) runs in a thread. In Wayfire, floating windows are scaled into the new work area after a resolution change through its IPC (shell/refit.py); labwc moves them back on screen itself.
Session sounds: synthesized, not sampled
- Alternatives: freedesktop's sound theme, recorded samples, no sounds (Fedora, Debian).
- Why: the startup and shutdown sounds are generated by
branding/sounds/generate.pywith numpy, like the rest of the artwork. They aim for the warm, organic feel of Ubuntu's sounds without copying them. A synthesized marimba (modal synthesis of a wooden bar: tuned partials at 1 : 3.93 : 9.2 that fade at different rates, plus the soft click of the mallet) plays a short motif in D major over a round bass note.- Startup rises and resolves on a chord held by a soft felt pad.
- Shutdown walks back down and fades.
- A short room reverb adds space. There are no samples and no licensing questions, and the sounds can be changed by editing code. They play with
pw-play(PipeWire). The shell waits 1.8 s for the shutdown sound before powering off, restarting or logging out. Both are on by default and switch off together in Settings → Sound.