タイトル画像

sway導入

こんにちは。設定しか勝たんと思っている静カニです。

設定といえばNixOS、NixOSはLinuxディストロ、Linuxディストロで問題になることと言えばDEです。ということでDE移行してきたのでメモっていきます。

選定

まずは移行先の選定が必要です。 条件としてこんな感じのを考えました。一応上にあるほど優先順位が高いです。

  1. GUIの設定がない
  2. 全部のウィンドウを最大化できる
  3. キーバインドが魔改造できる
  4. 全部のウィンドウを重ねられる(背景透過したら背後に別ウィンドウが見える)

1つ目と3つ目に関しては基本的にスタック型というよりはタイル型のウィンドウマネージャーに当てはまる傾向にあります。 そのため2つ目と4つ目の実現について検討するのは置いておいてタイル型になりそうということになりました。

比較的流行りのTWMといえばhyprlandです。ですがこいつは過去の2度ほどの失敗経験が蘇ってくるので捨てておきます。 そういえば2つ目に関してはタブ型のレイアウトがぴったりです(これがない意味でもhyprlandはクビです)。 そこで出てくるのはi3です。ただi3くんはX11でkeydが使えなくなるので却下。i3互換といえばswayです。ということでswayくんはwaylandなので出てきてもらいました。

設定

ということで設定パートです。NixOSなのでNixが多めの傾向です。

sway.nix

configuration.nix側のDE系の設定です。一応ほぼswayの設定なのでsway.nixです。

{ pkgs, pkgs-unstable, ... }:
let
  coreutils_bin = "${pkgs.coreutils}/bin";
  vime = pkgs.writeShellScriptBin "vime" ''
    export PATH="/run/current-system/sw/bin:/etc/profiles/per-user/shizukani-cp/bin:$PATH"

    FILE_PATH="/tmp/$(${coreutils_bin}/date +%Y%m%d%H%M%S).md"

    ${coreutils_bin}/touch "$FILE_PATH"

    VIME=1 ${pkgs.foot}/bin/foot ${pkgs-unstable.neovim}/bin/nvim "$FILE_PATH"

    if [ -f "$FILE_PATH" ]; then
      ${coreutils_bin}/sleep 0.1
      ${coreutils_bin}/printf %s "$(${coreutils_bin}/cat "$FILE_PATH")" | ${pkgs.wl-clipboard}/bin/wl-copy
      while [ "$(${pkgs.wl-clipboard}/bin/wl-paste | ${coreutils_bin}/tr -d '\n')" != "$(${coreutils_bin}/tr -d '\n' < "$FILE_PATH")" ]; do
        ${coreutils_bin}/sleep 0.05
      done

      ${pkgs.libnotify}/bin/notify-send -t 800 "Copy OK" "Sucessfully copied"
    fi
  '';
