sway: port to sane.programs API

This commit is contained in:
2024-02-21 23:18:57 +00:00
parent 55a6c828f2
commit 386651044e
11 changed files with 368 additions and 363 deletions

View File

@@ -28,7 +28,7 @@
sane.nixcache.substituters.desko = false;
sane.nixcache.remote-builders.desko = false;
sane.gui.sway.enable = true;
sane.programs.sway.enableFor.user.colin = true;
sane.programs.iphoneUtils.enableFor.user.colin = true;
sane.programs.steam.enableFor.user.colin = true;

View File

@@ -12,10 +12,10 @@
sane.services.wg-home.ip = config.sane.hosts.by-name."lappy".wg-home.ip;
# sane.guest.enable = true;
sane.gui.sway.enable = true;
boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
sane.programs.sway.enableFor.user.colin = true;
sane.programs."gnome.geary".config.autostart = true;
sane.programs.signal-desktop.config.autostart = true;
sane.programs.stepmania.enableFor.user.colin = true;

View File

@@ -85,6 +85,7 @@
./strings.nix
./sublime-music.nix
./supertuxkart.nix
./sway
./sway-autoscaler
./swaylock.nix
./swaynotificationcenter.nix

View File

@@ -0,0 +1,274 @@
{ config, lib, pkgs, ... }:
# docs: https://nixos.wiki/wiki/Sway
# sway-config docs: `man 5 sway`
let
cfg = config.sane.programs.sway;
wrapSway = configuredSway: let
# `wrapSway` exists to launch sway with our desired debugging facilities.
# i.e. redirect output to syslog.
systemd-cat = "${lib.getBin pkgs.systemd}/bin/systemd-cat";
swayLauncher = pkgs.writeShellScriptBin "sway" ''
# sway defaults to auto-generating a unix domain socket named "sway-ipc.$UID.NNNN.sock",
# which allows for multiple sway sessions under the same user.
# but the unpredictability makes static sandboxing & such difficult, so hardcode it:
export SWAYSOCK="$XDG_RUNTIME_DIR/sway-ipc.sock"
export XDG_CURRENT_DESKTOP=sway
echo "launching sway (sway.desktop)..." | ${systemd-cat} --identifier=sway
exec ${configuredSway}/bin/sway 2>&1 | ${systemd-cat} --identifier=sway
'';
in
pkgs.symlinkJoin {
inherit (configuredSway) meta version;
name = "sway-wrapped";
paths = [
swayLauncher
configuredSway
];
};
swayPackage = wrapSway (pkgs.sway-unwrapped.overrideAttrs (_: {
# isNixOS = true; #< doesn't matter
# TODO: something else is dragging a xwayland-enabled wlroots into the environment,
# making this actually kinda wasteful.
enableXWayland = cfg.config.xwayland;
}));
in
{
sane.programs.sway = {
configOption = with lib; mkOption {
default = {};
type = types.submodule {
options = {
extra_lines = mkOption {
type = types.lines;
description = ''
extra lines to append to the sway config
'';
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
'';
};
background = mkOption {
type = types.path;
};
font = mkOption {
type = types.str;
default = "pango:monospace 11";
description = ''
default font (for e.g. window titles)
'';
};
mod = mkOption {
type = types.str;
default = "Mod4";
description = ''
Super key (for non-application shortcuts).
- "Mod1" for Alt
- "Mod4" for logo key
'';
};
workspace_layout = mkOption {
type = types.str;
default = "default";
description = ''
how to arrange windows within new workspaces, by default:
- "default" (split)
- "tabbed"
- etc
'';
};
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.
'';
};
screenshot_cmd = mkOption {
type = types.str;
default = "grimshot copy area";
description = "command to run when user wants to take a screenshot";
};
};
};
};
packageUnwrapped = swayPackage;
suggestedPrograms = [
"guiApps"
"blueberry" # GUI bluetooth manager
"brightnessctl"
"conky" # for a nice background
"fontconfig"
"fuzzel"
# "gnome.gnome-bluetooth" # XXX(2023/05/14): broken
# "gnome.gnome-control-center" # XXX(2023/06/28): depends on webkitgtk4_1
"playerctl" # for waybar & particularly to have playerctld running
"pulsemixer" # for volume controls
"splatmoji" # used by sway config
"sway-contrib.grimshot" # used by sway config
# "swayidle" # enable if you need it
"swaylock" # used by sway config
"swaynotificationcenter" # notification daemon
"unl0kr" # greeter
"waybar" # used by sway config
"wdisplays" # like xrandr
"wl-clipboard"
"wob" # render volume changes on-screen
"xdg-desktop-portal"
# xdg-desktop-portal-gtk provides 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.Lockdown (@lockdown_iface@)
# - org.freedesktop.impl.portal.Settings (@settings_iface@)
# - org.freedesktop.impl.portal.Wallpaper (@wallpaper_iface@)
"xdg-desktop-portal-gtk"
# xdg-desktop-portal-wlr provides portals for screenshots/screen sharing
"xdg-desktop-portal-wlr"
"xdg-terminal-exec" # used by sway config
];
secrets.".config/sane-sway/snippets.txt" = ../../../../secrets/common/snippets.txt.bin;
fs.".config/xdg-desktop-portal/sway-portals.conf".symlink.text = ''
# portals.conf docs: <https://flatpak.github.io/xdg-desktop-portal/docs/portals.conf.html>
[preferred]
default=wlr;gtk
'';
fs.".config/sway/config".symlink.target = pkgs.callPackage ./sway-config.nix {
inherit config;
swayCfg = cfg.config;
};
services.sway-session = {
description = "sway-session: active iff sway desktop environment is baseline operational";
documentation = [
"https://github.com/swaywm/sway/issues/7862"
"https://github.com/alebastr/sway-systemd"
];
# we'd like to start graphical-session after sway is ready, but it's marked `RefuseManualStart` because Lennart.
# instead, create `sway-session.service` which `bindsTo` `graphical-session.target`.
# we can manually start `sway-session`, and the `bindsTo` means that it will start `graphical-session`,
# and then track `graphical-session`s state (that is: it'll stop when graphical-session stops).
#
# additionally, set `ConditionEnvironment` to guard that the sway environment variables *really have* been imported into systemd.
unitConfig.ConditionEnvironment = "SWAYSOCK";
# requiredBy = [ "graphical-session.target" ];
before = [ "graphical-session.target" ];
bindsTo = [ "graphical-session.target" ];
serviceConfig = {
ExecStart = "${pkgs.coreutils}/bin/true";
Type = "oneshot";
RemainAfterExit = true;
};
};
};
sane.gui.gtk = lib.mkIf cfg.enabled {
enable = lib.mkDefault true;
# gtk-theme = lib.mkDefault "Fluent-Light-compact";
gtk-theme = lib.mkDefault "Tokyonight-Light-B";
# icon-theme = lib.mkDefault "HighContrast"; # 4/5 coverage on moby
# icon-theme = lib.mkDefault "WhiteSur"; # 3.5/5 coverage on moby, but it provides a bunch for Fractal/Dino
# icon-theme = lib.mkDefault "Humanity"; # 3.5/5 coverage on moby, but it provides the bookmark icon
# icon-theme = lib.mkDefault "Paper"; # 3.5/5 coverage on moby, but it provides the bookmark icon
# icon-theme = lib.mkDefault "Nordzy"; # 3/5 coverage on moby
# icon-theme = lib.mkDefault "Fluent"; # 3/5 coverage on moby
# icon-theme = lib.mkDefault "Colloid"; # 3/5 coverage on moby
# icon-theme = lib.mkDefault "Qogir"; # 2.5/5 coverage on moby
# icon-theme = lib.mkDefault "rose-pine-dawn"; # 2.5/5 coverage on moby
# icon-theme = lib.mkDefault "Flat-Remix-Grey-Light"; # requires qtbase
};
# TODO: port to sane.programs
programs.xwayland = lib.mkIf cfg.enabled {
enable = cfg.config.xwayland;
};
services.gvfs = lib.mkIf cfg.enabled {
enable = true; # allow nautilus to mount remote filesystems (e.g. ftp://...)
package = lib.mkDefault (pkgs.gvfs.override {
# i don't need to mount samba shares, and samba build is expensive/flaky (mostly for cross, but even problematic on native)
samba = null;
});
};
# unlike other DEs, sway configures no audio stack
# administer with pw-cli, pw-mon, pw-top commands
services.pipewire = lib.mkIf cfg.enabled {
enable = true;
alsa.enable = true;
alsa.support32Bit = true; # ??
# emulate pulseaudio for legacy apps (e.g. sxmo-utils)
pulse.enable = true;
};
systemd.user.services."pipewire".wantedBy = lib.optionals cfg.enabled [ "graphical-session.target" ];
# 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>
# XXX(2023/10/12): rtkit does not play well on moby. any application sending audio out dies after 10s.
# security.rtkit.enable = true;
# persist per-device volume levels
sane.user.persist.byStore.plaintext = lib.optionals cfg.enabled [ ".local/state/wireplumber" ];
# 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";
# };
# };
# sane.persist.sys.byStore.plaintext = [ "/var/lib/alsa" ];
# TODO: this can go elsewhere
# networking = lib.mkIf cfg.enabled {
# networkmanager.enable = true;
# wireless.enable = lib.mkForce false;
# };
hardware.bluetooth = lib.mkIf cfg.enabled {
enable = true;
};
services.blueman = lib.mkIf cfg.enabled {
enable = true;
};
# gsd provides Rfkill, which is required for the bluetooth pane in gnome-control-center to work
# services.gnome.gnome-settings-daemon.enable = true;
# start the components of gsd we need at login
# systemd.user.targets."org.gnome.SettingsDaemon.Rfkill".wantedBy = [ "graphical-session.target" ];
# 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
# systemd.user.targets."gnome-session-initialized".enable = false;
# 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
}

