diff --git a/.bashrc b/.bashrc
index 5c19e69..797e96e 100644
--- a/.bashrc
+++ b/.bashrc
@@ -11,4 +11,10 @@ alias ls='ls --color=auto'
alias grep='grep --color=auto'
PS1='[\u@\h \W]\$ '
+export HOMEBREW_PREFIX="/home/linuxbrew/.linuxbrew"
+export HOMEBREW_CELLAR="/home/linuxbrew/.linuxbrew/Cellar"
+export HOMEBREW_REPOSITORY="/home/linuxbrew/.linuxbrew/Homebrew"
+export PATH="/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin${PATH+:$PATH}"
+export MANPATH="/home/linuxbrew/.linuxbrew/share/man${MANPATH+:$MANPATH}:"
+export INFOPATH="/home/linuxbrew/.linuxbrew/share/info:${INFOPATH:-}"
export EDITOR=nvim
diff --git a/.gitmodules b/.gitmodules
index 9542823..80df3c1 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,9 @@
[submodule "nvim"]
path = nvim
url = https://git.palko.ca/baobeld/nvim.git
+[submodule "ags"]
+ path = ags
+ url = https://git.palko.ca/baobeld/ags.git
[submodule "lux-shell"]
path = quickshell
url = https://git.palko.ca/baobeld/lux-shell.git
diff --git a/.zshrc b/.zshrc
index c139937..b0b70e2 100644
--- a/.zshrc
+++ b/.zshrc
@@ -75,6 +75,8 @@ cat ~/.cache/wal/sequences
# Add wisely, as too many plugins slow down shell startup.
plugins=(git gh bun npm yarn mise)
+source $ZSH/oh-my-zsh.sh
+
# User configuration
# export MANPATH="/usr/local/man:$MANPATH"
@@ -119,13 +121,9 @@ compinit
# End of lines added by compinstall
eval "$(mise activate zsh)"
+source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh
-source $ZSH/oh-my-zsh.sh
export EDITOR=nvim
-export QML_IMPORT_PATH=/usr/lib/qt6/qml
-export QML2_IMPORT_PATH=/usr/lib/qt6/qml
neofetch --ascii ~/dotfiles/ascii.txt
-
-source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
diff --git a/README.md b/README.md
index 1f2ac3c..3c922e9 100755
--- a/README.md
+++ b/README.md
@@ -43,13 +43,33 @@ Install...
Include = /etc/pacman.d/mirrorlist
```
+### TODO
+
+- [ ] zsh Configuration
+- [ ] scripted setup
+ - [ ] pacman packages
+ - [ ] yay packages
+ - [ ] brew packages
+ - [ ] aliases
+ - [ ] symlinks
+- [ ] hyprland Configuration
+ - [ ] waybar
+ - [ ] hyprpaper
+ - [ ] swaylock/hyprlock
+ - [ ] swayidle/hypridle
+ - [ ] keybindings
+ - [ ] wlogout
+
## Packages
This is a list of packages sorted by the package manager used to install them along with a small description of what they do as well as a link to their website/docs
### Pacman
+- Thunar - file manager
- zsh - Shell
+- Bitwarden - Password manager
+- Firefox - web browser
- git - Version control
- github-cli - Github CLI
- lazygit - CLI git client
@@ -61,18 +81,24 @@ This is a list of packages sorted by the package manager used to install them al
- xclip - Clipboard util
- ripgrep - CLI grep tool (used for neovim text search across files)
- btop - resource monitor
+- dunst - notification daemon
- cmatrix - THE MATRIX
- swappy - screenshot tool
- spotify - Music App
- discord - Messaging
+- caprine - Facebook messenger
- obsidian - markdown and stuff
#### Hyprland
+- waybar - wayland status bar
- swww - wallpaper animations
- hypridle - hyprland idle daemon
- hyprlock - hyprland lock screen
+- wlogout - logout manu
- pywal - generates color palettes from wallpaper
+- hyprland-plugins - self explanitory
+- SwayNotificationCenter - notification UI
### Yay
diff --git a/ags b/ags
new file mode 160000
index 0000000..27a2631
--- /dev/null
+++ b/ags
@@ -0,0 +1 @@
+Subproject commit 27a263142ff9b44151e81f57075dae704f43e272
diff --git a/hypr/hyprland/env.conf b/hypr/hyprland/env.conf
index b3b0423..b2cda83 100644
--- a/hypr/hyprland/env.conf
+++ b/hypr/hyprland/env.conf
@@ -29,9 +29,9 @@ env = ICON_THEME,WhiteSur-Dark
env = COLOR_SCHEME,prefer-dark
#Cursors
-env = XCURSOR_THEME,Volantes Cursors
+env = XCURSOR_THEME,Catppuccin-Macchiato-Dark
env = XCURSOR_SIZE,24
-env = HYPRCURSOR_THEME,Volantes Cursors
+env = HYPRCURSOR_THEME,Catppuccin-Macchiato-Dark
env = HYPRCURSOR_SIZE,24
diff --git a/hypr/hyprland/execs.conf b/hypr/hyprland/execs.conf
index 80fcfd9..34abb96 100644
--- a/hypr/hyprland/execs.conf
+++ b/hypr/hyprland/execs.conf
@@ -1,10 +1,13 @@
-# Key ring
-exec-once = gnome-keyring-daemon --start --components=secrets
+# Set cursor
+exec-once=hyprctl setcursor volantes_cursors 24
+# Notification Daemon
+exec-once = bash ~/.config/hypr/hyprland/scripts/start-swaync.sh
# Idle Daemon
exec-once = hypridle
-# Lux-shell
-exec-once = quickshell
+# Status-bar
+# exec-once = bash ~/.config/hypr/scripts/start-waybar.sh
+exec-once = bash ~/dotfiles/hypr/hyprland/scripts/run-ags.sh
# Emotes
exec-once = emote
# Wallpaper Daemon
diff --git a/hypr/hyprland/keybinds.conf b/hypr/hyprland/keybinds.conf
index 2edf954..48fef57 100644
--- a/hypr/hyprland/keybinds.conf
+++ b/hypr/hyprland/keybinds.conf
@@ -1,7 +1,4 @@
-# Lux Keybinds
-bind = $mainMod, ENTER, global, lux:launcher
-
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
bind = $mainMod, T, exec, $terminal
bind = $mainMod, X, killactive,
@@ -12,7 +9,7 @@ bind = $mainMod, R, exec, $menu
bind = $mainMod, P, pseudo, # dwindle
bind = $mainMod, V, togglesplit, # dwindle
bind = $mainMod, L, exec, $lockScreen # hyprlock
-bind = $mainMod, period, exec, gnome-characters
+bind = $mainMod, period, exec, emote
bind = $mainMod, B, exec, zen-browser
bind = $mainMod CTRL, F, fullscreen
bind = , PRINT, exec, grim -g "$(slurp)" - | swappy -f -
diff --git a/hypr/hyprland/scripts/run-ags.sh b/hypr/hyprland/scripts/run-ags.sh
new file mode 100644
index 0000000..e7ac9a6
--- /dev/null
+++ b/hypr/hyprland/scripts/run-ags.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+CONFIG_FILES="$HOME/dotfiles/ags"
+while true; do
+ sleep 1.6
+ ags run --gtk4 -d "$CONFIG_FILES" &
+ inotifywait -e create,modify -r "$CONFIG_FILES"
+ killall gjs
+done
diff --git a/hypr/hyprland/scripts/start-swaync.sh b/hypr/hyprland/scripts/start-swaync.sh
new file mode 100644
index 0000000..e90446d
--- /dev/null
+++ b/hypr/hyprland/scripts/start-swaync.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+CONFIG_FILES="$HOME/.config/swaync/ $HOME/.cache/wal/"
+
+trap "killall swaync" EXIT
+swaync &
+
+while true; do
+ inotifywait -e create,modify -r $CONFIG_FILES
+ swaync-client -R & swaync-client -rs
+done
diff --git a/nvim b/nvim
index 57de95c..4c428c3 160000
--- a/nvim
+++ b/nvim
@@ -1 +1 @@
-Subproject commit 57de95c811e74ea92d41b6de7363b6f8552f4666
+Subproject commit 4c428c3c2082f93e47e400be8d44fba1ab07e46f
diff --git a/quickshell b/quickshell
index 3e3275a..579af75 160000
--- a/quickshell
+++ b/quickshell
@@ -1 +1 @@
-Subproject commit 3e3275a84dc88021656a9bae816a37324a117bbc
+Subproject commit 579af752fa88747229969612ec3fb8ba10067001
diff --git a/swaync/config.json b/swaync/config.json
new file mode 100755
index 0000000..4201027
--- /dev/null
+++ b/swaync/config.json
@@ -0,0 +1,100 @@
+{
+ "$schema": "/etc/xdg/swaync/configSchema.json",
+ "positionX": "right",
+ "positionY": "top",
+ "layer": "overlay",
+ "control-center-layer": "right",
+ "layer-shell": true,
+ "cssPriority": "application",
+ "control-center-margin-top": 5,
+ "control-center-margin-bottom": 0,
+ "control-center-margin-right": 0,
+ "control-center-margin-left": 0,
+ "notification-2fa-action": true,
+ "notification-inline-replies": false,
+ "notification-icon-size": 24,
+ "notification-body-image-height": 100,
+ "notification-body-image-width": 200,
+ "timeout": 6,
+ "timeout-low": 3,
+ "timeout-critical": 0,
+ "fit-to-screen": false,
+ "control-center-width": 350,
+ "control-center-height": 720,
+ "notification-window-width": 400,
+ "keyboard-shortcuts": true,
+ "image-visibility": "when available",
+ "transition-time": 200,
+ "hide-on-clear": false,
+ "hide-on-action": true,
+ "script-fail-notify": true,
+ "widgets": [
+ "dnd",
+ "buttons-grid",
+ "mpris",
+ "volume",
+ "backlight",
+ "title",
+ "notifications"
+ ],
+ "widget-config": {
+ "title": {
+ "text": "Notifications",
+ "clear-all-button": true,
+ "button-text": "Clear"
+ },
+ "dnd": {
+ "text": "Do Not Disturb"
+ },
+ "label": {
+ "max-lines": 1,
+ "text": "Notification"
+ },
+ "mpris": {
+ "image-size": 48,
+ "image-radius": 9999
+ },
+ "volume": {
+ "label": ""
+ },
+ "backlight": {
+ "label": ""
+ },
+ "buttons-grid": {
+ "actions": [
+ {
+ "label": "",
+ "command": "systemctl poweroff"
+ },
+ {
+ "label": "",
+ "command": "systemctl reboot"
+ },
+ {
+ "label": "",
+ "command": "hyprlock"
+ },
+ {
+ "label": "",
+ "command": "hyprctl dispatch exit"
+ },
+ {
+ "label": "",
+ "command": "~/.config/hypr/scripts/AirplaneMode.sh"
+ },
+ {
+ "label": "",
+ "command": "pactl set-sink-mute @DEFAULT_SINK@ toggle"
+ },
+ {
+ "label": "",
+ "command": "pactl set-source-mute @DEFAULT_SOURCE@ toggle"
+ },
+ {
+ "label": "",
+ "command": "blueman-manager"
+ }
+ ]
+ }
+ }
+}
diff --git a/swaync/style.css b/swaync/style.css
new file mode 100644
index 0000000..59f9ff7
--- /dev/null
+++ b/swaync/style.css
@@ -0,0 +1,451 @@
+* {
+ all: unset;
+ font-size: 14px;
+ font-family: "Jetbrains Nerd Font";
+ transition: 200ms;
+}
+
+trough highlight {
+ background: #cdd6f4;
+}
+
+scale trough {
+ margin: 0rem 1rem;
+ background-color: #313244;
+ min-height: 8px;
+ min-width: 70px;
+}
+
+slider {
+ background-color: #89b4fa;
+}
+
+.floating-notifications.background .notification-row .notification-background {
+ box-shadow:
+ 0 0 8px 0 rgba(0, 0, 0, 0.8),
+ inset 0 0 0 1px #313244;
+ border-radius: 12.6px;
+ margin: 18px;
+ background-color: #1e1e2e;
+ color: #cdd6f4;
+ padding: 0;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .notification {
+ padding: 7px;
+ border-radius: 12.6px;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .notification.critical {
+ box-shadow: inset 0 0 7px 0 #f38ba8;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .notification
+ .notification-content {
+ margin: 7px;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .notification
+ .notification-content
+ .summary {
+ color: #cdd6f4;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .notification
+ .notification-content
+ .time {
+ color: #a6adc8;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .notification
+ .notification-content
+ .body {
+ color: #cdd6f4;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .notification
+ > *:last-child
+ > * {
+ min-height: 3.4em;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .notification
+ > *:last-child
+ > *
+ .notification-action {
+ border-radius: 7px;
+ color: #cdd6f4;
+ background-color: #313244;
+ box-shadow: inset 0 0 0 1px #45475a;
+ margin: 7px;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .notification
+ > *:last-child
+ > *
+ .notification-action:hover {
+ box-shadow: inset 0 0 0 1px #45475a;
+ background-color: #313244;
+ color: #cdd6f4;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .notification
+ > *:last-child
+ > *
+ .notification-action:active {
+ box-shadow: inset 0 0 0 1px #45475a;
+ background-color: #74c7ec;
+ color: #cdd6f4;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .close-button {
+ margin: 7px;
+ padding: 2px;
+ border-radius: 6.3px;
+ color: #1e1e2e;
+ background-color: #f38ba8;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .close-button:hover {
+ background-color: #eba0ac;
+ color: #1e1e2e;
+}
+
+.floating-notifications.background
+ .notification-row
+ .notification-background
+ .close-button:active {
+ background-color: #f38ba8;
+ color: #1e1e2e;
+}
+
+.control-center {
+ box-shadow:
+ 0 0 8px 0 rgba(0, 0, 0, 0.8),
+ inset 0 0 0 1px #313244;
+ border-radius: 12.6px;
+ margin: 18px;
+ background-color: #1e1e2e;
+ color: #cdd6f4;
+ padding: 14px;
+}
+
+.control-center .widget-title > label {
+ color: #cdd6f4;
+ font-size: 1.3em;
+}
+
+.control-center .widget-title button {
+ border-radius: 7px;
+ color: #cdd6f4;
+ background-color: #313244;
+ box-shadow: inset 0 0 0 1px #45475a;
+ padding: 8px;
+}
+
+.control-center .widget-title button:hover {
+ box-shadow: inset 0 0 0 1px #45475a;
+ background-color: #585b70;
+ color: #cdd6f4;
+}
+
+.control-center .widget-title button:active {
+ box-shadow: inset 0 0 0 1px #45475a;
+ background-color: #74c7ec;
+ color: #1e1e2e;
+}
+
+.control-center .notification-row .notification-background {
+ border-radius: 7px;
+ color: #cdd6f4;
+ background-color: #313244;
+ box-shadow: inset 0 0 0 1px #45475a;
+ margin-top: 14px;
+}
+
+.control-center .notification-row .notification-background .notification {
+ padding: 7px;
+ border-radius: 7px;
+}
+
+.control-center
+ .notification-row
+ .notification-background
+ .notification.critical {
+ box-shadow: inset 0 0 7px 0 #f38ba8;
+}
+
+.control-center
+ .notification-row
+ .notification-background
+ .notification
+ .notification-content {
+ margin: 7px;
+}
+
+.control-center
+ .notification-row
+ .notification-background
+ .notification
+ .notification-content
+ .summary {
+ color: #cdd6f4;
+}
+
+.control-center
+ .notification-row
+ .notification-background
+ .notification
+ .notification-content
+ .time {
+ color: #a6adc8;
+}
+
+.control-center
+ .notification-row
+ .notification-background
+ .notification
+ .notification-content
+ .body {
+ color: #cdd6f4;
+}
+
+.control-center
+ .notification-row
+ .notification-background
+ .notification
+ > *:last-child
+ > * {
+ min-height: 3.4em;
+}
+
+.control-center
+ .notification-row
+ .notification-background
+ .notification
+ > *:last-child
+ > *
+ .notification-action {
+ border-radius: 7px;
+ color: #cdd6f4;
+ background-color: #11111b;
+ box-shadow: inset 0 0 0 1px #45475a;
+ margin: 7px;
+}
+
+.control-center
+ .notification-row
+ .notification-background
+ .notification
+ > *:last-child
+ > *
+ .notification-action:hover {
+ box-shadow: inset 0 0 0 1px #45475a;
+ background-color: #313244;
+ color: #cdd6f4;
+}
+
+.control-center
+ .notification-row
+ .notification-background
+ .notification
+ > *:last-child
+ > *
+ .notification-action:active {
+ box-shadow: inset 0 0 0 1px #45475a;
+ background-color: #74c7ec;
+ color: #cdd6f4;
+}
+
+.control-center .notification-row .notification-background .close-button {
+ margin: 7px;
+ padding: 2px;
+ border-radius: 6.3px;
+ color: #1e1e2e;
+ background-color: #eba0ac;
+}
+
+.close-button {
+ border-radius: 6.3px;
+}
+
+.control-center .notification-row .notification-background .close-button:hover {
+ background-color: #f38ba8;
+ color: #1e1e2e;
+}
+
+.control-center
+ .notification-row
+ .notification-background
+ .close-button:active {
+ background-color: #f38ba8;
+ color: #1e1e2e;
+}
+
+.control-center .notification-row .notification-background:hover {
+ box-shadow: inset 0 0 0 1px #45475a;
+ background-color: #7f849c;
+ color: #cdd6f4;
+}
+
+.control-center .notification-row .notification-background:active {
+ box-shadow: inset 0 0 0 1px #45475a;
+ background-color: #74c7ec;
+ color: #cdd6f4;
+}
+
+.notification.critical progress {
+ background-color: #f38ba8;
+}
+
+.notification.low progress,
+.notification.normal progress {
+ background-color: #89b4fa;
+}
+
+.control-center-dnd {
+ margin-top: 5px;
+ border-radius: 8px;
+ background: #313244;
+ border: 1px solid #45475a;
+ box-shadow: none;
+}
+
+.control-center-dnd:checked {
+ background: #313244;
+}
+
+.control-center-dnd slider {
+ background: #45475a;
+ border-radius: 8px;
+}
+
+.widget-dnd {
+ margin: 0px;
+ font-size: 1.1rem;
+}
+
+.widget-dnd > switch {
+ font-size: initial;
+ border-radius: 8px;
+ background: #313244;
+ border: 1px solid #45475a;
+ box-shadow: none;
+}
+
+.widget-dnd > switch:checked {
+ background: #313244;
+}
+
+.widget-dnd > switch slider {
+ background: #45475a;
+ border-radius: 8px;
+ border: 1px solid #6c7086;
+}
+
+.widget-mpris .widget-mpris-player {
+ background: #313244;
+ padding: 7px;
+}
+
+.widget-mpris .widget-mpris-title {
+ font-size: 1.2rem;
+}
+
+.widget-mpris .widget-mpris-subtitle {
+ font-size: 0.8rem;
+}
+
+.widget-menubar > box > .menu-button-bar > button > label {
+ font-size: 3rem;
+ padding: 0.5rem 2rem;
+}
+
+.widget-menubar > box > .menu-button-bar > :last-child {
+ color: #f38ba8;
+}
+
+.power-buttons button:hover,
+.powermode-buttons button:hover,
+.screenshot-buttons button:hover {
+ background: #313244;
+}
+
+.control-center .widget-label > label {
+ color: #cdd6f4;
+ font-size: 2rem;
+}
+
+.widget-buttons-grid {
+ padding-top: 1rem;
+}
+
+.widget-buttons-grid > flowbox > flowboxchild > button label {
+ font-size: 2.5rem;
+}
+
+.widget-volume {
+ padding-top: 1rem;
+}
+
+.widget-volume label {
+ font-size: 1.5rem;
+ color: #74c7ec;
+}
+
+.widget-volume trough highlight {
+ background: #74c7ec;
+}
+
+.widget-backlight trough highlight {
+ background: #f9e2af;
+}
+
+.widget-backlight label {
+ font-size: 1.5rem;
+ color: #f9e2af;
+}
+
+.widget-backlight .KB {
+ padding-bottom: 1rem;
+}
+
+.image {
+ padding-right: 0.5rem;
+}
diff --git a/waybar/config.jsonc b/waybar/config.jsonc
new file mode 100644
index 0000000..066e918
--- /dev/null
+++ b/waybar/config.jsonc
@@ -0,0 +1,140 @@
+// -*- mode: jsonc -*-
+{
+ "layer": "top", // Waybar at top layer
+ // "position": "bottom", // Waybar position (top|bottom|left|right)
+ "height": 36, // Waybar height (to be removed for auto height)
+ // "width": 1280, // Waybar width
+ "spacing": 8, // Gaps between modules (4px)
+ // Choose the order of the modules
+ "modules-left": [
+ "custom/os",
+ "hyprland/workspaces",
+ "tray",
+ "hyprland/window",
+ ],
+ "modules-center": ["mpris"],
+ "modules-right": [
+ "custom/swww",
+ "wireplumber",
+ "network",
+ "cpu",
+ "memory",
+ "temperature",
+ "idle_inhibitor",
+ "clock",
+ "custom/swaync",
+ ],
+ "custom/os": {
+ "format": "",
+ "on-click": "~/.config/rofi/scripts/powermenu_t1",
+ },
+ "idle_inhibitor": {
+ "format": "{icon}",
+ "tooltip-format-activated": "On",
+ "tooltip-format-deactivated": "Off",
+ "format-icons": {
+ "activated": "",
+ "deactivated": "",
+ },
+ },
+ "hyprland/workspaces": {
+ "active-only": false,
+ "format": "{icon}",
+ "format-icons": {
+ "default": "",
+ "1": "",
+ "2": "",
+ "3": "",
+ "4": "",
+ },
+ "persistent-workspaces": {
+ "*": 4, // 5 workspaces by default on every monitor
+ },
+ },
+ "hyprland/window": {
+ "format": "{}",
+ "rewrite": {
+ "(.*) — Mozilla Firefox": " $1",
+ "~(.*)": " $1",
+ "nv": " neovim",
+ },
+ "separate-outputs": true,
+ "max-length": "40",
+ },
+ "tray": {
+ "icon-size": 18,
+ "spacing": 10,
+ },
+ "clock": {
+ "timezone": "America/New_York",
+ "tooltip-format": "{:%Y %B}\n{calendar}",
+ "format-alt": "{:%Y-%m-%d}",
+ "on-click": "",
+ },
+ "cpu": {
+ "format": " {usage}%",
+ "tooltip": true,
+ },
+ "memory": {
+ "format": " {}%",
+ },
+ "temperature": {
+ // "thermal-zone": 2,
+ // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input",
+ "critical-threshold": 80,
+ // "format-critical": "{temperatureC}°C {icon}",
+ "format": "{icon} {temperatureC}°C",
+ "format-icons": ["", "", "", "", ""],
+ },
+ "wireplumber": {
+ "format": " {volume}%",
+ "format-mute": " {volume}%",
+ "on-click": "~/.config/rofi/applets/bin/volume.sh",
+ },
+ "network": {
+ // "interface": "wlp2*", // (Optional) To force the use of this interface
+ "tooltip-format": "{essid}",
+ "format-wifi": " {signalStrength}%",
+ "format-ethernet": "",
+ "format-linked": "{ifname} (No IP) ",
+ "format-disconnected": "",
+ //"format-alt": "{ifname}: {ipaddr}/{cidr}"
+ },
+ "mpris": {
+ "format": "{player_icon} {dynamic}",
+ "format-paused": "{status_icon} {dynamic}",
+ "player-icons": {
+ "default": "▶",
+ "mpv": "🎵",
+ "spotify": "",
+ "firefox": "",
+ },
+ "interval": 1,
+ "status-icons": {
+ "paused": "⏸",
+ },
+ "dynamic-len": 36,
+ "on-click-middle": "playerctld shift",
+ },
+ "custom/swaync": {
+ "format": "{icon}",
+ "exec": "swaync-client -swb",
+ "return-type": "json",
+ "on-click": "sleep 0.1 && swaync-client -t -sw",
+ "format-icons": {
+ "notification": "⬤",
+ "none": " ",
+ "dnd-notification": "⬤",
+ "dnd-none": " ",
+ },
+ },
+ "custom/swww": {
+ "format": "{icon}",
+ "tooltip": false,
+ "tooltip-format": "Change Wallpaper",
+ "on-click": "~/dotfiles/.scripts/pywal-swww.sh",
+ "format-icons": {
+ "default": "",
+ },
+ },
+}
diff --git a/waybar/modules/mediaplayer.py b/waybar/modules/mediaplayer.py
new file mode 100755
index 0000000..e473697
--- /dev/null
+++ b/waybar/modules/mediaplayer.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python3
+import gi
+gi.require_version("Playerctl", "2.0")
+from gi.repository import Playerctl, GLib
+from gi.repository.Playerctl import Player
+import argparse
+import logging
+import sys
+import signal
+import gi
+import json
+import os
+from typing import List
+
+logger = logging.getLogger(__name__)
+
+def signal_handler(sig, frame):
+ logger.info("Received signal to stop, exiting")
+ sys.stdout.write("\n")
+ sys.stdout.flush()
+ # loop.quit()
+ sys.exit(0)
+
+
+class PlayerManager:
+ def __init__(self, selected_player=None, excluded_player=[]):
+ self.manager = Playerctl.PlayerManager()
+ self.loop = GLib.MainLoop()
+ self.manager.connect(
+ "name-appeared", lambda *args: self.on_player_appeared(*args))
+ self.manager.connect(
+ "player-vanished", lambda *args: self.on_player_vanished(*args))
+
+ signal.signal(signal.SIGINT, signal_handler)
+ signal.signal(signal.SIGTERM, signal_handler)
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
+ self.selected_player = selected_player
+ self.excluded_player = excluded_player.split(',') if excluded_player else []
+
+ self.init_players()
+
+ def init_players(self):
+ for player in self.manager.props.player_names:
+ if player.name in self.excluded_player:
+ continue
+ if self.selected_player is not None and self.selected_player != player.name:
+ logger.debug(f"{player.name} is not the filtered player, skipping it")
+ continue
+ self.init_player(player)
+
+ def run(self):
+ logger.info("Starting main loop")
+ self.loop.run()
+
+ def init_player(self, player):
+ logger.info(f"Initialize new player: {player.name}")
+ player = Playerctl.Player.new_from_name(player)
+ player.connect("playback-status",
+ self.on_playback_status_changed, None)
+ player.connect("metadata", self.on_metadata_changed, None)
+ self.manager.manage_player(player)
+ self.on_metadata_changed(player, player.props.metadata)
+
+ def get_players(self) -> List[Player]:
+ return self.manager.props.players
+
+ def write_output(self, text, player):
+ logger.debug(f"Writing output: {text}")
+
+ output = {"text": text,
+ "class": "custom-" + player.props.player_name,
+ "alt": player.props.player_name}
+
+ sys.stdout.write(json.dumps(output) + "\n")
+ sys.stdout.flush()
+
+ def clear_output(self):
+ sys.stdout.write("\n")
+ sys.stdout.flush()
+
+ def on_playback_status_changed(self, player, status, _=None):
+ logger.debug(f"Playback status changed for player {player.props.player_name}: {status}")
+ self.on_metadata_changed(player, player.props.metadata)
+
+ def get_first_playing_player(self):
+ players = self.get_players()
+ logger.debug(f"Getting first playing player from {len(players)} players")
+ if len(players) > 0:
+ # if any are playing, show the first one that is playing
+ # reverse order, so that the most recently added ones are preferred
+ for player in players[::-1]:
+ if player.props.status == "Playing":
+ return player
+ # if none are playing, show the first one
+ return players[0]
+ else:
+ logger.debug("No players found")
+ return None
+
+ def show_most_important_player(self):
+ logger.debug("Showing most important player")
+ # show the currently playing player
+ # or else show the first paused player
+ # or else show nothing
+ current_player = self.get_first_playing_player()
+ if current_player is not None:
+ self.on_metadata_changed(current_player, current_player.props.metadata)
+ else:
+ self.clear_output()
+
+ def on_metadata_changed(self, player, metadata, _=None):
+ logger.debug(f"Metadata changed for player {player.props.player_name}")
+ player_name = player.props.player_name
+ artist = player.get_artist()
+ title = player.get_title()
+
+ track_info = ""
+ if player_name == "spotify" and "mpris:trackid" in metadata.keys() and ":ad:" in player.props.metadata["mpris:trackid"]:
+ track_info = "Advertisement"
+ elif artist is not None and title is not None:
+ track_info = f"{artist} - {title}"
+ else:
+ track_info = title
+
+ if track_info:
+ if player.props.status == "Playing":
+ track_info = " " + track_info
+ else:
+ track_info = " " + track_info
+ # only print output if no other player is playing
+ current_playing = self.get_first_playing_player()
+ if current_playing is None or current_playing.props.player_name == player.props.player_name:
+ self.write_output(track_info, player)
+ else:
+ logger.debug(f"Other player {current_playing.props.player_name} is playing, skipping")
+
+ def on_player_appeared(self, _, player):
+ logger.info(f"Player has appeared: {player.name}")
+ if player is not None and (self.selected_player is None or player.name == self.selected_player):
+ self.init_player(player)
+ else:
+ logger.debug(
+ "New player appeared, but it's not the selected player, skipping")
+
+ def on_player_vanished(self, _, player):
+ logger.info(f"Player {player.props.player_name} has vanished")
+ self.show_most_important_player()
+
+def parse_arguments():
+ parser = argparse.ArgumentParser()
+
+ # Increase verbosity with every occurrence of -v
+ parser.add_argument("-v", "--verbose", action="count", default=0)
+
+ parser.add_argument("-x", "--exclude", "- Comma-separated list of excluded player")
+
+ # Define for which player we"re listening
+ parser.add_argument("--player")
+
+ parser.add_argument("--enable-logging", action="store_true")
+
+ return parser.parse_args()
+
+
+def main():
+ arguments = parse_arguments()
+
+ # Initialize logging
+ if arguments.enable_logging:
+ logfile = os.path.join(os.path.dirname(
+ os.path.realpath(__file__)), "media-player.log")
+ logging.basicConfig(filename=logfile, level=logging.DEBUG,
+ format="%(asctime)s %(name)s %(levelname)s:%(lineno)d %(message)s")
+
+ # Logging is set by default to WARN and higher.
+ # With every occurrence of -v it's lowered by one
+ logger.setLevel(max((3 - arguments.verbose) * 10, 0))
+
+ logger.info("Creating player manager")
+ if arguments.player:
+ logger.info(f"Filtering for player: {arguments.player}")
+ if arguments.exclude:
+ logger.info(f"Exclude player {arguments.exclude}")
+
+ player = PlayerManager(arguments.player, arguments.exclude)
+ player.run()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/waybar/style.css b/waybar/style.css
new file mode 100644
index 0000000..616818b
--- /dev/null
+++ b/waybar/style.css
@@ -0,0 +1,118 @@
+/*
+* Color Palette
+*/
+@import url("../../.cache/wal/colors-waybar.css");
+
+* {
+ /* `otf-font-awesome` is required to be installed for icons */
+ font-family: "JetBrainsMono Nerd Font", "Iosevka Nerd Font", FontAwesome,
+ Roboto, Helvetica, Arial, sans-serif;
+ text-shadow: none;
+ transition: color 0.5s ease-in-out;
+ transition: background-color 0.5s ease-in-out;
+ font-size: 16px;
+}
+
+@define-color button @color1;
+@define-color button-hover @color3;
+@define-color button-active @color2;
+
+/*
+* General
+*/
+@import url("./styles/waybar.css");
+
+/*
+ * Workspaces
+ */
+#workspaces {
+ border-radius: 9999px;
+ background: @button;
+}
+
+#workspaces button {
+ padding: 0 9px 0 4px;
+ box-shadow: none;
+ border: none;
+ border-radius: 9999px;
+ color: @color7;
+ font-size: 24px;
+}
+
+#workspaces button.active {
+ color: @button-active;
+}
+
+#workspaces button:hover {
+ background: transparent;
+ color: @button-hover;
+}
+
+#workspaces button.focused {
+ color: @button-active;
+}
+
+#workspaces button.urgent {
+ color: #bf616a;
+}
+
+/*
+* Modules
+*/
+#cpu,
+#memory,
+#network,
+#temperature {
+ color: @color7;
+}
+
+#clock,
+#idle_inhibitor,
+#mpris,
+#wireplumber {
+ background: @button;
+}
+
+#clock:hover,
+#idle_inhibitor:hover,
+#mpris:hover,
+#custom-swaync:hover,
+#wireplumber:hover,
+#custom-swww:hover,
+#custom-os:hover {
+ background: @button-hover;
+}
+
+#idle_inhibitor.activated {
+ background-color: @button-active;
+}
+
+#idle_inhibitor {
+ padding: 0 16px 0 12px;
+}
+
+#mpris.spotify {
+ background-color: #1db954;
+}
+
+#network.disconnected {
+ color: #f53c3c;
+}
+
+#temperature.critical {
+ color: #bf616a;
+}
+
+#wireplumber.muted {
+ color: #f53c3c;
+}
+
+#custom-swww {
+ padding: 0 16px 0 10px;
+ font-size: 18px;
+}
+
+#custom-os {
+ padding: 0 14px 0 8px;
+ font-size: 24px;
+}
diff --git a/waybar/styles/waybar.css b/waybar/styles/waybar.css
new file mode 100644
index 0000000..3a7e09d
--- /dev/null
+++ b/waybar/styles/waybar.css
@@ -0,0 +1,51 @@
+window#waybar {
+ background: @background;
+ animation: fadeIn 2.5s;
+ padding: 2px 0 2px 0;
+ border-radius: 0 0 20px 20px;
+}
+
+button {
+ text-shadow: 2px 2px;
+}
+
+window#waybar.hidden {
+ opacity: 0.2;
+}
+
+@keyframes fadeIn {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+
+.module {
+ border-radius: 9999px;
+ margin: 8px 0;
+}
+
+label.module {
+ padding: 0 10px;
+ /* box-shadow: inset 0 -2px; */
+}
+
+box.module {
+ padding: 0 10px;
+}
+
+.modules-left {
+ padding: 0 3px;
+ margin-left: 10px;
+}
+
+.modules-center {
+ padding: 0 3px;
+}
+
+.modules-right {
+ padding: 0 3px;
+ margin-right: 10px;
+}