in
{
  services.greetd = {
    enable = true;
    settings = {
      default_session = {
        command = "${pkgs.tuigreet}/bin/tuigreet --time --remember --cmd sway --theme border=magenta;text=cyan;prompt=green;time=red;action=blue;button=yellow;container=black;input=red";
        user = "greeter";
      };
    };
  };

  security.polkit.enable = true;
  programs.nm-applet.enable = true;

  programs.sway.enable = true;

  environment.systemPackages = [ vime ];

  environment.etc."sway/config".text = ''
    set $mod Mod4
    workspace_layout tabbed

    bindsym $mod+Return exec ${pkgs.foot}/bin/foot , exec ${pkgs-unstable.qutebrowser}/bin/qutebrowser
    bindsym $mod+d exec ${pkgs.rofi}/bin/rofi -modi drun\,run -show drun
    bindsym $mod+Shift+q kill
    bindsym $mod+Shift+e exec ${pkgs.wlogout}/bin/wlogout
    bindsym $mod+Shift+c reload
    bindsym $mod+Tab focus next
    bindsym $mod+j focus right
    bindsym $mod+k focus left

    bindsym $mod+f fullscreen toggle
    bindsym $mod+w layout tabbed
    bindsym $mod+s layout stacking
    bindsym Henkan_Mode exec ${vime}/bin/vime

    bindsym $mod+Shift+s exec ${pkgs.grim}/bin/grim -g "$(${pkgs.slurp}/bin/slurp)" - | ${pkgs.coreutils}/bin/tee ~/Pictures/Screenshots/$(date +%Y-%m-%d_%H-%M-%S).png | ${pkgs.wl-clipboard}/bin/wl-copy && ${pkgs.libnotify}/bin/notify-send "Captured screen"

    bindsym XF86AudioRaiseVolume exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
    bindsym XF86AudioLowerVolume exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
    bindsym XF86AudioMute exec ${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle

    bar {
      swaybar_command waybar
    }

    font pango:monospace 1
    titlebar_padding 1
    titlebar_border_thickness 1
    client.focused #000000 #000000 #000000 #000000 #000000
    client.unfocused #000000 #000000 #000000 #000000 #000000

    input "type:keyboard" {
      xkb_layout "jp"
    }

    exec ${pkgs.networkmanagerapplet}/bin/nm-applet --indicator
    exec ${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1
    exec dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=sway
    exec ${pkgs.dunst}/bin/dunst
  '';
  programs.waybar.enable = true;
}

vimeが皆さんご存知VIME、greetdがディスプレイマネージャ、polkitはPW系だかを管理してくれるやつ、nm-appletはネットワーク(WiFi)を管理してくれるやつです。

grimは多分スクショツール、libnotifyはnotify-sendコマンド用です。wl-clipboardwl-copy用、wlogoutはいい感じのログアウトメニューを提供してくれるやつです。

そしたら設定本体の解説パートです。

とりあえず$modをWinキー相当に設定しておきます。 次にかなり大事なtabsレイアウトを設定します。

returnではOSを起動した瞬間に起動するアプリを設定しておきます(自動起動にしてもいいかも)。dでランチャー(rofi)が起動します。 Qではそのウィンドウを閉じます。いつもの右上のやつですね。Macユーザーは…知りません。Cは設定のリロードです。configのcでしょうか。 tabは使ってないので分かりません。jとkでウィンドウ移動します。タイル型だと結構お約束だと思います。 f、w、sのモード変更系は使ってません。変換キーは例のVIME起動です。無論OSのIMEはないので生命線です。 Sでスクリーンショットです。Windowsのやつを意識しました。音量キーの対応もしてあります。というか対応しないと早朝と昼間で音量調整できなくて困る。

waybar使え設定してありますね。waybarの設定は後で紹介します。 waybarではウィンドウ一覧も表示する設定にするのですが、sway側でウィンドウ一覧を上に表示するのを抑制することができません。 ということで極限まで小さくして真っ黒にすることでほぼなかったことにしてます。 まあ当たり判定はあるのでマウスを上に持って行ってクリックすると別ウィンドウに飛んだりしますが。

キー配列はJIS配列です。US配列の方が合理的らしいですがぶっちゃけ覚えられないので却下。 さっきのnm-appletとpolkitを起動してなんかwaybarとかが動かないのでdbusの環境変数を設定して通知デーモン叩き起こして終了です。

desktop.nix

home-manager側のDE系の設定です。ちゃんこ鍋ぐらいごちゃ混ぜなのでdesktop.nixにしてあります。

{ config, ... }:
let
  inherit (config.lib.formats.rasi) mkLiteral;
in
{
  programs.wlogout = {
    enable = true;
    layout = [
      {
        label = "shutdown";
        action = "systemctl poweroff";
        text = "Shutdown (S)";
        keybind = "s";
      }
      {
        label = "reboot";
        action = "systemctl reboot";
        text = "Reboot (R)";
        keybind = "r";
      }
      {
        label = "lock";
        action = "swaylock";
        text = "Lock (L)";
        keybind = "l";
      }
      {
        label = "logout";
        action = "swaymsg exit";
        text = "Logout (E)";
        keybind = "e";
      }
      {
        label = "hibernate";
        action = "systemctl hibernate";
        text = "Hibernate (H)";
        keybind = "h";
      }
      {
        label = "suspend";
        action = "systemctl suspend";
        text = "Suspend (U)";
        keybind = "u";
      }
    ];
  };
  programs.rofi = {
    enable = true;
    theme = {
      "*" = {
        bg = mkLiteral "#24283b";
        hv = mkLiteral "#9274ca";
        primary = mkLiteral "#C5C8C6";
        ug = mkLiteral "#0B2447";
        font = "Monospace 11";
        background-color = mkLiteral "@bg";
        border = mkLiteral "0px";
        kl = mkLiteral "#7aa2f7";
        black = mkLiteral "#000000";
        transparent = mkLiteral "rgba(46,52,64,0)";
      };

      "window" = {
        width = 700;
        orientation = mkLiteral "horizontal";
        location = mkLiteral "center";
        anchor = mkLiteral "center";
        transparency = "screenshot";
        border-color = mkLiteral "@primary";
        border = mkLiteral "3px";
        border-radius = mkLiteral "6px";
        spacing = 0;
        children = map mkLiteral [ "mainbox" ];
      };

      "mainbox" = {
        spacing = 0;
        children = map mkLiteral [
          "inputbar"
          "message"
          "listview"
        ];
      };

      "inputbar" = {
        color = mkLiteral "@kl";
        padding = mkLiteral "11px";
        border = mkLiteral "0 0 2px 0";
        border-color = mkLiteral "@primary";
      };

      "message" = {
        padding = 0;
      };

      "textbox" = {
        color = mkLiteral "@kl";
        padding = mkLiteral "10px";
      };

      "entry, prompt, case-indicator" = {
        text-font = mkLiteral "inherit";
        text-color = mkLiteral "inherit";
      };

      "entry" = {
        cursor = mkLiteral "pointer";
      };

      "prompt" = {
        margin = mkLiteral "0px 5px 0px 0px";
      };

      "listview" = {
        layout = mkLiteral "vertical";
        padding = mkLiteral "8px";
        lines = 12;
        columns = 1;
        dynamic = false;
      };

      "element" = {
        padding = mkLiteral "2px";
        vertical-align = 1;
        color = mkLiteral "@kl";
        font = mkLiteral "inherit";
      };

      "element-text" = {
        background-color = mkLiteral "inherit";
        text-color = mkLiteral "inherit";
      };

      "element selected.normal" = {
        color = mkLiteral "@black";
        background-color = mkLiteral "@hv";
      };

      "element normal active" = {
        background-color = mkLiteral "@hv";
        color = mkLiteral "@black";
      };

      "element-text, element-icon" = {
        background-color = mkLiteral "inherit";
        text-color = mkLiteral "inherit";
      };

      "element normal urgent" = {
        background-color = mkLiteral "@primary";
      };

      "element selected active" = {
        background = mkLiteral "@hv";
        foreground = mkLiteral "@bg";
      };

      "button" = {
        padding = mkLiteral "6px";
        color = mkLiteral "@primary";
        horizontal-align = mkLiteral "0.5";

        border = mkLiteral "2px 0px 2px 2px";
        border-radius = mkLiteral "4px 0px 0px 4px";
        border-color = mkLiteral "@primary";
      };

      "button selected normal" = {
        border = mkLiteral "2px 0px 2px 2px";
        border-color = mkLiteral "@primary";
      };

      "scrollbar" = {
        enabled = true;
      };
    };
    extraConfig = {
      modi = "combi";
      show-icons = true;
    };
  };
  services.dunst = {
    enable = true;
    settings = {
      global = {
        layer = "top";
        monitor = 0;
        follow = "mouse";
        width = 300;
        height = "(0, 300)";
        offset = "(30, 30)";
        origin = "bottom-left";
        transparency = 10;
        font = "BitstromWera Nerd Font Mono 13, Noto Sans Mono CJK JP 13";
        frame_width = 1;
        frame_color = "#888888";
      };
      urgency_normal = {
        background = "#222436";
        foreground = "#c8d3f5";
        frame_color = "#c8d3f5";
        timeout = 10;
      };
      urgency_low = {
        background = "#1e2030";
        foreground = "#c8d3f5";
        frame_color = "#c8d3f5";
        timeout = 5;
      };
      urgency_critical = {
        background = "#2f334d";
        foreground = "#c53b53";
        frame_color = "#c53b53";
        timeout = 20;
      };
    };
  };
}

wlogout、rofi、dunstの設定です。wlogoutの方は適当に内容をカスタマイズしています。 標準でキー一発でできる仕様がなかった気がするので自分で設定せざるを得ない。 dunstとrofiはTokyo night系のテーマっぽくして(dunstは)大きさ系の設定をして終了。

waybar/config

地味にwaybarもswayとセットで設定したので書いていきます。なんかwaybarのconfigはxdg.configFileで配置してます。なんででしょうか。

{
    "layer": "top",
    "position": "left",
    "width": 40,
    "spacing": 5,
    "modules-left": ["sway/workspaces", "wlr/taskbar"],
    "modules-center": [],
    "modules-right": ["cpu", "memory", "pulseaudio", "network", "custom/bluetooth", "clock", "battery"],

    "cpu": {
        "interval": 1,
        "format": "C{usage}%"
    },
    "memory": {
        "interval": 1,
        "format": "M{percentage}%",
        "tooltip-format": "RAM: {used:0.1f}GB / {total:0.1f}GB ({percentage}%)"
    },
    "pulseaudio": {
        "format": "{icon}\n{volume}%",
        "format-muted": "󰝟",
        "format-icons": {
            "default": ["󰕿", "󰖀", "󰕾"]
        },
        "on-click": "pavucontrol"
    },
    "network": {
        "format-wifi": "",
        "format-ethernet": "󰈀",
        "format-disconnected": "󰤮",
        "on-click": "foot nmtui"
    },
    "custom/bluetooth": {
        "format": "",
        "on-click": "foot bluetuith"
    },
    "clock": {
        "format": "{:%m/%d\n%H:%M}"
    },
    "battery": {
        "interval": 1,
        "states": {
            "warning": 30,
            "critical": 15
        },
        "format": "{capacity}%",
        "format-charging": "[{capacity}%]",
        "format-plugged": "[{capacity}%]",
    },
    "wlr/taskbar": {
        "format": "{icon}",
        "on-click": "activate",
        "on-click-middle": "close",
        "sort-by-number": true
    }
}

とりあえず左垂直タブ至上主義者なので左に設置します。

そして件のタスクバーを配置します。このタスクバーとswayのfocus left/rightが往々にして合致しないのでよく頭がこんがらがっています。

そして今までtmuxにあったCPU/RAM使用率を出しています。 Qutebrowser使っていると(古いバージョンだったのが悪いのか)メモリがとんでもないことになってGraphic processがearlyoomくんに斬られるんですよね。 なのでタスクバーに配置しています。CPUはおまけです。正直20%越えることすらほぼない。 (ちなみにQutebrowserを新しいバージョンにした結果メモリは抑えられましたがYouTubeの動画を新しいタブで開こうとして固まることがあります。解決できたら記事にするかも)

そしたら音量表示をしておきます。これがないと今何%なのか分からなくなって早朝に爆音BGMを流すことになってしまいます。まあこれはいい感じに。 次にnetwork/bluetooth系のボタンを表示しておきます。これらはTUIのアプリへのリンクにしてあります。bluetuithくんの閉じる方法分かりにくいからなんとかしたい。

そしたらdatetime表示です。01/02\n15:04みたいな感じで結構オーソドックスな感じだと思います。 秒表示欲しいですがちょっと充電消費が気になるのと、費用(スペース)対効果が低いので放置で大丈夫でしょう。

一番下にバッテリー表示があります。充電中はブラケットで囲むようになってるのがこだわりポイントです。

waybar/style.css

一応style.cssもありますが解説しようにもconfigと密結合すぎて解説しづらそうなので解説なしです。

* {
  font-family: monospace;
  font-size: 14px;
}

window#waybar {
  background: rgba(43, 48, 59, 0.5);
  color: #ffffff;
}

#workspaces button {
  padding: 0 5px;
  color: #ffffff;
}

#taskbar button {
  padding: 5px;
  border-left: 3px solid transparent;
}

#taskbar button.active {
  background-color: rgba(255, 255, 255, 0.1);
  border-left: 3px solid #ffffff;
  color: #ffffff;
}

#taskbar button:hover {
  background-color: rgba(255, 255, 255, 0.2);
}

#battery {
  color: #ffffff;
  margin-top: 10px;
}

#battery.charging,
#battery.plugged {
  color: #26A65B;
  font-weight: bold;
}

#memory.warning {
  color: #f1c40f;
}

#memory.critical {
  color: #e74c3c;
}

今後の計画

waybarの背景色を一時間で色相環が一周するようにしたいです。

あと最初の選定条件の4つ目(背景透過で背後ウィンドウ)もやりたいですね。

まとめ

自分好みに改造できる範囲がまた一つ広がりました。