Howto run commands automatically with nixGL?
I much appreciate the effort you put into this package. It made me able to use nix on ubuntu.
Could you shed light on how to solve the following? I normally start programs via the gnome shell launcher.
- Is there a way to automatically make GL programs such as firefox start with nixGL?
- How can one figure out if a program benefits from nixGL?
In the firefox example, I went on to investigate due to it being extremely slow. That's not something I normally do.
Thank you for your feedback, it is highly appreciated.
Is there a way to automatically make GL programs such as firefox start with nixGL?
You can create your own derivation and wrap them with nixGL. I should document that or either write a magic wrapper which does that automatically.
In theory, you can also run your shell launcher in nixGL and environment variables should propagate correctly. But it may be a bit more work because you'll have to change the way you start your graphical session.
What is your opinion on wrapping .desktop files?
For example, have a script in .profile that replaces copies the .desktop file to a new directory and prepends the Exec line with nixGL
I solved it for now by adding the export part in the nixGL script to my $HOME/.profile.
In my case that's:
export LD_LIBRARY_PATH=/nix/store/xl3cr8kwlpi0j7il0zx4cb6sw05wx40g-libglvnd-1.2.0/lib:/nix/store/lhfyarm78k3lswp6xcvp51s9x4j6baq0-nvidia-440.64/lib:/nix/store/k8x3i9r3lyx4sv0wls45akvkk23bflg7-nvidia-440.64-lib32/lib:/nix/store/aw33ya4m2qmfjhvrxlvlw9gl28ixviya-libglvnd-1.2.0/lib:${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
It's not perfect, This will add these paths every time .profile is executed.
Which means, logging out and in without a reboot will make the $LD_LIBRARY_PATH grow.
The scripts in this gist switch automatically between intel and nivida drivers:
https://gist.github.com/rvanlaar/5ca5ed3cd4314c12e8518b5672ae3774
Thank you. It is appreciated.
I had a look at your script and indeed that's an interesting approach. However I'm afraid of the robusntess of it. It needs the egpu utility installed (which may not be installed by nix because it may need host system specific configuration, I don't know).
Another problem of having a "global" nixGL installation (so, basically, something in .profile as you suggest) is that you will have the problem of installing a nixGL which is not compatible with the nix program you are trying to run. This may not be a problem for users of nix which are only using a fixed channel, but if you take my example, I have hundreds of different nix project on my computer, all pinned to a potential different nixpkgs clone, incompatible with my global nixpkgs clone.
However I'm not saying that your solution is unacceptable because it does not fix all issues. I do think that there is value in your proposal because I do think that improving the quality of life of a few users, even if that's not all users, is a good reason. I just don't want to complexify nixGL for the other users (those for which your proposal won't work).
Let me think about it.
The points you raise are valid, those are issues I have with my current script as well.
It's not robust enough yet:
- It needs manual intervention when nixGL is updated and
- the egpu switcher script is very specific to my situation (as you already mentioned)
What could be an option is to have nixGL scripts that can be sourced from .profile, so have them without the "$@" at the end. That would solve 1.
The documentation could also show the .profile route with an example about how to switch between exports. People could present their own scripts. Which would make item 2 a bit more bearable for end users.
You have really good point here. Indeed, nixGL may generate sourceable scripts usable in .profile and even in the nixGLXXX command.
I'll give a try at this refactoring when time is available. Thank you!
That would be great. I really love how open you are to improvements to nixGL.
Just had this idea: Have a minimal OpenGL program in nixGL that crashes without the proper libs?
It could be used as follows (in pseudo code):
nixGLIntel test_gl
intel=$?
nixGLNvidia test_gl
nvida=$?
if [ intel -eq 0 ]; then
source /path/to/nixGLintel
elif [ nvidia -eq 0]; then
source /path/to/nixGLnvidia
fi
# Test if the paths are set correctly
test_gl
status=$?
if [ status -gt 0 ]; then
echo "Problem loading nixGL scripts."
exit 1
fi
This is what I do for reference on a non-nixos system:
[Desktop Entry]
Type=Application
TryExec=/home/michael/.nix-profile/bin/alacritty
MimeType=
Exec= /home/michael/.nix-profile/bin/nixGL /home/michael/.nix-profile/bin/alacritty %F
Icon=Alacritty
Terminal=false
Categories=System;TerminalEmulator;
Name=Alacritty
GenericName=Terminal
Comment=A cross-platform, GPU enhanced terminal emulator
StartupWMClass=Alacritty
Actions=New;
X-Desktop-File-Install-Version=0.24
[Desktop Action New]
Name=New Terminal
Exec=alacritty
An update. I've stopped using nix packages for graphical applications.
Two things changed,
- I moved to an AMD graphics cards and
- the mesa versions for ubuntu and nix-packages diverged
Having the nixGLIntel exports in my .profile per default resulted in a broken desktop environment. It crashed after login.
It was too much hassle to always start the program with nixGLIntel $program.
I'm also looking forward again to having the latest VScode and firefox working together with gnome-shell again.
What is your opinion on wrapping
.desktopfiles?For example, have a script in .profile that replaces copies the
.desktopfile to a new directory and prepends the Exec line withnixGL
I was also interested in having working .desktop files. The following simple wrapper, adapted from here, meets my requirements. It that just wraps the binaries themselves, and no change to .desktop files is needed as those will transparently call wrapped binaries:
# a `home.nix` that is fed into home-manager
{pkgs, lib, ...}:
let
# ...
nixGLWrap = pkg: pkgs.runCommand "${pkg.name}-nixgl-wrapper" {} ''
mkdir $out
ln -s ${pkg}/* $out
rm $out/bin
mkdir $out/bin
for bin in ${pkg}/bin/*; do
wrapped_bin=$out/bin/$(basename $bin)
echo "exec ${lib.getExe pkgs.nixgl.nixGLIntel} $bin \$@" > $wrapped_bin
chmod +x $wrapped_bin
done
'';
in {
# ...
programs.alacritty = {
enable = true;
package = nixGLWrap pkgs.alacritty;
# ...
};
}
@hab25 solution worked great for me.
I have replaced nixGLIntel with nixGLDefault. For future records, you can directly wrap the packages in home.packages as below:
# a `home.nix` that is fed into home-manager
{config, pkgs, lib, ...}:
let
# ...
nixgl = import <nixgl> {} ;
nixGLWrap = pkg: pkgs.runCommand "${pkg.name}-nixgl-wrapper" {} ''
mkdir $out
ln -s ${pkg}/* $out
rm $out/bin
mkdir $out/bin
for bin in ${pkg}/bin/*; do
wrapped_bin=$out/bin/$(basename $bin)
echo "exec ${lib.getExe nixgl.auto.nixGLDefault} $bin \$@" > $wrapped_bin
chmod +x $wrapped_bin
done
'';
in {
# ...
home.packages = [
nixgl.auto.nixGLDefault
(nixGLWrap pkgs.kitty)
(nixGLWrap pkgs.inkscape)
# ...
];
}
I'm not sure if there will be unintended consequences, such as reported in Issue #116. My use case, is to use Home Manager as a replacement for brew. So far, so good.
@gzagatti
There is a bug in the code I previously shared, it makes it so you can't pass an argument that contains spaces to the wrapped program (e.g. mpv "some video file whose name contains spaces"), due to bad word splitting.
To fix it, change, in the code,
echo "exec ${lib.getExe nixgl.auto.nixGLDefault} $bin \$@" > $wrapped_bin
to
echo "exec ${lib.getExe nixgl.auto.nixGLDefault} $bin \"\$@\"" > $wrapped_bin
(that is, \$@ to \"\$@\")
I'm not sure if there will be unintended consequences, such as reported in Issue https://github.com/guibou/nixGL/issues/116.
https://github.com/guibou/nixGL/issues/116 does happen, but it has been tolerable; the only time I've noticed it is when trying to call google-chrome-stable from a nixGLWrapped alacritty; chrome outputs some errors about bad driver paths but still seems to work correctly.
I modified the wrapper to simply replace the desktop files of packages and symlink every other file, so now all of the menus just work.
To launch programs from a terminal you still need to use the prefix nixGLIntel, or you can set up some aliases in your ~/.bashrc
The below bash script is to install packages as root so that all users have access to the installed programs.
If you have a Nvidia card, you will need to modify the script and add the impure flag.
I have this running on Ubuntu, but it should work on any other distro. You will need to log out and then back in again for the desktop files to appear in your menu.
This could help out with #90 and #114
#!/bin/bash
curl -sL -o nix-installer https://install.determinate.systems/nix/nix-installer-x86_64-linux
chmod +x nix-installer
sudo ./nix-installer install
sudo -i nix-channel --add https://github.com/nix-community/home-manager/archive/master.tar.gz home-manager
sudo -i nix-channel --update
sudo -i nix-shell '<home-manager>' -A install
sudo -i home-manager init
sudo tee /root/.config/home-manager/home.nix <<'EOT'
{config, pkgs, nixgl, lib, ...}:
let
nixGLWrap = pkg: pkgs.runCommand "${pkg.name}-nixgl-wrapper" {} ''
mkdir $out
for folder in $(cd ${pkg} && find -L -type d -links 2); do
folder=$(echo $folder | cut -c 2-)
mkdir -p "$out$folder"
done;
for file in $(cd ${pkg} && find -L); do
file=$(echo $file | cut -c 2-)
if [[ -f ${pkg}$file ]]; then
if [[ $file == *.desktop ]]; then
cp "${pkg}$file" "$out$file"
sed -i 's|TryExec=.*||' "$out$file"
sed -i 's|Exec=|Exec=nixGLIntel |' "$out$file"
else
ln -s "${pkg}$file" "$out$file"
fi
fi
done;
'';
in {
home.username = "root";
home.homeDirectory = "/root";
home.stateVersion = "22.11";
home.packages = [
nixgl.nixGLIntel
(nixGLWrap pkgs.rustdesk)
(nixGLWrap pkgs.rclone)
(nixGLWrap pkgs.imagemagick)
(nixGLWrap pkgs.lapce)
(nixGLWrap pkgs.olive-editor)
(nixGLWrap pkgs.gimp-with-plugins)
(nixGLWrap pkgs.firefox)
(nixGLWrap pkgs.vlc)
(nixGLWrap pkgs.ungoogled-chromium)
(nixGLWrap pkgs.libreoffice-fresh)
(nixGLWrap pkgs.thunderbird)
(nixGLWrap pkgs.fsearch)
(nixGLWrap pkgs.obsidian)
(nixGLWrap pkgs.stacer)
(nixGLWrap pkgs.libsForQt5.kalk)
(nixGLWrap pkgs.libsForQt5.spectacle)
(nixGLWrap pkgs.libsForQt5.okular)
(nixGLWrap pkgs.libsForQt5.gwenview)
(nixGLWrap pkgs.libsForQt5.ark)
];
home.pointerCursor = {
name = "breeze_cursors";
package = pkgs.libsForQt5.breeze-icons;
size = 24;
x11 = {
enable = true;
defaultCursor = "breeze_cursors";
};
};
gtk = {
enable = true;
iconTheme = {
package = pkgs.libsForQt5.breeze-icons;
name = "breeze";
};
theme = {
package = pkgs.libsForQt5.breeze-gtk;
name = "Breeze";
};
};
programs.home-manager.enable = true;
}
EOT
sudo tee /root/.config/home-manager/flake.nix <<'EOT'
{
description = "Home Manager configuration of root";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
nixGL = {
url = "github:guibou/nixGL";
flake = false;
};
};
outputs = { nixpkgs, home-manager, nixGL, ... }:
let
system = "x86_64-linux";
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
nixgl = import nixGL {
inherit pkgs;
};
in {
homeConfigurations."root" = home-manager.lib.homeManagerConfiguration {
inherit pkgs;
extraSpecialArgs = {
inherit nixgl;
};
modules = [ ./home.nix ];
};
};
}
EOT
sudo -i home-manager switch
sudo tee /etc/X11/Xsession.d/10nixshare <<'EOT'
export XDG_DATA_DIRS="$XDG_DATA_DIRS:/root/.nix-profile/share"
export XCURSOR_PATH="$XCURSOR_PATH:/root/.nix-profile/share/icons"
EOT
sudo tee /etc/profile.d/nixshare.sh <<'EOT'
export XDG_DATA_DIRS="$XDG_DATA_DIRS:/root/.nix-profile/share"
export XCURSOR_PATH="$XCURSOR_PATH:/root/.nix-profile/share/icons"
EOT
sudo tee /etc/environment.d/nixshare.conf <<'EOT'
XDG_DATA_DIRS=${XDG_DATA_DIRS:+$XDG_DATA_DIRS:}/root/.nix-profile/share
XCURSOR_PATH=${XCURSOR_PATH:+$XCURSOR_PATH:}/root/.nix-profile/share/icons
EOT
Thanks a lot for the nixGLWrap solution, it works very well! In case you also started to get the following warning, I found a way to get rid of the warning:
trace: warning: getExe: Package nixGL does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo "bar".
My new code:
nixGLWrap = pkg: pkgs.runCommand "${pkg.name}-nixgl-wrapper" {} ''
mkdir $out
ln -s ${pkg}/* $out
rm $out/bin
mkdir $out/bin
for bin in ${pkg}/bin/*; do
wrapped_bin=$out/bin/$(basename $bin)
echo "exec ${lib.getExe' nixGL.auto.nixGLDefault "nixGL"} $bin \"\$@\"" > $wrapped_bin
chmod +x $wrapped_bin
done
'';
So basically just replace lib.getExe nixgl.auto.nixGLDefault with lib.getExe' nixGL.auto.nixGLDefault "nixGL".
These pointers were helpful. I'm a Nix beginner but I needed to customize additional command line arguments. I based on the cookbook and am now using:
nixGLWrap = let
wrap = pkg: program: args: pkgs.writeShellScriptBin program ''
exec ${pkgs.nixgl.nixGLIntel}/bin/nixGLIntel ${pkg}/bin/${program} ${args} "$@"
'';
in
pkg: programArgs: pkgs.symlinkJoin {
name = "${pkg.name}-nixgl-wrapper";
paths = (lib.mapAttrsToList (wrap pkg) programArgs) ++ [ pkg ];
};
Used like:
(nixGLWrap pkgs.ungoogled-chromium {
chromium = "--ozone-platform=wayland --enable-features=TouchpadOverscrollHistoryNavigation";
})