diff --git a/ansible/environments/laptop/host_vars/odofreebsd b/ansible/environments/laptop/host_vars/odofreebsd
index 4779fac..4d4373d 100644
--- a/ansible/environments/laptop/host_vars/odofreebsd
+++ b/ansible/environments/laptop/host_vars/odofreebsd
@@ -18,3 +18,16 @@ cores: 8
build_user:
name: talexander
group: talexander
+users:
+ talexander:
+ initialize: true
+ uid: 11235
+ gid: 11235
+ groups:
+ - name: wheel
+ - name: video
+ authorized_keys:
+ - yubikey
+ - main_fido
+ - backup_fido
+ - homeassistant
diff --git a/ansible/playbook.yaml b/ansible/playbook.yaml
index 172ed7e..6e9ca02 100644
--- a/ansible/playbook.yaml
+++ b/ansible/playbook.yaml
@@ -10,9 +10,12 @@
- network
- sshd
- base
- - firewall
+ # - firewall
- cpu
- ntp
- build
- graphics
- gpg
+ - fonts
+ - alacritty
+ - sway
diff --git a/ansible/roles/alacritty/files/alacritty.yml b/ansible/roles/alacritty/files/alacritty.yml
new file mode 100644
index 0000000..a15e641
--- /dev/null
+++ b/ansible/roles/alacritty/files/alacritty.yml
@@ -0,0 +1,103 @@
+# If `true`, bold text is drawn using the bright color variants.
+draw_bold_text_with_bright_colors: true
+
+colors:
+ # Default colors
+ primary:
+ background: "0x000000"
+ foreground: "0xeaeaea"
+
+ # Bright and dim foreground colors
+ #
+ # The dimmed foreground color is calculated automatically if it is not present.
+ # If the bright foreground color is not set, or `draw_bold_text_with_bright_colors`
+ # is `false`, the normal foreground color will be used.
+ #dim_foreground: '0x9a9a9a'
+ #bright_foreground: '0xffffff'
+
+ # Cursor colors
+ #
+ # Colors which should be used to draw the terminal cursor. If these are unset,
+ # the cursor color will be the inverse of the cell color.
+ #cursor:
+ # text: '0x000000'
+ # cursor: '0xffffff'
+
+ # Selection colors
+ #
+ # Colors which should be used to draw the selection area. If selection
+ # background is unset, selection color will be the inverse of the cell colors.
+ # If only text is unset the cell text color will remain the same.
+ #selection:
+ # text: '0xeaeaea'
+ # background: '0x404040'
+
+ # Normal colors
+ normal:
+ black: "0x000000"
+ red: "0xd54e53"
+ green: "0xb9ca4a"
+ yellow: "0xe6c547"
+ blue: "0x7aa6da"
+ magenta: "0xc397d8"
+ cyan: "0x70c0ba"
+ white: "0xeaeaea"
+
+ # Bright colors
+ bright:
+ black: "0x666666"
+ red: "0xff3334"
+ green: "0x9ec400"
+ yellow: "0xe7c547"
+ blue: "0x7aa6da"
+ magenta: "0xb77ee0"
+ cyan: "0x54ced6"
+ white: "0xffffff"
+
+ # Dim colors
+ #
+ # If the dim colors are not set, they will be calculated automatically based
+ # on the `normal` colors.
+ #dim:
+ # black: '0x000000'
+ # red: '0x8c3336'
+ # green: '0x7a8530'
+ # yellow: '0x97822e'
+ # blue: '0x506d8f'
+ # magenta: '0x80638e'
+ # cyan: '0x497e7a'
+ # white: '0x9a9a9a'
+
+ # Indexed Colors
+ #
+ # The indexed colors include all colors from 16 to 256.
+ # When these are not set, they're filled with sensible defaults.
+ #
+ # Example:
+ # `- { index: 16, color: '0xff00ff' }`
+ #
+ indexed_colors: []
+
+scrolling:
+ # Maximum number of lines in the scrollback buffer.
+ # Specifying '0' will disable scrolling.
+ history: 10000
+
+ # Number of lines the viewport will move for every line scrolled when
+ # scrollback is enabled (history > 0).
+ multiplier: 3
+
+font:
+ size: 11.0
+
+hints:
+ enabled:
+ # Disable opening links when clicked
+ - regex:
+ "(ipfs:|ipns:|magnet:|mailto:|gemini:|gopher:|https:|http:|news:|file:|git:|ssh:|ftp:)\
+ [^\u0000-\u001F\u007F-\u009F<>\"\\s{-}\\^⟨⟩`]+"
+ command: xdg-open
+ post_processing: true
+ mouse:
+ enabled: false
+ mods: None
diff --git a/ansible/roles/alacritty/meta/main.yaml b/ansible/roles/alacritty/meta/main.yaml
new file mode 100644
index 0000000..63a2741
--- /dev/null
+++ b/ansible/roles/alacritty/meta/main.yaml
@@ -0,0 +1,3 @@
+dependencies:
+ - users
+ - fonts
diff --git a/ansible/roles/alacritty/tasks/common.yaml b/ansible/roles/alacritty/tasks/common.yaml
new file mode 100644
index 0000000..62421de
--- /dev/null
+++ b/ansible/roles/alacritty/tasks/common.yaml
@@ -0,0 +1,20 @@
+- name: Install packages
+ package:
+ name:
+ - alacritty
+ state: present
+
+- import_tasks: tasks/freebsd.yaml
+ when: 'os_flavor == "freebsd"'
+
+- import_tasks: tasks/linux.yaml
+ when: 'os_flavor == "linux"'
+
+- include_tasks:
+ file: tasks/peruser.yaml
+ apply:
+ become: yes
+ become_user: "{{ initialize_user }}"
+ loop: "{{ users | dict2items | community.general.json_query('[?value.initialize==`true`].key') }}"
+ loop_control:
+ loop_var: initialize_user
diff --git a/ansible/roles/alacritty/tasks/freebsd.yaml b/ansible/roles/alacritty/tasks/freebsd.yaml
new file mode 100644
index 0000000..b417174
--- /dev/null
+++ b/ansible/roles/alacritty/tasks/freebsd.yaml
@@ -0,0 +1,5 @@
+# - name: Install packages
+# package:
+# name:
+# - foo
+# state: present
diff --git a/ansible/roles/alacritty/tasks/linux.yaml b/ansible/roles/alacritty/tasks/linux.yaml
new file mode 100644
index 0000000..e1835f0
--- /dev/null
+++ b/ansible/roles/alacritty/tasks/linux.yaml
@@ -0,0 +1,6 @@
+# - name: Install packages
+# pacman:
+# name:
+# - foo
+# state: present
+# update_cache: true
diff --git a/ansible/roles/alacritty/tasks/main.yaml b/ansible/roles/alacritty/tasks/main.yaml
new file mode 100644
index 0000000..dc9939d
--- /dev/null
+++ b/ansible/roles/alacritty/tasks/main.yaml
@@ -0,0 +1,2 @@
+- import_tasks: tasks/common.yaml
+ when: graphics_driver is defined
diff --git a/ansible/roles/alacritty/tasks/peruser.yaml b/ansible/roles/alacritty/tasks/peruser.yaml
new file mode 100644
index 0000000..3c76ba7
--- /dev/null
+++ b/ansible/roles/alacritty/tasks/peruser.yaml
@@ -0,0 +1,29 @@
+- include_role:
+ name: per_user
+
+- name: Create directories
+ file:
+ name: "{{ account_homedir.stdout }}/{{ item }}"
+ state: directory
+ mode: 0700
+ owner: "{{ account_name.stdout }}"
+ group: "{{ group_name.stdout }}"
+ loop:
+ - ".config/alacritty"
+
+- name: Copy files
+ copy:
+ src: "files/{{ item.src }}"
+ dest: "{{ account_homedir.stdout }}/{{ item.dest }}"
+ mode: 0600
+ owner: "{{ account_name.stdout }}"
+ group: "{{ group_name.stdout }}"
+ loop:
+ - src: alacritty.yml
+ dest: .config/alacritty/alacritty.yml
+
+- import_tasks: tasks/peruser_freebsd.yaml
+ when: 'os_flavor == "freebsd"'
+
+- import_tasks: tasks/peruser_linux.yaml
+ when: 'os_flavor == "linux"'
diff --git a/ansible/roles/alacritty/tasks/peruser_freebsd.yaml b/ansible/roles/alacritty/tasks/peruser_freebsd.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/ansible/roles/alacritty/tasks/peruser_linux.yaml b/ansible/roles/alacritty/tasks/peruser_linux.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/ansible/roles/base/tasks/freebsd.yaml b/ansible/roles/base/tasks/freebsd.yaml
index 379f3be..dfd509c 100644
--- a/ansible/roles/base/tasks/freebsd.yaml
+++ b/ansible/roles/base/tasks/freebsd.yaml
@@ -89,3 +89,16 @@
path: /etc/rc.conf
start: absent
when: rc_conf is not defined
+
+- name: Add fstab entries
+ mount:
+ name: "{{ item.dst }}"
+ src: "{{ item.src }}"
+ fstype: "{{ item.fstype }}"
+ opts: "{{ item.opts }}"
+ state: present
+ loop:
+ - dst: /tmp
+ src: tmpfs
+ fstype: tmpfs
+ opts: rw,mode=777
diff --git a/ansible/roles/fonts/files/fonts.conf b/ansible/roles/fonts/files/fonts.conf
new file mode 100644
index 0000000..b918607
--- /dev/null
+++ b/ansible/roles/fonts/files/fonts.conf
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+ /usr/share/fonts/gsfonts/*
+
+
+
+
+
+ serif
+
+ Source Serif Pro
+ Source Sans Pro
+
+
+
+
+
+ sans-serif
+
+ Source Sans Pro
+ Source Serif Pro
+
+
+
+
+
+ monospace
+
+ Cascadia Mono
+ Cascadia Code
+
+
+
+
+
+
+ Liberation Mono
+ Cascadia Mono
+
+
+
+
+ monospace
+ Cascadia Mono
+
+
+
+
+
+ rgb
+
+
+
+
+ true
+
+
+
+
+ hintslight
+
+
+
+
+ true
+
+
+
+
+ lcddefault
+
+
+
diff --git a/ansible/roles/fonts/meta/main.yaml b/ansible/roles/fonts/meta/main.yaml
new file mode 100644
index 0000000..655446a
--- /dev/null
+++ b/ansible/roles/fonts/meta/main.yaml
@@ -0,0 +1,2 @@
+dependencies:
+ - users
diff --git a/ansible/roles/fonts/tasks/common.yaml b/ansible/roles/fonts/tasks/common.yaml
new file mode 100644
index 0000000..d7c1735
--- /dev/null
+++ b/ansible/roles/fonts/tasks/common.yaml
@@ -0,0 +1,14 @@
+- import_tasks: tasks/freebsd.yaml
+ when: 'os_flavor == "freebsd"'
+
+- import_tasks: tasks/linux.yaml
+ when: 'os_flavor == "linux"'
+
+- include_tasks:
+ file: tasks/peruser.yaml
+ apply:
+ become: yes
+ become_user: "{{ initialize_user }}"
+ loop: "{{ users | dict2items | community.general.json_query('[?value.initialize==`true`].key') }}"
+ loop_control:
+ loop_var: initialize_user
diff --git a/ansible/roles/fonts/tasks/freebsd.yaml b/ansible/roles/fonts/tasks/freebsd.yaml
new file mode 100644
index 0000000..f0f1e03
--- /dev/null
+++ b/ansible/roles/fonts/tasks/freebsd.yaml
@@ -0,0 +1,10 @@
+- name: Install packages
+ package:
+ name:
+ - sourcecodepro-ttf
+ - source-sans-ttf
+ - cascadia-code
+ - noto
+ - noto-emoji
+ - noto-extra
+ state: present
diff --git a/ansible/roles/fonts/tasks/linux.yaml b/ansible/roles/fonts/tasks/linux.yaml
new file mode 100644
index 0000000..fd25b1f
--- /dev/null
+++ b/ansible/roles/fonts/tasks/linux.yaml
@@ -0,0 +1,13 @@
+- name: Install packages
+ pacman:
+ name:
+ - adobe-source-code-pro-fonts
+ - adobe-source-sans-fonts
+ # - ttf-cascadia-code # ttf existing at all breaks fonts in emacs
+ - otf-cascadia-code # ttf cascadia broken in emacs
+ - noto-fonts
+ - noto-fonts-cjk
+ - noto-fonts-emoji
+ - noto-fonts-extra
+ state: present
+ update_cache: true
diff --git a/ansible/roles/fonts/tasks/main.yaml b/ansible/roles/fonts/tasks/main.yaml
new file mode 100644
index 0000000..dc9939d
--- /dev/null
+++ b/ansible/roles/fonts/tasks/main.yaml
@@ -0,0 +1,2 @@
+- import_tasks: tasks/common.yaml
+ when: graphics_driver is defined
diff --git a/ansible/roles/fonts/tasks/peruser.yaml b/ansible/roles/fonts/tasks/peruser.yaml
new file mode 100644
index 0000000..b1cadee
--- /dev/null
+++ b/ansible/roles/fonts/tasks/peruser.yaml
@@ -0,0 +1,29 @@
+- include_role:
+ name: per_user
+
+- name: Create directories
+ file:
+ name: "{{ account_homedir.stdout }}/{{ item }}"
+ state: directory
+ mode: 0700
+ owner: "{{ account_name.stdout }}"
+ group: "{{ group_name.stdout }}"
+ loop:
+ - ".config/fontconfig"
+
+- name: Copy files
+ copy:
+ src: "files/{{ item.src }}"
+ dest: "{{ account_homedir.stdout }}/{{ item.dest }}"
+ mode: 0600
+ owner: "{{ account_name.stdout }}"
+ group: "{{ group_name.stdout }}"
+ loop:
+ - src: fonts.conf
+ dest: .config/fontconfig/fonts.conf
+
+- import_tasks: tasks/peruser_freebsd.yaml
+ when: 'os_flavor == "freebsd"'
+
+- import_tasks: tasks/peruser_linux.yaml
+ when: 'os_flavor == "linux"'
diff --git a/ansible/roles/fonts/tasks/peruser_freebsd.yaml b/ansible/roles/fonts/tasks/peruser_freebsd.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/ansible/roles/fonts/tasks/peruser_linux.yaml b/ansible/roles/fonts/tasks/peruser_linux.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/ansible/roles/sway/defaults/main.yaml b/ansible/roles/sway/defaults/main.yaml
new file mode 100644
index 0000000..b75baaf
--- /dev/null
+++ b/ansible/roles/sway/defaults/main.yaml
@@ -0,0 +1,11 @@
+default_sway_conf_files:
+ - background
+ - framework_input
+ - framework_display
+ - base_hotkeys
+ - movement
+ - windows
+ - screenshots
+ - notifications
+ - disable_focus_follows_mouse
+ - waybar
diff --git a/ansible/roles/sway/files/bliss.jpg b/ansible/roles/sway/files/bliss.jpg
new file mode 100644
index 0000000..ee7cd88
Binary files /dev/null and b/ansible/roles/sway/files/bliss.jpg differ
diff --git a/ansible/roles/sway/files/config b/ansible/roles/sway/files/config
new file mode 100644
index 0000000..2a64f88
--- /dev/null
+++ b/ansible/roles/sway/files/config
@@ -0,0 +1,27 @@
+# Default config for sway
+#
+# Copy this to ~/.config/sway/config and edit it to your liking.
+#
+# Read `man 5 sway` for a complete reference.
+
+### Variables
+#
+# Logo key. Use Mod1 for Alt.
+set $mod Mod4
+# set $mod Mod1
+# Home row direction keys, like vim
+set $left h
+set $down j
+set $up k
+set $right l
+# Your preferred terminal emulator
+set $term alacritty
+# Your preferred application launcher
+# Note: it's recommended that you pass the final command to sway
+# set $menu dmenu_path | dmenu | xargs swaymsg exec
+set $menu /usr/local/bin/wofi --show drun --gtk-dark
+
+bindsym $mod+grave exec $term
+
+include ~/.config/sway/config.d/*.conf
+include /etc/sway/config.d/*
diff --git a/ansible/roles/sway/files/dbus_rc.conf b/ansible/roles/sway/files/dbus_rc.conf
new file mode 100644
index 0000000..68bc109
--- /dev/null
+++ b/ansible/roles/sway/files/dbus_rc.conf
@@ -0,0 +1 @@
+dbus_enable="yes"
diff --git a/ansible/roles/sway/files/electron-flags.conf b/ansible/roles/sway/files/electron-flags.conf
new file mode 100644
index 0000000..51bdd86
--- /dev/null
+++ b/ansible/roles/sway/files/electron-flags.conf
@@ -0,0 +1,2 @@
+--enable-features=UseOzonePlatform
+--ozone-platform=wayland
diff --git a/ansible/roles/sway/files/launch_sway_freebsd.bash b/ansible/roles/sway/files/launch_sway_freebsd.bash
new file mode 100644
index 0000000..720bbd1
--- /dev/null
+++ b/ansible/roles/sway/files/launch_sway_freebsd.bash
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+#
+# Launch sway
+set -euo pipefail
+IFS=$'\n\t'
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+if [[ ! -v XDG_RUNTIME_DIR ]]; then
+ export XDG_RUNTIME_DIR=$(mktemp -d)
+ chmod 0700 "$XDG_RUNTIME_DIR"
+
+fi
+
+exec sway -d &> $HOME/.config/swaylog
diff --git a/ansible/roles/sway/files/launch_sway_linux.bash b/ansible/roles/sway/files/launch_sway_linux.bash
new file mode 100644
index 0000000..f7d9561
--- /dev/null
+++ b/ansible/roles/sway/files/launch_sway_linux.bash
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+#
+# Launch sway
+set -euo pipefail
+IFS=$'\n\t'
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+exec sway -d &> $HOME/.config/swaylog
diff --git a/ansible/roles/sway/files/mako_config b/ansible/roles/sway/files/mako_config
new file mode 100644
index 0000000..9e53de7
--- /dev/null
+++ b/ansible/roles/sway/files/mako_config
@@ -0,0 +1,2 @@
+[mode=do-not-disturb]
+invisible=1
diff --git a/ansible/roles/sway/files/seatd_rc.conf b/ansible/roles/sway/files/seatd_rc.conf
new file mode 100644
index 0000000..bdbb6df
--- /dev/null
+++ b/ansible/roles/sway/files/seatd_rc.conf
@@ -0,0 +1 @@
+seatd_enable="YES"
\ No newline at end of file
diff --git a/ansible/roles/sway/files/spaceglenda300.jpg b/ansible/roles/sway/files/spaceglenda300.jpg
new file mode 100644
index 0000000..d2bab4d
Binary files /dev/null and b/ansible/roles/sway/files/spaceglenda300.jpg differ
diff --git a/ansible/roles/sway/files/style.css b/ansible/roles/sway/files/style.css
new file mode 100644
index 0000000..1ccc260
--- /dev/null
+++ b/ansible/roles/sway/files/style.css
@@ -0,0 +1,190 @@
+/* -----------------------------------------------------------------------------
+ * Keyframes
+ * -------------------------------------------------------------------------- */
+
+@keyframes blink-warning {
+ 70% {
+ color: white;
+ }
+
+ to {
+ color: white;
+ background-color: orange;
+ }
+}
+
+@keyframes blink-critical {
+ 70% {
+ color: white;
+ }
+
+ to {
+ color: white;
+ background-color: red;
+ }
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Base styles
+ * -------------------------------------------------------------------------- */
+
+/* Reset all styles */
+* {
+ border: none;
+ border-radius: 0;
+ min-height: 0;
+ margin: 0;
+ padding: 0;
+}
+
+/* The whole bar */
+#waybar {
+ background: #323232;
+ color: white;
+ font-family: Cascadia Mono, monospace;
+ font-size: 10px;
+}
+
+/* Each module */
+#battery,
+#clock,
+#cpu,
+#custom-keyboard-layout,
+#custom-clock,
+#memory,
+#mode,
+#network,
+#pulseaudio,
+#temperature,
+#tray {
+ padding-left: 10px;
+ padding-right: 10px;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Module styles
+ * -------------------------------------------------------------------------- */
+
+#battery {
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+ animation-direction: alternate;
+}
+
+#battery.warning {
+ color: orange;
+}
+
+#battery.critical {
+ color: red;
+}
+
+#battery.warning.discharging {
+ animation-name: blink-warning;
+ animation-duration: 3s;
+}
+
+#battery.critical.discharging {
+ animation-name: blink-critical;
+ animation-duration: 2s;
+}
+
+#clock {
+ font-weight: bold;
+}
+
+#custom-clock {
+}
+
+#cpu {
+ /* No styles */
+}
+
+#cpu.warning {
+ color: orange;
+}
+
+#cpu.critical {
+ color: red;
+}
+
+#memory {
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+ animation-direction: alternate;
+}
+
+#memory.warning {
+ color: orange;
+}
+
+#memory.critical {
+ color: red;
+ animation-name: blink-critical;
+ animation-duration: 2s;
+}
+
+#mode {
+ background: #64727D;
+ border-top: 2px solid white;
+ /* To compensate for the top border and still have vertical centering */
+ padding-bottom: 2px;
+}
+
+#network {
+ /* No styles */
+}
+
+#network.disconnected {
+ color: orange;
+}
+
+#pulseaudio {
+ /* No styles */
+}
+
+#pulseaudio.muted {
+ /* No styles */
+}
+
+#custom-spotify {
+ color: rgb(102, 220, 105);
+}
+
+#temperature {
+ /* No styles */
+}
+
+#temperature.critical {
+ color: red;
+}
+
+#tray {
+ /* No styles */
+}
+
+#window {
+ font-weight: bold;
+}
+
+#workspaces button {
+ border-top: 2px solid transparent;
+ /* To compensate for the top border and still have vertical centering */
+ padding-bottom: 2px;
+ padding-left: 10px;
+ padding-right: 10px;
+ color: #888888;
+}
+
+#workspaces button.focused {
+ border-color: #4c7899;
+ color: white;
+ background-color: #285577;
+}
+
+#workspaces button.urgent {
+ border-color: #c9545d;
+ color: #c9545d;
+}
diff --git a/ansible/roles/sway/files/sway_config_files/background.conf b/ansible/roles/sway/files/sway_config_files/background.conf
new file mode 100644
index 0000000..65354c9
--- /dev/null
+++ b/ansible/roles/sway/files/sway_config_files/background.conf
@@ -0,0 +1,4 @@
+### Output configuration
+#
+# Default wallpaper (more resolutions are available in /usr/share/backgrounds/sway/)
+output * bg ~/.config/sway/bliss.jpg fill
diff --git a/ansible/roles/sway/files/sway_config_files/base_hotkeys.conf b/ansible/roles/sway/files/sway_config_files/base_hotkeys.conf
new file mode 100644
index 0000000..9dc0794
--- /dev/null
+++ b/ansible/roles/sway/files/sway_config_files/base_hotkeys.conf
@@ -0,0 +1,25 @@
+### Key bindings
+#
+# Basics:
+#
+ # kill focused window
+ bindsym $mod+Shift+q kill
+
+ # start your launcher
+ bindsym $mod+Return exec $menu
+
+ # 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
+
+ # Hotkey to lock screen, relies on swayidle running
+ bindsym $mod+l exec pkill -USR1 swayidle
+
+ # 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'
diff --git a/ansible/roles/sway/files/sway_config_files/disable_focus_follows_mouse.conf b/ansible/roles/sway/files/sway_config_files/disable_focus_follows_mouse.conf
new file mode 100644
index 0000000..243a937
--- /dev/null
+++ b/ansible/roles/sway/files/sway_config_files/disable_focus_follows_mouse.conf
@@ -0,0 +1,2 @@
+# Disable focus following mouse
+focus_follows_mouse no
diff --git a/ansible/roles/sway/files/sway_config_files/framework_display.conf b/ansible/roles/sway/files/sway_config_files/framework_display.conf
new file mode 100644
index 0000000..63906e3
--- /dev/null
+++ b/ansible/roles/sway/files/sway_config_files/framework_display.conf
@@ -0,0 +1 @@
+output 'Unknown 0x095F 0x00000000' scale 1.5
diff --git a/ansible/roles/sway/files/sway_config_files/framework_input.conf b/ansible/roles/sway/files/sway_config_files/framework_input.conf
new file mode 100644
index 0000000..4ddbb28
--- /dev/null
+++ b/ansible/roles/sway/files/sway_config_files/framework_input.conf
@@ -0,0 +1,8 @@
+input * xkb_rules "evdev"
+
+# Framework Touchpad
+input "2362:628:PIXA3854:01_093A:0274_TouchPad" {
+ dwt enabled
+ click_method clickfinger
+ tap enabled
+}
diff --git a/ansible/roles/sway/files/sway_config_files/movement.conf b/ansible/roles/sway/files/sway_config_files/movement.conf
new file mode 100644
index 0000000..4c089a8
--- /dev/null
+++ b/ansible/roles/sway/files/sway_config_files/movement.conf
@@ -0,0 +1,51 @@
+#
+# Moving around:
+#
+ # Move your focus around
+ # bindsym $mod+$left focus left
+ # bindsym $mod+$down focus down
+ # bindsym $mod+$up focus up
+ # bindsym $mod+$right focus right
+ # or use $mod+[up|down|left|right]
+ bindsym $mod+Left focus left
+ bindsym $mod+Down focus down
+ bindsym $mod+Up focus up
+ bindsym $mod+Right focus right
+
+ # _move_ the focused window with the same, but add Shift
+ bindsym $mod+Shift+$left move left
+ bindsym $mod+Shift+$down move down
+ bindsym $mod+Shift+$up move up
+ bindsym $mod+Shift+$right move right
+ # ditto, with arrow keys
+ bindsym $mod+Shift+Left move left
+ bindsym $mod+Shift+Down move down
+ bindsym $mod+Shift+Up move up
+ bindsym $mod+Shift+Right move right
+#
+# Workspaces:
+#
+ # switch to workspace
+ bindsym $mod+1 workspace 1
+ bindsym $mod+2 workspace 2
+ bindsym $mod+3 workspace 3
+ bindsym $mod+4 workspace 4
+ bindsym $mod+5 workspace 5
+ bindsym $mod+6 workspace 6
+ bindsym $mod+7 workspace 7
+ bindsym $mod+8 workspace 8
+ bindsym $mod+9 workspace 9
+ bindsym $mod+0 workspace 10
+ # move focused container to workspace
+ bindsym $mod+Shift+1 move container to workspace 1
+ bindsym $mod+Shift+2 move container to workspace 2
+ bindsym $mod+Shift+3 move container to workspace 3
+ bindsym $mod+Shift+4 move container to workspace 4
+ bindsym $mod+Shift+5 move container to workspace 5
+ bindsym $mod+Shift+6 move container to workspace 6
+ bindsym $mod+Shift+7 move container to workspace 7
+ bindsym $mod+Shift+8 move container to workspace 8
+ bindsym $mod+Shift+9 move container to workspace 9
+ bindsym $mod+Shift+0 move container to workspace 10
+ # Note: workspaces can have any name you want, not just numbers.
+ # We just use 1-10 as the default.
diff --git a/ansible/roles/sway/files/sway_config_files/notifications.conf b/ansible/roles/sway/files/sway_config_files/notifications.conf
new file mode 100644
index 0000000..e88db74
--- /dev/null
+++ b/ansible/roles/sway/files/sway_config_files/notifications.conf
@@ -0,0 +1,5 @@
+bindsym $mod+Escape exec makoctl dismiss
+bindsym $mod+Shift+Escape exec makoctl invoke
+
+# Notifications
+exec /usr/bin/mako
diff --git a/ansible/roles/sway/files/sway_config_files/screenshots.conf b/ansible/roles/sway/files/sway_config_files/screenshots.conf
new file mode 100644
index 0000000..0e2edbf
--- /dev/null
+++ b/ansible/roles/sway/files/sway_config_files/screenshots.conf
@@ -0,0 +1,9 @@
+# Screenshots
+#bindsym $mod+print exec slurp | grim -g - $(xdg-user-dir PICTURES)/$(date +'screenshot_%Y-%m-%d-%H%M%S.png')
+bindsym $mod+print exec slurp | grim -g - "$HOME/$(date +'screenshot_%Y-%m-%d-%H%M%S.png')"
+bindsym print exec grim "$HOME/$(date +'screenshot_%Y-%m-%d-%H%M%S.png')"
+# Maybe add --audio flag? can optionally specify specific device name from `pactl list sources | grep Name`
+bindsym $mod+Shift+print exec wf-recorder -g "$(slurp)" -f "$HOME/$(date +'screencast_%Y-%m-%d-%H%M%S.mkv')" -c h264_vaapi -d /dev/dri/renderD128
+bindsym Shift+print exec wf-recorder -f "$HOME/$(date +'screencast_%Y-%m-%d-%H%M%S.mkv')" -c h264_vaapi -d /dev/dri/renderD128
+bindsym $mod+ctrl+Shift+print exec killall -s SIGINT wf-recorder
+# Need to make a hotkey to end the recording
diff --git a/ansible/roles/sway/files/sway_config_files/waybar.conf b/ansible/roles/sway/files/sway_config_files/waybar.conf
new file mode 100644
index 0000000..f2f251c
--- /dev/null
+++ b/ansible/roles/sway/files/sway_config_files/waybar.conf
@@ -0,0 +1,16 @@
+#
+# Status Bar:
+#
+# Read `man 5 sway-bar` for more information about this section.
+bar {
+ position top
+
+ font pango:Cascadia Mono, FontAwesome 10
+ swaybar_command waybar
+
+ colors {
+ statusline #ffffff
+ background #323232
+ inactive_workspace #32323200 #32323200 #5c5c5c
+ }
+}
diff --git a/ansible/roles/sway/files/sway_config_files/windows.conf b/ansible/roles/sway/files/sway_config_files/windows.conf
new file mode 100644
index 0000000..c477ff8
--- /dev/null
+++ b/ansible/roles/sway/files/sway_config_files/windows.conf
@@ -0,0 +1,61 @@
+#
+# Layout stuff:
+#
+ # You can "split" the current object of your focus with
+ # $mod+b or $mod+v, for horizontal and vertical splits
+ # respectively.
+ bindsym $mod+h splith
+ bindsym $mod+v splitv
+
+ # Switch the current container between different layout styles
+ bindsym $mod+s layout stacking
+ bindsym $mod+w layout tabbed
+ bindsym $mod+e layout toggle split
+
+ # Make the current focus fullscreen
+ bindsym $mod+f fullscreen
+
+ # Toggle the current focus between tiling and floating mode
+ bindsym $mod+Shift+space floating toggle
+
+ # Swap focus between the tiling area and the floating area
+ bindsym $mod+space focus mode_toggle
+
+ # move focus to the parent container
+ bindsym $mod+a focus parent
+#
+# Scratchpad:
+#
+ # Sway has a "scratchpad", which is a bag of holding for windows.
+ # You can send windows there and get them back later.
+
+ # Move the currently focused window to the scratchpad
+ bindsym $mod+Shift+minus move scratchpad
+
+ # Show the next scratchpad window or hide the focused scratchpad window.
+ # If there are multiple scratchpad windows, this command cycles through them.
+ bindsym $mod+minus scratchpad show
+#
+# Resizing containers:
+#
+mode "resize" {
+ # left will shrink the containers width
+ # right will grow the containers width
+ # up will shrink the containers height
+ # down will grow the containers height
+ bindsym $left resize shrink width 10px
+ bindsym $down resize grow height 10px
+ bindsym $up resize shrink height 10px
+ bindsym $right resize grow width 10px
+
+ # ditto, with arrow keys
+ bindsym Left resize shrink width 10px
+ bindsym Down resize grow height 10px
+ bindsym Up resize shrink height 10px
+ bindsym Right resize grow width 10px
+
+ # return to default mode
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+}
+bindsym $mod+r mode "resize"
diff --git a/ansible/roles/sway/files/waybar_config.json b/ansible/roles/sway/files/waybar_config.json
new file mode 100644
index 0000000..4d13bc9
--- /dev/null
+++ b/ansible/roles/sway/files/waybar_config.json
@@ -0,0 +1,27 @@
+{
+ // "height": 10, // Waybar height (to be removed for auto height)
+ "modules-left": ["sway/workspaces", "sway/mode"],
+ "modules-right": ["idle_inhibitor", "custom/clock", "tray"],
+ "sway/workspaces": {
+ "disable-scroll": true,
+ },
+ "sway/mode": {
+ "format": "{}"
+ },
+ "idle_inhibitor": {
+ "format": "{icon}",
+ "format-icons": {
+ "activated": "",
+ "deactivated": ""
+ }
+ },
+ "tray": {
+ // "icon-size": 21,
+ "spacing": 10
+ },
+ "custom/clock": {
+ "exec": "waybar_custom_clock",
+ "return-type": "json",
+ "restart-interval": 30
+ }
+}
diff --git a/ansible/roles/sway/files/waybar_custom_clock.py b/ansible/roles/sway/files/waybar_custom_clock.py
new file mode 100644
index 0000000..d42ca54
--- /dev/null
+++ b/ansible/roles/sway/files/waybar_custom_clock.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+#
+# Custom waybar module for clocks. Implemented because the official clock module was downloading timezone definitions on every boot.
+import datetime
+import json
+import sys
+import time
+from dataclasses import dataclass
+from functools import lru_cache
+from typing import Final, List, Optional
+from zoneinfo import ZoneInfo
+
+INTERVAL: Final[int] = 10
+LOCAL_TIMEZONE = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo
+
+
+@dataclass
+class Update:
+ text: Optional[str]
+ alt: Optional[str]
+ tooltip: Optional[str]
+ css_class: Optional[List[str]]
+ percentage: Optional[str]
+
+ def dump(self) -> str:
+ # Dump a dict because we can't name our member variable "class"
+ return json.dumps(
+ {
+ k: v
+ for k, v in {
+ "text": self.text,
+ "alt": self.alt,
+ "tooltip": self.tooltip,
+ "class": self.css_class,
+ "percentage": self.percentage,
+ }.items()
+ if v is not None
+ }
+ )
+
+
+@lru_cache(maxsize=1)
+def make_calendar(today: datetime.date, tz: datetime.tzinfo):
+ width: Final[int] = 20
+ start_of_month = today.replace(day=1)
+ month_header = today.strftime("%B %Y")
+ padding = width - len(month_header)
+ right_padding = " " * (padding // 2)
+ left_padding = right_padding + (" " if padding % 2 == 1 else "")
+ header = f"{left_padding}{month_header}{right_padding}"
+ days_of_week = "Su Mo Tu We Th Fr Sa"
+
+ timezone_str = str(tz)
+
+ # Make the grid
+ first_day_padding = " " * (
+ 0 if start_of_month.weekday() == 6 else (3 * (1 + start_of_month.weekday())) - 1
+ )
+ output = f"{header}\n{days_of_week}\n{first_day_padding}"
+
+ print_day = start_of_month
+ while print_day.month == today.month:
+ if print_day.weekday() == 6:
+ output += "\n"
+ else:
+ output += " "
+ if print_day == today:
+ output += ''
+ output += f"{print_day.day: >2}"
+ if print_day == today:
+ output += ""
+ print_day += datetime.timedelta(days=1)
+
+ output += f'\n{timezone_str}'
+ return output
+
+
+def main():
+ tz: Optional[datetime.tzinfo] = (
+ ZoneInfo(sys.argv[1]) if len(sys.argv) >= 2 else LOCAL_TIMEZONE
+ )
+ if tz is None:
+ raise Exception("Failed to detect timezone.")
+
+ next_update = time.time()
+ while True:
+ time_before_next_update = next_update - time.time()
+ if time_before_next_update > 0:
+ time.sleep(time_before_next_update)
+ next_update = time.time() + INTERVAL
+
+ now = datetime.datetime.now(tz=tz)
+ text = now.strftime("%Y-%m-%d %H:%M")
+ tooltip = make_calendar(now.date(), tz)
+
+ out = Update(
+ text=text,
+ alt="foo",
+ tooltip=f"{tooltip}",
+ css_class=["foo"],
+ percentage="100",
+ )
+
+ print(out.dump(), flush=True)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible/roles/sway/files/xdg-desktop-portal-wlr-config b/ansible/roles/sway/files/xdg-desktop-portal-wlr-config
new file mode 100644
index 0000000..63e21df
--- /dev/null
+++ b/ansible/roles/sway/files/xdg-desktop-portal-wlr-config
@@ -0,0 +1,7 @@
+# [screencast]
+# output_name=HDMI-A-1
+# max_fps=30
+# exec_before=disable_notifications.sh
+# exec_after=enable_notifications.sh
+# chooser_type=simple
+# chooser_cmd=slurp -f %o -or
diff --git a/ansible/roles/sway/meta/main.yaml b/ansible/roles/sway/meta/main.yaml
new file mode 100644
index 0000000..4341718
--- /dev/null
+++ b/ansible/roles/sway/meta/main.yaml
@@ -0,0 +1,3 @@
+dependencies:
+ - users
+ - build # for aurutils on linux
diff --git a/ansible/roles/sway/tasks/common.yaml b/ansible/roles/sway/tasks/common.yaml
new file mode 100644
index 0000000..a3c22b8
--- /dev/null
+++ b/ansible/roles/sway/tasks/common.yaml
@@ -0,0 +1,25 @@
+- name: Install scripts
+ copy:
+ src: "files/{{ item.src }}"
+ dest: "{{ item.dest }}"
+ mode: 0755
+ owner: root
+ group: wheel
+ loop:
+ - src: waybar_custom_clock.py
+ dest: /usr/local/bin/waybar_custom_clock
+
+- import_tasks: tasks/freebsd.yaml
+ when: 'os_flavor == "freebsd"'
+
+- import_tasks: tasks/linux.yaml
+ when: 'os_flavor == "linux"'
+
+- include_tasks:
+ file: tasks/peruser.yaml
+ apply:
+ become: yes
+ become_user: "{{ initialize_user }}"
+ loop: "{{ users | dict2items | community.general.json_query('[?value.initialize==`true`].key') }}"
+ loop_control:
+ loop_var: initialize_user
diff --git a/ansible/roles/sway/tasks/freebsd.yaml b/ansible/roles/sway/tasks/freebsd.yaml
new file mode 100644
index 0000000..f9cedef
--- /dev/null
+++ b/ansible/roles/sway/tasks/freebsd.yaml
@@ -0,0 +1,48 @@
+- name: Install packages
+ package:
+ name:
+ - sway
+ - swaybg
+ - swayidle
+ - swaylock
+ - wofi
+ - mako
+ - grim
+ - xeyes
+ - qt5-wayland
+ # - gvfs
+ - xauth # for ssh x11 forwarding
+ - slurp # screen coordinates for screenshots
+ - libnotify # mako doesn't seem to work without it
+ - pcmanfm # For mounting drives
+ - wf-recorder # screen recording
+ - waybar
+ - xdg-desktop-portal
+ - xdg-desktop-portal-wlr # screen sharing
+ # - rofimoji
+ - wtype # for rofimoji to be able to insert characters
+ - dbus # for desktop notifications
+ - lumina-fm
+ state: present
+
+- name: Install service configuration
+ copy:
+ src: "files/{{ item }}_rc.conf"
+ dest: "/etc/rc.conf.d/{{ item }}"
+ mode: 0644
+ owner: root
+ group: wheel
+ loop:
+ - seatd
+ - dbus
+
+- name: Install scripts
+ copy:
+ src: "files/{{ item.src }}"
+ dest: "{{ item.dest }}"
+ mode: 0755
+ owner: root
+ group: wheel
+ loop:
+ - src: launch_sway_freebsd.bash
+ dest: /usr/local/bin/launch_sway
diff --git a/ansible/roles/sway/tasks/linux.yaml b/ansible/roles/sway/tasks/linux.yaml
new file mode 100644
index 0000000..2f8f648
--- /dev/null
+++ b/ansible/roles/sway/tasks/linux.yaml
@@ -0,0 +1,17 @@
+# - name: Install packages
+# pacman:
+# name:
+# - foo
+# state: present
+# update_cache: true
+
+- name: Install scripts
+ copy:
+ src: "files/{{ item.src }}"
+ dest: "{{ item.dest }}"
+ mode: 0755
+ owner: root
+ group: wheel
+ loop:
+ - src: launch_sway_linux.bash
+ dest: /usr/local/bin/launch_sway
diff --git a/ansible/roles/sway/tasks/main.yaml b/ansible/roles/sway/tasks/main.yaml
new file mode 100644
index 0000000..dc9939d
--- /dev/null
+++ b/ansible/roles/sway/tasks/main.yaml
@@ -0,0 +1,2 @@
+- import_tasks: tasks/common.yaml
+ when: graphics_driver is defined
diff --git a/ansible/roles/sway/tasks/peruser.yaml b/ansible/roles/sway/tasks/peruser.yaml
new file mode 100644
index 0000000..5f815e4
--- /dev/null
+++ b/ansible/roles/sway/tasks/peruser.yaml
@@ -0,0 +1,56 @@
+- include_role:
+ name: per_user
+
+- name: Create directories
+ file:
+ name: "{{ account_homedir.stdout }}/{{ item }}"
+ state: directory
+ mode: 0700
+ owner: "{{ account_name.stdout }}"
+ group: "{{ group_name.stdout }}"
+ loop:
+ - ".config/sway"
+ - ".config/sway/config.d"
+ - ".config/waybar"
+ - ".config/xdg-desktop-portal-wlr"
+ - ".config/mako"
+
+- name: Copy files
+ copy:
+ src: "files/{{ item.src }}"
+ dest: "{{ account_homedir.stdout }}/{{ item.dest }}"
+ mode: 0600
+ owner: "{{ account_name.stdout }}"
+ group: "{{ group_name.stdout }}"
+ loop:
+ - src: config
+ dest: .config/sway/config
+ - src: bliss.jpg
+ dest: .config/sway/bliss.jpg
+ - src: spaceglenda300.jpg
+ dest: .config/sway/spaceglenda300.jpg
+ - src: waybar_config.json
+ dest: .config/waybar/config
+ - src: style.css
+ dest: .config/waybar/style.css
+ - src: xdg-desktop-portal-wlr-config
+ dest: .config/xdg-desktop-portal-wlr/config
+ - src: electron-flags.conf
+ dest: .config/electron-flags.conf
+ - src: mako_config
+ dest: .config/mako/config
+
+- name: Configure dotfiles (conf directory)
+ copy:
+ src: "files/sway_config_files/{{ item }}.conf"
+ dest: "{{ account_homedir.stdout }}/.config/sway/config.d/{{ item }}.conf"
+ mode: 0600
+ owner: "{{ account_name.stdout }}"
+ group: "{{ group_name.stdout }}"
+ loop: "{{ sway_conf_files|default([]) + default_sway_conf_files }}"
+
+- import_tasks: tasks/peruser_freebsd.yaml
+ when: 'os_flavor == "freebsd"'
+
+- import_tasks: tasks/peruser_linux.yaml
+ when: 'os_flavor == "linux"'
diff --git a/ansible/roles/sway/tasks/peruser_freebsd.yaml b/ansible/roles/sway/tasks/peruser_freebsd.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/ansible/roles/sway/tasks/peruser_linux.yaml b/ansible/roles/sway/tasks/peruser_linux.yaml
new file mode 100644
index 0000000..e69de29