DYNAMONIX

> [!NOTE] > This project is maintained with the assistance of AI tools. All changes are subject to manual review and a comprehensive test suite to ensure stability and quality. ## What? A tui/cli program for tweaking niri wm display outputs dynamically without modifying the underlying configuration. Dynamonix speaks to the compositor only through the niri socket. It does not read the niri configuration file. It does not write the niri configuration file. ## Why? Setting up new monitors/display outputs and adjusting the layout requires making changes to the `niri` configuration file even if you only need to use that display output layout for a brief period. Now if niri is configured using nix/home-manager, you'll also need to do a `home-manager switch`. Dynamonix lets you tweak the current display output layout quickly with a pretty TUI without changing your niri configuration file. All changes made by dynamonix are in-memory and ephemeral. To use an arrangement again later, save it as a profile. A profile goes in a file of dynamonix, and your niri configuration file stays as it is. ``` dynamonix 3 outputs · 3 on · 2 pending changes ╭ arrangement ───────────────────────────────╮╭ outputs ──────────────────╮ │ ││▍● DP-2 3440x1440 ×1 │ │ ┌───────────────┐ ┌────────────┐ ││ ● HDMI-A-1 3840x2160 ×1 │ │ │ DP-2 │ │ HDMI-A-1 │ ││ ● eDP-1 1920x1200 ×1 │ │ │ 3440x1440 ×1 │ │ 3840x2160 │ │╰───────────────────────────╯ │ └───────┬───────┴──────┴────────────┘ │╭ properties ───────────────╮ │ │ eDP-1 ││ mode 3440x1440 ★ 1/3 │ │ │ 1920x1200 ×1 ││ rate 143.999 Hz │ │ └───────────────────────────────┤│ scale ×1 │ ╰────────────────────────────────────────────╯╰───────────────────────────╯ ``` **The compositor forgets these changes in two conditions:** - The compositor stops. - The niri configuration file changes, and the compositor reads it again. To keep an arrangement after a restart, save it as a profile, or write it in your niri configuration file. ## Installation ### Cargo The program needs Rust 1.90 or a later version. ```sh cargo install dynamonix ``` ### Nix The repository holds a flake. The flake builds the program for `x86_64-linux` and for `aarch64-linux`. Run the program one time. This command installs nothing: ```sh nix run git+https://git.devtechnica.com/sreedev/dynamonix ``` Put the program in your profile: ```sh nix profile install git+https://git.devtechnica.com/sreedev/dynamonix ``` Add the flake to the inputs of your own flake. The `follows` line keeps one copy of nixpkgs: ```nix { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; dynamonix = { url = "git+https://git.devtechnica.com/sreedev/dynamonix"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = { nixpkgs, dynamonix, ... }: { nixosConfigurations.example = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ ( { pkgs, ... }: { nixpkgs.overlays = [ dynamonix.overlays.default ]; environment.systemPackages = [ pkgs.dynamonix ]; } ) ]; }; }; } ``` The overlay gives the name `pkgs.dynamonix`. A configuration that uses no overlay takes the package from the flake directly: ```nix environment.systemPackages = [ dynamonix.packages.x86_64-linux.default ]; ``` ## Use Start the program in a niri session: ```sh dynamonix ``` Show the outputs and stop: ```sh dynamonix list ``` Show the interface with three monitors of a sample. This mode does not need a compositor: ```sh dynamonix --demo ``` Show the outputs as JSON for a script. The command writes one JSON object and nothing else: ```sh dynamonix list --json ``` Save the arrangement of the outputs with a name: ```sh dynamonix save desk ``` Send the arrangement of a saved profile without the interface: ```sh dynamonix apply desk ``` Let the program send the profile of a new group of monitors with no question: ```sh dynamonix --auto ``` Show the profiles that you saved. This command does not need a compositor: ```sh dynamonix profiles ``` Use a different socket: ```sh dynamonix --socket /run/user/1000/niri.wayland-2.12345.sock ``` ## Profiles A profile holds an arrangement of your monitors with a name. The program keeps the profiles in `$XDG_CONFIG_HOME/dynamonix/profiles.json`. It uses `~/.config/dynamonix/profiles.json` when the environment gives no absolute `$XDG_CONFIG_HOME`. A profile records the make, the model and the serial number of each monitor. It does not record the name of the output. A profile therefore finds the same monitors after you move a cable to a different connector. The `apply` command obeys the same safety rules as the interface. It examines the layout before it sends the layout. The command shows no interface, so it works in a niri key binding, in a script, and in a hotplug rule. The exit status tells a script what occurred: | Status | Condition | | --- | --- | | 0 | The compositor has the layout of the profile. | | 1 | The program stopped with an error. | | 2 | The program or the compositor did not accept the layout. | ## Autoswitch You connect a monitor or you remove a monitor while the interface runs. The program searches the profiles for the new group of monitors. When a profile matches, the program shows the name of the profile, and you press `Enter` to send it. The program sends nothing before that. Start the program with `--auto` to send the matching profile with no question. A group with no profile changes nothing. The safety rules hold in both conditions. The program does not send a layout with an error. It reports the error instead. ## Keys | Key | Operation | | --- | --- | | `←` `↑` `↓` `→` or `h` `j` `k` `l` | Move the selected output 48 pixels. The output aligns with the outputs near it. | | `Shift` and an arrow key | Move the output one pixel. The output does not align. | | `Tab` | Move the attention between the map and the list. | | `m` / `M` | Select the next mode or the previous mode. | | `+` / `-` | Make the scale factor larger or smaller. | | `r` / `R` | Turn the output 90 degrees. | | `e` | Turn the output on or off. | | `v` | Turn the variable refresh rate on or off. | | `a` | Put all the outputs in one row. | | `c` | Put all the outputs in one column. | | `n` | Move the arrangement to the origin. | | `u` | Remove the last change. | | `Ctrl` and `r` | Return the change that the last `u` removed. | | `U` | Remove all your changes. A `u` after it returns them. | | `Enter` | Send the changes to the compositor. | | `q` or `Esc` | Stop the program. | ## Pointer Press an output in the map and move the pointer. The output follows the pointer and aligns with the outputs near it, with the same rules as the keys. Hold `Shift` to move with no alignment. The movement of one hold makes one change, and one `u` removes it. A press outside of every output moves nothing. ## Safety The program examines an arrangement before it sends the arrangement. It stops two conditions: - The program does not turn off the last output. A computer with no output is difficult to use. - The program does not send a mode that the monitor does not have. The program gives a warning for two other conditions. It does not stop them: - Two outputs cover the same area. - The outputs make more than one group, with a space between the groups. ## Behavior of the compositor The program measured this behavior on niri 26.04: - The compositor makes the logical size from the physical size. It divides the physical size by the scale factor. It then removes the fraction. - The compositor accepts a scale factor between 0.1 and 10.0. It changes a value that is outside of these limits. It gives no error. - The compositor accepts a change for a monitor that is not connected. It keeps the change. It applies the change when the monitor connects. - The compositor has **no event for a change of the outputs**. A program that wants to know about a new monitor must ask the compositor again. The program therefore asks the compositor at a regular time. It also asks immediately after an other event. ## Documentation The [wiki](https://git.devtechnica.com/sreedev/dynamonix/wiki) holds the documents of the project: - [Architecture](https://git.devtechnica.com/sreedev/dynamonix/wiki/Architecture) - [Tests](https://git.devtechnica.com/sreedev/dynamonix/wiki/Tests) - [Continuous integration](https://git.devtechnica.com/sreedev/dynamonix/wiki/Continuous-integration) ## License Refer to the [LICENSE](LICENSE) file at the root of the repository.