diff --git a/.gitignore b/.gitignore index 0e301f8c4..db8457e1f 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ test/cases/**/*.actual.png glad.zip /Box_test.ppm /Box_test_diff.ppm +/ghostty.qcow2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index af3c30be7..a7233b2c2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,3 +77,100 @@ pull request will be accepted with a high degree of certainty. > **Pull requests are NOT a place to discuss feature design.** Please do > not open a WIP pull request to discuss a feature. Instead, use a discussion > and link to your branch. + +## Nix Virtual Machines + +Several Nix virtual machine definitions are provided by the project for testing +and developing Ghostty against multiple different Linux desktop environments. + +Running these requires a working Nix installation, either Nix on your +favorite Linux distribution, NixOS, or macOS with nix-darwin installed. Further +requirements for macOS are detailed below. + +VMs should only be run on your local desktop and then powered off when not in +use, which will discard any changes to the VM. + +The VM definitions provide minimal software "out of the box" but additional +software can be installed by using standard Nix mechanisms like `nix run nixpkgs#`. + +### Linux + +1. Check out the Ghostty source and change to the directory. +2. Run `nix run .#`. `` can be any of the VMs defined in the + `nix/vm` directory (without the `.nix` suffix) excluding any file prefixed + with `common` or `create`. +3. The VM will build and then launch. Depending on the speed of your system, this + can take a while, but eventually you should get a new VM window. +4. The Ghostty source directory should be mounted to `/tmp/shared` in the VM. Depending + on what UID and GID of the user that you launched the VM as, `/tmp/shared` _may_ be + writable by the VM user, so be careful! + +### macOS + +1. To run the VMs on macOS you will need to enable the Linux builder in your `nix-darwin` + config. This _should_ be as simple as adding `nix.linux-builder.enable=true` to your + configuration and then rebuilding. See [this](https://nixcademy.com/posts/macos-linux-builder/) + blog post for more information about the Linux builder and how to tune the performance. +2. Once the Linux builder has been enabled, you should be able to follow the Linux instructions + above to launch a VM. + +### Custom VMs + +To easily create a custom VM without modifying the Ghostty source, create a new +directory, then create a file called `flake.nix` with the following text in the +new directory. + +``` +{ + inputs = { + nixpkgs.url = "nixpkgs/nixpkgs-unstable"; + ghostty.url = "github:ghostty-org/ghostty"; + }; + outputs = { + nixpkgs, + ghostty, + ... + }: { + nixosConfigurations.custom-vm = ghostty.create-gnome-vm { + nixpkgs = nixpkgs; + system = "x86_64-linux"; + overlay = ghostty.overlays.releasefast; + # module = ./configuration.nix # also works + module = {pkgs, ...}: { + environment.systemPackages = [ + pkgs.btop + ]; + }; + }; + }; +} +``` + +The custom VM can then be run with a command like this: + +``` +nix run .#nixosConfigurations.custom-vm.config.system.build.vm +``` + +A file named `ghostty.qcow2` will be created that is used to persist any changes +made in the VM. To "reset" the VM to default delete the file and it will be +recreated the next time you run the VM. + +### Contributing new VM definitions + +#### VM Acceptance Criteria + +We welcome the contribution of new VM definitions, as long as they meet the following criteria: + +1. The should be different enough from existing VM definitions that they represent a distinct + user (and developer) experience. +2. There's a significant Ghostty user population that uses a similar environment. +3. The VMs can be built using only packages from the current stable NixOS release. + +#### VM Definition Criteria + +1. VMs should be as minimal as possible so that they build and launch quickly. + Additional software can be added at runtime with a command like `nix run nixpkgs#`. +2. VMs should not expose any services to the network, or run any remote access + software like SSH daemons, VNC or RDP. +3. VMs should auto-login using the "ghostty" user. diff --git a/flake.nix b/flake.nix index 83d4af414..3256c7c15 100644 --- a/flake.nix +++ b/flake.nix @@ -31,38 +31,81 @@ zig, ... }: - builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} (builtins.map (system: let - pkgs-stable = nixpkgs-stable.legacyPackages.${system}; - pkgs-unstable = nixpkgs-unstable.legacyPackages.${system}; - in { - devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix { - zig = zig.packages.${system}."0.13.0"; - wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {}; - }; + builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} ( + builtins.map ( + system: let + pkgs-stable = nixpkgs-stable.legacyPackages.${system}; + pkgs-unstable = nixpkgs-unstable.legacyPackages.${system}; + in { + devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix { + zig = zig.packages.${system}."0.13.0"; + wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {}; + }; - packages.${system} = let - mkArgs = optimize: { - inherit optimize; + packages.${system} = let + mkArgs = optimize: { + inherit optimize; - revision = self.shortRev or self.dirtyShortRev or "dirty"; - }; - in rec { - ghostty-debug = pkgs-stable.callPackage ./nix/package.nix (mkArgs "Debug"); - ghostty-releasesafe = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseSafe"); - ghostty-releasefast = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseFast"); + revision = self.shortRev or self.dirtyShortRev or "dirty"; + }; + in rec { + ghostty-debug = pkgs-stable.callPackage ./nix/package.nix (mkArgs "Debug"); + ghostty-releasesafe = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseSafe"); + ghostty-releasefast = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseFast"); - ghostty = ghostty-releasefast; - default = ghostty; - }; + ghostty = ghostty-releasefast; + default = ghostty; + }; - formatter.${system} = pkgs-stable.alejandra; + formatter.${system} = pkgs-stable.alejandra; - # Our supported systems are the same supported systems as the Zig binaries. - }) (builtins.attrNames zig.packages)) + apps.${system} = let + runVM = ( + module: let + vm = import ./nix/vm/create.nix { + inherit system module; + nixpkgs = nixpkgs-stable; + overlay = self.overlays.debug; + }; + program = pkgs-stable.writeShellScript "run-ghostty-vm" '' + SHARED_DIR=$(pwd) + export SHARED_DIR + + ${pkgs-stable.lib.getExe vm.config.system.build.vm} "$@" + ''; + in { + type = "app"; + program = "${program}"; + } + ); + in { + wayland-cinnamon = runVM ./nix/vm/wayland-cinnamon.nix; + wayland-gnome = runVM ./nix/vm/wayland-gnome.nix; + wayland-plasma6 = runVM ./nix/vm/wayland-plasma6.nix; + x11-cinnamon = runVM ./nix/vm/x11-cinnamon.nix; + x11-gnome = runVM ./nix/vm/x11-gnome.nix; + x11-plasma6 = runVM ./nix/vm/x11-plasma6.nix; + x11-xfce = runVM ./nix/vm/x11-xfce.nix; + }; + } + # Our supported systems are the same supported systems as the Zig binaries. + ) (builtins.attrNames zig.packages) + ) // { - overlays.default = final: prev: { - ghostty = self.packages.${prev.system}.default; + overlays = { + default = self.overlays.releasefast; + releasefast = final: prev: { + ghostty = self.packages.${prev.system}.ghostty-releasefast; + }; + debug = final: prev: { + ghostty = self.packages.${prev.system}.ghostty-debug; + }; }; + create-vm = import ./nix/vm/create.nix; + create-cinnamon-vm = import ./nix/vm/create-cinnamon.nix; + create-gnome-vm = import ./nix/vm/create-gnome.nix; + create-plasma6-vm = import ./nix/vm/create-plasma6.nix; + create-xfce-vm = import ./nix/vm/create-xfce.nix; }; nixConfig = { diff --git a/nix/vm/common-cinnamon.nix b/nix/vm/common-cinnamon.nix new file mode 100644 index 000000000..dabe5e701 --- /dev/null +++ b/nix/vm/common-cinnamon.nix @@ -0,0 +1,18 @@ +{...}: { + imports = [ + ./common.nix + ]; + + services.xserver = { + displayManager = { + lightdm = { + enable = true; + }; + }; + desktopManager = { + cinnamon = { + enable = true; + }; + }; + }; +} diff --git a/nix/vm/common-gnome.nix b/nix/vm/common-gnome.nix new file mode 100644 index 000000000..0c2bef150 --- /dev/null +++ b/nix/vm/common-gnome.nix @@ -0,0 +1,136 @@ +{ + config, + lib, + pkgs, + ... +}: { + imports = [ + ./common.nix + ]; + + services.xserver = { + displayManager = { + gdm = { + enable = true; + autoSuspend = false; + }; + }; + desktopManager = { + gnome = { + enable = true; + }; + }; + }; + + environment.systemPackages = [ + pkgs.gnomeExtensions.no-overview + ]; + + environment.gnome.excludePackages = with pkgs; [ + atomix + baobab + cheese + epiphany + evince + file-roller + geary + gnome-backgrounds + gnome-calculator + gnome-calendar + gnome-clocks + gnome-connections + gnome-contacts + gnome-disk-utility + gnome-extension-manager + gnome-logs + gnome-maps + gnome-music + gnome-photos + gnome-software + gnome-system-monitor + gnome-text-editor + gnome-themes-extra + gnome-tour + gnome-user-docs + gnome-weather + hitori + iagno + loupe + nautilus + orca + seahorse + simple-scan + snapshot + sushi + tali + totem + yelp + ]; + + programs.dconf = { + enable = true; + profiles.user.databases = [ + { + settings = with lib.gvariant; { + "org/gnome/desktop/background" = { + picture-uri = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png"; + picture-uri-dark = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png"; + picture-options = "centered"; + primary-color = "#000000000000"; + secondary-color = "#000000000000"; + }; + "org/gnome/desktop/interface" = { + color-scheme = "prefer-dark"; + }; + "org/gnome/desktop/notifications" = { + show-in-lock-screen = false; + }; + "org/gnome/desktop/screensaver" = { + lock-enabled = false; + picture-uri = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png"; + picture-options = "centered"; + primary-color = "#000000000000"; + secondary-color = "#000000000000"; + }; + "org/gnome/desktop/session" = { + idle-delay = mkUint32 0; + }; + "org/gnome/shell" = { + disable-user-extensions = false; + enabled-extensions = builtins.map (x: x.extensionUuid) ( + lib.filter (p: p ? extensionUuid) config.environment.systemPackages + ); + }; + }; + } + ]; + }; + + programs.geary.enable = false; + + services.gnome = { + gnome-browser-connector.enable = false; + gnome-initial-setup.enable = false; + gnome-online-accounts.enable = false; + gnome-remote-desktop.enable = false; + rygel.enable = false; + }; + + system.activationScripts = { + face = { + text = '' + mkdir -p /var/lib/AccountsService/{icons,users} + + cp ${pkgs.ghostty}/share/icons/hicolor/1024x1024/apps/com.mitchellh.ghostty.png /var/lib/AccountsService/icons/ghostty + + echo -e "[User]\nIcon=/var/lib/AccountsService/icons/ghostty\n" > /var/lib/AccountsService/users/ghostty + + chown root:root /var/lib/AccountsService/users/ghostty + chmod 0600 /var/lib/AccountsService/users/ghostty + + chown root:root /var/lib/AccountsService/icons/ghostty + chmod 0444 /var/lib/AccountsService/icons/ghostty + ''; + }; + }; +} diff --git a/nix/vm/common-plasma6.nix b/nix/vm/common-plasma6.nix new file mode 100644 index 000000000..e5c9bd4d8 --- /dev/null +++ b/nix/vm/common-plasma6.nix @@ -0,0 +1,21 @@ +{...}: { + imports = [ + ./common.nix + ]; + + services = { + displayManager = { + sddm = { + enable = true; + wayland = { + enable = true; + }; + }; + }; + desktopManager = { + plasma6 = { + enable = true; + }; + }; + }; +} diff --git a/nix/vm/common-xfce.nix b/nix/vm/common-xfce.nix new file mode 100644 index 000000000..12a20d8d8 --- /dev/null +++ b/nix/vm/common-xfce.nix @@ -0,0 +1,18 @@ +{...}: { + imports = [ + ./common.nix + ]; + + services.xserver = { + displayManager = { + lightdm = { + enable = true; + }; + }; + desktopManager = { + xfce = { + enable = true; + }; + }; + }; +} diff --git a/nix/vm/common.nix b/nix/vm/common.nix new file mode 100644 index 000000000..eefd7c1c0 --- /dev/null +++ b/nix/vm/common.nix @@ -0,0 +1,83 @@ +{pkgs, ...}: { + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + documentation.nixos.enable = false; + + networking.hostName = "ghostty"; + networking.domain = "mitchellh.com"; + + virtualisation.vmVariant = { + virtualisation.memorySize = 2048; + }; + + nix = { + settings = { + trusted-users = [ + "root" + "ghostty" + ]; + }; + extraOptions = '' + experimental-features = nix-command flakes + ''; + }; + + users.mutableUsers = false; + + users.groups.ghostty = {}; + + users.users.ghostty = { + description = "Ghostty"; + group = "ghostty"; + extraGroups = ["wheel"]; + isNormalUser = true; + initialPassword = "ghostty"; + }; + + environment.etc = { + "xdg/autostart/com.mitchellh.ghostty.desktop" = { + source = "${pkgs.ghostty}/share/applications/com.mitchellh.ghostty.desktop"; + }; + }; + + environment.systemPackages = [ + pkgs.kitty + pkgs.fish + pkgs.ghostty + pkgs.helix + pkgs.neovim + pkgs.xterm + pkgs.zsh + ]; + + security.polkit = { + enable = true; + }; + + services.dbus = { + enable = true; + }; + + services.displayManager = { + autoLogin = { + user = "ghostty"; + }; + }; + + services.libinput = { + enable = true; + }; + + services.qemuGuest = { + enable = true; + }; + + services.spice-vdagentd = { + enable = true; + }; + + services.xserver = { + enable = true; + }; +} diff --git a/nix/vm/create-cinnamon.nix b/nix/vm/create-cinnamon.nix new file mode 100644 index 000000000..a9d9e44d7 --- /dev/null +++ b/nix/vm/create-cinnamon.nix @@ -0,0 +1,12 @@ +{ + system, + nixpkgs, + overlay, + module, + uid ? 1000, + gid ? 1000, +}: +import ./create.nix { + inherit system nixpkgs overlay module uid gid; + common = ./common-cinnamon.nix; +} diff --git a/nix/vm/create-gnome.nix b/nix/vm/create-gnome.nix new file mode 100644 index 000000000..bcd31f2b6 --- /dev/null +++ b/nix/vm/create-gnome.nix @@ -0,0 +1,12 @@ +{ + system, + nixpkgs, + overlay, + module, + uid ? 1000, + gid ? 1000, +}: +import ./create.nix { + inherit system nixpkgs overlay module uid gid; + common = ./common-gnome.nix; +} diff --git a/nix/vm/create-plasma6.nix b/nix/vm/create-plasma6.nix new file mode 100644 index 000000000..ede5371f3 --- /dev/null +++ b/nix/vm/create-plasma6.nix @@ -0,0 +1,12 @@ +{ + system, + nixpkgs, + overlay, + module, + uid ? 1000, + gid ? 1000, +}: +import ./create.nix { + inherit system nixpkgs overlay module uid gid; + common = ./common-plasma6.nix; +} diff --git a/nix/vm/create-xfce.nix b/nix/vm/create-xfce.nix new file mode 100644 index 000000000..d1789472d --- /dev/null +++ b/nix/vm/create-xfce.nix @@ -0,0 +1,12 @@ +{ + system, + nixpkgs, + overlay, + module, + uid ? 1000, + gid ? 1000, +}: +import ./create.nix { + inherit system nixpkgs overlay module uid gid; + common = ./common-xfce.nix; +} diff --git a/nix/vm/create.nix b/nix/vm/create.nix new file mode 100644 index 000000000..f8fe8500d --- /dev/null +++ b/nix/vm/create.nix @@ -0,0 +1,42 @@ +{ + system, + nixpkgs, + overlay, + module, + common ? ./common.nix, + uid ? 1000, + gid ? 1000, +}: let + pkgs = import nixpkgs { + inherit system; + overlays = [ + overlay + ]; + }; +in + nixpkgs.lib.nixosSystem { + system = builtins.replaceStrings ["darwin"] ["linux"] system; + modules = [ + { + virtualisation.vmVariant = { + virtualisation.host.pkgs = pkgs; + }; + + nixpkgs.overlays = [ + overlay + ]; + + users.groups.ghostty = { + gid = gid; + }; + + users.users.ghostty = { + uid = uid; + }; + + system.stateVersion = nixpkgs.lib.trivial.release; + } + common + module + ]; + } diff --git a/nix/vm/wayland-cinnamon.nix b/nix/vm/wayland-cinnamon.nix new file mode 100644 index 000000000..531c882b6 --- /dev/null +++ b/nix/vm/wayland-cinnamon.nix @@ -0,0 +1,7 @@ +{...}: { + imports = [ + ./common-cinnamon.nix + ]; + + services.displayManager.defaultSession = "cinnamon-wayland"; +} diff --git a/nix/vm/wayland-gnome.nix b/nix/vm/wayland-gnome.nix new file mode 100644 index 000000000..eb277d5d1 --- /dev/null +++ b/nix/vm/wayland-gnome.nix @@ -0,0 +1,9 @@ +{...}: { + imports = [ + ./common-gnome.nix + ]; + + services.displayManager = { + defaultSession = "gnome"; + }; +} diff --git a/nix/vm/wayland-plasma6.nix b/nix/vm/wayland-plasma6.nix new file mode 100644 index 000000000..6e5a253b8 --- /dev/null +++ b/nix/vm/wayland-plasma6.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./common-plasma6.nix + ]; + services.displayManager.defaultSession = "plasma"; +} diff --git a/nix/vm/x11-cinnamon.nix b/nix/vm/x11-cinnamon.nix new file mode 100644 index 000000000..636f235a2 --- /dev/null +++ b/nix/vm/x11-cinnamon.nix @@ -0,0 +1,7 @@ +{...}: { + imports = [ + ./common-cinnamon.nix + ]; + + services.displayManager.defaultSession = "cinnamon"; +} diff --git a/nix/vm/x11-gnome.nix b/nix/vm/x11-gnome.nix new file mode 100644 index 000000000..1994aea82 --- /dev/null +++ b/nix/vm/x11-gnome.nix @@ -0,0 +1,9 @@ +{...}: { + imports = [ + ./common-gnome.nix + ]; + + services.displayManager = { + defaultSession = "gnome-xorg"; + }; +} diff --git a/nix/vm/x11-plasma6.nix b/nix/vm/x11-plasma6.nix new file mode 100644 index 000000000..7818a80ca --- /dev/null +++ b/nix/vm/x11-plasma6.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./common-plasma6.nix + ]; + services.displayManager.defaultSession = "plasmax11"; +} diff --git a/nix/vm/x11-xfce.nix b/nix/vm/x11-xfce.nix new file mode 100644 index 000000000..71eb87f2f --- /dev/null +++ b/nix/vm/x11-xfce.nix @@ -0,0 +1,7 @@ +{...}: { + imports = [ + ./common-xfce.nix + ]; + + services.displayManager.defaultSession = "xfce"; +}