exportd

MixOS keeps every desktop setting in settingsd. A desk that runs a foreign desktop (COSMIC, Plasma) or foreign toolkits (GTK, Qt) gets the same look through an exporter: a Mix Bus citizen that follows the settings topics and writes the foreign desktop's own config. The export is one way. settingsd is the source; a change made in the foreign desktop's own settings app is overwritten at the next revision, and nothing is read back.

Every exporter:

  • reconciles once when it connects, then writes each revision's changed keys in one burst, with no polling;
  • writes only the keys it owns, and only where the contents differ;
  • keeps the contents it replaced, so one command undoes it;
  • stops writing while its gate is false: appearance.export_desktop for the desktops, appearance.export_toolkits for GTK and Qt.

Colours and fonts come from settings.palette, the derived palette settingsd computes; an exporter names them and computes none.

COSMIC

scripts/cosmic.mix writes a COSMIC desktop's cosmic-config tree ($XDG_CONFIG_HOME/cosmic, one RON file per key). cosmic-comp, the panel and every libcosmic app watch those files, so the desktop follows at once.

mix --serve <prefix>/lib/exportd/cosmic.mix --name exportd-cosmic

The unit exportd-cosmic@.service runs it in a session under the mixos account. A session using another account needs a User= drop-in; the template is not enabled or started automatically.

What it writes

