·
27 commits
to main
since this release
Immutable
release. Only release title and notes can be modified.
PlasmaZones v3.1.0
Added
- Window Rules: a new unified settings page replaces the old Snapping Assignments, Tiling Assignments, Animations App Rules, and per-mode "disabled apps" lists. Rules are browsed, added, edited, drag-reordered, duplicated, and disabled from one place. Matching composes class, title, role, app-id, virtual desktop, activity, and screen predicates with AND/OR/NOT. A rule's actions cover snapping/tiling assignment, animation-curve and shader overrides, and exclusion from snapping, autotile, and effects, the four surfaces that previously each had their own editor.
org.plasmazones.WindowRulesD-Bus interface (dbus/org.plasmazones.WindowRules.xml):getAllRules,setAllRules,addRule,removeRule, and related lifecycle methods for programmatic rule management.phosphor-window-rulesLGPL-2.1+ library housing the rule model, parser, andRuleEvaluator, so third parties can link the matcher without inheriting GPL.- Snapping focus behavior: two new opt-in toggles on Snapping → Window → Behavior (both default off). focus new windows auto-activates a window when it is auto-placed into a zone on open, and focus follows mouse activates the snapped window under the cursor. Brings snapping to parity with the existing autotile focus options.
- Zone span toggle mode (#563): an opt-in switch in the Zone Span card so the span modifier can be tapped to start/stop spanning instead of held down for the whole drag (default off, motivated by accessibility).
- Restore floated window positions on login (#606): floated windows are now restored to the monitor and position they closed on after a KWin session restore (previously only snapped windows were restored cross-screen). Parallel per-engine toggles (both default on), restore unsnapped windows under Snapping → Window → Behavior and restore untiled windows under Tiling → Behavior, plus an engine-neutral per-window
RestorePositionrule action let specific windows opt in or out for either mode. - Per-window appearance for snapping: snapping gains its own border, corner-radius, hide-title-bar, and accent-color settings, mirroring tiling, and the former "Snapping → Appearance" page is renamed Zones. Window restore state across daemon restart and logout/login is now backed by a single
WindowPlacementStoreinstead of several overlapping mechanisms. - New window-rule actions for window chrome: per-window border, title-bar, corner-radius, accent-color, gap/padding, and opacity overrides, applied to snapped or floating windows (e.g. "floating windows on monitor 2 → no title bar + red border", "activity Gaming → zero gaps").
- New window-rule match conditions:
IsTransient,IsNotification,Width,Height, andIsFocused, plus a built-in "Don't animate small windows" template.IsFocusedlets any action be focus-scoped (e.g. "WHEN NOT focused → dim"). - Collapsible settings sidebar categories with smart-expand of the active page's category and animated chevrons, cutting clicks to reach deep pages.
- Per-monitor scope map: per-monitor settings cards carry a scope chip that opens a spatial map of the real monitor arrangement to switch outputs, replacing the tall repeated monitor-selector block. The scope choice persists across pages.
- Shader-driven window-move/resize morph (
window-morph): window move, resize, snap, and layout-switch transitions animate as a smooth shader cross-fade geometry morph instead of a plain C++ paint transform, the default for window-move and overridable per event. Overlay show/hide (OSD, zone selector, layout picker, snap assist) now defaults to the shader-basedfadeeffect. - In-app live shader preview: the Snapping → Shaders browser gains an animated, interactive preview with mouse and audio input, shader presets (load/save, shared with the editor), and an in-app compile-error banner.
- Shader authoring API: authors write only the effect body and read parameters by name (
p_<id>) instead of decoding UBO slots, with a generated preamble and entry-point conventions, plus aplasmazones-shader-validateCLI (with--animation/--overlaymodes and did-you-mean diagnostics) wired into CI to catch broken packs offline. - dma-buf zero-copy window-preview transport for snap-assist thumbnails (opt-in via the
PLASMAZONES_DMABUF_THUMBNAILSenv var, with default builds unchanged), the foundation for live window previews. - Phosphor SDK groundwork: the reusable LGPL Phosphor library line gains a Phase 1 foundation tier (
phosphor-theme,phosphor-popout,phosphor-registry,phosphor-ipc+ thephosphorctldriver,phosphor-shellper-screen helper) and a Phase 2 system-service tier (phosphor-service-{pipewire,network,bluetooth,brightness,notifications,polkit,idle,clipboard,lock,session,upower,mpris,icontheme,sni}). All of it is gated behindBUILD_PHOSPHOR_SHELL(default off) and driveable only from standalone examples/CLIs. It is groundwork for the standalone Phosphor shell direction and is not part of the shipping PlasmaZones tiler. - Resize-aware tiling (#666): six split-ratio algorithms (master-stack, wide, focus-sidebar, zen, deck, and horizontal-deck) now reflow on interactive resize. Dragging the master or boundary edge updates the split ratio for that desktop the same way a master-ratio keystroke does, without bleeding into other screens or the global default. The Layouts page shows a per-algorithm Reflows badge and can group algorithms by it.
- Suppress default layout assignment (#676): a new setting, with a matching
DefaultLayoutAssignmentwindow-rule action, stops a context from falling back to the synthesized default layout. A suppressed context shows no snapping overlay or zone selector, reports no layout, and shows a "No layout assigned" OSD. Switching it into autotile sets the mode without applying the global default algorithm until a concrete one is assigned. - Layouts page rebuilt as a searchable, card-based catalogue with collapsible capability groups, per-layout deep links, and global-search reveal, matching the shader browser and Window Rules. Tiling algorithms expose a Script State capability (filter, group-by, and a card badge) alongside the reflow and persistent-memory badges, layout and algorithm cards show their description on hover, and bundled snapping layouts open in a text editor.
Changed
- Single rule format: window assignments, per-mode disable lists, animation App Rules, and effect exclusion lists are unified into one rule list stored in
~/.config/plasmazones/windowrules.json. The KWin effect now consults the sameRuleEvaluatoras the daemon for animation App-Rule resolution and exclusion checks, so the two cannot drift. LayoutRegistry::walkCascaderemoved, replaced byRuleEvaluator. The old per-axis cascade (context-keyed assignments vs window-property matching) no longer exists. All matching goes through the evaluator.org.plasmazones.WindowTracking.setWindowMetadatawidened from 4 to 9 arguments to carry the additional fields the evaluator needs (role, app-id, desktop, activity, screen). The KWin effect and daemon must be installed and running as a matched pair.MinPeerApiVersionbumped 3 → 4, and either side refuses to register a mismatched peer rather than silently degrading. Packagers must rebuild and ship both binaries together.org.plasmazones.Layout.assignmentChangesAppliedsignal dropped its second argument (the per-key field tag). Subscribers that depended on that field must update or they will receive the wrong arity.- Scripted autotiling moved from QJSEngine (JavaScript) to an embedded, sandboxed Luau VM (
phosphor-scripting). The 25 bundled algorithms were ported*.js→*.luau, written against a new frozenpzstandard library, with a per-engine CPU-time watchdog and a 64 MiB heap cap. TheTilingAlgorithmcontract, daemon, editor, and settings are unchanged. Breaking for custom algorithms: the loader now discovers only*.luaufiles, so user scripts in~/.local/share/plasmazones/algorithms/written in the old JavaScript form are no longer loaded and must be rewritten in Luau (seedocs/architecture/luau-algorithm-authoring.md). - Snapping and Tiling settings aligned for parity. Tiling gains a dedicated Focus card. Section and label naming is unified across both modes ("Inner gap" / "Outer gap", "Window Handling", parallel quick-shortcut labels), and the placement settings are reorganized into a consistent Overlay / Window / Configuration shape with gaps moved into Window → Appearance and per-monitor gap selectors. User settings are preserved (C++ symbol renames only).
- Performance. Daemon peak heap is down ~58% (149 → 63 MB) and idle CPU drops from ~25% to ~0. The overlay and snap-assist release their full-screen image buffers when dismissed (≈33 MB per shader-enabled 4K screen, ≈6 MB of thumbnail cache), and a content-addressed on-disk shader cache speeds warm launches. Settings pages that were slow on first visit now open fast via a page-instance cache, background compile-warming, and viewport virtualization of the animation-event card lists.
- Settings app rebuilt on the reusable
phosphor-controllibrary (extracted from the in-app settings chrome, formerly namedphosphor-settings-ui), reducing the app to a thin consumer with visual parity. - Internal
pz→psymbol-prefix debrand across shader params, classes, macros, CMake helpers, and the Luau tiling global (pz→phosphor_luau). The user-facingPlasmaZonesbrand and the global-shortcut ID namespace are deliberately unchanged. All ad-hoc registries (shaders, animation, curves, tiles algorithms, layout sources) are unified onto a single thread-safeRegistry<T>primitive with public APIs preserved. - Nix flake restructured around a single package definition. The 312-line
flake.nixis now thin wiring, and the build recipe and the KWin-IID rationale live inpackaging/nix/{package,overlays,module,hm-module,devShell,formatter}.nix. The package is defined once inoverlays.nix(final.callPackage) and every output (packages,devShells,checks,formatter) derives fromlegacyPackages.<system>.extend overlay, replacing five independent build call sites that each had to remember to build against the right pkgs. The version is parsed once from the top-levelproject(PlasmaZones VERSION …)inCMakeLists.txtinsideflake.nix(where the flakeselfis a store path, so the read is pure) and threaded to the package as an argument. Reading it insidepackage.nixforced an import-from-derivation when nixpkgs builds from afetchFromGitHubsrc. LTO is now opt-in (enableLTO, default off) instead of forced, since every module/overlay consumer rebuilds against host pkgs with no cache reuse. The build source islib.fileset-scoped so editing docs/CI/flake files no longer invalidates it.nix fmtnow formats Nix, C++, and QML (reusing the in-tree.clang-format), and the NixOS module declares theplasmazonessystemd user service with autostart opt-in (default off, preserving the per-user "enable it yourself" policy). - Autotiling is on by default (#671): tiling now works out of the box so PlasmaZones behaves like a dynamic tiler with no setup. The companion behaviors (focus new windows, smart gaps, respect minimum size, exclude transient windows, and insert at the stack end) were already on by default, and the default algorithm (bsp) and gaps are unchanged. Only fresh installs are affected, and existing saved configs keep their current value.
- Settings UI polish across the Layouts and listing pages: a curated default picker shows a starter set of layouts and algorithms with the rest one eye-toggle away, a shared filter menu now drives the Layouts, Window Rules, and Shaders lists, and the sidebar, global search field, About credits, and virtual-screen preview labels got alignment and spacing fixes.
Removed
- Legacy
Display.SnappingDisabled*andDisplay.AutotileDisabled*config keys (auto-migrated into rules). - QJSEngine-based scripted-tiling path (the
ScriptedAlgorithmruntime, its JS builtins, and the bundled*.jsalgorithms), along with theQt6::Qmldependency inphosphor-tiles. Replaced by the Luau path above. setSnappingLayoutEntry,setTilingAlgorithmEntry, and related per-fieldSettings-sideQ_INVOKABLEs that the legacy KCM Assignments pages used. There is no QML replacement. Use the Window Rules page.- Legacy Snapping Assignments, Tiling Assignments, and Animations App Rules settings pages (replaced by Window Rules).
- Per-release
plasmazones.nixasset and itsgenerate-release-nix.shgenerator. The asset was a source-pinned build recipe (not a binary), so it saved no build time and only served non-flake Nix users, who can instead build any tag against their host's pkgs withpkgs.callPackage "${builtins.fetchTarball "https://github.com/fuddlesworth/PlasmaZones/archive/v<VERSION>.tar.gz"}/packaging/nix/package.nix" { version = "<VERSION>"; }. The release notes' standalone-install section now shows that form, and thebuild-nixrelease job is reduced to anix buildsmoke gate. Flake users are unaffected. - Stray
develop.nix: an unrelated dev flake (for "canaanepperson.com",nodejs_24) that had no connection to PlasmaZones. The dev environment lives in the flake'sdevShells.default.
Migration
- Config schema bumped v3 → v4. On first launch after upgrade,
~/.config/plasmazones/assignments.jsonis automatically converted into~/.config/plasmazones/windowrules.json, and the legacyDisplay.SnappingDisabled*/Display.AutotileDisabled*keys inconfig.jsonare folded into the same rule set. The migration is lossless and runs without user interaction. - Backout: the source file is renamed
assignments.json.migrated(not deleted), so a downgrade can restore the previous schema by manually renaming it back and starting an older daemon. - Recovery: if migration aborts because the source is malformed, the original file is renamed to
~/.config/plasmazones/assignments.json.corrupt.bak, the schema version stays at v3, andwindowrules.jsonis not created. The daemon does not silently flush the old assignments to an empty rule set. The user can inspect / repair the quarantined file and rename it back toassignments.json, and the next launch then retries the v3→v4 conversion. hiddenFromSelectornow relocates out of layout files during the v3→v4 layout-settings conversion. A v3 user who hid a layout previously kept the key embedded in the slimmed layout file instead of having it moved to thelayout-settings.jsonsidecar. The migration now carries it across with the other relocated keys. Autotile per-algorithm overrides also fold intolayout-settings.json, and the standaloneautotile-overrides.jsonis retired by a one-time self-deleting migration on load.
Fixed
- Layouts rendered stretched / ultrawide when editing on a 16:9 or 4K screen (#593): the editor canvas used a fixed-zone bounding box as its aspect reference, so editing an existing layout could distort it while creating a new one rendered correctly. The canvas now references the live screen unless fixed zones genuinely overflow it.
- A zone-spanning window blew up to fullscreen when switching layouts (#575): switching to a layout where the previously-spanned zones are non-contiguous (e.g. Grid 2×2 left column → Master+Stack) unioned them into a screen-sized bounding box. Non-contiguous mapped spans now collapse to the primary zone instead.
- KWin effect plugin silently never installed under Nix.
packaging/nix/package.nixsetKDE_INSTALL_QTPLUGINDIRto an absolute path inside the read-onlyqtbasestore output (${qt6.qtbase}/lib/qt6/plugins). A derivation may only write under its own$out, so the effect dropped out of the package closure entirely. The daemon ran but zone overlays never appeared on Nix installs. The plugin now installs into the package's own$out/${qt6.qtbase.qtPluginPrefix}(the canonical NixOSlib/qt-6/pluginslayout), where the running KWin discovers it via the system profile's aggregatedQT_PLUGIN_PATH. - Toggling from autotile back to snapping left windows stuck in their tiled positions instead of returning to where they were before tiling. The transition fell through to a stale current-assignment resnap that re-pinned the tiled geometry and suppressed the float-back. Windows now float back to their pre-tile positions.
- Master-ratio and master-count adjustments bled into the global default (#666): the increase/decrease ratio and master-count shortcuts wrote the new value into the global config whenever a screen had no per-screen override, so the tweak propagated to sibling screens and new states on the next algorithm switch or settings refresh. The adjustment now stays local to the active screen, desktop, and activity, and resets only on an algorithm switch or an explicit ratio/count change in settings.
Installation
Arch Linux (AUR):
yay -S plasmazones # or plasmazones-binArch Linux (manual):
sudo pacman -U plasmazones-3.1.0-*-x86_64.pkg.tar.zstKDE Neon / Debian-based:
sudo dpkg -i plasmazones_3.1.0-*_amd64.deb
sudo apt-get install -f # Install dependencies if neededFedora (COPR):
sudo dnf copr enable fuddlesworth/PlasmaZones
sudo dnf install plasmazonesFedora (manual RPM):
# Fedora 44
sudo dnf install plasmazones-3.1.0-*.fc44.x86_64.rpmopenSUSE Tumbleweed (OBS):
sudo zypper addrepo https://download.opensuse.org/repositories/home:fuddlesworth/openSUSE_Tumbleweed/home:fuddlesworth.repo
sudo zypper refresh
sudo zypper install plasmazonesUniversal Linux (AppDir):
For Fedora Atomic, Steam Deck, or non-root user installation:
tar xzf plasmazones-3.1.0-linux-x86_64.tar.gz
cd plasmazones-linux-x86_64
./install.shNixOS (flake):
# flake.nix inputs
plasmazones.url = "github:fuddlesworth/PlasmaZones";
# configuration.nix
programs.plasmazones.enable = true;NixOS (without flakes):
Build this tag's source against your host's pkgs (no release asset needed):
# configuration.nix
environment.systemPackages = [
(pkgs.callPackage
"${builtins.fetchTarball "https://github.com/fuddlesworth/PlasmaZones/archive/v3.1.0.tar.gz"}/packaging/nix/package.nix"
{ version = "3.1.0"; })
];Post-Installation
systemctl --user enable --now plasmazones.service
systemsettings kcm_plasmazones