1024 lines
36 KiB
Markdown
1024 lines
36 KiB
Markdown
# Ferese Window Manager Specification
|
|
|
|
Status: Draft for implementation
|
|
Target: Ferese v0
|
|
Language: Rust
|
|
Display protocol: Wayland
|
|
|
|
## 1. Purpose
|
|
|
|
Ferese is a Wayland tiling compositor for a conventional, keyboard-friendly
|
|
desktop. It combines predictable window management with compositor-owned rounded
|
|
geometry, shadows, animation, transparency, and semantic glass materials.
|
|
|
|
This document is the implementation source of truth for Ferese v0. The terms
|
|
**MUST**, **MUST NOT**, **SHOULD**, **SHOULD NOT**, and **MAY** are normative.
|
|
|
|
## 2. Product principles
|
|
|
|
1. Window-management behavior is predictable and familiar.
|
|
2. The compositor owns composition effects; clients request semantic intent.
|
|
3. Core state has one authoritative representation.
|
|
4. Rendering and animation never block input.
|
|
5. Optional shell processes may fail without terminating the compositor.
|
|
6. Effects degrade gracefully when performance requires it.
|
|
7. The nested compositor is the primary development environment.
|
|
8. Security-sensitive operations are unavailable to untrusted clients.
|
|
|
|
## 3. Scope
|
|
|
|
### 3.1 v0 requirements
|
|
|
|
Ferese v0 MUST provide:
|
|
|
|
- native Wayland compositing;
|
|
- automatic and manually adjustable tiling;
|
|
- floating and fullscreen windows;
|
|
- multiple workspaces and outputs;
|
|
- keyboard and pointer input;
|
|
- native Wayland clipboard, primary selection, and drag-and-drop;
|
|
- client-side and server-side decoration negotiation;
|
|
- rounded clipping, borders, shadows, transparency, and backdrop blur;
|
|
- frame-timestamp-driven window and workspace animations;
|
|
- damage-aware rendering and blur caching;
|
|
- validated live configuration reload;
|
|
- authenticated local IPC and a command-line client;
|
|
- a graphical settings application;
|
|
- an overview, launcher, OSD, and basic notifications;
|
|
- a direct DRM/KMS session; and
|
|
- XWayland support, including clipboard interoperability.
|
|
|
|
The completed v0 MUST support routine software-development work without regular
|
|
compositor restarts.
|
|
|
|
### 3.2 Non-goals
|
|
|
|
The following are outside v0:
|
|
|
|
- a file manager, display manager, or lock screen;
|
|
- network, Bluetooth, or package-management UI;
|
|
- a general plugin, scripting, or theme system;
|
|
- desktop widgets;
|
|
- advanced dynamic layout algorithms;
|
|
- exhaustive support for every Wayland protocol or toolkit edge case; and
|
|
- pixel-for-pixel imitation of another operating system.
|
|
|
|
Ferese MAY display a configured color or image, but a standalone wallpaper
|
|
daemon is not part of v0.
|
|
|
|
## 4. Architecture
|
|
|
|
### 4.1 Processes
|
|
|
|
| Executable | Responsibility |
|
|
| --- | --- |
|
|
| `ferese` | Compositing, policy, input, scene construction, and rendering |
|
|
| `ferese-shell` | Overview controls, launcher, workspace UI, notifications, and OSD |
|
|
| `ferese-settings` | Graphical configuration editor |
|
|
| `feresectl` | Command-line IPC client |
|
|
|
|
`ferese` MUST remain usable if another Ferese process exits. Shell and settings
|
|
code MUST NOT run inside the compositor.
|
|
|
|
```text
|
|
Wayland/XWayland clients
|
|
|
|
|
v
|
|
protocol adapters
|
|
|
|
|
v
|
|
authoritative core state
|
|
|
|
|
+----> layout targets
|
|
+----> animation state
|
|
|
|
|
v
|
|
scene graph
|
|
|
|
|
v
|
|
damage-aware renderer
|
|
|
|
|
v
|
|
nested window or DRM/KMS output
|
|
```
|
|
|
|
Protocol handlers MUST translate requests into domain operations. Business
|
|
logic MUST NOT accumulate in Smithay callbacks.
|
|
|
|
### 4.2 Technology baseline
|
|
|
|
The initial implementation uses Rust, Smithay, calloop, Smithay's GLES renderer,
|
|
`glow`, targeted GLSL shaders, DRM/KMS, GBM, EGL, dma-buf, libinput, xkbcommon,
|
|
libseat, serde, TOML, serde JSON, clap, tracing, and GPUI.
|
|
|
|
Dependencies MAY change when an architectural decision record explains the
|
|
reason and migration impact.
|
|
|
|
### 4.3 Repository layout
|
|
|
|
```text
|
|
ferese/
|
|
|-- Cargo.toml
|
|
|-- crates/
|
|
| |-- ferese-core/
|
|
| |-- ferese-layout/
|
|
| |-- ferese-render/
|
|
| |-- ferese-animation/
|
|
| |-- ferese-config/
|
|
| |-- ferese-ipc/
|
|
| |-- ferese-protocols/
|
|
| `-- ferese-shell-protocol/
|
|
|-- compositor/
|
|
|-- shell/
|
|
|-- settings/
|
|
|-- feresectl/
|
|
`-- protocols/
|
|
|-- ferese-effects-v1.xml
|
|
`-- ferese-shell-v1.xml
|
|
```
|
|
|
|
`ferese-layout` MUST be pure Rust and MUST NOT depend on Smithay or Wayland
|
|
objects.
|
|
|
|
### 4.4 Domain operations
|
|
|
|
Wayland callbacks, input handlers, IPC handlers, configuration reload, and shell
|
|
requests MUST enter core policy through explicit domain operations rather than
|
|
mutating unrelated managers directly. A representative shape is:
|
|
|
|
```rust
|
|
pub enum Action {
|
|
WindowMapped(WindowId),
|
|
WindowUnmapped(WindowId),
|
|
Focus(Direction),
|
|
Move(Direction),
|
|
Resize(Direction, f32),
|
|
SwitchWorkspace(WorkspaceId),
|
|
MoveToWorkspace(WindowId, WorkspaceId),
|
|
ToggleFloating(WindowId),
|
|
ToggleFullscreen(WindowId),
|
|
SetConfig(ConfigChange),
|
|
}
|
|
```
|
|
|
|
An action is applied as one state transaction. The transaction MUST either
|
|
complete with all Section 5 invariants restored or leave authoritative state
|
|
unchanged. Rendering, protocol configure requests, IPC events, and persistence
|
|
are consequences emitted after the state transition; they MUST NOT become a
|
|
second source of policy state.
|
|
|
|
## 5. Authoritative state
|
|
|
|
The compositor MUST maintain one state root:
|
|
|
|
```rust
|
|
pub struct FereseState {
|
|
pub outputs: OutputManager,
|
|
pub workspaces: WorkspaceManager,
|
|
pub windows: WindowManager,
|
|
pub seat: SeatState,
|
|
pub layout: LayoutManager,
|
|
pub animations: AnimationManager,
|
|
pub config: Config,
|
|
}
|
|
```
|
|
|
|
Smithay objects SHOULD remain behind adapter types rather than become the domain
|
|
model.
|
|
|
|
### 5.1 Identifiers
|
|
|
|
Outputs, workspaces, windows, layout nodes, and the logical seat MUST have stable
|
|
internal identifiers. An identifier MUST NOT be reused during one compositor
|
|
lifetime.
|
|
|
|
```rust
|
|
pub struct WindowId(u64);
|
|
pub struct WorkspaceId(u64);
|
|
pub struct OutputId(u64);
|
|
pub struct NodeId(u64);
|
|
pub struct SeatId(u64);
|
|
pub struct SurfaceId(u64);
|
|
```
|
|
|
|
Ferese v0 exposes exactly one logical seat. Multiple physical keyboards, mice,
|
|
and touch devices MAY feed that seat. Multi-seat policy is outside v0 and MUST
|
|
NOT be implied by the domain model.
|
|
|
|
### 5.2 Windows
|
|
|
|
```rust
|
|
pub struct Window {
|
|
pub id: WindowId,
|
|
pub app_id: Option<String>,
|
|
pub title: Option<String>,
|
|
pub workspace: WorkspaceId,
|
|
pub placement: WindowPlacement,
|
|
pub fullscreen: bool,
|
|
pub constraints: SizeConstraints,
|
|
pub urgent: bool,
|
|
pub material: WindowMaterial,
|
|
}
|
|
|
|
pub enum WindowPlacement {
|
|
Tiled,
|
|
Floating { rect: RectF },
|
|
}
|
|
```
|
|
|
|
Fullscreen is a presentation state layered over the preserved tiled or floating
|
|
placement. Entering fullscreen MUST NOT remove a tiled window from its layout
|
|
tree or a floating window from its floating list. At most one window may be
|
|
fullscreen on a workspace. While fullscreen is active, that window is the sole
|
|
normal managed-window content presented for the workspace, although permitted
|
|
shell overlays and popups may still appear. Leaving fullscreen restores the
|
|
preserved placement without reconstructing it.
|
|
|
|
A window MUST NOT independently store an output. Its output is derived from its
|
|
workspace, preventing stale ownership after workspace migration. Authoritative
|
|
keyboard focus belongs to `SeatState` and MUST NOT be duplicated on every
|
|
window or workspace. Layout hierarchy MUST NOT be embedded in `Window`.
|
|
|
|
### 5.3 Workspaces
|
|
|
|
```rust
|
|
pub struct Workspace {
|
|
pub id: WorkspaceId,
|
|
pub name: String,
|
|
pub output: Option<OutputId>,
|
|
pub root: Option<NodeId>,
|
|
pub floating: Vec<WindowId>,
|
|
pub last_focused: Option<WindowId>,
|
|
}
|
|
|
|
pub struct SeatState {
|
|
pub id: SeatId,
|
|
pub keyboard_focus: Option<WindowId>,
|
|
pub pointer_focus: Option<SurfaceId>,
|
|
pub focused_output: Option<OutputId>,
|
|
pub focused_workspace: Option<WorkspaceId>,
|
|
}
|
|
```
|
|
|
|
`Workspace::last_focused` is restoration/history state only. It MUST NOT be
|
|
interpreted as current keyboard focus.
|
|
|
|
These invariants MUST hold after every state transaction:
|
|
|
|
1. Every managed window belongs to exactly one workspace.
|
|
2. Every attached workspace belongs to exactly one connected output; only a
|
|
workspace awaiting an output may contain `None`.
|
|
3. An output exposes exactly one active workspace.
|
|
4. A window with `WindowPlacement::Tiled` appears exactly once in its workspace
|
|
tree and not in the floating list.
|
|
5. A window with `WindowPlacement::Floating` appears exactly once in the
|
|
floating list and not in the tree.
|
|
6. Fullscreen does not alter the window's underlying placement membership.
|
|
7. `seat.keyboard_focus`, when present, refers to a managed window on
|
|
`seat.focused_workspace`.
|
|
8. `Workspace::last_focused`, when present, refers to a window belonging to that
|
|
workspace.
|
|
9. At most one window is fullscreen on a workspace at a time.
|
|
|
|
Workspace migration MUST be atomic. If an output disappears, its workspaces move
|
|
to a deterministic remaining output. If none remains, they stay detached until
|
|
an output appears.
|
|
|
|
Numeric workspace names are unbounded, but the default bindings directly address
|
|
`1` through `9` and create them lazily. Named workspaces MAY be added after v0.
|
|
|
|
### 5.4 Focus semantics
|
|
|
|
The single v0 seat owns authoritative keyboard focus, pointer focus, focused
|
|
workspace, and focused output. A workspace stores only `last_focused` so focus
|
|
can be restored when that workspace becomes active.
|
|
|
|
When switching to a workspace that is already visible on another output, Ferese
|
|
MUST focus that output and workspace instead of moving or swapping the workspace.
|
|
Explicit workspace-move commands are responsible for relocating workspaces.
|
|
|
|
## 6. Layout
|
|
|
|
```rust
|
|
pub enum Node {
|
|
Window(WindowId),
|
|
Split {
|
|
axis: Axis,
|
|
ratio: f32,
|
|
first: NodeId,
|
|
second: NodeId,
|
|
},
|
|
Stack {
|
|
children: Vec<NodeId>,
|
|
active: usize,
|
|
},
|
|
}
|
|
|
|
pub enum Axis {
|
|
Horizontal,
|
|
Vertical,
|
|
}
|
|
```
|
|
|
|
Split ratios MUST be finite and clamped so descendants satisfy their minimum
|
|
sizes where possible. If all constraints cannot be met, the focused window takes
|
|
priority and the conflict MUST be logged.
|
|
|
|
The engine consumes a layout tree, usable logical geometry, size constraints,
|
|
and tiling configuration. It returns a deterministic `WindowId -> RectF` map and
|
|
constraint warnings. Identical input MUST produce identical output.
|
|
|
|
### 6.1 Automatic insertion
|
|
|
|
1. The first tiled window becomes the root.
|
|
2. A later window splits the focused leaf.
|
|
3. For `default_split = "automatic"`, use a horizontal split when the focused
|
|
rectangle is wider than tall and a vertical split otherwise.
|
|
4. The new window becomes focused after its first mapped commit.
|
|
|
|
### 6.2 Required operations
|
|
|
|
Ferese MUST support directional focus, movement and resizing; explicit split
|
|
direction; floating and fullscreen toggles; tiled-divider resize; floating
|
|
move/resize; and tiled drag-and-drop.
|
|
|
|
Drag targets are `left`, `right`, `top`, `bottom`, and `center`. A drag MUST show
|
|
a live preview. Center creates or joins a stack; an edge creates a split.
|
|
|
|
### 6.3 Gaps
|
|
|
|
Gaps belong exclusively to `[tiling]`. `inner_gap` separates tiles and
|
|
`outer_gap` separates tiles from output bounds. With `smart_gaps = true`, a lone
|
|
tiled window has no outer gap.
|
|
|
|
## 7. Geometry and animation
|
|
|
|
Ferese MUST distinguish buffer, surface-local, logical-output, and physical
|
|
coordinates. Layout and input policy use logical coordinates. Rendering performs
|
|
physical conversion. Fractional scaling MUST avoid cumulative rounding errors
|
|
between adjacent tiles.
|
|
|
|
A managed window has three geometry domains that MUST remain distinct:
|
|
|
|
1. **logical geometry:** the authoritative layout destination;
|
|
2. **visual geometry:** where the compositor currently presents the window while
|
|
an animation is in progress; and
|
|
3. **client geometry:** the most recently configured and committed client content
|
|
size.
|
|
|
|
A representative model is:
|
|
|
|
```rust
|
|
pub struct AnimatedRect {
|
|
pub current: RectF,
|
|
pub target: RectF,
|
|
pub velocity: RectF,
|
|
}
|
|
|
|
pub struct ClientGeometry {
|
|
pub last_configured_size: Size<i32>,
|
|
pub committed_size: Size<i32>,
|
|
}
|
|
|
|
pub struct WindowGeometry {
|
|
pub visual: AnimatedRect,
|
|
pub client: ClientGeometry,
|
|
}
|
|
```
|
|
|
|
`visual.target` is the authoritative logical layout rectangle. The target becomes
|
|
authoritative immediately after the state transaction even if the client has not
|
|
yet committed a buffer matching its new size.
|
|
|
|
For a non-interactive layout transition Ferese MUST:
|
|
|
|
1. compute and store the final target geometry;
|
|
2. send a configure for the final client size rather than one configure per
|
|
animation frame;
|
|
3. continue presenting the most recent committed client content while visual
|
|
geometry interpolates toward the target;
|
|
4. map pointer coordinates through the inverse visual transform before
|
|
delivering surface-local input; and
|
|
5. accept a newly committed target-sized buffer without restarting the visual
|
|
transition.
|
|
|
|
The renderer MAY use a short-lived snapshot of the previously committed content
|
|
to avoid a visible source-size discontinuity. Whether using a live committed
|
|
buffer or snapshot, client content MUST remain aligned with the visual geometry
|
|
used for hit testing.
|
|
|
|
During an interactive resize, configure events MAY follow pointer motion subject
|
|
to normal coalescing and client responsiveness. Keyboard navigation MUST use the
|
|
target layout topology, not transient visual geometry.
|
|
|
|
When animation is disabled or reduced motion is active, `visual.current` snaps
|
|
to `visual.target`. Geometry uses spring animation; opacity, dimming, blur
|
|
intensity, and overlays use bounded easing. Updates MUST use presentation or
|
|
monotonic frame timestamps and MUST NOT assume a fixed refresh rate.
|
|
|
|
Animations MUST stop within configured tolerances. Hidden animations SHOULD stop
|
|
requesting frames. Opening, closing, or moving a window MUST never block input.
|
|
|
|
## 8. Wayland behavior
|
|
|
|
### 8.1 Required native protocols
|
|
|
|
Before the native desktop is usable, Ferese MUST support the stable protocols
|
|
needed for:
|
|
|
|
- compositor, subcompositor, shared memory, and outputs;
|
|
- xdg-shell;
|
|
- xdg-activation;
|
|
- seats, keyboard, pointer, and available touch devices;
|
|
- data-device clipboard and drag-and-drop;
|
|
- primary selection;
|
|
- xdg-decoration;
|
|
- relative pointer and pointer constraints;
|
|
- presentation timing;
|
|
- viewporter and fractional scaling;
|
|
- idle inhibition; and
|
|
- Linux dma-buf where supported.
|
|
|
|
Layer-shell, screencopy, virtual input, and session-lock MAY be added after their
|
|
security policy is specified. Their absence does not block v0 unless a Ferese
|
|
component requires them.
|
|
|
|
### 8.2 Decorations
|
|
|
|
Ferese MUST support client-side decorations. If a client negotiates server-side
|
|
decorations, Ferese MUST provide usable move, resize, and close interactions.
|
|
Shadows, masks, and focus borders are compositor effects and may apply in either
|
|
mode.
|
|
|
|
### 8.3 XWayland
|
|
|
|
XWayland windows use the same `Window` abstraction as native surfaces. The
|
|
layout engine MUST remain protocol-agnostic.
|
|
|
|
XWayland support includes startup, mapping, focus, tiled geometry, floating
|
|
hints, selection bridging, and correct stacking for popups and override-redirect
|
|
windows.
|
|
|
|
## 9. Scene and rendering
|
|
|
|
Ferese MUST build an explicit scene from authoritative state and current
|
|
animation values. Nodes SHOULD cover surfaces, popups, decorations, shadows,
|
|
masks, borders, materials, shell UI, drag icons, and cursors.
|
|
|
|
Scene order is background, windows back-to-front, shell/overlays, drag icons,
|
|
then cursor. Within a window, the shadow is behind clipped background and client
|
|
content; the border is above. Rounded clipping MUST be established before
|
|
drawing clipped content or through an equivalent offscreen pass. Client buffers
|
|
MUST NOT be modified to implement effects.
|
|
|
|
Rendering MUST be damage-driven. Damage includes commits, movement, animation,
|
|
shadows, borders, material invalidation, shell, cursor, and output changes.
|
|
Moving or removing a node MUST damage both old and new bounds. Expansion MUST
|
|
include shadow, blur-kernel, and sampling extents.
|
|
|
|
Direct scanout SHOULD be possible when one compatible fullscreen surface covers
|
|
an output and no transform, overlay, capture, conversion, or effect requires
|
|
composition.
|
|
|
|
Rounded corners use a GPU mask or equivalent. Fullscreen windows use zero radius.
|
|
Shadows SHOULD use analytic shaders, cached textures, or reduced-resolution
|
|
intermediates rather than full-resolution per-frame blur.
|
|
|
|
## 10. Material system
|
|
|
|
Initial semantic materials are:
|
|
|
|
```text
|
|
normal
|
|
glass.panel
|
|
glass.popover
|
|
glass.menu
|
|
glass.hud
|
|
```
|
|
|
|
The compositor maps semantic names to theme-controlled parameters. Clients
|
|
SHOULD request names rather than shader values.
|
|
|
|
`ferese-effects-v1` is private to trusted Ferese components. It MAY set or clear
|
|
a semantic surface material. It MUST NOT expose shader code, unrestricted
|
|
capture, or unbounded effect values. Its global MUST be advertised only to
|
|
Wayland clients granted the `effects` capability by the private-client mechanism
|
|
in Section 13.
|
|
|
|
Glass renders as:
|
|
|
|
```text
|
|
lower scene content
|
|
-> capture expanded region
|
|
-> downsample
|
|
-> dual Kawase blur
|
|
-> upsample
|
|
-> saturation and brightness
|
|
-> tint and noise
|
|
-> glass foreground
|
|
```
|
|
|
|
Blur MUST exclude the glass surface and all content above it. Sampling bounds
|
|
MUST include the kernel radius.
|
|
|
|
A blur cache entry MUST identify its output and scale, visible and sample
|
|
regions, material parameters, contributing lower-layer generations, and result
|
|
texture. Changes to content, stacking, geometry, scale, transform, material,
|
|
workspace, or output MUST invalidate affected entries. A global generation MAY
|
|
be used initially; region-aware invalidation SHOULD replace it before performance
|
|
tuning is complete.
|
|
|
|
### 10.1 Built-in visual profile
|
|
|
|
v0 provides one restrained dark profile rather than a theme engine. Its defaults
|
|
are a subtle blue focus accent, 14 logical-pixel window radius, 10 logical-pixel
|
|
tiling gaps, soft shadows, 24 logical-pixel backdrop blur, and mildly increased
|
|
glass saturation. Focused and foreground content MUST remain distinguishable
|
|
without requiring transparency. Blur, glow, borders, and shadows SHOULD establish
|
|
hierarchy rather than compete for attention. Settings, overview, notifications,
|
|
launcher, and OSD MUST use the same semantic materials and spacing system.
|
|
|
|
## 11. Shell and overview
|
|
|
|
`ferese-shell` owns overview controls, launcher, workspace strip, notifications,
|
|
and volume and brightness OSD. Screenshot UI MAY be added after capture policy
|
|
and implementation are complete. The shell uses a capability-scoped private
|
|
Wayland connection plus authenticated IPC.
|
|
|
|
Overview MUST NOT require copying each managed window into the shell process.
|
|
The compositor owns the live window presentation and enters an overview scene
|
|
mode in which existing window scene nodes receive temporary visual transforms.
|
|
`ferese-shell` supplies controls such as labels, selection affordances, workspace
|
|
UI, and later search.
|
|
|
|
The overview binding is `Super+Tab`. Entering overview MUST animate windows from
|
|
their current visible geometry into overview geometry. Pointer and keyboard
|
|
selection MUST target the compositor-owned live window nodes. Dismissing overview
|
|
MUST animate those same nodes back to the current layout targets. Workspace
|
|
switching from overview MUST obey Section 5.4. Search MAY wait for the launcher
|
|
milestone.
|
|
|
|
### 11.1 Private shell surfaces
|
|
|
|
Because layer-shell is not required for v0, Ferese MUST provide a private
|
|
`ferese-shell-v1` protocol to clients with the `shell-surfaces` capability. A
|
|
shell surface is not a managed application window and MUST NOT enter tiling,
|
|
workspace focus history, or normal application task switching.
|
|
|
|
Initial semantic roles are:
|
|
|
|
```text
|
|
launcher
|
|
overview-controls
|
|
workspace-ui
|
|
notification
|
|
osd
|
|
```
|
|
|
|
The compositor owns placement, z-order bounds, input region enforcement, and
|
|
output association for these roles. The shell provides content and requests a
|
|
role; it MUST NOT receive unrestricted arbitrary z-order or geometry authority.
|
|
Interactive shell surfaces MAY receive keyboard/pointer focus while active, but
|
|
Ferese MUST restore the prior managed-window focus when the interaction ends.
|
|
Destroying or disconnecting the shell client MUST remove its shell surfaces
|
|
without changing managed-window state.
|
|
|
|
### 11.2 Application launching and notifications
|
|
|
|
The launcher MUST execute configured argument vectors directly and MUST NOT pass
|
|
them through a shell. Before launch, it MUST obtain an xdg-activation token tied
|
|
to the triggering user interaction and make the token available to the child.
|
|
The child receives the public Wayland display and normal session environment.
|
|
It MUST NOT inherit a private Wayland descriptor, IPC capability, or bearer
|
|
secret. A private descriptor deliberately inherited when the compositor starts a
|
|
trusted component is the sole exception during that component's initial exec;
|
|
the component MUST mark it close-on-exec before it can launch any child.
|
|
|
|
`ferese-shell` MUST implement the standard `org.freedesktop.Notifications` D-Bus
|
|
service for basic notifications. v0 MUST support summary, body, icon metadata,
|
|
expiry, replacement, close, and actions. Notification content is untrusted:
|
|
markup, image paths, and action identifiers MUST be parsed and rendered without
|
|
command execution. If the shell restarts, it MUST reacquire the service and
|
|
continue without affecting managed windows.
|
|
|
|
## 12. Configuration
|
|
|
|
The primary file is `$XDG_CONFIG_HOME/ferese/config.toml`, falling back to
|
|
`~/.config/ferese/config.toml`.
|
|
|
|
```toml
|
|
[appearance]
|
|
glass = true
|
|
blur_radius = 24.0
|
|
glass_opacity = 0.82
|
|
corner_radius = 14.0
|
|
|
|
[appearance.shadow]
|
|
enabled = true
|
|
radius = 32.0
|
|
offset_y = 8.0
|
|
opacity = 0.28
|
|
|
|
[appearance.active_border]
|
|
style = "subtle"
|
|
width = 2.0
|
|
|
|
[animations]
|
|
enabled = true
|
|
reduced_motion = false
|
|
speed = 1.0
|
|
|
|
[animations.spring]
|
|
stiffness = 320.0
|
|
damping = 28.0
|
|
mass = 1.0
|
|
|
|
[tiling]
|
|
inner_gap = 10.0
|
|
outer_gap = 10.0
|
|
smart_gaps = true
|
|
default_split = "automatic"
|
|
|
|
[input]
|
|
focus_follows_mouse = false
|
|
|
|
[commands]
|
|
terminal = ["foot"]
|
|
|
|
[[bindings]]
|
|
keys = "Super+Enter"
|
|
action = "spawn"
|
|
argument = "terminal"
|
|
|
|
[[bindings]]
|
|
keys = "Super+Q"
|
|
action = "close"
|
|
|
|
[[bindings]]
|
|
keys = "Super+Tab"
|
|
action = "show-overview"
|
|
|
|
[[bindings]]
|
|
keys = "Super+Space"
|
|
action = "show-launcher"
|
|
```
|
|
|
|
Command values are argument arrays, never shell command strings. Binding records
|
|
use a normalized key chord, action, and optional argument. The complete built-in
|
|
set is listed in Section 14; when a user supplies a record for a chord it
|
|
replaces that default. Duplicate normalized chords, unknown actions, invalid
|
|
arguments, and references to missing commands MUST fail validation. Every
|
|
built-in binding MUST be expressible and replaceable through this schema.
|
|
|
|
Unknown fields SHOULD warn. Invalid types, non-finite values, negative sizes,
|
|
and out-of-range opacity MUST fail validation.
|
|
|
|
Reload MUST debounce duplicate events, read the entire file, parse, validate,
|
|
construct, and apply the difference as one transaction. Invalid input MUST NOT
|
|
replace active configuration. Errors MUST be logged and emitted through IPC.
|
|
|
|
Settings and IPC MUST use the same validation path. Persistent changes MUST be
|
|
written atomically using a temporary file and rename.
|
|
|
|
## 13. IPC and security
|
|
|
|
### 13.1 Transport and trust
|
|
|
|
The user-control socket is `$XDG_RUNTIME_DIR/ferese/control.sock`. Its parent
|
|
directory MUST be owned by the user with mode `0700`; the socket MUST use `0600`.
|
|
The compositor MUST reject peers whose effective UID, read from OS peer
|
|
credentials, differs from its own.
|
|
|
|
Same-UID checks authorize user control only; they do not identify a trusted UI
|
|
client. Privileged Wayland access is capability-scoped per client. Initial
|
|
capabilities are:
|
|
|
|
```text
|
|
effects
|
|
shell-surfaces
|
|
global-metadata
|
|
capture
|
|
virtual-input
|
|
```
|
|
|
|
Trusted UI processes MUST receive a private Wayland connection created by the
|
|
compositor. Ferese creates a socket pair, registers the server end as a Wayland
|
|
client, and passes the client end only through an inherited descriptor such as
|
|
`WAYLAND_SOCKET`. Privilege MUST NOT depend on PID, executable name, command-line
|
|
arguments, environment secrets, or a discoverable private socket path.
|
|
|
|
The compositor associates an explicit capability set with each private Wayland
|
|
client and filters private globals and requests against that set. In particular,
|
|
`ferese-effects-v1` requires `effects` and `ferese-shell-v1` requires
|
|
`shell-surfaces`. `ferese-shell` receives only the capabilities required by
|
|
enabled shell features.
|
|
`ferese-settings` receives `effects` when spawned as a trusted Ferese component;
|
|
its configuration changes still use the normal validated IPC path.
|
|
|
|
If a trusted component launches another privileged Ferese component, it MUST ask
|
|
the compositor to create and pass a new capability-scoped private connection;
|
|
it MUST NOT forward its own Wayland descriptor or capabilities.
|
|
|
|
### 13.2 Framing
|
|
|
|
IPC v0 uses unsigned 32-bit big-endian length-prefixed UTF-8 JSON:
|
|
|
|
```text
|
|
4-byte payload length | JSON payload bytes
|
|
```
|
|
|
|
The maximum payload is 1 MiB. Oversized, truncated, invalid UTF-8, or invalid
|
|
JSON frames close the connection after a rate-limited diagnostic.
|
|
|
|
```json
|
|
{
|
|
"version": 1,
|
|
"id": 42,
|
|
"type": "command",
|
|
"command": "focus",
|
|
"args": { "direction": "left" }
|
|
}
|
|
```
|
|
|
|
Responses echo `id` and contain exactly one of `result` or `error`:
|
|
|
|
```json
|
|
{
|
|
"version": 1,
|
|
"id": 42,
|
|
"result": {}
|
|
}
|
|
```
|
|
|
|
Errors have stable codes. Unknown fields MAY be ignored; unknown commands and
|
|
unsupported versions MUST return errors.
|
|
|
|
### 13.3 Commands and events
|
|
|
|
User-control commands are:
|
|
|
|
```text
|
|
focus move
|
|
resize workspace
|
|
move-to-workspace toggle-floating
|
|
toggle-fullscreen close
|
|
reload-config get-focused-window
|
|
get-workspaces get-outputs
|
|
get-config set-config
|
|
show-overview hide-overview
|
|
show-launcher hide-launcher
|
|
```
|
|
|
|
Enumerating all window titles or capturing pixels requires shell privilege.
|
|
|
|
Events include `window-opened`, `window-closed`, `window-focused`,
|
|
`window-moved`, `workspace-changed`, `output-added`, `output-removed`,
|
|
`config-changed`, and `config-error`. Payloads MUST omit titles and application
|
|
IDs without shell privilege.
|
|
|
|
Subscribers have bounded queues. The server MUST disconnect a subscriber whose
|
|
queue overflows rather than block the compositor.
|
|
|
|
## 14. Input
|
|
|
|
Ferese MUST support click-to-focus, configurable focus-follows-pointer, floating
|
|
move/resize, tiled rearrangement and divider resizing, pointer constraints,
|
|
relative pointer, and configurable bindings.
|
|
|
|
```text
|
|
Super+Enter terminal
|
|
Super+Q close window
|
|
Super+H/J/K/L focus left/down/up/right
|
|
Super+Shift+H/J/K/L move left/down/up/right
|
|
Super+1..9 switch workspace
|
|
Super+Shift+1..9 move window to workspace
|
|
Super+F toggle fullscreen
|
|
Super+Shift+Space toggle floating
|
|
Super+Space launcher
|
|
Super+Tab overview
|
|
```
|
|
|
|
Bare `Super` MUST NOT activate overview. Interactive grabs MUST have explicit
|
|
cancellation. Losing a device, seat, surface, or output during a grab MUST end it
|
|
safely.
|
|
|
|
## 15. Outputs
|
|
|
|
Each connected output has a mode, scale, transform, logical position, usable
|
|
region, and one active workspace. v0 MAY choose preferred modes automatically
|
|
but MUST handle hotplug without losing windows.
|
|
|
|
On startup, the first output creates and activates workspace `1`. Selecting an
|
|
uncreated numeric workspace from `1` through `9` creates it on the focused
|
|
output. A newly connected output receives the lowest unused numeric workspace
|
|
name and activates it; this MUST NOT silently move an existing workspace.
|
|
|
|
Output removal selects a target from the remaining outputs in this order:
|
|
|
|
1. the v0 seat's focused output, if it remains connected;
|
|
2. nearest output to the removed output by logical-center distance;
|
|
3. lowest stable `OutputId` as a tie-breaker.
|
|
|
|
A workspace MAY move between outputs but MUST NOT appear on two outputs at once.
|
|
Switching to a workspace already visible on another output changes the seat's
|
|
focused output/workspace to that existing presentation and MUST NOT implicitly
|
|
move or swap workspaces.
|
|
|
|
When an output is removed, the migration target keeps its current active
|
|
workspace unless the removed output held the seat focus. In that case, the
|
|
removed output's active workspace becomes active on the target and the seat's
|
|
managed-window focus is restored there where possible.
|
|
|
|
## 16. Performance and observability
|
|
|
|
The target is sustained output refresh at 60 Hz on integrated graphics during
|
|
ordinary use. Animation MUST also work at higher and variable refresh rates.
|
|
|
|
Ferese SHOULD track frame, render, layout and blur duration; damaged pixels;
|
|
missed presentation deadlines; surface and glass-region counts; and active
|
|
animations. Performance tracing MUST be optional. Suggested targets are
|
|
`ferese::wayland`, `ferese::layout`, `ferese::render`, `ferese::input`,
|
|
`ferese::ipc`, and `ferese::xwayland`.
|
|
|
|
## 17. Testing
|
|
|
|
### 17.1 Layout
|
|
|
|
Unit tests MUST cover insertion, deletion, focus, movement, ratios, constraints,
|
|
resize, stacks, floating transitions, fullscreen placement preservation,
|
|
workspace migration, gaps, and deterministic geometry. They MUST NOT require
|
|
Wayland. Property tests
|
|
SHOULD prove arbitrary operation sequences do not duplicate or lose windows.
|
|
|
|
### 17.2 Configuration
|
|
|
|
Tests MUST cover defaults, partial files, unknown fields, invalid and non-finite
|
|
values, duplicate key chords, unknown binding actions, missing command
|
|
references, reload rollback, serialization, and atomic updates.
|
|
|
|
### 17.3 IPC and security
|
|
|
|
Tests MUST cover partial reads, multiple frames per read, oversized frames,
|
|
unsupported versions, subscriptions, slow clients, peer-UID rejection, and
|
|
denial of privileged commands without capability. Launch tests MUST prove that
|
|
children receive the public display and activation token but cannot inherit any
|
|
private Wayland or privileged IPC descriptor.
|
|
|
|
### 17.4 Rendering and integration
|
|
|
|
Pure tests MUST cover clipping, shadow bounds, damage expansion, blur sampling,
|
|
cache invalidation, transforms, animated hit testing, and refresh-rate-independent
|
|
interpolation. Geometry tests MUST cover mismatches between logical target,
|
|
visual geometry, configured client size, and committed client size. Nested
|
|
integration tests SHOULD cover configure/commit behavior, inverse-transformed
|
|
input during animation, clipboard, popups, drag-and-drop, focus, fullscreen,
|
|
xdg-activation, output removal, private-client capability filtering, overview
|
|
scene transitions, notification replacement/actions, and shell restart.
|
|
|
|
## 18. Milestones
|
|
|
|
A milestone is complete only when its acceptance criteria pass.
|
|
|
|
### M0: Native window on screen
|
|
|
|
Implement the nested backend, public Wayland socket, output, seat, xdg-shell,
|
|
basic renderer, and commit handling.
|
|
|
|
Acceptance: `foot` opens and redraws; keyboard, pointer, and popups work; closing
|
|
a client leaves the compositor running.
|
|
|
|
### M1: Essential native protocols
|
|
|
|
Implement clipboard, primary selection, drag-and-drop, decoration negotiation,
|
|
xdg-activation, relative pointer, pointer constraints, presentation timing, and
|
|
scaling.
|
|
|
|
Acceptance: text copies between native clients; drag-and-drop completes;
|
|
decorated and undecorated clients can move and resize; scaled hit testing matches
|
|
rendered content; a user-initiated test launch receives a valid activation token.
|
|
|
|
### M2: Tiling and workspaces
|
|
|
|
Implement identifiers, layout trees, insertion, focus, movement, resize, gaps,
|
|
floating, fullscreen, and workspaces.
|
|
|
|
Acceptance: five terminals tile without overlap; navigation works in all
|
|
directions; close reflows layout; modes restore correctly; randomized tests
|
|
preserve all invariants.
|
|
|
|
### M3: Animated geometry
|
|
|
|
Implement logical, visual, and client geometry separation plus timestamp-driven
|
|
animation.
|
|
|
|
Acceptance: layout changes do not jump; one non-interactive reflow does not send
|
|
per-frame configure events; a client may commit its final size mid-animation
|
|
without restarting the transition; inverse-transformed hit testing matches the
|
|
visible surface; input stays responsive.
|
|
|
|
### M4: Visual foundation
|
|
|
|
Implement rounded clipping, shadows, active borders, transparency, background,
|
|
and damage expansion.
|
|
|
|
Acceptance: masks clip correctly; old and new movement bounds repaint;
|
|
fullscreen has no unintended corners or gaps.
|
|
|
|
### M5: Glass materials
|
|
|
|
Implement semantic materials, capture, dual Kawase blur, tint, saturation, noise,
|
|
caching, invalidation, and the private effects protocol.
|
|
|
|
Acceptance: a Ferese panel blurs only content behind it; backdrop changes
|
|
invalidate correctly; the effects global is visible only to a client with the
|
|
`effects` capability; a public Wayland client cannot bind it; the demo stays
|
|
smooth on the reference integrated GPU.
|
|
|
|
### M6: IPC and CLI
|
|
|
|
Implement framing, authorization, events, and `feresectl`.
|
|
|
|
Acceptance: `feresectl focus left`, `feresectl workspace 2`, and
|
|
`feresectl get-workspaces` work reliably. Every request receives a response or
|
|
bounded-time error; malformed clients do not affect availability.
|
|
|
|
### M7: Settings
|
|
|
|
Build Appearance, Tiling, Shortcuts, Input, Workspaces, Displays, and About.
|
|
|
|
Acceptance: controls preview live; invalid changes do not replace active config;
|
|
committed changes survive restart; shortcut editing uses the canonical binding
|
|
schema and detects conflicts; the v0 Displays page accurately reports automatic
|
|
mode, scale, transform, and placement state but MAY be read-only; when spawned
|
|
as a trusted component, Settings receives only the private Wayland capabilities
|
|
it requires.
|
|
|
|
### M8: Overview and shell
|
|
|
|
Implement compositor overview scene mode, `ferese-shell-v1`, launcher, workspace
|
|
strip, OSD, and the standard desktop notifications D-Bus service.
|
|
|
|
Acceptance: `Super+Tab` controls overview; managed windows remain compositor-
|
|
owned live scene nodes rather than copied shell thumbnails; selection follows
|
|
the transformed visual geometry; applications launch without shell parsing or
|
|
privileged-descriptor inheritance; notification replacement and actions work;
|
|
shell restart does not disrupt windows; each trusted UI process receives only
|
|
its assigned capabilities.
|
|
|
|
### M9: Hardware session
|
|
|
|
Implement libseat lifecycle, DRM, GBM, devices, hotplug, and VT/session state.
|
|
|
|
Acceptance: Ferese starts from a TTY; deactivation releases resources correctly;
|
|
output removal migrates workspaces deterministically.
|
|
|
|
### M10: XWayland and stabilization
|
|
|
|
Implement XWayland startup, mapping, hints, focus, selection bridging, and
|
|
stacking. Complete crash, soak, and performance testing.
|
|
|
|
Acceptance: representative X11 applications tile and float; clipboard works both
|
|
ways between X11 and Wayland; a 24-hour mixed-client soak has no compositor crash
|
|
or unbounded resource growth; all Section 3.1 requirements pass.
|
|
|
|
## 19. First implementation slice
|
|
|
|
```text
|
|
nested Smithay compositor
|
|
-> two xdg-shell terminals
|
|
-> deterministic two-window tiling
|
|
-> logical/visual/client geometry separation
|
|
-> spring reflow with coalesced final configure
|
|
-> rounded clipping
|
|
-> one capability-scoped trusted glass panel
|
|
```
|
|
|
|
The slice succeeds when A and B tile automatically; closing B immediately updates
|
|
the logical layout while A expands visually; A receives no per-frame configure
|
|
flood; pointer input remains aligned through inverse transforms; a private client
|
|
with only the `effects` capability can display a panel that blurs A; a public
|
|
client cannot bind that protocol; terminal changes refresh the backdrop; and all
|
|
interaction remains responsive and damage-driven.
|
|
|
|
Settings, XWayland, hardware multi-output, notifications, and theming MUST NOT
|
|
delay this slice.
|
|
|
|
## 20. Definition of v0
|
|
|
|
Ferese v0 is complete when a user can:
|
|
|
|
1. start a direct Ferese session;
|
|
2. run ordinary Wayland and XWayland applications;
|
|
3. copy, paste, drag, and use application popups;
|
|
4. tile, focus, move, resize, float, fullscreen, and close windows;
|
|
5. use multiple workspaces and outputs;
|
|
6. configure gaps, materials, corners, borders, shadows, and animations live;
|
|
7. use overview, launcher, OSD, and basic notifications;
|
|
8. control permitted behavior through `feresectl`;
|
|
9. recover from shell or settings crashes without losing client sessions; and
|
|
10. perform routine programming work without regular compositor restarts.
|
|
|
|
The intended result is a low-latency tiling environment with a restrained,
|
|
coherent system shell. Visual effects support hierarchy and usability; they do
|
|
not replace correctness, responsiveness, or security.
|