Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ CF_DNS_API_TOKEN=<token>

## Credits

Wallpaper: <https://github.com/D3Ext/aesthetic-wallpapers>
Desktop background — `modules/home-manager/sway/assets/cafe-at-night_00_3840x2160.png`
— is "cafe at night" from [tokyo-night/wallpapers][wp] (`misc/cityscape/`), used
unmodified under the MIT license.

[wp]: https://github.com/tokyo-night/wallpapers/blob/main/misc/cityscape/cafe-at-night_00_3840x2160.png
116 changes: 116 additions & 0 deletions docs/gui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<!-- Generated by `nix run .#docs` from modules/home-manager/sway/default.nix — do not edit by hand. -->

# GUI (sway)

Port of the old hyprland setup (removed in `3a09509`). `Mod` is Super;
inside the `swayvm` VM that's the macOS Cmd key (QEMU runs with
full-grab, so cmd shortcuts reach the guest — use Ctrl+Option+G to
release the grab when you need macOS back, e.g. for Cmd+Tab).

## Keybindings

| Key | Action |
| --- | --- |
| `Mod+0` | `workspace number 10` |
| `Mod+1` | `workspace number 1` |
| `Mod+2` | `workspace number 2` |
| `Mod+3` | `workspace number 3` |
| `Mod+4` | `workspace number 4` |
| `Mod+5` | `workspace number 5` |
| `Mod+6` | `workspace number 6` |
| `Mod+7` | `workspace number 7` |
| `Mod+8` | `workspace number 8` |
| `Mod+9` | `workspace number 9` |
| `Mod+Alt+q` | `exit` |
| `Mod+Ctrl+h` | `resize shrink width 20px` |
| `Mod+Ctrl+j` | `resize grow height 20px` |
| `Mod+Ctrl+k` | `resize shrink height 20px` |
| `Mod+Ctrl+l` | `resize grow width 20px` |
| `Mod+Shift+0` | `move container to workspace number 10` |
| `Mod+Shift+1` | `move container to workspace number 1` |
| `Mod+Shift+2` | `move container to workspace number 2` |
| `Mod+Shift+3` | `move container to workspace number 3` |
| `Mod+Shift+4` | `move container to workspace number 4` |
| `Mod+Shift+5` | `move container to workspace number 5` |
| `Mod+Shift+6` | `move container to workspace number 6` |
| `Mod+Shift+7` | `move container to workspace number 7` |
| `Mod+Shift+8` | `move container to workspace number 8` |
| `Mod+Shift+9` | `move container to workspace number 9` |
| `Mod+Shift+f` | `floating toggle` |
| `Mod+Shift+h` | `move left` |
| `Mod+Shift+j` | `move down` |
| `Mod+Shift+k` | `move up` |
| `Mod+Shift+l` | `move right` |
| `Mod+Shift+s` | `move scratchpad` |
| `Mod+Shift+space` | `exec rofi -show run` |
| `Mod+Shift+x` | `exec grim -g "$(slurp)" - \| wl-copy` |
| `Mod+b` | `exec pkill waybar \|\| waybar` |
| `Mod+e` | `exec nautilus -w` |
| `Mod+f` | `fullscreen toggle` |
| `Mod+h` | `focus left` |
| `Mod+i` | `exec brain` |
| `Mod+j` | `focus down` |
| `Mod+k` | `focus up` |
| `Mod+l` | `focus right` |
| `Mod+n` | `exec chromium` |
| `Mod+p` | `layout toggle split` |
| `Mod+q` | `kill` |
| `Mod+s` | `scratchpad show` |
| `Mod+space` | `exec rofi -show drun` |
| `Mod+t` | `exec alacritty` |
| `Mod+x` | `exec grim - \| wl-copy` |
| `XF86AudioLowerVolume` | `exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-` |
| `XF86AudioMute` | `exec wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle` |
| `XF86AudioNext` | `exec playerctl next` |
| `XF86AudioPlay` | `exec playerctl play-pause` |
| `XF86AudioPrev` | `exec playerctl previous` |
| `XF86AudioRaiseVolume` | `exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+` |

Mouse, with `Mod` held: left-drag moves floating windows, right-drag
resizes them.

| Wheel | Action |
| --- | --- |
| `Mod+button4` | `workspace prev` |
| `Mod+button5` | `workspace next` |

## Theme

Catppuccin mocha, defined once in `modules/home-manager/palette.nix`
and read by sway, waybar, rofi and mako so they can't drift apart.

| Setting | Value |
| --- | --- |
| accent | `#cba6f7` (mauve) — focused border, selected rofi entry, current workspace, notification border |
| borders | `2px`, no titlebars |
| gaps | `8px` between windows, `4px` around the screen |

## Window rules

