diff --git a/.gitignore b/.gitignore
index abd4567..ab8439a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 result
 *.swp
-*.qcow2
\ No newline at end of file
+*.qcow2
+.idea/
\ No newline at end of file
diff --git a/README.md b/README.md
index 3424b58..a68b959 100644
--- a/README.md
+++ b/README.md
@@ -10,31 +10,6 @@ nixos-rebuild switch --flake flake_path_directory#hostname
 nix flake update --extra-experimental-features "nix-command flakes"
 ```
 
-## Configure VM
-
-Configure VM
-```nix
-users.users.<user>.initialPassword = "<password>";
-virtualisation.vmVariant = {
-  # following configuration is added only when building VM with build-vm
-  virtualisation = {
-    memorySize = <RAM in MiB>; # Use 8192MiB memory.
-    cores = <CPU Core number>;
-    # And more here https://github.com/NixOS/nixpkgs/blob/nixos-unstable/nixos/modules/virtualisation/qemu-vm.nix     
-  };
-};
-```
-
-Build
-```bash
-nixos-rebuild build-vm --flake .#nixos-test
-```
-
-Run
-```bash
-./result/bin/run-nixos-vm-vm
-```
-
 ## Show changements between revisions
 
 
@@ -72,36 +47,6 @@ error: cached failure of attribute 'nixosConfigurations.perso-desktop.config.sys
 sudo rm -fr /root/.cache/nix/
 ```
 
-## If package is marked as insecure
-
-Example:
-
-> error: Package 'nix-2.16.2' in /nix/store/nra828scc8qs92b9pxra5csqzffb6hpl-source/pkgs/tools/package-management/nix/default.nix:229 is marked as insecure, refusing to evaluate.
->
-> Known issues:
-> - CVE-2024-27297
-
-```bash
-nix path-info -r /run/current-system | grep nix-2.16.2
-```
-Result:
-> [...]
->
-> /nix/store/g4ss2h40n3j37bq20x1qw5s7nl82lch5-nix-2.16.2
->
-> [...]
-
-```bash
-nix-store -q --referrers /nix/store/g4ss2h40n3j37bq20x1qw5s7nl82lch5-nix-2.16.2
-```
-Result:
-> /nix/store/g4ss2h40n3j37bq20x1qw5s7nl82lch5-nix-2.16.2
->
-> /nix/store/72pfc05339izcwqhlbs8441brrdasas7-nix-2.16.2-dev
->
-> /nix/store/ln2z5d5izn8icm3wx94ci13ad19lzjhr-nixd-1.2.3
-
-nixd is not up to date and require nix 2.16.2
 
 ## To limit resources use during build
 
diff --git a/flake.lock b/flake.lock
index 326a4af..6726d62 100644
--- a/flake.lock
+++ b/flake.lock
@@ -12,11 +12,11 @@
         "systems": "systems"
       },
       "locked": {
-        "lastModified": 1716561646,
-        "narHash": "sha256-UIGtLO89RxKt7RF2iEgPikSdU53r6v/6WYB0RW3k89I=",
+        "lastModified": 1723293904,
+        "narHash": "sha256-b+uqzj+Wa6xgMS9aNbX4I+sXeb5biPDi39VgvSFqFvU=",
         "owner": "ryantm",
         "repo": "agenix",
-        "rev": "c2fc0762bbe8feb06a2e59a364fa81b3a57671c9",
+        "rev": "f6291c5935fdc4e0bef208cfc0dcab7e3f7a1c41",
         "type": "github"
       },
       "original": {
@@ -47,6 +47,203 @@
         "type": "github"
       }
     },
+    "doom-emacs": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1662497747,
+        "narHash": "sha256-4n7E1fqda7cn5/F2jTkOnKw1juG6XMS/FI9gqODL3aU=",
+        "owner": "doomemacs",
+        "repo": "doomemacs",
+        "rev": "3853dff5e11655e858d0bfae64b70cb12ef685ac",
+        "type": "github"
+      },
+      "original": {
+        "owner": "doomemacs",
+        "repo": "doomemacs",
+        "rev": "3853dff5e11655e858d0bfae64b70cb12ef685ac",
+        "type": "github"
+      }
+    },
+    "doom-snippets": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1694887483,
+        "narHash": "sha256-KlKhruPSLPSKqUnr5/U65arm16VrY9ORzm+XKNZhpTQ=",
+        "owner": "doomemacs",
+        "repo": "snippets",
+        "rev": "f022984ee1318a4015d5d081b3c3dab5a60dc6ff",
+        "type": "github"
+      },
+      "original": {
+        "owner": "doomemacs",
+        "repo": "snippets",
+        "type": "github"
+      }
+    },
+    "emacs-overlay": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1676366521,
+        "narHash": "sha256-i4UAY8t9Au9SJtsgYppa3NHSVf1YkV6yqnNIQd+Km4g=",
+        "owner": "nix-community",
+        "repo": "emacs-overlay",
+        "rev": "c16be6de78ea878aedd0292aa5d4a1ee0a5da501",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "emacs-overlay",
+        "rev": "c16be6de78ea878aedd0292aa5d4a1ee0a5da501",
+        "type": "github"
+      }
+    },
+    "emacs-so-long": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1575031854,
+        "narHash": "sha256-xIa5zO0ZaToDrec1OFjBK6l39AbA4l/CE4LInVu2hi0=",
+        "owner": "hlissner",
+        "repo": "emacs-so-long",
+        "rev": "ed666b0716f60e8988c455804de24b55919e71ca",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hlissner",
+        "repo": "emacs-so-long",
+        "type": "github"
+      }
+    },
+    "evil-escape": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1588439096,
+        "narHash": "sha256-aB2Ge5o/93B18tPf4fN1c+O46CNh/nOqwLJbox4c8Gw=",
+        "owner": "hlissner",
+        "repo": "evil-escape",
+        "rev": "819f1ee1cf3f69a1ae920e6004f2c0baeebbe077",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hlissner",
+        "repo": "evil-escape",
+        "type": "github"
+      }
+    },
+    "evil-markdown": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1626852210,
+        "narHash": "sha256-HBBuZ1VWIn6kwK5CtGIvHM1+9eiNiKPH0GUsyvpUVN8=",
+        "owner": "Somelauw",
+        "repo": "evil-markdown",
+        "rev": "8e6cc68af83914b2fa9fd3a3b8472573dbcef477",
+        "type": "github"
+      },
+      "original": {
+        "owner": "Somelauw",
+        "repo": "evil-markdown",
+        "type": "github"
+      }
+    },
+    "evil-org-mode": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1607203864,
+        "narHash": "sha256-JxwqVYDN6OIJEH15MVI6XOZAPtUWUhJQWHyzcrUvrFg=",
+        "owner": "hlissner",
+        "repo": "evil-org-mode",
+        "rev": "a9706da260c45b98601bcd72b1d2c0a24a017700",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hlissner",
+        "repo": "evil-org-mode",
+        "type": "github"
+      }
+    },
+    "evil-quick-diff": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1575189609,
+        "narHash": "sha256-oGzl1ayW9rIuq0haoiFS7RZsS8NFMdEA7K1BSozgnJU=",
+        "owner": "rgrinberg",
+        "repo": "evil-quick-diff",
+        "rev": "69c883720b30a892c63bc89f49d4f0e8b8028908",
+        "type": "github"
+      },
+      "original": {
+        "owner": "rgrinberg",
+        "repo": "evil-quick-diff",
+        "type": "github"
+      }
+    },
+    "explain-pause-mode": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1595842060,
+        "narHash": "sha256-++znrjiDSx+cy4okFBBXUBkRFdtnE2x+trkmqjB3Njs=",
+        "owner": "lastquestion",
+        "repo": "explain-pause-mode",
+        "rev": "2356c8c3639cbeeb9751744dbe737267849b4b51",
+        "type": "github"
+      },
+      "original": {
+        "owner": "lastquestion",
+        "repo": "explain-pause-mode",
+        "type": "github"
+      }
+    },
+    "flake-compat": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1673956053,
+        "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
+    "flake-utils": {
+      "inputs": {
+        "systems": "systems_2"
+      },
+      "locked": {
+        "lastModified": 1694529238,
+        "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "format-all": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1581716637,
+        "narHash": "sha256-ul7LCe60W8TIvUmUtZtZRo8489TK9iTPDsLHmzxY57M=",
+        "owner": "lassik",
+        "repo": "emacs-format-all-the-code",
+        "rev": "47d862d40a088ca089c92cd393c6dca4628f87d3",
+        "type": "github"
+      },
+      "original": {
+        "owner": "lassik",
+        "repo": "emacs-format-all-the-code",
+        "rev": "47d862d40a088ca089c92cd393c6dca4628f87d3",
+        "type": "github"
+      }
+    },
     "home-manager": {
       "inputs": {
         "nixpkgs": [
@@ -54,11 +251,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1716736760,
-        "narHash": "sha256-h3RmnNknKYtVA+EvUSra6QAwfZjC2q1G8YA7W0gat8Y=",
+        "lastModified": 1726036828,
+        "narHash": "sha256-ZQHbpyti0jcAKnwQY1lwmooecLmSG6wX1JakQ/eZNeM=",
         "owner": "nix-community",
         "repo": "home-manager",
-        "rev": "5d151429e1e79107acf6d06dcc5ace4e642ec239",
+        "rev": "8a1671642826633586d12ac3158e463c7a50a112",
         "type": "github"
       },
       "original": {
@@ -67,6 +264,52 @@
         "type": "github"
       }
     },
+    "nix-doom-emacs": {
+      "inputs": {
+        "doom-emacs": "doom-emacs",
+        "doom-snippets": "doom-snippets",
+        "emacs-overlay": "emacs-overlay",
+        "emacs-so-long": "emacs-so-long",
+        "evil-escape": "evil-escape",
+        "evil-markdown": "evil-markdown",
+        "evil-org-mode": "evil-org-mode",
+        "evil-quick-diff": "evil-quick-diff",
+        "explain-pause-mode": "explain-pause-mode",
+        "flake-compat": "flake-compat",
+        "flake-utils": "flake-utils",
+        "format-all": "format-all",
+        "nix-straight": [
+          "nix-straight"
+        ],
+        "nixpkgs": [
+          "nixpkgs"
+        ],
+        "nose": "nose",
+        "ob-racket": "ob-racket",
+        "org": "org",
+        "org-contrib": "org-contrib",
+        "org-yt": "org-yt",
+        "php-extras": "php-extras",
+        "revealjs": "revealjs",
+        "rotate-text": "rotate-text",
+        "sln-mode": "sln-mode",
+        "ts-fold": "ts-fold",
+        "ws-butler": "ws-butler"
+      },
+      "locked": {
+        "lastModified": 1701264882,
+        "narHash": "sha256-MBXR7x7Ua8qystlGr+lenwjQd7dsFNFpEFmtHhh10zM=",
+        "owner": "nix-community",
+        "repo": "nix-doom-emacs",
+        "rev": "f7413022370f24bb53cb450bfb2803233510113e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "nix-doom-emacs",
+        "type": "github"
+      }
+    },
     "nix-flatpak": {
       "locked": {
         "lastModified": 1711997201,
@@ -83,13 +326,30 @@
         "type": "github"
       }
     },
+    "nix-straight": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1696948727,
+        "narHash": "sha256-6fQamWVIyeLoFSJl1WKcIl+LUdZluzFla4H+4Z5Cv2E=",
+        "owner": "codingkoi",
+        "repo": "nix-straight.el",
+        "rev": "c64edbf49598453bd85dae1acef9a0f9d294185d",
+        "type": "github"
+      },
+      "original": {
+        "owner": "codingkoi",
+        "ref": "codingkoi/apply-librephoenixs-fix",
+        "repo": "nix-straight.el",
+        "type": "github"
+      }
+    },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1716509168,
-        "narHash": "sha256-4zSIhSRRIoEBwjbPm3YiGtbd8HDWzFxJjw5DYSDy1n8=",
+        "lastModified": 1725983898,
+        "narHash": "sha256-4b3A9zPpxAxLnkF9MawJNHDtOOl6ruL0r6Og1TEDGCE=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "bfb7a882678e518398ce9a31a881538679f6f092",
+        "rev": "1355a0cbfeac61d785b7183c0caaec1f97361b43",
         "type": "github"
       },
       "original": {
@@ -99,12 +359,174 @@
         "type": "github"
       }
     },
+    "nose": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1400604510,
+        "narHash": "sha256-daEi8Kta1oGaDEmUUDDQMahTTPOpvNpDKk22rlr7cB0=",
+        "owner": "emacsattic",
+        "repo": "nose",
+        "rev": "f8528297519eba911696c4e68fa88892de9a7b72",
+        "type": "github"
+      },
+      "original": {
+        "owner": "emacsattic",
+        "repo": "nose",
+        "type": "github"
+      }
+    },
+    "nur": {
+      "locked": {
+        "lastModified": 1726059953,
+        "narHash": "sha256-uuqSnIgC2g3W6wk6Of1HvOFgFvo5Q+h0469VwMzDaBI=",
+        "owner": "nix-community",
+        "repo": "NUR",
+        "rev": "72296d37a29541a017e545c8f4e2c5c37dbaba59",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "NUR",
+        "type": "github"
+      }
+    },
+    "ob-racket": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1584656173,
+        "narHash": "sha256-rBUYDDCXb+3D4xTPQo9UocbTPZ32kWV1Uya/1DmZknU=",
+        "owner": "xchrishawk",
+        "repo": "ob-racket",
+        "rev": "83457ec9e1e96a29fd2086ed19432b9d75787673",
+        "type": "github"
+      },
+      "original": {
+        "owner": "xchrishawk",
+        "repo": "ob-racket",
+        "type": "github"
+      }
+    },
+    "org": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1695726851,
+        "narHash": "sha256-qgbjspklSoI8M3cbCJOcUdjuijRgsL/+PSyEOW9VX4I=",
+        "owner": "emacs-straight",
+        "repo": "org-mode",
+        "rev": "aa9177e1a8b039c357d369c1c9aaab710bb247a9",
+        "type": "github"
+      },
+      "original": {
+        "owner": "emacs-straight",
+        "repo": "org-mode",
+        "type": "github"
+      }
+    },
+    "org-contrib": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1694946041,
+        "narHash": "sha256-X/HFG6NZe5BY00KvGbcsIuf9R6Lg8x7Uhd0Y5+Q3qZU=",
+        "owner": "emacsmirror",
+        "repo": "org-contrib",
+        "rev": "5eabbf22bdd4523c922a30787e98ee66c24221aa",
+        "type": "github"
+      },
+      "original": {
+        "owner": "emacsmirror",
+        "repo": "org-contrib",
+        "type": "github"
+      }
+    },
+    "org-yt": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1527381913,
+        "narHash": "sha256-dzQ6B7ryzatHCTLyEnRSbWO0VUiX/FHYnpHTs74aVUs=",
+        "owner": "TobiasZawada",
+        "repo": "org-yt",
+        "rev": "40cc1ac76d741055cbefa13860d9f070a7ade001",
+        "type": "github"
+      },
+      "original": {
+        "owner": "TobiasZawada",
+        "repo": "org-yt",
+        "type": "github"
+      }
+    },
+    "php-extras": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1573312690,
+        "narHash": "sha256-r4WyVbzvT0ra4Z6JywNBOw5RxOEYd6Qe2IpebHXkj1U=",
+        "owner": "arnested",
+        "repo": "php-extras",
+        "rev": "d410c5af663c30c01d461ac476d1cbfbacb49367",
+        "type": "github"
+      },
+      "original": {
+        "owner": "arnested",
+        "repo": "php-extras",
+        "type": "github"
+      }
+    },
+    "revealjs": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1695738029,
+        "narHash": "sha256-Z9c9Q41jMkj/DyXOiZYyIa7Gmn8VB8yauTyWrSsT+ps=",
+        "owner": "hakimel",
+        "repo": "reveal.js",
+        "rev": "88fbfc5751ad01e3f6adee5819eabeb9e73c3757",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hakimel",
+        "repo": "reveal.js",
+        "type": "github"
+      }
+    },
     "root": {
       "inputs": {
         "agenix": "agenix",
         "home-manager": "home-manager",
+        "nix-doom-emacs": "nix-doom-emacs",
         "nix-flatpak": "nix-flatpak",
-        "nixpkgs": "nixpkgs"
+        "nix-straight": "nix-straight",
+        "nixpkgs": "nixpkgs",
+        "nur": "nur"
+      }
+    },
+    "rotate-text": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1322962747,
+        "narHash": "sha256-SOeOgSlcEIsKhUiYDJv0p+mLUb420s9E2BmvZQvZ0wk=",
+        "owner": "debug-ito",
+        "repo": "rotate-text.el",
+        "rev": "48f193697db996855aee1ad2bc99b38c6646fe76",
+        "type": "github"
+      },
+      "original": {
+        "owner": "debug-ito",
+        "repo": "rotate-text.el",
+        "type": "github"
+      }
+    },
+    "sln-mode": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1423727528,
+        "narHash": "sha256-XqkqPyEJuTtFslOz1fpTf/Klbd/zA7IGpzpmum/MGao=",
+        "owner": "sensorflo",
+        "repo": "sln-mode",
+        "rev": "0f91d1b957c7d2a7bab9278ec57b54d57f1dbd9c",
+        "type": "github"
+      },
+      "original": {
+        "owner": "sensorflo",
+        "repo": "sln-mode",
+        "type": "github"
       }
     },
     "systems": {
@@ -121,6 +543,53 @@
         "repo": "default",
         "type": "github"
       }
+    },
+    "systems_2": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "ts-fold": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1695278494,
+        "narHash": "sha256-O4NcUC1u08W8ZslqoA/i+iTaLotKwheURXQWBxLLMFc=",
+        "owner": "jcs-elpa",
+        "repo": "ts-fold",
+        "rev": "70b2c79ff4daa7351d4e2917b0343b9a18d4d4f2",
+        "type": "github"
+      },
+      "original": {
+        "owner": "jcs-elpa",
+        "repo": "ts-fold",
+        "type": "github"
+      }
+    },
+    "ws-butler": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1634511126,
+        "narHash": "sha256-c0y0ZPtxxICPk+eaNbbQf6t+FRCliNY54CCz9QHQ8ZI=",
+        "owner": "hlissner",
+        "repo": "ws-butler",
+        "rev": "572a10c11b6cb88293de48acbb59a059d36f9ba5",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hlissner",
+        "repo": "ws-butler",
+        "type": "github"
+      }
     }
   },
   "root": "root",