Settingscosmic-config
appearance.mode (resolved; auto is settingsd's)com.system76.CosmicTheme.Mode v1 is_dark; auto_switch always false
the palette, appearance.contrast, accent, tints, backgroundsboth builders (com.system76.CosmicTheme.Dark.Builder, .Light.Builder v2) and both derived themes (.Dark, .Light v2): every container, component, the 32-colour palette, accent text, shade
appearance.roundness, appearance.densityboth builders' and themes' corner_radii, spacing; com.system76.CosmicTk v1 interface_density, header_size; the panel and dock radius, padding and spacing
appearance.frosted.*frosted, alpha_map, frosted_windows, frosted_system_interface, frosted_panel, frosted_applets
window.active_hint, window.hint_colour, window.gapsactive_hint, window_hint, gaps (the inner gap at least the hint width)
window.hint_shown, window.clip_*, window.shadow_tiled, window.edge_gravity, focuscom.system76.CosmicComp v1 active_hint, appearance_settings, edge_snap_threshold, focus_follows_cursor, focus_follows_cursor_delay, cursor_follows_focus
appearance.font_family, mono_family, icon_theme, window.controls.*CosmicTk v1 interface_font, monospace_font, icon_theme, show_minimize, show_maximize; apply_theme_global always false (MixOS's own GTK and Qt export does that work)
shell.panels.top, .bottomcom.system76.CosmicPanel.Panel and .Dock v1, and which of them com.system76.CosmicPanel v1 entries lists
wallpaper.*com.system76.CosmicBackground v1 all, same-on-all and one key per output

The fonts are the palette's too: the families MixOS draws, the session's choice or the bundled Inter and JetBrains Mono (COSMIC takes the family alone; it has no size setting). Panel applet lists and the wallpaper are written only once MixOS has its own; until then COSMIC keeps its own.

Each key is replaced atomically (a .atomicwrite temporary, then a rename), builders before themes and the mode last, so a mode flip lands on two complete themes.

Commands

CommandRequest and reply
exportd.cosmic.status→ {binding, root, revision, paused, dry_run, last}
exportd.cosmic.plan→ {changed: [path], unchanged}: what an export would write; writes nothing
exportd.cosmic.reconcileexport now; clears a pause
exportd.cosmic.undo{to: "previous" | "original"} → {restored, to, paused}: every key back to its contents before the latest write, or before the first; exporting then pauses until reconcile
exportd.cosmic.owned→ {paths}: every key file it has written

Environment

VariableDefault
MIXOS_SETTINGS_INSTANCE, MIXOS_SETTINGS_PROFILEthe hostname, default (the profile must be default)
MIXOS_SETTINGS_SERVICEsettingsd
MIXOS_EXPORTD_COSMIC_ROOT$XDG_CONFIG_HOME/cosmic
MIXOS_EXPORTD_STATE$XDG_STATE_HOME/mixos/exportd/cosmic (undo and pause)
MIXOS_EXPORTD_DRY_RUN=1plan only: log what would change, write nothing

KDE Plasma

scripts/kde.mix runs in the Plasma user's session as exportd-kde:

mix --serve <prefix>/lib/exportd/kde.mix --name exportd-kde

The user unit exportd-kde.service starts it with the graphical session, only when the session is Plasma (XDG_SESSION_DESKTOP=KDE).

It reconciles once at start, then once per settings revision that moves appearance, window, accessibility or shell.panels: a revision that moves several of them delivers one topic each, and the deliveries of a revision already reconciled are skipped. A reconcile reads settings.get and, only when an appearance.* value changed, settings.palette.

What it writes

settingsKDE
the resolved mode (resolved.mode of settings.get; never auto)kdeglobals [General] ColorScheme=MixOS-Dark or MixOS-Light, and the scheme's [Colors:*], [ColorEffects:*] and [WM] groups; both modes also as <data>/color-schemes/MixOS-{Dark,Light}.colors
the accent and colour axes, through the palette[General] AccentColor (accentColorFromWallpaper=false), [Colors:Selection], every scheme colour
the palette's fonts, accessibility.text_scale[General] font, menuFont, toolBarFont, fixed, [WM] activeFont
appearance.icon_theme[Icons] Theme, the -dark variant in dark mode when installed
window.hint_colour[WM] activeBackground, activeForeground
accessibility.reduced_motion[KDE] AnimationDurationFactor (0 when reduced, else 1)
appearance.densitykwinrc [org.kde.kdecoration2] BorderSize (Tiny, Normal, Large), BorderSizeAuto=false
window.caption_side, window.controls.*kwinrc [org.kde.kdecoration2] ButtonsOnLeft, ButtonsOnRight
shell.panels.<panel>.gap (enabled panels)floating of the Plasma panel on that panel's edge (the Plasma scripting interface); panels on other edges are left alone

The decoration plugin and theme (library, theme) are the user's and are never written; the keys above apply to whichever decoration is active.

Scheme colours are the palette's opaque values; a translucent one is refused. A font size in pixels becomes points at 96 dpi (× 0.75, to the nearest half point), after the text scale. kdeglobals [MixOS] carries a stamp with the revision written; the stamp alone never causes a write. A key the user made immutable (key[$i]) is not written, and the report names it (immutable: <file>|<group>|<key>).

How it writes

Keys are matched by their raw text, so Icon[de] or font[$i] is never mistaken for Icon or font. Each file is re-read, edited as text and replaced atomically while holding KConfig's own lock (<file>.lock), so a concurrent KConfig writer loses nothing. The last key of each group, and every font, scheme, accent, icon and animation key, then goes through kwriteconfig6 --notify, so KConfig's change notification reaches each group. Then busctl --user sends the KGlobalSettings notifyChange signal (palette, fonts), KIconLoader iconChanged, and org.kde.KWin.reconfigure; a call that fails is reported as <name>_unreachable. What a burst touched is recorded as dirty before the first write and cleared once every call succeeded, so after a crash or an unreachable session bus the next run notifies again.

Known limit: running apps follow a colour, icon and decoration change at once but keep their old font until they restart; apps started afterwards get the new font.

Not exported yet (key space §5.3 lists them for kde-export; the next slice): wallpaper (desktop containments), workspaces (kwinrc [Desktops], RollOverDesktops), input (kxkbrc, kcminputrc), time (clock use24hFormat, showDate), region, keymaps (ModifierOnlyShortcuts), the window behaviour rows (focus policy, snap zones, Super key) and appearance.frosted.* (KWin blur). The behaviour rows wait for a decision, since they change how the desktop behaves, not how it looks.

Commands

CommandDoes
exportd.kde.statusthe binding, last revision, gate, pause, the last run's report or error
exportd.kde.planwhat a reconcile would write now; writes nothing
exportd.kde.reconcilereconcile now; clears a pause
exportd.kde.undoput back the keys and panels the last burst changed, then pause
exportd.kde.restoreput back every key as it was before the first write, the scheme files (a user's own MixOS-*.colors gets its saved bytes back; files it created are removed) and the panels, forget the record, then pause

While paused (undo or restore, kept across restarts) no revision writes; exportd.kde.reconcile resumes.

Environment

VariableDefault
MIXOS_SETTINGS_INSTANCEthis node's name
MIXOS_SETTINGS_SERVICEsettingsd; the read endpoint (reserved settings topics still come from settingsd)
MIXOS_KDE_EXPORT_DRY=1plan and report, write nothing
MIXOS_KDE_EXPORT_STATE$XDG_STATE_HOME/mixos/kde-export (the undo record, saved scheme files, the pause marker)
MIXOS_KDE_EXPORT_RECONFIGURE=0no D-Bus calls
MIXOS_KDE_EXPORT_KWRITECONFIG, MIXOS_KDE_EXPORT_BUSCTLkwriteconfig6, busctl; a JSON argv list replaces them (the tests' stand-ins)

External programs: kwriteconfig6 (KDE Frameworks 6) and busctl.

GTK and Qt

scripts/gtkqt.mix (--name exportd-gtkqt, unit exportd-gtkqt@.service) writes GTK CSS and settings.ini keys and the qt5ct/qt6ct colour schemes and config on every desk but a KDE Plasma one, where the KDE export covers GTK and Qt. Its commands are exportd.gtkqt.status, .reconcile and .undo. The GTK and Qt page has the details.

Changelog

See the changelog.

Both desktop exporters read all values and the palette at one incarnation and revision, retrying a moving record before writing. Prefix topics are hints, never snapshot fragments. KDE broadcasts use busctl emit OBJECT INTERFACE SIGNAL (no positional service). Wallpaper RGB triples refuse non-opaque alpha; KDE hint colours are composited over each mode\u0027s titlebar.

This page as Markdown · This page describes the current main branch of MixOS.