about summary refs log tree commit diff
path: root/modules/workstation/windowing/sway
diff options
context:
space:
mode:
Diffstat (limited to 'modules/workstation/windowing/sway')
-rw-r--r--modules/workstation/windowing/sway/choose_workspace.nix9
-rw-r--r--modules/workstation/windowing/sway/choose_workspace.sh.in17
-rw-r--r--modules/workstation/windowing/sway/config.in125
-rw-r--r--modules/workstation/windowing/sway/default.nix49
-rw-r--r--modules/workstation/windowing/sway/status.cpp201
-rw-r--r--modules/workstation/windowing/sway/status.nix6
-rw-r--r--modules/workstation/windowing/sway/swayidle/default.nix23
-rw-r--r--modules/workstation/windowing/sway/swaylock/config.in3
-rw-r--r--modules/workstation/windowing/sway/swaylock/default.nix12
-rw-r--r--modules/workstation/windowing/sway/wallpaper.nix10
-rw-r--r--modules/workstation/windowing/sway/wlsunset/default.nix9
-rw-r--r--modules/workstation/windowing/sway/xdg-desktop-portal-wlr/default.nix13
12 files changed, 477 insertions, 0 deletions
diff --git a/modules/workstation/windowing/sway/choose_workspace.nix b/modules/workstation/windowing/sway/choose_workspace.nix
new file mode 100644
index 000000000000..fc162d627b60
--- /dev/null
+++ b/modules/workstation/windowing/sway/choose_workspace.nix
@@ -0,0 +1,9 @@
+{ substituteAll, bemenu, jq }:
+
+substituteAll {
+  dir = "bin";
+  name = "choose_workspace";
+  src = ./choose_workspace.sh.in;
+  isExecutable = true;
+  inherit bemenu jq;
+}
diff --git a/modules/workstation/windowing/sway/choose_workspace.sh.in b/modules/workstation/windowing/sway/choose_workspace.sh.in
new file mode 100644
index 000000000000..963746e0c810
--- /dev/null
+++ b/modules/workstation/windowing/sway/choose_workspace.sh.in
@@ -0,0 +1,17 @@
+#! @shell@ -ue
+swaymsg -t get_workspaces |
+    @jq@/bin/jq -r \
+        '(to_entries | map(select(.value.focused)) | .[0].key), .[].name' |
+    (
+        read index
+        exec @bemenu@/bin/bemenu \
+            -p workspace \
+            -I "$index" \
+            -H 24 \
+            --fn 'monospace 10' \
+            --nf '#777777' \
+            --hb '#285577' \
+            --hf '#ffffff' \
+            --tf '#777777' \
+            --ff '#ffffff'
+    )
diff --git a/modules/workstation/windowing/sway/config.in b/modules/workstation/windowing/sway/config.in
new file mode 100644
index 000000000000..52efc1fa90e3
--- /dev/null
+++ b/modules/workstation/windowing/sway/config.in
@@ -0,0 +1,125 @@
+set $mod Mod4
+set $left h
+set $down j
+set $up k
+set $right l
+
+default_border pixel
+default_floating_border normal
+
+client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a00
+client.unfocused        #333333 #222222 #888888 #292d2e #22222200
+
+for_window [app_id="float"] floating enable
+for_window [app_id="firefox" title="Picture-in-Picture"] floating enable
+for_window [class="Tor Browser"] floating enable
+for_window [class="XDvi"] floating enable
+for_window [instance="xdvi"] floating enable
+for_window [class="XDvi" instance="xdvi"] floating disable
+for_window [app_id="firefox" title="NoScript.*"] floating enable
+
+# Stop the Firefox sharing indicator (that appears when on an
+# audio/video call) tiling, or appearing in the center of the display,
+# or being focused.
+no_focus [title="(Firefox|Nightly) . Sharing Indicator"]
+for_window [title="(Firefox|Nightly) . Sharing Indicator"] {
+    floating enable
+    sticky enable
+
+    # I'd really like this to be at the top, horizontally centered.
+    # Or maybe at the bottom right.  But there's not really a way to
+    # do that in sway configuration, as far as I can tell.  I think
+    # I'd need to exec a program that would measure the display size
+    # and compute the coordinates or something.  That sounds horrible
+    # and fragile, so top left it is.
+    move position 0 0
+}
+
+input * natural_scroll enabled
+
+bindsym $mod+Return exec foot
+bindsym $mod+backslash exec firefox
+bindsym $mod+BackSpace kill
+bindsym $mod+d exec swaymsg exec "$(choosebin --tiebreak=begin,length,index)"
+
+# Brightness control
+bindsym $mod+F5 exec brightnessctl s 10%-
+bindsym $mod+F6 exec brightnessctl s 10%+
+
+# MPRIS
+bindsym $mod+F7 exec playerctl play-pause
+
+# PulseAudio
+bindsym $mod+F8 exec pactl set-sink-mute @DEFAULT_SINK@ toggle
+bindsym $mod+F9 exec pactl set-sink-volume @DEFAULT_SINK@ -5%
+bindsym $mod+F10 exec pactl set-sink-volume @DEFAULT_SINK@ +5%
+bindsym XF86AudioLowerVolume exec pactl set-sink-volume @DEFAULT_SINK@ -5%
+bindsym XF86AudioRaiseVolume exec pactl set-sink-volume @DEFAULT_SINK@ +5%
+
+# Drag floating windows by holding down $mod and left mouse button.
+# Resize them with right mouse button + $mod.
+# Despite the name, also works for non-floating windows.
+# Change normal to inverse to use left mouse button for resizing and right
+# mouse button for dragging.
+floating_modifier $mod normal
+
+# reload the configuration file
+bindsym $mod+Shift+c reload
+
+# exit sway (logs you out of your Wayland session)
+bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
+
+bindsym $mod+$left focus left
+bindsym $mod+$down focus down
+bindsym $mod+$up focus up
+bindsym $mod+$right focus right
+
+bindsym $mod+Shift+$left move left
+bindsym $mod+Shift+$down move down
+bindsym $mod+Shift+$up move up
+bindsym $mod+Shift+$right move right
+
+bindsym $mod+g exec swaymsg workspace "$(@choose_workspace@)"
+bindsym $mod+Shift+g exec swaymsg move container to workspace "$(@choose_workspace@)"
+
+bindsym $mod+b splith
+bindsym $mod+v splitv
+
+bindsym $mod+s layout stacking
+bindsym $mod+w layout tabbed
+bindsym $mod+e layout toggle split
+
+bindsym $mod+f fullscreen
+
+bindsym $mod+Shift+space floating toggle
+bindsym $mod+space focus mode_toggle
+
+bindsym $mod+a focus parent
+
+bindsym $mod+Shift+minus move scratchpad
+bindsym $mod+minus scratchpad show
+
+mode "resize" {
+    bindsym $left resize shrink width 10px
+    bindsym $down resize grow height 10px
+    bindsym $up resize shrink height 10px
+    bindsym $right resize grow width 10px
+
+    bindsym Return mode "default"
+    bindsym Escape mode "default"
+}
+bindsym $mod+r mode "resize"
+
+bar {
+    position top
+
+    status_command @status_command@
+
+    colors {
+        statusline #ffffff
+        background #00000077
+        inactive_workspace #33333377 #00000077 #FFFFFF77
+    }
+}
+
+@extraConfig@
diff --git a/modules/workstation/windowing/sway/default.nix b/modules/workstation/windowing/sway/default.nix
new file mode 100644
index 000000000000..675ef8dbc031
--- /dev/null
+++ b/modules/workstation/windowing/sway/default.nix
@@ -0,0 +1,49 @@
+{ pkgs, lib, config, ... }:
+
+let
+  inherit (lib) mdDoc mkOption optionalString;
+  inherit (lib.types) lines nullOr path;
+  inherit (pkgs) callPackage substituteAll;
+
+  cfg = config.programs.sway;
+in
+
+{
+  imports = [ ./swayidle ./swaylock ./wlsunset ./xdg-desktop-portal-wlr ];
+
+  options = {
+    programs.sway.extraConfig = mkOption {
+      type = lines;
+      description = mdDoc "Lines to append to sway's config file";
+      default = "";
+    };
+
+    programs.sway.wallpaper = mkOption {
+      type = nullOr path;
+      description = mdDoc "Path to wallpaper for sway and swaylock";
+      default = null;
+    };
+  };
+
+  config = {
+    environment.systemPackages = with pkgs; [ bemenu choose swayidle ];
+
+    programs.sway.enable = true;
+    programs.sway.wallpaper = callPackage ./wallpaper.nix { };
+    programs.sway.extraPackages = []; # extra packages can go in systemPackages.
+
+    programs.swayidle.enable = true;
+
+    users.users.qyliss.xdg.config.paths."sway/config" = substituteAll {
+      src = ./config.in;
+      choose_workspace =
+        "${callPackage ./choose_workspace.nix { }}/bin/choose_workspace";
+      status_command = "${callPackage ./status.nix { }}/bin/status";
+      extraConfig = cfg.extraConfig + optionalString (cfg.wallpaper != null) ''
+        output * bg ${cfg.wallpaper} fill
+      '';
+    };
+
+    xdg.portal.extraPortals = with pkgs; [ xdg-desktop-portal-gtk ];
+  };
+}
diff --git a/modules/workstation/windowing/sway/status.cpp b/modules/workstation/windowing/sway/status.cpp
new file mode 100644
index 000000000000..1f73458d86b2
--- /dev/null
+++ b/modules/workstation/windowing/sway/status.cpp
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// Copyright 2020 Alyssa Ross
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+#include <chrono>
+#include <fcntl.h>
+#include <filesystem>
+#include <iostream>
+#include <thread>
+#include <unistd.h>
+
+namespace fs = std::filesystem;
+
+using fs::directory_iterator;
+using std::chrono::seconds;
+using std::generic_category;
+using std::put_time;
+using std::stoi;
+using std::string;
+using std::stringstream;
+using std::system_error;
+using std::this_thread::sleep_for;
+using std::time;
+
+enum BatteryStatus {
+	Unknown,
+	Charging,
+	Discharging,
+	NotCharging,
+	Full,
+};
+
+class Battery {
+public:
+	Battery(string name);
+
+	string name() { return m_name; }
+	fs::path path();
+	BatteryStatus status();
+	int capacity();
+	int charge_now();
+
+private:
+	string m_name;
+	string attr(string name);
+};
+
+Battery::Battery(string name)
+{
+	m_name = name;
+}
+
+fs::path Battery::path()
+{
+	static fs::path base = "/sys/class/power_supply";
+	return base / name();
+}
+
+BatteryStatus Battery::status()
+{
+	auto status = attr("status");
+
+	if (status == "Charging")
+		return Charging;
+	if (status == "Discharging")
+		return Discharging;
+	if (status == "Not charging")
+		return NotCharging;
+	if (status == "Full")
+		return Full;
+
+	return Unknown;
+}
+
+int Battery::capacity()
+{
+	return stoi(attr("capacity"));
+}
+
+int Battery::charge_now()
+{
+	return stoi(attr("charge_now"));
+}
+
+string Battery::attr(string name)
+{
+	// Use read() to make sure this is done in a single read syscall,
+	// because sysfs doesn't like multiple reads.
+	int fd = open((path() / name).c_str(), O_RDONLY);
+	if (fd == -1)
+		throw system_error(errno, generic_category());
+	char buf[13];
+	int len = read(fd, &buf, 13);
+	if (len == -1)
+		throw system_error(errno, generic_category());
+	close(fd);
+	string value (buf, len);
+	if (value.back() == '\n')
+		value.pop_back();
+	return value;
+}
+
+int main(void)
+{
+	while (true) {
+		// Buffer output so it can be done all at once in a single write(),
+		// because of <https://github.com/swaywm/sway/issues/3857>.
+		stringstream out;
+
+		for (const auto& entry : directory_iterator("/sys/class/power_supply")) {
+			auto name = entry.path().filename().string();
+
+			Battery battery (name);
+			auto batdisplay = false;
+
+			try {
+				switch (battery.status()) {
+				case Charging:
+					out << "↑";
+					break;
+				case Discharging:
+					out << "↓";
+					break;
+				default:
+					out << " ";
+				}
+				batdisplay = true;
+			} catch (const system_error& ex) {
+				switch (ex.code().value()) {
+				case ENOENT:
+					break;
+				case ENODEV:
+					out << "? ";
+					batdisplay = true;
+					break;
+				default:
+					throw ex;
+				}
+			}
+
+			try {
+				int capacity = battery.capacity();
+				out << capacity << "%";
+				batdisplay = true;
+			} catch (const system_error& ex) {
+				switch (ex.code().value()) {
+				case ENOENT:
+					break;
+				case ENODEV:
+					out << "??%";
+					batdisplay = true;
+					break;
+				default:
+					throw ex;
+				}
+
+				try {
+					int charge_now = battery.charge_now();
+					out << charge_now;
+					batdisplay = true;
+				} catch (const system_error& ex) {
+					switch (ex.code().value()) {
+					case ENOENT:
+						break;
+					case ENODEV:
+						out << "??????";
+						batdisplay = true;
+						break;
+					default:
+						throw ex;
+					}
+				}
+			}
+
+			if (batdisplay)
+				out << "  ";
+		}
+
+		auto t = time(nullptr);
+		out << put_time(localtime(&t), "%F %T") << "\n";
+
+		auto buf = out.str();
+		if (write(STDOUT_FILENO, buf.c_str(), buf.length()) == -1)
+			perror("write");
+
+		sleep_for(seconds(1));
+	}
+}
diff --git a/modules/workstation/windowing/sway/status.nix b/modules/workstation/windowing/sway/status.nix
new file mode 100644
index 000000000000..2697317d7611
--- /dev/null
+++ b/modules/workstation/windowing/sway/status.nix
@@ -0,0 +1,6 @@
+{ runCommandCC }:
+
+runCommandCC "status" {} ''
+  mkdir -p $out/bin
+  c++ -std=c++17 -o $out/bin/status ${./status.cpp}
+''
diff --git a/modules/workstation/windowing/sway/swayidle/default.nix b/modules/workstation/windowing/sway/swayidle/default.nix
new file mode 100644
index 000000000000..8e5f264b5038
--- /dev/null
+++ b/modules/workstation/windowing/sway/swayidle/default.nix
@@ -0,0 +1,23 @@
+{ lib, config, ... }:
+
+let
+  cfg = config.programs.swayidle;
+in
+
+with lib;
+
+{
+  options = {
+    programs.swayidle.enable = mkEnableOption "swayidle";
+  };
+
+  config = mkIf cfg.enable {
+    programs.sway.extraConfig = ''
+      exec swayidle \
+          timeout 300 'swaylock -c 000000' \
+          timeout 600 'swaymsg "output * dpms off"' \
+          resume 'swaymsg "output * dpms on"' \
+          before-sleep 'swaylock -c 000000'
+    '';
+  };
+}
diff --git a/modules/workstation/windowing/sway/swaylock/config.in b/modules/workstation/windowing/sway/swaylock/config.in
new file mode 100644
index 000000000000..c6f280aa8f5b
--- /dev/null
+++ b/modules/workstation/windowing/sway/swaylock/config.in
@@ -0,0 +1,3 @@
+image=@wallpaper@
+indicator-idle-visible
+show-failed-attempts
diff --git a/modules/workstation/windowing/sway/swaylock/default.nix b/modules/workstation/windowing/sway/swaylock/default.nix
new file mode 100644
index 000000000000..be15c87ade32
--- /dev/null
+++ b/modules/workstation/windowing/sway/swaylock/default.nix
@@ -0,0 +1,12 @@
+{ pkgs, config, ... }:
+
+{
+  imports = [ ../../../../xdg ];
+
+  environment.systemPackages = with pkgs; [ swaylock ];
+
+  users.users.qyliss.xdg.config.paths."swaylock/config" = pkgs.substituteAll {
+    src = ./config.in;
+    wallpaper = config.programs.sway.wallpaper;
+  };
+}
diff --git a/modules/workstation/windowing/sway/wallpaper.nix b/modules/workstation/windowing/sway/wallpaper.nix
new file mode 100644
index 000000000000..bed6bbe33a6b
--- /dev/null
+++ b/modules/workstation/windowing/sway/wallpaper.nix
@@ -0,0 +1,10 @@
+{ fetchurl }:
+
+fetchurl {
+  url = "https://mir-s3-cdn-cf.behance.net/project_modules/2800_opt_1/36731876964505.5c793fa788b5d.jpg";
+  sha256 = "1c6camdipng8ws41sgpcxzrxb96crgip3wirqjgf2ajn60qg3v64";
+
+  meta = {
+    homepage = "https://www.behance.net/gallery/76964505/IQOO-style-frame-and-scene-design";
+  };
+}
diff --git a/modules/workstation/windowing/sway/wlsunset/default.nix b/modules/workstation/windowing/sway/wlsunset/default.nix
new file mode 100644
index 000000000000..ba954f3cd3bd
--- /dev/null
+++ b/modules/workstation/windowing/sway/wlsunset/default.nix
@@ -0,0 +1,9 @@
+{ pkgs, ... }:
+
+{
+  environment.systemPackages = with pkgs; [ wlsunset ];
+  
+  programs.sway.extraConfig = ''
+    exec wlsunset -l 51.5 -L 13.6
+  '';
+}
diff --git a/modules/workstation/windowing/sway/xdg-desktop-portal-wlr/default.nix b/modules/workstation/windowing/sway/xdg-desktop-portal-wlr/default.nix
new file mode 100644
index 000000000000..892e6a280d4c
--- /dev/null
+++ b/modules/workstation/windowing/sway/xdg-desktop-portal-wlr/default.nix
@@ -0,0 +1,13 @@
+{ pkgs, ... }:
+
+{
+  xdg.portal.wlr.enable = true;
+
+  programs.sway.extraConfig = ''
+    exec ${pkgs.writeShellScript "sway-portal-environment" ''
+      set -e
+      dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
+      systemctl --user stop pipewire xdg-desktop-portal xdg-desktop-portal-wlr
+    ''}
+  '';
+}