diff --git a/flake.nix b/flake.nix
index 8eb11a3..5ec27f8 100644
--- a/flake.nix
+++ b/flake.nix
@@ -3,6 +3,7 @@
 
   inputs = {
     nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
+    nur.url = github:nix-community/NUR;
 
     home-manager = {
       url = "github:nix-community/home-manager";
@@ -16,13 +17,30 @@
     };
 
     nix-flatpak.url = "github:gmodena/nix-flatpak/?ref=v0.4.1";
+
+    # Follow nix-doom-emacs completely when this is merged or fixed
+    # - https://github.com/nix-community/nix-doom-emacs/issues/409
+    # - https://github.com/nix-community/nix-straight.el/pull/4
+    nix-straight = {
+      url = "github:codingkoi/nix-straight.el?ref=codingkoi/apply-librephoenixs-fix";
+      flake = false;
+    };
+    nix-doom-emacs = {
+      url = "github:nix-community/nix-doom-emacs";
+      inputs = {
+        nix-straight.follows = "nix-straight";
+        nixpkgs.follows = "nixpkgs";
+      };
+    };
   };
 
   outputs = inputs@{
     nixpkgs,
+    nur,
     home-manager,
     agenix,
     nix-flatpak,
+    nix-doom-emacs,
     ...
   }:
   let
@@ -41,12 +59,15 @@
               ./hosts/${s.name}/configuration.nix
               home-manager.nixosModules.home-manager
               agenix.nixosModules.default
+              { nixpkgs.overlays = [ nur.overlay ]; }
               {
                 home-manager.useGlobalPkgs = true;
                 home-manager.useUserPackages = true;
                 home-manager.extraSpecialArgs = inputs;
                 home-manager.users.florian.imports = [
                   nix-flatpak.homeManagerModules.nix-flatpak
+                  nix-doom-emacs.hmModule
+                  
                   ./hosts/${s.name}/home.nix
                 ];
               }
diff --git a/hosts/nixos-test/home.nix b/hosts/nixos-test/home.nix
index 0aafc57..6dcf755 100644
--- a/hosts/nixos-test/home.nix
+++ b/hosts/nixos-test/home.nix
@@ -13,10 +13,6 @@
       kitty.enable = true;
     };
 
-    editors = {
-      vscode.enable = true;
-    };
-
     shell = {
       zsh.enable = true;
       atuin.enable = true;
diff --git a/hosts/perso-desktop/configuration.nix b/hosts/perso-desktop/configuration.nix
index 43e5e9a..8a538d4 100644
--- a/hosts/perso-desktop/configuration.nix
+++ b/hosts/perso-desktop/configuration.nix
@@ -20,6 +20,10 @@
   networking.hostName = "nixos-desktop-perso"; # Define your hostname.
 
   modules.system = {
+    apps = {
+      steam.enable = true;
+    };
+
     desktop = {
       plasma.enable = true;
     };
@@ -34,5 +38,9 @@
       printing.enable = true;
       waydroid.enable = true;
     };
+
+    server = {
+      distrobox.enable = true;
+    };
   };
 }
diff --git a/hosts/perso-desktop/home.nix b/hosts/perso-desktop/home.nix
index 0aafc57..48642dc 100644
--- a/hosts/perso-desktop/home.nix
+++ b/hosts/perso-desktop/home.nix
@@ -8,15 +8,12 @@
   modules.home = {
     apps = {
       chromium.enable = true;
+      firefox.enable = true;
       flatpak.enable = true;
       jetbrainsToolbox.enable = true;
       kitty.enable = true;
     };
 
-    editors = {
-      vscode.enable = true;
-    };
-
     shell = {
       zsh.enable = true;
       atuin.enable = true;
diff --git a/hosts/perso-laptop/home.nix b/hosts/perso-laptop/home.nix
index 0aafc57..6dcf755 100644
--- a/hosts/perso-laptop/home.nix
+++ b/hosts/perso-laptop/home.nix
@@ -13,10 +13,6 @@
       kitty.enable = true;
     };
 
-    editors = {
-      vscode.enable = true;
-    };
-
     shell = {
       zsh.enable = true;
       atuin.enable = true;
diff --git a/hosts/pro-laptop/configuration.nix b/hosts/pro-laptop/configuration.nix
index 4a0eec2..3c57635 100644
--- a/hosts/pro-laptop/configuration.nix
+++ b/hosts/pro-laptop/configuration.nix
@@ -19,18 +19,18 @@
 
   networking.hostName = "nixos-laptop-pro"; # Define your hostname.
 
-  hardware.nvidia.prime = {
-    offload = {
-      enable = true;
-      enableOffloadCmd = true;
-    };
-
-    # Make sure to use the correct Bus ID values for your system!
-    # information bus: pci@0000:00:02.0
-    intelBusId = "PCI:0:2:0";
-    # information bus: pci@0000:01:00.0
-    nvidiaBusId = "PCI:1:0:0";
-  };
+#   hardware.nvidia.prime = {
+#     offload = {
+#       enable = true;
+#       enableOffloadCmd = true;
+#     };
+#
+#     # Make sure to use the correct Bus ID values for your system!
+#     # information bus: pci@0000:00:02.0
+#     intelBusId = "PCI:0:2:0";
+#     # information bus: pci@0000:01:00.0
+#     nvidiaBusId = "PCI:1:0:0";
+#   };
 
   modules.system = {
     desktop = {
@@ -47,5 +47,9 @@
       printing.enable = true;
       waydroid.enable = true;
     };
+
+    server = {
+      distrobox.enable = true;
+    };
   };
 }
diff --git a/hosts/pro-laptop/home.nix b/hosts/pro-laptop/home.nix
index 0aafc57..48642dc 100644
--- a/hosts/pro-laptop/home.nix
+++ b/hosts/pro-laptop/home.nix
@@ -8,15 +8,12 @@
   modules.home = {
     apps = {
       chromium.enable = true;
+      firefox.enable = true;
       flatpak.enable = true;
       jetbrainsToolbox.enable = true;
       kitty.enable = true;
     };
 
-    editors = {
-      vscode.enable = true;
-    };
-
     shell = {
       zsh.enable = true;
       atuin.enable = true;
diff --git a/modules/common.nix b/modules/common.nix
new file mode 100644
index 0000000..c7eb3e6
--- /dev/null
+++ b/modules/common.nix
@@ -0,0 +1,5 @@
+{ ... }:
+
+{
+  nix.settings.experimental-features = [ "nix-command" "flakes" ];
+}
\ No newline at end of file
diff --git a/modules/home/apps/default.nix b/modules/home/apps/default.nix
index 888257b..5f6f130 100644
--- a/modules/home/apps/default.nix
+++ b/modules/home/apps/default.nix
@@ -1,10 +1,11 @@
-{ config, pkgs, ... }:
+{ ... }:
 
 {
   imports = [
     ./chromium
+    ./firefox
     ./flatpak
     ./jetbrainsToolbox
     ./kitty
   ];
-}
\ No newline at end of file
+}
diff --git a/modules/home/apps/firefox/default.nix b/modules/home/apps/firefox/default.nix
new file mode 100644
index 0000000..4ea8d76
--- /dev/null
+++ b/modules/home/apps/firefox/default.nix
@@ -0,0 +1,44 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.modules.home.apps.firefox;
+in
+{
+  options.modules.home.apps.firefox = {
+    enable = mkEnableOption ''
+      Enable firefox with my custom configurations
+    '';
+  };
+  config = mkIf cfg.enable {
+    programs.firefox = {
+      enable = true;
+
+      nativeMessagingHosts = [
+        pkgs.kdePackages.plasma-browser-integration
+      ];
+
+      profiles = {
+        perso = {
+          id = 0;
+
+          name = "Perso";
+
+          extensions = with pkgs.nur.repos.rycee.firefox-addons; [
+            ublock-origin
+            bitwarden
+            floccus
+            plasma-integration
+            istilldontcareaboutcookies
+            darkreader
+          ];
+
+          settings = {
+            # Enable multi-pip
+            "media.videocontrols.picture-in-picture.allow-multiple" = true;
+          };
+        };
+      };
+    };
+  };
+}
diff --git a/modules/home/apps/flatpak/default.nix b/modules/home/apps/flatpak/default.nix
index 052f6b0..4c05680 100644
--- a/modules/home/apps/flatpak/default.nix
+++ b/modules/home/apps/flatpak/default.nix
@@ -12,7 +12,6 @@ in
   };
 
   config = mkIf cfg.enable {
-
     services.flatpak = {
       enable = true;
 
@@ -21,22 +20,15 @@ in
       packages = [
         # Gaming
         { appId = "com.discordapp.Discord"; origin = "flathub";  }
-        { appId = "com.valvesoftware.Steam"; origin = "flathub";  }
         { appId = "net.lutris.Lutris"; origin = "flathub";  }
 
         # Pro
-        { appId = "com.slack.Slack"; origin = "flathub";  }
-        { appId = "com.skype.Client"; origin = "flathub";  }
-        { appId = "org.mozilla.Thunderbird"; origin = "flathub"; }
         { appId = "ch.protonmail.protonmail-bridge"; origin = "flathub";  }
         { appId = "org.kde.neochat"; origin = "flathub"; }
 
         # Loisir
         { appId = "com.spotify.Client"; origin = "flathub"; }
-        { appId = "io.gitlab.news_flash.NewsFlash"; origin = "flathub"; }
         { appId = "org.videolan.VLC"; origin = "flathub"; }
-        { appId = "com.obsproject.Studio"; origin = "flathub"; }
-        { appId = "io.github.achetagames.epic_asset_manager"; origin = "flathub"; }
       ];
     };
   };
diff --git a/modules/home/apps/jetbrainsToolbox/default.nix b/modules/home/apps/jetbrainsToolbox/default.nix
index c79f09f..c495cc2 100644
--- a/modules/home/apps/jetbrainsToolbox/default.nix
+++ b/modules/home/apps/jetbrainsToolbox/default.nix
@@ -11,6 +11,6 @@ in
     '';
   };
   config = mkIf cfg.enable {
-    home.packages = with pkgs; [jetbrains-toolbox];
+    home.packages = with pkgs; [jetbrains-toolbox graalvm-ce];
   };
 }
\ No newline at end of file
diff --git a/modules/home/default.nix b/modules/home/default.nix
index 6d528ce..adfece8 100644
--- a/modules/home/default.nix
+++ b/modules/home/default.nix
@@ -2,6 +2,7 @@
 
 {
   imports = [
+    ../common.nix
     ./apps
     ./editors
     ./desktop
@@ -16,6 +17,5 @@
   };
 
   programs.home-manager.enable = true;
-  nix.settings.experimental-features = [ "nix-command" "flakes" ];
 }
 
diff --git a/modules/home/editors/default.nix b/modules/home/editors/default.nix
index 40db812..f265d2c 100644
--- a/modules/home/editors/default.nix
+++ b/modules/home/editors/default.nix
@@ -2,6 +2,7 @@
 
 {
   imports = [
+    ./emacs
     ./vscode
   ];
 }
