From 392fc27de110d3548095e465d5cb38bd8d5730ea Mon Sep 17 00:00:00 2001 From: Kent Daleng Date: Sat, 15 Mar 2025 16:42:05 +0100 Subject: Use anchors on the wiki (#1266) * wiki testing * wiki updates * use .md with anchors, revert sidebar * bump wiki action * add some more anchors, fix some language * change links to be more descriptive by themselves --- wiki/Animation-Timing.md | 46 -------------------- wiki/Application-Issues.md | 4 +- wiki/Configuration:-Debug-Options.md | 2 +- wiki/Configuration:-Key-Bindings.md | 2 +- wiki/Configuration:-Layer-Rules.md | 7 ++-- wiki/Configuration:-Layout.md | 10 ++--- wiki/Configuration:-Miscellaneous.md | 5 +-- wiki/Configuration:-Outputs.md | 2 +- wiki/Configuration:-Overview.md | 2 +- wiki/Configuration:-Switch-Events.md | 2 +- wiki/Configuration:-Window-Rules.md | 18 ++++---- wiki/Design-Principles.md | 30 -------------- wiki/Developing-niri.md | 76 ---------------------------------- wiki/Development:-Animation-Timing.md | 46 ++++++++++++++++++++ wiki/Development:-Design-Principles.md | 30 ++++++++++++++ wiki/Development:-Developing-niri.md | 76 ++++++++++++++++++++++++++++++++++ wiki/Development:-Fractional-Layout.md | 34 +++++++++++++++ wiki/Development:-Redraw-Loop.md | 22 ++++++++++ wiki/Example-systemd-Setup.md | 2 +- wiki/FAQ.md | 8 ++-- wiki/Fractional-Layout.md | 34 --------------- wiki/Gestures.md | 2 +- wiki/Important-Software.md | 6 +-- wiki/Redraw-Loop.md | 22 ---------- wiki/Tabs.md | 2 +- wiki/Xwayland.md | 4 +- wiki/_Sidebar.md | 10 ++--- 27 files changed, 251 insertions(+), 253 deletions(-) delete mode 100644 wiki/Animation-Timing.md delete mode 100644 wiki/Design-Principles.md delete mode 100644 wiki/Developing-niri.md create mode 100644 wiki/Development:-Animation-Timing.md create mode 100644 wiki/Development:-Design-Principles.md create mode 100644 wiki/Development:-Developing-niri.md create mode 100644 wiki/Development:-Fractional-Layout.md create mode 100644 wiki/Development:-Redraw-Loop.md delete mode 100644 wiki/Fractional-Layout.md delete mode 100644 wiki/Redraw-Loop.md (limited to 'wiki') diff --git a/wiki/Animation-Timing.md b/wiki/Animation-Timing.md deleted file mode 100644 index e9d5f373..00000000 --- a/wiki/Animation-Timing.md +++ /dev/null @@ -1,46 +0,0 @@ -> *Time, Dr. Freeman? Is it really that... time again?* - -A compositor deals with one or more monitors on mostly fixed refresh cycles. -For example, a 170 Hz monitor can draw a frame every ~5.88 ms. - -Most of the time, the compositor doesn't actually redraw the monitor: when nothing changes on screen (e.g. you're reading a document and aren't moving your cursor), it would be wasteful to wake up the GPU to composite the same image. -During an animation however, screen contents do change every frame. -Niri will generally start drawing the next frame as soon as the previous one shows up on screen. - -Since the monitor refresh cycle is fixed in most cases (even with VRR, there's a maximum refresh rate), the compositor can predict when the next frame will show up on the monitor, and render ongoing animations for that exact moment in time. -This way, all animation frames are perfectly timed with no jitter, regardless of when exactly the rendering code had a chance to run. -For example, even if the compositor has to process new window events, delaying the rendering by a few ms, the animation timing will remain exactly aligned to the monitor refresh cycle. - -There are hence several properties that a compositor wants from its timing system. - -1. It should be possible to get the state of the animations at a specific time in the near future, for rendering a frame exactly timed to when the monitor will show it. - - This time override ability should be usable in tests to advance the time in a fully controlled fashion. -1. Animations in response to user actions should begin at the moment when the action happens. - For example, pressing a workspace switch key should start the animation at the instant when the user pressed the key (rather than, say, slightly in the future where we predicted the next monitor frame, which we had already rendered by now). -1. During the processing of a single action, querying the current time should return the exact same value. - Even if the processing finishes a few microseconds after it started, querying the time in the end should return the same thing. - This generally makes writing code much more sane; otherwise you'd need to for example avoid reading the position of some element twice in a row, since it could have moved by one pixel in-between, screwing with the logic. - Also, fetching the current system time [can be quite expensive](https://mastodon.online/@YaLTeR/109934977035721850) in terms of overhead. -1. It should be reasonably easy to implement an animation slow-down preference, so all animations can be slowed down or sped up by the same factor. - -The solution in niri is a `LazyClock`, a clock that remembers one timestamp. -Initially, the timestamp is empty, so when you ask `LazyClock` for the current time, it will fetch and return the system time, and also remember it. -Subsequently, it will keep returning the same timestamp that it had remembered. - -You can also clear the timestamp, then `LazyClock` will fetch the system time anew when it's needed. -In niri, the timestamp is cleared at the end of every event loop iteration, right before going to sleep waiting for new events. -This way, anything that happens next (like a user key press) will fetch and use the most up-to-date timestamp as soon as one is needed, but then the processing code will keep getting the exact same timestamp, since `LazyClock` stores it. - -You can also just manually set the timestamp to a specific value. -This is how we render a frame for the predicted time of when the monitor will show it. -Also, this is used by tests: they simply always set the timestamp and never use the system time. - -Finally, there's an `AdjustableClock` wrapper on top that provides the ability to control the slow-down rate by modifying the timestamps returned by the clock. - -An important detail is that with rate changes, timestamps from the `AdjustableClock` will drift away and become unrelated to the system time. -However, our target timestamp (for rendering) comes from the system time, so the override works directly on the underlying `LazyClock`. -That is, overriding the timestamp and then querying the `AdjustableClock` will return a *different* timestamp that is correct and consistent with the adjustments made by `AdjustableClock`. -This is reflected in the API by naming the function `Clock::set_unadjusted()` (and there's also `Clock::now_unadjusted()` to get the raw timestamp). - -The clock is shared among all animations in niri through passing around and storing a reference-counted pointer. -This way, overriding the time automatically applies to everything, whereas in tests we can use a separate clock per test so that they don't interfere with each other. diff --git a/wiki/Application-Issues.md b/wiki/Application-Issues.md index 34861876..5aba0b15 100644 --- a/wiki/Application-Issues.md +++ b/wiki/Application-Issues.md @@ -21,7 +21,7 @@ window-rule { This empty default column width lets WezTerm pick its own initial width which makes it show up properly. There's [another bug](https://github.com/wez/wezterm/issues/6472) in WezTerm that causes it to choose a wrong size when it's in a tiled state, and prevent resizing it. -Niri puts windows in the tiled state with `prefer-no-csd`. +Niri puts windows in the tiled state with [`prefer-no-csd`](./Configuration:-Miscellaneous.md#prefer-no-csd). So if you hit this problem, comment out `prefer-no-csd` in the niri config and restart WezTerm. ### Ghidra @@ -32,7 +32,7 @@ To fix this, run them with the `_JAVA_AWT_WM_NONREPARENTING=1` environment varia ### rofi-wayland There's a bug in rofi-wayland that prevents it from accepting keyboard input on niri with errors in the output. -It's been fixed in rofi, but the fix had not been released yet: https://github.com/davatorium/rofi/discussions/2008 +It's been fixed in rofi, but [the fix had not been released yet](https://github.com/davatorium/rofi/discussions/2008). ### Fullscreen games diff --git a/wiki/Configuration:-Debug-Options.md b/wiki/Configuration:-Debug-Options.md index b04f7ea7..e3586b7a 100644 --- a/wiki/Configuration:-Debug-Options.md +++ b/wiki/Configuration:-Debug-Options.md @@ -4,7 +4,7 @@ Niri has several options that are only useful for debugging, or are experimental They are not meant for normal use. > [!CAUTION] -> These options are **not** covered by the [config breaking change policy](./Configuration:-Overview.md). +> These options are **not** covered by the [config breaking change policy](./Configuration:-Overview.md#breaking-change-policy). > They can change or stop working at any point with little notice. Here are all the options at a glance: diff --git a/wiki/Configuration:-Key-Bindings.md b/wiki/Configuration:-Key-Bindings.md index 640f6fd4..513f3581 100644 --- a/wiki/Configuration:-Key-Bindings.md +++ b/wiki/Configuration:-Key-Bindings.md @@ -313,7 +313,7 @@ Actions for taking screenshots. - `screenshot`: opens the built-in interactive screenshot UI. - `screenshot-screen`, `screenshow-window`: takes a screenshot of the focused screen or window respectively. -The screenshot is both stored to the clipboard and saved to disk, according to the [`screenshot-path` option](./Configuration:-Miscellaneous.md). +The screenshot is both stored to the clipboard and saved to disk, according to the [`screenshot-path` option](./Configuration:-Miscellaneous#screenshot-path). Since: 25.02 You can disable saving to disk for a specific bind with the `write-to-disk=false` property: diff --git a/wiki/Configuration:-Layer-Rules.md b/wiki/Configuration:-Layer-Rules.md index ac99ac5a..84a8f7d6 100644 --- a/wiki/Configuration:-Layer-Rules.md +++ b/wiki/Configuration:-Layer-Rules.md @@ -6,7 +6,7 @@ Layer rules let you adjust behavior for individual layer-shell surfaces. They have `match` and `exclude` directives that control which layer-shell surfaces the rule should apply to, and a number of properties that you can set. Layer rules are processed and work very similarly to window rules, just with different matchers and properties. -Please read the [window rules](./Configuration:-Window-Rules.md) wiki page to learn how matching works. +Please read the [window rules wiki page](./Configuration:-Window-Rules.md) to learn how matching works. Here are all matchers and properties that a layer rule could have: @@ -78,8 +78,7 @@ They will be replaced with solid black rectangles. This can be useful for notifications. -The same caveats and instructions apply as for the `block-out-from` window rule. -Please read the `block-out-from` section in the [window rules](./Configuration:-Window-Rules.md) wiki page for more details. +The same caveats and instructions apply as for the [`block-out-from` window rule](./Configuration:-Window-Rules.md#block-out-from), so check the documentation there. ![Screenshot showing a notification visible normally, but blocked out on OBS.](./img/layer-block-out-from-screencast.png) @@ -115,7 +114,7 @@ layer-rule { Override the shadow options for the surface. -These rules have the same options as the normal shadow config in the [layout](./Configuration:-Layout.md) section, so check the documentation there. +These rules have the same options as the normal [`shadow` config in the layout section](./Configuration:-Layout.md#shadow), so check the documentation there. Unlike window shadows, layer surface shadows always need to be enabled with a layer rule. That is, enabling shadows in the layout config section won't automatically enable them for layer surfaces. diff --git a/wiki/Configuration:-Layout.md b/wiki/Configuration:-Layout.md index 45206673..7dbbced8 100644 --- a/wiki/Configuration:-Layout.md +++ b/wiki/Configuration:-Layout.md @@ -208,7 +208,7 @@ layout { > `default-column-width {}` causes niri to send a (0, H) size in the initial configure request. > > This is a bit [unclearly defined](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/155) in the Wayland protocol, so some clients may misinterpret it. -> Either way, `default-column-width {}` is most useful for specific windows, in form of a [window rule](https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules) with the same syntax. +> Either way, `default-column-width {}` is most useful for specific windows, in form of a [window rule](./Configuration:-Window-Rules.md#default-column-width) with the same syntax. ### `preset-window-heights` @@ -249,10 +249,10 @@ The difference is that the focus ring is drawn only around the active window, wh > That is, they will show up through semitransparent windows. > This is because windows using client-side decorations can have an arbitrary shape. > -> If you don't like that, you should uncomment the `prefer-no-csd` setting at the [top level](./Configuration:-Miscellaneous.md) of the config. +> If you don't like that, you should uncomment the [`prefer-no-csd` setting](./Configuration:-Miscellaneous.md#prefer-no-csd) at the top level of the config. > Niri will draw focus rings and borders *around* windows that agree to omit their client-side decorations. > -> Alternatively, you can override this behavior with the `draw-border-with-background` [window rule](https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules). +> Alternatively, you can override this behavior with the [`draw-border-with-background` window rule](./Configuration:-Window-Rules.md#draw-border-with-background). Focus ring and border have the following options. @@ -312,7 +312,7 @@ Similarly to colors, you can set `active-gradient` and `inactive-gradient`, whic Gradients are rendered the same as CSS [`linear-gradient(angle, from, to)`](https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient). The angle works the same as in `linear-gradient`, and is optional, defaulting to `180` (top-to-bottom gradient). -You can use any CSS linear-gradient tool on the web to set these up, like [this one](https://www.css-gradient.com/). +You can use any CSS linear-gradient tool on the web to set these up, like [css-gradient.com](https://www.css-gradient.com/). ```kdl layout { @@ -388,7 +388,7 @@ These will also remove client-side shadows if the window draws any. `inactive-color` lets you override the shadow color for inactive windows; by default, a more transparent `color` is used. -Shadow drawing will follow the window corner radius set with the `geometry-corner-radius` [window rule](https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules). +Shadow drawing will follow the window corner radius set with the [`geometry-corner-radius` window rule](./Configuration:-Window-Rules.md#geometry-corner-radius). > [!NOTE] > Currently, shadow drawing only supports matching radius for all corners. If you set `geometry-corner-radius` to four values instead of one, the first (top-left) corner radius will be used for shadows. diff --git a/wiki/Configuration:-Miscellaneous.md b/wiki/Configuration:-Miscellaneous.md index a478c472..8049dec2 100644 --- a/wiki/Configuration:-Miscellaneous.md +++ b/wiki/Configuration:-Miscellaneous.md @@ -40,7 +40,7 @@ Add lines like this to spawn processes at niri startup. `spawn-at-startup` accepts a path to the program binary as the first argument, followed by arguments to the program. -This option works the same way as the `spawn` key binding action, so please read about all its subtleties on the [key bindings](./Configuration:-Key-Bindings.md) page. +This option works the same way as the [`spawn` key binding action](./Configuration:-Key-Bindings.md#spawn), so please read about all its subtleties there. ```kdl spawn-at-startup "waybar" @@ -170,5 +170,4 @@ hotkey-overlay { } ``` -You can customize which binds the hotkey overlay shows using the `hotkey-overlay-title` property. -Check the [key bindings](./Configuration:-Key-Bindings.md) wiki page for details. +You can customize which binds the hotkey overlay shows using the [`hotkey-overlay-title` property](./Configuration:-Key-Bindings.md#custom-hotkey-overlay-titles). diff --git a/wiki/Configuration:-Outputs.md b/wiki/Configuration:-Outputs.md index 59bf9182..22e1c639 100644 --- a/wiki/Configuration:-Outputs.md +++ b/wiki/Configuration:-Outputs.md @@ -143,7 +143,7 @@ You can check whether an output supports VRR in `niri msg outputs`. > [!NOTE] > Some drivers have various issues with VRR. > -> If the cursor moves at a low framerate with VRR, try setting the `disable-cursor-plane` [debug flag](./Configuration:-Debug-Options.md) and reconnecting the monitor. +> If the cursor moves at a low framerate with VRR, try setting the [`disable-cursor-plane` debug flag](./Configuration:-Debug-Options.md#disable-cursor-plane) and reconnecting the monitor. > > If a monitor is not detected as VRR-capable when it should, sometimes unplugging a different monitor fixes it. > diff --git a/wiki/Configuration:-Overview.md b/wiki/Configuration:-Overview.md index 55819593..3966a650 100644 --- a/wiki/Configuration:-Overview.md +++ b/wiki/Configuration:-Overview.md @@ -131,7 +131,7 @@ output "eDP-1" { ### Defaults Omitting most of the sections of the config file will leave you with the default values for that section. -A notable exception is `binds {}`: they do not get filled with defaults, so make sure you do not erase this section. +A notable exception is [`binds {}`](./Configuration:-Key-Bindings.md): they do not get filled with defaults, so make sure you do not erase this section. ### Breaking Change Policy diff --git a/wiki/Configuration:-Switch-Events.md b/wiki/Configuration:-Switch-Events.md index 9c60cc81..c3d2907b 100644 --- a/wiki/Configuration:-Switch-Events.md +++ b/wiki/Configuration:-Switch-Events.md @@ -16,7 +16,7 @@ switch-events { ``` The syntax is similar to key bindings. -Currently, only the `spawn` action are supported. +Currently, only the [`spawn` action](./Configuration:-Key-Bindings.md#spawn) are supported. > [!NOTE] > In contrast to key bindings, switch event bindings are *always* executed, even when the session is locked. diff --git a/wiki/Configuration:-Window-Rules.md b/wiki/Configuration:-Window-Rules.md index 32ee253f..e07753fa 100644 --- a/wiki/Configuration:-Window-Rules.md +++ b/wiki/Configuration:-Window-Rules.md @@ -481,7 +481,7 @@ You can block out windows from xdg-desktop-portal screencasts. They will be replaced with solid black rectangles. This can be useful for password managers or messenger windows, etc. -For layer-shell notification pop-ups and the like, you can use a `block-out-from` [layer rule](./Configuration:-Layer-Rules.md). +For layer-shell notification pop-ups and the like, you can use a [`block-out-from` layer rule](./Configuration:-Layer-Rules.md#block-out-from). ![Screenshot showing a window visible normally, but blocked out on OBS.](./img/block-out-from-screencast.png) @@ -547,7 +547,7 @@ Opacity is applied to every surface of the window individually, so subsurfaces a Also, focus ring and border with background will show through semitransparent windows (see `prefer-no-csd` and the `draw-border-with-background` window rule below). -Opacity can be toggled on or off for a window using the [`toggle-window-rule-opacity`](./Configuration:-Key-Bindings.md) action. +Opacity can be toggled on or off for a window using the [`toggle-window-rule-opacity`](./Configuration:-Key-Bindings.md#toggle-window-rule-opacity) action. ```kdl // Make inactive windows semitransparent. @@ -657,7 +657,7 @@ window-rule { Set a scroll factor for all scroll events sent to a window. -This will be multiplied with the scroll factor set for your input device in the [input](/wiki/Configuration:-Input.md) section. +This will be multiplied with the scroll factor set for your input device in the [input section](/wiki/Configuration:-Input.md#pointing-devices). ```kdl // Make scrolling in Firefox a bit slower. @@ -693,7 +693,7 @@ window-rule { Override the focus ring and border options for the window. -These rules have the same options as the normal focus ring and border config in the [layout](./Configuration:-Layout.md) section, so check the documentation there. +These rules have the same options as the normal [`focus-ring` and `border` config in the layout section](./Configuration:-Layout.md#focus-ring-and-border), so check the documentation there. However, in addition to `off` to disable the border/focus ring, this window rule has an `on` flag that enables the border/focus ring for the window even if it was otherwise disabled. The `on` flag has precedence over the `off` flag, in case both are set. @@ -720,7 +720,7 @@ window-rule { Override the shadow options for the window. -This rule has the same options as the normal shadow config in the [layout](./Configuration:-Layout.md) section, so check the documentation there. +This rule has the same options as the normal [`shadow` config in the layout section](./Configuration:-Layout.md#shadow), so check the documentation there. However, in addition to `on` to enable the shadow, this window rule has an `off` flag that disables the shadow for the window even if it was otherwise enabled. The `on` flag has precedence over the `off` flag, in case both are set. @@ -742,7 +742,7 @@ window-rule { Override the tab indicator options for the window. -Options in this rule match the same options as the normal tab indicator config in the [layout](./Configuration:-Layout.md) section, so check the documentation there. +Options in this rule match the same options as the normal [`tab-indicator` config in the layout section](./Configuration:-Layout.md#tab-indicator), so check the documentation there. ```kdl // Make KeePassXC tab have a dark red inactive color. @@ -762,7 +762,7 @@ window-rule { Set the corner radius of the window. On its own, this setting will only affect the border and the focus ring—they will round their corners to match the geometry corner radius. -If you'd like to force-round the corners of the window itself, set `clip-to-geometry true` in addition to this setting. +If you'd like to force-round the corners of the window itself, set [`clip-to-geometry true`](#clip-to-geometry) in addition to this setting. ```kdl window-rule { @@ -803,7 +803,7 @@ window-rule { } ``` -Enable border, set `geometry-corner-radius` and `clip-to-geometry`, and you've got a classic setup: +Enable border, set [`geometry-corner-radius`](#geometry-corner-radius) and `clip-to-geometry`, and you've got a classic setup: ![](./img/border-radius-clip.png) @@ -834,7 +834,7 @@ Informs the window that it is tiled. Usually, windows will react by becoming rectangular and hiding their client-side shadows. Windows that snap their size to a grid (e.g. terminals like [foot](https://codeberg.org/dnkl/foot)) will usually disable this snapping when they are tiled. -By default, niri will set the tiled state to `true` together with [`prefer-no-csd`](./Configuration:-Miscellaneous.md) in order to improve behavior for apps that don't support server-side decorations. +By default, niri will set the tiled state to `true` together with [`prefer-no-csd`](./Configuration:-Miscellaneous.md#prefer-no-csd) in order to improve behavior for apps that don't support server-side decorations. You can use this window rule to override this, for example to get rectangular windows with CSD. ```kdl diff --git a/wiki/Design-Principles.md b/wiki/Design-Principles.md deleted file mode 100644 index 70450562..00000000 --- a/wiki/Design-Principles.md +++ /dev/null @@ -1,30 +0,0 @@ -These are some of the general principles for the design of niri's window layout. -They can be sidestepped in specific circumstances if there's a good reason. - -1. Opening a new window should not affect the sizes of any existing windows. -1. The focused window should not move around on its own. - - In particular: windows opening, closing, and resizing to the left of the focused window should not cause it to visually move. -1. Actions should apply immediately. - - Things like resizing or consuming into column take effect immediately, even if the window needs time to catch up. - - This is important both for compositor responsiveness and predictability, and for keeping the code sane and free of edge cases and unnecessary asynchrony. -1. If a window or popup is larger than the screen, it should be aligned in the top left corner. - - The top left area of a window is more likely to contain something important, so it should always be visible. -1. Setting window width or height to a fixed pixel size (e.g. `set-column-width 1280` or `default-column-width { fixed 1280; }`) will set the size of the window itself, however setting to a proportional size (e.g. `set-column-width 50%`) will set the size of the tile, including the border added by niri. - - With proportions, the user is looking to tile multiple windows on the screen, so they should include borders. - - With fixed sizes, the user wants to test a specific client size or take a specifically sized screenshot, so they should affect the window directly. - - After the size is set, it is always converted to a value that includes the borders, to make the code sane. That is, `set-column-width 1000` followed by changing the niri border width will resize the window accordingly. - -And here are some more principles I try to follow throughout niri. - -1. When disabled, eye-candy features should not affect the performance. - - Things like animations and custom shaders do not run and are not present in the render tree when disabled. Extra offscreen rendering is avoided. - - Animations specifically are still "started" even when disabled, but with a duration of 0 (this way, they end as soon as the time is advanced). This does not impact performance, but helps avoid a lot of edge cases in the code. -1. Eye-candy features should not cause unreasonable excessive rendering. - - For example, clip-to-geometry will prevent direct scanout in many cases (since the window surface is not completely visible). But in the cases where the surface or the subsurface *is* completely visible (fully within the clipped region), it will still allow for direct scanout. - - For example, animations *can* cause damage and even draw to an offscreen every frame, because they are expected to be short (and can be disabled). However, something like the rounded corners shader should not offscreen or cause excessive damage every frame, because it is long-running and constantly active. -1. Be mindful of invisible state. - - This is niri state that is not immediately apparent from looking at the screen. This is not bad per se, but you should carefully consider how to reduce the surprise factor. - - - For example, when a monitor disconnects, all its workspaces move to another connected monitor. In order to be able to restore these workspaces when the first monitor connects again, these workspaces keep the knowledge of which was their *original monitor*—this is an example of invisible state, since you can't tell it in any way by looking at the screen. This can have surprising consequences: imagine disconnecting a monitor at home, going to work, completely rearranging the windows there, then coming back home, and suddenly some random workspaces end up on your home monitor. In order to reduce this surprise factor, whenever a new window appears on a workspace, that workspace resets its *original monitor* to its current monitor. This way, the workspaces you actively worked on remain where they were. - - For example, niri preserves the view position whenever a window appears, or whenever a window goes full-screen, to restore it afterward. This way, dealing with temporary things like dialogs opening and closing, or toggling full-screen, becomes less annoying, since it doesn't mess up the view position. This is also invisible state, as you cannot tell by looking at the screen where closing a window will restore the view position. If taken to the extreme (previous view position saved forever for every open window), this can be surprising, as closing long-running windows would result in the view shifting around pretty much randomly. To reduce this surprise factor, niri remembers only one last view position per workspace, and forgets this stored view position upon window focus change. diff --git a/wiki/Developing-niri.md b/wiki/Developing-niri.md deleted file mode 100644 index 96a9344a..00000000 --- a/wiki/Developing-niri.md +++ /dev/null @@ -1,76 +0,0 @@ -## Running a Local Build - -The main way of testing niri during development is running it as a nested window. The second step is usually switching to a different TTY and running niri there. - -Once a feature or fix is reasonably complete, you generally want to run a local build as your main compositor for proper testing. The easiest way to do that is to install niri normally (from a distro package for example), then overwrite the binary with `sudo cp ./target/release/niri /usr/bin/niri`. Do make sure that you know how to revert to a working version in case everything breaks though. - -If you use an RPM-based distro, you can generate an RPM package for a local build with `cargo generate-rpm`. - -## Logging Levels - -Niri uses [`tracing`](https://lib.rs/crates/tracing) for logging. This is how logging levels are used: - -- `error!`: programming errors and bugs that are recoverable. Things you'd normally use `unwrap()` for. However, when a Wayland compositor crashes, it brings down the entire session, so it's better to recover and log an `error!` whenever reasonable. If you see an `ERROR` in the niri log, that always indicates a *bug*. -- `warn!`: something bad but still *possible* happened. Informing the user that they did something wrong, or that their hardware did something weird, falls into this category. For example, config parsing errors should be indicated with a `warn!`. -- `info!`: the most important messages related to normal operation. Running niri with `RUST_LOG=niri=info` should not make the user want to disable logging altogether. -- `debug!`: less important messages related to normal operation. Running niri with `debug!` messages hidden should not negatively impact the UX. -- `trace!`: everything that can be useful for debugging but is otherwise too spammy or performance intensive. `trace!` messages are *compiled out* of release builds. - -## Tests - -We have some unit tests, most prominently for the layout code and for config parsing. - -When adding new operations to the layout, add them to the `Op` enum at the bottom of `src/layout/mod.rs` (this will automatically include it in the randomized tests), and if applicable to the `every_op` arrays below. - -When adding new config options, include them in the config parsing test. - -### Running Tests - -Make sure to run `cargo test --all` to run tests from sub-crates too. - -Some tests are a bit too slow to run normally, like the randomized tests of the layout code, so they are normally skipped. Set the `RUN_SLOW_TESTS` variable to run them: - -``` -env RUN_SLOW_TESTS=1 cargo test --all -``` - -It also usually helps to run the randomized tests for a longer period, so that they can explore more inputs. You can control this with environment variables. This is how I usually run tests before pushing: - -``` -env RUN_SLOW_TESTS=1 PROPTEST_CASES=200000 PROPTEST_MAX_GLOBAL_REJECTS=200000 RUST_BACKTRACE=1 cargo test --release --all -``` - -### Visual Tests - -The `niri-visual-tests` sub-crate is a GTK application that runs hard-coded test cases so that you can visually check that they look right. It uses mock windows with the real layout and rendering code. It is especially helpful when working on animations. - -## Profiling - -We have integration with the [Tracy](https://github.com/wolfpld/tracy) profiler which you can enable by building niri with a feature flag: - -``` -cargo build --release --features=profile-with-tracy-ondemand -``` - -Then you can open Tracy (you will need the latest stable release) and attach to a running niri instance to collect profiling data. Profiling data is collected "on demand"—that is, only when Tracy is connected. You can run a niri build like this as your main compositor if you'd like. - -> [!NOTE] -> If you need to profile niri startup or the niri CLI, you can opt for "always on" profiling instead, using this feature flag: -> -> ``` -> cargo build --release --features=profile-with-tracy -> ``` -> -> When compiled this way, niri will **always** collect profiling data, so you can't run a build like this as your main compositor. - -To make a niri function show up in Tracy, instrument it like this: - -```rust -pub fn some_function() { - let _span = tracy_client::span!("some_function"); - - // Code of the function. -} -``` - -You can also enable Rust memory allocation profiling with `--features=profile-with-tracy-allocations`. diff --git a/wiki/Development:-Animation-Timing.md b/wiki/Development:-Animation-Timing.md new file mode 100644 index 00000000..e9d5f373 --- /dev/null +++ b/wiki/Development:-Animation-Timing.md @@ -0,0 +1,46 @@ +> *Time, Dr. Freeman? Is it really that... time again?* + +A compositor deals with one or more monitors on mostly fixed refresh cycles. +For example, a 170 Hz monitor can draw a frame every ~5.88 ms. + +Most of the time, the compositor doesn't actually redraw the monitor: when nothing changes on screen (e.g. you're reading a document and aren't moving your cursor), it would be wasteful to wake up the GPU to composite the same image. +During an animation however, screen contents do change every frame. +Niri will generally start drawing the next frame as soon as the previous one shows up on screen. + +Since the monitor refresh cycle is fixed in most cases (even with VRR, there's a maximum refresh rate), the compositor can predict when the next frame will show up on the monitor, and render ongoing animations for that exact moment in time. +This way, all animation frames are perfectly timed with no jitter, regardless of when exactly the rendering code had a chance to run. +For example, even if the compositor has to process new window events, delaying the rendering by a few ms, the animation timing will remain exactly aligned to the monitor refresh cycle. + +There are hence several properties that a compositor wants from its timing system. + +1. It should be possible to get the state of the animations at a specific time in the near future, for rendering a frame exactly timed to when the monitor will show it. + - This time override ability should be usable in tests to advance the time in a fully controlled fashion. +1. Animations in response to user actions should begin at the moment when the action happens. + For example, pressing a workspace switch key should start the animation at the instant when the user pressed the key (rather than, say, slightly in the future where we predicted the next monitor frame, which we had already rendered by now). +1. During the processing of a single action, querying the current time should return the exact same value. + Even if the processing finishes a few microseconds after it started, querying the time in the end should return the same thing. + This generally makes writing code much more sane; otherwise you'd need to for example avoid reading the position of some element twice in a row, since it could have moved by one pixel in-between, screwing with the logic. + Also, fetching the current system time [can be quite expensive](https://mastodon.online/@YaLTeR/109934977035721850) in terms of overhead. +1. It should be reasonably easy to implement an animation slow-down preference, so all animations can be slowed down or sped up by the same factor. + +The solution in niri is a `LazyClock`, a clock that remembers one timestamp. +Initially, the timestamp is empty, so when you ask `LazyClock` for the current time, it will fetch and return the system time, and also remember it. +Subsequently, it will keep returning the same timestamp that it had remembered. + +You can also clear the timestamp, then `LazyClock` will fetch the system time anew when it's needed. +In niri, the timestamp is cleared at the end of every event loop iteration, right before going to sleep waiting for new events. +This way, anything that happens next (like a user key press) will fetch and use the most up-to-date timestamp as soon as one is needed, but then the processing code will keep getting the exact same timestamp, since `LazyClock` stores it. + +You can also just manually set the timestamp to a specific value. +This is how we render a frame for the predicted time of when the monitor will show it. +Also, this is used by tests: they simply always set the timestamp and never use the system time. + +Finally, there's an `AdjustableClock` wrapper on top that provides the ability to control the slow-down rate by modifying the timestamps returned by the clock. + +An important detail is that with rate changes, timestamps from the `AdjustableClock` will drift away and become unrelated to the system time. +However, our target timestamp (for rendering) comes from the system time, so the override works directly on the underlying `LazyClock`. +That is, overriding the timestamp and then querying the `AdjustableClock` will return a *different* timestamp that is correct and consistent with the adjustments made by `AdjustableClock`. +This is reflected in the API by naming the function `Clock::set_unadjusted()` (and there's also `Clock::now_unadjusted()` to get the raw timestamp). + +The clock is shared among all animations in niri through passing around and storing a reference-counted pointer. +This way, overriding the time automatically applies to everything, whereas in tests we can use a separate clock per test so that they don't interfere with each other. diff --git a/wiki/Development:-Design-Principles.md b/wiki/Development:-Design-Principles.md new file mode 100644 index 00000000..70450562 --- /dev/null +++ b/wiki/Development:-Design-Principles.md @@ -0,0 +1,30 @@ +These are some of the general principles for the design of niri's window layout. +They can be sidestepped in specific circumstances if there's a good reason. + +1. Opening a new window should not affect the sizes of any existing windows. +1. The focused window should not move around on its own. + - In particular: windows opening, closing, and resizing to the left of the focused window should not cause it to visually move. +1. Actions should apply immediately. + - Things like resizing or consuming into column take effect immediately, even if the window needs time to catch up. + - This is important both for compositor responsiveness and predictability, and for keeping the code sane and free of edge cases and unnecessary asynchrony. +1. If a window or popup is larger than the screen, it should be aligned in the top left corner. + - The top left area of a window is more likely to contain something important, so it should always be visible. +1. Setting window width or height to a fixed pixel size (e.g. `set-column-width 1280` or `default-column-width { fixed 1280; }`) will set the size of the window itself, however setting to a proportional size (e.g. `set-column-width 50%`) will set the size of the tile, including the border added by niri. + - With proportions, the user is looking to tile multiple windows on the screen, so they should include borders. + - With fixed sizes, the user wants to test a specific client size or take a specifically sized screenshot, so they should affect the window directly. + - After the size is set, it is always converted to a value that includes the borders, to make the code sane. That is, `set-column-width 1000` followed by changing the niri border width will resize the window accordingly. + +And here are some more principles I try to follow throughout niri. + +1. When disabled, eye-candy features should not affect the performance. + - Things like animations and custom shaders do not run and are not present in the render tree when disabled. Extra offscreen rendering is avoided. + - Animations specifically are still "started" even when disabled, but with a duration of 0 (this way, they end as soon as the time is advanced). This does not impact performance, but helps avoid a lot of edge cases in the code. +1. Eye-candy features should not cause unreasonable excessive rendering. + - For example, clip-to-geometry will prevent direct scanout in many cases (since the window surface is not completely visible). But in the cases where the surface or the subsurface *is* completely visible (fully within the clipped region), it will still allow for direct scanout. + - For example, animations *can* cause damage and even draw to an offscreen every frame, because they are expected to be short (and can be disabled). However, something like the rounded corners shader should not offscreen or cause excessive damage every frame, because it is long-running and constantly active. +1. Be mindful of invisible state. + + This is niri state that is not immediately apparent from looking at the screen. This is not bad per se, but you should carefully consider how to reduce the surprise factor. + + - For example, when a monitor disconnects, all its workspaces move to another connected monitor. In order to be able to restore these workspaces when the first monitor connects again, these workspaces keep the knowledge of which was their *original monitor*—this is an example of invisible state, since you can't tell it in any way by looking at the screen. This can have surprising consequences: imagine disconnecting a monitor at home, going to work, completely rearranging the windows there, then coming back home, and suddenly some random workspaces end up on your home monitor. In order to reduce this surprise factor, whenever a new window appears on a workspace, that workspace resets its *original monitor* to its current monitor. This way, the workspaces you actively worked on remain where they were. + - For example, niri preserves the view position whenever a window appears, or whenever a window goes full-screen, to restore it afterward. This way, dealing with temporary things like dialogs opening and closing, or toggling full-screen, becomes less annoying, since it doesn't mess up the view position. This is also invisible state, as you cannot tell by looking at the screen where closing a window will restore the view position. If taken to the extreme (previous view position saved forever for every open window), this can be surprising, as closing long-running windows would result in the view shifting around pretty much randomly. To reduce this surprise factor, niri remembers only one last view position per workspace, and forgets this stored view position upon window focus change. diff --git a/wiki/Development:-Developing-niri.md b/wiki/Development:-Developing-niri.md new file mode 100644 index 00000000..96a9344a --- /dev/null +++ b/wiki/Development:-Developing-niri.md @@ -0,0 +1,76 @@ +## Running a Local Build + +The main way of testing niri during development is running it as a nested window. The second step is usually switching to a different TTY and running niri there. + +Once a feature or fix is reasonably complete, you generally want to run a local build as your main compositor for proper testing. The easiest way to do that is to install niri normally (from a distro package for example), then overwrite the binary with `sudo cp ./target/release/niri /usr/bin/niri`. Do make sure that you know how to revert to a working version in case everything breaks though. + +If you use an RPM-based distro, you can generate an RPM package for a local build with `cargo generate-rpm`. + +## Logging Levels + +Niri uses [`tracing`](https://lib.rs/crates/tracing) for logging. This is how logging levels are used: + +- `error!`: programming errors and bugs that are recoverable. Things you'd normally use `unwrap()` for. However, when a Wayland compositor crashes, it brings down the entire session, so it's better to recover and log an `error!` whenever reasonable. If you see an `ERROR` in the niri log, that always indicates a *bug*. +- `warn!`: something bad but still *possible* happened. Informing the user that they did something wrong, or that their hardware did something weird, falls into this category. For example, config parsing errors should be indicated with a `warn!`. +- `info!`: the most important messages related to normal operation. Running niri with `RUST_LOG=niri=info` should not make the user want to disable logging altogether. +- `debug!`: less important messages related to normal operation. Running niri with `debug!` messages hidden should not negatively impact the UX. +- `trace!`: everything that can be useful for debugging but is otherwise too spammy or performance intensive. `trace!` messages are *compiled out* of release builds. + +## Tests + +We have some unit tests, most prominently for the layout code and for config parsing. + +When adding new operations to the layout, add them to the `Op` enum at the bottom of `src/layout/mod.rs` (this will automatically include it in the randomized tests), and if applicable to the `every_op` arrays below. + +When adding new config options, include them in the config parsing test. + +### Running Tests + +Make sure to run `cargo test --all` to run tests from sub-crates too. + +Some tests are a bit too slow to run normally, like the randomized tests of the layout code, so they are normally skipped. Set the `RUN_SLOW_TESTS` variable to run them: + +``` +env RUN_SLOW_TESTS=1 cargo test --all +``` + +It also usually helps to run the randomized tests for a longer period, so that they can explore more inputs. You can control this with environment variables. This is how I usually run tests before pushing: + +``` +env RUN_SLOW_TESTS=1 PROPTEST_CASES=200000 PROPTEST_MAX_GLOBAL_REJECTS=200000 RUST_BACKTRACE=1 cargo test --release --all +``` + +### Visual Tests + +The `niri-visual-tests` sub-crate is a GTK application that runs hard-coded test cases so that you can visually check that they look right. It uses mock windows with the real layout and rendering code. It is especially helpful when working on animations. + +## Profiling + +We have integration with the [Tracy](https://github.com/wolfpld/tracy) profiler which you can enable by building niri with a feature flag: + +``` +cargo build --release --features=profile-with-tracy-ondemand +``` + +Then you can open Tracy (you will need the latest stable release) and attach to a running niri instance to collect profiling data. Profiling data is collected "on demand"—that is, only when Tracy is connected. You can run a niri build like this as your main compositor if you'd like. + +> [!NOTE] +> If you need to profile niri startup or the niri CLI, you can opt for "always on" profiling instead, using this feature flag: +> +> ``` +> cargo build --release --features=profile-with-tracy +> ``` +> +> When compiled this way, niri will **always** collect profiling data, so you can't run a build like this as your main compositor. + +To make a niri function show up in Tracy, instrument it like this: + +```rust +pub fn some_function() { + let _span = tracy_client::span!("some_function"); + + // Code of the function. +} +``` + +You can also enable Rust memory allocation profiling with `--features=profile-with-tracy-allocations`. diff --git a/wiki/Development:-Fractional-Layout.md b/wiki/Development:-Fractional-Layout.md new file mode 100644 index 00000000..220dc856 --- /dev/null +++ b/wiki/Development:-Fractional-Layout.md @@ -0,0 +1,34 @@ +There are two main coordinate spaces in niri: physical (pixels of every individual output) and logical (shared among all outputs, takes into account the scale of every output). +Wayland clients mostly work in the logical space, and it's the most convenient space to do all the layout in, since it bakes in the output scaling factor. + +However, many things need to be sized or positioned at integer physical coordinates. +For example, Wayland toplevel buffers are assumed to be placed at an integer physical pixel on an output (and `WaylandSurfaceRenderElement` will do that for you). +Borders and focus rings should also have a width equal to an integer number of physical pixels to stay crisp (not to mention that `SolidColorRenderElement` does not anti-alias lines at fractional pixel positions). + +Integer physical coordinates do not necessarily correspond to integer logical coordinates though. +Even with an integer scale = 2, a physical pixel at (1, 1) will be at the logical position of (0.5, 0.5). +This problem becomes much worse with fractional scale factors where most integer logical coordinates will fall on fractional physical coordinates. + +Thus, niri uses fractional logical coordinates for most of its layout. +However, one needs to be very careful to keep things aligned to the physical grid to avoid artifacts like: + +* Border width alternating 1 px thicker/thinner +* Border showing 1 px off from the window at certain positions +* 1 px gaps around rounded corners +* Slightly blurry window contents during resizes +* And so on... + +The way it's handled in niri is: + +1. All relevant sizes on a workspace are rounded to an integer physical coordinate according to the current output scale. Things like struts, gaps, border widths, working area location. + + It's important to understand that they remain fractional numbers in the logical space, but these numbers correspond to an integer number of pixels in the physical space. + The rounding looks something like: `(logical_size * scale).round() / scale`. + Whenever a workspace moves to an output with a different scale (or the output scale changes), all sizes are re-rounded from their original configured values to align with the new physical space. +2. The view offset and individual column/tile render offsets are *not* rounded to physical pixels, but: +3. `tiles_with_render_positions()` rounds tile positions to physical pixels as it returns them, +4. Custom shaders like opening, closing and resizing windows, are also careful to keep positions and sizes rounded to the physical pixels. + +The idea is that every tile can assume that it is rendered at an integer physical coordinate, therefore when shifting the position by, say, border width (also rounded to integer physical coordinates), the new position will stay rounded to integer physical coordinates. +The same logic works for the rest of the layout thanks to gaps, struts and working area being similarly rounded. +This way, the entire layout is always aligned, as long as it is positioned at an integer physical coordinate (which rounding the tile positions effectively achieves). diff --git a/wiki/Development:-Redraw-Loop.md b/wiki/Development:-Redraw-Loop.md new file mode 100644 index 00000000..7f3d1873 --- /dev/null +++ b/wiki/Development:-Redraw-Loop.md @@ -0,0 +1,22 @@ +On a TTY, only one frame can be submitted to an output at a time, and the compositor must wait until the output repaints (indicated by a VBlank) to be able to submit the next frame. +In niri we keep track of this via the `RedrawState` enum that you can find in an `OutputState`. + +Here's a diagram of state transitions for the `RedrawState` state machine: + + + + RedrawState state transition diagram + + +`Idle` is the default state, when the output does not need to be repainted. +Any operation that may cause the screen to update calls `queue_redraw()`, which moves the output to a `Queued` state. +Then, at the end of an event loop dispatch, niri calls `redraw()` for every `Queued` output. + +If the redraw causes damage (i.e. something on the output changed), we move into the `WaitingForVBlank` state, since we cannot redraw until we receive a VBlank event. +However, if there's no damage, we do not return to `Idle` right away. +Instead, we set a timer to fire roughly at when the next VBlank would occur, and transition to a `WaitingForEstimatedVBlank` state. + +This is necessary in order to throttle frame callbacks sent to applications to at most once per output refresh cycle. +Without this throttling, applications can start continuously redrawing without damage (for instance, if the application window is partially off-screen, and it is only the off-screen part that changes), and eating a lot of CPU in the process. + +Then, either the estimated VBlank timer completes, and we go back to `Idle`, or maybe we call `queue_redraw()` once more and try to redraw again. diff --git a/wiki/Example-systemd-Setup.md b/wiki/Example-systemd-Setup.md index a3a96de0..07780ae4 100644 --- a/wiki/Example-systemd-Setup.md +++ b/wiki/Example-systemd-Setup.md @@ -2,7 +2,7 @@ When starting niri from a display manager like GDM, or otherwise through the `ni This provides the necessary systemd integration to run programs like `mako` and services like `xdg-desktop-portal` bound to the graphical session. Here's an example on how you might set up [`mako`](https://github.com/emersion/mako), [`waybar`](https://github.com/Alexays/Waybar), [`swaybg`](https://github.com/swaywm/swaybg) and [`swayidle`](https://github.com/swaywm/swayidle) to run as systemd services with niri. -In contrast to the `spawn-at-startup` config option, this lets you easily monitor their status and output, and restart or reload them. +In contrast to [`spawn-at-startup`](./Configuration:-Miscellaneous.md#spawn-at-startup), this lets you easily monitor their status and output, and restart or reload them. 1. Install them, i.e. `sudo dnf install mako waybar swaybg swayidle` 2. Create a `niri.service.wants` folder: `mkdir -p ~/.config/systemd/user/niri.service.wants` diff --git a/wiki/FAQ.md b/wiki/FAQ.md index 85a9d6d6..62d2becd 100644 --- a/wiki/FAQ.md +++ b/wiki/FAQ.md @@ -1,6 +1,6 @@ ### How to disable client-side decorations/make windows rectangular? -Uncomment the `prefer-no-csd` setting at the [top level](./Configuration:-Miscellaneous.md) of the config, and then restart your apps. +Uncomment the [`prefer-no-csd` setting](./Configuration:-Miscellaneous.md#prefer-no-csd) at the top level of the config, and then restart your apps. Then niri will ask windows to omit client-side decorations, and also inform them that they are being tiled (which makes some windows rectangular, even if they cannot omit the decorations). Note that currently this will prevent edge window resize handles from showing up. @@ -8,14 +8,14 @@ You can still resize windows by holding Mod and the right mouse butto ### Why is the border/focus ring showing up through semitransparent windows? -Uncomment the `prefer-no-csd` setting at the [top level](./Configuration:-Miscellaneous.md) of the config, and then restart your apps. +Uncomment the [`prefer-no-csd` setting](./Configuration:-Miscellaneous.md#prefer-no-csd) at the top level of the config, and then restart your apps. Niri will draw focus rings and borders *around* windows that agree to omit their client-side decorations. By default, focus ring and border are rendered as a solid background rectangle behind windows. That is, they will show up through semitransparent windows. This is because windows using client-side decorations can have an arbitrary shape. -You can also override this behavior with the `draw-border-with-background` [window rule](./Configuration:-Window-Rules.md). +You can also override this behavior with the [`draw-border-with-background` window rule](./Configuration:-Window-Rules.md#draw-border-with-background). ### How to enable rounded corners for all windows? @@ -28,7 +28,7 @@ window-rule { } ``` -For more information, check [this wiki section](https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules#geometry-corner-radius). +For more information, check the [`geometry-corner-radius` window rule](./Configuration:-Window-Rules.md#geometry-corner-radius). ### How to hide the "Important Hotkeys" pop-up at the start? diff --git a/wiki/Fractional-Layout.md b/wiki/Fractional-Layout.md deleted file mode 100644 index 220dc856..00000000 --- a/wiki/Fractional-Layout.md +++ /dev/null @@ -1,34 +0,0 @@ -There are two main coordinate spaces in niri: physical (pixels of every individual output) and logical (shared among all outputs, takes into account the scale of every output). -Wayland clients mostly work in the logical space, and it's the most convenient space to do all the layout in, since it bakes in the output scaling factor. - -However, many things need to be sized or positioned at integer physical coordinates. -For example, Wayland toplevel buffers are assumed to be placed at an integer physical pixel on an output (and `WaylandSurfaceRenderElement` will do that for you). -Borders and focus rings should also have a width equal to an integer number of physical pixels to stay crisp (not to mention that `SolidColorRenderElement` does not anti-alias lines at fractional pixel positions). - -Integer physical coordinates do not necessarily correspond to integer logical coordinates though. -Even with an integer scale = 2, a physical pixel at (1, 1) will be at the logical position of (0.5, 0.5). -This problem becomes much worse with fractional scale factors where most integer logical coordinates will fall on fractional physical coordinates. - -Thus, niri uses fractional logical coordinates for most of its layout. -However, one needs to be very careful to keep things aligned to the physical grid to avoid artifacts like: - -* Border width alternating 1 px thicker/thinner -* Border showing 1 px off from the window at certain positions -* 1 px gaps around rounded corners -* Slightly blurry window contents during resizes -* And so on... - -The way it's handled in niri is: - -1. All relevant sizes on a workspace are rounded to an integer physical coordinate according to the current output scale. Things like struts, gaps, border widths, working area location. - - It's important to understand that they remain fractional numbers in the logical space, but these numbers correspond to an integer number of pixels in the physical space. - The rounding looks something like: `(logical_size * scale).round() / scale`. - Whenever a workspace moves to an output with a different scale (or the output scale changes), all sizes are re-rounded from their original configured values to align with the new physical space. -2. The view offset and individual column/tile render offsets are *not* rounded to physical pixels, but: -3. `tiles_with_render_positions()` rounds tile positions to physical pixels as it returns them, -4. Custom shaders like opening, closing and resizing windows, are also careful to keep positions and sizes rounded to the physical pixels. - -The idea is that every tile can assume that it is rendered at an integer physical coordinate, therefore when shifting the position by, say, border width (also rounded to integer physical coordinates), the new position will stay rounded to integer physical coordinates. -The same logic works for the rest of the layout thanks to gaps, struts and working area being similarly rounded. -This way, the entire layout is always aligned, as long as it is positioned at an integer physical coordinate (which rounding the tile positions effectively achieves). diff --git a/wiki/Gestures.md b/wiki/Gestures.md index 34dd3d69..72dabde0 100644 --- a/wiki/Gestures.md +++ b/wiki/Gestures.md @@ -12,7 +12,7 @@ Also see the [gestures configuration](./Configuration:-Gestures.md) wiki page. You can move windows by holding Mod and the left mouse button. -You can customize the look of the window insertion preview in the `insert-hint` [layout config](./Configuration:-Layout.md) section. +You can customize the look of the window insertion preview in the [`insert-hint` layout config](./Configuration:-Layout.md#insert-hint). Since: 25.01 Right click while moving to toggle between floating and tiling layout to put the window into. diff --git a/wiki/Important-Software.md b/wiki/Important-Software.md index 64b5e692..dda46098 100644 --- a/wiki/Important-Software.md +++ b/wiki/Important-Software.md @@ -2,7 +2,7 @@ Since niri is not a complete desktop environment, you will very likely want to r ### Notification Daemon -Many apps need one. For example, [mako](https://github.com/emersion/mako) works well. Use [a systemd setup](./Example-systemd-Setup.md) or `spawn-at-startup`. +Many apps need one. For example, [mako](https://github.com/emersion/mako) works well. Use [a systemd setup](./Example-systemd-Setup.md) or [`spawn-at-startup`](./Configuration:-Miscellaneous.md#spawn-at-startup). ### Portals @@ -14,7 +14,7 @@ Portals **require** [running niri as a session](./Getting-Started.md), which mea * `xdg-desktop-portal-gnome`: required for screencasting support. * `gnome-keyring`: implements the Secret portal, required for certain apps to work. -Then systemd should start them on-demand automatically. These particular portals are configured in `niri-portals.conf` which [must be installed](https://github.com/YaLTeR/niri/wiki/Getting-Started#installation) in the correct location. +Then systemd should start them on-demand automatically. These particular portals are configured in `niri-portals.conf` which [must be installed](./Getting-Started.md#manual-installation) in the correct location. Since we're using `xdg-desktop-portal-gnome`, Flatpak apps will read the GNOME UI settings. For example, to enable the dark style, run: @@ -24,7 +24,7 @@ dconf write /org/gnome/desktop/interface/color-scheme '"prefer-dark"' ### Authentication Agent -Required when apps need to ask for root permissions. Something like `plasma-polkit-agent` works fine. Start it [with systemd](./Example-systemd-Setup.md) or with `spawn-at-startup`. +Required when apps need to ask for root permissions. Something like `plasma-polkit-agent` works fine. Start it [with systemd](./Example-systemd-Setup.md) or with [`spawn-at-startup`](./Configuration:-Miscellaneous.md#spawn-at-startup). Note that to start `plasma-polkit-agent` with systemd on Fedora, you'll need to override its systemd service to add the correct dependency. Run: diff --git a/wiki/Redraw-Loop.md b/wiki/Redraw-Loop.md deleted file mode 100644 index 7f3d1873..00000000 --- a/wiki/Redraw-Loop.md +++ /dev/null @@ -1,22 +0,0 @@ -On a TTY, only one frame can be submitted to an output at a time, and the compositor must wait until the output repaints (indicated by a VBlank) to be able to submit the next frame. -In niri we keep track of this via the `RedrawState` enum that you can find in an `OutputState`. - -Here's a diagram of state transitions for the `RedrawState` state machine: - - - - RedrawState state transition diagram - - -`Idle` is the default state, when the output does not need to be repainted. -Any operation that may cause the screen to update calls `queue_redraw()`, which moves the output to a `Queued` state. -Then, at the end of an event loop dispatch, niri calls `redraw()` for every `Queued` output. - -If the redraw causes damage (i.e. something on the output changed), we move into the `WaitingForVBlank` state, since we cannot redraw until we receive a VBlank event. -However, if there's no damage, we do not return to `Idle` right away. -Instead, we set a timer to fire roughly at when the next VBlank would occur, and transition to a `WaitingForEstimatedVBlank` state. - -This is necessary in order to throttle frame callbacks sent to applications to at most once per output refresh cycle. -Without this throttling, applications can start continuously redrawing without damage (for instance, if the application window is partially off-screen, and it is only the off-screen part that changes), and eating a lot of CPU in the process. - -Then, either the estimated VBlank timer completes, and we go back to `Idle`, or maybe we call `queue_redraw()` once more and try to redraw again. diff --git a/wiki/Tabs.md b/wiki/Tabs.md index 1b2c42c0..e21f014a 100644 --- a/wiki/Tabs.md +++ b/wiki/Tabs.md