2023-07-12 10:03:58 +00:00
|
|
|
{ config, lib, pkgs, ... }:
|
2023-09-01 23:59:21 +00:00
|
|
|
|
2022-06-07 00:35:28 +00:00
|
|
|
# docs: https://nixos.wiki/wiki/Sway
|
2023-09-02 10:37:09 +00:00
|
|
|
# sway-config docs: `man 5 sway`
|
2022-06-07 00:35:28 +00:00
|
|
|
let
|
2022-08-01 07:23:49 +00:00
|
|
|
cfg = config.sane.gui.sway;
|
2023-09-08 10:59:31 +00:00
|
|
|
defaultPackage = let
|
|
|
|
# `defaultPackage` exists to create a `sway.desktop` file
|
|
|
|
# which will launch sway with our desired debugging facilities.
|
|
|
|
# i.e. redirect output to syslog.
|
|
|
|
scfg = config.programs.sway;
|
|
|
|
systemd-cat = "${pkgs.systemd}/bin/systemd-cat";
|
|
|
|
swayWithLogger = pkgs.writeShellScriptBin "sway-session" ''
|
|
|
|
echo "launching sway-session (sway.desktop)..." | ${systemd-cat} --identifier=sway-session
|
|
|
|
sway 2>&1 | ${systemd-cat} --identifier=sway-session
|
|
|
|
'';
|
2023-11-26 21:55:32 +00:00
|
|
|
origSway = pkgs.sway.override {
|
2023-09-08 10:59:31 +00:00
|
|
|
# this override is what `programs.nixos` would do internally if we left `package` unset.
|
|
|
|
extraSessionCommands = scfg.extraSessionCommands;
|
|
|
|
extraOptions = scfg.extraOptions;
|
|
|
|
withBaseWrapper = scfg.wrapperFeatures.base;
|
|
|
|
withGtkWrapper = scfg.wrapperFeatures.gtk;
|
|
|
|
isNixOS = true;
|
|
|
|
# TODO: `enableXWayland = ...`?
|
2023-11-26 21:55:32 +00:00
|
|
|
};
|
2023-09-08 10:59:31 +00:00
|
|
|
desktop-file = pkgs.runCommand "sway-desktop-wrapper" {} ''
|
|
|
|
mkdir -p $out/share/wayland-sessions
|
|
|
|
substitute ${origSway}/share/wayland-sessions/sway.desktop $out/share/wayland-sessions/sway.desktop \
|
|
|
|
--replace 'Exec=sway' 'Exec=${swayWithLogger}/bin/sway-session'
|
2023-09-24 17:24:29 +00:00
|
|
|
# XXX(2023/09/24) phog greeter (mobile greeter) will crash if DesktopNames is not set
|
|
|
|
echo "DesktopNames=Sway" >> $out/share/wayland-sessions/sway.desktop
|
2023-09-08 10:59:31 +00:00
|
|
|
'';
|
|
|
|
in pkgs.symlinkJoin {
|
|
|
|
inherit (origSway) name meta;
|
|
|
|
# the order of these `paths` is suchs that the desktop-file should claim share/wayland-sessions/sway.deskop,
|
|
|
|
# overriding whatever the origSway provides
|
|
|
|
paths = [ desktop-file origSway ];
|
|
|
|
passthru = {
|
|
|
|
inherit (origSway.passthru) providedSessions;
|
|
|
|
};
|
|
|
|
};
|
2022-06-07 00:35:28 +00:00
|
|
|
in
|
|
|
|
{
|
2023-09-01 23:59:21 +00:00
|
|
|
options = with lib; {
|
2022-08-01 07:23:49 +00:00
|
|
|
sane.gui.sway.enable = mkOption {
|
2022-06-07 00:35:28 +00:00
|
|
|
default = false;
|
|
|
|
type = types.bool;
|
|
|
|
};
|
2023-09-08 10:59:31 +00:00
|
|
|
sane.gui.sway.package = mkOption {
|
|
|
|
default = defaultPackage;
|
|
|
|
type = types.package;
|
|
|
|
};
|
2022-10-22 08:31:51 +00:00
|
|
|
sane.gui.sway.useGreeter = mkOption {
|
|
|
|
description = ''
|
|
|
|
launch sway via a greeter (like greetd's gtkgreet).
|
|
|
|
sway is usable without a greeter, but skipping the greeter means no PAM session.
|
|
|
|
'';
|
2023-01-29 08:13:09 +00:00
|
|
|
default = true;
|
2022-10-22 08:31:51 +00:00
|
|
|
type = types.bool;
|
|
|
|
};
|
2023-09-05 04:25:58 +00:00
|
|
|
sane.gui.sway.config = {
|
|
|
|
extra_lines = mkOption {
|
|
|
|
type = types.lines;
|
|
|
|
description = ''
|
|
|
|
extra lines to append to the sway config
|
|
|
|
'';
|
2023-09-05 04:51:12 +00:00
|
|
|
default = ''
|
|
|
|
# XXX: sway needs exclusive control of XF86Audio{Raise,Lower}Volume, so assign this from a block that it can override.
|
|
|
|
# TODO: factor the bindings out into proper options and be less hacky?
|
|
|
|
bindsym --locked XF86AudioRaiseVolume exec $volume_up
|
|
|
|
bindsym --locked XF86AudioLowerVolume exec $volume_down
|
|
|
|
'';
|
2023-09-05 04:25:58 +00:00
|
|
|
};
|
2023-09-06 07:55:03 +00:00
|
|
|
background = mkOption {
|
|
|
|
type = types.path;
|
|
|
|
};
|
2023-09-05 04:25:58 +00:00
|
|
|
font = mkOption {
|
2023-09-10 12:57:10 +00:00
|
|
|
type = types.str;
|
2023-09-05 04:25:58 +00:00
|
|
|
default = "pango:monospace 11";
|
|
|
|
description = ''
|
|
|
|
default font (for e.g. window titles)
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
mod = mkOption {
|
2023-09-10 12:57:10 +00:00
|
|
|
type = types.str;
|
2023-09-05 04:25:58 +00:00
|
|
|
default = "Mod4";
|
|
|
|
description = ''
|
|
|
|
Super key (for non-application shortcuts).
|
|
|
|
- "Mod1" for Alt
|
|
|
|
- "Mod4" for logo key
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
workspace_layout = mkOption {
|
2023-09-10 12:57:10 +00:00
|
|
|
type = types.str;
|
2023-09-05 04:25:58 +00:00
|
|
|
default = "default";
|
|
|
|
description = ''
|
|
|
|
how to arrange windows within new workspaces, by default:
|
|
|
|
- "default" (split)
|
|
|
|
- "tabbed"
|
|
|
|
- etc
|
|
|
|
'';
|
|
|
|
};
|
2023-09-06 07:55:03 +00:00
|
|
|
xwayland = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = true;
|
|
|
|
description = ''
|
|
|
|
whether or not to enable xwayland (allows running X11 apps on sway).
|
|
|
|
some electron apps (e.g. element-desktop) require xwayland.
|
|
|
|
'';
|
|
|
|
};
|
2023-09-05 04:25:58 +00:00
|
|
|
|
|
|
|
# TODO: split these into their own option scope
|
|
|
|
brightness_down_cmd = mkOption {
|
2023-09-10 12:57:10 +00:00
|
|
|
type = types.str;
|
2023-11-17 22:46:44 +00:00
|
|
|
default = "${pkgs.brightnessctl}/bin/brightnessctl set 2%-";
|
2023-09-05 04:25:58 +00:00
|
|
|
description = "command to run when use wants to decrease screen brightness";
|
|
|
|
};
|
|
|
|
brightness_up_cmd = mkOption {
|
2023-09-10 12:57:10 +00:00
|
|
|
type = types.str;
|
2023-09-05 04:25:58 +00:00
|
|
|
default = "${pkgs.brightnessctl}/bin/brightnessctl set +2%";
|
|
|
|
description = "command to run when use wants to increase screen brightness";
|
|
|
|
};
|
|
|
|
screenshot_cmd = mkOption {
|
2023-09-10 12:57:10 +00:00
|
|
|
type = types.str;
|
2023-09-05 04:25:58 +00:00
|
|
|
default = "${pkgs.sway-contrib.grimshot}/bin/grimshot copy area";
|
|
|
|
description = "command to run when user wants to take a screenshot";
|
|
|
|
};
|
|
|
|
};
|
2023-09-06 02:11:57 +00:00
|
|
|
sane.gui.sway.waybar.extra_style = mkOption {
|
|
|
|
type = types.lines;
|
|
|
|
default = ''
|
|
|
|
/* default font-size is about 14px, which is good for moby, but not quite for larger displays */
|
|
|
|
window#waybar {
|
|
|
|
font-size: 16px;
|
|
|
|
}
|
|
|
|
'';
|
|
|
|
description = ''
|
|
|
|
extra CSS rules to append to ~/.config/waybar/style.css
|
|
|
|
'';
|
|
|
|
};
|
2023-09-05 03:43:32 +00:00
|
|
|
sane.gui.sway.waybar.top = mkOption {
|
|
|
|
type = types.submodule {
|
|
|
|
# `attrsOf types.anything` (v.s. plain `attrs`) causes merging of the toplevel items.
|
|
|
|
# this allows for `waybar.top.x = lib.mkDefault a;` with `waybar.top.x = b;` to resolve to `b`.
|
|
|
|
# but note that `waybar.top.x.y = <multiple assignment>` won't be handled as desired.
|
|
|
|
freeformType = types.attrsOf types.anything;
|
|
|
|
};
|
|
|
|
default = {};
|
|
|
|
description = ''
|
|
|
|
Waybar configuration for the bar at the top of the display.
|
|
|
|
see: <https://github.com/Alexays/Waybar/wiki/Configuration>
|
|
|
|
example:
|
|
|
|
```nix
|
|
|
|
{
|
|
|
|
height = 40;
|
|
|
|
modules-left = [ "sway/workspaces" "sway/mode" ];
|
|
|
|
...
|
|
|
|
}
|
|
|
|
```
|
|
|
|
'';
|
|
|
|
};
|
2022-06-07 00:35:28 +00:00
|
|
|
};
|
2023-09-01 23:59:21 +00:00
|
|
|
|
|
|
|
config = lib.mkMerge [
|
2023-02-04 00:43:00 +00:00
|
|
|
{
|
|
|
|
sane.programs.swayApps = {
|
|
|
|
package = null;
|
|
|
|
suggestedPrograms = [
|
2023-02-04 00:50:47 +00:00
|
|
|
"guiApps"
|
2023-09-05 08:31:50 +00:00
|
|
|
"conky" # for a nice background
|
2023-04-24 07:22:33 +00:00
|
|
|
"splatmoji" # used by us, but 'enabling' it gets us persistence & cfg
|
2023-02-04 00:43:00 +00:00
|
|
|
"swaylock"
|
|
|
|
"swayidle"
|
|
|
|
"wl-clipboard"
|
2023-05-15 02:42:33 +00:00
|
|
|
"blueberry" # GUI bluetooth manager
|
2023-09-10 00:01:39 +00:00
|
|
|
"playerctl" # for waybar & particularly to have playerctld running
|
2023-09-06 08:57:25 +00:00
|
|
|
# "mako" # notification daemon
|
|
|
|
"swaynotificationcenter" # notification daemon
|
2023-02-04 00:43:00 +00:00
|
|
|
# # "pavucontrol"
|
2023-05-15 02:42:33 +00:00
|
|
|
# "gnome.gnome-bluetooth" # XXX(2023/05/14): broken
|
2023-06-29 10:56:56 +00:00
|
|
|
# "gnome.gnome-control-center" # XXX(2023/06/28): depends on webkitgtk4_1
|
2023-03-03 10:22:41 +00:00
|
|
|
"sway-contrib.grimshot"
|
2023-06-29 10:56:56 +00:00
|
|
|
"wdisplays" # like xrandr
|
2023-02-04 00:43:00 +00:00
|
|
|
];
|
2023-07-15 00:23:14 +00:00
|
|
|
|
|
|
|
secrets.".config/sane-sway/snippets.txt" = ../../../../secrets/common/snippets.txt.bin;
|
2022-06-07 00:35:28 +00:00
|
|
|
};
|
2023-09-05 03:43:32 +00:00
|
|
|
|
|
|
|
# default waybar
|
|
|
|
sane.gui.sway.waybar.top = import ./waybar-top.nix { inherit lib pkgs; };
|
2023-02-04 00:43:00 +00:00
|
|
|
}
|
2022-06-07 00:35:28 +00:00
|
|
|
|
2023-09-01 23:59:21 +00:00
|
|
|
(lib.mkIf cfg.enable {
|
2023-07-08 02:22:43 +00:00
|
|
|
sane.programs.fontconfig.enableFor.system = true;
|
2023-02-04 00:43:00 +00:00
|
|
|
sane.programs.swayApps.enableFor.user.colin = true;
|
2023-07-08 02:22:43 +00:00
|
|
|
|
2023-07-02 09:37:07 +00:00
|
|
|
sane.gui.gtk.enable = lib.mkDefault true;
|
2023-07-02 11:33:01 +00:00
|
|
|
# sane.gui.gtk.gtk-theme = lib.mkDefault "Fluent-Light-compact";
|
|
|
|
sane.gui.gtk.gtk-theme = lib.mkDefault "Tokyonight-Light-B";
|
2023-10-04 01:01:29 +00:00
|
|
|
sane.gui.gtk.icon-theme = lib.mkDefault "HighContrast"; # 4/5 coverage on moby
|
|
|
|
# sane.gui.gtk.icon-theme = lib.mkDefault "WhiteSur"; # 3.5/5 coverage on moby, but it provides a bunch for Fractal/Dino
|
|
|
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Humanity"; # 3.5/5 coverage on moby, but it provides the bookmark icon
|
|
|
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Paper"; # 3.5/5 coverage on moby, but it provides the bookmark icon
|
|
|
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Nordzy"; # 3/5 coverage on moby
|
|
|
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Fluent"; # 3/5 coverage on moby
|
|
|
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Colloid"; # 3/5 coverage on moby
|
|
|
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Qogir"; # 2.5/5 coverage on moby
|
|
|
|
# sane.gui.gtk.icon-theme = lib.mkDefault "rose-pine-dawn"; # 2.5/5 coverage on moby
|
|
|
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Flat-Remix-Grey-Light"; # requires qtbase
|
2023-02-04 00:43:00 +00:00
|
|
|
|
|
|
|
# swap in these lines to use SDDM instead of `services.greetd`.
|
|
|
|
# services.xserver.displayManager.sddm.enable = true;
|
|
|
|
# services.xserver.enable = true;
|
2023-09-07 10:45:23 +00:00
|
|
|
sane.gui.greetd = lib.mkIf cfg.useGreeter {
|
2023-02-04 00:43:00 +00:00
|
|
|
enable = true;
|
2023-09-07 10:45:23 +00:00
|
|
|
sway.enable = true; # have greetd launch a sway compositor in which we host a greeter
|
|
|
|
sway.gtkgreet = {
|
|
|
|
enable = true;
|
|
|
|
session.name = "sway-on-gtkgreet";
|
2023-09-08 10:59:31 +00:00
|
|
|
session.command = "${cfg.package}/bin/sway";
|
2023-09-07 10:45:23 +00:00
|
|
|
};
|
2023-02-04 00:43:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
# unlike other DEs, sway configures no audio stack
|
|
|
|
# administer with pw-cli, pw-mon, pw-top commands
|
|
|
|
services.pipewire = {
|
|
|
|
enable = true;
|
|
|
|
alsa.enable = true;
|
|
|
|
alsa.support32Bit = true; # ??
|
2023-09-04 11:10:30 +00:00
|
|
|
# emulate pulseaudio for legacy apps (e.g. sxmo-utils)
|
2023-02-04 00:43:00 +00:00
|
|
|
pulse.enable = true;
|
|
|
|
};
|
2023-10-10 21:45:19 +00:00
|
|
|
# rtkit/RealtimeKit: allow applications which want realtime audio (e.g. Dino? Pulseaudio server?) to request it.
|
|
|
|
# this might require more configuration (e.g. polkit-related) to work exactly as desired.
|
|
|
|
# - readme outlines requirements: <https://github.com/heftig/rtkit>
|
2023-10-13 03:35:00 +00:00
|
|
|
# XXX(2023/10/12): rtkit does not play well on moby. any application sending audio out dies after 10s.
|
|
|
|
# security.rtkit.enable = true;
|
2023-09-13 12:57:18 +00:00
|
|
|
# persist per-device volume levels
|
2023-11-08 15:32:50 +00:00
|
|
|
sane.user.persist.byStore.plaintext = [ ".local/state/wireplumber" ];
|
2023-09-13 12:57:18 +00:00
|
|
|
|
|
|
|
# persist per-device volume settings across power cycles.
|
|
|
|
# pipewire sits atop the kernel ALSA API, so alsa-utils knows about device volumes.
|
|
|
|
# but wireplumber also tries to do some of this
|
|
|
|
# systemd.services.alsa-store = {
|
|
|
|
# # based on <repo:nixos/nixpkgs:nixos/modules/services/audio/alsa.nix>
|
|
|
|
# description = "Store Sound Card State";
|
|
|
|
# wantedBy = [ "multi-user.target" ];
|
|
|
|
# serviceConfig = {
|
|
|
|
# Type = "oneshot";
|
|
|
|
# RemainAfterExit = true;
|
|
|
|
# ExecStart = "${pkgs.alsa-utils}/sbin/alsactl restore";
|
|
|
|
# ExecStop = "${pkgs.alsa-utils}/sbin/alsactl store --ignore";
|
|
|
|
# };
|
|
|
|
# };
|
2023-11-08 15:32:50 +00:00
|
|
|
# sane.persist.sys.byStore.plaintext = [ "/var/lib/alsa" ];
|
2023-02-04 00:43:00 +00:00
|
|
|
|
|
|
|
networking.useDHCP = false;
|
|
|
|
networking.networkmanager.enable = true;
|
|
|
|
networking.wireless.enable = lib.mkForce false;
|
|
|
|
|
|
|
|
hardware.bluetooth.enable = true;
|
|
|
|
services.blueman.enable = true;
|
|
|
|
# gsd provides Rfkill, which is required for the bluetooth pane in gnome-control-center to work
|
2023-06-29 10:56:56 +00:00
|
|
|
# services.gnome.gnome-settings-daemon.enable = true;
|
2023-02-04 00:43:00 +00:00
|
|
|
# start the components of gsd we need at login
|
2023-06-29 10:56:56 +00:00
|
|
|
# systemd.user.targets."org.gnome.SettingsDaemon.Rfkill".wantedBy = [ "graphical-session.target" ];
|
2023-02-04 00:43:00 +00:00
|
|
|
# go ahead and `systemctl --user cat gnome-session-initialized.target`. i dare you.
|
|
|
|
# the only way i can figure out how to get Rfkill to actually load is to just disable all the shit it depends on.
|
|
|
|
# it doesn't actually seem to need ANY of them in the first place T_T
|
2023-06-29 10:56:56 +00:00
|
|
|
# systemd.user.targets."gnome-session-initialized".enable = false;
|
2023-02-04 00:43:00 +00:00
|
|
|
# bluez can't connect to audio devices unless pipewire is running.
|
|
|
|
# a system service can't depend on a user service, so just launch it at graphical-session
|
|
|
|
systemd.user.services."pipewire".wantedBy = [ "graphical-session.target" ];
|
|
|
|
|
|
|
|
programs.sway = {
|
2023-08-02 08:16:05 +00:00
|
|
|
# provides xdg-desktop-portal-wlr, which exposes on dbus:
|
|
|
|
# - org.freedesktop.impl.portal.ScreenCast
|
|
|
|
# - org.freedesktop.impl.portal.Screenshot
|
2023-02-04 00:43:00 +00:00
|
|
|
enable = true;
|
2023-08-02 08:19:56 +00:00
|
|
|
extraPackages = []; # nixos adds swaylock, swayidle, foot, dmenu by default
|
2023-10-03 00:47:33 +00:00
|
|
|
# extraOptions = [ "--debug" ];
|
2023-08-02 07:40:57 +00:00
|
|
|
# "wrapGAppsHook wrapper to execute sway with required environment variables for GTK applications."
|
2023-10-11 09:59:10 +00:00
|
|
|
# this literally just sets XDG_DATA_DIRS to the gtk3 gsettings-schemas before launching sway.
|
|
|
|
# notably, this pulls in the *build* gtk3 -- probably not in an incompatible way
|
|
|
|
# but still as a mistake, and wasteful for cross compilation
|
|
|
|
wrapperFeatures.gtk = false;
|
|
|
|
# this sets XDG_CURRENT_DESKTOP=sway
|
|
|
|
# and makes sure that sway is launched dbus-run-session.
|
|
|
|
wrapperFeatures.base = true;
|
2023-09-08 10:59:31 +00:00
|
|
|
package = cfg.package;
|
2023-02-04 00:43:00 +00:00
|
|
|
};
|
2023-09-08 10:59:31 +00:00
|
|
|
programs.xwayland.enable = cfg.config.xwayland;
|
2023-08-02 08:16:05 +00:00
|
|
|
# provide portals for:
|
|
|
|
# - org.freedesktop.impl.portal.Access
|
|
|
|
# - org.freedesktop.impl.portal.Account
|
|
|
|
# - org.freedesktop.impl.portal.DynamicLauncher
|
|
|
|
# - org.freedesktop.impl.portal.Email
|
|
|
|
# - org.freedesktop.impl.portal.FileChooser
|
|
|
|
# - org.freedesktop.impl.portal.Inhibit
|
|
|
|
# - org.freedesktop.impl.portal.Notification
|
|
|
|
# - org.freedesktop.impl.portal.Print
|
|
|
|
# and conditionally (i.e. unless buildPortalsInGnome = false) for:
|
|
|
|
# - org.freedesktop.impl.portal.AppChooser (@appchooser_iface@)
|
|
|
|
# - org.freedesktop.impl.portal.Background (@background_iface@)
|
|
|
|
# - org.freedesktop.impl.portal.Lockdown (@lockdown_iface@)
|
|
|
|
# - org.freedesktop.impl.portal.RemoteDesktop (@remotedesktop_iface@)
|
|
|
|
# - org.freedesktop.impl.portal.ScreenCast (@screencast_iface@)
|
|
|
|
# - org.freedesktop.impl.portal.Screenshot (@screenshot_iface@)
|
|
|
|
# - org.freedesktop.impl.portal.Settings (@settings_iface@)
|
|
|
|
# - org.freedesktop.impl.portal.Wallpaper (@wallpaper_iface@)
|
|
|
|
xdg.portal.extraPortals = [
|
|
|
|
(pkgs.xdg-desktop-portal-gtk.override {
|
|
|
|
buildPortalsInGnome = false;
|
|
|
|
})
|
|
|
|
];
|
|
|
|
|
2023-09-05 03:43:32 +00:00
|
|
|
sane.user.fs = {
|
2023-09-04 11:10:30 +00:00
|
|
|
".config/waybar/config".symlink.target =
|
2023-09-05 03:43:32 +00:00
|
|
|
(pkgs.formats.json {}).generate "waybar-config.json" [
|
|
|
|
({ layer = "top"; } // cfg.waybar.top)
|
|
|
|
];
|
2023-02-04 00:43:00 +00:00
|
|
|
|
2023-09-04 11:10:30 +00:00
|
|
|
".config/waybar/style.css".symlink.text =
|
2023-09-06 02:11:57 +00:00
|
|
|
(builtins.readFile ./waybar-style.css) + cfg.waybar.extra_style;
|
2023-09-05 03:43:32 +00:00
|
|
|
|
2023-09-07 10:33:28 +00:00
|
|
|
".config/sway/config".symlink.target = import ./sway-config.nix {
|
2023-09-05 04:25:58 +00:00
|
|
|
inherit pkgs;
|
|
|
|
inherit (cfg) config;
|
2023-09-05 03:43:32 +00:00
|
|
|
};
|
2023-09-04 11:10:30 +00:00
|
|
|
};
|
2023-02-04 00:43:00 +00:00
|
|
|
})
|
|
|
|
];
|
2022-06-07 00:35:28 +00:00
|
|
|
}
|
|
|
|
|