View File

@@ -4,6 +4,7 @@
, writeShellApplication
}:
let
# TODO: port this snip_cmd_pkg to an ordinary `sane.programs`
prog = config.sane.programs;
# "bookmarking"/snippets inspired by Luke Smith:
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
@@ -15,7 +16,7 @@ let
prog.wtype.package
];
text = ''
snippet=$(cat ${../snippets.txt} ~/.config/sane-sway/snippets.txt | \
snippet=$(cat ${./snippets.txt} ~/.config/sane-sway/snippets.txt | \
fuzzel -d -i -w 60 | \
sed 's/ #.*$//')
wtype "$snippet"

View File

@@ -11,7 +11,6 @@ in
./greetd.nix
./gtk.nix
./phosh.nix
./sway
./sxmo
./theme
];

View File

@@ -1,268 +0,0 @@
{ config, lib, pkgs, ... }:
# docs: https://nixos.wiki/wiki/Sway
# sway-config docs: `man 5 sway`
let
cfg = config.sane.gui.sway;
wrapSway = configuredSway: let
# `wrapSway` exists to launch sway with our desired debugging facilities.
# i.e. redirect output to syslog.
systemd-cat = "${lib.getBin pkgs.systemd}/bin/systemd-cat";
swayLauncher = pkgs.writeShellScriptBin "sway" ''
# sway defaults to auto-generating a unix domain socket named "sway-ipc.$UID.NNNN.sock",
# which allows for multiple sway sessions under the same user.
# but the unpredictability makes static sandboxing & such difficult, so hardcode it:
export SWAYSOCK="$XDG_RUNTIME_DIR/sway-ipc.sock"
export XDG_CURRENT_DESKTOP=sway
echo "launching sway (sway.desktop)..." | ${systemd-cat} --identifier=sway
exec ${configuredSway}/bin/sway 2>&1 | ${systemd-cat} --identifier=sway
'';
in
pkgs.symlinkJoin {
inherit (configuredSway) meta version;
name = "sway-wrapped";
paths = [
swayLauncher
configuredSway
];
};
swayPackage = wrapSway (pkgs.sway-unwrapped.overrideAttrs (_: {
# isNixOS = true; #< doesn't matter
# TODO: something else is dragging a xwayland-enabled wlroots into the environment,
# making this actually kinda wasteful.
enableXWayland = cfg.config.xwayland;
}));
in
{
options = with lib; {
sane.gui.sway.enable = mkOption {
default = false;
type = types.bool;
};
sane.gui.sway.config = {
extra_lines = mkOption {
type = types.lines;
description = ''
extra lines to append to the sway config
'';
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
'';
};
background = mkOption {
type = types.path;
};
font = mkOption {
type = types.str;
default = "pango:monospace 11";
description = ''
default font (for e.g. window titles)
'';
};
mod = mkOption {
type = types.str;
default = "Mod4";
description = ''
Super key (for non-application shortcuts).
- "Mod1" for Alt
- "Mod4" for logo key
'';
};
workspace_layout = mkOption {
type = types.str;
default = "default";
description = ''
how to arrange windows within new workspaces, by default:
- "default" (split)
- "tabbed"
- etc
'';
};
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.
'';
};
screenshot_cmd = mkOption {
type = types.str;
default = "grimshot copy area";
description = "command to run when user wants to take a screenshot";
};
};
};
config = lib.mkMerge [
{
sane.programs.sway = {
packageUnwrapped = swayPackage;
suggestedPrograms = [
"guiApps"
"blueberry" # GUI bluetooth manager
"brightnessctl"
"conky" # for a nice background
"fontconfig"
"fuzzel"
# "gnome.gnome-bluetooth" # XXX(2023/05/14): broken
# "gnome.gnome-control-center" # XXX(2023/06/28): depends on webkitgtk4_1
"playerctl" # for waybar & particularly to have playerctld running
"pulsemixer" # for volume controls
"splatmoji" # used by sway config
"sway-contrib.grimshot" # used by sway config
# "swayidle" # enable if you need it
"swaylock" # used by sway config
"swaynotificationcenter" # notification daemon
"unl0kr" # greeter
"waybar" # used by sway config
"wdisplays" # like xrandr
"wl-clipboard"
"wob" # render volume changes on-screen
"xdg-desktop-portal"
# xdg-desktop-portal-gtk provides 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.Lockdown (@lockdown_iface@)
# - org.freedesktop.impl.portal.Settings (@settings_iface@)
# - org.freedesktop.impl.portal.Wallpaper (@wallpaper_iface@)
"xdg-desktop-portal-gtk"
# xdg-desktop-portal-wlr provides portals for screenshots/screen sharing
"xdg-desktop-portal-wlr"
"xdg-terminal-exec" # used by sway config
];
secrets.".config/sane-sway/snippets.txt" = ../../../../secrets/common/snippets.txt.bin;
fs.".config/xdg-desktop-portal/sway-portals.conf".symlink.text = ''
# portals.conf docs: <https://flatpak.github.io/xdg-desktop-portal/docs/portals.conf.html>
[preferred]
default=wlr;gtk
'';
fs.".config/sway/config".symlink.target = pkgs.callPackage ./sway-config.nix {
inherit config;
swayCfg = cfg.config;
};
services.sway-session = {
description = "sway-session: active iff sway desktop environment is baseline operational";
documentation = [
"https://github.com/swaywm/sway/issues/7862"
"https://github.com/alebastr/sway-systemd"
];
# we'd like to start graphical-session after sway is ready, but it's marked `RefuseManualStart` because Lennart.
# instead, create `sway-session.service` which `bindsTo` `graphical-session.target`.
# we can manually start `sway-session`, and the `bindsTo` means that it will start `graphical-session`,
# and then track `graphical-session`s state (that is: it'll stop when graphical-session stops).
#
# additionally, set `ConditionEnvironment` to guard that the sway environment variables *really have* been imported into systemd.
unitConfig.ConditionEnvironment = "SWAYSOCK";
# requiredBy = [ "graphical-session.target" ];
before = [ "graphical-session.target" ];
bindsTo = [ "graphical-session.target" ];
serviceConfig = {
ExecStart = "${pkgs.coreutils}/bin/true";
Type = "oneshot";
RemainAfterExit = true;
};
};
};
}
(lib.mkIf cfg.enable {
sane.programs.sway.enableFor.user.colin = true;
sane.gui.gtk.enable = lib.mkDefault true;
# sane.gui.gtk.gtk-theme = lib.mkDefault "Fluent-Light-compact";
sane.gui.gtk.gtk-theme = lib.mkDefault "Tokyonight-Light-B";
# 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
# 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; # ??
# emulate pulseaudio for legacy apps (e.g. sxmo-utils)
pulse.enable = true;
};
services.gvfs.enable = true; # allow nautilus to mount remote filesystems (e.g. ftp://...)
services.gvfs.package = lib.mkDefault (pkgs.gvfs.override {
# i don't need to mount samba shares, and samba build is expensive/flaky (mostly for cross, but even problematic on native)
samba = null;
});
# 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>
# XXX(2023/10/12): rtkit does not play well on moby. any application sending audio out dies after 10s.
# security.rtkit.enable = true;
# persist per-device volume levels
sane.user.persist.byStore.plaintext = [ ".local/state/wireplumber" ];
# 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";
# };
# };
# sane.persist.sys.byStore.plaintext = [ "/var/lib/alsa" ];
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
# services.gnome.gnome-settings-daemon.enable = true;
# start the components of gsd we need at login
# systemd.user.targets."org.gnome.SettingsDaemon.Rfkill".wantedBy = [ "graphical-session.target" ];
# 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
# systemd.user.targets."gnome-session-initialized".enable = false;
# 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.xwayland.enable = cfg.config.xwayland;
})
];
}

View File

@@ -292,15 +292,14 @@ in
(lib.mkIf cfg.enable (lib.mkMerge [
{
sane.programs.sway.enableFor.user.colin = true;
sane.programs.unl0kr.enableFor.user.colin = (cfg.greeter == "unl0kr");
sane.programs.waybar.config = {
top = import ./waybar-top.nix { inherit pkgs; };
# reset extra waybar style
extra_style = "";
};
sane.gui.sway = {
enable = true;
config = {
sane.programs.sway.config = {
# N.B. missing from upstream sxmo config here is:
# - `bindsym $mod+g exec sxmo_hook_locker.sh`
# - `bindsym $mod+t exec sxmo_appmenu.sh power`
@@ -394,7 +393,6 @@ in
exec_always ${sxmo_init}
'';
};
};
sane.programs.sxmoApps.enableFor.user.colin = true;

View File

@@ -22,7 +22,7 @@ in
};
config = {
sane.gui.sway.config.background = lib.mkDefault cfg.png;
sane.programs.sway.config.background = lib.mkDefault cfg.png;
sane.gui.sxmo.settings.SXMO_BG_IMG = lib.mkDefault (builtins.toString cfg.png);
};
}