diff --git a/.gitignore b/.gitignore index a90a9e3..373a9c5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,14 +3,8 @@ *.log ChatGPT Image*.png -# Local implementation notes and validation plans +# Local architecture decision notes /docs/adr/ -/docs/animation-validation.md -/docs/nested-resize-testing.md -/docs/native-soak-testing.md -/docs/ferese-window-manager-spec.md -/docs/ferese-theme-ui-spec.md -/docs/ferese-shell-design.md # Generated GitHub Pages site /build/site/ diff --git a/README.md b/README.md index 22249a7..256d7f4 100644 --- a/README.md +++ b/README.md @@ -2,60 +2,35 @@ Ferese

-

A beautiful, fluid Wayland desktop made to feel like yours.

+

A Wayland desktop with scrolling layouts and a shared theme.

Install · Configure · - Widgets · - Notifications + Documentation

-Ferese is a complete Wayland desktop built around personalization. Its custom -shell, native settings, live-reloading themes and wallpapers, desktop widgets, -fluid animations, and scrolling and tiling layouts work together as one -cohesive experience—beautiful by default and unmistakably yours. +Ferese is a Linux desktop with scrolling columns, tree tiling, and floating +windows. It includes a top bar, Settings, notifications, desktop widgets, a +lock screen, and screen sharing. Change themes and settings without restarting. ![Ferese Blue desktop with Settings, Control Center, and a clock widget](docs/images/ferese-desktop.webp) -## Why Ferese +## Features -- **Flexible layouts:** scrolling columns, tree tiling, floating windows and a - workspace overview. -- **A native shell:** menu bar, control center, settings, notifications and - desktop widgets designed to belong together, including themed authentication prompts. - Native apps share the [Ferese theme components](crates/ferese-theme/README.md). -- **Personal by design:** themes, wallpapers, accent colors, blur, borders and - motion that update without restarting your session. -- **Ready for real setups:** gestures, multi-monitor support, fractional - scaling, screenshots, background services and session locking. -- **Screen sharing:** a native display picker and PipeWire video streams for - portal-compatible recording and conferencing apps. +- Scrolling and tree layouts, floating windows, and a workspace overview. +- Configurable keyboard shortcuts and touchpad gestures. +- Multiple monitors with fractional scaling. +- Six theme presets, wallpapers, blur, and adjustable corners. +- Clock and sticky-note widgets. +- Screenshots with Satty editing, screen sharing, and top-bar recording. -![Ferese Overview showing open windows and two workspace thumbnails](docs/images/ferese-overview.webp) - -The overview brings windows and workspaces into one view. Read more about -[why Ferese builds the compositor and shell together](docs/why-ferese.md). - -## Make it yours - -Use the native Settings app for everyday customization, or edit -`~/.config/ferese/config.kdl` when you want complete control. Ferese reloads -changes as you make them and keeps the last working configuration if an edit is -invalid. - -![Ferese Settings and Control Center sharing the Gruvbox theme](docs/images/ferese-gruvbox.webp) - -Choose a preset or build your own look. Window behavior, gestures, shortcuts, -rules, displays, widgets and login items are configurable too. Start with the -[configuration guide](docs/configuration.md) or the -[example config](packaging/config.kdl). +![Ferese Overview showing windows and workspaces](docs/images/ferese-overview.webp) ## Install -Install Ferese on Linux with any Wayland-capable login manager, a command-based -greeter, or a local TTY. Start with the [installation guide](docs/installation.md) -for system dependencies and Rust 1.95 or newer, then build as your normal user: +Install the [dependencies](docs/installation.md#requirements), then build as your +normal user: ```sh git clone https://github.com/ferese-wm/ferese.git @@ -63,59 +38,48 @@ cd ferese ./scripts/install.sh ``` -Log out and select **Ferese** at your login screen. For greeters that take a -session command, use `/usr/local/bin/ferese-session`. The installer preserves -your configuration and other sessions, and retains the previous release for -rollback. The guide covers [all startup methods](docs/installation.md#start-a-session), -optional tools, updates, and recovery. +Log out, select **Ferese** at your login screen, and sign in. For a TTY or a +greeter that accepts a command, run `ferese-session`. +See [Installation](docs/installation.md) for previews, updates, and recovery. -To preview Ferese inside your current Wayland desktop instead: +## Configure -```sh -cargo build --release --locked -p ferese -p ferese-shell -p ferese-settings -p feresectl -p ferese-lock -target/release/ferese --backend nested --grant-effects --grant-shell-control -- \ - target/release/ferese-shell -``` +Open **Control Center → Settings**, or edit `~/.config/ferese/config.kdl`. +Changes reload automatically; invalid edits keep the last working configuration. -## Get around +![Ferese Settings and Control Center in Gruvbox](docs/images/ferese-gruvbox.webp) -These are the built-in defaults; every shortcut and gesture can be changed. -The login guide shows your active shortcuts. Disable it in **Settings → Shortcuts** -when you no longer need it. +See the [configuration reference](docs/configuration.md) and +[example config](packaging/config.kdl). + +## Default shortcuts + +`Super` is usually the Windows key. Change shortcuts and gestures in +**Settings → Shortcuts**. | Shortcut | Action | | --- | --- | | Super + Enter | Open a terminal | -| Super + H / J / K / L | Focus a window | +| Super + H / J / K / L | Focus left / down / up / right | +| Super + F | Maximize the window | +| Super + Shift + F | Toggle fullscreen | | Super + R | Cycle column width | -| Super + Tab | Open workspace and window overview | +| Super + Tab | Open overview | | Super + M | Switch scrolling / tree layout | +| Print Screen | Capture the active monitor and open Satty | | Super + Shift + S | Select a screenshot area | +| Super + Shift + E | Ask to log out | -Click the bar date for a calendar. Click the battery for power modes and -confirmed Restart, Power off and Suspend actions. +See [all shortcuts and mouse actions](docs/shortcuts.md), including Super-drag +to move and Super-right-drag to resize floating windows. -Three-finger swipes move between workspaces and windows. Open **Control Center → -Settings → Shortcuts** to change them or assign gestures to other actions. +Three-finger swipes up/down switch workspaces; left/right focus windows. +The shortcut guide appears at login until disabled in Settings. ## Documentation -- [Why Ferese](docs/why-ferese.md) -- [Installation and recovery](docs/installation.md) -- [Configuration reference](docs/configuration.md) -- [Desktop widgets](docs/desktop-widgets.md) -- [Notifications](docs/notifications.md) -- [Native locker](docs/locking.md) -- [Desktop portals and compatibility](docs/portals.md) -- [Screen sharing and recording](docs/screen-sharing.md) +Start with the [documentation index](docs/README.md) for user guides and +[development instructions](docs/development.md) for builds and testing. -## Development - -```sh -cargo test --workspace --locked -cargo fmt --all --check -``` - -Ferese is built in Rust on Smithay. Use release builds when evaluating motion -and startup performance, and keep another desktop session available while -testing hardware behavior. +Ferese is written in Rust using Smithay. Report bugs on +[GitHub](https://github.com/ferese-wm/ferese/issues) with reproduction steps and logs. diff --git a/compositor/src/state.rs b/compositor/src/state.rs index 770a608..a0a5c4e 100644 --- a/compositor/src/state.rs +++ b/compositor/src/state.rs @@ -65,7 +65,7 @@ use smithay::wayland::xdg_toplevel_icon::XdgToplevelIconManager; use crate::backends::direct::DirectBackendState; use crate::config::{Binding, DaemonConfig, InputSettings, OutputProfile, ThemeSettings}; -use crate::cursor::NamedCursor; +use crate::cursor::{NamedCursor, cursor_theme, load_named_cursor}; use crate::dimming::DimAnimation; use crate::gestures::{Swipe, SwipeDirection}; use crate::handlers::screencopy::PendingScreencopy; @@ -74,13 +74,14 @@ use crate::handlers::screenshot_worker::Worker; use crate::input::LockedPointerHint; use crate::input_capture::InputCapture; use crate::ipc::IpcSocketGuard; +use crate::overview::OverviewState; use crate::portal_session::PortalSession; use crate::portal_shortcuts::PortalShortcuts; use crate::resize_transaction::ResizeTransaction; use crate::session_lock::{IdleSettings, Lock}; use crate::shell_control::ShellSnapshot; use crate::stacking::WindowStack; -use crate::wallpaper::WallpaperState; +use crate::wallpaper::{WallpaperConfig, WallpaperState}; use crate::window_rules::{WindowRule, resolve as resolve_window_rules}; use crate::winit::{ BlurProgram, MaterialBuffers, MaterialProgram, NestedBackend, OverviewScrim, ResizeSnapshot, RoundedClipPrograms, @@ -344,10 +345,10 @@ pub struct Ferese { } pub struct RuntimeConfig { - pub(crate) lock_idle: crate::session_lock::IdleSettings, - pub(crate) autostart: Vec, + pub(crate) lock_idle: IdleSettings, + pub(crate) autostart: Vec, pub(crate) overview_font_family: String, - pub(crate) wallpaper: crate::wallpaper::WallpaperConfig, + pub(crate) wallpaper: WallpaperConfig, pub layout_mode: LayoutMode, pub gap_config: GapConfig, pub input_settings: InputSettings, @@ -371,9 +372,11 @@ impl Ferese { config: RuntimeConfig, ) -> Result> { let display_handle = display.handle(); + crate::handlers::screencopy::init_global(&display_handle); crate::effects::init_global(&display_handle); crate::shell_control::init_global(&display_handle); + let alpha_modifier_state = AlphaModifierState::new::(&display_handle); let compositor_state = CompositorState::new::(&display_handle); let cursor_shape_state = CursorShapeManagerState::new::(&display_handle); @@ -427,8 +430,8 @@ impl Ferese { let socket_name = Self::init_wayland_listener(display, event_loop)?; let start_time = Instant::now(); - let cursor_theme = crate::cursor::cursor_theme(); - let default_cursor = crate::cursor::load_named_cursor(&cursor_theme, CursorIcon::Default); + let cursor_theme = cursor_theme(); + let default_cursor = load_named_cursor(&cursor_theme, CursorIcon::Default); let named_cursors = HashMap::from([(CursorIcon::Default, default_cursor)]); let mut state = Self { @@ -457,10 +460,10 @@ impl Ferese { resize_transactions: HashMap::new(), resize_snapshots: HashMap::new(), nested_backend: None, - wallpaper: crate::wallpaper::WallpaperState::with_wakeup(config.wallpaper, Some(event_loop.get_signal())), + wallpaper: WallpaperState::with_wakeup(config.wallpaper, Some(event_loop.get_signal())), maximized_windows: HashSet::new(), maximized_column_widths: HashMap::new(), - window_stack: crate::stacking::WindowStack::default(), + window_stack: WindowStack::default(), floating_above_fullscreen: HashMap::new(), natural_floating_pending: HashSet::new(), window_borders: HashMap::new(), @@ -473,7 +476,7 @@ impl Ferese { overview_scrims: HashMap::new(), material_programs: HashMap::new(), material_buffers: HashMap::new(), - portal_shortcuts: crate::portal_shortcuts::PortalShortcuts::default(), + portal_shortcuts: PortalShortcuts::default(), pending_logout: None, logout_query: None, logout_owner: None, @@ -506,20 +509,20 @@ impl Ferese { cursor_theme, named_cursors, intercepted_keys: HashSet::new(), - swipe: crate::gestures::Swipe::default(), + swipe: Swipe::default(), idle_inhibitors: HashMap::new(), active_shortcuts_inhibitor: None, direct_backend: None, _ipc_socket: None, pending_dmabuf_imports: Vec::new(), pending_screencopies: Vec::new(), - screenshot: crate::handlers::screenshot::Coordinator::new(), + screenshot: Coordinator::new(), screenshot_parts: None, screenshot_worker: None, shell_resources: Vec::new(), shell_snapshot_serial: 0, last_shell_snapshot: None, - overview: crate::overview::OverviewState::with_font_family(config.overview_font_family), + overview: OverviewState::with_font_family(config.overview_font_family), next_window_id: 1, next_output_id: 1, last_animation_tick: start_time, @@ -557,6 +560,7 @@ impl Ferese { state.screenshot_parts = Some(screenshot.parts); state.screenshot_worker = Some(screenshot.worker); state._ipc_socket = Some(screenshot._guard); + Ok(state) } diff --git a/crates/ferese-theme/README.md b/crates/ferese-theme/README.md index de0b05f..6fac543 100644 --- a/crates/ferese-theme/README.md +++ b/crates/ferese-theme/README.md @@ -5,7 +5,7 @@ prompt and screen-sharing/recording chooser. - `Palette` resolves KDL colors and corners and builds the native toolkit theme. - `PRESETS` defines the six built-in color schemes. -- `font` and `text` share bounded font-name storage and explicit font selection. +- `font` and `text` provide shared font selection and text. - `controls` supplies surfaces, navigation, font-aware text buttons and input styles. - `menus` supplies headings, rows, sections, separators, switches and slider styles. - `calendar` supplies date/time labels, stacked clock digits and calendar grids. @@ -16,9 +16,8 @@ prompt and screen-sharing/recording chooser. Keep the attachment alive with its window and make the client background transparent after attachment succeeds. Other compositors retain the client fill. -Applications own layout, data, actions and animation timing. Pass their current -palette and animation progress into the shared components; do not build another -native theme or copy a common control style into an application. +Applications supply layout, data, actions, and animation progress. Extend shared +components when a style is needed across apps. ```rust let palette = ferese_theme::Palette::from_document(Some(&document)); diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..e934802 --- /dev/null +++ b/docs/README.md @@ -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). diff --git a/docs/animation-validation.md b/docs/animation-validation.md new file mode 100644 index 0000000..9806999 --- /dev/null +++ b/docs/animation-validation.md @@ -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. diff --git a/docs/configuration.md b/docs/configuration.md index faa3c2c..74ee86e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -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. diff --git a/docs/desktop-widgets.md b/docs/desktop-widgets.md index 1ff87c2..2e5bd66 100644 --- a/docs/desktop-widgets.md +++ b/docs/desktop-widgets.md @@ -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 | diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..d7aa035 --- /dev/null +++ b/docs/development.md @@ -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. diff --git a/docs/ferese-shell-design.md b/docs/ferese-shell-design.md new file mode 100644 index 0000000..40fea05 --- /dev/null +++ b/docs/ferese-shell-design.md @@ -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. diff --git a/docs/ferese-theme-ui-spec.md b/docs/ferese-theme-ui-spec.md new file mode 100644 index 0000000..221a143 --- /dev/null +++ b/docs/ferese-theme-ui-spec.md @@ -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. diff --git a/docs/ferese-window-manager-spec.md b/docs/ferese-window-manager-spec.md new file mode 100644 index 0000000..106c14b --- /dev/null +++ b/docs/ferese-window-manager-spec.md @@ -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. diff --git a/docs/installation.md b/docs/installation.md index c57e45e..dc061f7 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -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: diff --git a/docs/locking.md b/docs/locking.md index 1f7e2b4..9aefbf4 100644 --- a/docs/locking.md +++ b/docs/locking.md @@ -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. diff --git a/docs/native-soak-testing.md b/docs/native-soak-testing.md new file mode 100644 index 0000000..e3fc31e --- /dev/null +++ b/docs/native-soak-testing.md @@ -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-.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. diff --git a/docs/nested-resize-testing.md b/docs/nested-resize-testing.md new file mode 100644 index 0000000..6e8dde7 --- /dev/null +++ b/docs/nested-resize-testing.md @@ -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. diff --git a/docs/notifications.md b/docs/notifications.md index fbe8610..1ecb169 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -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. diff --git a/docs/performance-baseline.md b/docs/performance-baseline.md index 0973f33..d96064c 100644 --- a/docs/performance-baseline.md +++ b/docs/performance-baseline.md @@ -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. diff --git a/docs/portals.md b/docs/portals.md index 227c3e8..4f90803 100644 --- a/docs/portals.md +++ b/docs/portals.md @@ -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 - > 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). diff --git a/docs/screen-sharing.md b/docs/screen-sharing.md index c53152e..8766648 100644 --- a/docs/screen-sharing.md +++ b/docs/screen-sharing.md @@ -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. diff --git a/docs/shortcuts.md b/docs/shortcuts.md new file mode 100644 index 0000000..dab7dd1 --- /dev/null +++ b/docs/shortcuts.md @@ -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. diff --git a/docs/why-ferese.md b/docs/why-ferese.md index d5f8388..7dbebc0 100644 --- a/docs/why-ferese.md +++ b/docs/why-ferese.md @@ -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. diff --git a/scripts/build-site.py b/scripts/build-site.py index 2202965..9336c14 100644 --- a/scripts/build-site.py +++ b/scripts/build-site.py @@ -16,10 +16,13 @@ PAGES = [ ('why-ferese', 'Why Ferese', ROOT / 'docs/why-ferese.md'), ('installation', 'Installation', ROOT / 'docs/installation.md'), ('configuration', 'Configuration', ROOT / 'docs/configuration.md'), + ('shortcuts', 'Shortcuts and gestures', ROOT / 'docs/shortcuts.md'), ('desktop-widgets', 'Desktop widgets', ROOT / 'docs/desktop-widgets.md'), ('notifications', 'Notifications', ROOT / 'docs/notifications.md'), - ('locking', 'Native locker', ROOT / 'docs/locking.md'), + ('locking', 'Lock screen', ROOT / 'docs/locking.md'), ('screen-sharing', 'Screen sharing', ROOT / 'docs/screen-sharing.md'), + ('portals', 'Desktop portals', ROOT / 'docs/portals.md'), + ('development', 'Development', ROOT / 'docs/development.md'), ] md = MarkdownIt('commonmark', {'html': False}).enable('table') OUTPUT.mkdir(parents=True, exist_ok=True) @@ -101,7 +104,7 @@ for page_number, (slug, title, source) in enumerate(PAGES):
{body}
View this guide on GitHub
-''' +''' (OUTPUT / 'docs' / f'{slug}.html').write_text(doc) (OUTPUT / 'docs/search-index.json').write_text(json.dumps(search_index, ensure_ascii=False)) print(f'Built website and {len(PAGES)} handbook pages at {OUTPUT}') diff --git a/site/README.md b/site/README.md index 7bf28a7..0304fcb 100644 --- a/site/README.md +++ b/site/README.md @@ -2,7 +2,6 @@ The public site is static HTML and CSS. The handbook is generated from the project's Markdown guides, so website and repository documentation stay in sync. -There are no client frameworks, external fonts, or tracking scripts. ## Local preview @@ -18,8 +17,7 @@ site sources or Markdown guides, then refresh the browser. - `site/index.html` and `site/styles.css`: homepage and shared visual design. - `site/content/index.md`: handbook introduction. -- `docs/{why-ferese,installation,configuration,desktop-widgets,notifications,locking,screen-sharing}.md`: - source guides. +- `docs/*.md`: guides; published pages are listed in `scripts/build-site.py`. - `scripts/build-site.py`: renders Markdown, resolves links, generates section navigation, previous/next links, and the search index. - `site/app.js`: handbook search and code copying. Reading and navigation work diff --git a/site/content/index.md b/site/content/index.md index 7d60613..5551c19 100644 --- a/site/content/index.md +++ b/site/content/index.md @@ -1,69 +1,53 @@ -# The Ferese handbook +# Ferese handbook -Ferese is a complete Wayland desktop built around personalization. Its custom shell, native Settings, live themes, and flexible layouts work together as one cohesive experience. +Ferese is a Linux Wayland desktop with scrolling columns, tree tiling, floating +windows, and a shared theme for its desktop tools. -This handbook covers installing Ferese, finding your way around, and making the desktop your own. +## Get started -[Why Ferese](why-ferese.md) explains why the desktop is designed as one integrated system rather than assembled from separately configured tools. - -## Start with your first session - -1. Read [Installation](installation.md) for dependencies, building, login-manager or TTY startup, and recovery. +1. [Install Ferese](installation.md), or try a [nested preview](installation.md#preview-and-logs). 2. Open **Control Center → Settings** to choose a theme and configure your desktop. -3. Explore the [configuration reference](configuration.md) for layouts, shortcuts, window rules, and displays. +3. Use the [configuration reference](configuration.md) for shortcuts, gestures, window rules, and displays. -Want to try it before switching desktops? The [preview instructions](installation.md#preview-and-logs) cover running Ferese inside an existing Wayland session. +## Default shortcuts -## Find your way around - -Ferese supports scrolling columns, tree tiling, floating windows, and a workspace overview. These are the built-in keyboard shortcuts; all can be changed in Settings. +`Super` is usually the Windows key. All these shortcuts can be changed in Settings. | Shortcut | Action | | --- | --- | | Super + Enter | Open a terminal | -| Super + H / J / K / L | Focus a window | -| Super + R | Cycle column width | -| Super + Tab | Open workspace and window overview | +| Super + H / J / K / L | Focus left / down / up / right | +| Super + F | Maximize the window | +| Super + Shift + F | Toggle fullscreen | +| Super + Tab | Open overview | | Super + M | Switch scrolling / tree layout | +| Print Screen | Capture the active monitor and open Satty | | Super + Shift + S | Select a screenshot area | +| Super + Shift + E | Ask to log out | -Three-finger swipes move between workspaces and windows. Open **Control Center → Settings → Shortcuts** to change the defaults or assign gestures to other actions. +See [all shortcuts and mouse actions](shortcuts.md), including Super-drag to move +and Super-right-drag to resize floating windows. -## Make it feel like yours +Three-finger swipes up/down switch workspaces; left/right focus windows. +Change gestures in **Settings → Shortcuts**. The login shortcut guide can be +disabled there too. -Use **Settings** for everyday customization. Choose an appearance preset, change the wallpaper, adjust shell and window corners, or add a clock and sticky notes through [Desktop widgets](desktop-widgets.md). +## Desktop guides -[Native locker](locking.md) covers lock-screen appearance, authentication, and idle locking. +- [Desktop widgets](desktop-widgets.md): clocks and sticky notes. +- [Notifications](notifications.md): history and Do Not Disturb. +- [Lock screen](locking.md): appearance, automatic locking, and display sleep. +- [Screen sharing and recording](screen-sharing.md): share an app or display, or save a video. +- [Desktop portals](portals.md): integration with applications. -[Notifications](notifications.md) explains popup cards, history, Do Not Disturb, and app actions. +Use Settings or edit `~/.config/ferese/config.kdl`. Changes reload automatically; +invalid edits keep the last working configuration. -If you prefer a text editor, your configuration lives at: +## Troubleshooting -```text -~/.config/ferese/config.kdl -``` +See [logs and recovery](installation.md#logout-and-recovery). `feresectl exit` +ends the session immediately; `feresectl request-logout` asks for confirmation. -Ferese reloads changes as you make them. An invalid edit keeps the last working configuration. Settings also preserves a backup of your previous save. See [Saving and validation](configuration.md#saving-and-validation). - -## Share your screen - -Use Ferese’s native display picker to share a monitor with a portal-compatible -recording or conferencing app. To save a video directly, click the recording -icon in the top bar. See [Screen sharing and recording](screen-sharing.md) for setup, stopping a session, and supported sources. - -## Keep a recovery path - -Keep another desktop session available while testing hardware behavior. Save your work before logging out: `feresectl exit` and **Super + Shift + E** end the current session immediately. - -The [recovery guide](installation.md#logout-and-recovery) explains logs, installed releases, and rollback. - -## Work on Ferese - -Ferese is built in Rust on Smithay. Use release builds when evaluating motion and startup performance. From the repository checkout: - -```sh -cargo test --workspace --locked -cargo fmt --all --check -``` - -The [source repository](https://github.com/ferese-wm/ferese) is the place to explore implementation details and report issues. When reporting a problem, include how to reproduce it, whether you used the nested or DRM backend, and relevant logs. +Report problems in the [issue tracker](https://github.com/ferese-wm/ferese/issues). +Include reproduction steps, whether Ferese ran as a nested preview or a direct +session, and relevant logs. Contributors can start with [Development](development.md). diff --git a/site/index.html b/site/index.html index 78e72a5..8a3510b 100644 --- a/site/index.html +++ b/site/index.html @@ -6,15 +6,15 @@ - Ferese — A desktop that feels like yours + Ferese — Wayland desktop @@ -47,11 +47,11 @@

- A beautiful, fluid Wayland desktop made to feel like yours. + A Wayland desktop with scrolling layouts and a shared theme.

- A complete Linux desktop with a custom shell, native settings, - scrolling and tiling layouts, and live-reloading themes. + A Linux desktop with a top bar, Settings, + scrolling and tiling layouts, and themes that update live.