From 9f3f5c5a9db6157f86bcbcf8d0350e10501b8ef8 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Thu, 5 Feb 2026 18:35:42 -0800 Subject: [PATCH] nixos: add emacs-auto wrapper + desktop files --- nixos/bootstrap.nix | 2 +- nixos/emacs-overlay.nix | 132 ++++++++++++++++++++++++++++++++++++++++ nixos/essential.nix | 2 +- nixos/nix.nix | 1 + nixos/overlay.nix | 5 -- 5 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 nixos/emacs-overlay.nix diff --git a/nixos/bootstrap.nix b/nixos/bootstrap.nix index f2296cef..50482504 100644 --- a/nixos/bootstrap.nix +++ b/nixos/bootstrap.nix @@ -4,7 +4,7 @@ ./essential.nix ]; environment.systemPackages = with pkgs; [ - emacs + emacs-auto ]; programs.zsh.enable = true; networking.firewall.enable = false; diff --git a/nixos/emacs-overlay.nix b/nixos/emacs-overlay.nix new file mode 100644 index 00000000..b1e8eeff --- /dev/null +++ b/nixos/emacs-overlay.nix @@ -0,0 +1,132 @@ +final: prev: +{ + # Keep pkgs.emacs as a real Emacs package (used by other derivations / emacsPackages), + # but provide an "auto" wrapper for interactive use that selects the right GUI backend. + emacs = prev."emacs30-pgtk".override { + withNativeCompilation = true; + withTreeSitter = true; + }; + + emacs-wayland = final.emacs; + + emacs-x11 = prev.emacs30.override { + withNativeCompilation = true; + withTreeSitter = true; + }; + + # Runtime launcher that chooses: + # - Wayland session: pgtk build with GDK_BACKEND=wayland (avoids XWayland) + # - X11 session: X11 build with GDK_BACKEND=x11 + # - No display: falls back to -nw + # + # Override with EMACS_AUTO_BACKEND=wayland|x11|tty. + emacs-auto = final.stdenvNoCC.mkDerivation { + pname = "emacs-auto"; + version = final.emacs.version or "unknown"; + dontUnpack = true; + nativeBuildInputs = [ final.makeWrapper ]; + + installPhase = + let + emacsWayland = final.emacs-wayland; + emacsX11 = final.emacs-x11; + in + '' + mkdir -p "$out/bin" "$out/share/applications" "$out/share/icons" "$out/share/pixmaps" + + # Ensure desktop integrations (icons) exist even though we don't install the raw emacs packages. + if [ -d "${emacsWayland}/share/icons" ]; then + ln -s "${emacsWayland}/share/icons/hicolor" "$out/share/icons/hicolor" + fi + if [ -d "${emacsWayland}/share/pixmaps" ]; then + ln -s "${emacsWayland}/share/pixmaps/"* "$out/share/pixmaps/" || true + fi + + # Convenience explicit launchers. + makeWrapper ${emacsWayland}/bin/emacs "$out/bin/emacs-wayland" \ + --set GDK_BACKEND wayland + makeWrapper ${emacsX11}/bin/emacs "$out/bin/emacs-x11" \ + --set GDK_BACKEND x11 + + # Main launcher. + cat > "$out/bin/emacs" <<'EOF_EMACS_WRAPPER' +#!${final.runtimeShell} +set -eu + +backend="''${EMACS_AUTO_BACKEND:-}" +tty=0 +for a in "$@"; do + case "$a" in + -nw|--nw|--tty|--terminal|--no-window-system) tty=1 ;; + esac +done + +if [ "$backend" = "wayland" ] || [ "$backend" = "pgtk" ]; then + exec "@out@/bin/emacs-wayland" "$@" +fi +if [ "$backend" = "x11" ]; then + exec "@out@/bin/emacs-x11" "$@" +fi +if [ "$backend" = "tty" ]; then + exec "@emacsX11@/bin/emacs" -nw "$@" +fi + +if [ "$tty" -eq 1 ]; then + # Respect the user's explicit -nw, but still run a consistent binary. + exec "@emacsX11@/bin/emacs" "$@" +fi + +# Prefer Wayland if it looks like a Wayland session. +if [ -n "''${WAYLAND_DISPLAY:-}" ] || [ "''${XDG_SESSION_TYPE:-}" = "wayland" ] || [ -n "''${HYPRLAND_INSTANCE_SIGNATURE:-}" ]; then + exec "@out@/bin/emacs-wayland" "$@" +fi + +# Otherwise, if X is available, use the X11 build. +if [ -n "''${DISPLAY:-}" ] || [ "''${XDG_SESSION_TYPE:-}" = "x11" ]; then + exec "@out@/bin/emacs-x11" "$@" +fi + +# Headless fallback. +exec "@emacsX11@/bin/emacs" -nw "$@" +EOF_EMACS_WRAPPER + + substituteInPlace "$out/bin/emacs" \ + --subst-var out \ + --replace-fail "@emacsX11@" "${emacsX11}" + chmod +x "$out/bin/emacs" + + # emacsclient is compatible across builds as long as versions match; use one. + ln -s ${emacsX11}/bin/emacsclient "$out/bin/emacsclient" + + cat > "$out/share/applications/emacs.desktop" <<'EOF' +[Desktop Entry] +Name=Emacs +GenericName=Text Editor +Comment=Edit text +Exec=emacs %F +TryExec=emacs +Icon=emacs +Type=Application +Terminal=false +Categories=Development;TextEditor; +MimeType=text/plain; +StartupWMClass=Emacs +EOF + + cat > "$out/share/applications/emacsclient.desktop" <<'EOF' +[Desktop Entry] +Name=Emacs (Client) +GenericName=Text Editor +Comment=Edit text using a running Emacs server +Exec=emacsclient -c -a emacs %F +TryExec=emacsclient +Icon=emacs +Type=Application +Terminal=false +Categories=Development;TextEditor; +MimeType=text/plain; +StartupWMClass=Emacs +EOF + ''; + }; +} diff --git a/nixos/essential.nix b/nixos/essential.nix index 42289b40..8ed54cba 100644 --- a/nixos/essential.nix +++ b/nixos/essential.nix @@ -14,7 +14,7 @@ direnv dpkg efibootmgr - emacs + emacs-auto fd ffmpeg file diff --git a/nixos/nix.nix b/nixos/nix.nix index 509cb9ef..fff0e74c 100644 --- a/nixos/nix.nix +++ b/nixos/nix.nix @@ -55,6 +55,7 @@ nixpkgs.overlays = [ # (import ./nvidia-container-toolkit-overlay.nix) (import ./runc-overlay.nix) + (import ./emacs-overlay.nix) (import ./overlay.nix) # Use codex and claude-code from dedicated flakes with cachix (final: prev: { diff --git a/nixos/overlay.nix b/nixos/overlay.nix index ba5825ae..36d93696 100644 --- a/nixos/overlay.nix +++ b/nixos/overlay.nix @@ -248,11 +248,6 @@ in }; }); - emacs = prev."emacs30-pgtk".override { - withNativeCompilation = true; - withTreeSitter = true; - }; - python-with-my-packages = let my-python-packages = python-packages: with python-packages; [