diff --git a/.config/hypr/hyprland.lua b/.config/hypr/hyprland.lua index d5120f0..8f5ce29 100644 --- a/.config/hypr/hyprland.lua +++ b/.config/hypr/hyprland.lua @@ -12,7 +12,9 @@ hl.env("XCURSOR_SIZE", "24") hl.env("LIBVA_DRIVER_NAME", "nvidia") hl.env("XDG_SESSION_TYPE", "wayland") hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia") -hl.env("WLR_DRM_DEVICES", "/dev/dri/card1:/dev/dri/card0") +-- NVIDIA renders the desktop and owns the physical display. AMD remains a +-- fallback device; Waydroid uses NVIDIA through its Venus transport. +hl.env("AQ_DRM_DEVICES", "/dev/dri/nvidia-dgpu:/dev/dri/amd-igpu") hl.env("GDK_SCALE", "1") hl.env("GDK_BACKEND", "wayland,x11") hl.env("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1") @@ -210,13 +212,10 @@ hl.exec_cmd("gsettings set org.gnome.desktop.interface font-antialiasing 'rgba'" hl.exec_cmd("gsettings set org.gnome.desktop.interface font-hinting 'full'") -- DMS 1.5+ owns these fragments. In particular, Display Settings rewrites outputs.lua. --- require("dms.colors") +require("dms/colors") require("dms/outputs") --- require("dms.layout") -- require("dms.cursor") -- require("dms.binds") --- require("dms.binds-user") --- require("dms.windowrules") require("dms/layout") require("dms/binds-user") require("dms/windowrules") diff --git a/.config/zed/settings.json b/.config/zed/settings.json index 52eaad1..7c213e8 100644 --- a/.config/zed/settings.json +++ b/.config/zed/settings.json @@ -20,9 +20,9 @@ "light": "One Light", "dark": "One Dark", }, - "minimap": { - "show": "auto" - }, +// "minimap": { +// "show": "auto" +// }, "format_on_save": "modifications", "diagnostics": { "inline": { diff --git a/flake.lock b/flake.lock index bebaa27..099c7a6 100644 --- a/flake.lock +++ b/flake.lock @@ -2211,6 +2211,8 @@ "rusic": "rusic", "spacebot-src": "spacebot-src", "spacedrive-src": "spacedrive-src", + "waydroid-nvidia-nix": "waydroid-nvidia-nix", + "waydroid-script": "waydroid-script", "zed": "zed", "zen-browser": "zen-browser" } @@ -2511,6 +2513,21 @@ "type": "github" } }, + "systems_8": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "treefmt-nix": { "inputs": { "nixpkgs": "nixpkgs_3" @@ -2617,6 +2634,65 @@ "type": "github" } }, + "waydroid-nvidia-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "wnv-src": "wnv-src" + }, + "locked": { + "lastModified": 1784900959, + "narHash": "sha256-wh11g5RcmQR/vSPBG1q08wA9F9ZmS5IkJ97+OUVo9RU=", + "owner": "yigexuanmu", + "repo": "waydroid-nvidia-nix", + "rev": "180697edb1ea2c53fed49d6a07d20a39af563083", + "type": "github" + }, + "original": { + "owner": "yigexuanmu", + "repo": "waydroid-nvidia-nix", + "type": "github" + } + }, + "waydroid-script": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "systems": "systems_8" + }, + "locked": { + "lastModified": 1767603363, + "narHash": "sha256-zSHZlhHJHWZRE3I5pYWhD4o8aNpa8rTiEtl2qJTuRjw=", + "owner": "casualsnek", + "repo": "waydroid_script", + "rev": "d5289cfd8929e86e7f0dc89ecadcef8b66930eec", + "type": "github" + }, + "original": { + "owner": "casualsnek", + "repo": "waydroid_script", + "type": "github" + } + }, + "wnv-src": { + "flake": false, + "locked": { + "lastModified": 1784896711, + "narHash": "sha256-D1qG0+t9+6bdIEGZw6LaiIsU3yzASwY/VF0U2PM1Pcw=", + "owner": "Shiro836", + "repo": "waydroid-nvidia", + "rev": "4239f9055d9ecee765cf6890227cf4d1d9f7b13e", + "type": "github" + }, + "original": { + "owner": "Shiro836", + "ref": "v0.1.2", + "repo": "waydroid-nvidia", + "type": "github" + } + }, "xdph": { "inputs": { "hyprland-protocols": [ diff --git a/flake.nix b/flake.nix index 557ee28..3c2060f 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,13 @@ { inputs = { + waydroid-script = { + url = "github:casualsnek/waydroid_script"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + waydroid-nvidia-nix = { + url = "github:yigexuanmu/waydroid-nvidia-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; paseo = { url = "github:getpaseo/paseo"; inputs.nixpkgs.follows = "nixpkgs"; diff --git a/pkgs/dms-settings-reopen.patch b/pkgs/dms-settings-reopen.patch new file mode 100644 index 0000000..5a859dd --- /dev/null +++ b/pkgs/dms-settings-reopen.patch @@ -0,0 +1,77 @@ +--- a/quickshell/Services/PopoutService.qml ++++ b/quickshell/Services/PopoutService.qml +@@ -388,8 +388,40 @@ + property string _settingsPendingTab: "" + property int _settingsPendingTabIndex: -1 + ++ property double _settingsShownAt: 0 ++ ++ function _settingsWindowDead() { ++ if (!settingsModal?.visible) ++ return false; ++ // Toplevel registration is async; a freshly shown window looks dead. ++ if (Date.now() - _settingsShownAt < 2000) ++ return false; ++ const settingsTitle = I18n.tr("Settings", "settings window title"); ++ for (const toplevel of ToplevelManager.toplevels.values) { ++ if (toplevel.title === "Settings" || toplevel.title === settingsTitle) ++ return false; ++ } ++ return true; ++ } ++ ++ function _rebuildDeadSettings() { ++ settingsModal.visible = false; ++ settingsModal = null; ++ settingsModalLoader.active = false; ++ _settingsWantsOpen = true; ++ _settingsWantsToggle = false; ++ Qt.callLater(() => { ++ if (settingsModalLoader) ++ settingsModalLoader.activeAsync = true; ++ }); ++ } ++ + function openSettings() { + if (settingsModal) { ++ if (_settingsWindowDead()) { ++ _rebuildDeadSettings(); ++ return; ++ } + settingsModal.show(); + } else if (settingsModalLoader) { + _settingsWantsOpen = true; +@@ -399,8 +431,23 @@ + } + } + ++ Connections { ++ target: root.settingsModal ++ function onVisibleChanged() { ++ if (root.settingsModal?.visible) ++ root._settingsShownAt = Date.now(); ++ } ++ } ++ + function openSettingsWithTab(tabName: string) { + if (settingsModal) { ++ if (_settingsWindowDead()) { ++ _settingsPendingTab = tabName; ++ _rebuildDeadSettings(); ++ return; ++ } + settingsModal.showWithTabName(tabName); + return; + } +@@ -413,6 +460,11 @@ + + function openSettingsWithTabIndex(tabIndex: int) { + if (settingsModal) { ++ if (_settingsWindowDead()) { ++ _settingsPendingTabIndex = tabIndex; ++ _rebuildDeadSettings(); ++ return; ++ } + settingsModal.showWithTab(tabIndex); + return; + } diff --git a/servers/fern/configuration.nix b/servers/fern/configuration.nix index e0cc8a5..f1fe3a6 100644 --- a/servers/fern/configuration.nix +++ b/servers/fern/configuration.nix @@ -3,11 +3,34 @@ lib, config, reenv, + waydroid-script, + waydroid-nvidia-nix, ... }: let system = pkgs.stdenv.hostPlatform.system; + waydroidNvidia = + (waydroid-nvidia-nix.packages.${system}.waydroid-nvidia-full).overrideAttrs + (_: { + postFixup = '' + wrapProgram $out/bin/waydroid \ + --prefix PATH : ${lib.makeBinPath [ pkgs.lxc pkgs.kmod pkgs.util-linux ]} + wrapProgram $out/lib/waydroid/data/scripts/waydroid-net.sh \ + --prefix PATH : ${ + lib.makeBinPath [ + pkgs.lxc + pkgs.kmod + pkgs.iptables + pkgs.nftables + pkgs.iproute2 + pkgs.dnsmasq + pkgs.gawk + pkgs.getent + ] + } + ''; + }); ninfs = pkgs.python3Packages.buildPythonApplication { pname = "ninfs"; @@ -86,7 +109,16 @@ let ); in { + imports = [ + waydroid-nvidia-nix.nixosModules.waydroid-nvidia + ]; + services.hardware.openrgb.enable = true; + # The split RemoteDesktop/ScreenCast portal session emits malformed D-Bus + # traffic with XDPH 1.4.1. Override capture without replacing Sunshine's + # mutable web-UI configuration. + systemd.user.services.sunshine.serviceConfig.ExecStart = + lib.mkForce ''"/run/wrappers/bin/sunshine" "capture=wlr"''; # Hyprland's FALLBACK output has no capturable framebuffer. Keep a real # headless output available so Sunshine can stream when no monitor is attached. @@ -181,6 +213,7 @@ in }; environment.systemPackages = (with pkgs; [ + wl-clipboard mtkclient blender android-studio-full @@ -209,6 +242,7 @@ in wireshark-cli avalonia-ilspy ]) + ++ [ waydroid-script.packages.${system}.default ] ++ (with reenv.packages.${system}; [ bindiff ghidra-with-extensions @@ -336,7 +370,41 @@ in nvidiaSettings = true; }; services.xserver.videoDrivers = [ "nvidia" ]; + # Stable device names for Hyprland's AMD-primary multi-GPU renderer. + services.udev.extraRules = '' + SUBSYSTEM=="drm", KERNEL=="card*", KERNELS=="0000:11:00.0", SYMLINK+="dri/amd-igpu" + SUBSYSTEM=="drm", KERNEL=="card*", KERNELS=="0000:01:00.0", SYMLINK+="dri/nvidia-dgpu" + ''; # powerManagement.enable = true; hardware.nvidia-container-toolkit.enable = true; virtualisation.docker.daemon.settings.features.cdi = true; + + # Keep the host resolver off Waydroid's 192.168.240.1:53 listener. + services.dnsmasq.settings = { + listen-address = "127.0.0.1"; + bind-interfaces = true; + }; + + services.waydroid-nvidia = { + enable = true; + package = waydroidNvidia; + refreshRate = 200; + }; + + virtualisation.waydroid = { + enable = true; + package = waydroidNvidia; + }; + + # Native-bridge installers reuse these global binfmt names. Container + # restarts do not clear them, so switching libndk/libhoudini otherwise leaves + # stale interpreters pointing at files removed from the Android overlay. + systemd.services.waydroid-container.preStart = '' + for handler in arm_exe arm_dyn arm64_exe arm64_dyn; do + handlerPath="/proc/sys/fs/binfmt_misc/$handler" + if [[ -e "$handlerPath" ]]; then + echo -1 > "$handlerPath" + fi + done + ''; } diff --git a/servers/ui-mode/configuration.nix b/servers/ui-mode/configuration.nix index 5fd194a..ef51f9e 100644 --- a/servers/ui-mode/configuration.nix +++ b/servers/ui-mode/configuration.nix @@ -154,7 +154,6 @@ fonts.enableDefaultPackages = true; # https://github.com/NixOS/nixpkgs/issues/409986 environment.etc."xdg/menus/applications.menu".source = ./dolphin.menu; - environment.etc."pam_init".source = "${pkgs.kdePackages.kwallet-pam}/libexec/pam_kwallet_init"; users.users.dan = { isNormalUser = true; @@ -301,6 +300,20 @@ package = pkgs.kdePackages.kwallet-pam; }; }; + # pam_kwallet starts ksecretd before the UWSM session is ready. Complete its + # handshake once graphical-session.target has the imported session environment. + systemd.user.services.plasma-kwallet-pam = { + description = "Unlock KWallet from PAM credentials"; + wantedBy = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + serviceConfig = { + ExecStart = "${pkgs.kdePackages.kwallet-pam}/libexec/pam_kwallet_init"; + Type = "simple"; + Slice = "background.slice"; + Restart = "no"; + }; + }; + services.logind.settings.Login = { HandlePowerKey = "suspend"; diff --git a/servers/ui-mode/home.nix b/servers/ui-mode/home.nix index c3e8595..6eb59c6 100644 --- a/servers/ui-mode/home.nix +++ b/servers/ui-mode/home.nix @@ -23,6 +23,13 @@ }: let system = pkgs.stdenv.hostPlatform.system; + dmsShell = dms.packages.${system}.dms-shell.overrideAttrs (oldAttrs: { + postPatch = (oldAttrs.postPatch or "") + '' + if [[ -f quickshell/Services/PopoutService.qml ]]; then + patch -p1 < ${../../pkgs/dms-settings-reopen.patch} + fi + ''; + }); unstable = import nixpkgs-unstable { system = pkgs.system; @@ -470,6 +477,7 @@ in # programs.dsearch.enable = true; programs.dank-material-shell = { enable = true; + package = dmsShell; systemd.enable = true; # niri = { # enableKeybinds = false; # Sets static preset keybinds