Compare commits
5 Commits
wip-s6-2
...
wip-nix-fa
Author | SHA1 | Date | |
---|---|---|---|
084541da4c | |||
f7a82a845c | |||
2bdef04552 | |||
2822a6f0dd | |||
ab6e362f0c |
@@ -15,9 +15,8 @@ the only hard dependency for my exported pkgs/modules should be [nixpkgs][nixpkg
|
||||
building [hosts/](./hosts/) will require [sops][sops].
|
||||
|
||||
you might specifically be interested in these files (elaborated further in #key-points-of-interest):
|
||||
- ~~[`sxmo-utils`](./pkgs/additional/sxmo-utils/default.nix)~~
|
||||
- ~~[example SXMO deployment](./hosts/modules/gui/sxmo/default.nix)~~
|
||||
- these files will remain until my config settles down, but i no longer use or maintain SXMO.
|
||||
- [`sxmo-utils`](./pkgs/additional/sxmo-utils/default.nix)
|
||||
- [example SXMO deployment](./hosts/modules/gui/sxmo/default.nix)
|
||||
- [my implementation of impermanence](./modules/persist/default.nix)
|
||||
- my way of deploying dotfiles/configuring programs per-user:
|
||||
- [modules/fs/](./modules/fs/default.nix)
|
||||
|
15
TODO.md
15
TODO.md
@@ -11,15 +11,16 @@
|
||||
### sops/secrets
|
||||
- rework secrets to leverage `sane.fs`
|
||||
- remove sops activation script as it's covered by my systemd sane.fs impl
|
||||
- user secrets could just use `gocryptfs`, like with ~/private?
|
||||
- can gocryptfs support nested filesystems, each with different perms (for desko, moby, etc)?
|
||||
|
||||
### roles
|
||||
- allow any host to take the role of `uninsane.org`
|
||||
- will make it easier to test new services?
|
||||
|
||||
### upstreaming
|
||||
- split out a sxmo module usable by NUR consumers
|
||||
- bump nodejs version in lemmy-ui
|
||||
- add updateScripts to all my packages in nixpkgs
|
||||
- fix lightdm-mobile-greeter for newer libhandy
|
||||
- REVIEW/integrate jellyfin dataDir config: <https://github.com/NixOS/nixpkgs/pull/233617>
|
||||
|
||||
#### upstreaming to non-nixpkgs repos
|
||||
@@ -46,8 +47,6 @@
|
||||
- limit access to `~/knowledge/secrets` through an agent that requires GUI approval, so a firefox exploit can't steal all my logins
|
||||
- port sane-sandboxed to a compiled language (hare?)
|
||||
- it adds like 50-70ms launch time _on my laptop_. i'd hate to know how much that is on the pinephone.
|
||||
- remove /run/wrappers from the sandbox path
|
||||
- they're mostly useless when using no-new-privs, just an opportunity to forget to specify deps
|
||||
- make dconf stuff less monolithic
|
||||
- i.e. per-app dconf profiles for those which need it. possible static config.
|
||||
- canaries for important services
|
||||
@@ -55,11 +54,6 @@
|
||||
- integrate `nix check` into Gitea actions?
|
||||
|
||||
### user experience
|
||||
- xdg-desktop-portal shouldn't kill children on exit
|
||||
- *maybe* a job for `setsid -f`?
|
||||
- replace starship prompt with something more efficient
|
||||
- watch `forkstat`: it does way too much
|
||||
- cleanup waybar so that it's not invoking playerctl every 2 seconds
|
||||
- install apps:
|
||||
- display QR codes for WiFi endpoints: <https://linuxphoneapps.org/apps/noappid.wisperwind.wifi2qr/>
|
||||
- shopping list (not in nixpkgs): <https://linuxphoneapps.org/apps/ro.hume.cosmin.shoppinglist/>
|
||||
@@ -82,7 +76,6 @@
|
||||
|
||||
#### moby
|
||||
- fix cpuidle (gets better power consumption): <https://xnux.eu/log/077.html>
|
||||
- moby: tune keyboard layout
|
||||
- SwayNC:
|
||||
- don't show MPRIS if no players detected
|
||||
- this is a problem of playerctld, i guess
|
||||
@@ -104,7 +97,6 @@
|
||||
- RSS: integrate a paywall bypass
|
||||
- e.g. self-hosted [ladder](https://github.com/everywall/ladder) (like 12ft.io)
|
||||
- neovim: set up language server (lsp; rnix-lsp; nvim-lspconfig)
|
||||
- neovim: integrate LLMs
|
||||
- Helix: make copy-to-system clipboard be the default
|
||||
- firefox/librewolf: persist history
|
||||
- just not cookies or tabs
|
||||
@@ -122,6 +114,7 @@
|
||||
|
||||
### perf
|
||||
- debug nixos-rebuild times
|
||||
- i bet sane.programs adds a LOT of time, with how it automatically creates an attrs for EVERY package in nixpkgs.
|
||||
- add `pkgs.impure-cached.<foo>` package set to build things with ccache enabled
|
||||
- every package here can be auto-generated, and marked with some env var so that it doesn't pollute the pure package set
|
||||
- would be super handy for package prototyping!
|
||||
|
205
flake.lock
generated
205
flake.lock
generated
@@ -1,76 +1,20 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-compat": {
|
||||
"locked": {
|
||||
"lastModified": 1688025799,
|
||||
"narHash": "sha256-ktpB4dRtnksm9F5WawoIkEneh1nrEvuxb5lJFt1iOyw=",
|
||||
"owner": "nix-community",
|
||||
"repo": "flake-compat",
|
||||
"rev": "8bf105319d44f6b9f0d764efa4fdef9f1cc9ba1c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"nixpkgs-wayland",
|
||||
"nix-eval-jobs",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701473968,
|
||||
"narHash": "sha256-YcVE5emp1qQ8ieHUnxt1wCZCC3ZfAS+SRRWZ2TMda7E=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"lib-aggregate": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710184940,
|
||||
"narHash": "sha256-FzYm4td3FJfzOAuEkCXt3KdUgZuA072OAQXqIq+IAMo=",
|
||||
"owner": "nix-community",
|
||||
"repo": "lib-aggregate",
|
||||
"rev": "45b75bf534592c0c1c881a1c447f7fdb37a87eaf",
|
||||
"lastModified": 1698882062,
|
||||
"narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "lib-aggregate",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@@ -91,56 +35,33 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-eval-jobs": {
|
||||
"nix-fast-build": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"nix-github-actions": "nix-github-actions",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1705242886,
|
||||
"narHash": "sha256-TLj334vRwFtSym3m+NnKcNCnKKPNoTC/TDZL40vmOso=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-eval-jobs",
|
||||
"rev": "6b03a93296faf174b97546fd573c8b379f523a8d",
|
||||
"lastModified": 1703607026,
|
||||
"narHash": "sha256-Emh0BPoqlS4ntp2UJrwydXfIP4qIMF0VBB2FUE3/M/E=",
|
||||
"owner": "Mic92",
|
||||
"repo": "nix-fast-build",
|
||||
"rev": "4376b8a33b217ee2f78ba3dcff01a3e464d13a46",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-eval-jobs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-github-actions": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs-wayland",
|
||||
"nix-eval-jobs",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701208414,
|
||||
"narHash": "sha256-xrQ0FyhwTZK6BwKhahIkUVZhMNk21IEI1nUcWSONtpo=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"rev": "93e39cc1a087d65bcf7a132e75a650c44dd2b734",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"owner": "Mic92",
|
||||
"repo": "nix-fast-build",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1703134684,
|
||||
"narHash": "sha256-SQmng1EnBFLzS7WSRyPM9HgmZP2kLJcPAz+Ug/nug6o=",
|
||||
"lastModified": 1698890957,
|
||||
"narHash": "sha256-DJ+SppjpPBoJr0Aro9TAcP3sxApCSieY6BYBCoWGUX8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d6863cbcbbb80e71cecfc03356db1cda38919523",
|
||||
"rev": "c082856b850ec60cda9f0a0db2bc7bd8900d708c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -152,26 +73,29 @@
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1710031547,
|
||||
"narHash": "sha256-pkUg3hOKuGWMGF9WEMPPN/G4pqqdbNGJQ54yhyQYDVY=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "630ebdc047ca96d8126e16bb664c7730dc52f6e6",
|
||||
"dir": "lib",
|
||||
"lastModified": 1698611440,
|
||||
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"dir": "lib",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-next-unpatched": {
|
||||
"locked": {
|
||||
"lastModified": 1710337169,
|
||||
"narHash": "sha256-u2/74bhQuWykUZDWUIhHd6IpZiaQ0hSpTBbx0y9opkE=",
|
||||
"lastModified": 1708992120,
|
||||
"narHash": "sha256-t/8QV+lEroW5fK44w5oEUalIM0eYYVGs833AHDCIl4s=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4ee0840ba2ecc50458ab1677d108afcd691f4815",
|
||||
"rev": "6daf4de0662e1d895d220a4a4ddb356eb000abe9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -183,11 +107,11 @@
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1710033658,
|
||||
"narHash": "sha256-yiZiVKP5Ya813iYLho2+CcFuuHpaqKc/CoxOlANKcqM=",
|
||||
"lastModified": 1708819810,
|
||||
"narHash": "sha256-1KosU+ZFXf31GPeCBNxobZWMgHsSOJcrSFA6F2jhzdE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b17375d3bb7c79ffc52f3538028b2ec06eb79ef8",
|
||||
"rev": "89a2a12e6c8c6a56c72eb3589982c8e2f89c70ea",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -199,11 +123,11 @@
|
||||
},
|
||||
"nixpkgs-unpatched": {
|
||||
"locked": {
|
||||
"lastModified": 1710339354,
|
||||
"narHash": "sha256-+P5ccUPiLouHexb8aJrUOVOIja9qm+fG57pgAu7uIRs=",
|
||||
"lastModified": 1708995544,
|
||||
"narHash": "sha256-YJgLopKOKVTggnKzjX4OiAS22hx/vNv397DcsAyTZgY=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2dbc8f62d8af7a1ab962e4b20d12b25ddcb86ced",
|
||||
"rev": "5bd8df40204f47a12263f3614c72cd5b6832a9a0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -213,35 +137,12 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-wayland": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"lib-aggregate": "lib-aggregate",
|
||||
"nix-eval-jobs": "nix-eval-jobs",
|
||||
"nixpkgs": [
|
||||
"nixpkgs-unpatched"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710317949,
|
||||
"narHash": "sha256-bwReMiWPA2wYBvKEMhO8pJcu+o+7ocy5hGkSoawTHu0=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs-wayland",
|
||||
"rev": "771cb198c281db6918829651f194bf4db32e342d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs-wayland",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"mobile-nixos": "mobile-nixos",
|
||||
"nix-fast-build": "nix-fast-build",
|
||||
"nixpkgs-next-unpatched": "nixpkgs-next-unpatched",
|
||||
"nixpkgs-unpatched": "nixpkgs-unpatched",
|
||||
"nixpkgs-wayland": "nixpkgs-wayland",
|
||||
"sops-nix": "sops-nix",
|
||||
"uninsane-dot-org": "uninsane-dot-org"
|
||||
}
|
||||
@@ -254,11 +155,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710195194,
|
||||
"narHash": "sha256-KFxCJp0T6TJOz1IOKlpRdpsCr9xsvlVuWY/VCiAFnTE=",
|
||||
"lastModified": 1708987867,
|
||||
"narHash": "sha256-k2lDaDWNTU5sBVHanYzjDKVDmk29RHIgdbbXu5sdzBA=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "e52d8117b330f690382f1d16d81ae43daeb4b880",
|
||||
"rev": "a1c8de14f60924fafe13aea66b46157f0150f4cf",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -267,35 +168,19 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs-wayland",
|
||||
"nix-eval-jobs",
|
||||
"nix-fast-build",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1702979157,
|
||||
"narHash": "sha256-RnFBbLbpqtn4AoJGXKevQMCGhra4h6G2MPcuTSZZQ+g=",
|
||||
"lastModified": 1698438538,
|
||||
"narHash": "sha256-AWxaKTDL3MtxaVTVU5lYBvSnlspOS0Fjt8GxBgnU0Do=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "2961375283668d867e64129c22af532de8e77734",
|
||||
"rev": "5deb8dc125a9f83b65ca86cf0c8167c46593e0b1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
85
flake.nix
85
flake.nix
@@ -48,11 +48,6 @@
|
||||
# nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-staging-next";
|
||||
nixpkgs-next-unpatched.url = "github:nixos/nixpkgs?ref=staging-next";
|
||||
|
||||
nixpkgs-wayland = {
|
||||
url = "github:nix-community/nixpkgs-wayland";
|
||||
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
||||
};
|
||||
|
||||
mobile-nixos = {
|
||||
# <https://github.com/nixos/mobile-nixos>
|
||||
# only used for building disk images, not relevant after deployment
|
||||
@@ -62,6 +57,10 @@
|
||||
url = "github:nixos/mobile-nixos?ref=d25d3b87e7f300d8066e31d792337d9cd7ecd23b";
|
||||
flake = false;
|
||||
};
|
||||
nix-fast-build = {
|
||||
# https://github.com/Mic92/nix-fast-build
|
||||
url = "github:Mic92/nix-fast-build";
|
||||
};
|
||||
sops-nix = {
|
||||
# <https://github.com/Mic92/sops-nix>
|
||||
# used to distribute secrets to my hosts
|
||||
@@ -81,8 +80,8 @@
|
||||
self,
|
||||
nixpkgs-unpatched,
|
||||
nixpkgs-next-unpatched ? nixpkgs-unpatched,
|
||||
nixpkgs-wayland,
|
||||
mobile-nixos,
|
||||
nix-fast-build,
|
||||
sops-nix,
|
||||
uninsane-dot-org,
|
||||
...
|
||||
@@ -103,7 +102,30 @@
|
||||
patchNixpkgs = variant: nixpkgs: (import ./nixpatches/flake.nix).outputs {
|
||||
inherit variant nixpkgs;
|
||||
self = patchNixpkgs variant nixpkgs;
|
||||
};
|
||||
} // {
|
||||
# sourceInfo includes fields (square brackets for the ones which are not always present):
|
||||
# - [dirtyRev]
|
||||
# - [dirtyShortRev]
|
||||
# - lastModified
|
||||
# - lastModifiedDate
|
||||
# - narHash
|
||||
# - outPath
|
||||
# - [rev]
|
||||
# - [revCount]
|
||||
# - [shortRev]
|
||||
# - submodules
|
||||
#
|
||||
# these values are used within nixpkgs:
|
||||
# - to give a friendly name to the nixos system (`readlink /run/current-system` -> `...nixos-system-desko-24.05.20240227.dirty`)
|
||||
# - to alias `import <nixpkgs>` so that nix uses the system's nixpkgs when called externally (supposedly).
|
||||
#
|
||||
# these values seem to exist both within the `sourceInfo` attrset and at the top-level.
|
||||
# for a list of all implicit flake outputs (which is what these seem to be):
|
||||
# $ nix-repl
|
||||
# > lf .
|
||||
# > <tab>
|
||||
inherit (self) sourceInfo;
|
||||
} // self.sourceInfo;
|
||||
|
||||
nixpkgs' = patchNixpkgs "master" nixpkgs-unpatched;
|
||||
nixpkgsCompiledBy = system: nixpkgs'.legacyPackages."${system}";
|
||||
@@ -190,18 +212,12 @@
|
||||
let
|
||||
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
|
||||
uninsane = uninsane-dot-org.overlays.default;
|
||||
wayland = final: prev: {
|
||||
# default is to dump the packages into `waylandPkgs` *and* the toplevel.
|
||||
# but i just want the `waylandPkgs` set
|
||||
inherit (nixpkgs-wayland.overlays.default final prev)
|
||||
waylandPkgs
|
||||
new-wayland-protocols #< 2024/03/10: nixpkgs-wayland assumes this will be in the toplevel
|
||||
;
|
||||
};
|
||||
# TODO: why do i have to use `self.inputs.nix-fast-build` instead of just `nix-fast-build` here?
|
||||
nix-fast-build = (_: prev: self.inputs.nix-fast-build.packages."${prev.stdenv.system}" or {});
|
||||
in
|
||||
(mobile final prev)
|
||||
// (nix-fast-build final prev)
|
||||
// (uninsane final prev)
|
||||
// (wayland final prev)
|
||||
;
|
||||
};
|
||||
|
||||
@@ -257,8 +273,6 @@
|
||||
pkgs = self.legacyPackages."x86_64-linux";
|
||||
sanePkgs = import ./pkgs { inherit pkgs; };
|
||||
deployScript = host: addr: action: pkgs.writeShellScript "deploy-${host}" ''
|
||||
set -e
|
||||
|
||||
host="${host}"
|
||||
addr="${addr}"
|
||||
action="${if action != null then action else ""}"
|
||||
@@ -272,8 +286,8 @@
|
||||
fi
|
||||
}
|
||||
|
||||
nix build ".#nixosConfigurations.$host.config.system.build.toplevel" --out-link "./build/result-$host" "$@"
|
||||
storePath="$(readlink ./build/result-$host)"
|
||||
nix build ".#nixosConfigurations.$host.config.system.build.toplevel" --out-link "./result-$host" "$@"
|
||||
storePath="$(readlink ./result-$host)"
|
||||
|
||||
# mimic `nixos-rebuild --target-host`, in effect:
|
||||
# - nix-copy-closure ...
|
||||
@@ -376,8 +390,6 @@
|
||||
- or `nix run '.#preDeploy'` to target all hosts
|
||||
- `nix run '.#check'`
|
||||
- make sure all systems build; NUR evaluates
|
||||
- `nix run '.#bench'`
|
||||
- benchmark the eval time of common targets this flake provides
|
||||
|
||||
specific build targets of interest:
|
||||
- `nix build '.#imgs.rescue'`
|
||||
@@ -521,7 +533,6 @@
|
||||
--option allow-import-from-derivation true \
|
||||
--drv-path --show-trace \
|
||||
-I nixpkgs=${nixpkgs-unpatched} \
|
||||
-I nixpkgs-overlays=${./.}/hosts/common/nix/overlay \
|
||||
-I ../../ \
|
||||
| tee # tee to prevent interactive mode
|
||||
'');
|
||||
@@ -533,7 +544,7 @@
|
||||
checkHost = host: let
|
||||
shellHost = pkgs.lib.replaceStrings [ "-" ] [ "_" ] host;
|
||||
in ''
|
||||
nix build -v '.#nixosConfigurations.${host}.config.system.build.toplevel' --out-link ./build/result-${host} -j2 "$@"
|
||||
nix build -v '.#nixosConfigurations.${host}.config.system.build.toplevel' --out-link ./result-${host} -j2 "$@"
|
||||
RC_${shellHost}=$?
|
||||
'';
|
||||
in builtins.toString (pkgs.writeShellScript
|
||||
@@ -582,31 +593,7 @@
|
||||
check.rescue = {
|
||||
type = "app";
|
||||
program = builtins.toString (pkgs.writeShellScript "check-rescue" ''
|
||||
nix build -v '.#imgs.rescue' --out-link ./build/result-rescue-img -j2
|
||||
'');
|
||||
};
|
||||
|
||||
bench = {
|
||||
type = "app";
|
||||
program = builtins.toString (pkgs.writeShellScript "bench" ''
|
||||
doBench() {
|
||||
attrPath="$1"
|
||||
shift
|
||||
echo -n "benchmarking eval of '$attrPath'... "
|
||||
/run/current-system/sw/bin/time -f "%e sec" -o /dev/stdout \
|
||||
nix eval --no-eval-cache --quiet --raw ".#$attrPath" --apply 'result: if result != null then "" else "unexpected null"' $@ 2> /dev/null
|
||||
}
|
||||
|
||||
if [ -n "$1" ]; then
|
||||
doBench "$@"
|
||||
else
|
||||
doBench hostConfigs
|
||||
doBench hostConfigs.lappy
|
||||
doBench hostConfigs.lappy.sane.programs
|
||||
doBench hostConfigs.lappy.sane.users.colin
|
||||
doBench hostConfigs.lappy.sane.fs
|
||||
doBench hostConfigs.lappy.environment.systemPackages
|
||||
fi
|
||||
nix build -v '.#imgs.rescue' --out-link ./result-rescue-img -j2
|
||||
'');
|
||||
};
|
||||
};
|
||||
|
@@ -2,6 +2,7 @@
|
||||
{
|
||||
imports = [
|
||||
./fs.nix
|
||||
./polyfill.nix
|
||||
];
|
||||
|
||||
sane.roles.client = true;
|
||||
|
41
hosts/by-name/lappy/polyfill.nix
Normal file
41
hosts/by-name/lappy/polyfill.nix
Normal file
@@ -0,0 +1,41 @@
|
||||
# doesn't actually *enable* anything,
|
||||
# but sets up any modules such that if they *were* enabled, they'll act as expected.
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.gui.sxmo = {
|
||||
noidle = true; #< power button requires 1s hold, which makes it impractical to be dealing with.
|
||||
settings = {
|
||||
# XXX: make sure the user is part of the `input` group!
|
||||
SXMO_LISGD_INPUT_DEVICE = "/dev/input/by-id/usb-Wacom_Co._Ltd._Pen_and_multitouch_sensor-event-if00";
|
||||
# these identifiers are from `swaymsg -t get_inputs`
|
||||
SXMO_VOLUME_BUTTON = "1:1:AT_Translated_Set_2_keyboard";
|
||||
# SXMO_VOLUME_BUTTON = "none";
|
||||
# N.B.: thinkpad's power button requires a full second press to do anything
|
||||
SXMO_POWER_BUTTON = "0:1:Power_Button";
|
||||
# SXMO_POWER_BUTTON = "none";
|
||||
SXMO_DISABLE_LEDS = "1";
|
||||
SXMO_UNLOCK_IDLE_TIME = "120"; # default
|
||||
# sxmo tries to determine device type from /proc/device-tree/compatible,
|
||||
# but that doesn't seem to exist on NixOS? (or maybe it just doesn't exist
|
||||
# on non-aarch64 builds).
|
||||
# the device type informs (at least):
|
||||
# - SXMO_WIFI_MODULE
|
||||
# - SXMO_RTW_SCAN_INTERVAL
|
||||
# - SXMO_TOUCHSCREEN_ID
|
||||
# - SXMO_MONITOR
|
||||
# - SXMO_ALSA_CONTROL_NAME
|
||||
# - SXMO_SWAY_SCALE
|
||||
# see <repo:mil/sxmo-utils:scripts/deviceprofiles>
|
||||
# SXMO_DEVICE_NAME = "pine64,pinephone-1.2";
|
||||
# if sxmo doesn't know the device, it can't decide whether to use one_button or three_button mode
|
||||
# and so it just wouldn't handle any button inputs (sxmo_hook_inputhandler.sh not on path)
|
||||
SXMO_DEVICE_NAME = "three_button_touchscreen";
|
||||
};
|
||||
package = (pkgs.sxmo-utils.override { preferSystemd = true; }).overrideAttrs (base: {
|
||||
postPatch = (base.postPatch or "") + ''
|
||||
# after volume-button navigation mode, restore full keyboard functionality
|
||||
cp ${./xkb_mobile_normal_buttons} ./configs/xkb/xkb_mobile_normal_buttons
|
||||
'';
|
||||
});
|
||||
};
|
||||
}
|
@@ -36,15 +36,10 @@
|
||||
|
||||
sops.secrets.colin-passwd.neededForUsers = true;
|
||||
|
||||
# sane.gui.sxmo.enable = true;
|
||||
sane.programs.sway.enableFor.user.colin = true;
|
||||
sane.programs.swaylock.enableFor.user.colin = false; #< not usable on touch
|
||||
sane.programs.schlock.enableFor.user.colin = true;
|
||||
sane.programs.swayidle.config.actions.screenoff.delay = 300;
|
||||
sane.programs.swayidle.config.actions.screenoff.enable = true;
|
||||
sane.programs.sane-input-handler.enableFor.user.colin = true;
|
||||
sane.gui.sxmo.enable = true;
|
||||
# sane.programs.consoleUtils.enableFor.user.colin = false;
|
||||
# sane.programs.guiApps.enableFor.user.colin = false;
|
||||
sane.programs.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile!
|
||||
sane.programs.fcitx5.enableFor.user.colin = false; # does not cross compile
|
||||
sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile
|
||||
sane.programs.nvme-cli.enableFor.system = false; # does not cross compile (libhugetlbfs)
|
||||
|
||||
@@ -57,6 +52,7 @@
|
||||
# sane.programs.signal-desktop.config.autostart = true; # TODO: enable once electron stops derping.
|
||||
# sane.programs."gnome.geary".config.autostart = true;
|
||||
# sane.programs.calls.config.autostart = true;
|
||||
sane.programs.mpv.config.vo = "wlshm"; #< see hosts/common/programs/mpv.nix for details
|
||||
|
||||
sane.programs.firefox.mime.priority = 300; # prefer other browsers when possible
|
||||
# HACK/TODO: make `programs.P.env.VAR` behave according to `mime.priority`
|
||||
@@ -128,13 +124,43 @@
|
||||
# enable rotation sensor
|
||||
hardware.sensor.iio.enable = true;
|
||||
|
||||
# inject specialized alsa configs via the environment.
|
||||
# specifically, this gets the pinephone headphones & internal earpiece working.
|
||||
# see pkgs/patched/alsa-ucm-conf for more info.
|
||||
environment.variables.ALSA_CONFIG_UCM2 = "/run/current-system/sw/share/alsa/ucm2";
|
||||
environment.pathsToLink = [ "/share/alsa/ucm2" ];
|
||||
environment.systemPackages = [
|
||||
(pkgs.alsa-ucm-conf-sane.override {
|
||||
# internal speaker has a tendency to break :(
|
||||
preferEarpiece = true;
|
||||
})
|
||||
];
|
||||
systemd = let
|
||||
ucm-env = config.environment.variables.ALSA_CONFIG_UCM2;
|
||||
in {
|
||||
# cribbed from <repo:nixos/mobile-nixos:modules/quirks/audio.nix>
|
||||
|
||||
# pipewire
|
||||
user.services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
user.services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
user.services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
|
||||
# pulseaudio
|
||||
# user.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
# services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
|
||||
|
||||
# TODO: move elsewhere...
|
||||
systemd.services.ModemManager.serviceConfig = {
|
||||
services.ModemManager.serviceConfig = {
|
||||
# N.B.: the extra "" in ExecStart serves to force upstream ExecStart to be ignored
|
||||
ExecStart = [ "" "${pkgs.modemmanager}/bin/ModemManager --debug" ];
|
||||
# --debug sets DEBUG level logging: so reset
|
||||
ExecStartPost = [ "${pkgs.modemmanager}/bin/mmcli --set-logging=INFO" ];
|
||||
};
|
||||
};
|
||||
|
||||
services.udev.extraRules = let
|
||||
chmod = "${pkgs.coreutils}/bin/chmod";
|
||||
|
@@ -85,7 +85,6 @@ in
|
||||
"lima.sched_timeout_ms=2000"
|
||||
];
|
||||
|
||||
# services.xserver.displayManager.job.preStart = ensureHWReady;
|
||||
# systemd.services.greetd.preStart = ensureHWReady;
|
||||
systemd.services.unl0kr.preStart = ensureHWReady;
|
||||
services.xserver.displayManager.job.preStart = ensureHWReady;
|
||||
systemd.services.greetd.preStart = ensureHWReady;
|
||||
}
|
||||
|
@@ -24,22 +24,73 @@
|
||||
backlight = "backlight"; # /sys/class/backlight/*backlight*/brightness
|
||||
};
|
||||
|
||||
sane.programs.alacritty.config.fontSize = 9;
|
||||
sane.gui.sxmo = {
|
||||
nogesture = true;
|
||||
settings = {
|
||||
### hardware: touch screen
|
||||
SXMO_LISGD_INPUT_DEVICE = "/dev/input/by-path/platform-1c2ac00.i2c-event";
|
||||
# vol and power are detected correctly by upstream
|
||||
|
||||
sane.programs.sway.config = {
|
||||
font = "pango:monospace 10";
|
||||
mod = "Mod1"; # prefer Alt
|
||||
workspace_layout = "tabbed";
|
||||
### preferences
|
||||
DEFAULT_COUNTRY = "US";
|
||||
|
||||
SXMO_AUTOROTATE = "1"; # enable auto-rotation at launch. has no meaning in stock/upstream sxmo-utils
|
||||
|
||||
# BEMENU lines (wayland DMENU):
|
||||
# - camera is 9th entry
|
||||
# - flashlight is 10th entry
|
||||
# - config is 14th entry. inside that:
|
||||
# - autorotate is 11th entry
|
||||
# - system menu is 19th entry
|
||||
# - close is 20th entry
|
||||
# - power is 15th entry
|
||||
# - close is 16th entry
|
||||
SXMO_BEMENU_LANDSCAPE_LINES = "11"; # default 8
|
||||
SXMO_BEMENU_PORTRAIT_LINES = "16"; # default 16
|
||||
SXMO_LOCK_IDLE_TIME = "15"; # how long between screenoff -> lock -> back to screenoff (default: 8)
|
||||
# gravity: how far to tilt the device before the screen rotates
|
||||
# for a given setting, normal <-> invert requires more movement then left <-> right
|
||||
# i.e. the settingd doesn't feel completely symmetric
|
||||
# SXMO_ROTATION_GRAVITY default is 16374
|
||||
# SXMO_ROTATION_GRAVITY = "12800"; # uncomfortably high
|
||||
# SXMO_ROTATION_GRAVITY = "12500"; # kinda uncomfortable when walking
|
||||
SXMO_ROTATION_GRAVITY = "12000";
|
||||
SXMO_SCREENSHOT_DIR = "/home/colin/Pictures"; # default: "$HOME"
|
||||
|
||||
# sway/wayland scaling:
|
||||
# - conflicting info out there on how scaling actually works
|
||||
# at the least, for things where it matters (mpv), it seems like scale settings have 0 effect on perf
|
||||
# ways to enforce scaling:
|
||||
# - <https://wiki.archlinux.org/title/HiDPI>
|
||||
# - `swaymsg -- output DSI-1 scale 2.0` (scales everything)
|
||||
# - `dconf write /org/gnome/desktop/interface/text-scaling-factor 2.0` (scales ONLY TEXT)
|
||||
# - `GDK_DPI_SCALE=2.0` (scales ONLY TEXT)
|
||||
#
|
||||
# application notes:
|
||||
# - cozy: in landscape, playback position is not visible unless scale <= 1.7
|
||||
# - if in a tab, then scale 1.6 is the max
|
||||
# SXMO_SWAY_SCALE = "1.5"; # hard to press gPodder icons
|
||||
SXMO_SWAY_SCALE = "1.6";
|
||||
# SXMO_SWAY_SCALE = "1.8";
|
||||
# SXMO_SWAY_SCALE = "2";
|
||||
SXMO_WORKSPACE_WRAPPING = "5"; # how many workspaces. default: 4
|
||||
|
||||
# wvkbd layers:
|
||||
# - full
|
||||
# - landscape
|
||||
# - special (e.g. coding symbols like ~)
|
||||
# - emoji
|
||||
# - nav
|
||||
# - simple (like landscape, but no parens/tab/etc; even fewer chars)
|
||||
# - simplegrid (simple, but grid layout)
|
||||
# - dialer (digits)
|
||||
# - cyrillic
|
||||
# - arabic
|
||||
# - persian
|
||||
# - greek
|
||||
# - georgian
|
||||
WVKBD_LANDSCAPE_LAYERS = "landscape,special,emoji";
|
||||
WVKBD_LAYERS = "full,special,emoji";
|
||||
};
|
||||
|
||||
sane.programs.waybar.config = {
|
||||
fontSize = 14;
|
||||
height = 26;
|
||||
persistWorkspaces = [ "1" "2" "3" "4" "5" ];
|
||||
modules.media = false;
|
||||
modules.network = false;
|
||||
modules.perf = false;
|
||||
modules.windowTitle = false;
|
||||
# TODO: show modem state
|
||||
};
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@
|
||||
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
sane.persist.enable = false; # what we mean here is that the image is immutable; `/` is still tmpfs.
|
||||
sane.persist.enable = false;
|
||||
sane.nixcache.enable = false; # don't want to be calling out to dead machines that we're *trying* to rescue
|
||||
|
||||
# auto-login at shell
|
||||
|
@@ -82,34 +82,34 @@
|
||||
};
|
||||
sane.fs."/mnt/usb-hdd".mount = {};
|
||||
|
||||
sane.persist.sys.byStore.plaintext = [{
|
||||
path = "/var/media";
|
||||
method = "bind"; #< this HAS to be `bind` if we're going to persist the whole thing but create subdirs, as below.
|
||||
user = "colin";
|
||||
group = "media";
|
||||
mode = "0755";
|
||||
}];
|
||||
sane.fs."/var/media/archive".dir = {};
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
# TODO: this is overly broad; only need media and share directories to be persisted
|
||||
{ user = "colin"; group = "users"; path = "/var/lib/uninsane"; method = "bind"; }
|
||||
];
|
||||
# force some problematic directories to always get correct permissions:
|
||||
sane.fs."/var/lib/uninsane/media".dir.acl = {
|
||||
user = "colin"; group = "media"; mode = "0775";
|
||||
};
|
||||
sane.fs."/var/lib/uninsane/media/archive".dir = {};
|
||||
# this is file.text instead of symlink.text so that it may be read over a remote mount (where consumers might not have any /nix/store/.../README.md path)
|
||||
sane.fs."/var/media/archive/README.md".file.text = ''
|
||||
sane.fs."/var/lib/uninsane/media/archive/README.md".file.text = ''
|
||||
this directory is for media i wish to remove from my library,
|
||||
but keep for a short time in case i reverse my decision.
|
||||
treat it like a system trash can.
|
||||
'';
|
||||
sane.fs."/var/media/Books".dir = {};
|
||||
sane.fs."/var/media/Books/Audiobooks".dir = {};
|
||||
sane.fs."/var/media/Books/Books".dir = {};
|
||||
sane.fs."/var/media/Books/Visual".dir = {};
|
||||
sane.fs."/var/media/collections".dir = {};
|
||||
sane.fs."/var/media/datasets".dir = {};
|
||||
sane.fs."/var/media/freeleech".dir = {};
|
||||
sane.fs."/var/media/Music".dir = {};
|
||||
sane.fs."/var/media/Pictures".dir = {};
|
||||
sane.fs."/var/media/Videos".dir = {};
|
||||
sane.fs."/var/media/Videos/Film".dir = {};
|
||||
sane.fs."/var/media/Videos/Shows".dir = {};
|
||||
sane.fs."/var/media/Videos/Talks".dir = {};
|
||||
|
||||
sane.fs."/var/lib/uninsane/media/Books".dir = {};
|
||||
sane.fs."/var/lib/uninsane/media/Books/Audiobooks".dir = {};
|
||||
sane.fs."/var/lib/uninsane/media/Books/Books".dir = {};
|
||||
sane.fs."/var/lib/uninsane/media/Books/Visual".dir = {};
|
||||
sane.fs."/var/lib/uninsane/media/collections".dir = {};
|
||||
sane.fs."/var/lib/uninsane/media/datasets".dir = {};
|
||||
sane.fs."/var/lib/uninsane/media/freeleech".dir = {};
|
||||
sane.fs."/var/lib/uninsane/media/Music".dir = {};
|
||||
sane.fs."/var/lib/uninsane/media/Pictures".dir = {};
|
||||
sane.fs."/var/lib/uninsane/media/Videos".dir = {};
|
||||
sane.fs."/var/lib/uninsane/media/Videos/Film".dir = {};
|
||||
sane.fs."/var/lib/uninsane/media/Videos/Shows".dir = {};
|
||||
sane.fs."/var/lib/uninsane/media/Videos/Talks".dir = {};
|
||||
# this is file.text instead of symlink.text so that it may be read over a remote mount (where consumers might not have any /nix/store/.../README.md path)
|
||||
sane.fs."/var/lib/uninsane/datasets/README.md".file.text = ''
|
||||
this directory may seem redundant with ../media/datasets. it isn't.
|
||||
@@ -122,19 +122,19 @@
|
||||
user = "colin";
|
||||
group = "users";
|
||||
mode = "0777";
|
||||
path = "/var/media/Videos";
|
||||
path = "/var/lib/uninsane/media/Videos";
|
||||
}
|
||||
{
|
||||
user = "colin";
|
||||
group = "users";
|
||||
mode = "0777";
|
||||
path = "/var/media/freeleech";
|
||||
path = "/var/lib/uninsane/media/freeleech";
|
||||
}
|
||||
{
|
||||
user = "colin";
|
||||
group = "users";
|
||||
mode = "0775";
|
||||
path = "/var/lib/uninsane/datasets";
|
||||
mode = "0777";
|
||||
path = "/var/lib/uninsane/media/datasets";
|
||||
}
|
||||
];
|
||||
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
fileSystems."/var/export/media" = {
|
||||
# everything in here could be considered publicly readable (based on the viewer's legal jurisdiction)
|
||||
device = "/var/media";
|
||||
device = "/var/lib/uninsane/media";
|
||||
options = [ "rbind" ];
|
||||
};
|
||||
# fileSystems."/var/export/playground" = {
|
||||
|
@@ -1,6 +1,5 @@
|
||||
{ lib, ... }:
|
||||
|
||||
lib.mkIf false #< i don't actively use navidrome
|
||||
{
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
{ user = "navidrome"; group = "navidrome"; path = "/var/lib/navidrome"; method = "bind"; }
|
||||
@@ -10,7 +9,7 @@ lib.mkIf false #< i don't actively use navidrome
|
||||
# docs: https://www.navidrome.org/docs/usage/configuration-options/
|
||||
Address = "127.0.0.1";
|
||||
Port = 4533;
|
||||
MusicFolder = "/var/media/Music";
|
||||
MusicFolder = "/var/lib/uninsane/media/Music";
|
||||
CovertArtPriority = "*.jpg, *.JPG, *.png, *.PNG, embedded";
|
||||
AutoImportPlaylists = false;
|
||||
ScanSchedule = "@every 1h";
|
||||
|
@@ -55,8 +55,8 @@ in
|
||||
|
||||
# web blog/personal site
|
||||
# alternative way to link stuff into the share:
|
||||
# sane.fs."/var/www/sites/uninsane.org/share/Ubunchu".mount.bind = "/var/media/Books/Visual/HiroshiSeo/Ubunchu";
|
||||
# sane.fs."/var/media/Books/Visual/HiroshiSeo/Ubunchu".dir = {};
|
||||
# sane.fs."/var/lib/uninsane/share/Ubunchu".mount.bind = "/var/lib/uninsane/media/Books/Visual/HiroshiSeo/Ubunchu";
|
||||
# sane.fs."/var/lib/uninsane/media/Books/Visual/HiroshiSeo/Ubunchu".dir = {};
|
||||
services.nginx.virtualHosts."uninsane.org" = publog {
|
||||
# a lot of places hardcode https://uninsane.org,
|
||||
# and then when we mix http + non-https, we get CORS violations
|
||||
|
@@ -44,13 +44,13 @@
|
||||
# [Alias]/path/on/disk
|
||||
# NOTE: Music library is quick to scan; videos take a solid 10min to scan.
|
||||
# TODO: re-enable the other libraries
|
||||
# "[Audioooks]/var/media/Books/Audiobooks"
|
||||
# "[Books]/var/media/Books/Books"
|
||||
# "[Manga]/var/media/Books/Visual"
|
||||
# "[games]/var/media/games"
|
||||
"[Music]/var/media/Music"
|
||||
# "[Film]/var/media/Videos/Film"
|
||||
# "[Shows]/var/media/Videos/Shows"
|
||||
# "[Audioooks]/var/lib/uninsane/media/Books/Audiobooks"
|
||||
# "[Books]/var/lib/uninsane/media/Books/Books"
|
||||
# "[Manga]/var/lib/uninsane/media/Books/Visual"
|
||||
# "[games]/var/lib/uninsane/media/games"
|
||||
"[Music]/var/lib/uninsane/media/Music"
|
||||
# "[Film]/var/lib/uninsane/media/Videos/Film"
|
||||
# "[Shows]/var/lib/uninsane/media/Videos/Shows"
|
||||
];
|
||||
# directories.downloads = "..." # TODO
|
||||
# directories.incomplete = "..." # TODO
|
||||
|
@@ -72,8 +72,8 @@ in
|
||||
# see: https://git.zknt.org/mirror/transmission/commit/cfce6e2e3a9b9d31a9dafedd0bdc8bf2cdb6e876?lang=bg-BG
|
||||
anti-brute-force-enabled = false;
|
||||
|
||||
download-dir = "/var/media";
|
||||
incomplete-dir = "/var/media/incomplete";
|
||||
download-dir = "/var/lib/uninsane/media";
|
||||
incomplete-dir = "/var/lib/uninsane/media/incomplete";
|
||||
# transmission regularly fails to move stuff from the incomplete dir to the main one, so disable:
|
||||
# TODO: uncomment this line!
|
||||
incomplete-dir-enabled = false;
|
||||
|
@@ -43,6 +43,17 @@
|
||||
fi
|
||||
'';
|
||||
};
|
||||
system.activationScripts.notifyActive = {
|
||||
text = ''
|
||||
# send a notification to any sway users logged in, that the system has been activated/upgraded.
|
||||
# this probably doesn't work if more than one sway session exists on the system.
|
||||
_notifyActiveSwaySock="$(echo /run/user/*/sway-ipc*.sock)"
|
||||
if [ -e "$_notifyActiveSwaySock" ]; then
|
||||
SWAYSOCK="$_notifyActiveSwaySock" ${config.sane.programs.sway.packageUnwrapped}/bin/swaymsg -- exec \
|
||||
"${pkgs.libnotify}/bin/notify-send 'nixos activated' 'version: $(cat $systemConfig/nixos-version)'"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
# link debug symbols into /run/current-system/sw/lib/debug
|
||||
# hopefully picked up by gdb automatically?
|
||||
|
@@ -121,7 +121,6 @@ let
|
||||
texts = [
|
||||
(fromDb "acoup.blog/feed") # history, states. author: <https://historians.social/@bretdevereaux/following>
|
||||
(fromDb "amosbbatto.wordpress.com" // tech)
|
||||
(fromDb "anish.lakhwara.com" // tech)
|
||||
(fromDb "applieddivinitystudies.com" // rat)
|
||||
(fromDb "artemis.sh" // tech)
|
||||
(fromDb "ascii.textfiles.com" // tech) # Jason Scott
|
||||
@@ -162,7 +161,6 @@ let
|
||||
(fromDb "palladiummag.com" // uncat)
|
||||
(fromDb "philosopher.coach" // rat) # Peter Saint-Andre -- side project of stpeter.im
|
||||
(fromDb "pomeroyb.com" // tech)
|
||||
(fromDb "postmarketos.org/blog" // tech)
|
||||
(fromDb "preposterousuniverse.com" // rat) # Sean Carroll
|
||||
(fromDb "profectusmag.com" // uncat)
|
||||
(fromDb "project-insanity.org" // tech) # shared blog by a few NixOS devs, notably onny
|
||||
@@ -174,11 +172,9 @@ let
|
||||
(fromDb "sagacioussuricata.com" // tech) # ian (Sanctuary)
|
||||
(fromDb "semiaccurate.com" // tech)
|
||||
(fromDb "sideways-view.com" // rat) # Paul Christiano
|
||||
(fromDb "slatecave.net" // tech)
|
||||
(fromDb "slimemoldtimemold.com" // rat)
|
||||
(fromDb "spectrum.ieee.org" // tech)
|
||||
(fromDb "stpeter.im/atom.xml" // pol)
|
||||
(fromDb "thediff.co" // pol) # Byrne Hobart
|
||||
# (fromDb "theregister.com" // tech)
|
||||
(fromDb "thisweek.gnome.org" // tech)
|
||||
(fromDb "tuxphones.com" // tech)
|
||||
@@ -186,15 +182,17 @@ let
|
||||
(fromDb "unintendedconsequenc.es" // rat)
|
||||
# (fromDb "vitalik.ca" // tech) # moved to vitalik.eth.limo
|
||||
(fromDb "vitalik.eth.limo" // tech) # Vitalik Buterin
|
||||
# (fromDb "webcurious.co.uk" // uncat) # link aggregator; defunct?
|
||||
(fromDb "webcurious.co.uk" // uncat)
|
||||
(fromDb "xn--gckvb8fzb.com" // tech)
|
||||
(mkSubstack "astralcodexten" // rat // daily) # Scott Alexander
|
||||
(mkSubstack "byrnehobart" // pol // infrequent)
|
||||
# (mkSubstack "doomberg" // tech // weekly) # articles are all pay-walled
|
||||
(mkSubstack "eliqian" // rat // weekly)
|
||||
(mkSubstack "oversharing" // pol // daily)
|
||||
(mkSubstack "samkriss" // humor // infrequent)
|
||||
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
|
||||
(mkText "http://boginjr.com/feed" // tech // infrequent)
|
||||
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
|
||||
(mkText "https://forum.merveilles.town/rss.xml" // pol // infrequent) #quality RSS list here: <https://forum.merveilles.town/thread/57/share-your-rss-feeds%21-6/>
|
||||
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
|
||||
(mkText "https://jvns.ca/atom.xml" // tech // weekly) # Julia Evans
|
||||
@@ -213,7 +211,6 @@ let
|
||||
(fromDb "youtube.com/@Exurb1a")
|
||||
(fromDb "youtube.com/@hbomberguy")
|
||||
(fromDb "youtube.com/@JackStauber")
|
||||
(fromDb "youtube.com/@NativLang")
|
||||
(fromDb "youtube.com/@PolyMatter")
|
||||
# (fromDb "youtube.com/@rossmanngroup" // pol // tech) # Louis Rossmann
|
||||
(fromDb "youtube.com/@TechnologyConnections" // tech)
|
||||
|
@@ -79,6 +79,11 @@
|
||||
HandleLidSwitch=lock
|
||||
'';
|
||||
|
||||
# some packages build only if binfmt *isn't* present
|
||||
nix.settings.system-features = lib.mkIf (config.boot.binfmt.emulatedSystems == []) [
|
||||
"no-binfmt"
|
||||
];
|
||||
|
||||
# services.snapper.configs = {
|
||||
# root = {
|
||||
# subvolume = "/";
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{ config, lib, ... }:
|
||||
{ config, ... }:
|
||||
{
|
||||
sane.user.persist.byStore.plaintext = [
|
||||
"archive"
|
||||
@@ -29,17 +29,14 @@
|
||||
];
|
||||
|
||||
# convenience
|
||||
sane.user.fs = let
|
||||
persistEnabled = config.sane.persist.enable;
|
||||
in {
|
||||
".persist/private" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.private.origin; };
|
||||
".persist/plaintext" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.plaintext.origin; };
|
||||
".persist/ephemeral" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.cryptClearOnBoot.origin; };
|
||||
sane.user.fs.".persist/private".symlink.target = config.sane.persist.stores.private.origin;
|
||||
sane.user.fs.".persist/plaintext".symlink.target = config.sane.persist.stores.plaintext.origin;
|
||||
sane.user.fs.".persist/ephemeral".symlink.target = config.sane.persist.stores.cryptClearOnBoot.origin;
|
||||
|
||||
"nixos".symlink.target = "dev/nixos";
|
||||
sane.user.fs."nixos".symlink.target = "dev/nixos";
|
||||
|
||||
"Books/servo".symlink.target = "/mnt/servo/media/Books";
|
||||
"Videos/servo".symlink.target = "/mnt/servo/media/Videos";
|
||||
"Pictures/servo-macros".symlink.target = "/mnt/servo/media/Pictures/macros";
|
||||
};
|
||||
sane.user.fs."Books/servo".symlink.target = "/mnt/servo/media/Books";
|
||||
sane.user.fs."Videos/servo".symlink.target = "/mnt/servo/media/Videos";
|
||||
# sane.user.fs."Music/servo".symlink.target = "/mnt/servo/media/Music";
|
||||
sane.user.fs."Pictures/servo-macros".symlink.target = "/mnt/servo/media/Pictures/macros";
|
||||
}
|
||||
|
@@ -53,9 +53,8 @@
|
||||
|
||||
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages.
|
||||
# this is actually a no-op, and the real action happens in assigning `nix.settings.nix-path`.
|
||||
nix.nixPath = (lib.optionals config.sane.enableSlowPrograms [
|
||||
nix.nixPath = [
|
||||
"nixpkgs=${pkgs.path}"
|
||||
]) ++ [
|
||||
# note the import starts at repo root: this allows `./overlay/default.nix` to access the stuff at the root
|
||||
# "nixpkgs-overlays=${../../..}/hosts/common/nix-path/overlay"
|
||||
# as long as my system itself doesn't rely on NIXPKGS at runtime, we can point the overlays to git
|
||||
@@ -64,13 +63,7 @@
|
||||
];
|
||||
|
||||
# ensure new deployments have a source of this repo with which they can bootstrap.
|
||||
# this however changes on every commit and can be slow to copy for e.g. `moby`.
|
||||
environment.etc."nixos" = lib.mkIf config.sane.enableSlowPrograms {
|
||||
source = ../../..;
|
||||
};
|
||||
environment.etc."nix/registry.json" = lib.mkIf (!config.sane.enableSlowPrograms) {
|
||||
enable = false;
|
||||
};
|
||||
environment.etc."nixos".source = ../../..;
|
||||
|
||||
systemd.services.nix-daemon.serviceConfig = {
|
||||
# the nix-daemon manages nix builders
|
||||
|
@@ -13,7 +13,7 @@
|
||||
"/run/current-system/sw"
|
||||
];
|
||||
|
||||
# NIXPKGS_CONFIG defaults to "/etc/nix/nixpkgs-config.nix" in <nixos/modules/programs/environment.nix>.
|
||||
# NIXPKGS_CONFIG defaults to "/etc/nix/nixpkgs-config.nix", for idfk why.
|
||||
# that's never existed on my system and everything does fine without it set empty (no nixpkgs API to forcibly *unset* it).
|
||||
environment.variables.NIXPKGS_CONFIG = lib.mkForce "";
|
||||
# XDG_CONFIG_DIRS defaults to "/etc/xdg", which doesn't exist.
|
||||
@@ -42,26 +42,4 @@
|
||||
# so as to inform when trying to run a non-nixos binary?
|
||||
# IMO that's confusing: i thought /lib/ld-linux.so was some file actually required by nix.
|
||||
environment.stub-ld.enable = false;
|
||||
|
||||
# `less.enable` sets LESSKEYIN_SYSTEM, LESSOPEN, LESSCLOSE env vars, which does confusing "lesspipe" things, so disable that.
|
||||
# it's enabled by default from `<nixos/modules/programs/environment.nix>`, who also sets `PAGER="less"` and `EDITOR="nano"` (keep).
|
||||
programs.less.enable = lib.mkForce false;
|
||||
environment.variables.PAGER = lib.mkOverride 900 ""; # mkDefault sets 1000. non-override is 100. 900 will beat the nixpkgs `mkDefault` but not anyone else.
|
||||
environment.variables.EDITOR = lib.mkOverride 900 "";
|
||||
|
||||
# several packages (dconf, modemmanager, networkmanager, gvfs, polkit, udisks, bluez/blueman, feedbackd, etc)
|
||||
# will add themselves to the dbus search path.
|
||||
# i prefer dbus to only search XDG paths (/share/dbus-1) for service files, as that's more introspectable.
|
||||
# see: <repo:nixos/nixpkgs:nixos/modules/services/system/dbus.nix>
|
||||
# TODO: sandbox dbus? i pretty explicitly don't want to use it as a launcher.
|
||||
services.dbus.packages = lib.mkForce [
|
||||
"/run/current-system/sw"
|
||||
# config.system.path
|
||||
# pkgs.dbus
|
||||
# pkgs.polkit.out
|
||||
# pkgs.modemmanager
|
||||
# pkgs.networkmanager
|
||||
# pkgs.udisks
|
||||
# pkgs.wpa_supplicant
|
||||
];
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@
|
||||
{
|
||||
sane.programs.aerc = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "inplace"; #< /share/aerc/aerc.conf refers to other /share files by absolute path
|
||||
sandbox.wrapperType = "inplace";
|
||||
sandbox.net = "clearnet";
|
||||
secrets.".config/aerc/accounts.conf" = ../../../secrets/common/aerc_accounts.conf.bin;
|
||||
mime.associations."x-scheme-handler/mailto" = "aerc.desktop";
|
||||
|
@@ -3,28 +3,14 @@
|
||||
# - `man 5 alacritty`
|
||||
# - defaults: <https://github.com/alacritty/alacritty/releases> -> alacritty.yml
|
||||
# - irc: #alacritty on libera.chat
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.alacritty;
|
||||
in
|
||||
{ lib, ... }:
|
||||
{
|
||||
sane.programs.alacritty = {
|
||||
configOption = with lib; mkOption {
|
||||
default = {};
|
||||
type = types.submodule {
|
||||
options.fontSize = mkOption {
|
||||
type = types.int;
|
||||
default = 14;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
sandbox.enable = false;
|
||||
env.TERMINAL = lib.mkDefault "alacritty";
|
||||
|
||||
fs.".config/alacritty/alacritty.toml".symlink.text = ''
|
||||
[font]
|
||||
size = ${builtins.toString cfg.config.fontSize}
|
||||
size = 14
|
||||
|
||||
[[keyboard.bindings]]
|
||||
mods = "Control"
|
||||
@@ -50,21 +36,6 @@ in
|
||||
mods = "Control|Shift"
|
||||
key = "PageDown"
|
||||
action = "ScrollPageDown"
|
||||
|
||||
# disable OS shortcuts which leak through...
|
||||
# see sway config or sane-input-handler for more info on why these leak through
|
||||
[[keyboard.bindings]]
|
||||
key = "AudioVolumeUp"
|
||||
action = "None"
|
||||
[[keyboard.bindings]]
|
||||
key = "AudioVolumeDown"
|
||||
action = "None"
|
||||
[[keyboard.bindings]]
|
||||
key = "Power"
|
||||
action = "None"
|
||||
[[keyboard.bindings]]
|
||||
key = "PowerOff"
|
||||
action = "None"
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
@@ -1,65 +0,0 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.alsa-ucm-conf;
|
||||
in
|
||||
{
|
||||
sane.programs.alsa-ucm-conf = {
|
||||
configOption = with lib; mkOption {
|
||||
default = {};
|
||||
type = types.submodule {
|
||||
options.preferEarpiece = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# upstream alsa ships with PinePhone audio configs, but they don't actually produce sound.
|
||||
# see: <https://github.com/alsa-project/alsa-ucm-conf/pull/134>
|
||||
# these audio files come from some revision of:
|
||||
# - <https://gitlab.manjaro.org/manjaro-arm/packages/community/phosh/alsa-ucm-pinephone>
|
||||
#
|
||||
# alternative to patching is to plumb `ALSA_CONFIG_UCM2 = "${./ucm2}"` environment variable into the relevant places
|
||||
# e.g. `systemd.services.pulseaudio.environment`.
|
||||
# that leaves more opportunity for gaps (i.e. missing a service),
|
||||
# on the other hand this method causes about 500 packages to be rebuilt (including qt5 and webkitgtk).
|
||||
#
|
||||
# note that with these files, the following audio device support:
|
||||
# - headphones work.
|
||||
# - "internal earpiece" works.
|
||||
# - "internal speaker" doesn't work (but that's probably because i broke the ribbon cable)
|
||||
# - "analog output" doesn't work.
|
||||
packageUnwrapped = pkgs.alsa-ucm-conf.overrideAttrs (upstream: {
|
||||
postPatch = (upstream.postPatch or "") + ''
|
||||
cp ${./ucm2/PinePhone}/* ucm2/Allwinner/A64/PinePhone/
|
||||
|
||||
# fix the self-contained ucm files i source from to have correct path within the alsa-ucm-conf source tree
|
||||
substituteInPlace ucm2/Allwinner/A64/PinePhone/PinePhone.conf \
|
||||
--replace 'HiFi.conf' '/Allwinner/A64/PinePhone/HiFi.conf'
|
||||
substituteInPlace ucm2/Allwinner/A64/PinePhone/PinePhone.conf \
|
||||
--replace 'VoiceCall.conf' '/Allwinner/A64/PinePhone/VoiceCall.conf'
|
||||
'' + lib.optionalString cfg.config.preferEarpiece ''
|
||||
# decrease the priority of the internal speaker so that sounds are routed
|
||||
# to the earpiece by default.
|
||||
# this is just personal preference.
|
||||
substituteInPlace ucm2/Allwinner/A64/PinePhone/* \
|
||||
--replace 'PlaybackPriority 300' 'PlaybackPriority 100'
|
||||
'';
|
||||
});
|
||||
|
||||
sandbox.enable = false; #< only provides #out/share/alsa
|
||||
|
||||
# alsa-lib package only looks in its $out/share/alsa to find runtime config data, by default.
|
||||
# but ALSA_CONFIG_UCM2 is an env var that can override that.
|
||||
# this is particularly needed by wireplumber;
|
||||
# also *maybe* pipewire and pipewire-pulse.
|
||||
# taken from <repo:nixos/mobile-nixos:modules/quirks/audio.nix>
|
||||
env.ALSA_CONFIG_UCM2 = "/run/current-system/sw/share/alsa/ucm2";
|
||||
|
||||
enableFor.system = lib.mkIf (builtins.any (en: en) (builtins.attrValues cfg.enableFor.user)) true;
|
||||
};
|
||||
|
||||
environment.pathsToLink = lib.mkIf cfg.enabled [
|
||||
"/share/alsa/ucm2"
|
||||
];
|
||||
}
|
@@ -31,6 +31,7 @@
|
||||
};
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistWayland = true;
|
||||
|
||||
persist.byStore.plaintext = [
|
||||
|
@@ -62,7 +62,6 @@ in
|
||||
# "iw"
|
||||
"jq"
|
||||
"killall"
|
||||
"less"
|
||||
# "libcap_ng" # for `netcap`
|
||||
"lsof"
|
||||
# "miniupnpc"
|
||||
@@ -166,7 +165,6 @@ in
|
||||
];
|
||||
|
||||
consoleMediaUtils = declPackageSet [
|
||||
"blast-ugjka" # cast audio to UPNP/DLNA devices (via pulseaudio sink)
|
||||
# "catt" # cast videos to chromecast
|
||||
"ffmpeg"
|
||||
"go2tv" # cast videos to UPNP/DLNA device (i.e. tv).
|
||||
@@ -205,17 +203,19 @@ in
|
||||
# INDIVIDUAL PACKAGE DEFINITIONS
|
||||
|
||||
alsaUtils.sandbox.method = "landlock";
|
||||
alsaUtils.sandbox.wrapperType = "wrappedDerivation";
|
||||
alsaUtils.sandbox.whitelistAudio = true; #< not strictly necessary?
|
||||
|
||||
backblaze-b2 = {};
|
||||
|
||||
blanket.sandbox.method = "bwrap";
|
||||
blanket.sandbox.wrapperType = "wrappedDerivation";
|
||||
blanket.sandbox.whitelistAudio = true;
|
||||
# blanket.sandbox.whitelistDbus = [ "user" ]; # TODO: untested
|
||||
blanket.sandbox.whitelistWayland = true;
|
||||
|
||||
blueberry.sandbox.method = "bwrap";
|
||||
blueberry.sandbox.wrapperType = "inplace"; #< various /lib scripts refer to the bins by full path
|
||||
blueberry.sandbox.wrapperType = "inplace"; # /etc/xdg/autostart hardcodes paths
|
||||
blueberry.sandbox.whitelistWayland = true;
|
||||
blueberry.sandbox.extraPaths = [
|
||||
"/dev/rfkill"
|
||||
@@ -225,9 +225,11 @@ in
|
||||
];
|
||||
|
||||
bridge-utils.sandbox.method = "bwrap"; #< bwrap, landlock: both work
|
||||
bridge-utils.sandbox.wrapperType = "wrappedDerivation";
|
||||
bridge-utils.sandbox.net = "all";
|
||||
|
||||
brightnessctl.sandbox.method = "landlock"; # also bwrap, but landlock is more responsive
|
||||
brightnessctl.sandbox.wrapperType = "wrappedDerivation";
|
||||
brightnessctl.sandbox.extraPaths = [
|
||||
"/sys/class/backlight"
|
||||
"/sys/class/leds"
|
||||
@@ -236,6 +238,7 @@ in
|
||||
brightnessctl.sandbox.whitelistDbus = [ "system" ];
|
||||
|
||||
btrfs-progs.sandbox.method = "bwrap"; #< bwrap, landlock: both work
|
||||
btrfs-progs.sandbox.wrapperType = "wrappedDerivation";
|
||||
btrfs-progs.sandbox.autodetectCliPaths = "existing"; # e.g. `btrfs filesystem df /my/fs`
|
||||
|
||||
"cacert.unbundled".sandbox.enable = false;
|
||||
@@ -246,6 +249,7 @@ in
|
||||
|
||||
# cryptsetup: typical use is `cryptsetup open /dev/loopxyz mappedName`, and creates `/dev/mapper/mappedName`
|
||||
cryptsetup.sandbox.method = "landlock";
|
||||
cryptsetup.sandbox.wrapperType = "wrappedDerivation";
|
||||
cryptsetup.sandbox.extraPaths = [
|
||||
"/dev/mapper"
|
||||
"/dev/random"
|
||||
@@ -259,10 +263,12 @@ in
|
||||
cryptsetup.sandbox.autodetectCliPaths = "existing";
|
||||
|
||||
ddrescue.sandbox.method = "landlock"; # TODO:sandbox: untested
|
||||
ddrescue.sandbox.wrapperType = "wrappedDerivation";
|
||||
ddrescue.sandbox.autodetectCliPaths = "existingOrParent";
|
||||
|
||||
# auth token, preferences
|
||||
delfin.sandbox.method = "bwrap";
|
||||
delfin.sandbox.wrapperType = "wrappedDerivation";
|
||||
delfin.sandbox.whitelistAudio = true;
|
||||
delfin.sandbox.whitelistDbus = [ "user" ]; # else `mpris` plugin crashes the player
|
||||
delfin.sandbox.whitelistDri = true;
|
||||
@@ -271,6 +277,7 @@ in
|
||||
delfin.persist.byStore.private = [ ".config/delfin" ];
|
||||
|
||||
dig.sandbox.method = "bwrap";
|
||||
dig.sandbox.wrapperType = "wrappedDerivation";
|
||||
dig.sandbox.net = "all";
|
||||
|
||||
# creds, but also 200 MB of node modules, etc
|
||||
@@ -286,15 +293,18 @@ in
|
||||
dtc.sandbox.autodetectCliPaths = true; # TODO:sandbox: untested
|
||||
|
||||
dtrx.sandbox.method = "bwrap";
|
||||
dtrx.sandbox.wrapperType = "wrappedDerivation";
|
||||
dtrx.sandbox.whitelistPwd = true;
|
||||
dtrx.sandbox.autodetectCliPaths = "existing"; #< for the archive
|
||||
|
||||
duplicity = {};
|
||||
|
||||
e2fsprogs.sandbox.method = "landlock";
|
||||
e2fsprogs.sandbox.wrapperType = "wrappedDerivation";
|
||||
e2fsprogs.sandbox.autodetectCliPaths = "existing";
|
||||
|
||||
efibootmgr.sandbox.method = "landlock";
|
||||
efibootmgr.sandbox.wrapperType = "wrappedDerivation";
|
||||
efibootmgr.sandbox.extraPaths = [
|
||||
"/sys/firmware/efi"
|
||||
];
|
||||
@@ -302,12 +312,14 @@ in
|
||||
eg25-control = {};
|
||||
|
||||
electrum.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
electrum.sandbox.wrapperType = "wrappedDerivation";
|
||||
electrum.sandbox.net = "all"; # TODO: probably want to make this run behind a VPN, always
|
||||
electrum.sandbox.whitelistWayland = true;
|
||||
electrum.persist.byStore.cryptClearOnBoot = [ ".electrum" ]; #< TODO: use XDG dirs!
|
||||
|
||||
endless-sky.persist.byStore.plaintext = [ ".local/share/endless-sky" ];
|
||||
endless-sky.sandbox.method = "bwrap";
|
||||
endless-sky.sandbox.wrapperType = "wrappedDerivation";
|
||||
endless-sky.sandbox.whitelistAudio = true;
|
||||
endless-sky.sandbox.whitelistDri = true;
|
||||
endless-sky.sandbox.whitelistWayland = true;
|
||||
@@ -318,12 +330,14 @@ in
|
||||
emote.persist.byStore.plaintext = [ ".local/share/Emote" ];
|
||||
|
||||
ethtool.sandbox.method = "landlock";
|
||||
ethtool.sandbox.wrapperType = "wrappedDerivation";
|
||||
ethtool.sandbox.capabilities = [ "net_admin" ];
|
||||
|
||||
# eza `ls` replacement
|
||||
# landlock is OK, only `whitelistPwd` doesn't make the intermediate symlinks traversable, so it breaks on e.g. ~/Videos/servo/Shows/foo
|
||||
# eza.sandbox.method = "landlock";
|
||||
eza.sandbox.method = "bwrap";
|
||||
eza.sandbox.wrapperType = "wrappedDerivation"; # slow to build
|
||||
eza.sandbox.autodetectCliPaths = true;
|
||||
eza.sandbox.whitelistPwd = true;
|
||||
eza.sandbox.extraHomePaths = [
|
||||
@@ -333,9 +347,11 @@ in
|
||||
];
|
||||
|
||||
fatresize.sandbox.method = "landlock";
|
||||
fatresize.sandbox.wrapperType = "wrappedDerivation";
|
||||
fatresize.sandbox.autodetectCliPaths = "parent"; # /dev/sda1 -> needs /dev/sda
|
||||
|
||||
fd.sandbox.method = "landlock";
|
||||
fd.sandbox.wrapperType = "wrappedDerivation"; # slow to build
|
||||
fd.sandbox.autodetectCliPaths = true;
|
||||
fd.sandbox.whitelistPwd = true;
|
||||
fd.sandbox.extraHomePaths = [
|
||||
@@ -345,12 +361,15 @@ in
|
||||
];
|
||||
|
||||
ffmpeg.sandbox.method = "bwrap";
|
||||
ffmpeg.sandbox.wrapperType = "wrappedDerivation"; # slow to build
|
||||
ffmpeg.sandbox.autodetectCliPaths = "existingFileOrParent"; # it outputs uncreated files -> parent dir needs mounting
|
||||
|
||||
file.sandbox.method = "bwrap";
|
||||
file.sandbox.wrapperType = "wrappedDerivation";
|
||||
file.sandbox.autodetectCliPaths = true;
|
||||
|
||||
findutils.sandbox.method = "bwrap";
|
||||
findutils.sandbox.wrapperType = "wrappedDerivation";
|
||||
findutils.sandbox.autodetectCliPaths = true;
|
||||
findutils.sandbox.whitelistPwd = true;
|
||||
findutils.sandbox.extraHomePaths = [
|
||||
@@ -362,12 +381,14 @@ in
|
||||
fluffychat-moby.persist.byStore.plaintext = [ ".local/share/chat.fluffy.fluffychat" ];
|
||||
|
||||
font-manager.sandbox.method = "bwrap";
|
||||
font-manager.packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.font-manager.override {
|
||||
font-manager.sandbox.wrapperType = "inplace"; # .desktop and dbus .service file refer to /libexec
|
||||
font-manager.packageUnwrapped = pkgs.font-manager.override {
|
||||
# build without the "Google Fonts" integration feature, to save closure / avoid webkitgtk_4_0
|
||||
withWebkit = false;
|
||||
});
|
||||
};
|
||||
|
||||
forkstat.sandbox.method = "landlock"; #< doesn't seem to support bwrap
|
||||
forkstat.sandbox.wrapperType = "wrappedDerivation";
|
||||
forkstat.sandbox.extraConfig = [
|
||||
"--sane-sandbox-keep-namespace" "pid"
|
||||
];
|
||||
@@ -375,7 +396,12 @@ in
|
||||
"/proc"
|
||||
];
|
||||
|
||||
# fuzzel: TODO: re-enable sandbox. i use fuzzel both as an entry system (snippets) AND an app-launcher.
|
||||
# as an app-launcher, it cannot be sandboxed without over-restricting the app it launches.
|
||||
# should probably make it not be an app-launcher
|
||||
fuzzel.sandbox.enable = false;
|
||||
fuzzel.sandbox.method = "bwrap"; #< landlock nearly works, but unable to open ~/.cache
|
||||
fuzzel.sandbox.wrapperType = "wrappedDerivation";
|
||||
fuzzel.sandbox.whitelistWayland = true;
|
||||
fuzzel.persist.byStore.private = [
|
||||
# this is a file of recent selections
|
||||
@@ -383,11 +409,12 @@ in
|
||||
];
|
||||
|
||||
gawk.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
gawk.sandbox.wrapperType = "inplace"; # /share/gawk libraries refer to /libexec
|
||||
gawk.sandbox.wrapperType = "inplace"; # share/gawk libraries refer to /libexec
|
||||
gawk.sandbox.autodetectCliPaths = true;
|
||||
|
||||
gdb.sandbox.enable = false; # gdb doesn't sandbox well. i don't know how you could.
|
||||
# gdb.sandbox.method = "landlock"; # permission denied when trying to attach, even as root
|
||||
gdb.sandbox.wrapperType = "wrappedDerivation";
|
||||
gdb.sandbox.autodetectCliPaths = true;
|
||||
|
||||
geoclue2-with-demo-agent = {};
|
||||
@@ -397,7 +424,7 @@ in
|
||||
gh.persist.byStore.private = [ ".config/gh" ];
|
||||
|
||||
gimp.sandbox.method = "bwrap";
|
||||
gimp.sandbox.net = "clearnet"; #< for Xwayland
|
||||
gimp.sandbox.wrapperType = "wrappedDerivation";
|
||||
gimp.sandbox.whitelistWayland = true;
|
||||
gimp.sandbox.extraHomePaths = [
|
||||
"Pictures/albums"
|
||||
@@ -416,32 +443,39 @@ in
|
||||
];
|
||||
|
||||
"gnome.gnome-calculator".sandbox.method = "bwrap";
|
||||
"gnome.gnome-calculator".sandbox.wrapperType = "inplace"; # /libexec/gnome-calculator-search-provider
|
||||
"gnome.gnome-calculator".sandbox.whitelistWayland = true;
|
||||
|
||||
# gnome-calendar surely has data to persist, but i use it strictly to do date math, not track events.
|
||||
"gnome.gnome-calendar".sandbox.method = "bwrap";
|
||||
"gnome.gnome-calendar".sandbox.wrapperType = "wrappedDerivation";
|
||||
"gnome.gnome-calendar".sandbox.whitelistWayland = true;
|
||||
|
||||
"gnome.gnome-clocks".sandbox.method = "bwrap";
|
||||
"gnome.gnome-clocks".sandbox.wrapperType = "wrappedDerivation";
|
||||
"gnome.gnome-clocks".sandbox.whitelistWayland = true;
|
||||
"gnome.gnome-clocks".suggestedPrograms = [ "dconf" ];
|
||||
|
||||
# gnome-disks
|
||||
"gnome.gnome-disk-utility".sandbox.method = "bwrap";
|
||||
"gnome.gnome-disk-utility".sandbox.wrapperType = "inplace"; # /etc/xdg/autostart
|
||||
"gnome.gnome-disk-utility".sandbox.whitelistDbus = [ "system" ];
|
||||
"gnome.gnome-disk-utility".sandbox.whitelistWayland = true;
|
||||
|
||||
# seahorse: dump gnome-keyring secrets.
|
||||
# N.B.: it can also manage ~/.ssh keys, but i explicitly don't add those to the sandbox for now.
|
||||
"gnome.seahorse".sandbox.method = "bwrap";
|
||||
"gnome.seahorse".sandbox.wrapperType = "wrappedDerivation";
|
||||
"gnome.seahorse".sandbox.whitelistDbus = [ "user" ];
|
||||
"gnome.seahorse".sandbox.whitelistWayland = true;
|
||||
|
||||
gnome-2048.sandbox.method = "bwrap";
|
||||
gnome-2048.sandbox.wrapperType = "wrappedDerivation";
|
||||
gnome-2048.sandbox.whitelistWayland = true;
|
||||
gnome-2048.persist.byStore.plaintext = [ ".local/share/gnome-2048/scores" ];
|
||||
|
||||
gnome-frog.sandbox.method = "bwrap";
|
||||
gnome-frog.sandbox.wrapperType = "wrappedDerivation";
|
||||
gnome-frog.sandbox.whitelistWayland = true;
|
||||
gnome-frog.sandbox.whitelistDbus = [ "user" ];
|
||||
gnome-frog.sandbox.extraPaths = [
|
||||
@@ -468,9 +502,11 @@ in
|
||||
# 2. no two shaded tiles can be direct N/S/E/W neighbors
|
||||
# - win once (1) and (2) are satisfied
|
||||
"gnome.hitori".sandbox.method = "bwrap";
|
||||
"gnome.hitori".sandbox.wrapperType = "wrappedDerivation";
|
||||
"gnome.hitori".sandbox.whitelistWayland = true;
|
||||
|
||||
gnugrep.sandbox.method = "bwrap";
|
||||
gnugrep.sandbox.wrapperType = "wrappedDerivation";
|
||||
gnugrep.sandbox.autodetectCliPaths = true;
|
||||
gnugrep.sandbox.whitelistPwd = true;
|
||||
gnugrep.sandbox.extraHomePaths = [
|
||||
@@ -479,24 +515,20 @@ in
|
||||
".persist/plaintext"
|
||||
];
|
||||
|
||||
# sed: there is an edgecase of `--file=<foo>`, wherein `foo` won't be whitelisted.
|
||||
gnused.sandbox.method = "bwrap";
|
||||
gnused.sandbox.autodetectCliPaths = "existingFile";
|
||||
gnused.sandbox.whitelistPwd = true; #< `-i` flag creates a temporary file in pwd (?) and then moves it.
|
||||
|
||||
gnused = {};
|
||||
gpsd = {};
|
||||
|
||||
gptfdisk.sandbox.method = "landlock";
|
||||
gptfdisk.sandbox.wrapperType = "wrappedDerivation";
|
||||
gptfdisk.sandbox.extraPaths = [
|
||||
"/dev"
|
||||
];
|
||||
gptfdisk.sandbox.autodetectCliPaths = "existing"; #< sometimes you'll use gdisk on a device file.
|
||||
|
||||
grim.sandbox.method = "bwrap";
|
||||
grim.sandbox.autodetectCliPaths = "existingOrParent";
|
||||
grim.sandbox.whitelistWayland = true;
|
||||
grim = {};
|
||||
|
||||
hase.sandbox.method = "bwrap";
|
||||
hase.sandbox.wrapperType = "wrappedDerivation";
|
||||
hase.sandbox.net = "clearnet";
|
||||
hase.sandbox.whitelistAudio = true;
|
||||
hase.sandbox.whitelistDri = true;
|
||||
@@ -504,12 +536,15 @@ in
|
||||
|
||||
# hdparm: has to be run as sudo. e.g. `sudo hdparm -i /dev/sda`
|
||||
hdparm.sandbox.method = "bwrap";
|
||||
hdparm.sandbox.wrapperType = "wrappedDerivation";
|
||||
hdparm.sandbox.autodetectCliPaths = true;
|
||||
|
||||
host.sandbox.method = "landlock";
|
||||
host.sandbox.wrapperType = "wrappedDerivation";
|
||||
host.sandbox.net = "all"; #< technically, only needs to contact localhost's DNS server
|
||||
|
||||
htop.sandbox.method = "landlock";
|
||||
htop.sandbox.wrapperType = "wrappedDerivation";
|
||||
htop.sandbox.extraPaths = [
|
||||
"/proc"
|
||||
"/sys/devices"
|
||||
@@ -520,13 +555,16 @@ in
|
||||
];
|
||||
|
||||
iftop.sandbox.method = "landlock";
|
||||
iftop.sandbox.wrapperType = "wrappedDerivation";
|
||||
iftop.sandbox.capabilities = [ "net_raw" ];
|
||||
|
||||
# inetutils: ping, ifconfig, hostname, traceroute, whois, ....
|
||||
# N.B.: inetutils' `ping` is shadowed by iputils' ping (by nixos, intentionally).
|
||||
inetutils.sandbox.method = "landlock"; # want to keep the same netns, at least.
|
||||
inetutils.sandbox.wrapperType = "wrappedDerivation";
|
||||
|
||||
inkscape.sandbox.method = "bwrap";
|
||||
inkscape.sandbox.wrapperType = "wrappedDerivation";
|
||||
inkscape.sandbox.whitelistWayland = true;
|
||||
inkscape.sandbox.extraHomePaths = [
|
||||
"Pictures/albums"
|
||||
@@ -542,6 +580,7 @@ in
|
||||
inkscape.sandbox.autodetectCliPaths = true;
|
||||
|
||||
iotop.sandbox.method = "landlock";
|
||||
iotop.sandbox.wrapperType = "wrappedDerivation";
|
||||
iotop.sandbox.extraPaths = [
|
||||
"/proc"
|
||||
];
|
||||
@@ -549,31 +588,38 @@ in
|
||||
|
||||
# provides `ip`, `routel`, others
|
||||
iproute2.sandbox.method = "landlock";
|
||||
iproute2.sandbox.wrapperType = "wrappedDerivation";
|
||||
iproute2.sandbox.net = "all";
|
||||
iproute2.sandbox.capabilities = [ "net_admin" ];
|
||||
|
||||
iptables.sandbox.method = "landlock";
|
||||
iptables.sandbox.wrapperType = "wrappedDerivation";
|
||||
iptables.sandbox.net = "all";
|
||||
iptables.sandbox.capabilities = [ "net_admin" ];
|
||||
|
||||
# iputils provides `ping` (and arping, clockdiff, tracepath)
|
||||
iputils.sandbox.method = "landlock";
|
||||
iputils.sandbox.wrapperType = "wrappedDerivation";
|
||||
iputils.sandbox.net = "all";
|
||||
iputils.sandbox.capabilities = [ "net_raw" ];
|
||||
|
||||
iw.sandbox.method = "landlock";
|
||||
iw.sandbox.wrapperType = "wrappedDerivation";
|
||||
iw.sandbox.net = "all";
|
||||
iw.sandbox.capabilities = [ "net_admin" ];
|
||||
|
||||
jq.sandbox.method = "bwrap";
|
||||
jq.sandbox.wrapperType = "wrappedDerivation";
|
||||
jq.sandbox.autodetectCliPaths = "existingFile";
|
||||
|
||||
killall.sandbox.method = "landlock";
|
||||
killall.sandbox.wrapperType = "wrappedDerivation";
|
||||
killall.sandbox.extraPaths = [
|
||||
"/proc"
|
||||
];
|
||||
|
||||
krita.sandbox.method = "bwrap";
|
||||
krita.sandbox.wrapperType = "wrappedDerivation";
|
||||
krita.sandbox.whitelistWayland = true;
|
||||
krita.sandbox.autodetectCliPaths = "existing";
|
||||
krita.sandbox.extraHomePaths = [
|
||||
@@ -591,9 +637,11 @@ in
|
||||
libcap_ng.sandbox.enable = false; # there's something about /proc/$pid/fd which breaks `readlink`/stat with every sandbox technique (except capsh-only)
|
||||
|
||||
libnotify.sandbox.method = "bwrap";
|
||||
libnotify.sandbox.wrapperType = "wrappedDerivation";
|
||||
libnotify.sandbox.whitelistDbus = [ "user" ]; # notify-send
|
||||
|
||||
losslesscut-bin.sandbox.method = "bwrap";
|
||||
losslesscut-bin.sandbox.wrapperType = "wrappedDerivation";
|
||||
losslesscut-bin.sandbox.extraHomePaths = [
|
||||
"Music"
|
||||
"Pictures/from" # videos from e.g. mobile phone
|
||||
@@ -608,11 +656,12 @@ in
|
||||
losslesscut-bin.sandbox.whitelistX = true;
|
||||
|
||||
lsof.sandbox.method = "capshonly"; # lsof doesn't sandbox under bwrap or even landlock w/ full access to /
|
||||
lsof.sandbox.wrapperType = "wrappedDerivation";
|
||||
|
||||
lua = {};
|
||||
|
||||
"mate.engrampa".packageUnwrapped = pkgs.rmDbusServices pkgs.mate.engrampa;
|
||||
"mate.engrampa".sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
"mate.engrampa".sandbox.wrapperType = "inplace";
|
||||
"mate.engrampa".sandbox.whitelistWayland = true;
|
||||
"mate.engrampa".sandbox.autodetectCliPaths = "existingOrParent";
|
||||
"mate.engrampa".sandbox.extraHomePaths = [
|
||||
@@ -625,6 +674,7 @@ in
|
||||
];
|
||||
|
||||
mercurial.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
mercurial.sandbox.wrapperType = "wrappedDerivation";
|
||||
mercurial.sandbox.net = "clearnet";
|
||||
mercurial.sandbox.whitelistPwd = true;
|
||||
|
||||
@@ -632,6 +682,7 @@ in
|
||||
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
|
||||
monero-gui.persist.byStore.plaintext = [ ".bitmonero" ];
|
||||
monero-gui.sandbox.method = "bwrap";
|
||||
monero-gui.sandbox.wrapperType = "wrappedDerivation";
|
||||
monero-gui.sandbox.net = "all";
|
||||
monero-gui.sandbox.extraHomePaths = [
|
||||
"records/finance/cryptocurrencies/monero"
|
||||
@@ -640,16 +691,20 @@ in
|
||||
mumble.persist.byStore.private = [ ".local/share/Mumble" ];
|
||||
|
||||
nano.sandbox.method = "bwrap";
|
||||
nano.sandbox.wrapperType = "wrappedDerivation";
|
||||
nano.sandbox.autodetectCliPaths = "existingFileOrParent";
|
||||
|
||||
netcat.sandbox.method = "landlock";
|
||||
netcat.sandbox.wrapperType = "wrappedDerivation";
|
||||
netcat.sandbox.net = "all";
|
||||
|
||||
nethogs.sandbox.method = "capshonly"; # *partially* works under landlock w/ full access to /
|
||||
nethogs.sandbox.wrapperType = "wrappedDerivation";
|
||||
nethogs.sandbox.capabilities = [ "net_admin" "net_raw" ];
|
||||
|
||||
# provides `arp`, `hostname`, `route`, `ifconfig`
|
||||
nettools.sandbox.method = "landlock";
|
||||
nettools.sandbox.wrapperType = "wrappedDerivation";
|
||||
nettools.sandbox.net = "all";
|
||||
nettools.sandbox.capabilities = [ "net_admin" "net_raw" ];
|
||||
nettools.sandbox.extraPaths = [
|
||||
@@ -657,6 +712,7 @@ in
|
||||
];
|
||||
|
||||
networkmanagerapplet.sandbox.method = "bwrap";
|
||||
networkmanagerapplet.sandbox.wrapperType = "wrappedDerivation";
|
||||
networkmanagerapplet.sandbox.whitelistWayland = true;
|
||||
networkmanagerapplet.sandbox.whitelistDbus = [ "system" ];
|
||||
|
||||
@@ -669,9 +725,11 @@ in
|
||||
];
|
||||
|
||||
nmap.sandbox.method = "bwrap";
|
||||
nmap.sandbox.wrapperType = "wrappedDerivation";
|
||||
nmap.sandbox.net = "all"; # clearnet and lan
|
||||
|
||||
nmon.sandbox.method = "landlock";
|
||||
nmon.sandbox.wrapperType = "wrappedDerivation";
|
||||
nmon.sandbox.extraPaths = [
|
||||
"/proc"
|
||||
];
|
||||
@@ -680,6 +738,7 @@ in
|
||||
|
||||
# `nvme list` only shows results when run as root.
|
||||
nvme-cli.sandbox.method = "landlock";
|
||||
nvme-cli.sandbox.wrapperType = "wrappedDerivation";
|
||||
nvme-cli.sandbox.extraPaths = [
|
||||
"/sys/devices"
|
||||
"/sys/class/nvme"
|
||||
@@ -691,11 +750,13 @@ in
|
||||
|
||||
# contains only `oathtool`, which i only use for evaluating TOTP codes from CLI/stdin
|
||||
oath-toolkit.sandbox.method = "bwrap";
|
||||
oath-toolkit.sandbox.wrapperType = "wrappedDerivation";
|
||||
|
||||
# settings (electron app)
|
||||
obsidian.persist.byStore.plaintext = [ ".config/obsidian" ];
|
||||
|
||||
parted.sandbox.method = "landlock";
|
||||
parted.sandbox.wrapperType = "wrappedDerivation";
|
||||
parted.sandbox.extraPaths = [
|
||||
"/dev"
|
||||
];
|
||||
@@ -704,10 +765,12 @@ in
|
||||
patchelf = {};
|
||||
|
||||
pavucontrol.sandbox.method = "bwrap";
|
||||
pavucontrol.sandbox.wrapperType = "wrappedDerivation";
|
||||
pavucontrol.sandbox.whitelistAudio = true;
|
||||
pavucontrol.sandbox.whitelistWayland = true;
|
||||
|
||||
pciutils.sandbox.method = "landlock";
|
||||
pciutils.sandbox.wrapperType = "wrappedDerivation";
|
||||
pciutils.sandbox.extraPaths = [
|
||||
"/sys/bus/pci"
|
||||
"/sys/devices"
|
||||
@@ -716,6 +779,7 @@ in
|
||||
"perlPackages.FileMimeInfo".sandbox.enable = false; #< TODO: sandbox `mimetype` but not `mimeopen`.
|
||||
|
||||
powertop.sandbox.method = "landlock";
|
||||
powertop.sandbox.wrapperType = "wrappedDerivation";
|
||||
powertop.sandbox.capabilities = [ "ipc_lock" "sys_admin" ];
|
||||
powertop.sandbox.extraPaths = [
|
||||
"/proc"
|
||||
@@ -724,23 +788,18 @@ in
|
||||
"/sys/kernel"
|
||||
];
|
||||
|
||||
# procps: free, pgrep, pidof, pkill, ps, pwait, top, uptime, couple others
|
||||
procps.sandbox.method = "bwrap";
|
||||
procps.sandbox.extraConfig = [
|
||||
"--sane-sandbox-keep-namespace" "pid"
|
||||
];
|
||||
|
||||
pstree.sandbox.method = "landlock";
|
||||
pstree.sandbox.wrapperType = "wrappedDerivation";
|
||||
pstree.sandbox.extraPaths = [
|
||||
"/proc"
|
||||
];
|
||||
|
||||
pulseaudio = {};
|
||||
|
||||
pulsemixer.sandbox.method = "landlock";
|
||||
pulsemixer.sandbox.wrapperType = "wrappedDerivation";
|
||||
pulsemixer.sandbox.whitelistAudio = true;
|
||||
|
||||
pwvucontrol.sandbox.method = "bwrap";
|
||||
pwvucontrol.sandbox.wrapperType = "wrappedDerivation";
|
||||
pwvucontrol.sandbox.whitelistAudio = true;
|
||||
pwvucontrol.sandbox.whitelistWayland = true;
|
||||
|
||||
@@ -748,6 +807,7 @@ in
|
||||
requests
|
||||
]);
|
||||
python3-repl.sandbox.method = "bwrap";
|
||||
python3-repl.sandbox.wrapperType = "wrappedDerivation";
|
||||
python3-repl.sandbox.net = "clearnet";
|
||||
python3-repl.sandbox.extraHomePaths = [
|
||||
"/"
|
||||
@@ -758,24 +818,22 @@ in
|
||||
qemu.slowToBuild = true;
|
||||
|
||||
rsync.sandbox.method = "bwrap";
|
||||
rsync.sandbox.wrapperType = "wrappedDerivation";
|
||||
rsync.sandbox.net = "clearnet";
|
||||
rsync.sandbox.autodetectCliPaths = "existingOrParent";
|
||||
|
||||
rustc = {};
|
||||
|
||||
sane-open-desktop.sandbox.enable = false; #< trivial script, and all our deps are sandboxed
|
||||
sane-open-desktop.suggestedPrograms = [
|
||||
"gdbus"
|
||||
];
|
||||
|
||||
screen.sandbox.enable = false; #< tty; needs to run anything
|
||||
|
||||
sequoia.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
sequoia.sandbox.wrapperType = "wrappedDerivation"; # slow to build
|
||||
sequoia.sandbox.whitelistPwd = true;
|
||||
sequoia.sandbox.autodetectCliPaths = true;
|
||||
|
||||
shattered-pixel-dungeon.persist.byStore.plaintext = [ ".local/share/.shatteredpixel/shattered-pixel-dungeon" ];
|
||||
shattered-pixel-dungeon.sandbox.method = "bwrap";
|
||||
shattered-pixel-dungeon.sandbox.wrapperType = "wrappedDerivation";
|
||||
shattered-pixel-dungeon.sandbox.whitelistAudio = true;
|
||||
shattered-pixel-dungeon.sandbox.whitelistDri = true;
|
||||
shattered-pixel-dungeon.sandbox.whitelistWayland = true;
|
||||
@@ -783,8 +841,7 @@ in
|
||||
# printer/filament settings
|
||||
slic3r.persist.byStore.plaintext = [ ".Slic3r" ];
|
||||
|
||||
slurp.sandbox.method = "bwrap";
|
||||
slurp.sandbox.whitelistWayland = true;
|
||||
slurp = {};
|
||||
|
||||
# use like `sudo smartctl /dev/sda -a`
|
||||
smartmontools.sandbox.method = "landlock";
|
||||
@@ -793,6 +850,7 @@ in
|
||||
smartmontools.sandbox.capabilities = [ "sys_rawio" ];
|
||||
|
||||
sops.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
sops.sandbox.wrapperType = "wrappedDerivation";
|
||||
sops.sandbox.extraHomePaths = [
|
||||
".config/sops"
|
||||
"dev/nixos"
|
||||
@@ -802,6 +860,7 @@ in
|
||||
];
|
||||
|
||||
soundconverter.sandbox.method = "bwrap";
|
||||
soundconverter.sandbox.wrapperType = "wrappedDerivation";
|
||||
soundconverter.sandbox.whitelistWayland = true;
|
||||
soundconverter.sandbox.extraHomePaths = [
|
||||
"Music"
|
||||
@@ -815,16 +874,19 @@ in
|
||||
soundconverter.sandbox.autodetectCliPaths = "existingOrParent";
|
||||
|
||||
sox.sandbox.method = "bwrap";
|
||||
sox.sandbox.wrapperType = "wrappedDerivation";
|
||||
sox.sandbox.autodetectCliPaths = "existingFileOrParent";
|
||||
sox.sandbox.whitelistAudio = true;
|
||||
|
||||
space-cadet-pinball.persist.byStore.plaintext = [ ".local/share/SpaceCadetPinball" ];
|
||||
space-cadet-pinball.sandbox.method = "bwrap";
|
||||
space-cadet-pinball.sandbox.wrapperType = "wrappedDerivation";
|
||||
space-cadet-pinball.sandbox.whitelistAudio = true;
|
||||
space-cadet-pinball.sandbox.whitelistDri = true;
|
||||
space-cadet-pinball.sandbox.whitelistWayland = true;
|
||||
|
||||
speedtest-cli.sandbox.method = "bwrap";
|
||||
speedtest-cli.sandbox.wrapperType = "wrappedDerivation";
|
||||
speedtest-cli.sandbox.net = "all";
|
||||
|
||||
sqlite = {};
|
||||
@@ -832,6 +894,7 @@ in
|
||||
strace.sandbox.enable = false; #< needs to `exec` its args, and therefore support *anything*
|
||||
|
||||
subversion.sandbox.method = "bwrap";
|
||||
subversion.sandbox.wrapperType = "wrappedDerivation";
|
||||
subversion.sandbox.net = "clearnet";
|
||||
subversion.sandbox.whitelistPwd = true;
|
||||
sudo.sandbox.enable = false;
|
||||
@@ -843,11 +906,8 @@ in
|
||||
superTux.sandbox.whitelistWayland = true;
|
||||
superTux.persist.byStore.plaintext = [ ".local/share/supertux2" ];
|
||||
|
||||
swappy.sandbox.method = "bwrap";
|
||||
swappy.sandbox.autodetectCliPaths = "existingFileOrParent";
|
||||
swappy.sandbox.whitelistWayland = true;
|
||||
|
||||
tcpdump.sandbox.method = "landlock";
|
||||
tcpdump.sandbox.wrapperType = "wrappedDerivation";
|
||||
tcpdump.sandbox.net = "all";
|
||||
tcpdump.sandbox.autodetectCliPaths = "existingFileOrParent";
|
||||
tcpdump.sandbox.capabilities = [ "net_admin" "net_raw" ];
|
||||
@@ -857,10 +917,12 @@ in
|
||||
tokodon.persist.byStore.private = [ ".cache/KDE/tokodon" ];
|
||||
|
||||
tree.sandbox.method = "landlock";
|
||||
tree.sandbox.wrapperType = "wrappedDerivation";
|
||||
tree.sandbox.autodetectCliPaths = true;
|
||||
tree.sandbox.whitelistPwd = true;
|
||||
|
||||
tumiki-fighters.sandbox.method = "bwrap";
|
||||
tumiki-fighters.sandbox.wrapperType = "wrappedDerivation";
|
||||
tumiki-fighters.sandbox.whitelistAudio = true;
|
||||
tumiki-fighters.sandbox.whitelistDri = true; #< not strictly necessary, but triples CPU perf
|
||||
tumiki-fighters.sandbox.whitelistWayland = true;
|
||||
@@ -869,28 +931,34 @@ in
|
||||
util-linux.sandbox.enable = false; #< TODO: possible to sandbox if i specific a different profile for each of its ~50 binaries
|
||||
|
||||
unzip.sandbox.method = "bwrap";
|
||||
unzip.sandbox.wrapperType = "wrappedDerivation";
|
||||
unzip.sandbox.autodetectCliPaths = "existingOrParent";
|
||||
unzip.sandbox.whitelistPwd = true;
|
||||
|
||||
usbutils.sandbox.method = "bwrap"; # breaks `usbhid-dump`, but `lsusb`, `usb-devices` work
|
||||
usbutils.sandbox.wrapperType = "wrappedDerivation";
|
||||
usbutils.sandbox.extraPaths = [
|
||||
"/sys/devices"
|
||||
"/sys/bus/usb"
|
||||
];
|
||||
|
||||
visidata.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
visidata.sandbox.wrapperType = "wrappedDerivation";
|
||||
visidata.sandbox.autodetectCliPaths = true;
|
||||
|
||||
# `vulkaninfo`, `vkcube`
|
||||
vulkan-tools.sandbox.method = "landlock";
|
||||
vulkan-tools.sandbox.wrapperType = "wrappedDerivation";
|
||||
|
||||
vvvvvv.sandbox.method = "bwrap";
|
||||
vvvvvv.sandbox.wrapperType = "wrappedDerivation";
|
||||
vvvvvv.sandbox.whitelistAudio = true;
|
||||
vvvvvv.sandbox.whitelistDri = true; #< playable without, but burns noticably more CPU
|
||||
vvvvvv.sandbox.whitelistWayland = true;
|
||||
vvvvvv.persist.byStore.plaintext = [ ".local/share/VVVVVV" ];
|
||||
|
||||
w3m.sandbox.method = "bwrap";
|
||||
w3m.sandbox.wrapperType = "wrappedDerivation";
|
||||
w3m.sandbox.net = "all";
|
||||
w3m.sandbox.extraHomePaths = [
|
||||
# little-used feature, but you can save web pages :)
|
||||
@@ -898,9 +966,11 @@ in
|
||||
];
|
||||
|
||||
wdisplays.sandbox.method = "bwrap";
|
||||
wdisplays.sandbox.wrapperType = "wrappedDerivation";
|
||||
wdisplays.sandbox.whitelistWayland = true;
|
||||
|
||||
wget.sandbox.method = "bwrap";
|
||||
wget.sandbox.wrapperType = "wrappedDerivation";
|
||||
wget.sandbox.net = "all";
|
||||
wget.sandbox.whitelistPwd = true; # saves to pwd by default
|
||||
|
||||
@@ -908,13 +978,16 @@ in
|
||||
|
||||
# `wg`, `wg-quick`
|
||||
wireguard-tools.sandbox.method = "landlock";
|
||||
wireguard-tools.sandbox.wrapperType = "wrappedDerivation";
|
||||
wireguard-tools.sandbox.capabilities = [ "net_admin" ];
|
||||
|
||||
# provides `iwconfig`, `iwlist`, `iwpriv`, ...
|
||||
wirelesstools.sandbox.method = "landlock";
|
||||
wirelesstools.sandbox.wrapperType = "wrappedDerivation";
|
||||
wirelesstools.sandbox.capabilities = [ "net_admin" ];
|
||||
|
||||
wl-clipboard.sandbox.method = "bwrap";
|
||||
wl-clipboard.sandbox.wrapperType = "wrappedDerivation";
|
||||
wl-clipboard.sandbox.whitelistWayland = true;
|
||||
|
||||
wtype = {};
|
||||
@@ -925,11 +998,13 @@ in
|
||||
xwayland.sandbox.net = "clearnet"; #< just assuming this is needed (X11 traffic)
|
||||
xwayland.sandbox.whitelistDri = true; #< would assume this gives better gfx perf
|
||||
|
||||
xdg-terminal-exec.sandbox.enable = false; # xdg-terminal-exec is a launcher for $TERM
|
||||
xterm.sandbox.enable = false; # need to be able to do everything
|
||||
|
||||
yarn.persist.byStore.plaintext = [ ".cache/yarn" ];
|
||||
|
||||
yt-dlp.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
yt-dlp.sandbox.wrapperType = "wrappedDerivation";
|
||||
yt-dlp.sandbox.net = "all";
|
||||
yt-dlp.sandbox.whitelistPwd = true; # saves to pwd by default
|
||||
|
||||
|
@@ -10,6 +10,7 @@
|
||||
};
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.autodetectCliPaths = true;
|
||||
|
@@ -88,6 +88,7 @@ in
|
||||
{
|
||||
sane.programs.bemenu = {
|
||||
sandbox.method = "bwrap"; # landlock works, but requires *all* of /run/user/$ID to be granted.
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.extraHomePaths = [
|
||||
".cache/fontconfig" #< else it complains, and is *way* slower
|
||||
|
@@ -1,216 +0,0 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p blast-ugjka
|
||||
# vim: set filetype=python :
|
||||
|
||||
import ctypes
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import socket
|
||||
import subprocess
|
||||
|
||||
from enum import Enum
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# map from known devices -> required flags
|
||||
DEVICE_MAP = {
|
||||
"Theater TV": [],
|
||||
"[LG] webOS TV OLED55C9PUA": [ "-usewav" ],
|
||||
}
|
||||
|
||||
def set_pdeathsig(sig=signal.SIGTERM):
|
||||
"""
|
||||
helper function to ensure once parent process exits, its children processes will automatically die.
|
||||
see: <https://stackoverflow.com/a/43152455>
|
||||
see: <https://www.man7.org/linux/man-pages/man2/prctl.2.html>
|
||||
"""
|
||||
libc = ctypes.CDLL("libc.so.6")
|
||||
return libc.prctl(1, sig)
|
||||
|
||||
MY_PID = None
|
||||
|
||||
def reap_children(sig=None, frame=None):
|
||||
global MY_PID
|
||||
# reset SIGTERM handler to avoid recursing
|
||||
signal.signal(signal.SIGTERM, signal.Handlers.SIG_DFL)
|
||||
logger.info("killing all children (of pid %d)", MY_PID)
|
||||
os.killpg(MY_PID, signal.SIGTERM)
|
||||
|
||||
def reap_on_exit():
|
||||
"""
|
||||
catch when the parent exits, and map that to SIGTERM for this process.
|
||||
when this process receives SIGTERM, also terminate all descendent processes.
|
||||
|
||||
this is done because:
|
||||
1. mpv invokes this, but (potentially) via the sandbox wrapper.
|
||||
2. when mpv exits, it `SIGKILL`s that sandbox wrapper.
|
||||
3. bwrap does not pass SIGKILL or SIGTERM to its child.
|
||||
4. hence, we neither receive that signal NOR can we pass it on simply by killing our immediate children
|
||||
(since any bwrap'd children wouldn't pass that signal on...)
|
||||
really, the proper fix would be on mpv's side:
|
||||
- mpv should create a new process group when it launches a command, and kill that process group on exit.
|
||||
or fix this in the sandbox wrapper:
|
||||
- why *doesn't* bwrap forward the signals?
|
||||
- there's --die-with-parent, but i can't apply that *system wide* and expect reasonably behavior
|
||||
<https://github.com/containers/bubblewrap/issues/529>
|
||||
"""
|
||||
global MY_PID
|
||||
MY_PID = os.getpid()
|
||||
# create a new process group, pgid = gid
|
||||
os.setpgid(MY_PID, MY_PID)
|
||||
|
||||
set_pdeathsig(signal.SIGTERM)
|
||||
signal.signal(signal.SIGTERM, reap_children)
|
||||
|
||||
def get_ranked_ip_addrs():
|
||||
"""
|
||||
return the IP addresses most likely to be LAN addresses
|
||||
based on: <https://stackoverflow.com/a/1267524>
|
||||
"""
|
||||
_name, _aliases, static_addrs = socket.gethostbyname_ex(socket.gethostname())
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(("1", 53))
|
||||
con_addr, _port = s.getsockname()
|
||||
return sorted(set(static_addrs + [ con_addr ]), key=lambda a: (a.startswith("127"), a))
|
||||
|
||||
|
||||
class ParserState(Enum):
|
||||
Break = "break"
|
||||
Receiver = "receiver"
|
||||
Ips = "ip"
|
||||
|
||||
class Status(Enum):
|
||||
Continue = "continue"
|
||||
Error = "error"
|
||||
RedoWithFlags = "redo_with_flags"
|
||||
Launched = "launched"
|
||||
|
||||
class BlastDriver:
|
||||
parsing: ParserState | None = None
|
||||
last_write: str | None = None
|
||||
def __init__(self, blast_flags: list[str] = []):
|
||||
self.ranked_ips = get_ranked_ip_addrs()
|
||||
self.blast = subprocess.Popen(
|
||||
["blast", "-source", "blast.monitor"] + blast_flags,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
# this pdeathsig isn't necessary; seems it might result in leaked pulse outputs
|
||||
# preexec_fn=set_pdeathsig
|
||||
)
|
||||
self.blast_flags = list(blast_flags)
|
||||
self.receiver_names = []
|
||||
self.ips = []
|
||||
|
||||
def writeline(self, line: str) -> None:
|
||||
logger.debug("[send] %s", line)
|
||||
self.blast.stdin.write(f"{line}\n".encode())
|
||||
self.blast.stdin.flush()
|
||||
self.last_write = line
|
||||
|
||||
def readline(self) -> str:
|
||||
line = self.blast.stdout.readline().decode('utf-8').strip()
|
||||
line = line.replace('\x1b[1A\x1b[K', '') #< escape codes
|
||||
logger.debug("[recv] %r", line)
|
||||
return line
|
||||
|
||||
def set_state(self, state: ParserState):
|
||||
logger.debug("[pars] %s", state)
|
||||
self.parsing = state
|
||||
|
||||
def feedline(self, line: str) -> (Status, str|None):
|
||||
"""
|
||||
apply a line from blast's stdout to modify parser state.
|
||||
returns a status code (e.g. Status.Continue), and optionally a reply to send back to blast.
|
||||
"""
|
||||
if line == "Loading...":
|
||||
return Status.Continue, None
|
||||
elif line == "----------":
|
||||
self.set_state(ParserState.Break)
|
||||
return Status.Continue, None
|
||||
elif line == "DLNA receivers":
|
||||
self.set_state(ParserState.Receiver)
|
||||
return Status.Continue, None
|
||||
elif line == "Your LAN ip addresses":
|
||||
self.set_state(ParserState.Ips)
|
||||
return Status.Continue, None
|
||||
elif line == "Select the DLNA device:":
|
||||
assert len(self.receiver_names) == 1, self.receiver_names
|
||||
name = self.receiver_names[0]
|
||||
if name in DEVICE_MAP and DEVICE_MAP[name] != self.blast_flags:
|
||||
return Status.RedoWithFlags, None
|
||||
return Status.Continue, "0"
|
||||
elif line == "Select the lan IP address for the stream:":
|
||||
for r in self.ranked_ips:
|
||||
if r in self.ips:
|
||||
return Status.Launched, str(self.ips.index(r))
|
||||
# fallback: just guess the best IP
|
||||
return Status.Launched, "0"
|
||||
elif self.parsing == ParserState.Receiver:
|
||||
id_, name = line.split(": ")
|
||||
assert id_ == str(len(self.receiver_names)), (id_, self.receiver_names)
|
||||
self.receiver_names.append(name)
|
||||
return Status.Continue, None
|
||||
elif self.parsing == ParserState.Ips:
|
||||
id_, ip = line.split(": ")
|
||||
assert id_ == str(len(self.ips)), (id_, self.ips)
|
||||
self.ips.append(ip)
|
||||
return Status.Continue, None
|
||||
elif line == f"[{self.last_write}]":
|
||||
# it's echoing to us what we wrote
|
||||
return Status.Continue, None
|
||||
# elif line == "":
|
||||
# return Status.Continue, None
|
||||
else:
|
||||
logger.info("unrecognized output (state=%s): %r", self.parsing, line)
|
||||
return Status.Error, None
|
||||
|
||||
def step(self) -> Status:
|
||||
"""
|
||||
advance the interaction between us and blast.
|
||||
reads a line from blast, modifies internal state, maybe sends a reply.
|
||||
could block indefinitely.
|
||||
"""
|
||||
line = self.readline()
|
||||
status, reply = self.feedline(line)
|
||||
if reply is not None:
|
||||
self.writeline(reply)
|
||||
return status
|
||||
|
||||
def try_blast(*args, **kwargs) -> BlastDriver | None:
|
||||
blast = BlastDriver(*args, **kwargs)
|
||||
status = Status.Continue
|
||||
while status == Status.Continue:
|
||||
status = blast.step()
|
||||
|
||||
if status == Status.RedoWithFlags:
|
||||
dev = blast.receiver_names[0]
|
||||
blast_flags = DEVICE_MAP[dev]
|
||||
logger.info("re-exec blast for %s with flags: %r", dev, blast_flags)
|
||||
blast.blast.terminate()
|
||||
return try_blast(blast_flags=blast_flags)
|
||||
elif status == Status.Error:
|
||||
logger.info("blast error => terminating")
|
||||
blast.blast.terminate()
|
||||
else:
|
||||
# successfully launched
|
||||
return blast
|
||||
|
||||
|
||||
def main():
|
||||
logging.basicConfig()
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
reap_on_exit()
|
||||
|
||||
blast = try_blast()
|
||||
|
||||
if blast is not None:
|
||||
logger.info("waiting until blast exits")
|
||||
blast.blast.wait()
|
||||
|
||||
reap_children()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@@ -1,51 +0,0 @@
|
||||
# blast: tunnel audio from a pulseaudio sink to a UPnP/DLNA device (like a TV).
|
||||
# - expect 7s of latency
|
||||
# - can cast the default sink, or create a new one "blast.monitor"
|
||||
# and either assign that to default or assign apps to it.
|
||||
# compatibility:
|
||||
# - there is no single invocation which will be compatible with all known devices.
|
||||
# - sony tv:
|
||||
# - `blast` (default): WORKS
|
||||
# - `-usewav`: FAILS
|
||||
# - LG TV:
|
||||
# - `-usewav`: WORKS!
|
||||
# - `-useaac`: FAILS
|
||||
# - `-useflac`: FAILS
|
||||
# - `-uselpcm`: FAILS
|
||||
# - `-uselpcmle`: FAILS
|
||||
# - `-format aac`: FAILS
|
||||
# - `-bitrate 128`: FAILS
|
||||
# - `-nochunked`: FAILS
|
||||
# - `-format "ogg" -mime 'audio/x-opus+ogg'`: FAILS
|
||||
# - `-mime audio/ac3 -format ac3`: FAILS
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.blast-ugjka;
|
||||
in
|
||||
{
|
||||
sane.programs.blast-ugjka = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.net = "clearnet";
|
||||
};
|
||||
|
||||
sane.programs.blast-to-default = {
|
||||
# helper to deal with blast's interactive CLI
|
||||
packageUnwrapped = pkgs.static-nix-shell.mkPython3Bin {
|
||||
pname = "blast-to-default";
|
||||
pkgs = [ "blast-ugjka" ];
|
||||
srcRoot = ./.;
|
||||
};
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.extraConfig = [
|
||||
# else it fails to reap its children (or, maybe, it fails to hook its parent's death signal?)
|
||||
# might be possible to remove this, but kinda hard to see a clean way.
|
||||
"--sane-sandbox-keep-namespace" "pid"
|
||||
];
|
||||
suggestedPrograms = [ "blast-ugjka" ];
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = lib.mkIf cfg.enabled [ 9000 ];
|
||||
}
|
@@ -111,11 +111,6 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.extraRuntimePaths = [
|
||||
"/" #< just needs "bonsai", but needs to create it first...
|
||||
];
|
||||
|
||||
services.bonsaid = {
|
||||
description = "bonsai: programmable input dispatcher";
|
||||
after = [ "graphical-session.target" ];
|
||||
|
@@ -1,17 +0,0 @@
|
||||
{ ... }:
|
||||
{
|
||||
sane.programs.celeste64 = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDri = true;
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.extraPaths = [
|
||||
"/dev/input" #< for controllers
|
||||
];
|
||||
|
||||
persist.byStore.plaintext = [
|
||||
# save data, controls map
|
||||
".local/share/Celeste64"
|
||||
];
|
||||
};
|
||||
}
|
@@ -1,19 +1,9 @@
|
||||
{ pkgs, ... }:
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
sane.programs.cozy = {
|
||||
packageUnwrapped = pkgs.cozy.overrideAttrs (upstream: {
|
||||
postPatch = (upstream.postPatch or "") + ''
|
||||
# disable all reporting.
|
||||
# this can be done via the settings, but that's troublesome and easy to forget.
|
||||
# specifically, i don't want moby to be making these network requests several times per hour
|
||||
# while it might be roaming or trying to put the RF to sleep.
|
||||
substituteInPlace cozy/application_settings.py \
|
||||
--replace-fail 'self._settings.get_int("report-level")' '0'
|
||||
'';
|
||||
});
|
||||
|
||||
sandbox.method = "bwrap"; # landlock gives: _multiprocessing.SemLock: Permission Denied
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; # mpris
|
||||
sandbox.whitelistWayland = true;
|
||||
|
@@ -9,47 +9,17 @@ let
|
||||
in
|
||||
{
|
||||
sane.programs.dconf = {
|
||||
configOption = with lib; mkOption {
|
||||
type = types.submodule {
|
||||
options = {
|
||||
site = mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [];
|
||||
description = ''
|
||||
extra packages to link into /etc/dconf
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
};
|
||||
|
||||
packageUnwrapped = pkgs.rmDbusServicesInPlace pkgs.dconf;
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "inplace"; #< dbus/systemd services live in `.out` but point to `.lib` data.
|
||||
sandbox.whitelistDbus = [ "user" ];
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
persist.byStore.private = [
|
||||
".config/dconf"
|
||||
];
|
||||
|
||||
services.dconf = {
|
||||
description = "dconf configuration database/server";
|
||||
after = [ "graphical-session.target" ];
|
||||
wantedBy = [ "graphical-session.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = "${lib.getLib cfg.package}/libexec/dconf-service";
|
||||
Type = "simple";
|
||||
Restart = "always";
|
||||
RestartSec = "5s";
|
||||
};
|
||||
};
|
||||
|
||||
# supposedly necessary for packages which haven't been wrapped (i.e. wrapGtkApp?),
|
||||
# but in practice seems unnecessary.
|
||||
# env.GIO_EXTRA_MODULES = "${pkgs.dconf.lib}/lib/gio/modules";
|
||||
|
||||
config.site = [
|
||||
programs.dconf = lib.mkIf cfg.enabled {
|
||||
# note that `programs.dconf` doesn't allow specifying the dconf package.
|
||||
enable = true;
|
||||
packages = [
|
||||
(pkgs.writeTextFile {
|
||||
name = "dconf-user-profile";
|
||||
destination = "/etc/dconf/profile/user";
|
||||
@@ -60,18 +30,4 @@ in
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
# TODO: get dconf to read these from ~/.config/dconf ?
|
||||
environment.etc.dconf = lib.mkIf cfg.enabled {
|
||||
source = pkgs.symlinkJoin {
|
||||
name = "dconf-system-config";
|
||||
paths = map (x: "${x}/etc/dconf") cfg.config.site;
|
||||
nativeBuildInputs = [ (lib.getBin pkgs.dconf) ];
|
||||
postBuild = ''
|
||||
if test -d $out/db; then
|
||||
dconf update $out/db
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -1,139 +0,0 @@
|
||||
|
||||
/* Notification center */
|
||||
|
||||
.blurredBG, #main_window, .blurredBG.low, .blurredBG.normal {
|
||||
background: rgba(255, 255, 255, 1.0);
|
||||
}
|
||||
|
||||
.noti-center.time {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
|
||||
.notification.content {
|
||||
margin-left: 15px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.appname {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.blurredBG.notification {
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.blurredBG.notification.critical {
|
||||
background: rgba(255, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.notificationInCenter.critical {
|
||||
background: rgba(155, 0, 20, 0.5);
|
||||
}
|
||||
|
||||
/* Labels */
|
||||
|
||||
label {
|
||||
color: #322;
|
||||
}
|
||||
|
||||
label.notification {
|
||||
color: #322;
|
||||
}
|
||||
|
||||
label.critical {
|
||||
color: #000;
|
||||
}
|
||||
.notificationInCenter label.critical {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
|
||||
/* Buttons */
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
color: #322;
|
||||
border-radius: 3px;
|
||||
border-width: 0px;
|
||||
background-position: 0px 0px;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border-radius: 3px;
|
||||
background: rgba(0, 20, 20, 0.2);
|
||||
border-width: 0px;
|
||||
border-top: transparent;
|
||||
border-color: #f00;
|
||||
color: #fee;
|
||||
}
|
||||
|
||||
|
||||
/* Custom Buttons */
|
||||
|
||||
.userbutton {
|
||||
background: rgba(20,0,0, 0.15);
|
||||
}
|
||||
|
||||
.userbuttonlabel {
|
||||
color: #222;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.userbutton:hover {
|
||||
background: rgba(20, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.userbuttonlabel:hover {
|
||||
color: #111;
|
||||
}
|
||||
|
||||
button.buttonState1 {
|
||||
background: rgba(20,0,0,0.5);
|
||||
}
|
||||
|
||||
.userbuttonlabel.buttonState1 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button.buttonState1:hover {
|
||||
background: rgba(20,0,0, 0.4);
|
||||
}
|
||||
|
||||
.userbuttonlabel.buttonState1:hover {
|
||||
color: #111;
|
||||
}
|
||||
|
||||
button.buttonState2 {
|
||||
background: rgba(255,255,255,0.3);
|
||||
}
|
||||
|
||||
.userbuttonlabel.buttonState2 {
|
||||
color: #111;
|
||||
}
|
||||
|
||||
button.buttonState2:hover {
|
||||
background: rgba(20,0,0, 0.3);
|
||||
}
|
||||
|
||||
.userbuttonlabel.buttonState2:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
|
||||
/* Images */
|
||||
|
||||
image.deadd-noti-center.notification.image {
|
||||
margin-left: 20px;
|
||||
}
|
@@ -1,263 +0,0 @@
|
||||
### Margins for notification-center/notifications
|
||||
margin-top: 0
|
||||
margin-right: 0
|
||||
|
||||
### Margins for notification-center
|
||||
margin-bottom: 0
|
||||
|
||||
### Width of the notification center/notifications in pixels.
|
||||
width: 360
|
||||
|
||||
### Command to run at startup. This can be used to setup
|
||||
### button states.
|
||||
# startup-command: deadd-notification-center-startup
|
||||
|
||||
### Monitor on which the notification center/notifications will be
|
||||
### printed. If "follow-mouse" is set true, this does nothing.
|
||||
monitor: 0
|
||||
|
||||
### If true, the notification center/notifications will open on the
|
||||
### screen, on which the mouse is. Overrides the "monitor" setting.
|
||||
follow-mouse: false
|
||||
|
||||
notification-center:
|
||||
### Margin at the top/right/bottom of the notification center in
|
||||
### pixels. This can be used to avoid overlap between the notification
|
||||
### center and bars such as polybar or i3blocks.
|
||||
margin-top: 40
|
||||
# margin-right: 0
|
||||
# margin-bottom: 0
|
||||
|
||||
### Width of the notification center in pixels.
|
||||
# width: 500
|
||||
|
||||
### Monitor on which the notification center will be printed. If
|
||||
### "follow-mouse" is set true, this does nothing.
|
||||
# monitor: 0
|
||||
|
||||
### If true, the notification center will open on the screen, on which
|
||||
### the mouse is. Overrides the "monitor" setting.
|
||||
# follow-mouse: false
|
||||
|
||||
### Notification center closes when the mouse leaves it
|
||||
hide-on-mouse-leave: true
|
||||
|
||||
### If newFirst is set to true, newest notifications appear on the top
|
||||
### of the notification center. Else, notifications stack, from top to
|
||||
### bottom.
|
||||
new-first: true
|
||||
|
||||
### If true, the transient field in notifications will be ignored,
|
||||
### thus the notification will be persisted in the notification
|
||||
### center anyways
|
||||
ignore-transient: false
|
||||
|
||||
### Custom buttons in notification center
|
||||
buttons:
|
||||
### Numbers of buttons that can be drawn on a row of the notification
|
||||
### center.
|
||||
# buttons-per-row: 5
|
||||
|
||||
### Height of buttons in the notification center (in pixels).
|
||||
# buttons-height: 60
|
||||
|
||||
### Horizontal and vertical margin between each button in the
|
||||
### notification center (in pixels).
|
||||
# buttons-margin: 2
|
||||
|
||||
### Button actions and labels. For each button you must specify a
|
||||
### label and a command.
|
||||
actions:
|
||||
# - label: VPN
|
||||
# command: "sudo vpnToggle"
|
||||
# - label: Bluetooth
|
||||
# command: bluetoothToggle
|
||||
# - label: Wifi
|
||||
# command: wifiToggle
|
||||
# - label: Screensaver
|
||||
# command: screensaverToggle
|
||||
# - label: Keyboard
|
||||
# command: keyboardToggle
|
||||
|
||||
notification:
|
||||
### If true, markup (<u>, <i>, <b>, <a>) will be displayed properly
|
||||
use-markup: true
|
||||
|
||||
### If true, html entities (& for &, % for %, etc) will be
|
||||
### parsed properly. This is useful for chromium-based apps, which
|
||||
### tend to send these in notifications.
|
||||
parse-html-entities: true
|
||||
|
||||
dbus:
|
||||
|
||||
### If noti-closed messages are enabled, the sending application
|
||||
### will know that a notification was closed/timed out. This can
|
||||
### be an issue for certain applications, that overwrite
|
||||
### notifications on status updates (e.g. Spotify on each
|
||||
### song). When one of these applications thinks, the notification
|
||||
### has been closed/timed out, they will not overwrite existing
|
||||
### notifications but send new ones. This can lead to redundant
|
||||
### notifications in the notification center, as the close-message
|
||||
### is send regardless of the notification being persisted.
|
||||
send-noti-closed: false
|
||||
|
||||
app-icon:
|
||||
|
||||
### If set to true: If no icon is passed by the app_icon parameter
|
||||
### and no application "desktop-entry"-hint is present, deadd will
|
||||
### try to guess the icon from the application name (if present).
|
||||
guess-icon-from-name: true
|
||||
|
||||
### The display size of the application icons in the notification
|
||||
### pop-ups and in the notification center
|
||||
icon-size: 20
|
||||
|
||||
image:
|
||||
|
||||
### The maximal display size of images that are part of
|
||||
### notifications for notification pop-ups and in the notification
|
||||
### center
|
||||
size: 100
|
||||
|
||||
### The margin around the top, bottom, left, and right of
|
||||
### notification images.
|
||||
margin-top: 15
|
||||
margin-bottom: 15
|
||||
margin-left: 15
|
||||
margin-right: 0
|
||||
|
||||
### Apply modifications to certain notifications:
|
||||
### Each modification rule needs a "match" and either a "modify" or
|
||||
### a "script" entry.
|
||||
modifications:
|
||||
### Match:
|
||||
### Matches the notifications against these rules. If all of the
|
||||
### values (of one modification rule) match, the "modify"/"script"
|
||||
### part is applied.
|
||||
# - match:
|
||||
### Possible match criteria:
|
||||
# title: "Notification title"
|
||||
# body: "Notification body"
|
||||
# time: "12:44"
|
||||
# app-name: "App name"
|
||||
# urgency: "low" # "low", "normal" or "critical"
|
||||
|
||||
# modify:
|
||||
### Possible modifications
|
||||
# title: "abc"
|
||||
# body: "abc"
|
||||
# app-name: "abc"
|
||||
# app-icon: "file:///abc.png"
|
||||
### The timeout has three special values:
|
||||
### timeout: 0 -> don't time out at all
|
||||
### timeout: -1 -> use default timeout
|
||||
### timeout: 1 -> don't show as pop-up
|
||||
### timeout: >1 -> milliseconds until timeout
|
||||
# timeout: 1
|
||||
# margin-right: 10
|
||||
# margin-top: 10
|
||||
# image: "file:///abc.png"
|
||||
# image-size: 10
|
||||
# transient: true
|
||||
# send-noti-closed: false
|
||||
### Remove action buttons from notifications
|
||||
# remove-actions: true
|
||||
### Set the action-icons hint to true, action labels will then
|
||||
### be intergreted as GTK icon names
|
||||
# action-icons: true
|
||||
### List of actions, where the even elements (0, 2, ...) are the
|
||||
### action name and the odd elements are the label
|
||||
# actions:
|
||||
# - previous
|
||||
# - media-skip-backward
|
||||
# - play
|
||||
# - media-playback-start
|
||||
# - next
|
||||
# - media-skip-forward
|
||||
### Action commands, where the keys (e.g. "play") is the action
|
||||
### name and the value is a program call that should be executed
|
||||
### on action. Prevents sending of the action to the application.
|
||||
# action-commands:
|
||||
# play: playerctl play-pause
|
||||
# previous: playerctl previous
|
||||
# next: playerctl next
|
||||
|
||||
### Add a class-name to the notification container, that can be
|
||||
### used for specific styling of notifications using the
|
||||
### deadd.css file
|
||||
# class-name: "abc"
|
||||
|
||||
# - match:
|
||||
# app-name: "Chromium"
|
||||
|
||||
### Instead of modifying a notification directly, a script can be
|
||||
### run, which will receive the notification as JSON on STDIN. It
|
||||
### is expected to return JSON/YAML configuration that defines the
|
||||
### modifications that should be applied. Minimum complete return
|
||||
### value must be '{"modify": {}, "match": {}}'. Always leave the "match"
|
||||
### object empty (technical reasons, i.e. I am lazy).
|
||||
# script: "linux-notification-center-parse-chromium"
|
||||
- match:
|
||||
app-name: "Spotify"
|
||||
modify:
|
||||
image-size: 80
|
||||
timeout: 1
|
||||
send-noti-closed: true
|
||||
class-name: "Spotify"
|
||||
action-icons: true
|
||||
actions:
|
||||
- previous
|
||||
- media-skip-backward
|
||||
- play
|
||||
- media-playback-start
|
||||
- next
|
||||
- media-skip-forward
|
||||
action-commands:
|
||||
play: playerctl play-pause
|
||||
previous: playerctl previous
|
||||
next: playerctl next
|
||||
|
||||
# - match:
|
||||
# title: Bildschirmhelligkeit
|
||||
# modify:
|
||||
# image-size: 60
|
||||
popup:
|
||||
|
||||
### Default timeout used for notifications in milli-seconds. This can
|
||||
### be overwritten with the "-t" option (or "--expire-time") of the
|
||||
### notify-send command.
|
||||
default-timeout: 10000
|
||||
|
||||
### Margin above/right/between notifications (in pixels). This can
|
||||
### be used to avoid overlap between notifications and a bar such as
|
||||
### polybar or i3blocks.
|
||||
margin-top: 50
|
||||
margin-right: 50
|
||||
margin-between: 20
|
||||
|
||||
### Defines after how many lines of text the body will be truncated.
|
||||
### Use 0 if you want to disable truncation.
|
||||
max-lines-in-body: 3
|
||||
|
||||
### Determines whether the GTK widget that displays the notification body
|
||||
### in the notification popup will be hidden when empty. This is especially
|
||||
### useful for transient notifications that display a progress bar.
|
||||
# hide-body-if-empty: false
|
||||
|
||||
### Monitor on which the notifications will be
|
||||
### printed. If "follow-mouse" is set true, this does nothing.
|
||||
# monitor: 0
|
||||
|
||||
### If true, the notifications will open on the
|
||||
### screen, on which the mouse is. Overrides the "monitor" setting.
|
||||
# follow-mouse: false
|
||||
|
||||
click-behavior:
|
||||
|
||||
### The mouse button for dismissing a popup. Must be either "mouse1",
|
||||
### "mouse2", "mouse3", "mouse4", or "mouse5"
|
||||
dismiss: mouse1
|
||||
|
||||
### The mouse button for opening a popup with the default action.
|
||||
### Must be either "mouse1", "mouse2", "mouse3", "mouse4", or "mouse5"
|
||||
default-action: mouse3
|
@@ -1,17 +0,0 @@
|
||||
# docs are via README only:
|
||||
# - <https://github.com/phuhl/linux_notification_center>
|
||||
# reload config:
|
||||
# - `notify-send a --hint=boolean:deadd-notification-center:true --hint=string:type:reloadStyle`
|
||||
# toggle visibility:
|
||||
# - `kill -s USR1 $(pidof deadd-notification-center)`
|
||||
# clear notifications:
|
||||
# - `notify-send a --hint=boolean:deadd-notification-center:true --hint=string:type:clearInCenter`
|
||||
# set state of user button 0 to "highlighted" (true)
|
||||
# - `notify-send a --hint=boolean:deadd-notification-center:true --hint=int:id:0 --hint=boolean:state:true --hint=type:string:buttons`
|
||||
{ ... }:
|
||||
{
|
||||
sane.programs.deadd-notification-center = {
|
||||
fs.".config/deadd/deadd.css".symlink.target = ./deadd.css;
|
||||
fs.".config/deadd/deadd.yml".symlink.target = ./deadd.yml;
|
||||
};
|
||||
}
|
@@ -5,31 +5,25 @@
|
||||
./abaddon.nix
|
||||
./aerc.nix
|
||||
./alacritty.nix
|
||||
./alsa-ucm-conf
|
||||
./animatch.nix
|
||||
./assorted.nix
|
||||
./audacity.nix
|
||||
./bemenu.nix
|
||||
./blast-ugjka
|
||||
./bonsai.nix
|
||||
./brave.nix
|
||||
./bubblewrap.nix
|
||||
./calls.nix
|
||||
./cantata.nix
|
||||
./catt.nix
|
||||
./celeste64.nix
|
||||
./chatty.nix
|
||||
./conky
|
||||
./cozy.nix
|
||||
./dconf.nix
|
||||
./deadd-notification-center
|
||||
./dialect.nix
|
||||
./dino.nix
|
||||
./dissent.nix
|
||||
./element-desktop.nix
|
||||
./epiphany.nix
|
||||
./evince.nix
|
||||
./fcitx5.nix
|
||||
./feedbackd.nix
|
||||
./firefox.nix
|
||||
./firejail.nix
|
||||
@@ -51,6 +45,7 @@
|
||||
./gpodder.nix
|
||||
./grimshot.nix
|
||||
./gthumb.nix
|
||||
./gtkcord4.nix
|
||||
./handbrake.nix
|
||||
./helix.nix
|
||||
./imagemagick.nix
|
||||
@@ -58,7 +53,6 @@
|
||||
./kdenlive.nix
|
||||
./komikku.nix
|
||||
./koreader
|
||||
./less.nix
|
||||
./libreoffice.nix
|
||||
./lemoa.nix
|
||||
./loupe.nix
|
||||
@@ -67,7 +61,7 @@
|
||||
./mepo.nix
|
||||
./mimeo
|
||||
./mopidy.nix
|
||||
./mpv
|
||||
./mpv.nix
|
||||
./msmtp.nix
|
||||
./nautilus.nix
|
||||
./neovim.nix
|
||||
@@ -87,10 +81,7 @@
|
||||
./rhythmbox.nix
|
||||
./ripgrep.nix
|
||||
./rofi
|
||||
./sane-input-handler
|
||||
./sane-screenshot.nix
|
||||
./sane-scripts.nix
|
||||
./schlock.nix
|
||||
./sfeed.nix
|
||||
./signal-desktop.nix
|
||||
./splatmoji.nix
|
||||
@@ -103,10 +94,8 @@
|
||||
./supertuxkart.nix
|
||||
./sway
|
||||
./sway-autoscaler
|
||||
./swayidle.nix
|
||||
./swaylock.nix
|
||||
./swaynotificationcenter
|
||||
./sysvol.nix
|
||||
./swaynotificationcenter.nix
|
||||
./tangram.nix
|
||||
./tor-browser.nix
|
||||
./tuba.nix
|
||||
@@ -119,19 +108,19 @@
|
||||
./wireplumber.nix
|
||||
./wireshark.nix
|
||||
./wob
|
||||
./wvkbd.nix
|
||||
./xarchiver.nix
|
||||
./xdg-desktop-portal.nix
|
||||
./xdg-desktop-portal-gtk.nix
|
||||
./xdg-desktop-portal-wlr.nix
|
||||
./xdg-terminal-exec.nix
|
||||
./xdg-utils.nix
|
||||
./zathura.nix
|
||||
./zeal.nix
|
||||
./zecwallet-lite.nix
|
||||
./zsh
|
||||
];
|
||||
|
||||
config = {
|
||||
# XXX: this might not be necessary. try removing this and cacert.unbundled (servo)?
|
||||
environment.etc."ssl/certs".source = "${pkgs.cacert.unbundled}/etc/ssl/certs/*";
|
||||
|
||||
};
|
||||
}
|
||||
|
@@ -5,9 +5,6 @@
|
||||
sandbox.wrapperType = "inplace"; # share/search_providers/ calls back into the binary, weird wrap semantics
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.extraHomePaths = [
|
||||
".config/dconf" # won't start without it
|
||||
];
|
||||
suggestedPrograms = [ "dconf" ]; #< to persist settings
|
||||
|
||||
packageUnwrapped = pkgs.dialect.overrideAttrs (upstream: {
|
||||
|
@@ -46,6 +46,7 @@ in
|
||||
};
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||
|
@@ -17,6 +17,7 @@
|
||||
];
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||
|
@@ -12,13 +12,11 @@
|
||||
sandbox.wrapperType = "inplace"; # /share/epiphany/default-bookmarks.rdf refers back to /share; dbus files to /libexec
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; #< silently fails to start without it.
|
||||
# default sandboxing breaks rendering in weird ways. sites are super zoomed in / not scaled.
|
||||
# enabling DRI/DRM (as below) seems to fix that.
|
||||
sandbox.whitelistDri = true;
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.extraHomePaths = [
|
||||
".config/dconf" # else will always prompt "make default browser?"
|
||||
".config/epiphany" #< else it gets angry at launch
|
||||
"tmp"
|
||||
];
|
||||
|
@@ -1,123 +0,0 @@
|
||||
# fcitx5 is an "input method", to e.g. allow typing CJK on qwerty.
|
||||
# but i also misuse it to allow typing emoji on qwerty:
|
||||
# - press `Super+backtick`
|
||||
# - type something like "effort"
|
||||
# - it should be underlined, at the least
|
||||
# - if well supported (e.g. Firefox; also gtk4, alacritty on sway 1.10+), a drop-down fuzzy matcher will appear
|
||||
# - press space
|
||||
# - "effort" should be replaced by `(ง •̀_•́)ง`
|
||||
#
|
||||
## debugging
|
||||
# - `fcitx5-diagnose`
|
||||
#
|
||||
## config/docs:
|
||||
# - `fcitx5-configtool`, then check ~/.config/fcitx5 files
|
||||
# - <https://fcitx-im.org/wiki/Fcitx_5>
|
||||
# - <https://wiki.archlinux.org/title/Fcitx5>
|
||||
# - theming: <https://wiki.archlinux.org/title/Fcitx5#Themes_and_appearance>
|
||||
# - <https://en.wikipedia.org/wiki/Fcitx>
|
||||
# - wayland specifics: <https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland>
|
||||
# - quickphrase (emoji): <https://fcitx-im.org/wiki/QuickPhrase>
|
||||
# - override phrases via `~/.config/fcitx/data/QuickPhrase.mb`
|
||||
# - customize bindings via `fcitx5-configtool` > addons > QuickPhrase
|
||||
# - theming:
|
||||
# - nixpkgs has a few themes: `fcitx5-{material-color,nord,rose-pine}`
|
||||
# - NUR has a few themes
|
||||
# - <https://github.com/catppuccin/fcitx5>
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.fcitx5;
|
||||
in
|
||||
{
|
||||
sane.programs.fcitx5 = {
|
||||
packageUnwrapped = pkgs.fcitx5-with-addons.override {
|
||||
addons = with pkgs; [
|
||||
# fcitx5-mozc # japanese input: <https://github.com/fcitx/mozc>
|
||||
fcitx5-gtk # <https://github.com/fcitx/fcitx5-gtk>
|
||||
];
|
||||
};
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistDbus = [ "user" ];
|
||||
sandbox.whitelistWayland = true; # for `fcitx5-configtool, if nothing else`
|
||||
sandbox.extraHomePaths = [
|
||||
# ".config/fcitx"
|
||||
".config/fcitx5"
|
||||
".local/share/fcitx5"
|
||||
];
|
||||
|
||||
fs.".config/fcitx5/conf/quickphrase.conf".symlink.text = ''
|
||||
# Choose key modifier
|
||||
Choose Modifier=None
|
||||
# Enable Spell check
|
||||
Spell=True
|
||||
FallbackSpellLanguage=en
|
||||
|
||||
[TriggerKey]
|
||||
# defaults: Super+grave, Super+semicolon
|
||||
# gtk apps use ctrl+period, so super+period is a nice complement
|
||||
0=Super+grave
|
||||
1=Super+semicolon
|
||||
2=Super+period
|
||||
'';
|
||||
fs.".config/fcitx5/conf/classicui.conf".symlink.text = ''
|
||||
Theme=sane
|
||||
Font="Sans 20"
|
||||
Vertical Candidate List=True
|
||||
'';
|
||||
fs.".local/share/fcitx5/themes/sane/theme.conf".symlink.text = ''
|
||||
# i omit several keys, especially the ones which don't seem to do much.
|
||||
# for a theme which uses many more options, see:
|
||||
# - <https://github.com/catppuccin/fcitx5/blob/main/src/catppuccin-mocha/theme.conf>
|
||||
[Metadata]
|
||||
Name=sane
|
||||
ScaleWithDPI=True
|
||||
|
||||
[InputPanel]
|
||||
NormalColor=#d8d8d8
|
||||
HighlightCandidateColor=#FFFFFF
|
||||
HighlightColor=#FFFFFF
|
||||
HighlightBackgroundColor=#1f5e54
|
||||
|
||||
[InputPanel/Background]
|
||||
Color=#1f5e54
|
||||
|
||||
[InputPanel/Highlight]
|
||||
Color=#418379
|
||||
|
||||
[InputPanel/Highlight/Margin]
|
||||
Left=20
|
||||
Right=20
|
||||
Top=7
|
||||
Bottom=7
|
||||
|
||||
[InputPanel/TextMargin]
|
||||
Left=20
|
||||
Right=20
|
||||
Top=6
|
||||
Bottom=6
|
||||
'';
|
||||
|
||||
services.fcitx5 = {
|
||||
description = "fcitx5: input method (IME) for emoji/internationalization";
|
||||
after = [ "graphical-session.target" ];
|
||||
wantedBy = [ "graphical-session.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart="${cfg.package}/bin/fcitx5";
|
||||
Type = "simple";
|
||||
Restart = "always";
|
||||
RestartSec = "10s";
|
||||
};
|
||||
};
|
||||
|
||||
env.XMODIFIERS = "@im=fcitx";
|
||||
# setting IM_MODULE is generally not required on wayland, but can be used to override the toolkit's own dialogs with our own.
|
||||
# env.GTK_IM_MODULE = "fcitx";
|
||||
# enable if you want them:
|
||||
# env.QT_IM_MODULE = "fcitx";
|
||||
# env.QT_PLUGIN_PATH = [ "${cfg.package}/${pkgs.qt6.qtbase.qtPluginPrefix}" ];
|
||||
# env.SDL_IM_MODULE = "fcitx";
|
||||
# env.GLFW_IM_MODULE = "ibus"; # for KiTTY, as per <https://wiki.archlinux.org/title/Fcitx5#Integration>
|
||||
};
|
||||
}
|
@@ -25,6 +25,7 @@ in
|
||||
};
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistDbus = [ "user" ];
|
||||
sandbox.whitelistAudio = true;
|
||||
|
||||
|
@@ -234,7 +234,7 @@ in
|
||||
sane.programs.firefox = {
|
||||
inherit packageUnwrapped;
|
||||
sandbox.method = "bwrap"; # landlock works, but requires all of /proc to be linked
|
||||
sandbox.wrapperType = "inplace"; # trivial package; cheap enough to wrap inplace
|
||||
sandbox.wrapperType = "inplace"; # probably wrappedDerivation could work too.
|
||||
sandbox.net = "all";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; # mpris
|
||||
@@ -322,8 +322,6 @@ in
|
||||
defaultPref("widget.use-xdg-desktop-portal.mime-handler", 1);
|
||||
defaultPref("widget.use-xdg-desktop-portal.open-uri", 1);
|
||||
|
||||
defaultPref("browser.toolbars.bookmarks.visibility", "never");
|
||||
|
||||
// auto-open mpv:// URIs without prompting.
|
||||
// can do this with other protocols too (e.g. matrix?). see about:config for common handlers.
|
||||
defaultPref("network.protocol-handler.external.mpv", true);
|
||||
|
@@ -1,10 +1,6 @@
|
||||
# to preview fonts:
|
||||
# - `font-manager` (gui)
|
||||
# - useful to determine official name; codepoint support
|
||||
# docs:
|
||||
# - <https://slatecave.net/notebook/fontconfig/>
|
||||
# debugging:
|
||||
# - `fc-conflist` -> show all config files loaded
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
# nerdfonts takes popular open fonts and patches them to support a wider range of glyphs, notably emoji.
|
||||
@@ -34,12 +30,13 @@ in
|
||||
{
|
||||
sane.programs.fontconfig = {
|
||||
sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.autodetectCliPaths = "existingOrParent"; #< this might be overkill; or, how many programs reference fontconfig internally?
|
||||
|
||||
# persist.byStore.plaintext = [
|
||||
# # < 10 MiB. however, nixos generates its own fontconfig cache at build time now.
|
||||
# ".cache/fontconfig"
|
||||
# ];
|
||||
persist.byStore.plaintext = [
|
||||
# < 10 MiB
|
||||
".cache/fontconfig"
|
||||
];
|
||||
};
|
||||
|
||||
fonts = lib.mkIf config.sane.programs.fontconfig.enabled {
|
||||
@@ -47,8 +44,8 @@ in
|
||||
fontconfig.defaultFonts = {
|
||||
emoji = [
|
||||
"Noto Color Emoji"
|
||||
# "Font Awesome 6 Free"
|
||||
# "Font Awesome 6 Brands"
|
||||
"Font Awesome 6 Free"
|
||||
"Font Awesome 6 Brands"
|
||||
];
|
||||
monospace = [
|
||||
"Hack Nerd Font Propo"
|
||||
@@ -70,7 +67,7 @@ in
|
||||
# TODO: reduce this font set.
|
||||
# - probably need only one of dejavu/freefont/liberation
|
||||
dejavu_fonts # 10 MiB; DejaVu {Sans,Serif,Sans Mono,Math TeX Gyre}; also available as a NerdFonts (Sans Mono only)
|
||||
# font-awesome # 2 MiB; Font Awesome 6 {Free,Brands}
|
||||
font-awesome # 2 MiB; Font Awesome 6 {Free,Brands}
|
||||
freefont_ttf # 11 MiB; Free{Mono,Sans,Serif}
|
||||
gyre-fonts # 4 MiB; Tex Gyre *; ttf substitutes for standard PostScript fonts
|
||||
# hack-font # 1 MiB; Hack; also available as a NerdFonts
|
||||
|
@@ -28,6 +28,7 @@ in
|
||||
# packageUnwrapped = pkgs.fractal-next;
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||
@@ -59,9 +60,9 @@ in
|
||||
|
||||
persist.byStore.private = [
|
||||
# XXX by default fractal stores its state in ~/.local/share/<build-profile>/<UUID>.
|
||||
# ".local/share/hack" # for debug-like builds
|
||||
# ".local/share/stable" # for normal releases
|
||||
".local/share/fractal" # for version 5+
|
||||
".local/share/hack" # for debug-like builds
|
||||
".local/share/stable" # for normal releases
|
||||
".local/share/fractal" # for version 5+, i think?
|
||||
];
|
||||
|
||||
suggestedPrograms = [ "gnome-keyring" ];
|
||||
|
@@ -3,6 +3,7 @@
|
||||
{
|
||||
sane.programs.frozen-bubble = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet"; # net play
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistWayland = true;
|
||||
|
@@ -9,6 +9,7 @@
|
||||
{
|
||||
sane.programs.g4music = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; # mpris
|
||||
sandbox.whitelistWayland = true;
|
||||
|
@@ -4,6 +4,7 @@
|
||||
packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.glib "bin/gdbus";
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistDbus = [ "user" ]; #< XXX: maybe future users will also want system access
|
||||
};
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ in
|
||||
};
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||
sandbox.whitelistWayland = true;
|
||||
|
@@ -19,6 +19,7 @@ in
|
||||
'';
|
||||
});
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.whitelistPwd = true;
|
||||
sandbox.autodetectCliPaths = true; # necessary for git-upload-pack
|
||||
|
@@ -6,10 +6,10 @@ in
|
||||
sane.programs.gnome-keyring = {
|
||||
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-keyring;
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistDbus = [ "user" ];
|
||||
sandbox.extraRuntimePaths = [
|
||||
"keyring" #< only needs keyring/control, but has to *create* that.
|
||||
# "keyring/control"
|
||||
"keyring/control"
|
||||
];
|
||||
sandbox.capabilities = [
|
||||
# ipc_lock: used to `mlock` the secrets so they don't get swapped out.
|
||||
@@ -28,7 +28,7 @@ in
|
||||
|
||||
fs.".local/share/keyrings/default" = {
|
||||
file.text = "Default_keyring.keyring"; #< no trailing newline
|
||||
# wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ];
|
||||
wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ];
|
||||
wantedBeforeBy = [ #< don't create this as part of `multi-user.target`
|
||||
"gnome-keyring.service" # TODO: sane.programs should declare this dependency for us
|
||||
];
|
||||
@@ -43,7 +43,7 @@ in
|
||||
lock-on-idle=false
|
||||
lock-after=false
|
||||
'';
|
||||
# wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ];
|
||||
wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ];
|
||||
wantedBeforeBy = [ #< don't create this as part of `multi-user.target`
|
||||
"gnome-keyring.service"
|
||||
];
|
||||
@@ -55,7 +55,6 @@ in
|
||||
wantedBy = [ "graphical-session.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/gnome-keyring-daemon --start --foreground --components=secrets";
|
||||
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -m 0700 -p %t/keyring";
|
||||
Type = "simple";
|
||||
Restart = "always";
|
||||
RestartSec = "20s";
|
||||
|
@@ -1,8 +1,8 @@
|
||||
{ pkgs, ... }:
|
||||
{ ... }:
|
||||
{
|
||||
sane.programs."gnome.gnome-maps" = {
|
||||
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-maps;
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "inplace"; #< dbus files
|
||||
sandbox.whitelistDri = true; # for perf
|
||||
sandbox.whitelistDbus = [
|
||||
"system" # system is required for non-portal location services
|
||||
|
@@ -4,7 +4,7 @@
|
||||
{
|
||||
sane.programs."gnome.gnome-weather" = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "inplace"; #< /share/org.gnome.Weather/org.gnome.Weather file refers to bins by full path
|
||||
sandbox.wrapperType = "inplace";
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.net = "clearnet";
|
||||
suggestedPrograms = [ "dconf" ]; #< stores city/location settings
|
||||
|
@@ -34,6 +34,7 @@ in
|
||||
{
|
||||
sane.programs.go2tv = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.autodetectCliPaths = true;
|
||||
# for GUI invocation, allow the common media directories
|
||||
|
@@ -23,6 +23,7 @@ in {
|
||||
});
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistDbus = [ "user" ]; # it won't launch without it, dunno exactly why.
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.net = "clearnet";
|
||||
|
@@ -15,6 +15,7 @@
|
||||
"wl-clipboard"
|
||||
];
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.whitelistDbus = [ "user" ];
|
||||
sandbox.autodetectCliPaths = "existingFileOrParent";
|
||||
|
@@ -3,10 +3,10 @@
|
||||
# - notification sounds can be handled by swaync
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.dissent;
|
||||
cfg = config.sane.programs.gtkcord4;
|
||||
in
|
||||
{
|
||||
sane.programs.dissent = {
|
||||
sane.programs.gtkcord4 = {
|
||||
configOption = with lib; mkOption {
|
||||
default = {};
|
||||
type = types.submodule {
|
||||
@@ -17,21 +17,22 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
packageUnwrapped = pkgs.dissent.overrideAttrs (upstream: {
|
||||
packageUnwrapped = pkgs.gtkcord4.overrideAttrs (upstream: {
|
||||
postConfigure = (upstream.postConfigure or "") + ''
|
||||
# dissent uses go-keyring to interface with the org.freedesktop.secrets provider (i.e. gnome-keyring).
|
||||
# gtkcord4 uses go-keyring to interface with the org.freedesktop.secrets provider (i.e. gnome-keyring).
|
||||
# go-keyring hardcodes `login.keyring` as the keyring to store secrets in, instead of reading `~/.local/share/keyring/default`.
|
||||
# `login.keyring` seems to be a special keyring preconfigured (by gnome-keyring) to encrypt everything to the user's password.
|
||||
# that's redundant with my fs-level encryption and makes the keyring less inspectable,
|
||||
# so patch dissent to use Default_keyring instead.
|
||||
# so patch gtkcord4 to use Default_keyring instead.
|
||||
# see:
|
||||
# - <https://github.com/diamondburned/dissent/issues/139>
|
||||
# - <https://github.com/diamondburned/gtkcord4/issues/139>
|
||||
# - <https://github.com/zalando/go-keyring/issues/46>
|
||||
substituteInPlace vendor/github.com/zalando/go-keyring/secret_service/secret_service.go \
|
||||
--replace '"login"' '"Default_keyring"'
|
||||
'';
|
||||
});
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||
@@ -51,17 +52,18 @@ in
|
||||
];
|
||||
|
||||
persist.byStore.private = [
|
||||
".cache/dissent"
|
||||
".config/dissent" # empty?
|
||||
".cache/gtkcord4"
|
||||
".config/gtkcord4" # empty?
|
||||
];
|
||||
|
||||
services.dissent = {
|
||||
description = "dissent Discord client";
|
||||
services.gtkcord4 = {
|
||||
description = "gtkcord4 Discord client";
|
||||
after = [ "graphical-session.target" ];
|
||||
# partOf = [ "graphical-session.target" ];
|
||||
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/dissent";
|
||||
ExecStart = "${cfg.package}/bin/gtkcord4";
|
||||
Type = "simple";
|
||||
Restart = "always";
|
||||
RestartSec = "20s";
|
@@ -2,6 +2,7 @@
|
||||
{
|
||||
sane.programs.handbrake = {
|
||||
sandbox.method = "landlock"; #< also supports bwrap, but landlock ensures we don't write to non-mounted tmpfs dir
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.extraHomePaths = [
|
||||
|
@@ -2,6 +2,7 @@
|
||||
{
|
||||
sane.programs.kdenlive = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.extraHomePaths = [
|
||||
"Music"
|
||||
"Pictures/from" # e.g. Videos taken from my phone
|
||||
|
@@ -11,6 +11,7 @@
|
||||
});
|
||||
|
||||
sandbox.method = "bwrap"; # TODO:sandbox untested
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.whitelistDbus = [ "user" ]; # needs to connect to dconf via dbus
|
||||
sandbox.whitelistDri = true; #< required
|
||||
|
@@ -46,6 +46,7 @@ in {
|
||||
sane.programs.koreader = {
|
||||
packageUnwrapped = pkgs.koreader-from-src;
|
||||
sandbox.method = "bwrap"; # sandboxes fine under landlock too, except for FTP
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.whitelistDri = true; # reduces startup time and subjective page flip time
|
||||
sandbox.whitelistWayland = true;
|
||||
|
@@ -2,6 +2,7 @@
|
||||
{
|
||||
sane.programs.lemoa = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.whitelistDbus = [ "user" ]; # for clicking links
|
||||
sandbox.whitelistDri = true;
|
||||
|
@@ -1,8 +0,0 @@
|
||||
{ ... }:
|
||||
{
|
||||
sane.programs.less = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.autodetectCliPaths = "existingFile";
|
||||
env.PAGER = "less";
|
||||
};
|
||||
}
|
@@ -12,6 +12,7 @@
|
||||
}));
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.autodetectCliPaths = "parent";
|
||||
sandbox.extraHomePaths = [
|
||||
|
@@ -10,13 +10,19 @@
|
||||
# bwrap (loupe image viewer) doesn't like to run inside landlock
|
||||
# "bwrap: failed to make / slave: Operation not permitted"
|
||||
sandbox.method = "bwrap"; # supports landlock or bwrap
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistDri = true;
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; #< so that it can in theory open the image viewer using fdo portal... but it doesn't :|
|
||||
sandbox.extraHomePaths = [
|
||||
".config/dconf" #< else it segfaults during post-process
|
||||
# ".config/megapixels"
|
||||
# ".config/xcb"
|
||||
# ".xcb"
|
||||
".local/share/applications" #< needed for viewing photos, until i can sort out the portal stuff
|
||||
# ".local/share/icons"
|
||||
# ".icons" #< actually needed!
|
||||
# ".themes"
|
||||
# ".nix-profile"
|
||||
".cache/mesa_shader_cache" # loads way faster
|
||||
"tmp"
|
||||
"Pictures" #< TODO: make this Pictures/Photos and save photos there
|
||||
|
@@ -5,6 +5,7 @@
|
||||
{
|
||||
sane.programs.mepo = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "all"; # for tiles *and* for localhost comm to gpsd
|
||||
sandbox.whitelistDri = true;
|
||||
sandbox.whitelistWayland = true;
|
||||
|
@@ -1,8 +1,6 @@
|
||||
# mimeo is an exec dispatcher like xdg-open, but which allows mapping different URL regexes to different handlers.
|
||||
# mimeo is an exec dispatcher like xdg-open, but why allows mapping different URL regexes to different handlers.
|
||||
# my setup sets mimeo as the default http/https handler,
|
||||
# and from there it dispatches specialized rules, falling back to the original http/https handler if no URL specialization exists
|
||||
#
|
||||
# alternative to mimeo is jaro: <https://github.com/isamert/jaro>
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
mimeo-open-desktop = pkgs.static-nix-shell.mkPython3Bin {
|
||||
|
195
hosts/common/programs/mpv.nix
Normal file
195
hosts/common/programs/mpv.nix
Normal file
@@ -0,0 +1,195 @@
|
||||
# mpv docs:
|
||||
# - <https://mpv.io/manual/master>
|
||||
# - <https://github.com/mpv-player/mpv/wiki>
|
||||
# curated mpv mods/scripts/users:
|
||||
# - <https://github.com/stax76/awesome-mpv>
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.sane.programs.mpv;
|
||||
in
|
||||
{
|
||||
sane.programs.mpv = {
|
||||
configOption = with lib; mkOption {
|
||||
default = {};
|
||||
type = types.submodule {
|
||||
options.vo = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "--vo=FOO flag to pass to mpv";
|
||||
};
|
||||
};
|
||||
};
|
||||
packageUnwrapped = (pkgs.wrapMpv pkgs.mpv-unwrapped {
|
||||
scripts = with pkgs.mpvScripts; [
|
||||
mpris
|
||||
uosc
|
||||
# pkgs.mpv-uosc-latest
|
||||
];
|
||||
extraMakeWrapperArgs = lib.optionals (cfg.config.vo != null) [
|
||||
# 2023/08/29: fixes an error where mpv on moby launches with the message
|
||||
# "DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory"
|
||||
# audio still works, and controls, screenshotting, etc -- just not the actual rendering
|
||||
#
|
||||
# this is likely a regression for mpv 0.36.0.
|
||||
# the actual error message *appears* to come from the mesa library, but it's tough to trace.
|
||||
#
|
||||
# TODO(2023/12/03): remove once mesa 23.3.1 lands: <https://github.com/NixOS/nixpkgs/pull/265740>
|
||||
#
|
||||
# backend compatibility (2023/10/22):
|
||||
# run with `--vo=help` to see a list of all output options.
|
||||
# non-exhaustive (W=works, F=fails, A=audio-only, U=audio+ui only (no video))
|
||||
# ? null Null video output
|
||||
# A (default)
|
||||
# A dmabuf-wayland Wayland dmabuf video output
|
||||
# A libmpv render API for libmpv (mpv plays the audio, but doesn't even render a window)
|
||||
# A vdpau VDPAU with X11
|
||||
# F drm Direct Rendering Manager (software scaling)
|
||||
# F gpu-next Video output based on libplacebo
|
||||
# F vaapi VA API with X11
|
||||
# F x11 X11 (software scaling)
|
||||
# F xv X11/Xv
|
||||
# U gpu Shader-based GPU Renderer
|
||||
# W caca libcaca (terminal rendering)
|
||||
# W sdl SDL 2.0 Renderer
|
||||
# W wlshm Wayland SHM video output (software scaling)
|
||||
"--add-flags" "--vo=${cfg.config.vo}"
|
||||
];
|
||||
}).overrideAttrs (base: {
|
||||
buildCommand = base.buildCommand + ''
|
||||
# runHook postFixup to allow sandbox wrappers to wrap the binaries
|
||||
runHook postFixup
|
||||
'';
|
||||
});
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.autodetectCliPaths = true;
|
||||
sandbox.net = "all";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; #< mpris
|
||||
sandbox.whitelistDri = true; #< mpv has excellent fallbacks to non-DRI, but DRI offers a good 30%-50% reduced CPU
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.extraHomePaths = [
|
||||
".config/mpv" #< else mpris plugin crashes on launch
|
||||
# it's common for album (or audiobook, podcast) images/lyrics/metadata to live adjacent to the primary file.
|
||||
# CLI detection is too poor to pick those up, so expose the common media dirs to the sandbox to make that *mostly* work.
|
||||
"Books/local"
|
||||
"Books/servo"
|
||||
"Music"
|
||||
"Videos/local"
|
||||
"Videos/servo"
|
||||
];
|
||||
|
||||
persist.byStore.plaintext = [
|
||||
# for `watch_later`
|
||||
".local/state/mpv"
|
||||
];
|
||||
fs.".config/mpv/input.conf".symlink.text = let
|
||||
execInTerm = "${pkgs.xdg-terminal-exec}/bin/xdg-terminal-exec";
|
||||
in ''
|
||||
# docs:
|
||||
# - <https://mpv.io/manual/master/#list-of-input-commands>
|
||||
# - script-binding: <https://mpv.io/manual/master/#command-interface-script-binding>
|
||||
# - properties: <https://mpv.io/manual/master/#property-list>
|
||||
|
||||
# let volume/power keys be interpreted by the system.
|
||||
# this is important for sxmo.
|
||||
# mpv defaults is POWER = close, VOLUME_{UP,DOWN} = adjust application-level volume
|
||||
POWER ignore
|
||||
VOLUME_UP ignore
|
||||
VOLUME_DOWN ignore
|
||||
|
||||
# uosc menu
|
||||
# text after the shebang is parsed by uosc to construct the menu and names
|
||||
menu script-binding uosc/menu
|
||||
s script-binding uosc/subtitles #! Subtitles
|
||||
a script-binding uosc/audio #! Audio tracks
|
||||
q script-binding uosc/stream-quality #! Stream quality
|
||||
p script-binding uosc/items #! Playlist
|
||||
c script-binding uosc/chapters #! Chapters
|
||||
> script-binding uosc/next #! Navigation > Next
|
||||
< script-binding uosc/prev #! Navigation > Prev
|
||||
o script-binding uosc/open-file #! Navigation > Open file
|
||||
# set video-aspect-override "-1" #! Utils > Aspect ratio > Default
|
||||
# set video-aspect-override "16:9" #! Utils > Aspect ratio > 16:9
|
||||
# set video-aspect-override "4:3" #! Utils > Aspect ratio > 4:3
|
||||
# set video-aspect-override "2.35:1" #! Utils > Aspect ratio > 2.35:1
|
||||
# script-binding uosc/audio-device #! Utils > Audio devices
|
||||
# script-binding uosc/editions #! Utils > Editions
|
||||
ctrl+s async screenshot #! Utils > Screenshot
|
||||
alt+i script-binding uosc/keybinds #! Utils > Key bindings
|
||||
O script-binding uosc/show-in-directory #! Utils > Show in directory
|
||||
# script-binding uosc/open-config-directory #! Utils > Open config directory
|
||||
# set pause yes; run ${execInTerm} go2tv -v "''${stream-open-filename}" #! Cast
|
||||
# set pause yes; run ${execInTerm} go2tv -u "''${stream-open-filename}" #! Cast (...) > Stream
|
||||
# set pause yes; run go2tv #! Cast (...) > GUI
|
||||
# TODO: unify "Cast" and "Cast (stream)" options above.
|
||||
'';
|
||||
fs.".config/mpv/mpv.conf".symlink.text = ''
|
||||
save-position-on-quit=yes
|
||||
keep-open=yes
|
||||
|
||||
# force GUI, even for tracks w/o album art
|
||||
# see: <https://www.reddit.com/r/mpv/comments/rvrrpt/oscosdgui_and_arch_linux/>
|
||||
player-operation-mode=pseudo-gui
|
||||
|
||||
# use uosc instead (for On Screen Controls)
|
||||
osc=no
|
||||
# uosc provides its own seeking/volume indicators, so you also don't need this
|
||||
osd-bar=no
|
||||
# uosc will draw its own window controls if you disable window border
|
||||
border=no
|
||||
'';
|
||||
fs.".config/mpv/script-opts/osc.conf".symlink.text = ''
|
||||
# make the on-screen controls *always* visible
|
||||
# unfortunately, this applies to full-screen as well
|
||||
# - docs: <https://mpv.io/manual/master/#on-screen-controller-visibility>
|
||||
# if uosc is installed, this file is unused
|
||||
visibility=always
|
||||
'';
|
||||
fs.".config/mpv/script-opts/uosc.conf".symlink.text = let
|
||||
play_pause_btn = "cycle:play_arrow:pause:no=pause/yes=play_arrow";
|
||||
rev_btn = "command:replay_10:seek -10";
|
||||
fwd_btn = "command:forward_30:seek 30";
|
||||
in ''
|
||||
# docs:
|
||||
# - <https://github.com/tomasklaen/uosc>
|
||||
# - <https://github.com/tomasklaen/uosc/blob/main/src/uosc.conf>
|
||||
# - <https://superuser.com/questions/1775550/add-new-buttons-to-mpv-uosc-ui>
|
||||
timeline_style=bar
|
||||
timeline_persistency=paused,audio
|
||||
controls_persistency=paused,audio
|
||||
volume_persistency=audio
|
||||
volume_opacity=0.75
|
||||
|
||||
# speed_persistency=paused,audio
|
||||
# vvv want a close button?
|
||||
top_bar=always
|
||||
top_bar_persistency=paused
|
||||
|
||||
controls=menu,<video>subtitles,<has_many_audio>audio,<has_many_video>video,<has_many_edition>editions,<stream>stream-quality,space,${rev_btn},${play_pause_btn},${fwd_btn},space,speed:1.0,gap,<video>fullscreen
|
||||
|
||||
text_border=6.0
|
||||
font_bold=yes
|
||||
color=foreground=ff8080,background_text=ff8080
|
||||
|
||||
ui_scale=1.0
|
||||
'';
|
||||
|
||||
# mime.priority = 200; # default = 100; 200 means to yield to other apps
|
||||
mime.priority = 50; # default = 100; 50 in order to take precedence over vlc.
|
||||
mime.associations."audio/flac" = "mpv.desktop";
|
||||
mime.associations."audio/mpeg" = "mpv.desktop";
|
||||
mime.associations."audio/x-opus+ogg" = "mpv.desktop";
|
||||
mime.associations."audio/x-vorbis+ogg" = "mpv.desktop";
|
||||
mime.associations."video/mp4" = "mpv.desktop";
|
||||
mime.associations."video/quicktime" = "mpv.desktop";
|
||||
mime.associations."video/webm" = "mpv.desktop";
|
||||
mime.associations."video/x-flv" = "mpv.desktop";
|
||||
mime.associations."video/x-matroska" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(www.)?youtube.com/watch\?.*v=" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(www.)?youtube.com/v/" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(www.)?youtu.be/.+" = "mpv.desktop";
|
||||
};
|
||||
}
|
||||
|
@@ -1,3 +0,0 @@
|
||||
# font size used by mpv's console (`); default 16
|
||||
# font_size=28
|
||||
scale=2
|
@@ -1,166 +0,0 @@
|
||||
# curated mpv mods/scripts/users:
|
||||
# - <https://github.com/stax76/awesome-mpv>
|
||||
# mpv docs:
|
||||
# - <https://mpv.io/manual/master>
|
||||
# - <https://github.com/mpv-player/mpv/wiki>
|
||||
# extensions i use:
|
||||
# - <https://github.com/jonniek/mpv-playlistmanager>
|
||||
# other extensions that could be useful:
|
||||
# - list: <https://github.com/stax76/awesome-mpv>
|
||||
# - list: <https://nudin.github.io/mpv-script-directory/>
|
||||
# - browse DLNA shares: <https://github.com/chachmu/mpvDLNA>
|
||||
# - act as a DLNS renderer (sink): <https://github.com/xfangfang/Macast>
|
||||
# debugging:
|
||||
# - enter console by pressing backtick.
|
||||
# > `set volume 50` -> sets application volume to 50%
|
||||
# > `set ao-volume 50` -> sets system-wide volume to 50%
|
||||
# > `show-text "vol: ${volume}"` -> get the volume
|
||||
# - show script output by running mpv with `--msg-level=all=trace`
|
||||
# - and then just `print(...)` from lua & it'll show in terminal
|
||||
# - invoke mpv with `--no-config` to have it not read ~/.config/mpv/*
|
||||
# - press `i` to show decoder info
|
||||
#
|
||||
# usage tips:
|
||||
# - `<` or `>` to navigate prev/next-file-in-folder (uosc)
|
||||
# - shift+enter to view the playlist, then arrow-keys to navigate (mpv-playlistmanager)
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.sane.programs.mpv;
|
||||
uosc = pkgs.mpvScripts.uosc.overrideAttrs (upstream: {
|
||||
# patch so that the volume control corresponds to `ao-volume`, i.e. the system-wide volume.
|
||||
# this is particularly nice for moby, because it avoids the awkwardness that system volume
|
||||
# is hard to adjust while screen is on.
|
||||
# note that only under alsa (`-ao=alsa`) does `ao-volume` actually correspond to system volume.
|
||||
postPatch = (upstream.postPatch or "") + ''
|
||||
substituteInPlace src/uosc/main.lua \
|
||||
--replace-fail "mp.observe_property('volume'" "mp.observe_property('ao-volume'"
|
||||
substituteInPlace src/uosc/elements/Volume.lua \
|
||||
--replace-fail "mp.commandv('set', 'volume'" "mp.commandv('set', 'ao-volume'" \
|
||||
--replace-fail "mp.set_property_native('volume'" "mp.set_property('ao-volume'"
|
||||
|
||||
# `ao-volume` isn't actually an observable property.
|
||||
# as of 2024/03/02, they *may* be working on that:
|
||||
# - <https://github.com/mpv-player/mpv/pull/13604#issuecomment-1971665736>
|
||||
# in the meantime, just query the volume every tick (i.e. frame).
|
||||
# alternative is mpv's JSON IPC feature, where i could notify its socket whenever pipewire volume changes.
|
||||
cat <<EOF >> src/uosc/main.lua
|
||||
function update_ao_volume()
|
||||
local vol = mp.get_property('ao-volume')
|
||||
if vol ~= nil then
|
||||
vol = tonumber(vol)
|
||||
if vol ~= state.volume then
|
||||
set_state('volume', vol)
|
||||
request_render()
|
||||
end
|
||||
end
|
||||
end
|
||||
-- tick seems to occur on every redraw (even when volume is hidden).
|
||||
-- in practice: for every new frame of the source, or whenever the cursor is moved.
|
||||
mp.register_event('tick', update_ao_volume)
|
||||
-- if paused and cursor isn't moving, then `tick` isn't called. fallback to a timer.
|
||||
mp.add_periodic_timer(2, update_ao_volume)
|
||||
-- invoke immediately to ensure state.volume is non-nil
|
||||
update_ao_volume()
|
||||
if state.volume == nil then
|
||||
state.volume = 0
|
||||
end
|
||||
EOF
|
||||
'';
|
||||
});
|
||||
in
|
||||
{
|
||||
sane.programs.mpv = {
|
||||
packageUnwrapped = with pkgs; wrapMpv mpv-unwrapped {
|
||||
scripts = [
|
||||
mpvScripts.mpris
|
||||
mpvScripts.mpv-playlistmanager
|
||||
uosc
|
||||
# pkgs.mpv-uosc-latest
|
||||
];
|
||||
# extraMakeWrapperArgs = lib.optionals (cfg.config.vo != null) [
|
||||
# # 2023/08/29: fixes an error where mpv on moby launches with the message
|
||||
# # "DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory"
|
||||
# # audio still works, and controls, screenshotting, etc -- just not the actual rendering
|
||||
# #
|
||||
# # this is likely a regression for mpv 0.36.0.
|
||||
# # the actual error message *appears* to come from the mesa library, but it's tough to trace.
|
||||
# #
|
||||
# # 2024/03/02: no longer necessary, with mesa 23.3.1: <https://github.com/NixOS/nixpkgs/pull/265740>
|
||||
# #
|
||||
# # backend compatibility (2023/10/22):
|
||||
# # run with `--vo=help` to see a list of all output options.
|
||||
# # non-exhaustive (W=works, F=fails, A=audio-only, U=audio+ui only (no video))
|
||||
# # ? null Null video output
|
||||
# # A (default)
|
||||
# # A dmabuf-wayland Wayland dmabuf video output
|
||||
# # A libmpv render API for libmpv (mpv plays the audio, but doesn't even render a window)
|
||||
# # A vdpau VDPAU with X11
|
||||
# # F drm Direct Rendering Manager (software scaling)
|
||||
# # F gpu-next Video output based on libplacebo
|
||||
# # F vaapi VA API with X11
|
||||
# # F x11 X11 (software scaling)
|
||||
# # F xv X11/Xv
|
||||
# # U gpu Shader-based GPU Renderer
|
||||
# # W caca libcaca (terminal rendering)
|
||||
# # W sdl SDL 2.0 Renderer
|
||||
# # W wlshm Wayland SHM video output (software scaling)
|
||||
# "--add-flags" "--vo=${cfg.config.vo}"
|
||||
# ];
|
||||
};
|
||||
|
||||
suggestedPrograms = [
|
||||
"blast-to-default"
|
||||
"go2tv"
|
||||
"xdg-terminal-exec"
|
||||
];
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.autodetectCliPaths = true;
|
||||
sandbox.net = "all";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; #< mpris
|
||||
sandbox.whitelistDri = true; #< mpv has excellent fallbacks to non-DRI, but DRI offers a good 30%-50% reduced CPU
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.extraHomePaths = [
|
||||
".config/mpv" #< else mpris plugin crashes on launch
|
||||
".local/share/applications" #< for xdg-terminal-exec (go2tv)
|
||||
# it's common for album (or audiobook, podcast) images/lyrics/metadata to live adjacent to the primary file.
|
||||
# CLI detection is too poor to pick those up, so expose the common media dirs to the sandbox to make that *mostly* work.
|
||||
"Books/local"
|
||||
"Books/servo"
|
||||
"Music"
|
||||
"Videos/gPodder"
|
||||
"Videos/local"
|
||||
"Videos/servo"
|
||||
];
|
||||
|
||||
persist.byStore.plaintext = [
|
||||
# for `watch_later`
|
||||
".local/state/mpv"
|
||||
];
|
||||
fs.".config/mpv/scripts/sane/main.lua".symlink.target = ./sane-main.lua;
|
||||
fs.".config/mpv/input.conf".symlink.target = ./input.conf;
|
||||
fs.".config/mpv/mpv.conf".symlink.target = ./mpv.conf;
|
||||
fs.".config/mpv/script-opts/osc.conf".symlink.target = ./osc.conf;
|
||||
fs.".config/mpv/script-opts/console.conf".symlink.target = ./console.conf;
|
||||
fs.".config/mpv/script-opts/uosc.conf".symlink.target = ./uosc.conf;
|
||||
fs.".config/mpv/script-opts/playlistmanager.conf".symlink.target = ./playlistmanager.conf;
|
||||
|
||||
# mime.priority = 200; # default = 100; 200 means to yield to other apps
|
||||
mime.priority = 50; # default = 100; 50 in order to take precedence over vlc.
|
||||
mime.associations."audio/flac" = "mpv.desktop";
|
||||
mime.associations."audio/mpeg" = "mpv.desktop";
|
||||
mime.associations."audio/x-opus+ogg" = "mpv.desktop";
|
||||
mime.associations."audio/x-vorbis+ogg" = "mpv.desktop";
|
||||
mime.associations."video/mp4" = "mpv.desktop";
|
||||
mime.associations."video/quicktime" = "mpv.desktop";
|
||||
mime.associations."video/webm" = "mpv.desktop";
|
||||
mime.associations."video/x-flv" = "mpv.desktop";
|
||||
mime.associations."video/x-matroska" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(www.)?youtube.com/watch\?.*v=" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(www.)?youtube.com/v/" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(www.)?youtu.be/.+" = "mpv.desktop";
|
||||
};
|
||||
}
|
||||
|
@@ -1,37 +0,0 @@
|
||||
# docs:
|
||||
# - <https://mpv.io/manual/master/#list-of-input-commands>
|
||||
# - script-binding: <https://mpv.io/manual/master/#command-interface-script-binding>
|
||||
# - properties: <https://mpv.io/manual/master/#property-list>
|
||||
|
||||
# let volume/power keys be interpreted by the system.
|
||||
# this is important for sxmo.
|
||||
# mpv defaults is POWER = close, VOLUME_{UP,DOWN} = adjust application-level volume
|
||||
POWER ignore
|
||||
VOLUME_UP ignore
|
||||
VOLUME_DOWN ignore
|
||||
|
||||
# uosc menu
|
||||
# text after the shebang is parsed by uosc to construct the menu and names
|
||||
menu script-binding uosc/menu
|
||||
s script-binding uosc/subtitles #! Subtitles
|
||||
a script-binding uosc/audio #! Audio tracks
|
||||
q script-binding uosc/stream-quality #! Stream quality
|
||||
p script-binding uosc/items #! Playlist
|
||||
c script-binding uosc/chapters #! Chapters
|
||||
> script-binding uosc/next #! Navigation > Next
|
||||
< script-binding uosc/prev #! Navigation > Prev
|
||||
o script-binding uosc/open-file #! Navigation > Open file
|
||||
# set video-aspect-override "-1" #! Utils > Aspect ratio > Default
|
||||
# set video-aspect-override "16:9" #! Utils > Aspect ratio > 16:9
|
||||
# set video-aspect-override "4:3" #! Utils > Aspect ratio > 4:3
|
||||
# set video-aspect-override "2.35:1" #! Utils > Aspect ratio > 2.35:1
|
||||
# script-binding uosc/audio-device #! Utils > Audio devices
|
||||
# script-binding uosc/editions #! Utils > Editions
|
||||
ctrl+s async screenshot #! Utils > Screenshot
|
||||
alt+i script-binding uosc/keybinds #! Utils > Key bindings
|
||||
O script-binding uosc/show-in-directory #! Utils > Show in directory
|
||||
# script-binding uosc/open-config-directory #! Utils > Open config directory
|
||||
ctrl+r script-binding sane/blast #! Audiocast
|
||||
ctrl+t script-binding sane/go2tv-video #! Cast
|
||||
# script-binding sane/go2tv-stream #! Cast (...) > Stream
|
||||
# script-binding sane/go2tv-gui #! Cast (...) > GUI
|
@@ -1,26 +0,0 @@
|
||||
# write ~/.local/state/mpv/watch_later on exit, to allow resume
|
||||
save-position-on-quit=yes
|
||||
# identify resumed files by filename only, since i use so many symlinks and doubt mpv does well with that.
|
||||
ignore-path-in-watch-later-config
|
||||
|
||||
# keep-open: don't exit on completion of last file in playlist
|
||||
keep-open=yes
|
||||
# seeking once at the end of the file causes auto-resume
|
||||
keep-open-pause=no
|
||||
|
||||
# force GUI, even for tracks w/o album art
|
||||
# see: <https://www.reddit.com/r/mpv/comments/rvrrpt/oscosdgui_and_arch_linux/>
|
||||
player-operation-mode=pseudo-gui
|
||||
|
||||
# use uosc instead (for On Screen Controls)
|
||||
osc=no
|
||||
# uosc provides its own seeking/volume indicators, so you also don't need this
|
||||
osd-bar=no
|
||||
# uosc will draw its own window controls if you disable window border
|
||||
border=no
|
||||
|
||||
# ao=alsa so that uosc can work with ao-volume (see my uosc patch)
|
||||
ao=alsa
|
||||
# with `ao-volume`, the max actually is 100.
|
||||
# to go higher you'll have to use the system's native controls.
|
||||
volume-max=100
|
@@ -1,5 +0,0 @@
|
||||
# make the on-screen controls *always* visible
|
||||
# unfortunately, this applies to full-screen as well
|
||||
# - docs: <https://mpv.io/manual/master/#on-screen-controller-visibility>
|
||||
# if uosc is installed, this file is unused
|
||||
visibility=always
|
@@ -1,4 +0,0 @@
|
||||
# script docs: <https://github.com/jonniek/mpv-playlistmanager>
|
||||
|
||||
# auto-populate playlist with other files in the same directory, on launch.
|
||||
loadfiles_on_start=yes
|
@@ -1,34 +0,0 @@
|
||||
function subprocess(in_terminal, args)
|
||||
if in_terminal then
|
||||
args = { "xdg-terminal-exec", table.unpack(args) }
|
||||
end
|
||||
mp.command_native({
|
||||
name = "subprocess",
|
||||
args = args,
|
||||
detach = false,
|
||||
capture_stdout = false,
|
||||
capture_stderr = false,
|
||||
-- capture_size=0,
|
||||
passthrough_stdin = false,
|
||||
playback_only = false,
|
||||
})
|
||||
end
|
||||
|
||||
function invoke_go2tv(in_terminal, args)
|
||||
mp.commandv("set", "pause", "yes")
|
||||
subprocess(in_terminal, { "go2tv", table.unpack(args) })
|
||||
end
|
||||
|
||||
function invoke_go2tv_on_open_file(mode)
|
||||
local path = mp.get_property("stream-open-filename");
|
||||
return invoke_go2tv(true, { mode, path })
|
||||
end
|
||||
|
||||
mp.add_key_binding(nil, "blast", function() subprocess(false, { "blast-to-default" }) end)
|
||||
mp.add_key_binding(nil, 'go2tv-gui', function() invoke_go2tv(false, {}) end)
|
||||
mp.add_key_binding(nil, 'go2tv-video', function() invoke_go2tv_on_open_file("-v") end)
|
||||
mp.add_key_binding(nil, 'go2tv-stream', function() invoke_go2tv_on_open_file("-s") end)
|
||||
|
||||
-- uncomment for debugging:
|
||||
-- if mpv fails to eval this script (e.g. syntax error), then it will fail to quit on launch
|
||||
-- mp.command('quit')
|
@@ -1,32 +0,0 @@
|
||||
# docs:
|
||||
# - <https://github.com/tomasklaen/uosc>
|
||||
# - <https://github.com/tomasklaen/uosc/blob/main/src/uosc.conf>
|
||||
# - <https://superuser.com/questions/1775550/add-new-buttons-to-mpv-uosc-ui>
|
||||
timeline_style=bar
|
||||
timeline_line_width=4
|
||||
timeline_size=36
|
||||
timeline_persistency=paused,audio
|
||||
controls_persistency=paused,audio
|
||||
volume_persistency=audio
|
||||
|
||||
# speed_persistency=paused,audio
|
||||
# vvv want a close button?
|
||||
top_bar=always
|
||||
top_bar_persistency=paused,audio
|
||||
|
||||
controls=menu,<video>subtitles,<has_many_audio>audio,<has_many_video>video,<has_many_edition>editions,<stream>stream-quality,space,command:replay_10:seek -10,cycle:play_arrow:pause:no=pause/yes=play_arrow,command:forward_30:seek 30,space,speed:1.0,gap,<video>fullscreen
|
||||
|
||||
# text_border: shadow to place around icons/text which is rendered over the video
|
||||
text_border=5.0
|
||||
# border_radius: rounding of volume slider, etc.
|
||||
border_radius=5.0
|
||||
font_scale=1.5
|
||||
font_bold=yes
|
||||
# refine=text_width: slightly better text rendering
|
||||
refine=text_width
|
||||
color=foreground=ff8080,background_text=ff8080
|
||||
# N.B.: if `opacity=` is set non-empty, then ALL items must be specified (else they get 0 opacity).
|
||||
# opacity values *must* be a multiple of 0.1
|
||||
opacity=timeline=0.8,position=1,chapters=0.8,slider=0.8,slider_gauge=0.8,controls=0,speed=0.8,menu=1,submenu=0.4,border=1,title=0.8,tooltip=1,thumbnail=1,curtain=0.8,idle_indicator=0.8,audio_indicator=0.5,buffering_indicator=0.3,playlist_position=0.8
|
||||
|
||||
stream_quality_options=1440,1080,720,480,360,240,144
|
@@ -1,16 +1,16 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs."gnome.nautilus" = {
|
||||
# some of its dbus services don't even refer to real paths
|
||||
packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.gnome.nautilus.overrideAttrs (orig: {
|
||||
packageUnwrapped = pkgs.gnome.nautilus.overrideAttrs (orig: {
|
||||
# enable the "Audio and Video Properties" pane. see: <https://nixos.wiki/wiki/Nautilus>
|
||||
buildInputs = orig.buildInputs ++ (with pkgs.gst_all_1; [
|
||||
gst-plugins-good
|
||||
gst-plugins-bad
|
||||
]);
|
||||
}));
|
||||
});
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "inplace";
|
||||
sandbox.whitelistDbus = [ "user" ]; # for portals launching apps
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.extraHomePaths = [
|
||||
|
@@ -88,6 +88,7 @@ in
|
||||
{
|
||||
sane.programs.neovim = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.autodetectCliPaths = "existingOrParent";
|
||||
sandbox.whitelistWayland = true; # for system clipboard integration
|
||||
# sandbox.whitelistPwd = true;
|
||||
|
@@ -11,6 +11,7 @@
|
||||
});
|
||||
|
||||
sandbox.method = "firejail";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.net = "vpn";
|
||||
|
||||
|
@@ -3,6 +3,7 @@
|
||||
# provides `nix-locate`, backed by the manually run `nix-index`
|
||||
sane.programs.nix-index = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.extraPaths = [
|
||||
"/nix"
|
||||
|
@@ -2,6 +2,7 @@
|
||||
{
|
||||
sane.programs.notejot = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistWayland = true;
|
||||
suggestedPrograms = [ "dconf" ]; #< else it can't persist notes
|
||||
|
||||
|
@@ -21,6 +21,7 @@ in
|
||||
};
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.net = "clearnet";
|
||||
|
||||
secrets.".config/ntfy-sh/topic" = ../../../secrets/common/ntfy-sh-topic.bin;
|
||||
|
@@ -3,6 +3,7 @@
|
||||
{
|
||||
sane.programs.open-in-mpv = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistDbus = [ "user" ]; # for xdg-open/portals
|
||||
|
||||
# taken from <https://github.com/Baldomo/open-in-mpv>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# administer with pw-cli, pw-mon, pw-top commands
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.pipewire;
|
||||
in
|
||||
@@ -18,10 +18,6 @@ in
|
||||
sandbox.extraRuntimePaths = [ "/" ];
|
||||
sandbox.extraPaths = [
|
||||
"/dev/snd"
|
||||
# desko/lappy don't need these, but moby complains if not present
|
||||
"/dev/video0"
|
||||
"/dev/video1"
|
||||
"/dev/video2"
|
||||
];
|
||||
sandbox.extraHomePaths = [
|
||||
# pulseaudio cookie
|
||||
@@ -34,23 +30,10 @@ in
|
||||
wantedBy = [ "graphical-session.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/pipewire";
|
||||
ExecStartPost = pkgs.writeShellScript "pipewire-wait-started" ''
|
||||
waitFor() {
|
||||
while [ ! -e "$1" ]; do
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
waitFor "$XDG_RUNTIME_DIR/pipewire-0"
|
||||
waitFor "$XDG_RUNTIME_DIR/pipewire-0-manager"
|
||||
'';
|
||||
ExecStopPost = ''rm -f "$XDG_RUNTIME_DIR/{pipewire-0,pipewire-0.lock,pipewire-0-manager,pipewire-0-manager.lock}"'';
|
||||
Type = "simple";
|
||||
Restart = "always";
|
||||
RestartSec = "5s";
|
||||
};
|
||||
|
||||
# environment.PIPEWIRE_LOG_SYSTEMD = "false";
|
||||
# environment.PIPEWIRE_DEBUG = "*:3,mod.raop*:5,pw.rtsp-client*:5";
|
||||
};
|
||||
services.pipewire-pulse = {
|
||||
description = "pipewire-pulse: Pipewire compatibility layer for PulseAudio clients";
|
||||
@@ -58,16 +41,6 @@ in
|
||||
wantedBy = [ "pipewire.service" ];
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/pipewire-pulse";
|
||||
ExecStartPost = pkgs.writeShellScript "pipewire-pulse-wait-started" ''
|
||||
waitFor() {
|
||||
while [ ! -e "$1" ]; do
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
waitFor "$XDG_RUNTIME_DIR/pulse/native"
|
||||
waitFor "$XDG_RUNTIME_DIR/pulse/pid"
|
||||
'';
|
||||
ExecStopPost = ''rm -f "$XDG_RUNTIME_DIR/pulse/{native,pid}"'';
|
||||
Type = "simple";
|
||||
Restart = "always";
|
||||
RestartSec = "5s";
|
||||
|
@@ -2,6 +2,7 @@
|
||||
{
|
||||
sane.programs.planify = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistDbus = [ "user" ]; # for dconf? else it can't persist any tasks/notes
|
||||
sandbox.whitelistWayland = true;
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
{
|
||||
sane.programs.playerctl = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "inplace"; #< /lib/pkgconfig/playerctl.pc refers to $out by full path
|
||||
sandbox.wrapperType = "inplace";
|
||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||
|
||||
services.playerctld = {
|
||||
|
@@ -3,6 +3,7 @@
|
||||
sane.programs.portfolio-filemanager = {
|
||||
# this is all taken pretty directly from nautilus config
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "inplace";
|
||||
sandbox.whitelistDbus = [ "user" ]; # for portals launching apps
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.extraHomePaths = [
|
||||
|
@@ -2,6 +2,7 @@
|
||||
{
|
||||
sane.programs.ripgrep = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.autodetectCliPaths = true;
|
||||
sandbox.whitelistPwd = true;
|
||||
sandbox.extraHomePaths = [
|
||||
|
@@ -6,112 +6,33 @@
|
||||
|
||||
configuration {
|
||||
modes: "combi";
|
||||
font: "mono 20";
|
||||
show-icons: true;
|
||||
combi-modes: "filebrowser,drun,run";
|
||||
kb-accept-entry: "Return,KP_Enter,XF86PowerOff";
|
||||
kb-row-up: "Up,XF86AudioRaiseVolume";
|
||||
kb-row-down: "Down,XF86AudioLowerVolume";
|
||||
cache-dir: "~/.cache/rofi";
|
||||
|
||||
/* to position rofi to the top of the screen: */
|
||||
/* location: 2; */
|
||||
|
||||
combi {
|
||||
/* this is rendered in the filter box, here we disable it */
|
||||
display-name: "";
|
||||
}
|
||||
/* combi-display-format: "{mode} {text}"; */
|
||||
/* combi-display-format: "{text}"; */
|
||||
combi-display-format: "{mode}{text}";
|
||||
combi-modes: "filebrowser,drun";
|
||||
|
||||
drun {
|
||||
display-name: " ";
|
||||
}
|
||||
drun-use-desktop-cache: true;
|
||||
|
||||
filebrowser {
|
||||
/* directory: filebrowser starting directory. leave unset to start at the last directory. */
|
||||
/* directory: "/home"; */
|
||||
|
||||
/* filebrowser starting directory */
|
||||
directory: "/home";
|
||||
/* display-name: text to prepend in combi mode */
|
||||
display-name: "/";
|
||||
/* `command` is the prefix to prepend (along with a space) *before* passing it off to `run-command` */
|
||||
command: "xdg-open";
|
||||
directories-first: true;
|
||||
/* sorting-method: name/atime/ctime/mtime */
|
||||
sorting-method: "name";
|
||||
show-hidden: false;
|
||||
}
|
||||
|
||||
drun {
|
||||
display-name: " ";
|
||||
}
|
||||
run {
|
||||
display-name: "run ";
|
||||
}
|
||||
/* combi-display-format: "{mode} {text}"; */
|
||||
/* combi-display-format: "{text}"; */
|
||||
combi-display-format: "{mode}{text}";
|
||||
|
||||
/* launch applications via my own launcher, which directs them through to xdg-desktop-portal */
|
||||
run-command: "rofi-run-command '{app_id}.desktop' {cmd}";
|
||||
|
||||
drun-use-desktop-cache: true;
|
||||
}
|
||||
|
||||
/* theme */
|
||||
* {
|
||||
/* my own variables */
|
||||
bg: #1d1721; /* slight purple */
|
||||
fg0: #d8d8d8; /* inactive text (light grey) */
|
||||
fg1: #ffffff; /* active text (white) */
|
||||
accent0: #1f5e54; /* darker but saturated teal */
|
||||
accent1: #418379; /* teal (matches nixos-bg) */
|
||||
accent2: #5b938a; /* brighter but muted teal */
|
||||
|
||||
/* map my variables to variables rofi uses internally */
|
||||
background-color: var(accent0);
|
||||
background: var(accent0);
|
||||
/* foreground: non-alternating text, scrollbar, borders, separators */
|
||||
foreground: var(fg0);
|
||||
|
||||
/* override derived styles */
|
||||
alternate-active-background: var(accent0);
|
||||
alternate-normal-background: var(accent0);
|
||||
alternate-active-foreground: var(fg0);
|
||||
alternate-normal-foreground: var(fg0);
|
||||
border-color: var(accent0);
|
||||
text-color: var(fg0);
|
||||
selected-active-background: var(accent1);
|
||||
selected-normal-background: var(accent1);
|
||||
selected-active-foreground: var(fg1);
|
||||
selected-normal-foreground: var(fg1);
|
||||
separatorcolor: var(accent1);
|
||||
}
|
||||
entry {
|
||||
placeholder: "";
|
||||
text-color: var(fg1);
|
||||
}
|
||||
num-rows, num-filtered-rows {
|
||||
text-color: var(fg0);
|
||||
}
|
||||
prompt, textbox-prompt-colon {
|
||||
/* hide */
|
||||
text-color: var(accent0);
|
||||
}
|
||||
scrollbar {
|
||||
handle-color: var(accent2);
|
||||
}
|
||||
window {
|
||||
/* rofi supports very complex calculations here */
|
||||
/* one may even read environment variables (useful for knowing if screen is rotated?) */
|
||||
/* `calc(... min 100%)` ensures it never overflows */
|
||||
/* rofi is aware of the top bar (waybar) and any virtual keyboards,
|
||||
* so e.g. height: 100% will occupy 100% of the height *not* allocated to bars/kbds.
|
||||
* however with y-offset, it becomes possible to overflow */
|
||||
|
||||
width: calc(960 min 100%);
|
||||
/* 520px @ font size 20 gives 13 rows + filter */
|
||||
/* 482px @ font size 20 gives 12 rows + filter */
|
||||
/* 446px @ font size 20 gives 11 rows + filter */
|
||||
/* 90.5% @ font size 20, sway scale 2.0, moby in landscape mode: gives 7 rows + filter */
|
||||
height: calc(446 min 90.5%);
|
||||
|
||||
/* anchor the *north* edge of the window at the *north* location of the screen */
|
||||
anchor: north;
|
||||
location: north;
|
||||
/* 11.2% lines up nicely with Firefox */
|
||||
y-offset: 11.2%;
|
||||
}
|
||||
@theme "gruvbox-light"
|
||||
|
@@ -25,27 +25,28 @@
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
rofi-unwrapped = pkgs.rofi-wayland-unwrapped.overrideAttrs (upstream: {
|
||||
patches = (upstream.patches or []) ++ [
|
||||
(pkgs.fetchpatch {
|
||||
url = "https://git.uninsane.org/colin/rofi/commit/8e01fcd16f97f4c2a5bc63ade58c894a938f89d9.patch";
|
||||
name = "run-{shell-,}command: expand `{app_id}` inside the template string";
|
||||
hash = "sha256-DXafvvKrNyDOH11lpRdC2ljydb422ttY68oY5K3fKWo=";
|
||||
})
|
||||
(pkgs.fetchpatch {
|
||||
url = "https://git.uninsane.org/colin/rofi/commit/249450a2b58c3cf7ced911cadb8c4c60d3315dd0.patch";
|
||||
name = "filebrowser: include entries of d_type DT_UNKNOWN";
|
||||
hash = "sha256-gz3N4uo7IWzzqaPHHVhby/e9NbtzcFJRQwgdNYxO/Yw=";
|
||||
})
|
||||
];
|
||||
# my patches made for tip don't cleanly apply to stable, so advance the entire src
|
||||
src = pkgs.fetchFromGitea {
|
||||
domain = "git.uninsane.org";
|
||||
owner = "colin";
|
||||
repo = "rofi";
|
||||
fetchSubmodules = true;
|
||||
# rev = "dev-sane"; #< fetchFromGitea doesn't support tags (?)
|
||||
rev = "1edfceaeefa2cae971ae90dc55811f3b7592a1b4";
|
||||
hash = "sha256-oIWLwec1LRsss12S92ebBWQk14FBJWc6QcYxzOU3eFI=";
|
||||
};
|
||||
# patches = (upstream.patches or []) ++ [
|
||||
# (pkgs.fetchpatch {
|
||||
# url = "https://git.uninsane.org/colin/rofi/commit/d8bb0b9944ec1f3bf7479c9f127ec09d4198e87f.patch";
|
||||
# name = "run-{shell-,}command: expand `{app_id}` inside the template string";
|
||||
# hash = "sha256-XiZRvr+BARU7h3OPU0NUUEem3isnUVER69zucSqvNNk=";
|
||||
# })
|
||||
# ];
|
||||
});
|
||||
# rofi-emoji = pkgs.rofi-emoji.override {
|
||||
# # plugins must be compiled against the same rofi they're loaded by
|
||||
# inherit rofi-unwrapped;
|
||||
# };
|
||||
# rofi-file-browser = pkgs.rofi-file-browser.override {
|
||||
# # plugins must be compiled against the same rofi they're loaded by
|
||||
# rofi = rofi-unwrapped;
|
||||
# };
|
||||
in
|
||||
{
|
||||
sane.programs.rofi = {
|
||||
@@ -53,15 +54,8 @@ in
|
||||
# it's actively maintained though, and more of an overlay than a true fork.
|
||||
packageUnwrapped = pkgs.rofi-wayland.override {
|
||||
inherit rofi-unwrapped;
|
||||
plugins = [
|
||||
# rofi-[extended-]-file-browser: <https://github.com/marvinkreis/rofi-file-browser-extended>
|
||||
# because the builtin rofi filebrowser only partially lists ~/Videos/servo/Shows, seemingly at random.
|
||||
# but rofi-file-browser doesn't compile against my patched rofi (oops)
|
||||
# rofi-file-browser
|
||||
|
||||
# rofi-emoji: "insert" mode doesn't work; use a wrapper like `splatmoji` instead.
|
||||
# rofi-emoji
|
||||
];
|
||||
# plugins = [ rofi-emoji ];
|
||||
};
|
||||
|
||||
suggestedPrograms = [
|
||||
@@ -69,12 +63,11 @@ in
|
||||
];
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.wrapperType = "wrappedDerivation";
|
||||
sandbox.whitelistDbus = [ "user" ]; #< to launch apps via the portal
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.extraHomePaths = [
|
||||
".local/share/applications" #< to locate .desktop files
|
||||
"Books/local"
|
||||
"Books/servo"
|
||||
"Music"
|
||||
"Pictures/albums"
|
||||
"Pictures/cat"
|
||||
@@ -82,22 +75,18 @@ in
|
||||
"Pictures/Photos"
|
||||
"Pictures/Screenshots"
|
||||
"Pictures/servo-macros"
|
||||
"Videos/gPodder"
|
||||
"Videos/local"
|
||||
"Videos/servo"
|
||||
"knowledge"
|
||||
"tmp"
|
||||
];
|
||||
sandbox.extraPaths = [
|
||||
"/mnt/servo/media"
|
||||
"/mnt/servo/playground"
|
||||
];
|
||||
|
||||
fs.".config/rofi/config.rasi".symlink.target = ./config.rasi;
|
||||
# redirect its default drun cache location
|
||||
fs.".cache/rofi3.druncache".symlink.target = "rofi/rofi3.druncache";
|
||||
fs.".cache/rofi-drun-desktop.cache".symlink.target = "rofi/rofi-drun-desktop.cache";
|
||||
persist.byStore.cryptClearOnBoot = [
|
||||
# this gets us a few things:
|
||||
# - file browser remembers its last directory
|
||||
# - caching of .desktop files (perf)
|
||||
# optional, for caching .desktop files rofi finds on disk (perf)
|
||||
".cache/rofi"
|
||||
];
|
||||
};
|
||||
@@ -106,47 +95,13 @@ in
|
||||
packageUnwrapped = pkgs.static-nix-shell.mkBash {
|
||||
pname = "rofi-run-command";
|
||||
srcRoot = ./.;
|
||||
pkgs = [ "sane-open-desktop" "xdg-utils" ];
|
||||
pkgs = [ "glib" "xdg-utils" ];
|
||||
};
|
||||
sandbox.enable = false; #< trivial script, and all our deps are sandboxed
|
||||
|
||||
suggestedPrograms = [
|
||||
"sane-open-desktop"
|
||||
"gdbus"
|
||||
"xdg-utils"
|
||||
];
|
||||
};
|
||||
|
||||
sane.programs.rofi-snippets = {
|
||||
packageUnwrapped = pkgs.static-nix-shell.mkBash {
|
||||
pname = "rofi-snippets";
|
||||
srcRoot = ./.;
|
||||
pkgs = [
|
||||
"gnused"
|
||||
"rofi"
|
||||
"wtype"
|
||||
];
|
||||
nativeBuildInputs = [
|
||||
pkgs.copyDesktopItems
|
||||
];
|
||||
desktopItems = [
|
||||
(pkgs.makeDesktopItem {
|
||||
name = "rofi-snippets";
|
||||
exec = "rofi-snippets";
|
||||
desktopName = "rofi macro to insert common texts";
|
||||
})
|
||||
];
|
||||
};
|
||||
# if i could remove the sed, then maybe possible to not sandbox.
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.extraHomePaths = [
|
||||
".cache/rofi"
|
||||
".config/rofi/config.rasi"
|
||||
];
|
||||
|
||||
suggestedPrograms = [ "rofi" ];
|
||||
|
||||
fs.".config/rofi-snippets/public.txt".symlink.target = ./snippets.txt;
|
||||
secrets.".config/rofi-snippets/private.txt" = ../../../../secrets/common/snippets.txt.bin;
|
||||
};
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p sane-open-desktop -p xdg-utils
|
||||
#!nix-shell -i bash -p glib -p xdg-utils
|
||||
|
||||
# use:
|
||||
# rofi-run-command <handler>.desktop [cmd [args ...]]
|
||||
@@ -14,13 +14,11 @@ shift
|
||||
binArgs=("$@")
|
||||
|
||||
if [ "$desktop" != .desktop ]; then
|
||||
# launching an app; the file browser position is no longer interesting: clear it so it opens in ~ next time.
|
||||
# better UX would be to manage this in the other branch:
|
||||
# - open in ~ by default, regardless of last directory
|
||||
# - after launching a *file*, when that file is closed, re-open rofi in that file's directory.
|
||||
# however, `xdg-open` and the `OpenFile` xdg-desktop-portal API don't give any obvious way to block for the app to close.
|
||||
rm -f ~/.cache/rofi/rofi3.filebrowsercache
|
||||
exec sane-open-desktop "$desktop"
|
||||
exec gdbus call --session --timeout 10 \
|
||||
--dest org.freedesktop.portal.Desktop \
|
||||
--object-path /org/freedesktop/portal/desktop \
|
||||
--method org.freedesktop.portal.DynamicLauncher.Launch \
|
||||
"$desktop" {}
|
||||
elif [ "$binary" = "xdg-open" ]; then
|
||||
exec xdg-open "$@"
|
||||
fi
|
||||
|
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p gnused -p rofi -p wtype
|
||||
|
||||
# "bookmarking"/snippets inspired by Luke Smith:
|
||||
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
|
||||
|
||||
snippet=$(cat ~/.config/rofi-snippets/public.txt ~/.config/rofi-snippets/private.txt | \
|
||||
rofi -dmenu | \
|
||||
sed 's/ #.*$//')
|
||||
wtype "$snippet"
|
@@ -1,197 +0,0 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.sane-input-handler;
|
||||
doExec = inputName: transitions: {
|
||||
type = "exec";
|
||||
command = [
|
||||
"setsid" "-f" #< fork before invoking the input handler, else it runs synchronously
|
||||
"sane-input-handler"
|
||||
inputName
|
||||
];
|
||||
inherit transitions;
|
||||
};
|
||||
onDelay = ms: transitions: {
|
||||
type = "delay";
|
||||
delay_duration = ms * 1000000;
|
||||
inherit transitions;
|
||||
};
|
||||
onEvent = eventName: transitions: {
|
||||
type = "event";
|
||||
event_name = eventName;
|
||||
inherit transitions;
|
||||
};
|
||||
friendlyToBonsai = { trigger ? null, terminal ? false, timeout ? {}, power_pressed ? {}, power_released ? {}, voldown_pressed ? {}, voldown_released ? {}, volup_pressed ? {}, volup_released ? {} }@args:
|
||||
if trigger != null then [
|
||||
(doExec trigger (friendlyToBonsai (builtins.removeAttrs args ["trigger"])))
|
||||
] else let
|
||||
events = [ ]
|
||||
++ (lib.optional (timeout != {}) (onDelay (timeout.ms or 400) (friendlyToBonsai (builtins.removeAttrs timeout ["ms"]))))
|
||||
++ (lib.optional (power_pressed != {}) (onEvent "power_pressed" (friendlyToBonsai power_pressed)))
|
||||
++ (lib.optional (power_released != {}) (onEvent "power_released" (friendlyToBonsai power_released)))
|
||||
++ (lib.optional (voldown_pressed != {}) (onEvent "voldown_pressed" (friendlyToBonsai voldown_pressed)))
|
||||
++ (lib.optional (voldown_released != {}) (onEvent "voldown_released" (friendlyToBonsai voldown_released)))
|
||||
++ (lib.optional (volup_pressed != {}) (onEvent "volup_pressed" (friendlyToBonsai volup_pressed)))
|
||||
++ (lib.optional (volup_released != {}) (onEvent "volup_released" (friendlyToBonsai volup_released)))
|
||||
;
|
||||
in assert terminal -> events == []; events;
|
||||
|
||||
# trigger ${button}_hold_N every `holdTime` ms until ${button} is released
|
||||
recurseHold = button: { count ? 1, maxHolds ? 5, prefix ? "", holdTime ? 600, ... }@opts: lib.optionalAttrs (count <= maxHolds) {
|
||||
"${button}_released".terminal = true; # end the hold -> back to root state
|
||||
timeout = {
|
||||
ms = holdTime;
|
||||
trigger = "${prefix}${button}_hold_${builtins.toString count}";
|
||||
} // (recurseHold button (opts // { count = count+1; }));
|
||||
};
|
||||
|
||||
# trigger volup_tap_N or voldown_tap_N on every tap.
|
||||
# if a volume button is held, then switch into `recurseHold`'s handling instead
|
||||
volumeActions = { count ? 1, maxTaps ? 5, prefix ? "", timeout ? 600, ... }@opts: lib.optionalAttrs (count != maxTaps) {
|
||||
volup_pressed = (recurseHold "volup" opts) // {
|
||||
volup_released = {
|
||||
trigger = "${prefix}volup_tap_${builtins.toString count}";
|
||||
timeout.ms = timeout;
|
||||
} // (volumeActions (opts // { count = count+1; }));
|
||||
};
|
||||
voldown_pressed = (recurseHold "voldown" opts) // {
|
||||
voldown_released = {
|
||||
trigger = "${prefix}voldown_tap_${builtins.toString count}";
|
||||
timeout.ms = timeout;
|
||||
} // (volumeActions (opts // { count = count+1; }));
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
sane.programs.sane-input-handler = {
|
||||
configOption = with lib; mkOption{
|
||||
default = {};
|
||||
type = types.submodule {
|
||||
options = {
|
||||
devices = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
"0:1:Power_Button" #< Thinkpad power button
|
||||
"1:1:AT_Translated_Set_2_keyboard" #< Thinkpad volume buttons (plus all its other buttons)
|
||||
"0:0:axp20x-pek" #< Pinephone power button
|
||||
"1:1:1c21800.lradc" #< Pinephone volume buttons
|
||||
];
|
||||
description = ''
|
||||
list of devices which we should listen for special inputs from.
|
||||
find these names with:
|
||||
`swaymsg -t get_inputs --raw | jq 'map(.identifier)'`
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
packageUnwrapped = pkgs.static-nix-shell.mkBash {
|
||||
pname = "sane-input-handler";
|
||||
srcRoot = ./.;
|
||||
pkgs = {
|
||||
inherit (pkgs) coreutils killall playerctl procps sane-open-desktop util-linux wireplumber;
|
||||
sway = config.sane.programs.sway.package.sway-unwrapped;
|
||||
};
|
||||
};
|
||||
suggestedPrograms = [
|
||||
"bonsai"
|
||||
"killall"
|
||||
"playerctl"
|
||||
"procps"
|
||||
"sane-open-desktop"
|
||||
"sway"
|
||||
"wireplumber"
|
||||
"wvkbd"
|
||||
];
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; #< to launch applications
|
||||
sandbox.extraRuntimePaths = [ "sway-ipc.sock" ];
|
||||
sandbox.extraConfig = [
|
||||
"--sane-sandbox-keep-namespace" "pid"
|
||||
];
|
||||
};
|
||||
|
||||
# sane.programs.actkbd = {
|
||||
# fs.".config/actkbd/actkbd.conf".symlink.text = ''
|
||||
# 114:key::bonsaictl -e voldown_pressed
|
||||
# 114:rel::bonsaictl -e voldown_released
|
||||
# 115:key::bonsaictl -e volup_pressed
|
||||
# 115:rel::bonsaictl -e volup_released
|
||||
# # note that power might be on its own /dev/input/event* device separate from the volume buttons
|
||||
# 116:key::bonsaictl -e power_pressed
|
||||
# 116:rel::bonsaictl -e power_released
|
||||
# '';
|
||||
# services.actkbd = {
|
||||
# # TODO: apparently i need one instance per /dev/input, which also means i need udev, etc.
|
||||
# description = "actkbd: keyboard input mapping";
|
||||
# after = [ "graphical-session.target" ];
|
||||
# wantedBy = [ "graphical-session.target" ];
|
||||
|
||||
# serviceConfig = {
|
||||
# ExecStart = "${config.sane.programs.actkbd.package}/bin/actkbd -c /home/colin/.config/actkbd/actkbd.conf";
|
||||
# Type = "simple";
|
||||
# Restart = "always";
|
||||
# RestartSec = "5s";
|
||||
# };
|
||||
# };
|
||||
# };
|
||||
|
||||
# TODO: duplicated sandboxing here is just ugly
|
||||
sane.programs.bonsai.sandbox = lib.mkIf cfg.enabled (
|
||||
builtins.removeAttrs cfg.sandbox [ "method" ] #< else infinite recursion
|
||||
);
|
||||
sane.programs.bonsai.config.transitions = lib.mkIf cfg.enabled (friendlyToBonsai {
|
||||
# map sequences of "events" to an argument to pass to sane-input-handler
|
||||
|
||||
# map: power (short), power (short) x2, power (long)
|
||||
power_pressed.timeout.ms = 900; # press w/o release. this is a long timeout because it's tied to the "kill window" action.
|
||||
power_pressed.timeout.trigger = "powerhold";
|
||||
power_pressed.power_released.timeout.trigger = "powerbutton_one";
|
||||
power_pressed.power_released.timeout.ms = 300;
|
||||
power_pressed.power_released.power_pressed.trigger = "powerbutton_two";
|
||||
# map power (short) -> volup/voldown
|
||||
power_pressed.power_released.volup_pressed.trigger = "powerbutton_volup";
|
||||
power_pressed.power_released.voldown_pressed.trigger = "powerbutton_voldown";
|
||||
|
||||
# map: volume taps and holds
|
||||
volup_pressed = (recurseHold "volup" {}) // {
|
||||
# this either becomes volup_hold_* (via recurseHold, above) or:
|
||||
# - a short volup_tap_1 followed by:
|
||||
# - a *finalized* volup_1 (i.e. end of action)
|
||||
# - more taps/holds, in which case we prefix it with `modal_<action>`
|
||||
# to denote that we very explicitly entered this state.
|
||||
#
|
||||
# it's clunky: i do it this way so that voldown can map to keyboard/terminal in unlock mode
|
||||
# but trigger media controls in screenoff
|
||||
# in a way which *still* allows media controls if explicitly entered into via a tap on volup first
|
||||
volup_released = (volumeActions { prefix = "modal_"; }) // {
|
||||
trigger = "volup_tap_1";
|
||||
timeout.ms = 300;
|
||||
timeout.trigger = "volup_1";
|
||||
};
|
||||
};
|
||||
voldown_pressed = (volumeActions {}).voldown_pressed // {
|
||||
trigger = "voldown_start";
|
||||
};
|
||||
});
|
||||
|
||||
sane.programs.sway.config.extra_lines = lib.mkIf cfg.enabled (
|
||||
''
|
||||
# bindsym --input-device=... :
|
||||
# i wish to route certain events both to bonsai and/or the application
|
||||
# (rather, if there is an application which would receive them, send them, else re-route to bonsai).
|
||||
# bindsym --input-device=* SWALLOWS all events: it sends them to bonsaid and NOT any gui app.
|
||||
# bindsym --input-device=<dev> DOESN'T SWALLOW EVENTS: it sends them to BOTH places (yay!).
|
||||
# however, this is considered a BUG. i am relying on a bug here that may be fixed in future sway versions:
|
||||
# - <https://github.com/swaywm/sway/issues/6961>
|
||||
# if so, migrate this to an evdev daemon, such as `evdevremapkeys` or `actkbd`
|
||||
'' + lib.concatStringsSep "\n" (lib.forEach cfg.config.devices (dev: ''
|
||||
bindsym --locked --input-device=${dev} --no-repeat XF86PowerOff exec bonsaictl -e power_pressed
|
||||
bindsym --locked --input-device=${dev} --release XF86PowerOff exec bonsaictl -e power_released
|
||||
bindsym --locked --input-device=${dev} --no-repeat XF86AudioRaiseVolume exec bonsaictl -e volup_pressed
|
||||
bindsym --locked --input-device=${dev} --release XF86AudioRaiseVolume exec bonsaictl -e volup_released
|
||||
bindsym --locked --input-device=${dev} --no-repeat XF86AudioLowerVolume exec bonsaictl -e voldown_pressed
|
||||
bindsym --locked --input-device=${dev} --release XF86AudioLowerVolume exec bonsaictl -e voldown_released
|
||||
''))
|
||||
);
|
||||
}
|
@@ -1,226 +0,0 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils -p killall -p playerctl -p procps -p sane-open-desktop -p sway -p util-linux -p wireplumber
|
||||
|
||||
# input map considerations
|
||||
# - using compound actions causes delays.
|
||||
# e.g. if volup->volup is a distinct action from volup, then single-volup action is forced to wait the maximum button delay.
|
||||
# - actions which are to be responsive should therefore have a dedicated key.
|
||||
# - a dedicated "kill" combo is important for unresponsive fullscreen apps, because appmenu doesn't show in those
|
||||
# - although better may be to force appmenu to show over FS apps
|
||||
# - bonsai mappings are static, so buttons can't benefit from non-compounding unless they're mapped accordingly for all lock states
|
||||
# - this limitation could be removed, but with work
|
||||
#
|
||||
# example of a design which considers these things:
|
||||
# - when unlocked:
|
||||
# - volup toggle -> app menu
|
||||
# - voldown press -> keyboard
|
||||
# - voldown hold -> terminal
|
||||
# - power x2 -> screenoff
|
||||
# - hold power -> kill app
|
||||
# - when locked:
|
||||
# - volup tap -> volume up
|
||||
# - volup hold -> media seek forward
|
||||
# - voldown tap -> volume down
|
||||
# - voldown hold -> media seek backward
|
||||
# - power x1 -> screen on
|
||||
# - power x2 -> play/pause media
|
||||
# some trickiness allows for media controls in unlocked mode:
|
||||
# - volup tap -> enter media mode
|
||||
# - i.e. in this state, vol tap/hold is mapped to volume/seek
|
||||
# - if, after entering media mode, no more taps occur, then we trigger the default app-menu action
|
||||
# limitations/downsides:
|
||||
# - power mappings means phone is artificially slow to unlock.
|
||||
# - media controls when unlocked have quirks:
|
||||
# - mashing voldown to decrease the volume will leave you with a toggled keyboard.
|
||||
# - seeking backward isn't possible except by first tapping volup.
|
||||
|
||||
|
||||
# increments to use for volume adjustment (in %)
|
||||
VOL_INCR=5
|
||||
KEYBOARD="${KEYBOARD:-wvkbd-mobintl}"
|
||||
|
||||
action="$1"
|
||||
|
||||
isTouchOn() {
|
||||
# success if all touch inputs have their events enabled
|
||||
swaymsg -t get_inputs --raw \
|
||||
| jq --exit-status '. | map(select(.type == "touch")) | all(.libinput.send_events == "enabled")' \
|
||||
> /dev/null
|
||||
}
|
||||
isScreenOn() {
|
||||
# success if all outputs have power
|
||||
swaymsg -t get_outputs --raw \
|
||||
| jq --exit-status '. | all(.power)' \
|
||||
> /dev/null
|
||||
}
|
||||
|
||||
isAllOn() {
|
||||
isTouchOn && isScreenOn
|
||||
}
|
||||
|
||||
isInhibited() {
|
||||
pidof rofi
|
||||
}
|
||||
|
||||
|
||||
ignore() {
|
||||
true
|
||||
}
|
||||
inhibited() {
|
||||
true
|
||||
}
|
||||
unmapped() {
|
||||
true
|
||||
}
|
||||
|
||||
allOn() {
|
||||
swaymsg -- output '*' power true
|
||||
swaymsg -- input type:touch events enabled
|
||||
}
|
||||
allOff() {
|
||||
swaymsg -- output '*' power false
|
||||
swaymsg -- input type:touch events disabled
|
||||
}
|
||||
|
||||
toggleKeyboard() {
|
||||
local kbpid=$(pidof "$KEYBOARD")
|
||||
if [ -z "$kbpid" ] || ! ( env kill -s RTMIN+0 "$kbpid" ); then
|
||||
echo "sane-input-handler: failed to toggle keyboard: $KEYBOARD"
|
||||
fi
|
||||
}
|
||||
|
||||
handleWith() {
|
||||
state=
|
||||
if [ -n "$_isInhibited" ]; then
|
||||
state="inhibited+"
|
||||
fi
|
||||
if [ -n "$_isAllOn" ]; then
|
||||
state="${state}on"
|
||||
else
|
||||
state="${state}off"
|
||||
fi
|
||||
echo "sane-input-handler: state=$state action=$action: handleWith: $@"
|
||||
"$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
dispatchDefault() {
|
||||
case "$action" in
|
||||
"powerbutton_one")
|
||||
# power once => unlock
|
||||
handleWith allOn
|
||||
;;
|
||||
"powerbutton_two")
|
||||
# power twice => screenoff
|
||||
handleWith allOff
|
||||
;;
|
||||
# powerbutton_three: intentional no-op because overloading the kill-window handler is risky
|
||||
|
||||
volup_tap*|modal_volup_tap*)
|
||||
handleWith wpctl set-volume @DEFAULT_AUDIO_SINK@ "$VOL_INCR"%+
|
||||
;;
|
||||
voldown_tap*|modal_voldown_tap*)
|
||||
handleWith wpctl set-volume @DEFAULT_AUDIO_SINK@ "$VOL_INCR"%-
|
||||
;;
|
||||
|
||||
volup_hold*|modal_volup_hold*)
|
||||
handleWith playerctl position 30+
|
||||
;;
|
||||
voldown_hold*|modal_voldown_hold*)
|
||||
handleWith playerctl position 10-
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
dispatchOff() {
|
||||
case "$action" in
|
||||
"powerbutton_two")
|
||||
# power twice => toggle media player
|
||||
handleWith playerctl play-pause
|
||||
;;
|
||||
"powerhold")
|
||||
# power toggle during deep sleep often gets misread as power hold, so treat same
|
||||
handleWith allOn
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
dispatchOn() {
|
||||
case "$action" in
|
||||
# powerbutton_one: intentional default to no-op
|
||||
# powerbutton_two: intentional default to screenoff
|
||||
"powerhold")
|
||||
# power thrice: kill active window
|
||||
# TODO: disable this if locked (with e.g. schlock, swaylock, etc)
|
||||
handleWith swaymsg kill
|
||||
;;
|
||||
"powerbutton_volup")
|
||||
# power (tap) -> volup: rotate CCW
|
||||
handleWith swaymsg -- output '-' transform 90 anticlockwise
|
||||
;;
|
||||
"powerbutton_voldown")
|
||||
# power (tap) -> voldown: rotate CW
|
||||
handleWith swaymsg -- output '-' transform 90 clockwise
|
||||
;;
|
||||
|
||||
"volup_tap_1")
|
||||
# swallow: this could be the start to a media control (multi taps / holds),
|
||||
# or it could be just a single tap -> release, handled next/below
|
||||
handleWith ignore
|
||||
;;
|
||||
"volup_1")
|
||||
# volume up once: system menu
|
||||
handleWith sane-open-desktop rofi.desktop
|
||||
;;
|
||||
|
||||
"voldown_start")
|
||||
# volume down once: toggle keyboard
|
||||
handleWith toggleKeyboard
|
||||
;;
|
||||
"voldown_hold_2")
|
||||
# hold voldown to launch terminal
|
||||
# note we already triggered the keyboard; that's fine: usually keyboard + terminal go together :)
|
||||
# voldown_hold_1 frequently triggers during short taps meant only to reveal the keyboard,
|
||||
# so prefer a longer hold duration
|
||||
handleWith sane-open-desktop xdg-terminal-exec.desktop
|
||||
;;
|
||||
"voldown_tap_1")
|
||||
# swallow, to prevent keyboard from also triggering media controls
|
||||
handleWith ignore
|
||||
;;
|
||||
voldown_hold_*)
|
||||
# swallow, to prevent terminal from also triggering media controls
|
||||
handleWith ignore
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
dispatchInhibited() {
|
||||
case "$action" in
|
||||
"powerhold")
|
||||
# power thrice: escape hatch in case rofi has hung
|
||||
handleWith killall -9 rofi
|
||||
;;
|
||||
*)
|
||||
# eat everything else (and let rofi consume it)
|
||||
handleWith inhibited
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_isAllOn="$(isAllOn && echo 1 || true)"
|
||||
_isInhibited="$(isInhibited && echo 1 || true)"
|
||||
|
||||
if [ -n "$_isInhibited" ]; then
|
||||
dispatchInhibited
|
||||
fi
|
||||
|
||||
if [ -n "$_isAllOn" ]; then
|
||||
dispatchOn
|
||||
else
|
||||
dispatchOff
|
||||
fi
|
||||
|
||||
dispatchDefault
|
||||
|
||||
handleWith unmapped
|
@@ -1,18 +0,0 @@
|
||||
{ ... }:
|
||||
{
|
||||
sane.programs.sane-screenshot = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; #< to send notifications
|
||||
sandbox.extraHomePaths = [
|
||||
"Pictures/Screenshots"
|
||||
];
|
||||
suggestedPrograms = [
|
||||
"libnotify"
|
||||
"swappy"
|
||||
"sway-contrib.grimshot"
|
||||
"util-linux"
|
||||
"wl-clipboard"
|
||||
];
|
||||
};
|
||||
}
|
@@ -54,6 +54,7 @@ in
|
||||
|
||||
"sane-scripts.bt-add".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
net = "clearnet";
|
||||
# TODO: migrate `transmission_passwd` to `secrets` api
|
||||
extraPaths = [ "/run/secrets/transmission_passwd" ];
|
||||
@@ -61,6 +62,7 @@ in
|
||||
|
||||
"sane-scripts.bt-rm".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
net = "clearnet";
|
||||
# TODO: migrate `transmission_passwd` to `secrets` api
|
||||
extraPaths = [ "/run/secrets/transmission_passwd" ];
|
||||
@@ -68,6 +70,7 @@ in
|
||||
|
||||
"sane-scripts.bt-search".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
net = "clearnet";
|
||||
# TODO: migrate `jackett_apikey` to `secrets` api
|
||||
extraPaths = [ "/run/secrets/jackett_apikey" ];
|
||||
@@ -75,6 +78,7 @@ in
|
||||
|
||||
"sane-scripts.bt-show".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
net = "clearnet";
|
||||
# TODO: migrate `transmission_passwd` to `secrets` api
|
||||
extraPaths = [ "/run/secrets/transmission_passwd" ];
|
||||
@@ -86,11 +90,13 @@ in
|
||||
|
||||
"sane-scripts.deadlines".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
extraHomePaths = [ "knowledge/planner/deadlines.tsv" ];
|
||||
};
|
||||
|
||||
"sane-scripts.dev-cargo-loop".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
net = "clearnet";
|
||||
whitelistPwd = true;
|
||||
extraPaths = [
|
||||
@@ -104,6 +110,7 @@ in
|
||||
|
||||
"sane-scripts.find-dotfiles".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
extraHomePaths = [
|
||||
"/"
|
||||
".persist/ephemeral"
|
||||
@@ -113,6 +120,7 @@ in
|
||||
|
||||
"sane-scripts.ip-check".sandbox = {
|
||||
method = "landlock";
|
||||
wrapperType = "wrappedDerivation";
|
||||
net = "all";
|
||||
};
|
||||
|
||||
@@ -120,6 +128,7 @@ in
|
||||
|
||||
"sane-scripts.private-change-passwd".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
autodetectCliPaths = "existing"; #< for the new `private` location
|
||||
capabilities = [ "sys_admin" ]; # it needs to mount the new store
|
||||
extraHomePaths = [
|
||||
@@ -131,6 +140,7 @@ in
|
||||
# instead, we put ourselves in a mount namespace, do the mount, and drop into a shell or run a command.
|
||||
# this actually has an OK side effect, that the mount isn't shared, and so we avoid contention/interleaving that would cause the ending `umount` to fail.
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
# cap_sys_admin is needed to mount stuff.
|
||||
# ordinarily /run/wrappers/bin/mount would do that via setuid, but sandboxes have no_new_privs by default.
|
||||
capabilities = [ "sys_admin" ];
|
||||
@@ -141,6 +151,7 @@ in
|
||||
};
|
||||
"sane-scripts.private-init".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
capabilities = [ "sys_admin" ]; # it needs to mount the new store
|
||||
extraHomePaths = [
|
||||
".persist/private"
|
||||
@@ -151,6 +162,7 @@ in
|
||||
|
||||
"sane-scripts.reclaim-boot-space".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
extraPaths = [ "/boot" ];
|
||||
};
|
||||
|
||||
@@ -161,6 +173,7 @@ in
|
||||
|
||||
"sane-scripts.reboot".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
extraPaths = [
|
||||
"/run/dbus"
|
||||
"/run/systemd"
|
||||
@@ -169,11 +182,13 @@ in
|
||||
|
||||
"sane-scripts.reclaim-disk-space".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
extraPaths = [ "/nix/var/nix" ];
|
||||
};
|
||||
|
||||
"sane-scripts.secrets-unlock".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
extraHomePaths = [
|
||||
".ssh/id_ed25519"
|
||||
".ssh/id_ed25519.pub"
|
||||
@@ -199,6 +214,7 @@ in
|
||||
|
||||
"sane-scripts.shutdown".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
extraPaths = [
|
||||
"/run/dbus"
|
||||
"/run/systemd"
|
||||
@@ -215,6 +231,7 @@ in
|
||||
|
||||
"sane-scripts.tag-music".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
autodetectCliPaths = "existing";
|
||||
};
|
||||
|
||||
@@ -239,6 +256,7 @@ in
|
||||
(builtins.attrNames config.sane.vpn);
|
||||
"sane-scripts.vpn".sandbox = {
|
||||
method = "landlock"; #< bwrap can't handle `ip link` stuff even with cap_net_admin
|
||||
wrapperType = "wrappedDerivation";
|
||||
net = "all";
|
||||
capabilities = [ "net_admin" ];
|
||||
extraHomePaths = [ ".config/sane-vpn" ];
|
||||
@@ -246,6 +264,7 @@ in
|
||||
|
||||
"sane-scripts.which".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
extraHomePaths = [
|
||||
# for SXMO
|
||||
".config/sxmo/hooks"
|
||||
@@ -254,6 +273,7 @@ in
|
||||
|
||||
"sane-scripts.wipe".sandbox = {
|
||||
method = "bwrap";
|
||||
wrapperType = "wrappedDerivation";
|
||||
whitelistDbus = [ "user" ]; #< for `secret-tool` and `systemd --user stop <service>
|
||||
extraHomePaths = [
|
||||
# could be more specific, but at a maintenance cost.
|
||||
@@ -264,21 +284,6 @@ in
|
||||
".local/share"
|
||||
".librewolf"
|
||||
".mozilla"
|
||||
".persist/ephemeral/.cache"
|
||||
".persist/ephemeral/.config"
|
||||
".persist/ephemeral/.local/share"
|
||||
".persist/ephemeral/.librewolf"
|
||||
".persist/ephemeral/.mozilla"
|
||||
".persist/plaintext/.cache"
|
||||
".persist/plaintext/.config"
|
||||
".persist/plaintext/.local/share"
|
||||
".persist/plaintext/.librewolf"
|
||||
".persist/plaintext/.mozilla"
|
||||
".persist/private/.cache"
|
||||
".persist/private/.config"
|
||||
".persist/private/.local/share"
|
||||
".persist/private/.librewolf"
|
||||
".persist/private/.mozilla"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
@@ -1,24 +0,0 @@
|
||||
# limitations:
|
||||
# - schlock fails open (pkill it and the wayland session is left unprotected)
|
||||
# - schlock does not accept keyboard input; hence, unusable without a touchscreen
|
||||
# - pin is not synchronized with PAM.
|
||||
# - generate a hashed pin with: `mkpin`
|
||||
# - does not seem to render in landscape mode
|
||||
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.schlock;
|
||||
in
|
||||
{
|
||||
sane.programs.schlock = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistWayland = true;
|
||||
|
||||
secrets.".config/schlock/schlock.pin" = ../../../secrets/common/schlock.pin.bin;
|
||||
};
|
||||
|
||||
sane.programs.swayidle.config = lib.mkIf cfg.enabled {
|
||||
actions.schlock.desktop = "schlock.desktop";
|
||||
actions.schlock.delay = 1800;
|
||||
};
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user