Simplify documentation and website; expand shortcut guides

This commit is contained in:
Samuel Onoja committed 2026-09-30 07:00:20 +01:00
1 parent 46509f880a
commit 4d60c296a7
26 files changed
+1060 -1032

No files matched your search

+29
View File
@@ -0,0 +1,29 @@
# Documentation
## Get started
- [Installation](installation.md): dependencies, login, previews, updates, and recovery.
- [Configuration](configuration.md): KDL syntax, layouts, input, shortcuts, and displays.
- [Why Ferese](why-ferese.md): layouts and the shared desktop theme.
## Use the desktop
- [Shortcuts and gestures](shortcuts.md): keyboard navigation, Super-drag, resize, and window grouping.
- [Desktop widgets](desktop-widgets.md): clocks and sticky notes.
- [Notifications](notifications.md): popups, history, and Do Not Disturb.
- [Lock screen](locking.md): appearance, automatic locking, and display sleep.
- [Screen sharing and recording](screen-sharing.md): choosing sources and stopping a session.
- [Desktop portals](portals.md): application integration and supported services.
## Develop Ferese
- [Development](development.md): builds, tests, and portal checks.
- [Compositor architecture](ferese-window-manager-spec.md).
- [Shell behavior](ferese-shell-design.md).
- [Theme components](ferese-theme-ui-spec.md).
- [Animation checks](animation-validation.md).
- [Nested resize checks](nested-resize-testing.md).
- [Client soak testing](native-soak-testing.md).
- [Performance measurements](performance-baseline.md).
- [Website development](../site/README.md).
+26
View File
@@ -0,0 +1,26 @@
# Animation regression checks
Run `cargo test -p ferese-animation -p ferese --offline` for transition,
resize requests, reduced motion, stacking, and refresh-rate regression tests.
For visual validation, use a disposable Ferese session with three tiled windows
containing text and at least one floating window. Test both the nested and DRM
backends. Record the session at its native refresh rate when comparing builds;
single screenshots cannot establish smoothness.
| Scenario | Expected result |
| --- | --- |
| Super+F on the middle column, then exit | Neighbours remain behind the expanding window. The shrinking window stays above them until the transition finishes. |
| Toggle Super+F repeatedly at 40–80 ms intervals | Every reversal starts at the presented frame. Corners, border and shadow follow the same zoom progress. The final state matches the last input. |
| Cycle width, then immediately Super+F | The old column-width spring cannot override maximized width. |
| Exit maximize or fullscreen and immediately resize or scroll | The current frame is retained when the target changes; scrolling resumes without a position jump. |
| Drag a floating window; resize from its top-left corner | The window follows the pointer directly. The opposite edge stays anchored. Delayed client commits do not move or raise it. |
| Zoom responsive and slow-redrawing clients | Each zoom requests the destination size once. Client redraws do not generate intermediate resize requests or restart the motion. |
| Zoom the leftmost and rightmost columns in and out | Each outer edge moves monotonically toward its destination; neither side trembles or reverses near the screen edge. |
| Disable borders and shadows, then zoom | Rounded corner pixels repaint through the final frame; no corner remnants remain. |
| Repeat on 60 Hz and 144 Hz outputs; move focus between them | Similar elapsed-time motion with no double advancement. Check frame timing on actual DRM hardware. |
| Enable reduced motion | Geometry and decorations reach their final values together. |
Zoom requests the destination client size once and scales the available buffer
along the transition. This avoids repeated content reflow during motion. It is
not a buffer snapshot or a guarantee of perfectly sharp intermediate text.
+81 -106
View File
@@ -2,8 +2,9 @@
Edit `~/.config/ferese/config.kdl` (or `$XDG_CONFIG_HOME/ferese/config.kdl`),
or open **Control Center → Settings**. Start with the [example](../packaging/config.kdl).
All supported configuration sections are listed below; [desktop widgets](desktop-widgets.md)
covers the complete clock and sticky-note fields separately.
This reference covers layouts, appearance, input, and desktop behavior.
See [Desktop widgets](desktop-widgets.md), [Notifications](notifications.md),
and [Desktop portals](portals.md) for their additional options.
## KDL syntax
@@ -44,8 +45,7 @@ its command as positional arguments. Duplicate fields are rejected.
The dotted paths in the reference tables below describe nested sections.
Settings preserves comments and custom fields when editing, while normalizing
indentation. Runtime configuration uses KDL only. KDL parsing requires Rust 1.95
or newer to build.
indentation. Configuration uses KDL.
## Saving and validation
@@ -74,15 +74,15 @@ Live config is limited to 60 KiB.
| `layout` key | Type / values | Default | Meaning |
| --- | --- | --- | --- |
| `mode` | `"scrolling"`, `"tree"` | `"scrolling"` | Workspace layout |
| `inner_gap` | number ≥ 0 | `10` | Between windows |
| `outer_gap` | number ≥ 0 | `4` | Around workspace edges |
| `smart_gaps` | boolean | `false` | Remove outer gaps for one tiled window |
| `inner-gap` | number ≥ 0 | `10` | Between windows |
| `outer-gap` | number ≥ 0 | `4` | Around workspace edges |
| `smart-gaps` | boolean | `false` | Remove outer gaps for one tiled window |
| `scrolling` key | Type / values | Default | Meaning |
| --- | --- | --- | --- |
| `default_column_width` | proportion > 0; or `"full"` | `0.5` | Width of new columns |
| `focus_strategy` | `"minimal"`, `"center_on_focus"`, `"paged"` | `"minimal"` | Viewport movement on focus |
| `width_presets` | array of widths | `[0.3333333333333333, 0.5, 0.6666666666666666, "full"]` | Super+R cycle; empty uses defaults |
| `default-column-width` | proportion > 0; or `"full"` | `0.5` | Width of new columns |
| `focus-strategy` | `"minimal"`, `"center_on_focus"`, `"paged"` | `"minimal"` | Viewport movement on focus |
| `width-presets` | array of widths | `[0.3333333333333333, 0.5, 0.6666666666666666, "full"]` | Super+R cycle; empty uses defaults |
`minimal` scrolls only to reveal the focused window. `center_on_focus` centers
it. `paged` packs columns into viewport-sized pages: halves form pairs, thirds
@@ -94,95 +94,73 @@ Changing default width preserves manually resized columns.
| Section / key | Type | Default | Meaning |
| --- | --- | --- | --- |
| `animations.enabled` | boolean | `true` | Enable motion |
| `animations.reduced_motion` | boolean | `false` | Disable animated motion |
| `animations.reduced-motion` | boolean | `false` | Disable animated motion |
| `animations.speed` | number > 0 | `1` | Higher is faster; `0.75` is slower |
| `animations.spring.mass` | number > 0 | `1` | Window-motion spring mass |
| `animations.spring.stiffness` | number > 0 | `700` | Spring stiffness |
| `animations.spring.damping` | number ≥ 0 | `53` | Spring damping |
| `animations.viewport_spring.mass` | number > 0 | `1` | Scrolling spring mass |
| `animations.viewport_spring.stiffness` | number > 0 | `320` | Scrolling stiffness |
| `animations.viewport_spring.damping_ratio` | number > 0 | `1` | `1` is critically damped |
| `animations.viewport-spring.mass` | number > 0 | `1` | Scrolling spring mass |
| `animations.viewport-spring.stiffness` | number > 0 | `320` | Scrolling stiffness |
| `animations.viewport-spring.damping-ratio` | number > 0 | `1` | `1` is critically damped |
## Appearance
Settings → Appearance offers six presets: **Ferese Blue** (default),
**Monochrome**, **Gruvbox**, **Dracula**, **Ayu Light**, and **Monokai**.
The two-row gallery previews each palette on a miniature desktop; a checkmark
identifies the active preset.
Ferese Blue uses the README logo's `#3D7BE6`. Each preset coordinates surfaces,
text, menu-bar colors, borders and the focus ring. Ayu Light also switches native
Settings controls to light styling. Presets preserve wallpaper, typography,
geometry and opacity settings; colors remain individually editable.
The community palettes are desktop adaptations with text shades adjusted for
readability. Sources: [Gruvbox](https://github.com/morhetz/gruvbox),
[Dracula](https://github.com/dracula/visual-studio-code),
[Ayu](https://github.com/ayu-theme/ayu-colors), and
[classic Monokai](https://github.com/microsoft/vscode/blob/main/extensions/theme-monokai/themes/monokai-color-theme.json).
Marketplace install counts informed the shortlist; they are not a measurement
of desktop users. Monochrome and Gruvbox replace similar blue palettes to keep
these six visually distinct.
Ferese Blue uses the logo accent, `#3D7BE6`. Presets update colors and preserve
wallpaper, fonts, corners, and opacity. Colors remain individually editable.
| Section / key | Type | Default | Meaning |
| --- | --- | --- | --- |
| `appearance.corner_radius` | number ≥ 0 | unset | Legacy fallback for shell radius |
| `appearance.inactive_dim.enabled` | boolean | `false` | Dim unfocused windows |
| `appearance.inactive_dim.amount` | number 0–1 | `0.15` | Darkening strength |
| `appearance.inactive_dim.duration_ms` | number ≥ 0 | `150` | Dimming and focus-ring transition; 0 snaps |
| `theme.typography.font_family` | string | system sans-serif | Shell, Settings and overview font |
| `appearance.corner-radius` | number ≥ 0 | unset | Legacy fallback for shell radius |
| `appearance.inactive-dim.enabled` | boolean | `false` | Dim unfocused windows |
| `appearance.inactive-dim.amount` | number 0–1 | `0.15` | Darkening strength |
| `appearance.inactive-dim.duration-ms` | number ≥ 0 | `150` | Dimming and focus-ring transition; 0 snaps |
| `theme.typography.font-family` | string | system sans-serif | Shell, Settings and overview font |
| `theme.background.path` | string | bundled Ferese wallpaper | Wallpaper image path; an existing selection overrides the default |
| `theme.background.mode` | `"fill"`, `"fit"` | `"fill"` | Crop or letterbox |
| `theme.material.style` | `"solid"`, `"translucent"` | `"solid"` | Shell background material |
| `theme.material.opacity` | number 0–1 | `0.78` | Shared shell background opacity for bars, menus, popovers, notifications and themed dialogs. Text/icons stay opaque; 0 hides the material. Solid mode is always opaque |
| `theme.material.blur_radius` | number ≥ 0 | `12` | Translucent backdrop blur, capped at 32; 0 disables |
| `theme.material.blur-radius` | number ≥ 0 | `12` | Translucent backdrop blur, capped at 32; 0 disables |
| `theme.colors` key | Default | Meaning |
| --- | --- | --- |
| `surface_base` | `"#111821"` | Material/card base color |
| `text_primary` | `"#F4F7FB"` | Main text |
| `text_muted` | `"#8793A2"` | Secondary text |
| `surface-base` | `"#111821"` | Material/card base color |
| `text-primary` | `"#F4F7FB"` | Main text |
| `text-muted` | `"#8793A2"` | Secondary text |
| `accent` | `"#3D7BE6"` | Active controls and solid focus ring |
| `border` | `"#FFFFFF18"` | Unfocused window border |
| `shadow` | `"#00000055"` | Window shadow color |
Ferese derives the text and icon color for filled accent controls from the
configured theme. It keeps the configured text color and, when necessary, shades
the accent fill slightly to reach a 4.5:1 contrast ratio. If that would require a
large color change, it chooses black or white instead. The same rule is shared by
native dialogs, settings selections, and notification badges. Hover, pressed,
and disabled accent-button states use the same policy. Small filled controls
resolve accent transparency against their theme surface so wallpaper cannot
make their labels unreadable. No separate per-application color setting is needed.
The target follows [normal-text contrast guidance](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html).
Filled controls choose a contrasting text color automatically.
| `theme.geometry` key | Type | Default | Meaning |
| --- | --- | --- | --- |
| `border_width` | number ≥ 0 | `1` | Window border thickness |
| `focus_ring_width` | number ≥ 0 | `2` | Focused border thickness |
| `window_radius` | number ≥ 0 | `14` | Managed-window corners, independent of the shell |
| `shell_radius` | number ≥ 0 | `14` | All shell surfaces, cards, widgets and interaction backgrounds; 0 makes them square |
| `top_bar_height` | number > 0 | `28` | Menu-bar height |
| `top_bar_margin_top` | integer ≥ 0 | `0` | Space above bar |
| `top_bar_window_gap` | integer ≥ 0 | `0` | Clearance below bar |
| `top_bar_margin_horizontal` | integer ≥ 0 | `0` | Bar side margins |
| `top_bar_radius` | number ≥ 0 | unset | Legacy fallback for shell radius |
| `panel_padding` | number ≥ 0 | `12` | Bar inner padding |
| `control_gap` | number ≥ 0 | `12` | Right-side control spacing |
| `border-width` | number ≥ 0 | `1` | Window border thickness |
| `focus-ring-width` | number ≥ 0 | `2` | Focused border thickness |
| `window-radius` | number ≥ 0 | `14` | Managed-window corners, independent of the shell |
| `shell-radius` | number ≥ 0 | `14` | All shell surfaces, cards, widgets and interaction backgrounds; 0 makes them square |
| `top-bar-height` | number > 0 | `28` | Menu-bar height |
| `top-bar-margin-top` | integer ≥ 0 | `0` | Space above bar |
| `top-bar-window-gap` | integer ≥ 0 | `0` | Clearance below bar |
| `top-bar-margin-horizontal` | integer ≥ 0 | `0` | Bar side margins |
| `top-bar-radius` | number ≥ 0 | unset | Legacy fallback for shell radius |
| `panel-padding` | number ≥ 0 | `12` | Bar inner padding |
| `control-gap` | number ≥ 0 | `12` | Right-side control spacing |
| Section / key | Type | Default | Meaning |
| --- | --- | --- | --- |
| `theme.surface.bar.background` | color | `"#1C202EF2"` | Shell fallback background; compositor materials use `surface_base` |
| `theme.surface.bar.text_primary` | color | `"#F0F3FA"` | Bar text/icons |
| `theme.surface.bar.text_muted` | color | `"#AAB4C7"` | Inactive bar foreground |
| `theme.shadow.soft.offset_y` | number | `4` | Window shadow vertical offset |
| `theme.surface.bar.background` | color | `"#1C202EF2"` | Shell fallback background; compositor materials use `surface-base` |
| `theme.surface.bar.text-primary` | color | `"#F0F3FA"` | Bar text/icons |
| `theme.surface.bar.text-muted` | color | `"#AAB4C7"` | Inactive bar foreground |
| `theme.shadow.soft.offset-y` | number | `4` | Window shadow vertical offset |
| `theme.shadow.soft.blur` | number ≥ 0 | `18` | Window shadow softness |
| `theme.shadow.soft.opacity` | number 0–1 | `0.20` | Window shadow strength |
Solid materials omit blur; translucent materials blur behind the surface color.
True fullscreen removes decorations. Focus transitions never scale the content.
Optional `theme.focus_ring.gradient` and `theme.border.gradient` use the same
Optional `theme.focus-ring.gradient` and `theme.border.gradient` use the same
keys: required `from` and `to` colors, and `angle` (number, default `0`). Angles
are clockwise: 0 is left-to-right, 90 top-to-bottom. Omit the gradient section
for solid `accent` or `border` colors.
@@ -199,16 +177,25 @@ theme {
}
```
## Workspaces
A single-output session starts with workspace 1. Numbered workspace shortcuts create
workspaces on demand; switching forward past the last workspace also creates the
next one when the current workspace contains windows. Empty workspaces are
cleaned up automatically, keeping at most one empty workspace in addition to
any empty workspace currently shown on another display. Additional displays
receive their own workspace.
## Input
| `input` key | Type | Default | Meaning |
| --- | --- | --- | --- |
| `focus_follows_mouse` | boolean | `false` | Focus visible windows under the pointer without raising them or scrolling; click or use keyboard focus to reveal a window |
| `xkb_layout` | nonempty string | `"us"` | XKB layout |
| `xkb_variant` | string | `""` | XKB variant |
| `xkb_options` | string array | `[]` | XKB options |
| `repeat_rate` | integer > 0 | `25` | Repeats per second |
| `repeat_delay_ms` | integer ≥ 0 | `600` | Delay before repeat |
| `focus-follows-mouse` | boolean | `false` | Focus visible windows under the pointer without raising them or scrolling; click or use keyboard focus to reveal a window |
| `xkb-layout` | nonempty string | `"us"` | XKB layout |
| `xkb-variant` | string | `""` | XKB variant |
| `xkb-options` | string array | `[]` | XKB options |
| `repeat-rate` | integer > 0 | `25` | Repeats per second |
| `repeat-delay-ms` | integer ≥ 0 | `600` | Delay before repeat |
With focus-follows-mouse enabled, hovering a visible part of a window changes
keyboard focus without raising it or moving the scrolling viewport. Clicking or
@@ -221,7 +208,7 @@ Clicking a workspace card's background switches workspaces while keeping Overvie
open. Previews follow layout order and use the configured shell font and colors.
The touchpad device keys are booleans, all defaulting to `true`: `tap`,
`natural_scroll` and `disable_while_typing`. These apply to DRM devices; nested
`natural-scroll` and `disable-while-typing`. These apply to DRM devices; nested
previews use the host's physical input settings.
Three-finger swipes navigate on release: up goes to the next workspace on the
@@ -254,6 +241,8 @@ Adjust recognition distance in **Settings → Keyboard & mouse**, or with
## Commands and bindings
See [Shortcuts and gestures](shortcuts.md) for the full default keymap and mouse actions.
`commands` maps arbitrary names to nonempty argument arrays. The built-in
`terminal "foot"` can be overridden. Commands run directly, without a shell.
@@ -292,7 +281,7 @@ Super+Shift+1–9 move to workspace; Super+R width cycle; Super+C center;
Super+[/] consume/expel; Super+F maximize; Super+Shift+F fullscreen;
Super+M layout; Super+Shift+Space floating; Super+Tab overview;
Super+Shift+S area screenshot; Print Screen whole-screen screenshot;
Super+Shift+E immediate logout.
Super+Shift+E logout confirmation.
Hold Super and drag a floating window with the left mouse button to move it.
Hold Super and drag with the right mouse button to resize it from the nearest
@@ -314,19 +303,8 @@ the same save and copy workflow.
`ferese-screenshot --all` captures every enabled output as one image.
The editor window is sized from the captured image rather than the output, so a
small area selection does not open a large window. Sizing is advisory: the
compositor floats the editor at whatever size Satty requests, clamped to the
workspace.
A capture is assembled from the readback each output produces when it is next
redrawn, so a request is abandoned if an output has not been redrawn within 30
seconds. That is reported as an error rather than a partial or empty image, and
the request holds no resources once it is abandoned. Nothing is left in the
runtime directory: `feresctl screenshot` writes a private file that the caller
opens and unlinks, and any file a dead client leaves behind is reclaimed after
an hour.
Satty opens as a floating window sized for the captured image and constrained
to the workspace.
## Window rules
@@ -335,7 +313,7 @@ window rules can override that behavior.
| `window-rule` key | Type | Default / meaning |
| --- | --- | --- |
| `app_id` | nonempty string | Optional exact match; case-insensitive, `.desktop` suffix ignored |
| `app-id` | nonempty string | Optional exact match; case-insensitive, `.desktop` suffix ignored |
| `title` | nonempty string | Optional exact, case-sensitive match |
| `transient` | boolean | Optional parent-dialog match |
| `workspace` | integer > 0 | Leave placement unchanged |
@@ -369,8 +347,8 @@ are connected wins. Find connector names and persistent identities using
| `transform` | enum below | `"normal"` |
| `position` | `[integer, integer]` | Automatic horizontal placement |
Transforms: `normal`, `rotate_90`, `rotate_180`, `rotate_270`, `flipped`,
`flipped_90`, `flipped_180`, `flipped_270`. Unspecified connected displays stay
Transforms: `normal`, `rotate-90`, `rotate-180`, `rotate-270`, `flipped`,
`flipped-90`, `flipped-180`, `flipped-270`. Unspecified connected displays stay
enabled with defaults. Disabling every usable output is rejected.
```kdl
@@ -390,10 +368,10 @@ returns. Ferese does not change the system's suspend policy.
| `status` key | Type | Default | Meaning |
| --- | --- | --- | --- |
| `keybinding-guide` | boolean | `true` | Show the active shortcut guide at login until disabled in Settings → Shortcuts. |
| `window_title` | boolean | `true` | Focused window title in the bar center when space allows |
| `battery_percentage` | boolean | `true` | Show percentage beside icon |
| `low_battery_threshold` | integer 0–100 | `20` | Warning-color threshold |
| `settings_command` | argument array | `["ferese-settings"]` | Settings launcher; `[]` hides the action |
| `window-title` | boolean | `true` | Focused window title in the bar center when space allows |
| `battery-percentage` | boolean | `true` | Show percentage beside icon |
| `low-battery-threshold` | integer 0–100 | `20` | Warning-color threshold |
| `settings-command` | argument array | `["ferese-settings"]` | Settings launcher; `[]` hides the action |
## Login items and locking
@@ -443,22 +421,19 @@ environment for screenshots to work.
Text input is available by default; Ctrl+Alt+Escape releases an active shortcut
inhibitor. Use `feresectl --help` for runtime control commands.
## Notifications
Settings → Notifications controls popups, Do Not Disturb, and the default timeout. See [Notifications](notifications.md) for KDL options and app behavior.
A single-output session starts with workspace 1. Numbered workspace shortcuts create
workspaces on demand; switching forward past the last workspace also creates the
next one when the current workspace contains windows. Empty workspaces are
cleaned up automatically, keeping at most one empty workspace in addition to
any empty workspace currently shown on another display. Additional displays
receive their own workspace.
Shell rounding is controlled in Settings → Appearance → Shell corner radius.
Small controls cap the radius to fit their size. Window rounding remains under
Settings → Windows. `theme.geometry.shell_radius` takes precedence over the old
`appearance.corner_radius` and then `theme.geometry.top_bar_radius` keys; when
Settings → Windows. `theme.geometry.shell-radius` takes precedence over the old
`appearance.corner-radius` and then `theme.geometry.top-bar-radius` keys; when
none are set, the shell uses 14 px. Legacy clock/note radius fields no longer
override shell rounding.
Power actions use a centered system confirmation. The `exit` key binding (Super+Shift+E by default) requests logout confirmation; `feresectl request-logout` does the same. `feresectl exit` remains an immediate administrative exit.
## Power and logout
Power actions use a centered confirmation. **Super+Shift+E** and
`feresectl request-logout` ask to log out. `feresectl exit` ends the session
immediately.
## Notifications
See [Notifications](notifications.md) for popups, Do Not Disturb, and timeout options.
+25 -33
View File
@@ -1,13 +1,8 @@
# Desktop widgets
The shell's desktop clock sits above the wallpaper and behind normal windows.
Drag the clock to reposition it; it reserves no workspace space.
All desktop widgets share dragging: grab the clock or a note's title. Release
to save the position, or press Escape to cancel.
Dragging stops at other visible widgets on the same output; edges can slide
along each other. Disabled widgets and widgets on other outputs do not block it.
Each matching output gets a small, transparent surface—not a full-screen canvas.
Lock-screen rendering remains separate and does not expose the desktop clock.
Clock and sticky-note widgets sit above the wallpaper and behind windows.
Drag the clock or a note's title to move it. Release to save, or press Escape to
cancel. Widgets stop at each other's edges on the same display.
Enable it in Settings → Desktop widgets or in your Ferese config:
@@ -42,42 +37,40 @@ desktop-widgets {
Positions: `top_left`, `top_center`, `top_right`, `center_left`, `center`,
`center_right`, `bottom_left`, `bottom_center`, `bottom_right`.
Margins affect anchored axes only: a centered horizontal position ignores
`margin_x`; a centered vertical position ignores `margin_y`.
`margin-x`; a centered vertical position ignores `margin-y`.
All geometry and text sizes use logical pixels and follow the output's scale.
Increase the widget width/height for longer formats or larger text.
Pixel is the default clock style: heavy hours above minutes, alternating theme colors, and a small plain date above. Its bundled Cantarell display font can be replaced with the font selector. Minimal retains the plain time/date layout. Pixel zero-pads hours and hides am/pm for the stacked display. Formats with seconds or additional text retain their full formatted label on one line. Both styles shrink time text to fit smaller widget dimensions; an explicit text color overrides the alternating colors.
Choose **Pixel** for stacked hours and minutes with alternating theme colors,
or **Minimal** for a plain time/date layout. Pixel zero-pads hours and hides am/pm.
Formats containing seconds or extra text use a single line. Both styles shrink
time text to fit. Select a different font in Settings, or set `font-family`.
Saving reloads appearance and labels live. Placement, size, output filters and
enable/disable changes recreate only clock surfaces; normal windows and the bar
remain intact. Disconnecting an output destroys its clock; reconnecting creates
one if its name matches the filter. Invalid configuration keeps the last working
configuration. Theme changes apply to colors/fonts that have no explicit override.
Formatting uses [Jiff's strftime-compatible formats](https://docs.rs/jiff/latest/jiff/fmt/strtime/index.html).
Seconds update on the shell's existing timer. Without seconds, unchanged labels
produce no additional clock buffer damage; there is no widget animation loop.
Widget placement can be changed by dragging or through Settings and KDL.
Sticky notes also support editing directly on the desktop.
Changes apply live. Colors and fonts follow the theme unless overridden.
Formatting uses [strftime-compatible formats](https://docs.rs/jiff/latest/jiff/fmt/strtime/index.html).
Use `%H:%M` for 24-hour time or `%-I:%M %p` for 12-hour time.
## Clock limits
Clock and note corners follow the shared shell radius. Their `radius` fields
are accepted for compatibility but do not override it.
All clock keys are optional. The example enables the clock (disabled by default);
the other values illustrate defaults or their empty-string equivalents.
`outputs` is an array of strings; `enabled`, `bold`, `show_date`, `lowercase` are
`outputs` is an array of strings; `enabled`, `bold`, `show-date`, `lowercase` are
booleans; `width`, `height`, and margins are integers. Other sizes/opacity are numbers.
| Fields | Accepted range |
| --- | --- |
| `width`, `height` | 64–1600, 32–800 |
| `margin_x`, `margin_y` | 0–8192 |
| `time_size`, `date_size` | 8–240, 8–96 |
| `margin-x`, `margin-y` | 0–8192 |
| `time-size`, `date-size` | 8–240, 8–96 |
| `opacity` | 0–1 |
| `gap`, `padding`, `radius` | 0–64, 0–64, 0–128 |
| `font_family`, `time_format`, `date_format` | At most 128 bytes each |
| `font-family`, `time-format`, `date-format` | At most 128 bytes each |
| `outputs` | At most 32 exact output names, each 1–128 bytes |
Formats must be valid; `time_zone` must be empty or a recognized IANA name.
Formats must be valid; `time-zone` must be empty or a recognized IANA name.
Colors are empty (inherit), `#RRGGBB` or `#RRGGBBAA`; all numeric values must be finite.
## Sticky notes
@@ -87,7 +80,8 @@ click Edit for its multiline editor, then Done to finish. Text autosaves after
500 ms without typing. Escape cancels a drag or finishes editing. Position saves
on release; dragging changes the anchor to `top_left` and saves pixel margins.
Title, size and style are also editable in Settings. Notes persist in the config.
Choose each note’s font in Settings using the installed-font dropdown or enter a family name manually. Default font inherits the interface font. In KDL, use `font-family "Comfortaa"` inside the `note` node.
Choose a note’s font in Settings or set `font-family "Comfortaa"` in its `note`
node. Without an override, notes use the interface font.
These are plain-text notes, not Markdown or HTML.
@@ -99,9 +93,7 @@ desktop-widgets {
Repeat the `note` node for additional notes; IDs must be unique. Cards stay behind windows
and take keyboard focus only when clicked. Set `interactive #false` for a fully
click-through card. Long text wraps; increase height if needed. Notes have no idle
animation loop. Clock and notes share a compact drag preview moved by the
compositor, not an output-sized canvas. It is released when dragging ends.
click-through card. Long text wraps; increase height if needed.
| Key | Type | Default / accepted values |
| --- | --- | --- |
@@ -112,10 +104,10 @@ compositor, not an output-sized canvas. It is released when dragging ends.
| `text` | string | `""`; multiline plain text, maximum 16 KiB |
| `outputs` | string array | `[]` means all; at most 32 exact names, each 1–128 bytes |
| `anchor` | string | `"top_right"`; same nine positions as clock |
| `margin_x`, `margin_y` | integer | `48`, `80`; 0–8192; anchored edges only |
| `margin-x`, `margin-y` | integer | `48`, `80`; 0–8192; anchored edges only |
| `width`, `height` | integer | `320`, `240`; 120–1200, 80–1200 |
| `font_family` | string | omitted/empty follows desktop font; maximum 128 bytes |
| `text_size`, `title_size` | number | `16`, `18`; 8–96 |
| `font-family` | string | omitted/empty follows desktop font; maximum 128 bytes |
| `text-size`, `title-size` | number | `16`, `18`; 8–96 |
| `color` | string | omitted/empty/`"theme"` follows primary text; or hex color |
| `background` | string | omitted/`"theme"` follows surface base; empty is transparent; or hex color |
| `opacity` | number | `0.9`; 0–1, multiplies text/background alpha |
+80
View File
@@ -0,0 +1,80 @@
# Development
Install the [build dependencies](installation.md#requirements), then run:
```sh
cargo build --release --locked --workspace
cargo test --workspace --locked
cargo fmt --all --check
```
Use release builds for performance and animation checks. Preview changes in a
[nested session](installation.md#preview-and-logs) before testing hardware behavior.
Keep another desktop available for direct-session recovery.
## Test guides
- [Animation checks](animation-validation.md): transitions, interruption, and hit testing.
- [Nested resize checks](nested-resize-testing.md): wallpaper and bar responsiveness.
- [Client soak testing](native-soak-testing.md): repeated client lifecycle and resource growth.
- [Performance measurements](performance-baseline.md): sampling and historical results.
The [compositor](ferese-window-manager-spec.md), [shell](ferese-shell-design.md),
and [theme](ferese-theme-ui-spec.md) references describe the main code boundaries.
## Portal checks
Run backend unit tests and compare D-Bus contracts against installed portal XML:
```sh
cargo test --locked -p xdg-desktop-portal-ferese
python3 scripts/tests/test_portal_contracts.py
```
The contract test uses a private bus. It checks signatures, appearance changes,
invalid-config retention, and caller authorization. It does not prove complete
compatibility with every app or open consent dialogs.
With release binaries built, run isolated integration checks:
```sh
FERESE_TEST_SHORTCUTS=1 python3 scripts/tests/test_shortcuts_isolated.py
FERESE_TEST_WINDOW_CAPTURE=1 python3 scripts/tests/test_window_capture_isolated.py
FERESE_TEST_INHIBIT=1 python3 scripts/tests/test_inhibit_isolated.py
FERESE_TEST_RESTORE=1 python3 scripts/tests/test_restore_isolated.py
FERESE_TEST_WINDOW_STREAM=1 python3 scripts/tests/test_window_stream_isolated.py
```
These cover shortcut cleanup, window capture, inhibitors, restored permissions,
and live window streams. Check each script's dependencies before running it.
They use disposable nested sessions or private buses.
For monitor streaming and lock revocation, install Python GI/GStreamer with
`pipewiresrc`, `dbus-daemon`, `xdg-desktop-portal`, and `bwrap`, then run:
```sh
FERESE_TEST_PORTAL=1 \
FERESE_TEST_PORTAL_BINARY=target/release/xdg-desktop-portal-ferese \
python3 scripts/tests/test_portal_isolated.py
```
Add `FERESE_TEST_PORTAL_CONSENT=1` to exercise the picker. Choose the temporary
display and click **Share**. Tests must target the nested preview, not the host desktop.
Check real encoding and the private InputCapture transport:
```sh
cargo test --locked -p xdg-desktop-portal-ferese --bin ferese-record encoder_finishes_a_real_webm -- --ignored
cargo test --locked -p ferese input_capture::tests
cargo test --locked -p xdg-desktop-portal-ferese eis::tests -- --ignored
```
Encoding needs the recorder's GStreamer plugins. The transport test needs libei
and uses synthetic input over a private socket pair. Hardware pointer barriers,
multiple monitors, display sleep, and live authentication still need session tests.
## Report a bug
Include the commit or installed release, reproduction steps, backend (nested or
direct), monitor scales/transforms, and relevant logs. Avoid publishing passwords,
access tokens, or private window content.
+65
View File
@@ -0,0 +1,65 @@
# Shell behavior
This is a maintainer reference. User options are documented in
[Configuration](configuration.md). Common UI belongs in
[ferese-theme](../crates/ferese-theme/README.md).
## Bar and popovers
The shell creates a bar for each output. Workspace buttons use compositor state
and appear in numeric order. The active window title occupies the center when
space allows. Date and time form one calendar button. Right-side controls open
sound, battery, notifications, recording, and Control Center popovers.
A popover is anchored to its control and constrained to the same output.
Opening another primary popover replaces the current one. Popovers reserve no
workspace space; the bar reserves its configured height and margins.
True fullscreen hides the bar without changing the preserved tiled layout.
Use real service availability for controls. Handle missing services and failed
actions without blocking the UI. Settings opens as a separate application.
## Overview
The compositor presents window previews; the shell supplies labels and workspace
controls. Do not continuously copy windows into the shell process.
Clicking a workspace background selects that workspace and keeps overview open
so its windows can be selected. Clicking a window activates that exact window.
Directional keys change selection, Enter activates it, and Escape dismisses.
Activation must use the selected window ID, not the window focused before overview.
## Focus and dialogs
The bar and notification popups do not normally take keyboard focus. Interactive
shell surfaces restore the previous valid app focus when dismissed.
Power actions and logout use a centered system confirmation, including blockers
when present. Input outside the confirmation must not reach applications.
Authentication, screen-sharing, and recording dialogs are separate themed windows.
Use one visible frame, content-sized height, and an output-sized height limit.
Scrollable lists must leave headings and action buttons accessible.
Attach dialog material to the content card using the shared theme helper.
Match blur regions to the displayed card throughout opening and resize; do not
paint an opaque window behind a translucent card.
## Notifications and widgets
The shell provides the standard notification service. Normal popups respect
Do Not Disturb; critical messages bypass it. History is held in memory and clears
when the shell exits. See [Notifications](notifications.md).
Clock and note widgets sit behind application windows, reserve no workspace space,
and save positions after dragging. Only interactive notes take keyboard focus.
See [Desktop widgets](desktop-widgets.md).
## Lifecycle
Shell failure must not destroy managed windows or workspace state. Reconstruct
bars and controls from compositor state after reconnecting, and clear dead shell
focus. Notification history is not reconstructed from disk.
Keep slow service queries, image loading, and filesystem work away from UI event
handling. Stop animation work once the UI settles. Validate popover placement,
fullscreen transitions, dialog dismissal, and shell restart in a nested session.
+81
View File
@@ -0,0 +1,81 @@
# Theme components
Ferese's native apps use [ferese-theme](../crates/ferese-theme/README.md) for
colors, fonts, controls, menus, icons, calendar elements, and dialog materials.
Use [Configuration → Appearance](configuration.md#appearance) for supported keys
and defaults.
## Shared appearance
The six presets are Ferese Blue, Monochrome, Gruvbox, Dracula, Ayu Light, and
Monokai. Ferese Blue uses the logo accent, `#3D7BE6`. Presets change colors while
preserving wallpaper, typography, corners, and opacity.
The interface font applies to shell text and native dialogs. Clock and note
widgets can override it. Managed-window corners and shell corners are separate
settings; small controls cap their corners to fit their size.
## Materials
Two material styles are supported:
| Style | Background |
| --- | --- |
| `solid` | Opaque theme surface |
| `translucent` | Theme tint over blurred background content |
Bars, popovers, notifications, and themed dialogs share `theme.material.opacity`
and `blur-radius`. Text and icons stay opaque. An opacity of zero hides the
material; blur radius zero disables blur. Solid mode stays opaque.
```kdl
theme {
colors {
accent "#3D7BE6"
}
material {
style "translucent"
opacity 0.78
blur-radius 12
}
geometry {
shell-radius 14
window-radius 14
}
}
```
The compositor owns blur and tint. `material::ModalMaterial` attaches native
dialogs to that rendering path. Keep the attachment alive with the window and
make the attached client background transparent. Outside Ferese, retain a client
background so the dialog remains usable.
## Components
| Module | Use |
| --- | --- |
| `palette` | Resolve configuration colors and build the native toolkit theme |
| `controls` | Surfaces, navigation, text buttons, and input styles |
| `menus` | Rows, headings, sections, separators, switches, and sliders |
| `calendar` | Date/time labels, clock digits, and calendar grids |
| `icons` | Shared SVG assets, sizing, and tint |
| `typography` | Shared font selection and text |
| `contrast` | Readable labels on filled controls |
| `material` | Attach modal cards to compositor materials |
Applications provide data, layout, actions, and animation progress. Extend shared
components when a style is needed in multiple apps. Avoid hardcoded app-specific
fonts, foreground colors, or opacity values.
## UI checks
- Check text and icons in all six presets, including Ayu Light.
- Check rest, hover, pressed, focused, selected, and disabled controls.
- Keep accent-button labels readable after typing or changing focus.
- Keep controls usable with solid materials and blur disabled.
- Align material regions with the visible card at every output scale.
- Give dialogs one frame; cap their height and scroll their content, preserving actions.
- Respect reduced motion and stop requesting animation frames after settling.
Theme changes affect appearance; they do not change focus, workspace ownership,
input permissions, or authentication policy.
+109
View File
@@ -0,0 +1,109 @@
# Compositor architecture
This is a maintainer reference. For supported configuration and shortcuts, use
[Configuration](configuration.md); for builds and checks, use [Development](development.md).
## Processes and crates
| Component | Responsibility |
| --- | --- |
| `ferese` | Wayland clients, windows, workspaces, input, outputs, and rendering |
| `ferese-shell` | Bar, popovers, notifications, widgets, and system confirmations |
| `ferese-settings` | Configuration editor |
| `feresectl` | Command-line control |
| `ferese-lock` | Lock-screen UI and PAM authentication |
| `ferese-polkit-agent` | System authentication prompts |
| `xdg-desktop-portal-ferese` | Desktop portal requests and PipeWire streams |
| `ferese-record` | Video encoding and saving |
The compositor uses Smithay and calloop. The direct backend renders through
DRM/KMS; the nested backend renders into a window on another Wayland desktop.
Native UIs use libcosmic. Layout, animation, configuration, IPC, protocols, and
[theme components](../crates/ferese-theme/README.md) live in separate crates.
## State and ownership
The event loop owns compositor state and Wayland objects. Background workers
return results through bounded channels; results must be checked against current
window, output, and request identities before application.
Keep these relationships valid after a state change:
- Each window belongs to one workspace and appears once in its layout or floating list.
- Each attached workspace belongs to one output; each output has one active workspace.
- A window's output follows workspace ownership.
- Fullscreen preserves the underlying layout or floating placement.
- Focus points to a live window or authorized shell surface.
- Workspace focus history is distinct from current keyboard focus.
Monitor removal migrates workspaces to a remaining output. Returning monitors
restore eligible workspaces; explicit user moves take priority. Selecting a
workspace visible on another monitor focuses that monitor.
## Layout and presentation
Scrolling layouts arrange columns horizontally. Windows within a column divide
its height. A workspace owns its viewport; presentation subtracts that viewport
from window positions. Tree layouts use nested splits and stacks. Layout changes
preserve window membership and floating placement.
Keep layout targets, animated rectangles, and committed client sizes separate.
A layout transition requests the destination client size rather than sending
one configure per animation frame. Available content is scaled and clipped until
the matching client buffer arrives. Hit testing uses the same presented geometry.
Interrupted animations continue from their current position.
Input and layout use logical pixels. Rendering converts them using the output's
scale and transform. Application popups use their parent's output and presented
position when calculating bounds.
## Rendering and frame delivery
Render damage from client commits, animations, effects, cursor motion, and output
changes. Movement damages both old and new bounds, including shadows and blur.
Output-specific callbacks follow window ownership. Presentation feedback is
collected only for surfaces the renderer actually displayed on that output.
An unchanged frame still permits paced client callbacks without submitting a
new display buffer.
Animations use elapsed time across outputs, with a timer while transitions remain
active. An idle focused monitor must not stall another monitor's animation.
The compositor renders blur from content behind the requesting surface. Material
regions and client content must share coordinates during placement, scaling,
and animation. See [Theme components](ferese-theme-ui-spec.md).
## Input
Use milliseconds for keyboard, pointer, touch, and gesture protocol events;
relative-motion timestamps retain microseconds. Keep keyboard focus, pointer
focus, and window stacking separate. Hover focus does not scroll or raise windows.
Pointer grabs and constraints must end cleanly when their surface disappears.
A locked pointer's position hint is stored while locked, then resolved against
current surface geometry and clamped to an output when the lock is released.
## IPC and private protocols
User control uses `$XDG_RUNTIME_DIR/ferese/control.sock`, restricted to the
session user. The parent directory is private and peers are checked by UID.
IPC frames are length-prefixed JSON with bounded message sizes, workers, and queues.
See `feresectl --help` for commands.
Private Wayland capabilities grant shell control or material effects to trusted
components. Public layer-shell access alone grants neither. Trusted connections
and descriptors must not leak to launched applications. Capture paths check
session policy and reject capture while locked.
Session locking must protect every output, including hotplug, and remain secure
if the lock UI crashes. [Portal integration](portals.md) documents application
consent and the currently supported services.
## Review and testing
Check output ownership, presented geometry, focus restoration, request lifetimes,
and bounded work whenever changing a protocol or rendering path. Test both
backends; a nested result does not establish hardware behavior.
Use the [animation](animation-validation.md), [resize](nested-resize-testing.md),
and [soak](native-soak-testing.md) checks alongside the workspace tests.
+24 -58
View File
@@ -6,23 +6,12 @@ TTY. You do not need to replace your existing login manager or desktop.
## Requirements
You need a working graphics driver with DRM/KMS and EGL support, an authenticated
local login session with seat access through logind or seatd, D-Bus, and
Linux-PAM for the native locker. A nested preview needs an existing Wayland
desktop instead of direct access to the graphics device.
Use a Wayland-capable graphics driver and a local user session with seat access
through logind or seatd. A nested preview runs inside an existing Wayland desktop.
Build with **Rust 1.95 or newer**, a C/C++ toolchain, CMake, pkg-config, and the
native development libraries below. The installer builds Ferese, its shell,
Settings, command-line tools, locker, polkit agent, and desktop portal backend together. It does not install system
packages or enable background services at boot. On systemd systems, the installed
session helper activates `ferese-session.target` after exporting the compositor's
Wayland display, and stops it when the shell exits. This keeps
`graphical-session.target` active so desktop portals can start. The native portal
is D-Bus activated and supervised by systemd; it stops with the session so a
previous release cannot remain running after logout. Session startup also refreshes
an already-running native portal after importing the current display environment.
Nested previews
do not alter the host's activation environment or session targets.
Building requires **Rust 1.95 or newer**, a C/C++ toolchain, CMake, pkg-config,
and the libraries below. The installer builds all Ferese components; install
system packages separately.
### Fedora
@@ -60,11 +49,9 @@ sudo apt install build-essential git curl cmake pkg-config libwayland-dev \
gstreamer1.0-plugins-good
```
These are package recipes, not a claim that every distribution release has been
tested. Fedora is the currently exercised installation path. Older releases
may need newer packages or additional repositories. For other distributions,
install the equivalent libraries; see the upstream
[Smithay dependencies](https://github.com/Smithay/smithay#system-dependencies).
Fedora is the tested installation path. Package names and versions may differ
on other distributions. See [Smithay dependencies](https://github.com/Smithay/smithay#system-dependencies)
for equivalent libraries.
### Rust toolchain
@@ -98,32 +85,17 @@ The script builds locked release binaries, then requests administrator access
through `sudo` or `pkexec` for installation. Do not run the whole build as root.
The first build needs network access to fetch Rust dependencies.
The installer adds:
The installer adds the session launcher, desktop tools, portal backend, login
entry, icons, default wallpaper, and example config. Releases live under
`/usr/local/lib/ferese/releases/`; commands live under `/usr/local/bin/`.
The `current` and `previous` links support updates and rollback.
- Versioned releases under `/usr/local/lib/ferese/releases/`, with `current` and
`previous` links for upgrades and rollback.
- Commands under `/usr/local/bin/`, including `ferese-session`, `ferese-settings`,
`feresectl`, `ferese-lock`, `ferese-polkit-agent`, `ferese-screenshot`, and
`xdg-desktop-portal-ferese`, and `ferese-record`.
- D-Bus activation and Ferese-specific desktop portal registration.
- A Wayland session entry at `/usr/share/wayland-sessions/ferese.desktop`.
- A Settings application entry, icons, the default wallpaper, and an example config.
- `/etc/pam.d/ferese-lock`, using your distribution's authentication stack.
An existing administrator-supplied policy is preserved.
Your existing configuration and other desktop sessions are preserved. The
installer does not change your default session or login manager.
Ferese starts its own polkit authentication agent with the shell. Password
prompts follow the configured font, accent, surface color, and corner radius.
Authentication still uses polkit's system helper and policy. The agent starts
only inside a Ferese session; logging into another desktop leaves its agent
unchanged. Restart your Ferese session after upgrading to use the new prompt.
Your configuration, other desktop sessions, and existing PAM policy are preserved.
Ferese starts its authentication agent and portal services with the session.
### Initial configuration
Ferese has built-in defaults. To start from the example without replacing an
existing config:
Ferese has built-in defaults. To start from the example without replacing your config:
```sh
mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/ferese"
@@ -144,9 +116,7 @@ command in Settings if you prefer another terminal.
Save your work and log out. Open your login screen's session selector, choose
**Ferese**, and sign in. Any manager that supports Wayland sessions and reads
`/usr/share/wayland-sessions/` can discover the installed entry. The selector's
location and whether it appears before or after choosing your account depend
on the login manager.
`/usr/share/wayland-sessions/` can discover the installed entry.
If Ferese is missing, confirm that the session entry exists and its launcher is
executable:
@@ -169,15 +139,9 @@ If your greeter asks for a session command instead of reading desktop files, use
/usr/local/bin/ferese-session
```
For a setup such as greetd, configure that as the command launched **after user
authentication**, using your greeter's session-command option. Preserve the
existing authentication and greeter configuration. Do not set Ferese itself as
the privileged greeter process. See the
[greetd documentation](https://sr.ht/~kennylevinsen/greetd/) for your chosen frontend.
The launcher starts the compositor and shell together, sets the desktop
environment variables, and starts a D-Bus session when one is not already
available. Launch it instead of the bare compositor for a complete desktop.
For greetd, use this as the session command after authentication; see the
[greetd documentation](https://sr.ht/~kennylevinsen/greetd/).
The launcher starts the compositor, shell, and session services together.
### From a TTY
@@ -229,7 +193,8 @@ inside an existing Wayland desktop:
```sh
cargo build --release --locked -p ferese -p ferese-shell -p ferese-settings -p feresectl -p ferese-lock -p xdg-desktop-portal-ferese
target/release/ferese --backend nested --grant-effects --grant-shell-control -- \
FERESE_ENABLE_SCREENCOPY=1 target/release/ferese --backend nested \
--grant-effects --grant-shell-control -- \
target/release/ferese-shell
```
@@ -283,12 +248,13 @@ Installer options:
`--dry-run` prints the planned commands. `--offline` requires dependencies to be
cached already. `--skip-build` installs the binaries already in `target/release/`;
use it only after building all six components from the intended revision.
use it only after building all components from the intended revision.
Run `./scripts/install.sh --help` for all options.
## Logout and recovery
Save your work before **Super+Shift+E** or `feresectl exit`: logout is immediate.
Use **Super+Shift+E** or `feresectl request-logout` to ask for logout confirmation.
`feresectl exit` ends the session immediately; save your work first.
If Ferese freezes, switch to another TTY with **Ctrl+Alt+F1–F12**, sign in, and
identify the affected compositor process:
+38 -83
View File
@@ -1,79 +1,43 @@
# Native locker
# Lock screen
Ferese Lock uses `ext-session-lock-v1`, not a fullscreen application overlay.
Each output receives its own surface, including outputs added while locked.
The compositor protects the desktop if the client exits or crashes.
Ferese Lock follows your desktop wallpaper, colors, font, and shell corners.
Open **Settings → Lock screen** to change its clock, date, wallpaper softness,
and inactivity delays.
## Preview
## Preview and lock
```sh
cargo build --release --locked -p ferese-lock
target/release/ferese-lock --preview
ferese-lock --preview # ordinary window; does not lock or check passwords
ferese-lock # lock; returns after compositor confirmation
ferese-lock --foreground # stay attached until unlock
```
This is an ordinary, closable window. Submitting the field does not call PAM,
lock, or unlock anything. Do not type your actual password into a visual preview.
Do not enter your real password in a preview. The account picture comes from
AccountsService or `~/.face`; a silhouette is used when neither is available.
Settings → Lock screen provides clock visibility, date visibility, time format,
wallpaper dimming and softness, inactivity delays, and a safe preview button. Your account picture is loaded from
AccountsService or `~/.face`, with a user silhouette as a fallback.
Escape clears the password. Caps Lock is shown beside authentication status.
Failed attempts show an error and delay retries. Authentication uses the system's
PAM policy; both password and account validation must succeed.
The locker reads Ferese's wallpaper, theme colors, typography, and shell radius
at startup. The wallpaper is softened once and shared across displays. Set softness
to zero to keep it sharp. Escape clears the password. Caps Lock is shown above authentication
status. Password verification runs on a worker, and repeated attempts are held
for two seconds after failure, in addition to delays enforced by PAM.
## Automatic locking
## Installation and locking
Install `swayidle`, then add this to `~/.config/ferese/config.kdl` to lock after
five minutes and before system sleep:
The normal `scripts/install.sh` includes the native binary and installs
`/etc/pam.d/ferese-lock` if absent. Existing administrator policies are preserved.
Fedora/Arch installations use `system-auth`; Debian-family installations use
`common-auth` and `common-account`; the fallback uses the system `login` stack.
There is no setuid binary, password command argument, or shadow-file reader.
Both authentication and account validation must succeed. Expired credentials
must be updated outside the locker. Interactive multi-factor enrollment and
password changes are not supported by this initial password UI.
```sh
ferese-lock # returns after confirmation, UI stays running
ferese-lock --foreground # stays attached until unlock
```kdl
autostart {
command "swayidle" "-w" "timeout" "300" "ferese-lock" "before-sleep" "ferese-lock" "lock" "ferese-lock"
}
```
The `swayidle -w` example in [Configuration](configuration.md#login-items-and-locking)
can invoke the default command. Auto-locking is opt-in. This change does not
install or start an idle daemon, replace your login manager, or change the Control Center's
suspend action. Test real password authentication inside a nested Ferese session
before enabling this on your desktop.
## Security and recovery
Never interpret the preview as proof of a locked desktop. A missing PAM policy
or unsupported compositor produces an error before a lock request. A failure
or authentication-worker panic never authorizes unlock. A crash after the lock
request may leave the session permanently locked; end that session from another
TTY if necessary. Do not restart the compositor to test crash behavior on a live
session with unsaved work.
The pinned libcosmic backend emits `Locked` twice: once when requesting a lock
and once on the actual protocol callback. The startup handshake waits for the
second notification. Re-audit this behavior whenever upgrading libcosmic. The
parent times out with an error after 15 seconds and leaves the UI running; it
never reports success merely because the window appeared.
Validation references: [session-lock protocol](https://wayland.app/protocols/ext-session-lock-v1),
[PAM authentication](https://man7.org/linux/man-pages/man3/pam_authenticate.3.html),
and [PAM account checks](https://man7.org/linux/man-pages/man3/pam_acct_mgmt.3.html).
Test password unlocking before enabling automatic locking. The installer provides
`/etc/pam.d/ferese-lock` and preserves an existing administrator policy.
## Inactivity and display sleep
A locked display dims after 30 seconds without input and sleeps after 120 seconds.
Dimming fades over half a second. Any keyboard, pointer or touch activity restores
brightness and wakes the display while keeping the session locked. The compositor
owns this policy, so it remains effective if the locker crashes. Sleep starts only
after the compositor has confirmed that every display is protected.
Settings → Lock screen adjusts both delays. Zero disables that action:
Locked displays dim after 30 seconds without input and sleep after 120 seconds.
Keyboard, pointer, or touch activity wakes them without unlocking. Set either
delay to zero to disable it, in Settings or KDL:
```kdl
lock-screen {
@@ -82,28 +46,19 @@ lock-screen {
}
```
These delays are independent of automatic locking. Existing idle inhibitors from
apps cannot keep an already locked display awake. In a direct session, display
sleep uses DRM DPMS without removing outputs or rearranging workspaces; nested
previews show black instead of powering off the host display. This puts displays
to sleep, not the computer. Suspend remains a separate system power action.
These delays start after locking, independently of the automatic-lock timer.
Display sleep does not suspend the computer. A nested preview shows black
instead of powering off the host display.
## Practical swaylock comparison
## Recovery and limits
| Behavior | Ferese Lock |
| --- | --- |
| Secure Wayland locking and protection after a locker crash | Session-lock protocol, compositor-owned fallback |
| All displays, including hotplug | Separate lock surface for each display |
| Readiness before an idle manager permits suspend | Default command waits for compositor confirmation |
| Password verification | PAM authentication and account checks on a worker |
| Password clearing and Caps Lock feedback | Escape clears; Caps Lock indicator |
| Authentication failure feedback and retry delay | Error message and bounded retry delay |
| Wallpaper, font and color customization | Shared Ferese theme and Settings |
| Inactivity and display power | Compositor dimming and DPMS; input restores brightness |
The lock covers all displays, including newly connected ones. If the lock UI
crashes, the compositor keeps the session protected. Recovery may require ending
that session from another TTY; see [Recovery](installation.md#logout-and-recovery).
[Swaylock](https://github.com/swaywm/swaylock/blob/master/swaylock.1.scd) provides
additional appearance flags, layout indicators and readiness-FD CLI options.
Ferese uses its shared configuration and existing startup acknowledgement instead.
Like the [swayidle example](https://github.com/swaywm/swayidle/blob/master/swayidle.1.scd),
locking and display sleep are separate from system suspend. Password changes and
interactive multi-factor prompts remain outside this password-only interface.
The password interface does not support password changes or interactive
multi-factor prompts. Update expired credentials outside the locker.
Developers should validate locking and readiness against
[ext-session-lock-v1](https://wayland.app/protocols/ext-session-lock-v1).
See [Development](development.md) for tests.
+67
View File
@@ -0,0 +1,67 @@
# Native-client soak testing
The soak runner repeatedly creates and destroys native Wayland clients while
exercising focus, resize, column width, fullscreen, floating, and workspace
paths. Every tenth iteration also terminates a client abruptly. If a local
malformed-client probe is available, it opens three isolated connections that
send malformed Wayland messages. The runner requires
Ferese to disconnect each malformed client and then checks that the compositor
and authenticated IPC socket remain responsive. It also records compositor RSS
growth.
Build `ferese` and `feresectl` and start a disposable
[nested session](installation.md#preview-and-logs). Select its Wayland socket and
compositor PID explicitly, then run:
```sh
WAYLAND_DISPLAY=wayland-N FERESE_PID=PID ./scripts/soak-native.sh
```
Replace `wayland-N` and `PID` with the preview's values. Do not use the host socket
or rely on automatic PID selection when multiple sessions are running.
The default duration is 24 hours. A shorter validation run is:
```bash
WAYLAND_DISPLAY=wayland-N \
FERESE_PID=PID \
FERESE_SOAK_SECONDS=300 \
./scripts/soak-native.sh
```
Development probes are local-only, not distributed in the Cargo workspace.
Supply an existing executable with `FERESE_MALFORMED_CLIENT` to enable protocol
checks. An explicitly configured missing probe is an error; without a probe,
the runner reports that protocol checks are skipped.
The local probe can also be run independently against a disposable Ferese session:
```bash
WAYLAND_DISPLAY=wayland-1 /path/to/ferese-malformed-client
target/debug/feresectl get-outputs
```
Do not point the malformed-client probe at the host compositor socket.
Environment controls:
- `FERESE_SOAK_SECONDS`: run duration, default `86400`;
- `FERESE_SOAK_DELAY`: delay between operations, default `0.15` seconds;
- `FERESE_SOAK_CLIENT`: native client executable, default `foot`;
- `FERESECTL`: path to `feresectl`, default `target/debug/feresectl`;
- `FERESE_MALFORMED_CLIENT`: path to the malformed protocol probe, default
`target/debug/ferese-malformed-client`;
- `FERESE_PID`: compositor PID; automatic lookup is only a fallback;
- `FERESE_SOAK_LOG`: result log, default `/tmp/ferese-soak-<pid>.log`.
- `FERESE_SOAK_MAX_RSS_GROWTH_KIB`: maximum RSS growth, default `131072`
(128 MiB);
- `FERESE_SOAK_MAX_FD_GROWTH`: maximum open-file-descriptor growth, default
`32`;
- `FERESE_SOAK_MAX_THREAD_GROWTH`: maximum thread growth, default `8`.
Run the same workload once with the nested backend and once from a direct DRM
session. Samples are taken after spawned clients are reaped. A passing run
requires the compositor to remain alive, IPC queries to succeed, and RSS, file
descriptor, and thread growth to stay within the declared budgets. Tighten the
defaults for release hardware when its normal cache behavior is known. Preserve
the soak log and the `ferese::render` summaries with the release artifacts.
+36
View File
@@ -0,0 +1,36 @@
# Nested resize checks
Build and launch a preview with protocol and render logging:
```sh
cargo build --release --locked -p ferese -p ferese-shell
WAYLAND_DEBUG=1 FERESE_TRACE_PERFORMANCE=1 \
target/release/ferese --backend nested --grant-effects --grant-shell-control -- \
target/release/ferese-shell 2>/tmp/ferese-resize.log
```
Use a wallpaper, drag each edge of the host window, and repeatedly maximize and
restore it. Check that the wallpaper and bar follow without long stalls, including
at fractional scaling.
## Compare renderers
Repeat with `ICED_BACKEND=wgpu` and `ICED_BACKEND=tiny-skia`. The shell enables
wgpu; software fallback can be slower when resampling wallpaper.
Trace the whole sequence: host resize, layer configure, client acknowledgement,
new buffer commit, then compositor rendering. A quick acknowledgement does not
mean the client has finished drawing. `FERESE_TRACE_PERFORMANCE` measures compositor
work, not shell rasterization.
## Historical check
A KDE Wayland check at 175% scale used debug binaries and protocol tracing.
Median configure acknowledgement was 1.35 ms with tiny-skia and 2.27 ms with
wgpu. Wallpaper buffer intervals were 5919 ms with tiny-skia (only two buffers)
and 9.85 ms median with wgpu. Capture durations differed, so these are diagnostic
observations rather than throughput benchmarks. GPU intervals also had stalls
up to about 200 ms.
Repeat on the current build. A nested result does not establish direct-session
resize or frame-pacing behavior.
+28 -38
View File
@@ -1,37 +1,24 @@
# Notifications
Ferese Shell provides themed notification cards and an in-session history panel.
Cards appear beneath the bar on the focused output and follow the selected theme.
Click the notification icon in the bar to open history, toggle Do Not Disturb, or
clear notifications. Escape, clicking outside, or the close button closes the history panel.
The center uses the same anchored popup and animation as the other bar menus.
Both the center and popup cards follow the shell material: translucent mode uses
the compositor’s blur and tint, while solid mode stays opaque.
## Send a notification
Notifications use the standard `org.freedesktop.Notifications` D-Bus interface.
Existing apps and `notify-send` work without Ferese-specific commands:
```sh
notify-send --app-name=Ferese --icon=dialog-information "Hello" "Your desktop notification"
```
Ferese shows compact notification popups and a history panel opened from the bar.
Their colors, font, corners, and background follow the shell theme.
## Popups and history
Up to three popup cards are visible at once. History keeps the most recent 100
notifications in memory and clears when the shell exits. Hovering a popup pauses
its timer. Critical notifications bypass Do Not Disturb and do not expire unless
the sender supplies a timeout. App action buttons emit the standard action signal;
a replacement updates the same notification ID. Transient messages are removed
from history when they close.
Up to three popups are visible at once. Hovering pauses a popup's timer.
History keeps the latest 100 notifications and clears when the shell exits.
App action buttons work when provided by the sender. Transient messages leave
history when closed.
## Settings and Do Not Disturb
Critical notifications bypass Do Not Disturb and remain visible unless the app
provides a timeout. Other apps can also request a timeout or no timeout.
Settings → Notifications controls popup visibility, the initial Do Not Disturb
state, and the default timeout. The bar's Do Not Disturb toggle is temporary;
changing the Settings option saves the preference for future sessions. Apps can
request their own timeout, including no timeout.
## Settings
**Settings → Notifications** controls popup visibility, the default timeout, and
the Do Not Disturb preference. The bar toggle changes Do Not Disturb for the
current session; Settings saves it for future sessions. Hidden popups still enter
history.
```kdl
notifications {
@@ -41,17 +28,20 @@ notifications {
}
```
The timeout must be between 1000 and 30000 milliseconds. Hiding popups still
records notifications in history. Appearance follows the Ferese theme.
The default timeout accepts 1000–30000 milliseconds.
## Session integration
## Send a test notification
Only one notification server can own the D-Bus interface. The Ferese session
launcher stops the SwayNotificationCenter user service before starting its shell.
For a manually started shell, stop that service with `systemctl --user stop
swaync.service`. If another server already owns the interface, Ferese leaves it
running and logs that native notifications are unavailable.
```sh
notify-send --app-name=Ferese --icon=dialog-information "Hello" "Your desktop notification"
```
This implementation supports plain text, app icons, urgency, transient/resident
hints, actions, replacements, and close signals. It does not advertise rich body
markup, embedded image data, notification sounds, or history persistence.
## Troubleshooting
Only one notification server can run on the session bus. The session launcher
stops `swaync.service` before starting the shell. When launching the shell manually,
stop any existing notification server first. If another server owns the interface,
Ferese logs that native notifications are unavailable.
Ferese supports plain text, app icons, actions, replacement, and urgency. Rich
markup, embedded images, sounds, and saved notification history are not supported.
+39 -206
View File
@@ -1,217 +1,50 @@
# Release performance baseline
# Performance measurements
Measured 2026-09-27 at commit `08a5866`, using
`cargo build --release --locked -p ferese -p ferese-shell -p feresectl`.
Use release builds and a fixed workload when comparing performance. Record the
commit, output size, scale, refresh rate, graphics driver, and whether the session
is nested or direct. Keep the nested preview visible: host occlusion changes
rendering behavior.
Two disposable nested sessions used separate runtime/config directories, default
configuration, the bundled wallpaper, and the release shell. Performance logging
was enabled with `FERESE_TRACE_PERFORMANCE=1`. The nested output reported
1422 × 1696 physical pixels, 2× scale and 60 Hz. The parent DRM output was a
2880 × 1800 eDP panel at 120 Hz and 2× scale.
## Collect a sample
## Measurements
```sh
cargo build --release --locked -p ferese -p ferese-shell -p feresectl
FERESE_TRACE_PERFORMANCE=1 target/release/ferese --backend nested \
--grant-effects --grant-shell-control -- target/release/ferese-shell
```
CPU is process CPU time from `/proc/PID/stat` divided by elapsed monotonic time,
expressed as a percentage of one core. First-run samples lasted 10 seconds;
second-run samples lasted 5 seconds. These are short observational samples,
not statistically established improvements or before/after comparisons.
Compare idle, several windows, overview, Settings navigation, translucent
popovers, and screen capture separately. Allow startup to settle and repeat each
sample. Record process CPU, child-process CPU, RSS, render attempts, and damaged
frames. Keep build work and unrelated activity outside the sampling interval.
| Scenario | Compositor CPU, run 1 / run 2 | Shell CPU, run 1 / run 2 |
CPU percentages below use one core as 100%. Short samples are observations,
not guarantees or hardware-session benchmarks.
## Historical results
On 2026-09-27 at `08a5866`, two nested runs used a 1422 × 1696 output at 2× scale,
reporting 60 Hz on a 120 Hz host. Samples lasted 5–10 seconds.
| Scenario | Compositor CPU, two runs | Shell CPU, two runs |
| --- | --- | --- |
| Idle shell, no application window | 5.3% / 5.8% | 1.5% / 1.6% |
| Idle, no application window | 5.3% / 5.8% | 1.5% / 1.6% |
| Overview with one terminal | 18.6% / 16.2% | 1.3% / 1.4% |
| After window transitions, terminal still open | 6.6% / 6.4% | 1.5% / 1.4% |
| After transitions | 6.6% / 6.4% | 1.5% / 1.4% |
Compositor RSS was approximately 81–82 MiB initially and 91–92 MiB after the
short exercise. This does not establish a leak or long-term memory stability.
Main-thread voluntary context switches were approximately 162–165/s at idle
and 498–505/s in overview; these are scheduling proxies, not hardware wakeups.
Compositor RSS increased from about 81–82 MiB to 91–92 MiB during the exercise.
These samples do not establish a leak or long-term stability. A follow-up had
lower rendering rates, so the original observations did not prove duplicate scheduling.
A five-second idle log interval contained 60 damaged frames and 531 no-damage
attempts, approximately 118 attempts/s. A later overview interval contained
591 damaged frames in 5001 ms. Nested frame scheduling and overview damage
therefore deserve investigation first. The parent runs at 120 Hz, so the
mismatch with the nested output's reported 60 Hz must be understood before
attributing these counts to duplicate scheduling.
A 20-second status-worker comparison removed five commands per poll (15 to 10).
Total measured CPU time including child processes fell from 2.41 to 1.90 seconds.
The timing wrapper was included, and this was one comparison.
## Behavioral exercise
For push status delivery (`4bf0757` to `b0583eb`), a synthetic transport test
measured median delivery delay of 129.398 ms before and 0.130 ms after. It excluded
queries, UI dispatch, and painting. Visible shell samples suggested lower typical
CPU, but an after-run spike raised the mean; they did not establish a robust
average CPU saving.
Both sessions exited with status 0. The probes created a terminal, entered and
left overview, and toggled maximize, fullscreen and floating twice each.
The second session exercised `theme.typography.font_family`, wallpaper fit/fill,
and replacement image paths through filesystem-driven reloads. Logs confirmed
accepted reloads and replacement wallpaper decoding. This verifies execution
and liveness, not pixel-perfect appearance or GPU allocation-failure recovery.
The first probe used an incorrect font key; only the second probe counts as
font reload coverage.
Raw local artifacts:
- `/tmp/ferese-profile-7cbrpopn/{results.json,session.log}`
- `/tmp/ferese-profile-h2ypepn6/{results.json,session.log}`
- Probe scripts: `/tmp/ferese-profile.py` and `/tmp/ferese-profile-reloads.py`
## Limits and next work
The installed DRM compositor is release `20260927-042025-168287`; its binary hash
differs from the tested release build. A read-only host sample measured 9.8%
compositor CPU and 1.7% shell CPU, but overlapped the nested workload and is not
an idle baseline or validation of the new DRM code. No session was replaced.
Only one monitor was connected, so multi-monitor behavior remains untested.
Next investigate nested refresh scheduling and repeated overview damage, using
these scenarios for before/after measurements. Preserve client frame callbacks,
animation pacing and idle responsiveness. Status command polling remains a
secondary target; blur invalidation needs a separate translucent-material
workload before ranking its cost. New-build DRM and multi-monitor validation
remain necessary before claiming hardware performance improvements.
## Follow-up investigation
A repeat baseline (`/tmp/ferese-profile-n8437l1i`) confirmed 60 Hz after startup
but ran near 60 attempts/s rather than 118. Compositor CPU was 3.0% idle, 9.2%
in overview and 4.6% after transitions. The earlier high-rate behavior is not
consistently reproduced, so it is not sufficient evidence of duplicate redraws.
The candidate refresh-period guard was discarded after testing.
The guard run (`/tmp/ferese-profile-llcrkvm5`) measured 4.0% idle, 8.6% overview,
and 3.6% after transitions. Render attempts fell slightly below the nominal
cadence. This did not establish a benefit, so no frame-pacing change was kept.
The next investigation is unconditional thumbnail damage in overview.
## Broader profiling (visible preview)
Artifacts: `/tmp/ferese-profile-all-0lhodit4/`; script:
`/tmp/ferese-profile-all.py`. This run used the unchanged release compositor,
release shell and release Settings. The user kept the preview visible after an
earlier hidden-window run proved that host occlusion invalidated render samples.
Six-second samples are exploratory, not statistically stable benchmarks.
The captured output was 2856 × 1696 pixels; the preview was resized during the
session, so these workloads must not be compared as if output size were fixed.
| Scenario | Compositor CPU | Shell CPU | Shell waited-child CPU | Settings CPU |
| --- | ---: | ---: | ---: | ---: |
| Idle shell | 4.33% | 1.33% | 7.50% | — |
| Three terminals | 6.67% | 1.50% | 11.33% | — |
| Overview, three terminals | 17.50% | 1.17% | 8.17% | — |
| Settings open, idle | 6.17% | 1.50% | 10.83% | 0.00% |
| After capture/window transitions | 8.50% | 1.33% | 11.00% | 2.50% |
Percentages use one CPU core as 100%. Child CPU is Linux's accumulated CPU time
for reaped children, separate from the shell process. This includes the timing
wrapper: a PATH-local `timeout` wrapper invokes GNU time and the real timeout
command. Read-only PipeWire queries use the host runtime socket; no status
control actions were sent. The native installed shell cross-check, without
instrumentation, measured 2.0% self CPU plus 8.8% waited-child CPU over ten
seconds. It is an older binary and a different scene, so this corroborates the
existence of subprocess overhead rather than providing an exact comparison.
The preview recorded 273 successful status command invocations across 21 polls:
13 per poll. The three login1 capability calls consumed 1.88 seconds total wall
time, about 90 ms/poll. Three nmcli queries consumed 1.35 seconds total; two wpctl
queries consumed 0.80 seconds. GNU time's hundredth-second granularity makes
per-command CPU totals unsuitable for precise attribution. Stable capability
queries, device metadata and frequently changing values currently share one
poll cycle. Separate refresh policies or service subscriptions deserve priority.
Settings mapped in 109 ms, measured from process launch until IPC reported it
focused (not first completed paint). Idle Settings RSS was 108068 KiB, with 38
threads and 65 descriptors. Zero measured CPU over six seconds means below this
sample's resolution, not a guarantee of zero work. Startup font enumeration was
not isolated separately. Settings interaction/search/page-switch profiling is
still outstanding.
Twenty `feresectl get-outputs` calls had median process-inclusive latency of
3.05 ms and maximum 3.95 ms. Five captures took 1.13 seconds total, including
client startup, transfer and PNG encoding. Capture was enabled only in the
isolated compositor with `FERESE_ENABLE_SCREENCOPY=1`. The first screenshot was
visually inspected and showed the shell and Settings rendered. It does not
measure GPU readback independently. Window maximize/fullscreen/layout toggles
completed, but there was no instrumented input-to-photon latency measurement.
The four-second visible overview perf recording (`cpu-clock:u`, 999 Hz) collected
471 samples with no lost samples. Shared-object attribution: 47.77% Ferese
executable (including statically linked dependencies), 30.36% Mesa Gallium,
14.23% libc, 2.97% Wayland client library. Approximately 96.4% of samples were in
the main compositor thread. Texture upload/memory-copy paths appear in the
sampled stacks. This is user-space CPU sampling, not GPU timing; short samples
and incomplete frame-pointer unwinding limit function-level attribution.
The earlier broad run (`/tmp/ferese-profile-all-boiq_r30`) had a 37-second render
reporting gap while hidden, no usable perf samples, and a disabled screencopy
protocol. Its render/overview and capture results are excluded. Its bare idle
sample was 1.0% CPU, but visibility differences preclude subtracting it from the
visible shell run.
Next priority: shell status subprocesses. Then investigate sustained overview
damage and texture uploads with controlled window visibility and dimensions.
Control-center interaction, widgets, translucent-material workloads, long-term
memory growth, new-build DRM and multi-monitor runs remain unprofiled. No source
changes or profiling-note commits were made during this profiling pass.
## Status-worker optimization comparison
Compared the production status module at `08a5866` with the new single-command
brightness reader and reusable native D-Bus connections. A standalone harness
started `Service::start(None)`, consumed updates every 250 ms, and ran for 20
seconds without a UI. Both variants used the same host runtime/services and
command timing wrapper. Artifacts: `/tmp/ferese-status-compare/`.
Both completed nine polls. Notifications were available in this comparison, so
the original worker launched 15 commands/poll and the new one launched 10:
135 versus 90 invocations. Four busctl launches and one brightnessctl launch per
poll were removed. When notifications are absent, the equivalent counts are
13 versus 8. Poll frequency, fresh capability queries and post-action generation
handling remain unchanged; no status-value caching was introduced.
GNU time measured total user+system CPU, including waited children: 2.41 seconds
before, 1.90 seconds after over 20.01 seconds each (about 21% lower in this single
comparison). Timing-wrapper overhead is included, so this is not a precise
uninstrumented CPU improvement estimate. Native D-Bus adds persistent sockets
and a runtime thread pool; UI samples observed roughly 26–28 shell threads and
48 descriptors versus roughly 18 threads and 42 descriptors previously.
One UI run showed a transient self-CPU spike; a subsequent perf recording
identified tiny-skia raster work rather than D-Bus queries among its hot paths.
The UI-free comparison avoids treating variable UI activity as polling cost.
Validation: workspace suite and explicit private-dbus-daemon test passed. The
private bus exercised live capability changes, yes/challenge/no/na handling,
notification-owner appearance/disappearance, error replies, missing methods,
and a reply delayed past the configured timeout. Brightness parser tests cover
rounding, zero brightness, invalid classes and malformed/nonfinite values.
### Push status delivery measurement (b0583eb)
Compared release binaries for 4bf0757 and b0583eb with the same nested compositor,
empty isolated config and a visible preview. Binary SHA-256 hashes differ and
were checked after forcing a shell rebuild to avoid shared-target artifact reuse.
No status-command timing wrappers were active. Discarded an initial baseline
that overlapped compilation. Retained after/before/after runs, each with three
10-second samples following a three-second startup settle. Artifacts and harness:
`/tmp/ferese-status-push-measure/` (`summary.json`, `ui.py`, `latency.rs`).
Shell CPU percentages of one core:
- Before: 1.5, 1.4, 1.6 (median 1.5).
- After run 1: 0.8, 1.1, 1.1.
- After run 2: 1.0, 1.1, 7.5 (combined after median 1.1).
The 7.5% sample coincided with a large damaged-pixel burst (44.3 million in a
five-second interval), higher RSS and two additional descriptors. Its cause was
not isolated; it is retained, not silently excluded. Combined after mean is
2.1%, versus 1.5% before, so these short runs do not establish a robust average
CPU saving. Typical steady-state samples suggest a 0.4 percentage-point reduction.
Status child CPU remained roughly 6–8% of one core, dominating shell self CPU.
Steady-state damaged frames were about 12/s before versus 6.6–7.6/s in the first
after run. The repeat had similar quiet intervals plus bursts at 15.6 and 22.7/s.
Total render attempts stayed around 60/s, confirming active rather than hidden
preview sampling. No claim is made about DRM-session CPU or total idle wakeups.
An isolated transport harness used the old bounded mpsc + 250ms polling path and
an extracted copy of the new Updates::stream implementation, with 40 synthetic
updates per path at 317ms intervals. Worker-to-consumer delay:
- Before: median 129.398ms, p95 239.957ms, maximum 245.881ms.
- After: median 0.130ms, p95 0.330ms, maximum 0.425ms.
This measures delivery only, excluding status queries, UI dispatch and painting.
The preview processes exited and the temporary source worktree was removed.
These results predate later changes. Run fresh comparisons for current claims,
and validate direct rendering and multiple monitors separately.
+45 -159
View File
@@ -1,174 +1,60 @@
# Desktop portals
Applications call the standard `org.freedesktop.portal.Desktop` frontend. Ferese's
native backend implements desktop integration; GTK remains the fallback for
standard file, print and application dialogs. Install both backends.
Desktop portals let applications request screen sharing, screenshots, wallpaper
changes, and other desktop services. Ferese provides themed consent dialogs;
GTK supplies file, print, and other standard dialogs.
## Native services
Install `xdg-desktop-portal`, `xdg-desktop-portal-gtk`, and Ferese's backend through
[Installation](installation.md). The session launcher starts the services with
the correct display environment.
- **Settings:** Ferese's color scheme, accent color, contrast preference and
reduced-motion setting. Changes are signalled after valid KDL appearance
changes; malformed saves leave the last valid preferences intact.
- **Screenshot:** screen, selected-area, selected-window and active-window PNG
captures, plus a screen color picker. Window captures render the selected
toplevel and its subsurfaces in isolation, preserving transparency; occluding
windows, separate popups, shell layers and compositor borders are excluded.
The native window picker lists window titles and application IDs. Every capture
asks for consent. Interactive requests offer area selection; area and color
selection use `slurp`. Captures use Ferese's bounded screenshot IPC.
The helper must disappear from the compositor before capture starts.
- **Wallpaper:** a preview and confirmation, then an atomic configuration update.
Desktop and lock-screen wallpaper can be set independently, or together.
Ferese copies accepted local images into its own private data directory so
temporary portal files can disappear safely. Image bytes and decoded dimensions
are bounded. `theme.background.lock-path` overrides the lock-screen image;
without it the lock screen follows `theme.background.path`.
- **Background:** native open-window/app state, change signals and per-instance
background consent. Legacy autostart requests create managed XDG desktop entries;
current frontends manage those files themselves. Direct sessions launch standard
XDG autostart entries after importing the display environment. User overrides,
Hidden, OnlyShowIn, NotShowIn and TryExec are respected; previews skip autostart.
- **Lockdown:** standard printing, save-to-disk, application-handler, location,
camera, microphone and sound-output restrictions. All default to unrestricted.
Configure boolean `disable-*` fields under `portals { lockdown { ... } }`;
changes are signalled to the frontend. Invalid policy types retain the current
policy. Only the portal frontend may write backend policy properties.
- **USB:** native confirmation showing device vendor/model and requested read-only
or read/write access. The portal frontend handles device enumeration and opening.
- **GlobalShortcuts:** session-scoped keyboard shortcuts with native trigger
selection, conflict checking, activation/release signals and reconfiguration.
Applications cannot replace Ferese bindings or reserved escape/VT shortcuts.
Closing a session or losing its compositor connection releases its shortcuts;
shortcuts do not activate on the lock screen. Saved choices are offered for
future sessions, with explicit approval before registering them.
- **Inhibit:** connection-scoped logout, suspend and idle inhibitors, backed by
logind descriptors and compositor idle inhibition. Native session monitors
report lock state and Running/QueryEnd/Ending transitions. Ending queries wait
for acknowledgements for up to one second. Power confirmations list blockers
and recheck them before acting; authentication failure cancels the query.
UserSwitch is recorded, but Ferese currently has no user-switch operation.
- **InputCapture:** approved keyboard and pointer capture over a private EIS
connection, with standard screen zones and pointer barriers on outside display
edges. Capture begins at a configured barrier; releasing it restores local
input. Ctrl+Alt+Escape returns control immediately. Locking, topology/keymap
changes, receiver disconnects and stalled consumers stop capture. Permission
is requested for every session; touch capture and remembered grants are not
offered. This is an input receiver, not remote input injection.
- **ScreenCast:** monitor and isolated window sharing with a native picker, PipeWire streams and explicit opt-in
saved permissions. The frontend stores and revokes saved choices. Invalid
restore data or changed display identity requires fresh consent.
See [screen sharing](screen-sharing.md) for lifetime and recording details.
## Supported services
Native request dialogs use Ferese's theme and are floating windows. Screenshot,
PickColor, Wallpaper, USB, InputCapture and ScreenCast consent dialogs use Wayland parent identifiers to establish
a transient relationship with the requesting window.
Cancelling a request or losing the portal frontend terminates its pending helper.
The packaged `ferese-portals.conf` selects these interfaces explicitly. Existing
user overrides in `~/.config/xdg-desktop-portal/` may take precedence. Install the
new compositor, backend and preference files together; screenshot synchronization
requires the updated compositor IPC.
## GNOME compatibility audit
The audit compares the [GNOME backend's registered interfaces](https://github.com/GNOME/xdg-desktop-portal-gnome/blob/main/data/meson.build)
with the [standard backend D-Bus contracts](https://flatpak.github.io/xdg-desktop-portal/docs/impl-dbus-interfaces.html).
Matching D-Bus signatures does not establish complete feature parity.
| Interface | Ferese integration |
| Service | Behavior |
| --- | --- |
| Settings | Native, including appearance change signals |
| Screenshot | Native screen/area/window/active-window capture and PickColor |
| Wallpaper | Native desktop, lock-screen and combined targets |
| ScreenCast | Native v4 monitors and isolated windows, logical geometry, live window resizing and opt-in saved display permissions |
| Access, Account, AppChooser, DynamicLauncher, FileChooser, Notification, Print | Delegated to GTK |
| Background | Native application state, per-instance consent and XDG autostart |
| Usb | Native consent; standard frontend handles enumeration and device descriptors |
| GlobalShortcuts | Native sessions, configurable keyboard triggers, conflict checks and activation/release signals |
| RemoteDesktop | Deferred: authorized input injection and EIS transport |
| Clipboard | Deferred with RemoteDesktop |
| InputCapture | Native v2 keyboard/pointer consent, zones, outside-edge barriers and private EIS receiver transport |
| Lockdown | Native seven-property policy provider with persisted configuration and change signals |
| Inhibit | Native inhibitors and session monitors; UserSwitch recorded until a switch operation exists |
| Settings | Color scheme, accent, contrast, and reduced-motion preferences |
| Screenshot | Screen, area, window, active-window capture, and color picking |
| Wallpaper | Preview and approval for desktop, lock screen, or both |
| ScreenCast | Display and window sharing through PipeWire |
| Background | Permission to run in the background and startup integration |
| USB | Device information and approval for requested access |
| GlobalShortcuts | App shortcut selection, conflict checks, and activation |
| Inhibit | App requests to delay logout, suspend, or idle actions |
| InputCapture | Approved keyboard/pointer capture for input-sharing applications |
| Lockdown | Administrator restrictions on printing, saving, handlers, location, camera, microphone, and sound |
| FileChooser, Print, Access, Account, AppChooser, DynamicLauncher, Notification | GTK backend |
The frontend supplies additional APIs such as OpenURI, network monitoring,
document export and permission storage. Adding a similarly named backend service
is neither required nor sufficient for those APIs.
RemoteDesktop and its session Clipboard integration are not implemented.
This does not affect ordinary application clipboard use.
Window streams render only the selected toplevel and its subsurfaces, including
on an inactive workspace. Overlapping apps, shell layers, separate popup windows
and compositor decorations are excluded. The private capture protocol is available
only to the native portal executable and checks session opt-in and locking.
Window resizing renegotiates PipeWire video dimensions. Closing the selected
window ends sharing; its lifetime ID cannot select a replacement app. Window
selections require fresh consent and are not saved as display permissions.
RemoteDesktop and its Clipboard integration remain deferred.
## Consent and capture
## Validation
Screenshot and screen-sharing requests ask for approval. Screen sharing can
remember display permissions when the application requests it and the user opts
in. Window sharing always asks again. See [Screen sharing](screen-sharing.md).
Window captures contain the selected app and its subsurfaces; separate popups,
other apps, shell layers, and compositor decorations are excluded.
Build the backend and run unit tests, then check its contracts on a private bus:
InputCapture starts at an approved display-edge barrier. **Ctrl+Alt+Escape**
returns local control. Locking, disconnecting the receiver, or changing displays
or the keyboard layout stops capture. Touch and remembered InputCapture grants
are not supported. InputCapture forwards local input; it does not inject remote input.
```sh
cargo build --locked -p xdg-desktop-portal-ferese
cargo test --locked -p xdg-desktop-portal-ferese
python3 scripts/tests/test_portal_contracts.py
```
Wallpaper approval copies the image into Ferese's data directory and updates the
config. Set `theme.background.lock-path` for a separate lock-screen image; otherwise
it follows the desktop wallpaper.
With a Wayland host and a built compositor, check shortcut connection cleanup
and configuration conflicts in an isolated nested compositor:
Global shortcuts cannot replace Ferese bindings or reserved escape/console keys.
They stop when the app session closes and never activate on the lock screen.
Power confirmations show app inhibitors before proceeding. Ferese has no
user-switch operation.
```sh
FERESE_TEST_SHORTCUTS=1 python3 scripts/tests/test_shortcuts_isolated.py
```
## Portal selection
Check window capture isolation, transparency and orientation with overlapping
clients in a temporary nested compositor:
The installer writes `/usr/share/xdg-desktop-portal/ferese-portals.conf`.
Overrides in `~/.config/xdg-desktop-portal/` may take precedence. If a request opens
the wrong dialog or no source appears, check those overrides and confirm that
`XDG_CURRENT_DESKTOP` includes `Ferese`.
```sh
FERESE_TEST_WINDOW_CAPTURE=1 python3 scripts/tests/test_window_capture_isolated.py
```
Capture a managed window directly from the CLI with `feresectl screenshot-window
<window-id> > capture.png`; `feresectl get-windows` lists IDs.
The contract test compares introspection against the installed official backend
XML, verifies appearance changes and invalid-save retention, and checks that
sensitive methods reject callers outside the portal frontend. It does not open
consent dialogs or capture the host desktop. Interactive consent, fractional-scale
capture, polkit and hotplug still require a live session test.
Check native inhibitor lifetimes, acknowledgement generations and session-ending
transitions against a mock logind in an isolated nested compositor:
```sh
FERESE_TEST_INHIBIT=1 python3 scripts/tests/test_inhibit_isolated.py
```
The shell now requires version 4 of Ferese's shell protocol; rebuild the shell
and compositor together when upgrading.
Check restored sharing, mode downgrade, stale-data fallback, output-generation
validation and frontend-disconnect cleanup with synthetic monitor identities on
an isolated bus and nested compositor:
```sh
FERESE_TEST_RESTORE=1 python3 scripts/tests/test_restore_isolated.py
```
Check live window pixels, overlap isolation, inactive workspaces, resizing and
closure using a disposable nested compositor and a real PipeWire consumer:
```sh
FERESE_TEST_WINDOW_STREAM=1 python3 scripts/tests/test_window_stream_isolated.py
```
Check InputCapture state and its transport against an installed libei receiver:
```sh
cargo test --locked -p ferese input_capture::tests
cargo test --locked -p xdg-desktop-portal-ferese eis::tests -- --ignored
```
The receiver test uses a private socket pair and synthetic input; it never
captures the host keyboard or changes host focus. Real pointer-barrier crossings
and interactive consent still need a live session check.
For capture issues, see [screen-sharing troubleshooting](screen-sharing.md#troubleshooting).
For backend contracts and integration checks, see [Development](development.md#portal-checks).
+50 -120
View File
@@ -1,142 +1,73 @@
# Screen sharing and recording
Ferese includes a ScreenCast portal backend, `xdg-desktop-portal-ferese`.
Applications use the standard desktop portal to request a display or window, then receive
video through PipeWire. A recorder or conferencing application handles encoding,
saving files, or sending video; those tasks do not run inside the compositor.
Share a monitor or window from a Wayland-compatible recording or conferencing app,
or save a video using the top bar. [Installation](installation.md) includes the
portal and PipeWire dependencies.
## Record from the top bar
1. Click the screen-recording icon in the top bar to open the display picker.
2. Choose a monitor in the native picker and click **Share**.
3. The icon changes to a stop square and shows elapsed time. Click it to stop.
1. Click the recording icon.
2. Select a monitor and click **Share**.
3. Click the stop square to finish.
The recorder finishes the video before sending a notification with its saved
path. Videos are saved as VP8 WebM files under
`Videos/Ferese` (using your configured XDG Videos folder). Audio is off.
The icon uses the same theme accent, hover effect, and corner radius as the
other bar controls.
Encoding runs in a separate native `ferese-record` process using GStreamer.
The shell remains responsive while a file is recorded or finalized. If saving
fails, an error appears in the notification center; an unfinished `.webm.part` file
may remain at the path shown in the error.
Videos are saved as VP8 WebM files in your XDG Videos folder under `Ferese`.
A notification shows the saved path. Audio and region recording are not supported
by the built-in recorder; other recording apps can handle audio separately.
If recording fails, a notification reports the error and any unfinished
`.webm.part` file.
## Start sharing
Use a Wayland screen-sharing or recording source in your application. Ferese
opens a native source picker. Depending on what the application requests, choose
from displays, windows, or both and press **Share**. Nothing is selected by default.
Applications that request multiple sources can select up to eight. Display and
window choices are grouped separately. Windows show their title and application.
Window streams capture only the selected app's toplevel and subsurfaces, even
when covered or on another workspace. Separate popups and compositor decorations
are excluded. Resizing a shared window updates the video size without ending the
session. Closing that window ends sharing.
The picker uses your Ferese theme colors, font, and shell corner radius. Its
background uses the same compositor material as popovers and authentication
prompts: opaque in solid mode, or blurred and translucent using the shared
**Appearance → Shell opacity** and blur settings. Text and icons remain opaque.
Select a Wayland screen-sharing source in your application. In Ferese's picker,
choose a display or window and click **Share**. Nothing is selected initially.
Apps requesting multiple sources can select up to eight.
Other applications keep a **screen sharing** window with a **Stop sharing** button.
Ferese’s built-in recorder uses the top-bar stop control instead, leaving your
windows accessible after display selection. The command-line recorder keeps the
sharing window when it is not launched by the shell. Press **Stop sharing** or
close the sharing window to revoke an ordinary application’s session. Closing the requesting application,
locking Ferese, disconnecting the selected display, or changing a monitor's capture
size also ends sharing. By default, each request asks for consent. If an application requests saved
permissions, the picker offers an **Allow without asking** switch, off by default.
Permissions can last while the application runs or until revoked, as requested
by the app. The standard portal frontend owns permission storage and revocation.
A changed or missing display, different app, or changed cursor options asks again.
Displays without a reliable EDID identity, including nested previews, always need
fresh consent. Window selections also require fresh consent and cannot enable saved
display permissions. Stopping a stream ends that stream; it does not erase a previously
saved permission.
Everything visible on a shared display, including notifications, is included.
A shared display includes everything visible on it, including notifications.
A shared window includes only that app's content and subsurfaces, even when
covered or on another workspace. Separate popups and compositor decorations are
excluded. Resizing a shared window updates its video size; closing it ends sharing.
## Installation
The picker follows your shell colors, font, corners, opacity, and blur.
The [standard installer](installation.md) builds and installs the backend, its
D-Bus activation entry, and a Ferese-specific portal preference file. Install
`pipewire`, a PipeWire session manager such as `wireplumber`,
`xdg-desktop-portal`, and `xdg-desktop-portal-gtk` using your distribution's package
manager. The GTK backend supplies standard dialogs such as file selection. Ferese also
provides native Settings, Screenshot and Wallpaper services; see
[desktop portals](portals.md).
The built-in recorder also needs GStreamer’s PipeWire, base, and good plugins
(`pipewiresrc`, `videoconvert`, `vp8enc`, and `webmmux`). The installation guide
includes these packages.
## Stop sharing
Log into a new Ferese session after installation. The session launcher imports
its Wayland display into the D-Bus activation environment. Nested previews leave
the host activation environment alone.
Click **Stop sharing** in the sharing window, or close that window. The built-in
recorder uses the top-bar stop button instead. Sharing also ends when the app
closes or Ferese locks. Disconnecting a shared monitor or changing its capture
size ends its stream; start sharing again afterward.
Existing custom window rules can float the picker and sharing indicator:
## Remember permissions
```kdl
window-rule app-id="dev.ferese.ScreenShare" floating=#true
```
When an app requests saved display permissions, the picker offers **Allow without
asking**, off by default. Approval lasts for the duration requested by the app.
The portal manages stored permissions and revocation.
The packaged example configuration already includes this rule. Existing user
configuration is preserved during installation.
A different app, changed display, or changed cursor option needs approval again.
Nested previews and displays without a reliable identity always ask. Window
permissions are not saved. Stopping a stream does not erase a saved permission.
## Current capabilities
## Requirements and limits
- Monitor and isolated window capture, one PipeWire stream per source, up to 30 fps.
- Window streams keep working across workspace switches and renegotiate on resize.
- Hidden or embedded pointer, as requested by the application.
- Logical stream position and size, including fractional output scales.
- Wayland parent-window relationships for the consent picker.
- Bounded frame storage and reusable shared-memory buffers.
- Cancellation during selection, an explicit stop control, and process cleanup.
- ScreenCast backend version 4 with explicit opt-in saved display permissions.
Install PipeWire, WirePlumber, `xdg-desktop-portal`, and `xdg-desktop-portal-gtk`.
The built-in recorder also needs GStreamer's PipeWire, base, and good plugins;
see the [package lists](installation.md#requirements).
Region selection and audio capture in Ferese’s recorder are not implemented yet.
The top-bar recorder currently requests monitors. Other recording applications can handle audio separately.
Video currently uses CPU copies through the compositor's shared-memory screencopy path, not DMA-BUF zero-copy. High-resolution
or multiple-display recording can therefore use significant CPU and memory
bandwidth. A paused stream checks capture availability once per second so locking
also ends sessions without an active consumer.
The installer supplies Ferese's portal backend and selects it for screen sharing.
Log into a new Ferese session after installation.
## Development checks
Build the backend after installing the native dependencies in the
[installation guide](installation.md#requirements):
```sh
cargo build --release --locked -p xdg-desktop-portal-ferese
cargo test --locked -p xdg-desktop-portal-ferese
# Exercise real video encoding and playback (requires the plugins above):
cargo test --locked -p xdg-desktop-portal-ferese --bin ferese-record encoder_finishes_a_real_webm -- --ignored
```
The opt-in integration test opens a temporary nested compositor and uses a
private D-Bus session. It requires built Ferese and locker binaries, Python
GI/GStreamer with `pipewiresrc`, `dbus-daemon`, `xdg-desktop-portal`, and `bwrap`.
It checks capture delivery and timestamps, cancellation, frontend loss, and lock
revocation:
```sh
FERESE_TEST_PORTAL=1 \
FERESE_TEST_PORTAL_BINARY=target/release/xdg-desktop-portal-ferese \
python3 scripts/tests/test_portal_isolated.py
```
Add `FERESE_TEST_PORTAL_CONSENT=1` to exercise the native picker and restricted
PipeWire connection. Select the temporary display and click **Share** when
prompted. Tests capture only the nested preview and leave host PAM policies and
D-Bus activation unchanged.
Streams run at up to 30 fps and support hidden or embedded cursors. Capture copies
frames through CPU-accessible buffers, so large or multiple streams can use
substantial CPU and memory bandwidth.
## Troubleshooting
Confirm that `XDG_CURRENT_DESKTOP` contains `Ferese`, PipeWire is running, and
`FERESE_ENABLE_SCREENCOPY` was not set to `0` before launching the session.
- Confirm PipeWire and WirePlumber are running.
- Confirm `XDG_CURRENT_DESKTOP` includes `Ferese`.
- Check that `FERESE_ENABLE_SCREENCOPY` was not set to `0` before session startup.
- Check for portal overrides in `~/.config/xdg-desktop-portal/`.
The installed preference is
`/usr/share/xdg-desktop-portal/ferese-portals.conf`. A user override under
`~/.config/xdg-desktop-portal/` can take precedence. Its ScreenCast preference
should name `ferese`:
The installed preference is `/usr/share/xdg-desktop-portal/ferese-portals.conf`.
Its ScreenCast entry should select Ferese:
```ini
[preferred]
@@ -144,8 +75,7 @@ default=gtk;
org.freedesktop.impl.portal.ScreenCast=ferese;
```
If capture dimensions change, stop and start sharing again. Ferese ends the old
session instead of feeding incorrectly sized frames to the application.
For development, `xdg-desktop-portal-ferese --sources` lists displays available
on the current `WAYLAND_DISPLAY`. This command does not start a stream.
`xdg-desktop-portal-ferese --sources` lists displays on the current
`WAYLAND_DISPLAY` without starting a stream. Nested previews do not replace the
host desktop's portal environment. See [Development](development.md#portal-checks)
for isolated sharing tests.
+106
View File
@@ -0,0 +1,106 @@
# Shortcuts and gestures
`Super` is usually the Windows key. These are the built-in defaults. Open
**Settings → Shortcuts** to change bindings and gestures or disable the login
guide. Your saved bindings can override these defaults.
## Move and resize with the mouse
| Action | Result |
| --- | --- |
| Super + left-button drag on a floating window | Move the window |
| Super + right-button drag on a floating window | Resize from the nearest corner |
| Super + Shift + Space | Switch the focused window between tiled and floating |
You can drag from inside the window; a title bar or resize border is not required.
To place an app freely, toggle it to floating, then Super-drag it.
## Focus, move, and resize
`H`, `J`, `K`, and `L` mean left, down, up, and right.
| Shortcut | Action |
| --- | --- |
| Super + H / J / K / L | Focus in that direction |
| Super + Shift + H / J / K / L | Move the focused window in that direction |
| Super + Ctrl + H / J / K / L | Resize in that direction |
| Super + Q | Close the focused window |
| Super + F | Maximize or restore the window |
| Super + Shift + F | Enter or leave fullscreen |
Maximize fills the available workspace. Fullscreen also hides the bar and window
decorations.
## Columns and layouts
| Shortcut | Action |
| --- | --- |
| Super + R | Cycle column width |
| Super + C | Center the focused column |
| Super + [ | Place the window in a neighbouring column |
| Super + ] | Extract the window into its own column |
| Super + M | Switch scrolling / tree layout |
| Super + Tab | Open or close overview |
In scrolling mode, **Super + [** uses the left neighbour when available, otherwise
the right. Try it with two terminals to place them vertically in one column.
**Super + ]** separates the focused window again.
In overview, select a workspace to see its windows, then click the window you
want. Directional focus keys move selection; Enter activates it and Escape closes
overview.
## Workspaces
| Shortcut | Action |
| --- | --- |
| Super + 1–9 | Switch to that workspace |
| Super + Shift + 1–9 | Move the window to that workspace |
Workspaces are created as needed. Selecting one visible on another monitor focuses
that monitor.
## Desktop actions
| Shortcut | Action |
| --- | --- |
| Super + Enter | Open the configured terminal (`foot` by default) |
| Print Screen | Capture the active monitor and open Satty |
| Super + Shift + S | Select a screenshot area and open Satty |
| Super + Shift + E | Ask to log out |
In Satty, Enter saves the edited screenshot and copies it to the clipboard;
Escape discards it. See [Configuration](configuration.md#commands-and-bindings)
for dependencies and custom screenshot commands.
## Touchpad gestures
| Three-finger swipe | Action on release |
| --- | --- |
| Up / down | Next / previous workspace on this monitor |
| Left / right | Focus the window to the right / left |
Short, diagonal, or cancelled swipes do nothing. Gestures can use the same actions
as keyboard bindings, including overview or moving a window. Four- and five-finger
bindings are supported too.
## Add a shortcut
For example, bind a lock command and use a swipe to open overview:
```kdl
commands {
lock "ferese-lock"
}
binding "Super+Alt+L" "spawn" "lock"
binding "Swipe3Up" "toggle-overview"
```
These are examples, not defaults. To disable a built-in binding:
```kdl
binding "Super+Q" disabled=#true
```
See [Commands and bindings](configuration.md#commands-and-bindings) for action
names, arguments, and physical-key matching.
+16 -60
View File
@@ -1,34 +1,26 @@
# Why Ferese
Omarchy and Ferese come from two different ideas about how a desktop should be
built. Omarchy brings existing Linux tools together and configures them as one
environment, which gives it access to mature components that already do their
jobs well.
Ferese combines window management and a desktop shell with one configuration.
Settings, the bar, notifications, widgets, authentication prompts, and the lock
screen share colors and fonts.
A setup like that might combine:
## Choose how windows fit
```text
Hyprland + bar + launcher + notifications + lock screen + theme scripts
```
Scrolling columns let you keep windows at useful widths and move through them
horizontally. Tree tiling divides the screen into nested splits. Floating windows
work alongside either layout, and overview shows windows across workspaces.
There is nothing wrong with this approach, and being able to replace each part
is useful, but those parts still have their own configuration, state, rendering,
and lifecycle. Matching their themes can make them look related, while keeping
their behavior in sync remains an ongoing part of maintaining the desktop.
![Ferese Overview showing windows and workspaces](images/ferese-overview.webp)
With Ferese, we are building the compositor and shell together, so workspaces,
layout, focus, outputs, input, and materials all come from the same desktop
state. The shell and Settings use first-party interfaces to work with that state
instead of reading separate versions of it from several external tools.
## Change the desktop together
This does not mean every Ferese component has to run in one process. It means
that the components share the same concepts, configuration, and protocols, so a
workspace or surface means the same thing everywhere it appears.
Choose a theme in Settings, then adjust the wallpaper, colors, font, corners,
and motion. Changes apply live. Window corners and shell corners can be set
independently.
## What this means in practice
![Ferese Settings and Control Center in Gruvbox](images/ferese-gruvbox.webp)
You can see the difference in something as simple as corner radius. Ferese has a
shared setting for shell surfaces:
For example, this changes shell corners:
```kdl
theme {
@@ -38,41 +30,5 @@ theme {
}
```
Changing that value updates the bar, Control Center, popovers, notifications,
widgets, and native lock screen together. Managed windows have their own radius
because they are a different kind of surface, but each value still has one clear
meaning wherever it is used.
![Ferese Settings and Control Center sharing the Gruvbox theme](images/ferese-gruvbox.webp)
In an assembled desktop, making the same visual change may mean editing the
window manager, bar CSS, launcher theme, notification daemon, and lock screen
config separately, then keeping those values aligned as the setup changes.
The same difference shows up in how the desktop behaves. Ferese implements
scrolling as part of its workspace model, renders blur behind shell surfaces in
the compositor, and builds the overview from the real window scene. Focus,
fullscreen, workspace geometry, and input all work from the same state rather
than waiting for separate tools to catch up with one another.
![Ferese Overview showing open windows and two workspace thumbnails](images/ferese-overview.webp)
When a window opens, Ferese can handle the result as one transition:
```text
window opens → layout updates → viewport moves → input follows
```
An assembled environment may need to publish a window-manager event, pass it
through IPC, run a script, update the bar, and start another animation before
the rest of the desktop reflects the change. Each step can be fast, but it is
still another boundary that has to be maintained.
## The tradeoff we are making
Building more of the desktop ourselves gives Ferese tighter control over how its
parts work together, but it also leaves us responsible for more code. Omarchy
can rely on mature components from across the Linux desktop ecosystem, while
Ferese has to build that same level of maturity over time, and that is a tradeoff
we are willing to make to keep the desktop consistent in both behavior and
configuration.
See [Configuration](configuration.md) for all options, or
[Installation](installation.md) to try Ferese in a nested preview.