{
  config,
  lib,
  pkgs,
  osConfig,
  ...
}:
{
  imports = [
    ./waybar.nix
    ./fuzzel.nix
    ./mako.nix
  ];
  stylix.targets.swaylock.useImage = false; # Using swaylock-effects instead
  home.packages = with pkgs; [
    libreoffice-fresh
    ffmpeg
    grim
    slurp
    swappy
    wl-clipboard
    swayimg
    swaybg
    (writeShellScriptBin "swaylockfx" ''
      ${swaylock-effects}/bin/swaylock --screenshots --clock --indicator --effect-blur 7x5 --effect-vignette 0.5:0.5
    '')
    (writeShellScriptBin "protonmail-setup-bridge" ''
      pkill -9 -f protonmail-bridge
      ${protonmail-bridge}/bin/protonmail-bridge -c
      swaymsg exec "${protonmail-bridge}/bin/protonmail-bridge -n"
    '')
  ];
  programs = {
    waybar.systemd.target = "sway-session.target";
    swaylock = {
      enable = true;
      package = pkgs.swaylock-effects;
    };
  };
  services = {
    kanshi.systemdTarget = "sway-session.target";
    gpg-agent.pinentryPackage = pkgs.pinentry-gnome3;
    avizo = {
      enable = true;
      settings.default.image-opacity = lib.mkForce "1.0";
    };
    kdeconnect = {
      enable = true;
      indicator = true;
    };
    swayidle = {
      enable = true;
      timeouts = [
        {
          timeout = 60 * 15; # 15 minutes
          command = "swaylockfx";
        }
      ];
    };
    udiskie = {
      enable = true;
      notify = true;
      automount = true;
      tray = "never";
    };
  };
  wayland.windowManager.sway = {
    enable = true;
    package = pkgs.swayfx.overrideAttrs (old: {
      passthru.providedSessions = [ "sway" ];
    });
    swaynag.enable = true;
    systemd.enable = true;
    xwayland = true;
    checkConfig = false;
    config = {
      input = {
        "*" = {
          xkb_layout = osConfig.services.xserver.xkb.layout;
        };
        "type:touchpad" = {
          tap = "enabled";
          natural_scroll = "enabled";
        };
      };
      seat = {
        "seat0" = {
          xcursor_theme = "${config.gtk.cursorTheme.name} ${toString config.gtk.cursorTheme.size}";
        };
      };
      modifier = "Mod4";
      terminal = "${config.programs.alacritty.package}/bin/alacritty";
      menu = "${config.programs.fuzzel.package}/bin/fuzzel";
      workspaceAutoBackAndForth = true;
      bars = [ ];
      gaps = {
        inner = 5;
        outer = 0;
      };
      startup = [
        { command = "${pkgs.libsForQt5.polkit-kde-agent}/libexec/polkit-kde-authentication-agent-1"; }
        { command = "${pkgs.protonmail-bridge}/bin/protonmail-bridge -n"; }
        { command = "${pkgs.dex}/bin/dex -a"; }
        { command = "${config.services.mako.package}/bin/mako"; }
      ];
      window = {
        border = 1;
        titlebar = false;
        commands = [
          {
            criteria.window_role = "(?:pop-up|bubble|dialog)";
            command = "floating enable";
          }
          {
            criteria.app_id = "(?:org\.kde\.polkit-kde-authentication-agent-1|Pinentry|pavucontrol|\.blueman-manager-wrapped|nm-connection-editor)";
            command = "floating enable";
          }
          {
            criteria.class = "(?:[Mm]atplotlib|.*\.py|Idl)";
            command = "floating enable";
          }
          {
            # Catch-all for file dialog windows
            criteria.title = "(?:Open|Save) (?:File|Folder|As)";
            command = "floating enable, resize set width 1030 height 710";
          }
          {
            # Inhibit idle when watching vidoes
            criteria.app_id = "(?:firefox|mpv)";
            command = "inhibit_idle fullscreen";
          }
          {
            # Window rules for game sessions.
            # Note: controllers don't reset idle timer
            criteria.class = "(?:steam_app|Minecraft).*";
            command = "floating enable, fullscreen enable, inhibit_idle focus";
          }
        ];
      };
      floating.border = 1;
      keybindings =
        let
          modifier = config.wayland.windowManager.sway.config.modifier;
        in
        lib.mkOptionDefault {
          # Movement
          "${modifier}+comma" = "workspace prev";
          "${modifier}+period" = "workspace next";
          "${modifier}+shift+comma" = "move workspace prev";
          "${modifier}+shift+period" = "move workspace next";
          "${modifier}+bracketleft" = "move workspace to output left";
          "${modifier}+bracketright" = "move workspace to output right";

          # Function keys
          ## Screenshot
          "Print" = "exec ${pkgs.grim}/bin/grim -g \"$(${pkgs.slurp}/bin/slurp)\" - | ${pkgs.swappy}/bin/swappy -f -";

          ## Sound
          "XF86AudioMute" = "exec ${config.services.avizo.package}/bin/volumectl %";
          "XF86AudioMicMute" = "exec ${config.services.avizo.package}/bin/volumectl -m %";
          "XF86AudioRaiseVolume" = "exec ${config.services.avizo.package}/bin/volumectl -u + 5";
          "XF86AudioLowerVolume" = "exec ${config.services.avizo.package}/bin/volumectl -u - 5";

          ## Playback
          "XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play-pause";
          "XF86AudioStop" = "exec ${pkgs.playerctl}/bin/playerctl play-pause";
          "XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous";
          "XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next";

          ## Brightness
          "XF86MonBrightnessUp" = "exec ${config.services.avizo.package}/bin/lightctl + 5";
          "XF86MonBrightnessDown" = "exec ${config.services.avizo.package}/bin/lightctl - 5";

          # System utilities
          "Mod4+Mod1+l" = "exec swaylockfx";
          "${modifier}+z" = "exec ${config.services.mako.package}/bin/makoctl restore";
          "${modifier}+Shift+z" = "exec ${config.services.mako.package}/bin/makoctl dismiss -a";
          "${modifier}+Shift+Return" = "exec ${config.programs.emacs.finalPackage}/bin/emacsclient -c";
        };
    };
    extraConfig = ''
      bindgesture swipe:right workspace prev
      bindgesture swipe:left workspace next
      corner_radius 5
      blur enable
      blur_xray disable
      blur_passes 3
      blur_radius 5
      shadows enable
      shadow_blur_radius 5
      layer_effects "system-menu" blur enable; blur_ignore_transparent enable; shadows enable
      layer_effects "gtk-layer-shell" blur enable; blur_ignore_transparent enable; shadows enable
      layer_effects "notifications" blur enable; blur_ignore_transparent enable; shadows enable
      layer_effects "launcher" blur enable; blur_ignore_transparent enable; shadows enable
      layer_effects "logout_dialog" blur enable; blur_ignore_transparent enable; shadows enable
      layer_effects "waybar" blur enable; blur_ignore_transparent enable; shadows enable
      layer_effects "avizo" blur enable; blur_ignore_transparent enable; shadows enable
      titlebar_separator enable
    '';
  };
  xdg.configFile."swappy/config".source = ./swappy.conf;
}