\ No newline at end of file
diff --git a/modules/home/editors/emacs/default.nix b/modules/home/editors/emacs/default.nix
new file mode 100644
index 0000000..5a70616
--- /dev/null
+++ b/modules/home/editors/emacs/default.nix
@@ -0,0 +1,19 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.modules.home.editors.emacs;
+in
+{
+  options.modules.home.editors.emacs = {
+    enable = mkEnableOption ''
+      Enable emacs with my custom configurations
+    '';
+  };
+  config = mkIf cfg.enable {
+    programs.doom-emacs = {
+      enable = true;
+      doomPrivateDir = ./doom.d;
+    };
+  };
+}
\ No newline at end of file
diff --git a/modules/home/editors/emacs/doom.d/config.el b/modules/home/editors/emacs/doom.d/config.el
new file mode 100644
index 0000000..576a6e5
--- /dev/null
+++ b/modules/home/editors/emacs/doom.d/config.el
@@ -0,0 +1,76 @@
+;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
+
+;; Place your private configuration here! Remember, you do not need to run 'doom
+;; sync' after modifying this file!
+
+
+;; Some functionality uses this to identify you, e.g. GPG configuration, email
+;; clients, file templates and snippets. It is optional.
+;; (setq user-full-name "John Doe"
+;;       user-mail-address "john@doe.com")
+
+;; Doom exposes five (optional) variables for controlling fonts in Doom:
+;;
+;; - `doom-font' -- the primary font to use
+;; - `doom-variable-pitch-font' -- a non-monospace font (where applicable)
+;; - `doom-big-font' -- used for `doom-big-font-mode'; use this for
+;;   presentations or streaming.
+;; - `doom-symbol-font' -- for symbols
+;; - `doom-serif-font' -- for the `fixed-pitch-serif' face
+;;
+;; See 'C-h v doom-font' for documentation and more examples of what they
+;; accept. For example:
+;;
+;; (setq doom-font (font-spec :family "Fira Code" :size 12 :weight 'semi-light)
+;;       doom-variable-pitch-font (font-spec :family "Fira Sans" :size 13))
+;;
+;; If you or Emacs can't find your font, use 'M-x describe-font' to look them
+;; up, `M-x eval-region' to execute elisp code, and 'M-x doom/reload-font' to
+;; refresh your font settings. If Emacs still can't find your font, it likely
+;; wasn't installed correctly. Font issues are rarely Doom issues!
+
+;; There are two ways to load a theme. Both assume the theme is installed and
+;; available. You can either set `doom-theme' or manually load a theme with the
+;; `load-theme' function. This is the default:
+(setq doom-theme 'doom-one)
+
+;; This determines the style of line numbers in effect. If set to `nil', line
+;; numbers are disabled. For relative line numbers, set this to `relative'.
+(setq display-line-numbers-type t)
+
+;; If you use `org' and don't want your org files in the default location below,
+;; change `org-directory'. It must be set before org loads!
+(setq org-directory "~/org/")
+
+
+;; Whenever you reconfigure a package, make sure to wrap your config in an
+;; `after!' block, otherwise Doom's defaults may override your settings. E.g.
+;;
+;;   (after! PACKAGE
+;;     (setq x y))
+;;
+;; The exceptions to this rule:
+;;
+;;   - Setting file/directory variables (like `org-directory')
+;;   - Setting variables which explicitly tell you to set them before their
+;;     package is loaded (see 'C-h v VARIABLE' to look up their documentation).
+;;   - Setting doom variables (which start with 'doom-' or '+').
+;;
+;; Here are some additional functions/macros that will help you configure Doom.
+;;
+;; - `load!' for loading external *.el files relative to this one
+;; - `use-package!' for configuring packages
+;; - `after!' for running code after a package has loaded
+;; - `add-load-path!' for adding directories to the `load-path', relative to
+;;   this file. Emacs searches the `load-path' when you load packages with
+;;   `require' or `use-package'.
+;; - `map!' for binding new keys
+;;
+;; To get information about any of these functions/macros, move the cursor over
+;; the highlighted symbol at press 'K' (non-evil users must press 'C-c c k').
+;; This will open documentation for it, including demos of how they are used.
+;; Alternatively, use `C-h o' to look up a symbol (functions, variables, faces,
+;; etc).
+;;
+;; You can also try 'gd' (or 'C-c c d') to jump to their definition and see how
+;; they are implemented.
\ No newline at end of file
diff --git a/modules/home/editors/emacs/doom.d/init.el b/modules/home/editors/emacs/doom.d/init.el
new file mode 100644
index 0000000..aa00999
--- /dev/null
+++ b/modules/home/editors/emacs/doom.d/init.el
@@ -0,0 +1,195 @@
+;;; init.el -*- lexical-binding: t; -*-
+
+;; This file controls what Doom modules are enabled and what order they load
+;; in. Remember to run 'doom sync' after modifying it!
+
+;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's
+;;      documentation. There you'll find a link to Doom's Module Index where all
+;;      of our modules are listed, including what flags they support.
+
+;; NOTE Move your cursor over a module's name (or its flags) and press 'K' (or
+;;      'C-c c k' for non-vim users) to view its documentation. This works on
+;;      flags as well (those symbols that start with a plus).
+;;
+;;      Alternatively, press 'gd' (or 'C-c c d') on a module to browse its
+;;      directory (for easy access to its source code).
+
+(doom! :input
+       ;;bidi              ; (tfel ot) thgir etirw uoy gnipleh
+       ;;chinese
+       ;;japanese
+       ;;layout            ; auie,ctsrnm is the superior home row
+
+       :completion
+       company           ; the ultimate code completion backend
+       ;;helm              ; the *other* search engine for love and life
+       ;;ido               ; the other *other* search engine...
+       ;;ivy               ; a search engine for love and life
+       vertico           ; the search engine of the future
+
+       :ui
+       ;;deft              ; notational velocity for Emacs
+       doom              ; what makes DOOM look the way it does
+       doom-dashboard    ; a nifty splash screen for Emacs
+       ;;doom-quit         ; DOOM quit-message prompts when you quit Emacs
+       (emoji +unicode)  ; 🙂
+       hl-todo           ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
+       ;;hydra
+       ;;indent-guides     ; highlighted indent columns
+       ligatures         ; ligatures and symbols to make your code pretty again
+       ;;minimap           ; show a map of the code on the side
+       modeline          ; snazzy, Atom-inspired modeline, plus API
+       nav-flash         ; blink cursor line after big motions
+       ;;neotree           ; a project drawer, like NERDTree for vim
+       ophints           ; highlight the region an operation acts on
+       (popup +defaults)   ; tame sudden yet inevitable temporary windows
+       tabs              ; a tab bar for Emacs
+       (treemacs +lsp)          ; a project drawer, like neotree but cooler
+       ;;unicode           ; extended unicode support for various languages
+       (vc-gutter +pretty) ; vcs diff in the fringe
+       vi-tilde-fringe   ; fringe tildes to mark beyond EOB
+       ;;window-select     ; visually switch windows
+       workspaces        ; tab emulation, persistence & separate workspaces
+       ;;zen               ; distraction-free coding or writing
+
+       :editor
+       (evil +everywhere); come to the dark side, we have cookies
+       file-templates    ; auto-snippets for empty files
+       fold              ; (nigh) universal code folding
+       ;;(format +onsave)  ; automated prettiness
+       ;;god               ; run Emacs commands without modifier keys
+       ;;lispy             ; vim for lisp, for people who don't like vim
+       multiple-cursors  ; editing in many places at once
+       ;;objed             ; text object editing for the innocent
+       ;;parinfer          ; turn lisp into python, sort of
+       ;;rotate-text       ; cycle region at point between text candidates
+       snippets          ; my elves. They type so I don't have to
+       ;;word-wrap         ; soft wrapping with language-aware indent
+
+       :emacs
+       dired             ; making dired pretty [functional]
+       electric          ; smarter, keyword-based electric-indent
+       (ibuffer +icons)         ; interactive buffer management
+       undo              ; persistent, smarter undo for your inevitable mistakes
+       vc                ; version-control and Emacs, sitting in a tree
+
+       :term
+       eshell            ; the elisp shell that works everywhere
+       ;;shell             ; simple shell REPL for Emacs
+       ;;term              ; basic terminal emulator for Emacs
+       ;;vterm             ; the best terminal emulation in Emacs
+
+       :checkers
+       syntax              ; tasing you for every semicolon you forget
+       ;;(spell +flyspell) ; tasing you for misspelling mispelling
+       ;;grammar           ; tasing grammar mistake every you make
+
+       :tools
+       ;;ansible
+       ;;biblio            ; Writes a PhD for you (citation needed)
+       ;;collab            ; buffers with friends
+       (debugger +lsp)          ; FIXME stepping through code, to help you add bugs
+       ;;direnv
+       ;;docker
+       ;;editorconfig      ; let someone else argue about tabs vs spaces
+       ;;ein               ; tame Jupyter notebooks with emacs
+       (eval +overlay)     ; run code, run (also, repls)
+       ;;gist              ; interacting with github gists
+       lookup              ; navigate your code and its documentation
+       lsp               ; M-x vscode
+       magit             ; a git porcelain for Emacs
+       ;;make              ; run make tasks from Emacs
+       ;;pass              ; password manager for nerds
+       ;;pdf               ; pdf enhancements
+       ;;prodigy           ; FIXME managing external services & code builders
+       rgb               ; creating color strings
+       ;;taskrunner        ; taskrunner for all your projects
+       ;;terraform         ; infrastructure as code
+       ;;tmux              ; an API for interacting with tmux
+       tree-sitter       ; syntax and parsing, sitting in a tree...
+       ;;upload            ; map local to remote projects via ssh/ftp
+
+       :os
+       (:if IS-MAC macos)  ; improve compatibility with macOS
+       ;;tty               ; improve the terminal Emacs experience
+
+       :lang
+       ;;agda              ; types of types of types of types...
+       ;;beancount         ; mind the GAAP
+       (cc +lsp)         ; C > C++ == 1
+       ;;cloju
+       ;;common-lisp       ; if you've seen one lisp, you've seen them all
+       ;;coq               ; proofs-as-programs
+       ;;crystal           ; ruby at the speed of c
+       ;;csharp            ; unity, .NET, and mono shenanigans
+       ;;data              ; config/data formats
+       (dart +flutter +lsp)   ; paint ui and not much else
+       ;;dhall
+       (elixir +lsp)            ; erlang done right
+       ;;elm               ; care for a cup of TEA?
+       emacs-lisp        ; drown in parentheses
+       ;;erlang            ; an elegant language for a more civilized age
+       ;;ess               ; emacs speaks statistics
+       ;;factor
+       ;;faust             ; dsp, but you get to keep your soul
+       ;;fortran           ; in FORTRAN, GOD is REAL (unless declared INTEGER)
+       ;;fsharp            ; ML stands for Microsoft's Language
+       ;;fstar             ; (dependent) types and (monadic) effects and Z3
+       ;;gdscript          ; the language you waited for
+       ;;(go +lsp)         ; the hipster dialect
+       ;;(graphql +lsp)    ; Give queries a REST
+       ;;(haskell +lsp)    ; a language that's lazier than I am
+       ;;hy                ; readability of scheme w/ speed of python
+       ;;idris             ; a language you can depend on
+       json                ; At least it ain't XML
+       (java +lsp)         ; the poster child for carpal tunnel syndrome
+       ;;javascript        ; all(hope(abandon(ye(who(enter(here))))))
+       ;;julia             ; a better, faster MATLAB
+       (kotlin +lsp)       ; a better, slicker Java(Script)
+       ;;latex             ; writing papers in Emacs has never been so fun
+       ;;lean              ; for folks with too much to prove
+       ;;ledger            ; be audit you can be
+       ;;lua               ; one-based indices? one-based indices
+       markdown          ; writing docs for people to ignore
+       ;;nim               ; python + lisp at the speed of c
+       ;;nix               ; I hereby declare "nix geht mehr!"
+       ;;ocaml             ; an objective camel
+       org               ; organize your plain life in plain text
+       ;;php               ; perl's insecure younger brother
+       ;;plantuml          ; diagrams for confusing people more
+       ;;purescript        ; javascript, but functional
+       ;;python            ; beautiful is better than ugly
+       (qt +lsp +xp)                ; the 'cutest' gui framework ever
+       ;;racket            ; a DSL for DSLs
+       ;;raku              ; the artist formerly known as perl6
+       ;;rest              ; Emacs as a REST client
+       ;;rst               ; ReST in peace
+       (ruby +lsp +rails)     ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
+       (rust +lsp)         ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
+       ;;scala             ; java, but good
+       ;;(scheme +guile)   ; a fully conniving family of lisps
+       sh                ; she sells {ba,z,fi}sh shells on the C xor
+       ;;sml
+       ;;solidity          ; do you need a blockchain? No.
+       ;;swift             ; who asked for emoji variables?
+       ;;terra             ; Earth and Moon in alignment for performance.
+       ;;web               ; the tubes
+       yaml              ; JSON, but readable
+       ;;zig               ; C, but simpler
+
+       :email
+       ;;(mu4e +org +gmail)
+       ;;notmuch
+       ;;(wanderlust +gmail)
+
+       :app
+       ;;calendar
+       ;;emms
+       ;;everywhere        ; *leave* Emacs!? You must be joking
+       ;;irc               ; how neckbeards socialize
+       ;;(rss +org)        ; emacs as an RSS reader
+       ;;twitter           ; twitter client https://twitter.com/vnought
+
+       :config
+       ;;literate
+       (default +bindings +smartparens))
\ No newline at end of file
diff --git a/modules/home/editors/emacs/doom.d/packages.el b/modules/home/editors/emacs/doom.d/packages.el
new file mode 100644
index 0000000..be08796
--- /dev/null
+++ b/modules/home/editors/emacs/doom.d/packages.el
@@ -0,0 +1,52 @@
+;; -*- no-byte-compile: t; -*-
+;;; $DOOMDIR/packages.el
+
+;; To install a package with Doom you must declare them here and run 'doom sync'
+;; on the command line, then restart Emacs for the changes to take effect -- or
+;; use 'M-x doom/reload'.
+
+
+;; To install SOME-PACKAGE from MELPA, ELPA or emacsmirror:
+;(package! some-package)
+
+;; To install a package directly from a remote git repo, you must specify a
+;; `:recipe'. You'll find documentation on what `:recipe' accepts here:
+;; https://github.com/radian-software/straight.el#the-recipe-format
+;(package! another-package
+;  :recipe (:host github :repo "username/repo"))
+
+;; If the package you are trying to install does not contain a PACKAGENAME.el
+;; file, or is located in a subdirectory of the repo, you'll need to specify
+;; `:files' in the `:recipe':
+;(package! this-package
+;  :recipe (:host github :repo "username/repo"
+;           :files ("some-file.el" "src/lisp/*.el")))
+
+;; If you'd like to disable a package included with Doom, you can do so here
+;; with the `:disable' property:
+;(package! builtin-package :disable t)
+
+;; You can override the recipe of a built in package without having to specify
+;; all the properties for `:recipe'. These will inherit the rest of its recipe
+;; from Doom or MELPA/ELPA/Emacsmirror:
+;(package! builtin-package :recipe (:nonrecursive t))
+;(package! builtin-package-2 :recipe (:repo "myfork/package"))
+
+;; Specify a `:branch' to install a package from a particular branch or tag.
+;; This is required for some packages whose default branch isn't 'master' (which
+;; our package manager can't deal with; see radian-software/straight.el#279)
+;(package! builtin-package :recipe (:branch "develop"))
+
+;; Use `:pin' to specify a particular commit to install.
+;(package! builtin-package :pin "1a2b3c4d5e")
+
+
+;; Doom's packages are pinned to a specific commit and updated from release to
+;; release. The `unpin!' macro allows you to unpin single packages...
+;(unpin! pinned-package)
+;; ...or multiple packages
+;(unpin! pinned-package another-pinned-package)
+;; ...Or *all* packages (NOT RECOMMENDED; will likely break things)
+;(unpin! t)
+
+(package! ripgrep)
\ No newline at end of file
diff --git a/modules/home/editors/vscode/default.nix b/modules/home/editors/vscode/default.nix
index 13ed250..14cd380 100644
--- a/modules/home/editors/vscode/default.nix
+++ b/modules/home/editors/vscode/default.nix
@@ -26,6 +26,7 @@ in
 
       userTasks = {};
 
+      # Waiting https://github.com/nix-community/home-manager/pull/5640 to support multi-profiles support
       extensions = with pkgs; with vscode-extensions; [
         # Nix
         bbenoist.nix
diff --git a/modules/home/shell/git/default.nix b/modules/home/shell/git/default.nix
index ad50961..e6eb7b1 100644
--- a/modules/home/shell/git/default.nix
+++ b/modules/home/shell/git/default.nix
@@ -13,10 +13,20 @@ in
   config = mkIf cfg.enable {
     programs.git = {
       enable = true;
+      lfs.enable = true;
+
       userName = "Florian RICHER";
       userEmail = "florian.richer@protonmail.com";
 
-      # signing.signByDefault = true;
+      signing = {
+        signByDefault = true;
+        key = "B19E3F4A2D806AB4793FDF2FC73D37CBED7BFC77";
+      };
+
+      extraConfig = {
+        url."https://invent.kde.org/".insteadOf = "kde:";
+        url."ssh://git@invent.kde.org/".pushInsteadOf = "kde:";
+      };
     };
   };
-}
\ No newline at end of file
+}
diff --git a/modules/system/apps/default.nix b/modules/system/apps/default.nix
new file mode 100644
index 0000000..557c5f2
--- /dev/null
+++ b/modules/system/apps/default.nix
@@ -0,0 +1,7 @@
+{ ... }:
+
+{
+  imports = [
+    ./steam
+  ];
+}
diff --git a/modules/system/apps/steam/default.nix b/modules/system/apps/steam/default.nix
new file mode 100644
index 0000000..448421c
--- /dev/null
+++ b/modules/system/apps/steam/default.nix
@@ -0,0 +1,22 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.modules.system.apps.steam;
+in
+{
+  options.modules.system.apps.steam = {
+    enable = mkEnableOption ''
+      Enable steam with my custom configurations
+    '';
+  };
+  config = mkIf cfg.enable {
+    programs.steam = {
+      enable = true;
+      extraPackages = with pkgs; [ gamescope ];
+      extraCompatPackages = with pkgs; [ proton-ge-bin ];
+    };
+
+    hardware.steam-hardware.enable = true;
+  };
+}
diff --git a/modules/system/common.nix b/modules/system/common.nix
index 43cc570..5838226 100644
--- a/modules/system/common.nix
+++ b/modules/system/common.nix
@@ -1,6 +1,10 @@
 { pkgs, ... }:
 
 {
+  imports = [
+    ../common.nix
+  ];
+
   # Set your time zone.
   time.timeZone = "Europe/Paris";
 
@@ -21,13 +25,39 @@
 
   nixpkgs.config.allowUnfree = true;
 
+  services.udev.packages = [ pkgs.yubikey-personalization ];
+  services.pcscd.enable = true;
+
+  programs.gnupg.agent = {
+    enable = true;
+    enableSSHSupport = true;
+  };
+
   # List packages installed in system profile. To search, run:
   # $ nix search wget
   environment.systemPackages = with pkgs; [
     vim
     nixd
+
+    # Usefull tools to debug
+    gdb
+    lldb
+    gammaray # QT Inspector
+
+    yubikey-manager
+
+    # Usefull for automatic informations collect software like KDE
+    vulkan-tools # For vulkaninfo command
+    wayland-utils # For wayland-info command
+    glxinfo
+    clinfo
+    aha
+    usbutils
+    pciutils
   ];
 
+  services.fwupd.enable = true;
+
   # Define a user account. Don't forget to set a password with ‘passwd’.
   users.users.florian = {
     isNormalUser = true;
@@ -37,6 +67,10 @@
 
   services.flatpak.enable = true; # Important can't be enabled from home-manager
 
+  # Downloads and provides debug symbols and source code for nix derivations to gdb and other debuginfod-capable debuggers as needed.
+  # Require https://github.com/symphorien/nixseparatedebuginfod?tab=readme-ov-file#gdb
+  services.nixseparatedebuginfod.enable = true;
+
   # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
 
   # Configure network proxy if necessary
diff --git a/modules/system/default.nix b/modules/system/default.nix
index 49a302c..08983d8 100644
--- a/modules/system/default.nix
+++ b/modules/system/default.nix
@@ -2,6 +2,7 @@
 
 {
   imports = [
+    ./apps
     ./desktop
     ./hardware
     ./server
diff --git a/modules/system/desktop/plasma/default.nix b/modules/system/desktop/plasma/default.nix
index b0387fe..261b54f 100644
--- a/modules/system/desktop/plasma/default.nix
+++ b/modules/system/desktop/plasma/default.nix
@@ -26,17 +26,6 @@ in
     environment.systemPackages = with pkgs; with kdePackages; [
       krfb # Use by kdeconnect for virtualmonitorplugin "krfb-virtualmonitor"
       discover
-
-      # Usefull for automatic informations collect software like KDE
-      vulkan-tools # For vulkaninfo command
-      wayland-utils # For wayland-info command
-      glxinfo
-      clinfo
-    ];
-
-    # Uncomment when kwin is available in nixpkgs and NVIDIA 555
-    nixpkgs.overlays = [
-      (import ../../../../overlays/kwin)
     ];
   };
-}
\ No newline at end of file
+}
diff --git a/modules/system/hardware/nvidia/default.nix b/modules/system/hardware/nvidia/default.nix
index fa6625a..f58b698 100644
--- a/modules/system/hardware/nvidia/default.nix
+++ b/modules/system/hardware/nvidia/default.nix
@@ -11,41 +11,51 @@ in
     '';
   };
   config = mkIf cfg.enable {
-    # Enable OpenGL
-    hardware.opengl = {
-      enable = true;
-      driSupport = true;
-      driSupport32Bit = true;
-    };
-
     # Load nvidia driver for Xorg and Wayland
     services.xserver.videoDrivers = ["nvidia"];
 
-    hardware.nvidia = {
-      # Modesetting is required.
-      modesetting.enable = true;
+    hardware = {
+      graphics = {
+        enable = true;
+        enable32Bit = true;
+      };
 
-      # Nvidia power management. Experimental, and can cause sleep/suspend to fail.
-      powerManagement.enable = false;
-      # Fine-grained power management. Turns off GPU when not in use.
-      # Experimental and only works on modern Nvidia GPUs (Turing or newer).
-      powerManagement.finegrained = false;
+      nvidia-container-toolkit.enable = true;
 
-      # Use the NVidia open source kernel module (not to be confused with the
-      # independent third-party "nouveau" open source driver).
-      # Support is limited to the Turing and later architectures. Full list of 
-      # supported GPUs is at: 
-      # https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus 
-      # Only available from driver 515.43.04+
-      # Currently alpha-quality/buggy, so false is currently the recommended setting.
-      open = false;
+      nvidia = {
+        # Modesetting is required.
+        modesetting.enable = true;
 
-      # Enable the Nvidia settings menu,
-      # accessible via `nvidia-settings`.
-      nvidiaSettings = true;
+        # Nvidia power management. Experimental, and can cause sleep/suspend to fail.
+        # Enable this if you have graphical corruption issues or application crashes after waking
+        # up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead
+        # of just the bare essentials.
+        powerManagement.enable = true;
 
-      # Optionally, you may need to select the appropriate driver version for your specific GPU.
-      package = config.boot.kernelPackages.nvidiaPackages.beta;
+        # Fine-grained power management. Turns off GPU when not in use.
+        # Experimental and only works on modern Nvidia GPUs (Turing or newer).
+        powerManagement.finegrained = false;
+
+        # Use the NVidia open source kernel module (not to be confused with the
+        # independent third-party "nouveau" open source driver).
+        # Support is limited to the Turing and later architectures. Full list of
+        # supported GPUs is at:
+        # https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus
+        # Only available from driver 515.43.04+
+        # Currently alpha-quality/buggy, so false is currently the recommended setting.
+        open = true;
+
+        # Enable the Nvidia settings menu,
+        # accessible via `nvidia-settings`.
+        nvidiaSettings = true;
+
+        # Optionally, you may need to select the appropriate driver version for your specific GPU.
+        package = config.boot.kernelPackages.nvidiaPackages.latest;
+      };
     };
+
+    # boot.extraModprobeConfig = ''
+    #   options nvidia NVreg_EnableGpuFirmware=0
+    # '';
   };
-}
\ No newline at end of file
+}
diff --git a/modules/system/hardware/pipewire/default.nix b/modules/system/hardware/pipewire/default.nix
index 1f46de4..75a6a5e 100644
--- a/modules/system/hardware/pipewire/default.nix
+++ b/modules/system/hardware/pipewire/default.nix
@@ -12,7 +12,6 @@ in
   };
   config = mkIf cfg.enable {
     # Enable sound with pipewire.
-    sound.enable = true;
     hardware.pulseaudio.enable = false;
     security.rtkit.enable = true;
     services.pipewire = {
@@ -28,4 +27,4 @@ in
       #media-session.enable = true;
     };
   };
-}
\ No newline at end of file
+}
diff --git a/modules/system/server/default.nix b/modules/system/server/default.nix
index d75b7fd..7533a63 100644
--- a/modules/system/server/default.nix
+++ b/modules/system/server/default.nix
@@ -2,7 +2,9 @@
 
 {
   imports = [
+    ./distrobox
     ./docker
+    ./ollama
     ./openssh
   ];
 }
\ No newline at end of file
diff --git a/modules/system/server/distrobox/default.nix b/modules/system/server/distrobox/default.nix
new file mode 100644
index 0000000..fec6131
--- /dev/null
+++ b/modules/system/server/distrobox/default.nix
@@ -0,0 +1,17 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.modules.system.server.distrobox;
+in
+{
+  options.modules.system.server.distrobox = {
+    enable = mkEnableOption ''
+      Enable distrobox with my custom configurations
+    '';
+  };
+  config = mkIf cfg.enable {
+    modules.system.server.docker.enable = true;
+    environment.systemPackages = with pkgs; [ distrobox ];
+  };
+}
\ No newline at end of file
diff --git a/modules/system/server/docker/default.nix b/modules/system/server/docker/default.nix
index 39c121a..dfdd13b 100644
--- a/modules/system/server/docker/default.nix
+++ b/modules/system/server/docker/default.nix
@@ -13,7 +13,8 @@ in
   config = mkIf cfg.enable {
     virtualisation.docker = {
       enable = true;
-      enableNvidia = config.modules.system.hardware.nvidia.enable;
     };
+
+    users.users.florian.extraGroups = [ "docker" ];
   };
 }
\ No newline at end of file
diff --git a/modules/system/server/ollama/default.nix b/modules/system/server/ollama/default.nix
new file mode 100644
index 0000000..79e1a6c
--- /dev/null
+++ b/modules/system/server/ollama/default.nix
@@ -0,0 +1,21 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.modules.system.server.ollama;
+  nvidiaEnabled = config.modules.system.hardware.nvidia.enable;
+in
+{
+  options.modules.system.server.ollama = {
+    enable = mkEnableOption ''
+      Enable ollama with my custom configurations
+    '';
+  };
+  config = mkIf cfg.enable {
+    services.ollama = {
+      enable = true;
+
+      acceleration = if nvidiaEnabled then "cuda" else null;
+    };
+  };
+}
diff --git a/overlays/.gitkeep b/overlays/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/overlays/kwin/5511.patch b/overlays/kwin/5511.patch
deleted file mode 100644
index 341dc2e..0000000
--- a/overlays/kwin/5511.patch
+++ /dev/null
@@ -1,2027 +0,0 @@
-From e1791de663807bc93c09418481cb844c7daeb8f7 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Wed, 22 Nov 2023 19:51:24 +0100
-Subject: [PATCH 1/5] wayland: implement linux-drm-syncobj-v1
-
-linux-drm-syncobj-v1 allows drivers and apps to synchronize KWin's buffer access
-to their rendering, and synchronize their rendering to KWin's buffer release. This
-fixes severe glitches with the proprietary NVidia driver and allows for some
-performance improvements with Mesa too.
-
-(cherry picked from commit 32addf4d599135678a5120470bc27a881f34a3d9)
----
- src/CMakeLists.txt                            |   1 +
- src/backends/drm/drm_egl_backend.cpp          |  16 ++
- src/backends/drm/drm_egl_backend.h            |   3 +
- src/backends/drm/drm_gpu.cpp                  |   8 +
- src/backends/drm/drm_gpu.h                    |   2 +
- src/compositor_wayland.cpp                    |   2 +-
- src/core/graphicsbuffer.cpp                   |   6 +
- src/core/graphicsbuffer.h                     |   8 +
- src/core/renderbackend.cpp                    |  11 +
- src/core/renderbackend.h                      |   5 +
- src/core/syncobjtimeline.cpp                  |  83 ++++++
- src/core/syncobjtimeline.h                    |  72 +++++
- src/opengl/eglnativefence.cpp                 |   5 +
- src/opengl/eglnativefence.h                   |   1 +
- .../scenes/opengl/abstract_egl_backend.cpp    |   5 +
- .../scenes/opengl/abstract_egl_backend.h      |   2 +-
- .../scenes/opengl/openglbackend.cpp           |   4 +
- .../scenes/opengl/openglbackend.h             |   3 +
- src/scene/item.h                              |   1 +
- src/scene/itemrenderer_opengl.cpp             |  20 +-
- src/scene/itemrenderer_opengl.h               |   9 +-
- src/scene/surfaceitem.cpp                     |   5 +
- src/scene/surfaceitem.h                       |   3 +
- src/scene/surfaceitem_wayland.cpp             |   6 +
- src/scene/surfaceitem_wayland.h               |   1 +
- src/scene/workspacescene_opengl.cpp           |   2 +-
- src/wayland/CMakeLists.txt                    |   8 +
- src/wayland/linux_drm_syncobj_v1.cpp          | 190 +++++++++++++
- src/wayland/linux_drm_syncobj_v1.h            |  63 +++++
- src/wayland/linux_drm_syncobj_v1_p.h          |  32 +++
- .../protocols/linux-drm-syncobj-v1.xml        | 261 ++++++++++++++++++
- src/wayland/surface.cpp                       |  20 ++
- src/wayland/surface.h                         |   8 +
- src/wayland/surface_p.h                       |   9 +
- src/wayland/transaction.cpp                   |  27 +-
- src/wayland/transaction_p.h                   |  15 +
- src/wayland_server.cpp                        |  17 ++
- src/wayland_server.h                          |   7 +
- 38 files changed, 935 insertions(+), 6 deletions(-)
- create mode 100644 src/core/syncobjtimeline.cpp
- create mode 100644 src/core/syncobjtimeline.h
- create mode 100644 src/wayland/linux_drm_syncobj_v1.cpp
- create mode 100644 src/wayland/linux_drm_syncobj_v1.h
- create mode 100644 src/wayland/linux_drm_syncobj_v1_p.h
- create mode 100644 src/wayland/protocols/linux-drm-syncobj-v1.xml
-
-diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
-index 4e0db4fe9a9..0130caf3b8e 100644
---- a/src/CMakeLists.txt
-+++ b/src/CMakeLists.txt
-@@ -73,6 +73,7 @@ target_sources(kwin PRIVATE
-     core/session_logind.cpp
-     core/session_noop.cpp
-     core/shmgraphicsbufferallocator.cpp
-+    core/syncobjtimeline.cpp
-     cursor.cpp
-     cursorsource.cpp
-     dbusinterface.cpp
-diff --git a/src/backends/drm/drm_egl_backend.cpp b/src/backends/drm/drm_egl_backend.cpp
-index cfb7a482a03..943407e1d9b 100644
---- a/src/backends/drm/drm_egl_backend.cpp
-+++ b/src/backends/drm/drm_egl_backend.cpp
-@@ -9,6 +9,7 @@
- #include "drm_egl_backend.h"
- #include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h"
- // kwin
-+#include "core/syncobjtimeline.h"
- #include "drm_abstract_output.h"
- #include "drm_backend.h"
- #include "drm_egl_cursor_layer.h"
-@@ -199,6 +200,21 @@ DrmGpu *EglGbmBackend::gpu() const
-     return m_backend->primaryGpu();
- }
- 
-+bool EglGbmBackend::supportsTimelines() const
-+{
-+    return m_backend->primaryGpu()->syncObjTimelinesSupported();
-+}
-+
-+std::unique_ptr<SyncTimeline> EglGbmBackend::importTimeline(FileDescriptor &&syncObjFd)
-+{
-+    uint32_t handle = 0;
-+    if (drmSyncobjFDToHandle(m_backend->primaryGpu()->fd(), syncObjFd.get(), &handle) != 0) {
-+        qCWarning(KWIN_DRM) << "importing syncobj timeline failed!" << strerror(errno);
-+        return nullptr;
-+    }
-+    return std::make_unique<SyncTimeline>(m_backend->primaryGpu()->fd(), handle);
-+}
-+
- } // namespace KWin
- 
- #include "moc_drm_egl_backend.cpp"
-diff --git a/src/backends/drm/drm_egl_backend.h b/src/backends/drm/drm_egl_backend.h
-index 43c431f07be..4a47e900cd3 100644
---- a/src/backends/drm/drm_egl_backend.h
-+++ b/src/backends/drm/drm_egl_backend.h
-@@ -63,6 +63,9 @@ public:
-     EglDisplay *displayForGpu(DrmGpu *gpu);
-     std::shared_ptr<EglContext> contextForGpu(DrmGpu *gpu);
- 
-+    bool supportsTimelines() const override;
-+    std::unique_ptr<SyncTimeline> importTimeline(FileDescriptor &&syncObjFd) override;
-+
- private:
-     bool initializeEgl();
-     bool initRenderingContext();
-diff --git a/src/backends/drm/drm_gpu.cpp b/src/backends/drm/drm_gpu.cpp
-index c5d258a2d7c..7141ed8beac 100644
---- a/src/backends/drm/drm_gpu.cpp
-+++ b/src/backends/drm/drm_gpu.cpp
-@@ -76,6 +76,9 @@ DrmGpu::DrmGpu(DrmBackend *backend, const QString &devNode, int fd, dev_t device
-     m_addFB2ModifiersSupported = drmGetCap(fd, DRM_CAP_ADDFB2_MODIFIERS, &capability) == 0 && capability == 1;
-     qCDebug(KWIN_DRM) << "drmModeAddFB2WithModifiers is" << (m_addFB2ModifiersSupported ? "supported" : "not supported") << "on GPU" << m_devNode;
- 
-+    m_supportsSyncTimelines = drmGetCap(fd, DRM_CAP_SYNCOBJ_TIMELINE, &capability) == 0 && capability == 1;
-+    qCDebug(KWIN_DRM) << "sync obj timelines are" << (m_supportsSyncTimelines ? "supported" : "not supported") << "on GPU" << this;
-+
-     // find out what driver this kms device is using
-     DrmUniquePtr<drmVersion> version(drmGetVersion(fd));
-     m_isI915 = strstr(version->name, "i915");
-@@ -699,6 +702,11 @@ bool DrmGpu::asyncPageflipSupported() const
-     return m_asyncPageflipSupported;
- }
- 
-+bool DrmGpu::syncObjTimelinesSupported() const
-+{
-+    return m_supportsSyncTimelines;
-+}
-+
- bool DrmGpu::isI915() const
- {
-     return m_isI915;
-diff --git a/src/backends/drm/drm_gpu.h b/src/backends/drm/drm_gpu.h
-index 0cc6b2126a3..c8dbde47af7 100644
---- a/src/backends/drm/drm_gpu.h
-+++ b/src/backends/drm/drm_gpu.h
-@@ -77,6 +77,7 @@ public:
-     bool atomicModeSetting() const;
-     bool addFB2ModifiersSupported() const;
-     bool asyncPageflipSupported() const;
-+    bool syncObjTimelinesSupported() const;
-     bool isI915() const;
-     bool isNVidia() const;
-     bool isVmwgfx() const;
-@@ -146,6 +147,7 @@ private:
-     bool m_isRemoved = false;
-     bool m_isActive = true;
-     bool m_forceModeset = false;
-+    bool m_supportsSyncTimelines = false;
-     clockid_t m_presentationClock;
-     gbm_device *m_gbmDevice;
-     FileDescriptor m_gbmFd;
-diff --git a/src/compositor_wayland.cpp b/src/compositor_wayland.cpp
-index f63ead77964..69916e9d348 100644
---- a/src/compositor_wayland.cpp
-+++ b/src/compositor_wayland.cpp
-@@ -86,7 +86,7 @@ bool WaylandCompositor::attemptOpenGLCompositing()
-     }
- 
-     m_scene = std::make_unique<WorkspaceSceneOpenGL>(backend.get());
--    m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererOpenGL>());
-+    m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererOpenGL>(backend->eglDisplayObject()));
-     m_backend = std::move(backend);
- 
-     qCDebug(KWIN_CORE) << "OpenGL compositing has been successfully initialized";
-diff --git a/src/core/graphicsbuffer.cpp b/src/core/graphicsbuffer.cpp
-index f234d09fa9f..e83fcd7bf8a 100644
---- a/src/core/graphicsbuffer.cpp
-+++ b/src/core/graphicsbuffer.cpp
-@@ -40,6 +40,7 @@ void GraphicsBuffer::unref()
-         if (m_dropped) {
-             delete this;
-         } else {
-+            m_releasePoints.clear();
-             Q_EMIT released();
-         }
-     }
-@@ -73,6 +74,11 @@ const ShmAttributes *GraphicsBuffer::shmAttributes() const
-     return nullptr;
- }
- 
-+void GraphicsBuffer::addReleasePoint(const std::shared_ptr<SyncReleasePoint> &releasePoint)
-+{
-+    m_releasePoints.push_back(releasePoint);
-+}
-+
- bool GraphicsBuffer::alphaChannelFromDrmFormat(uint32_t format)
- {
-     const auto info = FormatInfo::get(format);
-diff --git a/src/core/graphicsbuffer.h b/src/core/graphicsbuffer.h
-index cacf49dde5a..2e046e58de0 100644
---- a/src/core/graphicsbuffer.h
-+++ b/src/core/graphicsbuffer.h
-@@ -16,6 +16,8 @@
- namespace KWin
- {
- 
-+class SyncReleasePoint;
-+
- struct DmaBufAttributes
- {
-     int planeCount = 0;
-@@ -87,6 +89,11 @@ public:
-     virtual const DmaBufAttributes *dmabufAttributes() const;
-     virtual const ShmAttributes *shmAttributes() const;
- 
-+    /**
-+     * the added release point will be referenced as long as this buffer is referenced
-+     */
-+    void addReleasePoint(const std::shared_ptr<SyncReleasePoint> &releasePoint);
-+
-     static bool alphaChannelFromDrmFormat(uint32_t format);
- 
- Q_SIGNALS:
-@@ -95,6 +102,7 @@ Q_SIGNALS:
- protected:
-     int m_refCount = 0;
-     bool m_dropped = false;
-+    std::vector<std::shared_ptr<SyncReleasePoint>> m_releasePoints;
- };
- 
- /**
-diff --git a/src/core/renderbackend.cpp b/src/core/renderbackend.cpp
-index 98276385cec..5bbf37d9a4b 100644
---- a/src/core/renderbackend.cpp
-+++ b/src/core/renderbackend.cpp
-@@ -7,6 +7,7 @@
- #include "renderbackend.h"
- #include "renderloop_p.h"
- #include "scene/surfaceitem.h"
-+#include "syncobjtimeline.h"
- 
- #include <drm_fourcc.h>
- 
-@@ -103,6 +104,16 @@ std::unique_ptr<SurfaceTexture> RenderBackend::createSurfaceTextureWayland(Surfa
-     return nullptr;
- }
- 
-+bool RenderBackend::supportsTimelines() const
-+{
-+    return false;
-+}
-+
-+std::unique_ptr<SyncTimeline> RenderBackend::importTimeline(FileDescriptor &&syncObjFd)
-+{
-+    return nullptr;
-+}
-+
- } // namespace KWin
- 
- #include "moc_renderbackend.cpp"
-diff --git a/src/core/renderbackend.h b/src/core/renderbackend.h
-index b902a28984a..682e35aa0b8 100644
---- a/src/core/renderbackend.h
-+++ b/src/core/renderbackend.h
-@@ -8,6 +8,7 @@
- 
- #include "core/rendertarget.h"
- #include "effect/globals.h"
-+#include "utils/filedescriptor.h"
- 
- #include <QObject>
- 
-@@ -26,6 +27,7 @@ class SurfacePixmapX11;
- class SurfaceTexture;
- class PresentationFeedback;
- class RenderLoop;
-+class SyncTimeline;
- 
- class PresentationFeedback
- {
-@@ -88,6 +90,9 @@ public:
- 
-     virtual std::unique_ptr<SurfaceTexture> createSurfaceTextureX11(SurfacePixmapX11 *pixmap);
-     virtual std::unique_ptr<SurfaceTexture> createSurfaceTextureWayland(SurfacePixmap *pixmap);
-+
-+    virtual bool supportsTimelines() const;
-+    virtual std::unique_ptr<SyncTimeline> importTimeline(FileDescriptor &&syncObjFd);
- };
- 
- } // namespace KWin
-diff --git a/src/core/syncobjtimeline.cpp b/src/core/syncobjtimeline.cpp
-new file mode 100644
-index 00000000000..3e2c100370a
---- /dev/null
-+++ b/src/core/syncobjtimeline.cpp
-@@ -0,0 +1,83 @@
-+/*
-+    SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
-+
-+    SPDX-License-Identifier: GPL-2.0-or-later
-+*/
-+#include "syncobjtimeline.h"
-+
-+#include <sys/eventfd.h>
-+#include <xf86drm.h>
-+
-+namespace KWin
-+{
-+
-+SyncReleasePoint::SyncReleasePoint(const std::shared_ptr<SyncTimeline> &timeline, uint64_t timelinePoint)
-+    : m_timeline(timeline)
-+    , m_timelinePoint(timelinePoint)
-+{
-+}
-+
-+SyncReleasePoint::~SyncReleasePoint()
-+{
-+    m_timeline->signal(m_timelinePoint);
-+}
-+
-+SyncTimeline *SyncReleasePoint::timeline() const
-+{
-+    return m_timeline.get();
-+}
-+
-+uint64_t SyncReleasePoint::timelinePoint() const
-+{
-+    return m_timelinePoint;
-+}
-+
-+SyncTimeline::SyncTimeline(int drmFd, uint32_t handle)
-+    : m_drmFd(drmFd)
-+    , m_handle(handle)
-+{
-+}
-+
-+SyncTimeline::~SyncTimeline()
-+{
-+    drmSyncobjDestroy(m_drmFd, m_handle);
-+}
-+
-+FileDescriptor SyncTimeline::eventFd(uint64_t timelinePoint) const
-+{
-+    FileDescriptor ret{eventfd(0, EFD_CLOEXEC)};
-+    if (!ret.isValid()) {
-+        return {};
-+    }
-+    struct drm_syncobj_eventfd args
-+    {
-+        .handle = m_handle,
-+        .flags = 0,
-+        .point = timelinePoint,
-+        .fd = ret.get(),
-+    };
-+    if (drmIoctl(m_drmFd, DRM_IOCTL_SYNCOBJ_EVENTFD, &args) != 0) {
-+        return {};
-+    }
-+    return ret;
-+}
-+
-+void SyncTimeline::signal(uint64_t timelinePoint)
-+{
-+    drmSyncobjTimelineSignal(m_drmFd, &m_handle, &timelinePoint, 1);
-+}
-+
-+SyncReleasePointHolder::SyncReleasePointHolder(FileDescriptor &&requirement, std::unordered_set<std::shared_ptr<SyncReleasePoint>> &&releasePoints)
-+    : m_fence(std::move(requirement))
-+    , m_notifier(m_fence.get(), QSocketNotifier::Type::Read)
-+    , m_releasePoints(std::move(releasePoints))
-+{
-+    connect(&m_notifier, &QSocketNotifier::activated, this, &SyncReleasePointHolder::signaled);
-+    m_notifier.setEnabled(true);
-+}
-+
-+void SyncReleasePointHolder::signaled()
-+{
-+    delete this;
-+}
-+}
-diff --git a/src/core/syncobjtimeline.h b/src/core/syncobjtimeline.h
-new file mode 100644
-index 00000000000..cffdb75e330
---- /dev/null
-+++ b/src/core/syncobjtimeline.h
-@@ -0,0 +1,72 @@
-+/*
-+    SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
-+
-+    SPDX-License-Identifier: GPL-2.0-or-later
-+*/
-+#pragma once
-+#include "kwin_export.h"
-+#include "utils/filedescriptor.h"
-+
-+#include <QObject>
-+#include <QSocketNotifier>
-+#include <memory>
-+#include <stdint.h>
-+#include <unordered_set>
-+
-+namespace KWin
-+{
-+
-+class SyncTimeline;
-+
-+/**
-+ * A helper to signal the release point when it goes out of scope
-+ */
-+class KWIN_EXPORT SyncReleasePoint
-+{
-+public:
-+    explicit SyncReleasePoint(const std::shared_ptr<SyncTimeline> &timeline, uint64_t timelinePoint);
-+    ~SyncReleasePoint();
-+
-+    SyncTimeline *timeline() const;
-+    uint64_t timelinePoint() const;
-+
-+private:
-+    const std::shared_ptr<SyncTimeline> m_timeline;
-+    const uint64_t m_timelinePoint;
-+};
-+
-+class KWIN_EXPORT SyncTimeline
-+{
-+public:
-+    explicit SyncTimeline(int drmFd, uint32_t handle);
-+    ~SyncTimeline();
-+
-+    /**
-+     * @returns an event fd that gets signalled when the timeline point gets signalled
-+     */
-+    FileDescriptor eventFd(uint64_t timelinePoint) const;
-+
-+    void signal(uint64_t timelinePoint);
-+
-+private:
-+    const int32_t m_drmFd;
-+    const uint32_t m_handle;
-+};
-+
-+class SyncReleasePointHolder : public QObject
-+{
-+    Q_OBJECT
-+public:
-+    /**
-+     * @param requirement the filedescriptor that needs to be readable before the release points may be signalled. Once that's happened, this object deletes itself!'
-+     */
-+    explicit SyncReleasePointHolder(FileDescriptor &&requirement, std::unordered_set<std::shared_ptr<SyncReleasePoint>> &&releasePoints);
-+
-+private:
-+    void signaled();
-+
-+    const FileDescriptor m_fence;
-+    QSocketNotifier m_notifier;
-+    const std::unordered_set<std::shared_ptr<SyncReleasePoint>> m_releasePoints;
-+};
-+}
-diff --git a/src/opengl/eglnativefence.cpp b/src/opengl/eglnativefence.cpp
-index e68098bc768..13c42ade543 100644
---- a/src/opengl/eglnativefence.cpp
-+++ b/src/opengl/eglnativefence.cpp
-@@ -51,6 +51,11 @@ const FileDescriptor &EGLNativeFence::fileDescriptor() const
-     return m_fileDescriptor;
- }
- 
-+FileDescriptor &&EGLNativeFence::fileDescriptor()
-+{
-+    return std::move(m_fileDescriptor);
-+}
-+
- bool EGLNativeFence::waitSync() const
- {
-     return eglWaitSync(m_display->handle(), m_sync, 0) == EGL_TRUE;
-diff --git a/src/opengl/eglnativefence.h b/src/opengl/eglnativefence.h
-index d5f6bdbbba2..05c248ff8e0 100644
---- a/src/opengl/eglnativefence.h
-+++ b/src/opengl/eglnativefence.h
-@@ -27,6 +27,7 @@ public:
- 
-     bool isValid() const;
-     const FileDescriptor &fileDescriptor() const;
-+    FileDescriptor &&fileDescriptor();
-     bool waitSync() const;
- 
-     static EGLNativeFence importFence(EglDisplay *display, FileDescriptor &&fd);
-diff --git a/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp b/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp
-index 4e227aef462..4ad2064148b 100644
---- a/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp
-+++ b/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp
-@@ -13,6 +13,7 @@
- #include "opengl/egl_context_attribute_builder.h"
- #include "utils/common.h"
- #include "wayland/drmclientbuffer.h"
-+#include "wayland/linux_drm_syncobj_v1.h"
- #include "wayland_server.h"
- // kwin libs
- #include "opengl/eglimagetexture.h"
-@@ -217,9 +218,13 @@ void AbstractEglBackend::initWayland()
-         .formatTable = includeShaderConversions(filterFormats({}, true)),
-     });
- 
-+    waylandServer()->setRenderBackend(this);
-     LinuxDmaBufV1ClientBufferIntegration *dmabuf = waylandServer()->linuxDmabuf();
-     dmabuf->setRenderBackend(this);
-     dmabuf->setSupportedFormatsWithModifiers(m_tranches);
-+    if (auto syncObj = waylandServer()->linuxSyncObj()) {
-+        syncObj->setRenderBackend(this);
-+    }
- }
- 
- void AbstractEglBackend::initClientExtensions()
-diff --git a/src/platformsupport/scenes/opengl/abstract_egl_backend.h b/src/platformsupport/scenes/opengl/abstract_egl_backend.h
-index 0abd331ed6b..1f77898db50 100644
---- a/src/platformsupport/scenes/opengl/abstract_egl_backend.h
-+++ b/src/platformsupport/scenes/opengl/abstract_egl_backend.h
-@@ -34,7 +34,7 @@ public:
- 
-     EGLSurface surface() const;
-     EGLConfig config() const;
--    EglDisplay *eglDisplayObject() const;
-+    EglDisplay *eglDisplayObject() const override;
-     EglContext *contextObject();
- 
-     bool testImportBuffer(GraphicsBuffer *buffer) override;
-diff --git a/src/platformsupport/scenes/opengl/openglbackend.cpp b/src/platformsupport/scenes/opengl/openglbackend.cpp
-index 92ca501d96e..0bb6de64021 100644
---- a/src/platformsupport/scenes/opengl/openglbackend.cpp
-+++ b/src/platformsupport/scenes/opengl/openglbackend.cpp
-@@ -93,6 +93,10 @@ bool OpenGLBackend::checkGraphicsReset()
-     return true;
- }
- 
-+EglDisplay *OpenGLBackend::eglDisplayObject() const
-+{
-+    return nullptr;
-+}
- }
- 
- #include "moc_openglbackend.cpp"
-diff --git a/src/platformsupport/scenes/opengl/openglbackend.h b/src/platformsupport/scenes/opengl/openglbackend.h
-index 3318f4da187..e0749561826 100644
---- a/src/platformsupport/scenes/opengl/openglbackend.h
-+++ b/src/platformsupport/scenes/opengl/openglbackend.h
-@@ -19,6 +19,7 @@ namespace KWin
- class Output;
- class OpenGLBackend;
- class GLTexture;
-+class EglDisplay;
- 
- /**
-  * @brief The OpenGLBackend creates and holds the OpenGL context and is responsible for Texture from Pixmap.
-@@ -97,6 +98,8 @@ public:
- 
-     virtual std::pair<std::shared_ptr<GLTexture>, ColorDescription> textureForOutput(Output *output) const;
- 
-+    virtual EglDisplay *eglDisplayObject() const;
-+
- protected:
-     /**
-      * @brief Sets the backend initialization to failed.
-diff --git a/src/scene/item.h b/src/scene/item.h
-index 5e9e4f1c92a..b77fc91c64b 100644
---- a/src/scene/item.h
-+++ b/src/scene/item.h
-@@ -22,6 +22,7 @@ namespace KWin
- 
- class SceneDelegate;
- class Scene;
-+class SyncReleasePoint;
- 
- /**
-  * The Item class is the base class for items in the scene.
-diff --git a/src/scene/itemrenderer_opengl.cpp b/src/scene/itemrenderer_opengl.cpp
-index 44ec0918e33..3fee3863f2c 100644
---- a/src/scene/itemrenderer_opengl.cpp
-+++ b/src/scene/itemrenderer_opengl.cpp
-@@ -8,7 +8,9 @@
- #include "core/pixelgrid.h"
- #include "core/rendertarget.h"
- #include "core/renderviewport.h"
-+#include "core/syncobjtimeline.h"
- #include "effect/effect.h"
-+#include "opengl/eglnativefence.h"
- #include "platformsupport/scenes/opengl/openglsurfacetexture.h"
- #include "scene/decorationitem.h"
- #include "scene/imageitem.h"
-@@ -20,7 +22,8 @@
- namespace KWin
- {
- 
--ItemRendererOpenGL::ItemRendererOpenGL()
-+ItemRendererOpenGL::ItemRendererOpenGL(EglDisplay *eglDisplay)
-+    : m_eglDisplay(eglDisplay)
- {
-     const QString visualizeOptionsString = qEnvironmentVariable("KWIN_SCENE_VISUALIZE");
-     if (!visualizeOptionsString.isEmpty()) {
-@@ -46,6 +49,14 @@ void ItemRendererOpenGL::endFrame()
- {
-     GLVertexBuffer::streamingBuffer()->endOfFrame();
-     GLFramebuffer::popFramebuffer();
-+
-+    if (m_eglDisplay) {
-+        EGLNativeFence fence(m_eglDisplay);
-+        if (fence.isValid()) {
-+            new SyncReleasePointHolder(std::move(fence.fileDescriptor()), std::move(m_releasePoints));
-+        }
-+    }
-+    m_releasePoints.clear();
- }
- 
- QVector4D ItemRendererOpenGL::modulate(float opacity, float brightness) const
-@@ -173,6 +184,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context)
-                 .coordinateType = UnnormalizedCoordinates,
-                 .scale = scale,
-                 .colorDescription = item->colorDescription(),
-+                .bufferReleasePoint = nullptr,
-             });
-         }
-     } else if (auto decorationItem = qobject_cast<DecorationItem *>(item)) {
-@@ -187,6 +199,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context)
-                 .coordinateType = UnnormalizedCoordinates,
-                 .scale = scale,
-                 .colorDescription = item->colorDescription(),
-+                .bufferReleasePoint = nullptr,
-             });
-         }
-     } else if (auto surfaceItem = qobject_cast<SurfaceItem *>(item)) {
-@@ -202,6 +215,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context)
-                     .coordinateType = NormalizedCoordinates,
-                     .scale = scale,
-                     .colorDescription = item->colorDescription(),
-+                    .bufferReleasePoint = surfaceItem->bufferReleasePoint(),
-                 });
-             }
-         }
-@@ -216,6 +230,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context)
-                 .coordinateType = NormalizedCoordinates,
-                 .scale = scale,
-                 .colorDescription = item->colorDescription(),
-+                .bufferReleasePoint = nullptr,
-             });
-         }
-     }
-@@ -407,6 +422,9 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
-                 contents.planes[plane]->unbind();
-             }
-         }
-+        if (renderNode.bufferReleasePoint) {
-+            m_releasePoints.insert(renderNode.bufferReleasePoint);
-+        }
-     }
-     if (shader) {
-         ShaderManager::instance()->popShader();
-diff --git a/src/scene/itemrenderer_opengl.h b/src/scene/itemrenderer_opengl.h
-index c35839335c6..11ab8cea386 100644
---- a/src/scene/itemrenderer_opengl.h
-+++ b/src/scene/itemrenderer_opengl.h
-@@ -10,9 +10,13 @@
- #include "platformsupport/scenes/opengl/openglsurfacetexture.h"
- #include "scene/itemrenderer.h"
- 
-+#include <unordered_set>
-+
- namespace KWin
- {
- 
-+class EglDisplay;
-+
- class KWIN_EXPORT ItemRendererOpenGL : public ItemRenderer
- {
- public:
-@@ -28,6 +32,7 @@ public:
-         TextureCoordinateType coordinateType = UnnormalizedCoordinates;
-         qreal scale = 1.0;
-         ColorDescription colorDescription;
-+        std::shared_ptr<SyncReleasePoint> bufferReleasePoint;
-     };
- 
-     struct RenderContext
-@@ -41,7 +46,7 @@ public:
-         const qreal renderTargetScale;
-     };
- 
--    ItemRendererOpenGL();
-+    ItemRendererOpenGL(EglDisplay *eglDisplay);
- 
-     void beginFrame(const RenderTarget &renderTarget, const RenderViewport &viewport) override;
-     void endFrame() override;
-@@ -58,6 +63,8 @@ private:
-     void visualizeFractional(const RenderViewport &viewport, const QRegion &region, const RenderContext &renderContext);
- 
-     bool m_blendingEnabled = false;
-+    EglDisplay *const m_eglDisplay;
-+    std::unordered_set<std::shared_ptr<SyncReleasePoint>> m_releasePoints;
- 
-     struct
-     {
-diff --git a/src/scene/surfaceitem.cpp b/src/scene/surfaceitem.cpp
-index 4404dbc3c6c..f04053f3566 100644
---- a/src/scene/surfaceitem.cpp
-+++ b/src/scene/surfaceitem.cpp
-@@ -270,6 +270,11 @@ std::chrono::nanoseconds SurfaceItem::frameTimeEstimation() const
-     }
- }
- 
-+std::shared_ptr<SyncReleasePoint> SurfaceItem::bufferReleasePoint() const
-+{
-+    return m_bufferReleasePoint;
-+}
-+
- SurfaceTexture::~SurfaceTexture()
- {
- }
-diff --git a/src/scene/surfaceitem.h b/src/scene/surfaceitem.h
-index 5dfcfd0ad16..f6c8ca5d56d 100644
---- a/src/scene/surfaceitem.h
-+++ b/src/scene/surfaceitem.h
-@@ -38,6 +38,8 @@ public:
-     QSize bufferSize() const;
-     void setBufferSize(const QSize &size);
- 
-+    std::shared_ptr<SyncReleasePoint> bufferReleasePoint() const;
-+
-     QRegion mapFromBuffer(const QRegion &region) const;
- 
-     void addDamage(const QRegion &region);
-@@ -82,6 +84,7 @@ protected:
-     std::deque<std::chrono::nanoseconds> m_lastDamageTimeDiffs;
-     std::optional<std::chrono::steady_clock::time_point> m_lastDamage;
-     std::chrono::nanoseconds m_frameTimeEstimation = std::chrono::days(1000);
-+    std::shared_ptr<SyncReleasePoint> m_bufferReleasePoint;
- };
- 
- class KWIN_EXPORT SurfaceTexture
-diff --git a/src/scene/surfaceitem_wayland.cpp b/src/scene/surfaceitem_wayland.cpp
-index 82863c3a456..6f16c47142f 100644
---- a/src/scene/surfaceitem_wayland.cpp
-+++ b/src/scene/surfaceitem_wayland.cpp
-@@ -41,6 +41,7 @@ SurfaceItemWayland::SurfaceItemWayland(SurfaceInterface *surface, Scene *scene,
-             this, &SurfaceItemWayland::handleColorDescriptionChanged);
-     connect(surface, &SurfaceInterface::presentationModeHintChanged,
-             this, &SurfaceItemWayland::handlePresentationModeHintChanged);
-+    connect(surface, &SurfaceInterface::bufferReleasePointChanged, this, &SurfaceItemWayland::handleReleasePointChanged);
- 
-     SubSurfaceInterface *subsurface = surface->subSurface();
-     if (subsurface) {
-@@ -184,6 +185,11 @@ void SurfaceItemWayland::handlePresentationModeHintChanged()
-     setPresentationHint(m_surface->presentationModeHint());
- }
- 
-+void SurfaceItemWayland::handleReleasePointChanged()
-+{
-+    m_bufferReleasePoint = m_surface->bufferReleasePoint();
-+}
-+
- SurfacePixmapWayland::SurfacePixmapWayland(SurfaceItemWayland *item, QObject *parent)
-     : SurfacePixmap(Compositor::self()->backend()->createSurfaceTextureWayland(this), parent)
-     , m_item(item)
-diff --git a/src/scene/surfaceitem_wayland.h b/src/scene/surfaceitem_wayland.h
-index ab31192cb9a..c428f1ea417 100644
---- a/src/scene/surfaceitem_wayland.h
-+++ b/src/scene/surfaceitem_wayland.h
-@@ -48,6 +48,7 @@ private Q_SLOTS:
-     void handleSubSurfaceMappedChanged();
-     void handleColorDescriptionChanged();
-     void handlePresentationModeHintChanged();
-+    void handleReleasePointChanged();
- 
- protected:
-     std::unique_ptr<SurfacePixmap> createPixmap() override;
-diff --git a/src/scene/workspacescene_opengl.cpp b/src/scene/workspacescene_opengl.cpp
-index 3a024f56bd9..aabee9be151 100644
---- a/src/scene/workspacescene_opengl.cpp
-+++ b/src/scene/workspacescene_opengl.cpp
-@@ -40,7 +40,7 @@ namespace KWin
-  ***********************************************/
- 
- WorkspaceSceneOpenGL::WorkspaceSceneOpenGL(OpenGLBackend *backend)
--    : WorkspaceScene(std::make_unique<ItemRendererOpenGL>())
-+    : WorkspaceScene(std::make_unique<ItemRendererOpenGL>(backend->eglDisplayObject()))
-     , m_backend(backend)
- {
- }
-diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt
-index 41ee1c5581a..9d2add111fc 100644
---- a/src/wayland/CMakeLists.txt
-+++ b/src/wayland/CMakeLists.txt
-@@ -231,6 +231,10 @@ ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
-     PROTOCOL ${PROJECT_SOURCE_DIR}/src/wayland/protocols/xx-color-management-v2.xml
-     BASENAME xx-color-management-v2
- )
-+ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
-+    PROTOCOL ${PROJECT_SOURCE_DIR}/src/wayland/protocols/linux-drm-syncobj-v1.xml
-+    BASENAME linux-drm-syncobj-v1
-+)
- 
- target_sources(kwin PRIVATE
-     abstract_data_source.cpp
-@@ -265,6 +269,7 @@ target_sources(kwin PRIVATE
-     keyboard_shortcuts_inhibit_v1.cpp
-     keystate.cpp
-     layershell_v1.cpp
-+    linux_drm_syncobj_v1.cpp
-     linuxdmabufv1clientbuffer.cpp
-     lockscreen_overlay_v1.cpp
-     output.cpp
-@@ -344,6 +349,7 @@ install(FILES
-     keyboard_shortcuts_inhibit_v1.h
-     keystate.h
-     layershell_v1.h
-+    linux_drm_syncobj_v1.h
-     lockscreen_overlay_v1.h
-     output.h
-     output_order_v1.h
-@@ -390,10 +396,12 @@ install(FILES
- 
-     ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-content-type-v1.h
-     ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-frog-color-management-v1.h
-+    ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-linux-drm-syncobj-v1.h
-     ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-presentation-time.h
-     ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-xx-color-management-v2.h
-     ${CMAKE_CURRENT_BINARY_DIR}/wayland-content-type-v1-server-protocol.h
-     ${CMAKE_CURRENT_BINARY_DIR}/wayland-frog-color-management-v1-server-protocol.h
-+    ${CMAKE_CURRENT_BINARY_DIR}/wayland-linux-drm-syncobj-v1-server-protocol.h
-     ${CMAKE_CURRENT_BINARY_DIR}/wayland-presentation-time-server-protocol.h
-     ${CMAKE_CURRENT_BINARY_DIR}/wayland-xx-color-management-v2-server-protocol.h
- 
-diff --git a/src/wayland/linux_drm_syncobj_v1.cpp b/src/wayland/linux_drm_syncobj_v1.cpp
-new file mode 100644
-index 00000000000..589ccd78c3b
---- /dev/null
-+++ b/src/wayland/linux_drm_syncobj_v1.cpp
-@@ -0,0 +1,190 @@
-+/*
-+    KWin - the KDE window manager
-+    This file is part of the KDE project.
-+
-+    SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
-+
-+    SPDX-License-Identifier: GPL-2.0-or-later
-+*/
-+#include "linux_drm_syncobj_v1.h"
-+#include "core/syncobjtimeline.h"
-+#include "display.h"
-+#include "linux_drm_syncobj_v1_p.h"
-+#include "surface.h"
-+#include "surface_p.h"
-+#include "transaction.h"
-+#include "utils/resource.h"
-+
-+#include <xf86drm.h>
-+
-+namespace KWin
-+{
-+
-+static constexpr uint32_t s_version = 1;
-+
-+LinuxDrmSyncObjV1Interface::LinuxDrmSyncObjV1Interface(Display *display, QObject *parent)
-+    : QObject(parent)
-+    , QtWaylandServer::wp_linux_drm_syncobj_manager_v1(*display, s_version)
-+{
-+}
-+
-+void LinuxDrmSyncObjV1Interface::wp_linux_drm_syncobj_manager_v1_get_surface(Resource *resource, uint32_t id, wl_resource *surface)
-+{
-+    SurfaceInterface *surf = SurfaceInterface::get(surface);
-+    SurfaceInterfacePrivate *priv = SurfaceInterfacePrivate::get(surf);
-+    if (priv->syncObjV1) {
-+        wl_resource_post_error(resource->handle, error_surface_exists, "surface already exists");
-+        return;
-+    }
-+    priv->syncObjV1 = new LinuxDrmSyncObjSurfaceV1(surf, resource->client(), id);
-+}
-+
-+void LinuxDrmSyncObjV1Interface::wp_linux_drm_syncobj_manager_v1_import_timeline(Resource *resource, uint32_t id, int32_t rawFd)
-+{
-+    FileDescriptor fd(rawFd);
-+    // TODO add a GPU abstraction, instead of using the render backend
-+    if (!m_renderBackend || isGlobalRemoved()) {
-+        // to not crash the client, create an inert timeline
-+        new LinuxDrmSyncObjTimelineV1(resource->client(), id, nullptr);
-+        return;
-+    }
-+    auto timeline = m_renderBackend->importTimeline(std::move(fd));
-+    if (!timeline) {
-+        wl_resource_post_error(resource->handle, WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_INVALID_TIMELINE, "Importing timeline failed");
-+        return;
-+    }
-+    new LinuxDrmSyncObjTimelineV1(resource->client(), id, std::move(timeline));
-+}
-+
-+void LinuxDrmSyncObjV1Interface::setRenderBackend(RenderBackend *backend)
-+{
-+    m_renderBackend = backend;
-+}
-+
-+void LinuxDrmSyncObjV1Interface::wp_linux_drm_syncobj_manager_v1_destroy(Resource *resource)
-+{
-+    wl_resource_destroy(resource->handle);
-+}
-+
-+void LinuxDrmSyncObjV1Interface::remove()
-+{
-+    QtWaylandServer::wp_linux_drm_syncobj_manager_v1::globalRemove();
-+}
-+
-+void LinuxDrmSyncObjV1Interface::wp_linux_drm_syncobj_manager_v1_destroy_global()
-+{
-+    delete this;
-+}
-+
-+LinuxDrmSyncObjTimelineV1::LinuxDrmSyncObjTimelineV1(wl_client *client, uint32_t id, std::unique_ptr<SyncTimeline> &&timeline)
-+    : QtWaylandServer::wp_linux_drm_syncobj_timeline_v1(client, id, s_version)
-+    , m_timeline(std::move(timeline))
-+{
-+}
-+
-+LinuxDrmSyncObjTimelineV1::~LinuxDrmSyncObjTimelineV1()
-+{
-+}
-+
-+void LinuxDrmSyncObjTimelineV1::wp_linux_drm_syncobj_timeline_v1_destroy_resource(Resource *resource)
-+{
-+    delete this;
-+}
-+
-+void LinuxDrmSyncObjTimelineV1::wp_linux_drm_syncobj_timeline_v1_destroy(Resource *resource)
-+{
-+    wl_resource_destroy(resource->handle);
-+}
-+
-+std::shared_ptr<SyncTimeline> LinuxDrmSyncObjTimelineV1::timeline() const
-+{
-+    return m_timeline;
-+}
-+
-+LinuxDrmSyncObjSurfaceV1::LinuxDrmSyncObjSurfaceV1(SurfaceInterface *surface, wl_client *client, uint32_t id)
-+    : QtWaylandServer::wp_linux_drm_syncobj_surface_v1(client, id, s_version)
-+    , m_surface(surface)
-+{
-+}
-+
-+LinuxDrmSyncObjSurfaceV1::~LinuxDrmSyncObjSurfaceV1()
-+{
-+    if (m_surface) {
-+        SurfaceInterfacePrivate::get(m_surface)->syncObjV1 = nullptr;
-+    }
-+}
-+
-+void LinuxDrmSyncObjSurfaceV1::wp_linux_drm_syncobj_surface_v1_set_acquire_point(Resource *resource, wl_resource *timeline_resource, uint32_t point_hi, uint32_t point_lo)
-+{
-+    if (!m_surface) {
-+        wl_resource_post_error(resource->handle, WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface got destroyed already");
-+        return;
-+    }
-+    const auto timeline = resource_cast<LinuxDrmSyncObjTimelineV1 *>(timeline_resource);
-+    if (!timeline->timeline()) {
-+        // in the normal case this should never happen, but if it does,
-+        // there's nothing we can do about it without killing the client
-+        return;
-+    }
-+    const uint64_t point = (uint64_t(point_hi) << 32) | point_lo;
-+    const auto priv = SurfaceInterfacePrivate::get(m_surface);
-+    priv->pending->acquirePoint.timeline = timeline->timeline();
-+    priv->pending->acquirePoint.point = point;
-+}
-+
-+void LinuxDrmSyncObjSurfaceV1::wp_linux_drm_syncobj_surface_v1_set_release_point(Resource *resource, wl_resource *timeline_resource, uint32_t point_hi, uint32_t point_lo)
-+{
-+    if (!m_surface) {
-+        wl_resource_post_error(resource->handle, WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface got destroyed already");
-+        return;
-+    }
-+    const auto timeline = resource_cast<LinuxDrmSyncObjTimelineV1 *>(timeline_resource);
-+    if (!timeline->timeline()) {
-+        // in the normal case this should never happen, but if it does,
-+        // there's nothing we can do about it without killing the client
-+        return;
-+    }
-+    const uint64_t point = (uint64_t(point_hi) << 32) | point_lo;
-+    SurfaceInterfacePrivate::get(m_surface)->pending->releasePoint = std::make_unique<SyncReleasePoint>(timeline->timeline(), point);
-+}
-+
-+void LinuxDrmSyncObjSurfaceV1::wp_linux_drm_syncobj_surface_v1_destroy_resource(Resource *resource)
-+{
-+    delete this;
-+}
-+
-+void LinuxDrmSyncObjSurfaceV1::wp_linux_drm_syncobj_surface_v1_destroy(Resource *resource)
-+{
-+    wl_resource_destroy(resource->handle);
-+}
-+
-+bool LinuxDrmSyncObjSurfaceV1::maybeEmitProtocolErrors()
-+{
-+    const auto priv = SurfaceInterfacePrivate::get(m_surface);
-+    if (!priv->pending->bufferIsSet && !priv->pending->acquirePoint.timeline && !priv->pending->releasePoint) {
-+        return false;
-+    }
-+    if (!priv->pending->acquirePoint.timeline) {
-+        wl_resource_post_error(resource()->handle, error_no_acquire_point, "explicit sync is used, but no acquire point is set");
-+        return true;
-+    }
-+    if (!priv->pending->releasePoint) {
-+        wl_resource_post_error(resource()->handle, error_no_release_point, "explicit sync is used, but no release point is set");
-+        return true;
-+    }
-+    if (priv->pending->acquirePoint.timeline.get() == priv->pending->releasePoint->timeline()
-+        && priv->pending->acquirePoint.point >= priv->pending->releasePoint->timelinePoint()) {
-+        wl_resource_post_error(resource()->handle, error_conflicting_points, "acquire and release points are on the same timeline and acquire >= release");
-+        return true;
-+    }
-+    if (!priv->pending->buffer) {
-+        wl_resource_post_error(resource()->handle, error_no_buffer, "explicit sync is used, but no buffer is attached");
-+        return true;
-+    }
-+    if (!priv->pending->buffer->dmabufAttributes()) {
-+        wl_resource_post_error(resource()->handle, error_unsupported_buffer, "only linux dmabuf buffers are allowed to use explicit sync");
-+        return true;
-+    }
-+    return false;
-+}
-+}
-diff --git a/src/wayland/linux_drm_syncobj_v1.h b/src/wayland/linux_drm_syncobj_v1.h
-new file mode 100644
-index 00000000000..ff6ddbe0340
---- /dev/null
-+++ b/src/wayland/linux_drm_syncobj_v1.h
-@@ -0,0 +1,63 @@
-+/*
-+    KWin - the KDE window manager
-+    This file is part of the KDE project.
-+
-+    SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
-+
-+    SPDX-License-Identifier: GPL-2.0-or-later
-+*/
-+#pragma once
-+
-+#include "kwin_export.h"
-+#include "qwayland-server-linux-drm-syncobj-v1.h"
-+
-+#include <QObject>
-+#include <QPointer>
-+
-+namespace KWin
-+{
-+
-+class Display;
-+class SurfaceInterface;
-+class RenderBackend;
-+class SyncTimeline;
-+
-+class KWIN_EXPORT LinuxDrmSyncObjV1Interface : public QObject, private QtWaylandServer::wp_linux_drm_syncobj_manager_v1
-+{
-+    Q_OBJECT
-+public:
-+    explicit LinuxDrmSyncObjV1Interface(Display *display, QObject *parent = nullptr);
-+
-+    void setRenderBackend(RenderBackend *backend);
-+    void remove();
-+
-+private:
-+    void wp_linux_drm_syncobj_manager_v1_get_surface(Resource *resource, uint32_t id, wl_resource *surface) override;
-+    void wp_linux_drm_syncobj_manager_v1_import_timeline(Resource *resource, uint32_t id, int32_t fd) override;
-+    void wp_linux_drm_syncobj_manager_v1_destroy(Resource *resource) override;
-+    void wp_linux_drm_syncobj_manager_v1_destroy_global() override;
-+
-+    QPointer<RenderBackend> m_renderBackend;
-+};
-+
-+class LinuxDrmSyncObjSurfaceV1 : private QtWaylandServer::wp_linux_drm_syncobj_surface_v1
-+{
-+public:
-+    explicit LinuxDrmSyncObjSurfaceV1(SurfaceInterface *surface, wl_client *client, uint32_t id);
-+    ~LinuxDrmSyncObjSurfaceV1() override;
-+
-+    /**
-+     * checks for protocol errors that may need to be sent at commit time
-+     * @returns if any protocol errors were actually emitted
-+     */
-+    bool maybeEmitProtocolErrors();
-+
-+private:
-+    void wp_linux_drm_syncobj_surface_v1_set_acquire_point(Resource *resource, wl_resource *timeline, uint32_t point_hi, uint32_t point_lo) override;
-+    void wp_linux_drm_syncobj_surface_v1_set_release_point(Resource *resource, wl_resource *timeline, uint32_t point_hi, uint32_t point_lo) override;
-+    void wp_linux_drm_syncobj_surface_v1_destroy_resource(Resource *resource) override;
-+    void wp_linux_drm_syncobj_surface_v1_destroy(Resource *resource) override;
-+
-+    const QPointer<SurfaceInterface> m_surface;
-+};
-+}
-diff --git a/src/wayland/linux_drm_syncobj_v1_p.h b/src/wayland/linux_drm_syncobj_v1_p.h
-new file mode 100644
-index 00000000000..6efbc12ee30
---- /dev/null
-+++ b/src/wayland/linux_drm_syncobj_v1_p.h
-@@ -0,0 +1,32 @@
-+/*
-+    KWin - the KDE window manager
-+    This file is part of the KDE project.
-+
-+    SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
-+
-+    SPDX-License-Identifier: GPL-2.0-or-later
-+*/
-+#pragma once
-+#include "linux_drm_syncobj_v1.h"
-+
-+namespace KWin
-+{
-+
-+class LinuxDrmSyncObjTimelineV1 : public QtWaylandServer::wp_linux_drm_syncobj_timeline_v1
-+{
-+public:
-+    explicit LinuxDrmSyncObjTimelineV1(wl_client *client, uint32_t id, std::unique_ptr<SyncTimeline> &&timeline);
-+    ~LinuxDrmSyncObjTimelineV1() override;
-+
-+    /**
-+     * May return nullptr if the timeline resource is inert
-+     */
-+    std::shared_ptr<SyncTimeline> timeline() const;
-+
-+private:
-+    void wp_linux_drm_syncobj_timeline_v1_destroy_resource(Resource *resource) override;
-+    void wp_linux_drm_syncobj_timeline_v1_destroy(Resource *resource) override;
-+
-+    const std::shared_ptr<SyncTimeline> m_timeline;
-+};
-+}
-diff --git a/src/wayland/protocols/linux-drm-syncobj-v1.xml b/src/wayland/protocols/linux-drm-syncobj-v1.xml
-new file mode 100644
-index 00000000000..2c491eaf43a
---- /dev/null
-+++ b/src/wayland/protocols/linux-drm-syncobj-v1.xml
-@@ -0,0 +1,261 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<protocol name="linux_drm_syncobj_v1">
-+  <copyright>
-+    Copyright 2016 The Chromium Authors.
-+    Copyright 2017 Intel Corporation
-+    Copyright 2018 Collabora, Ltd
-+    Copyright 2021 Simon Ser
-+
-+    Permission is hereby granted, free of charge, to any person obtaining a
-+    copy of this software and associated documentation files (the "Software"),
-+    to deal in the Software without restriction, including without limitation
-+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+    and/or sell copies of the Software, and to permit persons to whom the
-+    Software is furnished to do so, subject to the following conditions:
-+
-+    The above copyright notice and this permission notice (including the next
-+    paragraph) shall be included in all copies or substantial portions of the
-+    Software.
-+
-+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+    DEALINGS IN THE SOFTWARE.
-+  </copyright>
-+
-+  <description summary="protocol for providing explicit synchronization">
-+    This protocol allows clients to request explicit synchronization for
-+    buffers. It is tied to the Linux DRM synchronization object framework.
-+
-+    Synchronization refers to co-ordination of pipelined operations performed
-+    on buffers. Most GPU clients will schedule an asynchronous operation to
-+    render to the buffer, then immediately send the buffer to the compositor
-+    to be attached to a surface.
-+
-+    With implicit synchronization, ensuring that the rendering operation is
-+    complete before the compositor displays the buffer is an implementation
-+    detail handled by either the kernel or userspace graphics driver.
-+
-+    By contrast, with explicit synchronization, DRM synchronization object
-+    timeline points mark when the asynchronous operations are complete. When
-+    submitting a buffer, the client provides a timeline point which will be
-+    waited on before the compositor accesses the buffer, and another timeline
-+    point that the compositor will signal when it no longer needs to access the
-+    buffer contents for the purposes of the surface commit.
-+
-+    Linux DRM synchronization objects are documented at:
-+    https://dri.freedesktop.org/docs/drm/gpu/drm-mm.html#drm-sync-objects
-+
-+    Warning! The protocol described in this file is currently in the testing
-+    phase. Backward compatible changes may be added together with the
-+    corresponding interface version bump. Backward incompatible changes can
-+    only be done by creating a new major version of the extension.
-+  </description>
-+
-+  <interface name="wp_linux_drm_syncobj_manager_v1" version="1">
-+    <description summary="global for providing explicit synchronization">
-+      This global is a factory interface, allowing clients to request
-+      explicit synchronization for buffers on a per-surface basis.
-+
-+      See wp_linux_drm_syncobj_surface_v1 for more information.
-+    </description>
-+
-+    <request name="destroy" type="destructor">
-+      <description summary="destroy explicit synchronization factory object">
-+        Destroy this explicit synchronization factory object. Other objects
-+        shall not be affected by this request.
-+      </description>
-+    </request>
-+
-+    <enum name="error">
-+      <entry name="surface_exists" value="0"
-+        summary="the surface already has a synchronization object associated"/>
-+      <entry name="invalid_timeline" value="1"
-+        summary="the timeline object could not be imported"/>
-+    </enum>
-+
-+    <request name="get_surface">
-+      <description summary="extend surface interface for explicit synchronization">
-+        Instantiate an interface extension for the given wl_surface to provide
-+        explicit synchronization.
-+
-+        If the given wl_surface already has an explicit synchronization object
-+        associated, the surface_exists protocol error is raised.
-+
-+        Graphics APIs, like EGL or Vulkan, that manage the buffer queue and
-+        commits of a wl_surface themselves, are likely to be using this
-+        extension internally. If a client is using such an API for a
-+        wl_surface, it should not directly use this extension on that surface,
-+        to avoid raising a surface_exists protocol error.
-+      </description>
-+      <arg name="id" type="new_id" interface="wp_linux_drm_syncobj_surface_v1"
-+        summary="the new synchronization surface object id"/>
-+      <arg name="surface" type="object" interface="wl_surface"
-+        summary="the surface"/>
-+    </request>
-+
-+    <request name="import_timeline">
-+      <description summary="import a DRM syncobj timeline">
-+        Import a DRM synchronization object timeline.
-+
-+        If the FD cannot be imported, the invalid_timeline error is raised.
-+      </description>
-+      <arg name="id" type="new_id" interface="wp_linux_drm_syncobj_timeline_v1"/>
-+      <arg name="fd" type="fd" summary="drm_syncobj file descriptor"/>
-+    </request>
-+  </interface>
-+
-+  <interface name="wp_linux_drm_syncobj_timeline_v1" version="1">
-+    <description summary="synchronization object timeline">
-+      This object represents an explicit synchronization object timeline
-+      imported by the client to the compositor.
-+    </description>
-+
-+    <request name="destroy" type="destructor">
-+      <description summary="destroy the timeline">
-+        Destroy the synchronization object timeline. Other objects are not
-+        affected by this request, in particular timeline points set by
-+        set_acquire_point and set_release_point are not unset.
-+      </description>
-+    </request>
-+  </interface>
-+
-+  <interface name="wp_linux_drm_syncobj_surface_v1" version="1">
-+    <description summary="per-surface explicit synchronization">
-+      This object is an add-on interface for wl_surface to enable explicit
-+      synchronization.
-+
-+      Each surface can be associated with only one object of this interface at
-+      any time.
-+
-+      Explicit synchronization is guaranteed to be supported for buffers
-+      created with any version of the linux-dmabuf protocol. Compositors are
-+      free to support explicit synchronization for additional buffer types.
-+      If at surface commit time the attached buffer does not support explicit
-+      synchronization, an unsupported_buffer error is raised.
-+
-+      As long as the wp_linux_drm_syncobj_surface_v1 object is alive, the
-+      compositor may ignore implicit synchronization for buffers attached and
-+      committed to the wl_surface. The delivery of wl_buffer.release events
-+      for buffers attached to the surface becomes undefined.
-+
-+      Clients must set both acquire and release points if and only if a
-+      non-null buffer is attached in the same surface commit. See the
-+      no_buffer, no_acquire_point and no_release_point protocol errors.
-+
-+      If at surface commit time the acquire and release DRM syncobj timelines
-+      are identical, the acquire point value must be strictly less than the
-+      release point value, or else the conflicting_points protocol error is
-+      raised.
-+    </description>
-+
-+    <request name="destroy" type="destructor">
-+      <description summary="destroy the surface synchronization object">
-+        Destroy this surface synchronization object.
-+
-+        Any timeline point set by this object with set_acquire_point or
-+        set_release_point since the last commit may be discarded by the
-+        compositor. Any timeline point set by this object before the last
-+        commit will not be affected.
-+      </description>
-+    </request>
-+
-+    <enum name="error">
-+      <entry name="no_surface" value="1"
-+        summary="the associated wl_surface was destroyed"/>
-+      <entry name="unsupported_buffer" value="2"
-+        summary="the buffer does not support explicit synchronization"/>
-+      <entry name="no_buffer" value="3" summary="no buffer was attached"/>
-+      <entry name="no_acquire_point" value="4"
-+        summary="no acquire timeline point was set"/>
-+      <entry name="no_release_point" value="5"
-+        summary="no release timeline point was set"/>
-+      <entry name="conflicting_points" value="6"
-+        summary="acquire and release timeline points are in conflict"/>
-+    </enum>
-+
-+    <request name="set_acquire_point">
-+      <description summary="set the acquire timeline point">
-+        Set the timeline point that must be signalled before the compositor may
-+        sample from the buffer attached with wl_surface.attach.
-+
-+        The 64-bit unsigned value combined from point_hi and point_lo is the
-+        point value.
-+
-+        The acquire point is double-buffered state, and will be applied on the
-+        next wl_surface.commit request for the associated surface. Thus, it
-+        applies only to the buffer that is attached to the surface at commit
-+        time.
-+
-+        If an acquire point has already been attached during the same commit
-+        cycle, the new point replaces the old one.
-+
-+        If the associated wl_surface was destroyed, a no_surface error is
-+        raised.
-+
-+        If at surface commit time there is a pending acquire timeline point set
-+        but no pending buffer attached, a no_buffer error is raised. If at
-+        surface commit time there is a pending buffer attached but no pending
-+        acquire timeline point set, the no_acquire_point protocol error is
-+        raised.
-+      </description>
-+      <arg name="timeline" type="object" interface="wp_linux_drm_syncobj_timeline_v1"/>
-+      <arg name="point_hi" type="uint" summary="high 32 bits of the point value"/>
-+      <arg name="point_lo" type="uint" summary="low 32 bits of the point value"/>
-+    </request>
-+
-+    <request name="set_release_point">
-+      <description summary="set the release timeline point">
-+        Set the timeline point that must be signalled by the compositor when it
-+        has finished its usage of the buffer attached with wl_surface.attach
-+        for the relevant commit.
-+
-+        Once the timeline point is signaled, and assuming the associated buffer
-+        is not pending release from other wl_surface.commit requests, no
-+        additional explicit or implicit synchronization with the compositor is
-+        required to safely re-use the buffer.
-+
-+        Note that clients cannot rely on the release point being always
-+        signaled after the acquire point: compositors may release buffers
-+        without ever reading from them. In addition, the compositor may use
-+        different presentation paths for different commits, which may have
-+        different release behavior. As a result, the compositor may signal the
-+        release points in a different order than the client committed them.
-+
-+        Because signaling a timeline point also signals every previous point,
-+        it is generally not safe to use the same timeline object for the
-+        release points of multiple buffers. The out-of-order signaling
-+        described above may lead to a release point being signaled before the
-+        compositor has finished reading. To avoid this, it is strongly
-+        recommended that each buffer should use a separate timeline for its
-+        release points.
-+
-+        The 64-bit unsigned value combined from point_hi and point_lo is the
-+        point value.
-+
-+        The release point is double-buffered state, and will be applied on the
-+        next wl_surface.commit request for the associated surface. Thus, it
-+        applies only to the buffer that is attached to the surface at commit
-+        time.
-+
-+        If a release point has already been attached during the same commit
-+        cycle, the new point replaces the old one.
-+
-+        If the associated wl_surface was destroyed, a no_surface error is
-+        raised.
-+
-+        If at surface commit time there is a pending release timeline point set
-+        but no pending buffer attached, a no_buffer error is raised. If at
-+        surface commit time there is a pending buffer attached but no pending
-+        release timeline point set, the no_release_point protocol error is
-+        raised.
-+      </description>
-+      <arg name="timeline" type="object" interface="wp_linux_drm_syncobj_timeline_v1"/>
-+      <arg name="point_hi" type="uint" summary="high 32 bits of the point value"/>
-+      <arg name="point_lo" type="uint" summary="low 32 bits of the point value"/>
-+    </request>
-+  </interface>
-+</protocol>
-diff --git a/src/wayland/surface.cpp b/src/wayland/surface.cpp
-index 23f981f2f81..6a33c5fca10 100644
---- a/src/wayland/surface.cpp
-+++ b/src/wayland/surface.cpp
-@@ -13,6 +13,7 @@
- #include "fractionalscale_v1_p.h"
- #include "frog_colormanagement_v1.h"
- #include "idleinhibit_v1_p.h"
-+#include "linux_drm_syncobj_v1.h"
- #include "linuxdmabufv1clientbuffer.h"
- #include "output.h"
- #include "pointerconstraints_v1_p.h"
-@@ -342,6 +343,10 @@ void SurfaceInterfacePrivate::surface_commit(Resource *resource)
- {
-     const bool sync = subsurface.handle && subsurface.handle->isSynchronized();
- 
-+    if (syncObjV1 && syncObjV1->maybeEmitProtocolErrors()) {
-+        return;
-+    }
-+
-     Transaction *transaction;
-     if (sync) {
-         if (!subsurface.transaction) {
-@@ -518,6 +523,9 @@ void SurfaceState::mergeInto(SurfaceState *target)
-         target->offset = offset;
-         target->damage = damage;
-         target->bufferDamage = bufferDamage;
-+        target->acquirePoint.timeline = std::exchange(acquirePoint.timeline, nullptr);
-+        target->acquirePoint.point = acquirePoint.point;
-+        target->releasePoint = std::move(releasePoint);
-         target->bufferIsSet = true;
-     }
-     if (viewport.sourceGeometryIsSet) {
-@@ -600,6 +608,7 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
-     const bool visibilityChanged = bufferChanged && bool(current->buffer) != bool(next->buffer);
-     const bool colorDescriptionChanged = next->colorDescriptionIsSet;
-     const bool presentationModeHintChanged = next->presentationModeHintIsSet;
-+    const bool bufferReleasePointChanged = next->releasePointIsSet;
- 
-     const QSizeF oldSurfaceSize = surfaceSize;
-     const QSize oldBufferSize = bufferSize;
-@@ -608,6 +617,9 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
- 
-     next->mergeInto(current.get());
-     bufferRef = current->buffer;
-+    if (bufferRef && current->releasePoint) {
-+        bufferRef->addReleasePoint(current->releasePoint);
-+    }
-     scaleOverride = pendingScaleOverride;
- 
-     if (current->buffer) {
-@@ -689,6 +701,9 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
-     if (presentationModeHintChanged) {
-         Q_EMIT q->presentationModeHintChanged();
-     }
-+    if (bufferReleasePointChanged) {
-+        Q_EMIT q->bufferReleasePointChanged();
-+    }
- 
-     if (bufferChanged) {
-         if (current->buffer && (!current->damage.isEmpty() || !current->bufferDamage.isEmpty())) {
-@@ -1179,6 +1194,11 @@ void SurfaceInterface::traverseTree(std::function<void(SurfaceInterface *surface
-     }
- }
- 
-+std::shared_ptr<SyncReleasePoint> SurfaceInterface::bufferReleasePoint() const
-+{
-+    return d->current->releasePoint;
-+}
-+
- } // namespace KWin
- 
- #include "moc_surface.cpp"
-diff --git a/src/wayland/surface.h b/src/wayland/surface.h
-index 597f06774f8..80d1fa55708 100644
---- a/src/wayland/surface.h
-+++ b/src/wayland/surface.h
-@@ -32,6 +32,7 @@ class SlideInterface;
- class SubSurfaceInterface;
- class SurfaceInterfacePrivate;
- class Transaction;
-+class SyncReleasePoint;
- 
- /**
-  * The SurfaceRole class represents a role assigned to a wayland surface.
-@@ -342,6 +343,12 @@ public:
- 
-     void setPreferredColorDescription(const ColorDescription &descr);
- 
-+    /**
-+     * @returns the release point that should be referenced as long as the buffer on this surface
-+     *          is, or may still be used by the compositor
-+     */
-+    std::shared_ptr<SyncReleasePoint> bufferReleasePoint() const;
-+
-     /**
-      * Traverses the surface sub-tree with this surface as the root.
-      */
-@@ -426,6 +433,7 @@ Q_SIGNALS:
- 
-     void colorDescriptionChanged();
-     void presentationModeHintChanged();
-+    void bufferReleasePointChanged();
- 
-     /**
-      * Emitted when the Surface has been committed.
-diff --git a/src/wayland/surface_p.h b/src/wayland/surface_p.h
-index f78bd107b44..775f9d7bd2e 100644
---- a/src/wayland/surface_p.h
-+++ b/src/wayland/surface_p.h
-@@ -28,6 +28,7 @@ class FractionalScaleV1Interface;
- class FrogColorManagementSurfaceV1;
- class PresentationTimeFeedback;
- class XXColorSurfaceV2;
-+class LinuxDrmSyncObjSurfaceV1;
- 
- struct SurfaceState
- {
-@@ -58,6 +59,7 @@ struct SurfaceState
-     bool contentTypeIsSet = false;
-     bool presentationModeHintIsSet = false;
-     bool colorDescriptionIsSet = false;
-+    bool releasePointIsSet = false;
-     qint32 bufferScale = 1;
-     OutputTransform bufferTransform = OutputTransform::Normal;
-     wl_list frameCallbacks;
-@@ -71,6 +73,12 @@ struct SurfaceState
-     PresentationModeHint presentationHint = PresentationModeHint::VSync;
-     ColorDescription colorDescription = ColorDescription::sRGB;
-     std::unique_ptr<PresentationTimeFeedback> presentationFeedback;
-+    struct
-+    {
-+        std::shared_ptr<SyncTimeline> timeline;
-+        uint64_t point = 0;
-+    } acquirePoint;
-+    std::shared_ptr<SyncReleasePoint> releasePoint;
- 
-     struct
-     {
-@@ -169,6 +177,7 @@ public:
-     TearingControlV1Interface *tearing = nullptr;
-     FrogColorManagementSurfaceV1 *frogColorManagement = nullptr;
-     XXColorSurfaceV2 *xxColorSurface = nullptr;
-+    LinuxDrmSyncObjSurfaceV1 *syncObjV1 = nullptr;
- 
-     struct
-     {
-diff --git a/src/wayland/transaction.cpp b/src/wayland/transaction.cpp
-index 93004ba863d..fcd19d4036f 100644
---- a/src/wayland/transaction.cpp
-+++ b/src/wayland/transaction.cpp
-@@ -5,7 +5,9 @@
- */
- 
- #include "wayland/transaction.h"
-+#include "core/syncobjtimeline.h"
- #include "utils/filedescriptor.h"
-+#include "wayland/clientconnection.h"
- #include "wayland/subcompositor.h"
- #include "wayland/surface_p.h"
- #include "wayland/transaction_p.h"
-@@ -76,6 +78,24 @@ bool TransactionDmaBufLocker::arm()
-     return !m_pending.isEmpty();
- }
- 
-+TransactionEventFdLocker::TransactionEventFdLocker(Transaction *transaction, FileDescriptor &&eventFd, ClientConnection *client)
-+    : m_transaction(transaction)
-+    , m_client(client)
-+    , m_eventFd(std::move(eventFd))
-+    , m_notifier(m_eventFd.get(), QSocketNotifier::Type::Read)
-+{
-+    transaction->lock();
-+    connect(&m_notifier, &QSocketNotifier::activated, this, &TransactionEventFdLocker::unlock);
-+    // when the client quits, the eventfd may never be signaled
-+    connect(m_client, &ClientConnection::aboutToBeDestroyed, this, &TransactionEventFdLocker::unlock);
-+}
-+
-+void TransactionEventFdLocker::unlock()
-+{
-+    m_transaction->unlock();
-+    delete this;
-+}
-+
- Transaction::Transaction()
- {
- }
-@@ -248,7 +268,12 @@ void Transaction::commit()
-     for (TransactionEntry &entry : m_entries) {
-         if (entry.state->bufferIsSet && entry.state->buffer) {
-             // Avoid applying the transaction until all graphics buffers have become idle.
--            if (auto locker = TransactionDmaBufLocker::get(entry.state->buffer)) {
-+            if (entry.state->acquirePoint.timeline) {
-+                auto eventFd = entry.state->acquirePoint.timeline->eventFd(entry.state->acquirePoint.point);
-+                if (entry.surface && eventFd.isValid()) {
-+                    new TransactionEventFdLocker(this, std::move(eventFd), entry.surface->client());
-+                }
-+            } else if (auto locker = TransactionDmaBufLocker::get(entry.state->buffer)) {
-                 locker->add(this);
-             }
-         }
-diff --git a/src/wayland/transaction_p.h b/src/wayland/transaction_p.h
-index 5bcf148aaad..29a9921e81f 100644
---- a/src/wayland/transaction_p.h
-+++ b/src/wayland/transaction_p.h
-@@ -33,4 +33,19 @@ private:
-     std::vector<std::unique_ptr<QSocketNotifier>> m_notifiers;
- };
- 
-+class TransactionEventFdLocker : public QObject
-+{
-+    Q_OBJECT
-+public:
-+    TransactionEventFdLocker(Transaction *transaction, FileDescriptor &&eventFd, ClientConnection *client);
-+
-+private:
-+    void unlock();
-+
-+    Transaction *const m_transaction;
-+    const QPointer<ClientConnection> m_client;
-+    const FileDescriptor m_eventFd;
-+    QSocketNotifier m_notifier;
-+};
-+
- } // namespace KWin
-diff --git a/src/wayland_server.cpp b/src/wayland_server.cpp
-index 2b02eb2fa69..037b633fc6c 100644
---- a/src/wayland_server.cpp
-+++ b/src/wayland_server.cpp
-@@ -41,6 +41,7 @@
- #include "wayland/inputmethod_v1.h"
- #include "wayland/keyboard_shortcuts_inhibit_v1.h"
- #include "wayland/keystate.h"
-+#include "wayland/linux_drm_syncobj_v1.h"
- #include "wayland/linuxdmabufv1clientbuffer.h"
- #include "wayland/lockscreen_overlay_v1.h"
- #include "wayland/output.h"
-@@ -811,6 +812,22 @@ QString WaylandServer::socketName() const
-     return QString();
- }
- 
-+LinuxDrmSyncObjV1Interface *WaylandServer::linuxSyncObj() const
-+{
-+    return m_linuxDrmSyncObj;
-+}
-+
-+void WaylandServer::setRenderBackend(RenderBackend *backend)
-+{
-+    if (backend->supportsTimelines()) {
-+        if (!m_linuxDrmSyncObj) {
-+            m_linuxDrmSyncObj = new LinuxDrmSyncObjV1Interface(m_display, m_display);
-+        }
-+    } else if (m_linuxDrmSyncObj) {
-+        m_linuxDrmSyncObj->remove();
-+    }
-+}
-+
- #if KWIN_BUILD_SCREENLOCKER
- WaylandServer::LockScreenPresentationWatcher::LockScreenPresentationWatcher(WaylandServer *server)
- {
-diff --git a/src/wayland_server.h b/src/wayland_server.h
-index 8eb6f31a176..60dc67d9670 100644
---- a/src/wayland_server.h
-+++ b/src/wayland_server.h
-@@ -58,6 +58,8 @@ class XdgSurfaceWindow;
- class XdgToplevelWindow;
- class PresentationTime;
- class XXColorManagerV2;
-+class LinuxDrmSyncObjV1Interface;
-+class RenderBackend;
- 
- class KWIN_EXPORT WaylandServer : public QObject
- {
-@@ -226,6 +228,10 @@ public:
-         return m_xdgActivationIntegration;
-     }
- 
-+    LinuxDrmSyncObjV1Interface *linuxSyncObj() const;
-+
-+    void setRenderBackend(RenderBackend *backend);
-+
- Q_SIGNALS:
-     void windowAdded(KWin::Window *);
-     void windowRemoved(KWin::Window *);
-@@ -284,6 +290,7 @@ private:
-     TearingControlManagerV1Interface *m_tearingControlInterface = nullptr;
-     XwaylandShellV1Interface *m_xwaylandShell = nullptr;
-     PresentationTime *m_presentationTime = nullptr;
-+    LinuxDrmSyncObjV1Interface *m_linuxDrmSyncObj = nullptr;
-     QList<Window *> m_windows;
-     InitializationFlags m_initFlags;
-     QHash<Output *, OutputInterface *> m_waylandOutputs;
--- 
-GitLab
-
-
-From 489761e5d772ad11d195c3eb679f6747d1aa2e46 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Sat, 6 Apr 2024 18:51:01 +0200
-Subject: [PATCH 2/5] core/syncobjtimeline: make explicit sync use
- SYNC_IOC_MERGE instead of waiting on the CPU side
-
-This brings some performance benefits, because the application can potentially reuse
-the buffer earlier, and it simplifies the code a bit
-
-(cherry picked from commit 4c6000b3e1829da5f0ccb81bbccfee37fb28cd24)
----
- src/core/syncobjtimeline.cpp      | 63 +++++++++++++++++++++++++------
- src/core/syncobjtimeline.h        | 28 ++++----------
- src/scene/itemrenderer_opengl.cpp |  5 ++-
- src/wayland/surface.h             |  6 ++-
- 4 files changed, 67 insertions(+), 35 deletions(-)
-
-diff --git a/src/core/syncobjtimeline.cpp b/src/core/syncobjtimeline.cpp
-index 3e2c100370a..1152e895722 100644
---- a/src/core/syncobjtimeline.cpp
-+++ b/src/core/syncobjtimeline.cpp
-@@ -6,8 +6,24 @@
- #include "syncobjtimeline.h"
- 
- #include <sys/eventfd.h>
-+#include <sys/ioctl.h>
- #include <xf86drm.h>
- 
-+#if defined(Q_OS_LINUX)
-+#include <linux/sync_file.h>
-+#else
-+struct sync_merge_data
-+{
-+    char name[32];
-+    __s32 fd2;
-+    __s32 fence;
-+    __u32 flags;
-+    __u32 pad;
-+};
-+#define SYNC_IOC_MAGIC '>'
-+#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
-+#endif
-+
- namespace KWin
- {
- 
-@@ -19,7 +35,39 @@ SyncReleasePoint::SyncReleasePoint(const std::shared_ptr<SyncTimeline> &timeline
- 
- SyncReleasePoint::~SyncReleasePoint()
- {
--    m_timeline->signal(m_timelinePoint);
-+    if (m_releaseFence.isValid()) {
-+        m_timeline->moveInto(m_timelinePoint, m_releaseFence);
-+    } else {
-+        m_timeline->signal(m_timelinePoint);
-+    }
-+}
-+
-+static FileDescriptor mergeSyncFds(const FileDescriptor &fd1, const FileDescriptor &fd2)
-+{
-+    struct sync_merge_data data
-+    {
-+        .name = "merged release fence",
-+        .fd2 = fd2.get(),
-+        .fence = -1,
-+    };
-+    int err = -1;
-+    do {
-+        err = ioctl(fd1.get(), SYNC_IOC_MERGE, &data);
-+    } while (err == -1 && (errno == EINTR || errno == EAGAIN));
-+    if (err < 0) {
-+        return FileDescriptor{};
-+    } else {
-+        return FileDescriptor(data.fence);
-+    }
-+}
-+
-+void SyncReleasePoint::addReleaseFence(const FileDescriptor &fd)
-+{
-+    if (m_releaseFence.isValid()) {
-+        m_releaseFence = mergeSyncFds(m_releaseFence, fd);
-+    } else {
-+        m_releaseFence = fd.duplicate();
-+    }
- }
- 
- SyncTimeline *SyncReleasePoint::timeline() const
-@@ -67,17 +115,8 @@ void SyncTimeline::signal(uint64_t timelinePoint)
-     drmSyncobjTimelineSignal(m_drmFd, &m_handle, &timelinePoint, 1);
- }
- 
--SyncReleasePointHolder::SyncReleasePointHolder(FileDescriptor &&requirement, std::unordered_set<std::shared_ptr<SyncReleasePoint>> &&releasePoints)
--    : m_fence(std::move(requirement))
--    , m_notifier(m_fence.get(), QSocketNotifier::Type::Read)
--    , m_releasePoints(std::move(releasePoints))
--{
--    connect(&m_notifier, &QSocketNotifier::activated, this, &SyncReleasePointHolder::signaled);
--    m_notifier.setEnabled(true);
--}
--
--void SyncReleasePointHolder::signaled()
-+void SyncTimeline::moveInto(uint64_t timelinePoint, const FileDescriptor &fd)
- {
--    delete this;
-+    drmSyncobjImportSyncFile(m_drmFd, m_handle, fd.get());
- }
- }
-diff --git a/src/core/syncobjtimeline.h b/src/core/syncobjtimeline.h
-index cffdb75e330..4ced3e06272 100644
---- a/src/core/syncobjtimeline.h
-+++ b/src/core/syncobjtimeline.h
-@@ -7,11 +7,8 @@
- #include "kwin_export.h"
- #include "utils/filedescriptor.h"
- 
--#include <QObject>
--#include <QSocketNotifier>
- #include <memory>
- #include <stdint.h>
--#include <unordered_set>
- 
- namespace KWin
- {
-@@ -30,9 +27,16 @@ public:
-     SyncTimeline *timeline() const;
-     uint64_t timelinePoint() const;
- 
-+    /**
-+     * Adds the fence of a graphics job that this release point should wait for
-+     * before the timeline point is signaled
-+     */
-+    void addReleaseFence(const FileDescriptor &fd);
-+
- private:
-     const std::shared_ptr<SyncTimeline> m_timeline;
-     const uint64_t m_timelinePoint;
-+    FileDescriptor m_releaseFence;
- };
- 
- class KWIN_EXPORT SyncTimeline
-@@ -47,26 +51,10 @@ public:
-     FileDescriptor eventFd(uint64_t timelinePoint) const;
- 
-     void signal(uint64_t timelinePoint);
-+    void moveInto(uint64_t timelinePoint, const FileDescriptor &fd);
- 
- private:
-     const int32_t m_drmFd;
-     const uint32_t m_handle;
- };
--
--class SyncReleasePointHolder : public QObject
--{
--    Q_OBJECT
--public:
--    /**
--     * @param requirement the filedescriptor that needs to be readable before the release points may be signalled. Once that's happened, this object deletes itself!'
--     */
--    explicit SyncReleasePointHolder(FileDescriptor &&requirement, std::unordered_set<std::shared_ptr<SyncReleasePoint>> &&releasePoints);
--
--private:
--    void signaled();
--
--    const FileDescriptor m_fence;
--    QSocketNotifier m_notifier;
--    const std::unordered_set<std::shared_ptr<SyncReleasePoint>> m_releasePoints;
--};
- }
-diff --git a/src/scene/itemrenderer_opengl.cpp b/src/scene/itemrenderer_opengl.cpp
-index 3fee3863f2c..ffcc6de3914 100644
---- a/src/scene/itemrenderer_opengl.cpp
-+++ b/src/scene/itemrenderer_opengl.cpp
-@@ -53,7 +53,10 @@ void ItemRendererOpenGL::endFrame()
-     if (m_eglDisplay) {
-         EGLNativeFence fence(m_eglDisplay);
-         if (fence.isValid()) {
--            new SyncReleasePointHolder(std::move(fence.fileDescriptor()), std::move(m_releasePoints));
-+            for (const auto &releasePoint : m_releasePoints) {
-+                releasePoint->addReleaseFence(fence.fileDescriptor());
-+            }
-+            m_releasePoints.clear();
-         }
-     }
-     m_releasePoints.clear();
-diff --git a/src/wayland/surface.h b/src/wayland/surface.h
-index 80d1fa55708..152f1accc10 100644
---- a/src/wayland/surface.h
-+++ b/src/wayland/surface.h
-@@ -344,8 +344,10 @@ public:
-     void setPreferredColorDescription(const ColorDescription &descr);
- 
-     /**
--     * @returns the release point that should be referenced as long as the buffer on this surface
--     *          is, or may still be used by the compositor
-+     * Returns the current release point for the buffer on this surface. The buffer keeps the
-+     * release point referenced as long as it's referenced itself; for synchronization on the
-+     * GPU side, the compositor has to either keep the release point referenced as long as the
-+     * GPU task is running, or add a fence for each GPU task to the release point
-      */
-     std::shared_ptr<SyncReleasePoint> bufferReleasePoint() const;
- 
--- 
-GitLab
-
-
-From eef4cbe0278e81c33b496932400854a74a08bfa4 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Tue, 16 Apr 2024 13:51:42 +0200
-Subject: [PATCH 3/5] wayland_server: guard against DRM_IOCTL_SYNCOBJ_EVENTFD
- being broken
-
-...either because it's not implemented, or because it's buggy.
-
-(cherry picked from commit 26afbfb6fa030c3dadf6a22309686f96ca033f59)
----
- src/wayland_server.cpp | 10 ++++++++++
- 1 file changed, 10 insertions(+)
-
-diff --git a/src/wayland_server.cpp b/src/wayland_server.cpp
-index 037b633fc6c..d9240f0e8ce 100644
---- a/src/wayland_server.cpp
-+++ b/src/wayland_server.cpp
-@@ -19,6 +19,7 @@
- #include "layershellv1window.h"
- #include "main.h"
- #include "options.h"
-+#include "utils/kernel.h"
- #include "utils/serviceutils.h"
- #include "virtualdesktops.h"
- #include "wayland/appmenu.h"
-@@ -820,6 +821,15 @@ LinuxDrmSyncObjV1Interface *WaylandServer::linuxSyncObj() const
- void WaylandServer::setRenderBackend(RenderBackend *backend)
- {
-     if (backend->supportsTimelines()) {
-+        // ensure the DRM_IOCTL_SYNCOBJ_EVENTFD ioctl is supported
-+        const auto linuxVersion = linuxKernelVersion();
-+        if (linuxVersion.majorVersion() < 6 && linuxVersion.minorVersion() < 6) {
-+            return;
-+        }
-+        // also ensure the implementation isn't totally broken, see https://lists.freedesktop.org/archives/dri-devel/2024-January/439101.html
-+        if (linuxVersion.majorVersion() == 6 && (linuxVersion.minorVersion() == 7 || (linuxVersion.minorVersion() == 6 && linuxVersion.patchVersion() < 19))) {
-+            return;
-+        }
-         if (!m_linuxDrmSyncObj) {
-             m_linuxDrmSyncObj = new LinuxDrmSyncObjV1Interface(m_display, m_display);
-         }
--- 
-GitLab
-
-
-From 2e9ecb0edf137309fb526adc7c8504e8c669b17f Mon Sep 17 00:00:00 2001
-From: Erik Kurzinger <ekurzinger@nvidia.com>
-Date: Tue, 16 Apr 2024 14:44:50 -0700
-Subject: [PATCH 4/5] core/syncobjtimeline: import release fence at correct
- timeline point
-
-SyncTimeline::moveInto imports the provided fence to the syncobj by
-calling drmSyncobjImportSyncFile. However, that function assumes the
-syncobj a binary syncobj, meaning the fence will be imported at timeline
-point 0, not at the intended timeline point.
-
-Since there is no timeline equivalent of drmSyncobjImportSyncFile, to
-achieve the correct behavior we create a temporary binary syncobj,
-import the fence into that, and then use drmSyncobjTransfer to transfer
-the fence to the desired timeline point on the destination syncobj.
-
-Signed-off-by: Erik Kurzinger <ekurzinger@nvidia.com>
-(cherry picked from commit 9ca7b9b1cf292e790223419dc197737f875109d4)
----
- src/core/syncobjtimeline.cpp | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
-diff --git a/src/core/syncobjtimeline.cpp b/src/core/syncobjtimeline.cpp
-index 1152e895722..80054e93480 100644
---- a/src/core/syncobjtimeline.cpp
-+++ b/src/core/syncobjtimeline.cpp
-@@ -117,6 +117,10 @@ void SyncTimeline::signal(uint64_t timelinePoint)
- 
- void SyncTimeline::moveInto(uint64_t timelinePoint, const FileDescriptor &fd)
- {
--    drmSyncobjImportSyncFile(m_drmFd, m_handle, fd.get());
-+    uint32_t tempHandle = 0;
-+    drmSyncobjCreate(m_drmFd, 0, &tempHandle);
-+    drmSyncobjImportSyncFile(m_drmFd, tempHandle, fd.get());
-+    drmSyncobjTransfer(m_drmFd, m_handle, timelinePoint, tempHandle, 0, 0);
-+    drmSyncobjDestroy(m_drmFd, tempHandle);
- }
- }
--- 
-GitLab
-
-
-From 463ae633e878004b1799f618641a0c44573c10f4 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Wed, 17 Apr 2024 03:26:34 +0200
-Subject: [PATCH 5/5] wayland/surface: fix the change signal for release points
- not being emitted
-
-(cherry picked from commit eed7943939524895225ab4cf4eb3a1fa880b42bf)
----
- src/wayland/surface.cpp | 2 +-
- src/wayland/surface_p.h | 1 -
- 2 files changed, 1 insertion(+), 2 deletions(-)
-
-diff --git a/src/wayland/surface.cpp b/src/wayland/surface.cpp
-index 6a33c5fca10..6a77e98d0ee 100644
---- a/src/wayland/surface.cpp
-+++ b/src/wayland/surface.cpp
-@@ -608,7 +608,7 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
-     const bool visibilityChanged = bufferChanged && bool(current->buffer) != bool(next->buffer);
-     const bool colorDescriptionChanged = next->colorDescriptionIsSet;
-     const bool presentationModeHintChanged = next->presentationModeHintIsSet;
--    const bool bufferReleasePointChanged = next->releasePointIsSet;
-+    const bool bufferReleasePointChanged = next->bufferIsSet && current->releasePoint != next->releasePoint;
- 
-     const QSizeF oldSurfaceSize = surfaceSize;
-     const QSize oldBufferSize = bufferSize;
-diff --git a/src/wayland/surface_p.h b/src/wayland/surface_p.h
-index 775f9d7bd2e..7bcf95c0bef 100644
---- a/src/wayland/surface_p.h
-+++ b/src/wayland/surface_p.h
-@@ -59,7 +59,6 @@ struct SurfaceState
-     bool contentTypeIsSet = false;
-     bool presentationModeHintIsSet = false;
-     bool colorDescriptionIsSet = false;
--    bool releasePointIsSet = false;
-     qint32 bufferScale = 1;
-     OutputTransform bufferTransform = OutputTransform::Normal;
-     wl_list frameCallbacks;
--- 
-GitLab
-
diff --git a/overlays/kwin/default.nix b/overlays/kwin/default.nix
deleted file mode 100644
index d766a6e..0000000
--- a/overlays/kwin/default.nix
+++ /dev/null
@@ -1,9 +0,0 @@
-self: super: {
-  kdePackages = super.kdePackages.overrideScope (kde-self: kde-super: rec {
-    kwin = kde-super.kwin.overrideAttrs (oldAttrs: rec {
-      patches = oldAttrs.patches ++ [
-        ./5511.patch # https://invent.kde.org/plasma/kwin/-/merge_requests/5511
-      ];
-    });
-  });
-}
\ No newline at end of file
diff --git a/tips/debug_insecure_package.md b/tips/debug_insecure_package.md
new file mode 100644
index 0000000..837ca9c
--- /dev/null
+++ b/tips/debug_insecure_package.md
@@ -0,0 +1,30 @@
+## If package is marked as insecure
+
+Example:
+
+> error: Package 'nix-2.16.2' in /nix/store/nra828scc8qs92b9pxra5csqzffb6hpl-source/pkgs/tools/package-management/nix/default.nix:229 is marked as insecure, refusing to evaluate.
+>
+> Known issues:
+> - CVE-2024-27297
+
+```bash
+nix path-info -r /run/current-system | grep nix-2.16.2
+```
+Result:
+> [...]
+>
+> /nix/store/g4ss2h40n3j37bq20x1qw5s7nl82lch5-nix-2.16.2
+>
+> [...]
+
+```bash
+nix-store -q --referrers /nix/store/g4ss2h40n3j37bq20x1qw5s7nl82lch5-nix-2.16.2
+```
+Result:
+> /nix/store/g4ss2h40n3j37bq20x1qw5s7nl82lch5-nix-2.16.2
+>
+> /nix/store/72pfc05339izcwqhlbs8441brrdasas7-nix-2.16.2-dev
+>
+> /nix/store/ln2z5d5izn8icm3wx94ci13ad19lzjhr-nixd-1.2.3
+
+nixd is not up to date and require nix 2.16.2
diff --git a/tips/distrobox/kde/.kde_bashrc b/tips/distrobox/kde/.kde_bashrc
new file mode 100644
index 0000000..1167121
--- /dev/null
+++ b/tips/distrobox/kde/.kde_bashrc
@@ -0,0 +1,24 @@
+# Required for kde-builder command
+if [[ $PATH != *".local/bin"* ]]; then
+  export PATH="$HOME/.local/bin:$PATH"
+fi
+
+# Workaround for NixOS
+# ENVs have nix store references and made conflict during build or run of KDE Apps, Shells, Frameworks
+alias new_shell="env -u PATH -u QML2_IMPORT_PATH -u QT_PLUGIN_PATH -u NIXPKGS_QT6_QML_IMPORT_PATH -u XDG_CONFIG_DIRS bash -l"
+
+alias build_plasma_mobile="kde-builder plasma-mobile plasma-settings plasma-desktop kactivitymanagerd"
+
+function run_mobile() {
+  source "$HOME/kde/build/plasma-mobile/prefix.sh"
+
+  QT_QPA_PLATFORM=offscreen plasma-mobile-envmanager --apply-settings
+
+  # Environment variables
+  export QT_WAYLAND_DISABLE_WINDOWDECORATION=1
+  export QT_QUICK_CONTROLS_MOBILE=1
+  export PLASMA_PLATFORM=phone:handheld
+  export QT_QUICK_CONTROLS_STYLE=org.kde.breeze
+
+  QT_QPA_PLATFORM=wayland dbus-run-session kwin_wayland --xwayland "plasmashell -p org.kde.plasma.mobileshell" --width 360 --height 720
+}
\ No newline at end of file
diff --git a/tips/distrobox/kde/configure.sh b/tips/distrobox/kde/configure.sh
new file mode 100755
index 0000000..f6e0812
--- /dev/null
+++ b/tips/distrobox/kde/configure.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+if ! grep -Fxq "source ~/.kde_bashrc" ~/.bashrc
+then
+  cp .kde_bashrc ~/.kde_bashrc
+  echo "source ~/.kde_bashrc" > ~/.bashrc
+  source ~/.bashrc
+fi
+
+echo "Installing kde-builder"
+curl 'https://invent.kde.org/sdk/kde-builder/-/raw/master/scripts/initial_setup.sh?ref_type=heads' > ~/initial_setup.sh
+bash ~/initial_setup.sh && rm ~/initial_setup.sh
+
+echo "Run initial setup from kde-builder"
+kde-builder --initial-setup
+
+echo "Install missing dependencies"
+sudo dnf install qt6-*-devel librsvg2-devel
+
+echo "Configuration DONE"
\ No newline at end of file
diff --git a/tips/distrobox/kde/distrobox.ini b/tips/distrobox/kde/distrobox.ini
new file mode 100644
index 0000000..180ff56
--- /dev/null
+++ b/tips/distrobox/kde/distrobox.ini
@@ -0,0 +1,6 @@
+[kdedev]
+image=quay.io/fedora/fedora:latest
+home="${HOME}/.distrobox_home/kdedev"
+nvidia=true
+root=false
+pull=true
diff --git a/tips/distrobox/kde/use_distrobox.md b/tips/distrobox/kde/use_distrobox.md
new file mode 100644
index 0000000..deda652
--- /dev/null
+++ b/tips/distrobox/kde/use_distrobox.md
@@ -0,0 +1,19 @@
+# How to develop on KDE
+
+## First configuration
+
+1. `distrobox assemble`
+   > You can add --replace to recreate distrobox container
+2. `distrobox enter kdedev`
+3. `bash configure.sh`
+4. `kde-builder run solid`
+   > You use NixOS ? It's necessary to run `new_shell` alias before.
+   > The alias unset all nixos env variables with nix store references to avoid conflicts with build or run of KDE Shells/Apps/Tools.
+
+## Usefull config in ~/.config/kdesrc-buildrc
+
+Autogenerate editor configuration
+
+- generate-clion-project-config
+- generate-vscode-project-config
+- generate-qtcreator-project-config
diff --git a/tips/how_to_test_pr.md b/tips/how_to_test_pr.md
new file mode 100644
index 0000000..3b4773e
--- /dev/null
+++ b/tips/how_to_test_pr.md
@@ -0,0 +1,36 @@
+# Example for ollama
+
+```nix
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.modules.system.server.ollama;
+  nvidiaEnabled = config.modules.system.hardware.nvidia.enable;
+  nixpkgsPr = builtins.fetchTarball {
+    url = "https://github.com/abysssol/nixpkgs/archive/ollama-driver-runpath.tar.gz";
+    sha256 = "1ixfvdpi2v4r9yrkvqnfk9whs8lyjhrkdph47bcznh8ak9aipr8p";
+  };
+in
+{
+  disabledModules = [ "services/misc/ollama.nix" ];
+  imports = [
+    (import "${nixpkgsPr}/nixos/modules/services/misc/ollama.nix")
+  ];
+
+  options.modules.system.server.ollama = {
+    enable = mkEnableOption ''
+      Enable ollama with my custom configurations
+    '';
+  };
+  config = mkIf cfg.enable {
+    services.ollama = {
+      enable = true;
+
+      package = (import nixpkgsPr { inherit (pkgs) system; config.allowUnfree = true; }).ollama;
+
+      acceleration = if nvidiaEnabled then "cuda" else null;
+    };
+  };
+}
+```
diff --git a/tips/test_on_vm.md b/tips/test_on_vm.md
new file mode 100644
index 0000000..ddaab4b
--- /dev/null
+++ b/tips/test_on_vm.md
@@ -0,0 +1,24 @@
+## Configure VM
+
+Configure VM
+```nix
+users.users.<user>.initialPassword = "<password>";
+virtualisation.vmVariant = {
+  # following configuration is added only when building VM with build-vm
+  virtualisation = {
+    memorySize = <RAM in MiB>; # Use 8192MiB memory.
+    cores = <CPU Core number>;
+    # And more here https://github.com/NixOS/nixpkgs/blob/nixos-unstable/nixos/modules/virtualisation/qemu-vm.nix
+  };
+};
+```
+
+Build
+```bash
+nixos-rebuild build-vm --flake .#nixos-test
+```
+
+Run
+```bash
+./result/bin/run-nixos-vm-vm
+```