diff --git a/modules/programs/ghostty.nix b/modules/programs/ghostty.nix index 5b8dcd3..748a801 100644 --- a/modules/programs/ghostty.nix +++ b/modules/programs/ghostty.nix @@ -9,7 +9,7 @@ flake.modules.homeManager.ghostty = { config, pkgs, lib, ... }: let - ghosttyPackage = pkgs.symlinkJoin { + ghosttyX11 = pkgs.symlinkJoin { name = "ghostty-x11"; paths = [ (config.lib.nixGL.wrap pkgs.ghostty) ]; nativeBuildInputs = [ pkgs.makeWrapper ]; @@ -24,6 +24,10 @@ TERMINAL = "ghostty"; }; + home.packages = with pkgs; [ + inputs.self.packages.${system}.ghosttyGPUCheck + ]; + targets.genericLinux.nixGL = { packages = inputs.nixgl.packages.${pkgs.stdenv.hostPlatform.system}; defaultWrapper = "mesa"; @@ -33,7 +37,7 @@ programs.ghostty = { enable = true; enableZshIntegration = true; - package = ghosttyPackage; + package = ghosttyX11; settings = { command = "TERM=xterm-256color ${lib.getExe pkgs.zsh}"; font-size = 12; @@ -72,7 +76,7 @@ # https://github.com/ghostty-org/ghostty/discussions/3763#discussioncomment-11699970 xdg.desktopEntries."com.mitchellh.ghostty" = let - ghosttyCmd = lib.getExe ghosttyPackage; + ghosttyCmd = lib.getExe ghosttyX11; in { name = "Ghostty"; @@ -100,4 +104,16 @@ }; }; }; + + perSystem = { system, pkgs, lib, ... }: { + packages.ghosttyGPUCheck = pkgs.writeShellApplication { + name = "ghostty-gpu-check"; + runtimeInputs = with pkgs; [ + bash + gnugrep + procps # for pgrep, pidof, etc. + ]; + text = builtins.readFile ../../scripts/ghostty-gpu-check.sh; + }; + }; } diff --git a/scripts/ghostty-gpu-check.sh b/scripts/ghostty-gpu-check.sh new file mode 100755 index 0000000..b8274b9 --- /dev/null +++ b/scripts/ghostty-gpu-check.sh @@ -0,0 +1,212 @@ +#!/usr/bin/env bash +set -u + +# --- pretty output helpers --- +BOLD="\033[1m" +DIM="\033[2m" +GREEN="\033[32m" +YELLOW="\033[33m" +RED="\033[31m" +CYAN="\033[36m" +RESET="\033[0m" + +section() { + echo + echo -e "${BOLD}${CYAN}==> $1${RESET}" +} + +ok() { echo -e "${GREEN}[OK]${RESET} $1"; } +warn() { echo -e "${YELLOW}[WARN]${RESET} $1"; } +err() { echo -e "${RED}[ERR]${RESET} $1"; } +info() { echo -e "${DIM}$1${RESET}"; } + +has_cmd() { + command -v "$1" >/dev/null 2>&1 +} + +found_process=0 +found_render_node=0 +found_gpu_libs=0 +software_rasterizer=0 + +section "1) Find a running Ghostty process" +info "What this does: tries the same two process lookups you ran (pgrep and pidof)." +info "Good outcome: at least one PID is returned." + +echo -e "${DIM}\$ pgrep -x ghostty${RESET}" +PGREP_OUT="$(pgrep -x ghostty || true)" +if [[ -n "${PGREP_OUT}" ]]; then + echo "${PGREP_OUT}" +else + echo "" +fi + +echo -e "${DIM}\$ pidof ghostty${RESET}" +PIDOF_OUT="$(pidof ghostty || true)" +if [[ -n "${PIDOF_OUT}" ]]; then + echo "${PIDOF_OUT}" +else + echo "" +fi + +if [[ -n "${PGREP_OUT}" || -n "${PIDOF_OUT}" ]]; then + found_process=1 + ok "Ghostty process detected." +else + warn "No Ghostty process found yet by either command." +fi + +section "2) Set GHOSTTY_PID" +info "What this does: picks one Ghostty PID for all later checks." +info "Good outcome: GHOSTTY_PID is set to a valid running Ghostty process." + +# Prefer step-1 outputs first (avoids re-running lookups that may block on some systems). +GHOSTTY_PID="" +if [[ -n "${PGREP_OUT}" ]]; then + GHOSTTY_PID="$(printf '%s\n' "${PGREP_OUT}" | head -n1)" + info "Using PID from step-1 pgrep output." +elif [[ -n "${PIDOF_OUT}" ]]; then + GHOSTTY_PID="$(printf '%s\n' "${PIDOF_OUT}" | awk '{print $1}')" + info "Using PID from step-1 pidof output." +else + echo -e "${DIM}\$ pgrep -xo ghostty${RESET}" + if has_cmd timeout; then + PGREP_RAW="$(timeout 3s pgrep -xo ghostty 2>&1)" + pgrep_status=$? + if (( pgrep_status == 124 )); then + warn "pgrep timed out after 3 seconds." + fi + else + PGREP_RAW="$(pgrep -xo ghostty 2>&1)" + pgrep_status=$? + fi + + if (( pgrep_status == 0 )); then + GHOSTTY_PID="$(printf '%s\n' "${PGREP_RAW}" | head -n1)" + else + if [[ -n "${PGREP_RAW}" ]]; then + warn "[pgrep] ${PGREP_RAW}" + fi + warn "pgrep exit code: ${pgrep_status} (1 usually means no matching process)." + + echo -e "${DIM}\$ pidof ghostty${RESET}" + PIDOF_RAW="$(pidof ghostty 2>&1)" + pidof_status=$? + + if (( pidof_status == 0 )); then + GHOSTTY_PID="$(printf '%s\n' "${PIDOF_RAW}" | awk '{print $1}')" + else + if [[ -n "${PIDOF_RAW}" ]]; then + warn "[pidof] ${PIDOF_RAW}" + fi + warn "pidof exit code: ${pidof_status}" + GHOSTTY_PID="" + fi + fi +fi + +if [[ -z "${GHOSTTY_PID}" ]]; then + err "Could not find a running Ghostty process to inspect." + echo "Open Ghostty, then run this script again." + exit 1 +fi + +export GHOSTTY_PID +ok "Using GHOSTTY_PID=${GHOSTTY_PID}" + +section "3) Check for an open GPU render device" +info "What this does: inspects /proc//fd for /dev/dri entries used for rendering." +info "Good outcome: see /dev/dri/renderD* (for example renderD128)." + +if has_cmd rg; then + echo -e "${DIM}\$ sudo ls -l /proc/\$GHOSTTY_PID/fd | rg dri${RESET}" + DRI_LINES="$(sudo ls -l "/proc/${GHOSTTY_PID}/fd" | rg dri || true)" +else + warn "'rg' not found; using grep instead" + echo -e "${DIM}\$ sudo ls -l /proc/\$GHOSTTY_PID/fd | grep dri${RESET}" + DRI_LINES="$(sudo ls -l "/proc/${GHOSTTY_PID}/fd" | grep dri || true)" +fi + +if [[ -n "${DRI_LINES}" ]]; then + echo "${DRI_LINES}" +else + echo "" +fi + +if echo "${DRI_LINES}" | grep -q '/dev/dri/renderD'; then + found_render_node=1 + ok "Found /dev/dri/renderD*: strong sign Ghostty is using GPU render infrastructure." +elif [[ -n "${DRI_LINES}" ]]; then + warn "Found /dev/dri entries, but no renderD node in this output." +else + warn "No /dev/dri entries seen for this process." +fi + +section "4) Check mapped graphics libraries" +info "What this does: reads process memory mappings for GL/EGL/Vulkan/Mesa libraries." +info "Good outcome: matches like libGL, libEGL, libvulkan, mesa, libgbm, libgallium appear." + +echo -e "${DIM}\$ sudo grep -E 'libEGL|libGL|libvulkan|mesa' /proc/\$GHOSTTY_PID/maps | head -n 30${RESET}" +LIB_LINES="$(sudo grep -E 'libEGL|libGL|libvulkan|mesa' "/proc/${GHOSTTY_PID}/maps" | head -n 30 || true)" + +if [[ -n "${LIB_LINES}" ]]; then + echo "${LIB_LINES}" + found_gpu_libs=1 + ok "Graphics stack libraries are mapped into Ghostty." +else + echo "" + warn "No GL/EGL/Vulkan/Mesa mappings matched this filter." +fi + +section "5) Optional probe for software rasterizer vs hardware driver hints" +info "What this does: scans for common software rasterizer strings (llvmpipe/swrast) and driver names." +info "Good outcome: no llvmpipe/swrast matches. Driver-name matches can vary by stack." + +echo -e "${DIM}\$ sudo grep -Ei 'llvmpipe|swrast|iris|radeonsi|nouveau|zink|nvidia|_dri\\.so' /proc/\$GHOSTTY_PID/maps${RESET}" +PROBE_LINES="$(sudo grep -Ei 'llvmpipe|swrast|iris|radeonsi|nouveau|zink|nvidia|_dri\.so' "/proc/${GHOSTTY_PID}/maps" || true)" + +if [[ -n "${PROBE_LINES}" ]]; then + echo "${PROBE_LINES}" + if echo "${PROBE_LINES}" | grep -Eiq 'llvmpipe|swrast'; then + software_rasterizer=1 + warn "Software rasterizer markers detected (llvmpipe/swrast)." + else + ok "Driver-related markers detected and no software rasterizer markers found." + fi +else + ok "No probe matches found. This is often fine; absence of llvmpipe/swrast is usually good." +fi + +section "Final interpretation" +if (( found_process == 1 )); then + ok "Process check: PASS" +else + warn "Process check: no process found initially (script still proceeded once PID was resolved)." +fi + +if (( found_render_node == 1 )); then + ok "Render node check: PASS (/dev/dri/renderD* present)" +else + warn "Render node check: no explicit renderD node found" +fi + +if (( found_gpu_libs == 1 )); then + ok "Graphics library check: PASS" +else + warn "Graphics library check: no matches" +fi + +if (( software_rasterizer == 1 )); then + warn "Software rasterizer check: POSSIBLE software rendering path" +else + ok "Software rasterizer check: no llvmpipe/swrast markers detected" +fi + +echo +if (( found_render_node == 1 && found_gpu_libs == 1 && software_rasterizer == 0 )); then + ok "Overall: strong evidence Ghostty is using GPU acceleration." +else + warn "Overall: mixed signals. Consider running a live GPU activity monitor during terminal redraw stress." +fi + +ok "Summary complete for PID ${GHOSTTY_PID}"