Any Way to Patch GTK3 systemwide

I want to patch GTK3 to disable Client Side Decoration, as of now I’ve found 2 solutions for non-nix systems. The first one is gtk3-nocsd, which LD_Preloads an so file. The second one is gtk3-classic, which patches GTK itself

Are there any way to implement both of them in nixos (eg. gtk3-classic for native apps via gtk patching, and gtk3-nocsd when you can’t patch the GTK apps like with flatpak)?

I found a rejected pull request in regards to gtk3-nocsd gtk3-nocsd: init at 3.0.1 by Abbie5 · Pull Request #70030 · NixOS/nixpkgs · GitHub

I also found a package request for gtk3-classic that hasn’t went anywhere Package request: gtk3-classic · Issue #334803 · NixOS/nixpkgs · GitHub

Someone mentioned about getting it working here but I haven’t managed to get it to patch or build

Thanks in advance

“Someone” saw your post and is happy to report they are still using gtk3-classic (as a “graft” overlay). This works by building gtk3 with gtk3-classic patches and “grafting” them (essentially replacing the gtk3 library reference) in applicable packages. This keeps the building to a minimum (i.e. only gtk3 not the packages that use gtk3).

I’ll try to rip out the applicable parts from my configuration:

The gtk3-classic build and “graft” mechanism (in file gtk3-classic.nix)

{ pkgs, lib, ... }:
let
   # This is a straight copy from upstream (which deprecated this function), as
   # I don't see another way of doing this
   readPathsFromFile = (rootPath: file:
      let
         lines = lib.splitString "\n" (builtins.readFile file);
         removeComments = lib.filter (line: line != "" && !(lib.hasPrefix "#" line));
         relativePaths = removeComments lines;
         absolutePaths = map (path: rootPath + "/${path}") relativePaths;
      in
         absolutePaths);

   # GTK3-classic
   gtk3-classic = pkgs.gtk3.overrideAttrs (old: {
      patches = old.patches ++ (let
         classic = pkgs.fetchFromGitHub {
            owner = "lah7";
            repo = "gtk3-classic";
            rev = old.version;
            sha256 = "sha256-85F8iLyWdBKMfyXYpyHXFYlvptCEQ6FDQ9YXnKSlgKU=";
         };
         quiltSeries = name: src: readPathsFromFile (builtins.path {
            path = src;
            name = (name + "-patches");
            filter = (path: type:
               (type == "regular") &&
               (lib.hasSuffix ".patch" path)
            );
         }) (src + "/series");
      in
         (quiltSeries "gtk3-classic" classic)
      );
   });
in
   pkg: let
      graft = (p: pkgs.replaceDependency {
         drv = p;
         oldDependency = pkgs.gtk3;
         newDependency = gtk3-classic;
      });
   in
      # Add override methods to make graft/replaceDependency act more like a
      # package derivation
      ((graft pkg) // {
         override = (o: graft (pkg.override o));
         overrideDerivation = (od: graft (pkg.overrideDerivation od));
      })

A set of GUI packages that use gtk3 (in your (home-manager) configuration.nix):

let
   guiApps = {
      terminal = {
         scope = "xfce";
         name = "xfce4-terminal";
         toolkit = "gtk3";
      };
      web-browser = {
         name = "firefox";
         toolkit = "gtk3";
      };
   };
in {
   # config = { #pkgs.enable ... etc };
};

The actual overlay graft (in your (home-manager) configuration.nix)

   config = {
      nixpkgs = {
         overlays = [
            # Graft GTK3-classic onto GTK3 applications
            (self: super:
               let
                  graft = import ./gtk3-classic.nix {
                     pkgs = super;
                     lib = super.lib;
                  };
                  gtk3Apps = builtins.filter (app:
                     ((app.toolkit or "") == "gtk3")
                  ) (builtins.attrValues guiApps);
                  gtk3Scopes = super.lib.unique (
                     builtins.catAttrs "scope" gtk3Apps
                  );
                  gtk3Graft = builtins.listToAttrs (builtins.map (app: {
                     name = app.name;
                     value = (graft super.${app.name});
                  }) (builtins.filter (app:
                     (!(app ? scope))
                  ) gtk3Apps));
                  gtk3GraftScoped = scope: (
                     builtins.listToAttrs (builtins.map (app: {
                        name = app.name;
                        # Need to use parent scope ('super' vs. 'ssuper') for graft
                        # to keep from building a new derivation
                        value = (graft super.${app.scope}.${app.name});
                     }) (builtins.filter (app:
                        ((app.scope or "") == scope)
                     ) gtk3Apps))
                  );
               in builtins.listToAttrs (builtins.map (scope: {
                  name = scope;
                  value = let
                     overrideScope = super.${scope}.overrideScope;
                  in
                     overrideScope (sself: ssuper: gtk3GraftScoped scope);
               }) gtk3Scopes) // (gtk3Graft)
            )
         ];
      };


      # other pkg.enable ... etc
   };

I don’t use gtk3-nocsd so can’t say about that one, but I’m sure if its just another set of patches you could modify the above to tack it on.

Thanks,
simplejack

Thank you very much, I will try this as soon as I have some free time, probably this weekend. Marking this as solution for now and I’ll try to get nocsd working too in case someone stumbles on this thread

So just making sure, does every software need the toolkit = "gtk3";, or will it work on every gtk3 application as long as I have the first and third code?