| Criteria | Command |
| --- | --- |
| `{"app_id":"1Password"}` | `floating enable` |
| `{"app_id":"org.gnome.Nautilus"}` | `floating enable` |

## Bar (waybar)

| Position | Modules |
| --- | --- |
| left | `sway/workspaces`, `sway/mode` |
| center | `clock` |
| right | `custom/spotify`, `backlight`, `pulseaudio`, `network`, `battery`, `tray` |

`Mod+b` restarts it if it dies.

## The VM

```
linux-builder start # only needed when the config changed
nix run .#swayvm
```

greetd auto-starts sway as `marco` (password `marco`). The VM boots
fullscreen because QEMU's cocoa display only reports its window size
to the guest at boot. Disk state lives in `swayvm.qcow2` in the
directory you run it from; delete it to reset. Ctrl+Option+G releases
the input grab.
1 change: 1 addition & 0 deletions docs/hosts.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
| `live` | NixOS | `x86_64-linux` | `26.11` |
| `mixer` | NixOS | `x86_64-linux` | `24.11` |
| `qraspi` | NixOS | `aarch64-linux` | `26.05` |
| `swayvm` | NixOS | `aarch64-linux` | `25.11` |
| `wooper` | NixOS | `aarch64-linux` | `25.05` |
| `wraspi` | NixOS | `aarch64-linux` | `25.05` |

Expand Down
30 changes: 23 additions & 7 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
./hosts/lamp/configuration.nix
self.darwinModules.default
self.darwinModules.work
self.darwinModules.linux-builder
home-manager.darwinModules.home-manager
];
specialArgs = {inherit inputs;};
Expand Down Expand Up @@ -173,6 +174,16 @@
specialArgs = {inherit inputs;};
};

"swayvm" = nixpkgs.lib.nixosSystem {
system = "aarch64-linux";
modules = [
./hosts/swayvm/configuration.nix
./modules/nixos/home-manager.nix
self.nixosModules.minimal
];
specialArgs = {inherit inputs;};
};

"live" = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
Expand All @@ -197,6 +208,7 @@
darwinModules = {
default = ./modules/darwin/base/default.nix;
work = ./modules/darwin/work/default.nix;
linux-builder = ./modules/darwin/linux-builder.nix;
};

homeConfigurations = let
Expand Down Expand Up @@ -262,13 +274,17 @@
nixpkgs.legacyPackages.${system}.alejandra
);

packages = nixpkgs.lib.genAttrs systems (system: {
docs = import ./lib/docs {
pkgs = nixpkgs.legacyPackages.${system};
inherit (nixpkgs) lib;
inherit inputs;
};
});
packages = nixpkgs.lib.genAttrs systems (system:
{
docs = import ./lib/docs {
pkgs = nixpkgs.legacyPackages.${system};
inherit (nixpkgs) lib;
inherit inputs;
};
}
// nixpkgs.lib.optionalAttrs (system == "aarch64-darwin") {
swayvm = self.nixosConfigurations.swayvm.config.system.build.vm;
});

apps = nixpkgs.lib.genAttrs systems (system: let
pkgs = nixpkgs.legacyPackages.${system};
Expand Down
87 changes: 87 additions & 0 deletions hosts/swayvm/configuration.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
inputs,
pkgs,
lib,
...
}: {
networking.hostName = "swayvm";

programs.sway.enable = true;
hardware.graphics.enable = true;

modules.home-manager.enable = true;
home-manager.backupFileExtension = "bak";
home-manager.users.marco = {
imports = [../../modules/home-manager/sway/default.nix];
# qemu's cocoa display maps guest pixels to macos points (no hidpi
# backing), so fonts render double: halve the base config's 18
programs.alacritty.settings.font.size = lib.mkForce 9;
};

fonts.packages = [pkgs.nerd-fonts.meslo-lg];

# the minimal profile ships almost no terminfo; alacritty's own comes
# with the package, this covers root shells and anything ssh'ing in
environment.enableAllTerminfo = true;

services.pipewire = {
enable = true;
pulse.enable = true;
};
security.rtkit.enable = true;

xdg.portal = {
enable = true;
wlr.enable = true;
extraPortals = [pkgs.xdg-desktop-portal-gtk];
};

services.greetd = {
enable = true;
settings.default_session = {
command = "${pkgs.sway}/bin/sway";
user = "marco";
};
};

environment.sessionVariables = {
# no 3d acceleration in the vm, let wlroots use llvmpipe
WLR_RENDERER_ALLOW_SOFTWARE = "1";
WLR_NO_HARDWARE_CURSORS = "1";
};

system.autoUpgrade.enable = false;

# dummy boot/fs config so the non-vm system still evaluates
fileSystems."/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
boot.loader.systemd-boot.enable = true;

virtualisation.vmVariant = {
virtualisation = {
host.pkgs = inputs.nixpkgs.legacyPackages.aarch64-darwin;
# no gpu passthrough behind qemu's cocoa display, so everything is
# drawn by llvmpipe on the cpu — it scales with cores, and the ram
# keeps the 9p-mounted host store in page cache between launches
cores = 8;
memorySize = 8192;
diskSize = 8192;
qemu.options = [
"-device virtio-gpu-pci"
"-device virtio-tablet-pci"
# cocoa only reports window size to the guest at boot, so start
# fullscreen; zoom-to-fit scales the picture if windowed later.
# full-grab captures cmd shortcuts so cmd works as sway's mod.
# no show-cursor: it sets cursor_hide=0 in ui/cocoa.m, which turns
# the [NSCursor hide] on grab into a no-op and leaves the macOS
# pointer floating over sway's own cursor
"-display cocoa,full-screen=on,zoom-to-fit=on,full-grab=on"
];
};
users.users.marco.initialPassword = "marco";
};

system.stateVersion = "25.11";
}
4 changes: 4 additions & 0 deletions lib/docs/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
inherit lib;
inherit (inputs) self;
};
"gui.md" = import ./gui.nix {
inherit lib;
inherit (inputs) self;
};
};
in
pkgs.linkFarm "docs" (lib.mapAttrsToList (name: text: {
Expand Down
97 changes: 97 additions & 0 deletions lib/docs/gui.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Renders docs/gui.md from the sway home-manager configuration.
{
lib,
self,
}: let
md = import ./md.nix {inherit lib;};

hm = self.nixosConfigurations.swayvm.config.home-manager.users.marco;
cfg = hm.wayland.windowManager.sway.config;

# /nix/store/<hash>-foo/bin/foo -> foo; context discarded so the docs
# don't depend on the vm's linux packages
clean = s:
lib.concatStrings (lib.filter builtins.isString
(builtins.split ''/nix/store/[^ )"']*/bin/'' (builtins.unsafeDiscardStringContext s)));

prettyKey = k: md.code (lib.replaceStrings ["Mod4"] ["Mod"] k);

bindRows =
lib.mapAttrsToList (k: cmd: [(prettyKey k) (md.code (clean cmd))])
cfg.keybindings;

wheelRows =
map (
l: let
toks = lib.splitString " " (lib.removePrefix "bindsym --whole-window " (lib.trim l));
in [
(prettyKey (lib.head toks))
(md.code (lib.concatStringsSep " " (lib.tail toks)))
]
) (lib.filter (l: lib.hasInfix "bindsym" l)
(lib.splitString "\n" hm.wayland.windowManager.sway.extraConfig));

ruleRows =
map (
w: [(md.code (builtins.toJSON w.criteria)) (md.code w.command)]
)
cfg.window.commands;

waybar = lib.head hm.programs.waybar.settings;
in ''
${md.banner "modules/home-manager/sway/default.nix"}

# GUI (sway)

Port of the old hyprland setup (removed in `3a09509`). `Mod` is Super;
inside the `swayvm` VM that's the macOS Cmd key (QEMU runs with
full-grab, so cmd shortcuts reach the guest — use Ctrl+Option+G to
release the grab when you need macOS back, e.g. for Cmd+Tab).

## Keybindings

${md.table ["Key" "Action"] bindRows}

Mouse, with `Mod` held: left-drag moves floating windows, right-drag
resizes them.

${md.table ["Wheel" "Action"] wheelRows}

## Theme

Catppuccin mocha, defined once in `modules/home-manager/palette.nix`
and read by sway, waybar, rofi and mako so they can't drift apart.

${md.table ["Setting" "Value"] [
["accent" "${md.code cfg.colors.focused.border} (mauve) — focused border, selected rofi entry, current workspace, notification border"]
["borders" "${md.code "${toString cfg.window.border}px"}, no titlebars"]
["gaps" "${md.code "${toString cfg.gaps.inner}px"} between windows, ${md.code "${toString cfg.gaps.outer}px"} around the screen"]
]}

## Window rules

${md.table ["Criteria" "Command"] ruleRows}

## Bar (waybar)

${md.table ["Position" "Modules"] [
["left" (md.codeList waybar.modules-left)]
["center" (md.codeList waybar.modules-center)]
["right" (md.codeList waybar.modules-right)]
]}

`Mod+b` restarts it if it dies.

## The VM

```
linux-builder start # only needed when the config changed
nix run .#swayvm
```

greetd auto-starts sway as `marco` (password `marco`). The VM boots
fullscreen because QEMU's cocoa display only reports its window size
to the guest at boot. Disk state lives in `swayvm.qcow2` in the
directory you run it from; delete it to reset. Ctrl+Option+G releases
the input grab.
''
Loading