From 8276cf83b4228c159f705a3001fa54c17341a161 Mon Sep 17 00:00:00 2001 From: Tobias Manske Date: Fri, 18 Aug 2023 19:38:09 +0200 Subject: [PATCH] Add hyprland/wayland config --- .dotter/global.toml | 24 +-- .dotter/pre_deploy.sh | 3 - gammastep/config.ini | 59 +++++++ hyprland/hooks/networkmanager.sh | 97 +++++++++++ hyprland/hyprland.conf | 238 +++++++++++++++++++++++++++ hyprland/hyprpaper.conf | 0 hyprland/scripts/displaybyname.sh | 13 ++ hyprland/scripts/launch_waybar.sh | 20 +++ hyprland/scripts/lock.sh | 17 ++ hyprland/scripts/screenshot.sh | 2 + hyprland/scripts/transformbyname.sh | 12 ++ kanshi/config | 1 + kanshi/config.d/10-dock | 11 ++ kanshi/config.d/90-mobile | 6 + waybar/config | 152 +++++++++++++++++ waybar/mocha.css | 37 +++++ waybar/scripts/dunst.sh | 21 +++ waybar/scripts/mediaplayer.py | 182 +++++++++++++++++++++ waybar/scripts/wifionice.sh | 72 ++++++++ waybar/style.css | 244 ++++++++++++++++++++++++++++ 20 files changed, 1193 insertions(+), 18 deletions(-) create mode 100644 gammastep/config.ini create mode 100755 hyprland/hooks/networkmanager.sh create mode 100644 hyprland/hyprland.conf create mode 100644 hyprland/hyprpaper.conf create mode 100755 hyprland/scripts/displaybyname.sh create mode 100755 hyprland/scripts/launch_waybar.sh create mode 100755 hyprland/scripts/lock.sh create mode 100755 hyprland/scripts/screenshot.sh create mode 100755 hyprland/scripts/transformbyname.sh create mode 100644 kanshi/config create mode 100644 kanshi/config.d/10-dock create mode 100644 kanshi/config.d/90-mobile create mode 100644 waybar/config create mode 100644 waybar/mocha.css create mode 100755 waybar/scripts/dunst.sh create mode 100644 waybar/scripts/mediaplayer.py create mode 100755 waybar/scripts/wifionice.sh create mode 100644 waybar/style.css diff --git a/.dotter/global.toml b/.dotter/global.toml index 9e11866..bfe0565 100644 --- a/.dotter/global.toml +++ b/.dotter/global.toml @@ -3,34 +3,28 @@ "zsh/.zimrc" = { target = "~/.zimrc", type = "symbolic" } [nvim.files] -"nvim/init.lua" = { target = "~/.config/nvim/init.lua", type = "symbolic" } -"nvim/lua" = { target = "~/.config/nvim/lua", type = "symbolic" } +"nvim" = { target = "~/.config/nvim", type = "symbolic" } [git.files] "git/.gitconfig" = { target = "~/.config/git/config", type = "symbolic" } "git/.gitmessage" = { target = "~/.config/git/message", type = "symbolic" } "git/.gitattributes" = { target = "~/.config/git/attributes", type = "symbolic" } -[grobi.files] -"grobi/grobi.conf" = { target = "~/.config/grobi.conf", type = "symbolic" } - [gnupg.files] "gnupg/gpg.conf" = { target = "~/.gnupg/gpg.conf", type = "symbolic" } "gnupg/gpg-agent.conf" = { target = "~/.gnupg/gpg-agent.conf", type = "symbolic" } -[i3.files] -"i3/config" = { target = "~/.config/i3/config", type = "symbolic" } -"i3/layouts" = { target = "~/.config/i3/layouts", type = "symbolic" } -"i3/scripts" = { target = "~/.config/i3/scripts", type = "symbolic" } +[kanshi.files] +"kanshi" = { target = "~/.config/kanshi", type = "symbolic" } -[picom.files] -"picom/picom.conf" = { target = "~/.config/picom.conf", type = "symbolic" } +[hyprland.files] +"hyprland" = { target = "~/.config/hypr", type = "symbolic" } -[polybar.files] -"polybar/config.ini" = { target = "~/.config/polybar/config", type = "symbolic" } +[waybar.files] +"waybar" = { target = "~/.config/waybar", type = "symbolic" } -[redshift.files] -"redshift/redshift.conf" = { target = "~/.config/redshift/redshift.conf", type = "symbolic" } +[gammastep.files] +"gammastep" = { target = "~/.config/gammastep", type = "symbolic" } [tmux.files] "tmux/.tmux.conf" = { target = "~/.tmux.conf", type = "symbolic" } diff --git a/.dotter/pre_deploy.sh b/.dotter/pre_deploy.sh index c7b0f80..bc886c3 100755 --- a/.dotter/pre_deploy.sh +++ b/.dotter/pre_deploy.sh @@ -2,6 +2,3 @@ set -e set -u -# Build i3 config -cd i3/conf -./build.sh diff --git a/gammastep/config.ini b/gammastep/config.ini new file mode 100644 index 0000000..f9962df --- /dev/null +++ b/gammastep/config.ini @@ -0,0 +1,59 @@ +; Global settings +[general] +; Set the day and night screen temperatures +temp-day=5000 +temp-night=3500 + +; Disable the smooth fade between temperatures when Redshift starts and stops. +; 0 will cause an immediate change between screen temperatures. +; 1 will gradually apply the new screen temperature over a couple of seconds. +fade=1 + +; Solar elevation thresholds. +; By default, Redshift will use the current elevation of the sun to determine +; whether it is daytime, night or in transition (dawn/dusk). When the sun is +; above the degrees specified with elevation-high it is considered daytime and +; below elevation-low it is considered night. +;elevation-high=3 +;elevation-low=-6 + +; Custom dawn/dusk intervals. +; Instead of using the solar elevation, the time intervals of dawn and dusk +; can be specified manually. The times must be specified as HH:MM in 24-hour +; format. +;dawn-time=6:00-7:45 +;dusk-time=18:35-20:15 + +; Set the screen brightness. Default is 1.0. +;brightness=0.9 +; It is also possible to use different settings for day and night +; since version 1.8. +;brightness-day=0.7 +;brightness-night=0.4 +; Set the screen gamma (for all colors, or each color channel +; individually) +gamma=0.8 +;gamma=0.8:0.7:0.8 +; This can also be set individually for day and night since +; version 1.10. +;gamma-day=0.8:0.7:0.8 +;gamma-night=0.6 + +; Set the location-provider: 'geoclue2', 'manual'. +; The location provider settings are in a different section. +location-provider=manual + +; Set the adjustment-method: 'randr', 'vidmode', 'drm', 'wayland'. +; 'randr' is the preferred X11 method, 'vidmode' is an older API +; that works in some cases when 'randr' does not. +; The adjustment method settings are in a different section. +adjustment-method=wayland + +; Configuration of the location-provider: +; type 'gammastep -l PROVIDER:help' to see the settings. +; ex: 'gammastep -l manual:help' +; Keep in mind that longitudes west of Greenwich (e.g. the Americas) +; are negative numbers. +[manual] +lat=49.04 +lon=8.44 diff --git a/hyprland/hooks/networkmanager.sh b/hyprland/hooks/networkmanager.sh new file mode 100755 index 0000000..e217670 --- /dev/null +++ b/hyprland/hooks/networkmanager.sh @@ -0,0 +1,97 @@ +#!/bin/sh + +network_print() { + connection_list=$(nmcli -t -f name,type,device,state connection show --order name --active 2>/dev/null | grep -v ':bridge:') + counter=0 + + if [ -n "$connection_list" ] && [ "$(echo "$connection_list" | wc -l)" -gt 0 ]; then + echo "$connection_list" | while read -r line; do + description=$(echo "$line" | sed -e 's/\\:/-/g' | cut -d ':' -f 1) + type=$(echo "$line" | sed -e 's/\\:/-/g' | cut -d ':' -f 2) + device=$(echo "$line" | sed -e 's/\\:/-/g' | cut -d ':' -f 3) + state=$(echo "$line" | sed -e 's/\\:/-/g' | cut -d ':' -f 4) + + if [ "$type" = "loopback" ]; then + continue + fi + if [ "$type" = "tun" ]; then + continue + fi + + if [ "$state" = "activated" ]; then + if [ "$type" = "802-11-wireless" ]; then + icon="直" + + signal=$(nmcli -t -f in-use,signal device wifi list ifname "$device" | grep "\*" | cut -d ':' -f 2) + if [ "$signal" -lt 40 ]; then + description="$description - %{F#f9cc18}$signal%%{F-}" + fi + elif [ "$type" = "802-3-ethernet" ]; then + icon="" + + speed="$(cat /sys/class/net/"$device"/speed)" + if [ "$speed" -ne -1 ]; then + if [ "$speed" -eq 1000 ]; then + speed="1G" + else + speed=$speed"M" + fi + else + speed="?" + fi + + description="$description ($speed)" + elif [ "$type" = "bluetooth" ]; then + icon="" + elif [ "$type" = "wireguard" ]; then + icon="旅" + elif [ "$type" = "vpn" ]; then + icon="旅" + else + icon='ﯳ' + fi + fi + + if [ $counter -gt 0 ]; then + printf " %s %s" "$icon" "$description" + else + printf "%s %s" "$icon" "$description" + fi + + counter=$((counter + 1)) + done + + printf "\n" + else + echo "#3" + fi +} + +network_update() { + pid=$(cat "$path_pid") + + if [ "$pid" != "" ]; then + kill -10 "$pid" + fi +} + +path_pid="/tmp/hypr-network-networkmanager.pid" + +case "$1" in + --update) + network_update + ;; + *) + echo $$ > $path_pid + + trap exit INT + trap "echo" USR1 + + while true; do + network_print + + sleep 60 & + wait + done + ;; +esac diff --git a/hyprland/hyprland.conf b/hyprland/hyprland.conf new file mode 100644 index 0000000..bb4ace2 --- /dev/null +++ b/hyprland/hyprland.conf @@ -0,0 +1,238 @@ +# +# Please note not all available settings / options are set here. +# For a full list, see the wiki +# + +# See https://wiki.hyprland.org/Configuring/Monitors/ +# monitor=eDP-1,preferred,auto,1.25 +# monitor=,preferred,auto,1 + +# See https://wiki.hyprland.org/Configuring/Keywords/ for more + +# Allow screensharing +exec-once = dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP + +# Execute your favorite apps at launch +# exec-once = waybar & hyprpaper & firefox +exec-once = swayidle & +exec-once = xiccd & +exec-once = dunst & +exec-once = ibus-daemon -drxR & +exec-once = sleep 3 && kanshi & +exec-once = bash ~/.config/hypr/scripts/launch_waybar.sh + +exec-once = easyeffects --gapplication-service & +exec-once = lxqt-policykit-agent & +exec-once = thunar --daemon & + +exec-once = env QT_QPA_PLATFORM=xcb /usr/lib/kdeconnectd +exec-once = env QT_QPA_PLATFORM=xcb kdeconnect-indicator + +# On reload: +exec = pkill -1 kanshi + +# Source a file (multi-file configs) +# source = ~/.config/hypr/myColors.conf + +# Some default env vars. +env = XCURSOR_SIZE,24 + +# For all categories, see https://wiki.hyprland.org/Configuring/Variables/ +input { + kb_layout = us + kb_variant = + kb_model = + kb_options = compose:caps,mod_led:compose + kb_rules = + + follow_mouse = 1 + + touchpad { + natural_scroll = true + } + + sensitivity = 0 # -1.0 - 1.0, 0 means no modification. +} + +general { + # See https://wiki.hyprland.org/Configuring/Variables/ for more + + gaps_in = 5 + gaps_out = 5 + border_size = 2 + col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg + col.inactive_border = rgba(595959aa) + + layout = master +} + +master { + new_is_master = false + allow_small_split = false +} + +decoration { + # See https://wiki.hyprland.org/Configuring/Variables/ for more + + rounding = 10 + + blur { + enabled = true + size = 3 + passes = 1 + } + + drop_shadow = yes + shadow_range = 4 + shadow_render_power = 3 + col.shadow = rgba(1a1a1aee) +} + +animations { + enabled = yes + + # Some default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more + + bezier = myBezier, 0.05, 0.9, 0.1, 1.05 + + animation = windows, 1, 7, myBezier + animation = windowsOut, 1, 7, default, popin 80% + animation = border, 1, 10, default + animation = borderangle, 1, 8, default + animation = fade, 1, 7, default + animation = workspaces, 1, 6, default +} + +gestures { + # See https://wiki.hyprland.org/Configuring/Variables/ for more + workspace_swipe = on +} + +# Example per-device config +# See https://wiki.hyprland.org/Configuring/Keywords/#executing for more +device:epic-mouse-v1 { + sensitivity = -0.5 +} + +# Example windowrule v1 +# windowrule = float, ^(kitty)$ +# Example windowrule v2 +# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$ +# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more + + +# See https://wiki.hyprland.org/Configuring/Keywords/ for more +$mainMod = SUPER + +# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more +# bind = $mainMod, Q, exec, kitty +bind = $mainMod SHIFT, Q, killactive, +bind = $mainMod, RETURN, exec, alacritty +bind = $mainMod CONTROL, L, exec, loginctl lock-session self +bindr = $mainMod, D, exec, dunstctl context +bind = $mainMod, F, fullscreen + +# Volume control +binde = , XF86AudioRaiseVolume, exec, pactl set-sink-volume @DEFAULT_SINK@ +100 +binde = , XF86AudioLowerVolume, exec, pactl set-sink-volume @DEFAULT_SINK@ -100 +binde = SHIFT, XF86AudioRaiseVolume, exec, pactl set-sink-volume @DEFAULT_SINK@ +1000 +binde = SHIFT, XF86AudioLowerVolume, exec, pactl set-sink-volume @DEFAULT_SINK@ -1000 +bind = , XF86AudioMute, exec, pactl set-sink-mute @DEFAULT_SINK@ toggle + +# Microphone control +bind = , XF86AudioMicMute, exec, pactl set-source-mute @DEFAULT_SOURCE@ toggle +bind = $mainMod, c, exec, pactl set-source-mute @DEFAULT_SOURCE@ toggle + +# Brightness control +binde = , XF86MonBrightnessUp, exec, brightnessctl s +2% +binde = , XF86MonBrightnessDown, exec, brightnessctl s 2%- +binde = SHIFT , XF86MonBrightnessUp, exec, brightnessctl s +10% +binde = SHIFT , XF86MonBrightnessDown, exec, brightnessctl s 10%- +bind = , XF86AudioMute, exec, pactl set-sink-mute @DEFAULT_SINK@ toggle + +# Flameshot +bindr = CONTROL SHIFT, Print, exec, ~/.config/hypr/scripts/screenshot.sh +bindr = , Print, exec, ~/.config/hypr/scripts/screenshot.sh + +# Media Control +bind = , XF86AudioPlay, exec, playerctl play-pause +bind = , XF86AudioNext, exec, playerctl next +bind = , XF86AudioPrev, exec, playerctl previous +bind = , XF86AudioStop, exec, playerctl stop + + +# ROFI +bind = ALT, SPACE, exec, rofi -show drun +bind = $mainMod, TAB, exec, rofi -show window +bind = $mainMod SHIFT, N, exec, networkmanager_dmenu --rofi -i +bind = $mainMod SHIFT, B, exec, rofi-bluetooth +bind = $mainMod SHIFT, E, exec, rofimoji --typer wtype + +# bind = $mainMod, M, exit, +# bind = $mainMod, E, exec, dolphin +bind = $mainMod CONTROL, SPACE, togglefloating, +bind = $mainMod, SPACE, layoutmsg, orientationnext +bind = $mainMod, M, layoutmsg, addmaster +bind = $mainMod SHIFT, M, layoutmsg, removemaster + +# Move focus with mainMod + arrow keys +bind = $mainMod, h, movefocus, l +bind = $mainMod, j, movefocus, d +bind = $mainMod, k, movefocus, u +bind = $mainMod, l, movefocus, r + +# Move windows with mainMod + arrow keys +bind = $mainMod SHIFT, h, movewindow, l +bind = $mainMod SHIFT, j, movewindow, d +bind = $mainMod SHIFT, k, movewindow, u +bind = $mainMod SHIFT, l, movewindow, r + +# Switch workspaces with mainMod + [0-9] +bind = $mainMod, 1, workspace, 1 +bind = $mainMod, 2, workspace, 2 +bind = $mainMod, 3, workspace, 3 +bind = $mainMod, 4, workspace, 4 +bind = $mainMod, 5, workspace, 5 +bind = $mainMod, 6, workspace, 6 +bind = $mainMod, 7, workspace, 7 +bind = $mainMod, 8, workspace, 8 +bind = $mainMod, 9, workspace, 9 +bind = $mainMod, 0, workspace, 10 + +# Move active window to a workspace with mainMod + SHIFT + [0-9] +bind = $mainMod SHIFT, 1, movetoworkspace, 1 +bind = $mainMod SHIFT, 2, movetoworkspace, 2 +bind = $mainMod SHIFT, 3, movetoworkspace, 3 +bind = $mainMod SHIFT, 4, movetoworkspace, 4 +bind = $mainMod SHIFT, 5, movetoworkspace, 5 +bind = $mainMod SHIFT, 6, movetoworkspace, 6 +bind = $mainMod SHIFT, 7, movetoworkspace, 7 +bind = $mainMod SHIFT, 8, movetoworkspace, 8 +bind = $mainMod SHIFT, 9, movetoworkspace, 9 +bind = $mainMod SHIFT, 0, movetoworkspace, 10 + +# Scroll through existing workspaces with mainMod + scroll +bind = $mainMod, mouse_down, workspace, e+1 +bind = $mainMod, mouse_up, workspace, e-1 + +# Move/resize windows with mainMod + LMB/RMB and dragging +bindm = $mainMod, mouse:272, movewindow +bindm = $mainMod, mouse:273, resizewindow + +bind = $mainMod, M, movecurrentworkspacetomonitor, +1 + +# will switch to a submap called resize +bind = $mainMod,R,submap,resize + +submap = resize + binde = ,H,resizeactive,-10 0 + binde = ,J,resizeactive,0 10 + binde = ,K,resizeactive,0 -10 + binde = ,L,resizeactive,10 0 + bind = ,escape,submap,reset + bind = ,return,submap,reset +submap = reset + +windowrulev2 = float, title:(KeePassXC - Access Request) +windowrulev2 = float, class:(lxqt-policykit-agent) +# vim: ft=hypr diff --git a/hyprland/hyprpaper.conf b/hyprland/hyprpaper.conf new file mode 100644 index 0000000..e69de29 diff --git a/hyprland/scripts/displaybyname.sh b/hyprland/scripts/displaybyname.sh new file mode 100755 index 0000000..08c13f9 --- /dev/null +++ b/hyprland/scripts/displaybyname.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +LOOK="$*" + +IFS=$'\n' +for d in $(hyprctl -j monitors | jq -r '.[] | "\(.make) \(.model) \(.serial);\(.name)"'); +do + if [[ "$d" == "$LOOK"* ]]; then + echo "$d" | awk -F';' '{print $2}' + exit + fi +done +exit 1 diff --git a/hyprland/scripts/launch_waybar.sh b/hyprland/scripts/launch_waybar.sh new file mode 100755 index 0000000..9b8b984 --- /dev/null +++ b/hyprland/scripts/launch_waybar.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +getDisplay () { + local DESK + + DESK="$(~/.config/hypr/scripts/displaybyname.sh Samsung Electric Company S34J55x H4LT100404)" + + [[ -n "$DESK" ]] && echo $DESK && return + echo "eDP-1" +} + +if [[ -z "$PRIMARY_DISPLAY" ]]; then + PRIMARY_DISPLAY="$(getDisplay)" +fi + +export PRIMARY_DISPLAY +envsubst < ~/.config/waybar/config > /tmp/waybar + +pkill waybar +waybar -c /tmp/waybar &! diff --git a/hyprland/scripts/lock.sh b/hyprland/scripts/lock.sh new file mode 100755 index 0000000..1b8346f --- /dev/null +++ b/hyprland/scripts/lock.sh @@ -0,0 +1,17 @@ +DUNST_STATE=$(dunstctl is-paused) +dunstctl set-paused true +swaylock \ + --screenshots \ + --clock \ + --indicator \ + --indicator-radius 100 \ + --indicator-thickness 7 \ + --effect-blur 7x5 \ + --effect-vignette 0.5:0.5 \ + --ring-color bb00cc \ + --key-hl-color 880033 \ + --line-color 00000000 \ + --inside-color 00000088 \ + --separator-color 00000000 \ + --fade-in 0.2 +dunstctl set-paused "$DUNST_STATE" diff --git a/hyprland/scripts/screenshot.sh b/hyprland/scripts/screenshot.sh new file mode 100755 index 0000000..946abec --- /dev/null +++ b/hyprland/scripts/screenshot.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grim -g "$(slurp -d)" - | swappy -f - diff --git a/hyprland/scripts/transformbyname.sh b/hyprland/scripts/transformbyname.sh new file mode 100755 index 0000000..0ccfb16 --- /dev/null +++ b/hyprland/scripts/transformbyname.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +NAME="$1" +TRANSFORM="$2" + +TARGET="$(~/.config/hypr/scripts/displaybyname.sh $NAME)" + +if [ -z "$TARGET" ]; then + exit 1 +fi + +hyprctl keyword monitor "${TARGET},transform,${TRANSFORM}" diff --git a/kanshi/config b/kanshi/config new file mode 100644 index 0000000..688d638 --- /dev/null +++ b/kanshi/config @@ -0,0 +1 @@ +include ~/.config/kanshi/config.d/* diff --git a/kanshi/config.d/10-dock b/kanshi/config.d/10-dock new file mode 100644 index 0000000..8f2d56e --- /dev/null +++ b/kanshi/config.d/10-dock @@ -0,0 +1,11 @@ +profile Dock { + output "BNQ BenQ GL2450H ACB00126019" enable mode 1920x1080 position 0,0 transform 270 scale 1 + output "BNQ BenQ GL2450H X4F00171019" enable mode 1920x1080 position 4520,0 transform normal scale 1 + output "Samsung Electric Company S34J55x H4LT100404" enable mode 3440x1440 position 1080,0 transform normal scale 1 + output eDP-1 disable + exec ~/.config/hypr/scripts/transformbyname.sh "BNQ BenQ GL2450H ACB00126019" 1 + exec ~/.config/hypr/scripts/transformbyname.sh "BNQ BenQ GL2450H X4F00171019" 3 + exec systemctl --user start gammastep.service + exec rfkill block wifi + exec bash ~/.config/hypr/scripts/launch_waybar.sh +} diff --git a/kanshi/config.d/90-mobile b/kanshi/config.d/90-mobile new file mode 100644 index 0000000..5236c76 --- /dev/null +++ b/kanshi/config.d/90-mobile @@ -0,0 +1,6 @@ +profile mobile { + output eDP-1 enable scale 1.25 position 0,0 + exec systemctl --user start gammastep.service + exec rfkill unblock wifi + exec bash ~/.config/hypr/scripts/launch_waybar.sh +} diff --git a/waybar/config b/waybar/config new file mode 100644 index 0000000..90ba8f9 --- /dev/null +++ b/waybar/config @@ -0,0 +1,152 @@ +// vim: ft=jsonc +{ + "layer": "top", // Waybar at top layer + // "position": "bottom", // Waybar position (top|bottom|left|right) + // "height": 30, // Waybar height (to be removed for auto height) + // "width": 1280, // Waybar width + "spacing": 4, // Gaps between modules (4px) + "output": "${PRIMARY_DISPLAY}", + // Choose the order of the modules + "modules-left": ["wlr/workspaces", "hyprland/submap", "custom/wifionice", "custom/spotify"], + "modules-center": ["hyprland/window", "custom/dunst"], + "modules-right": ["idle_inhibitor", "pulseaudio", "custom/network", "cpu", "memory", "temperature", "backlight", "battery", "clock", "tray"], + // Modules configuration + "wlr/workspaces": { + "disable-scroll": false, + "sort-by-number": true, + "all-outputs": true, + "warp-on-scroll": false, + "format": "{name}: {icon}", + "format-icons": { + "1": "", + "2": "", + "3": "", + "4": "", + "5": "", + "urgent": "", + "focused": "", + "default": "" + } + }, + "keyboard-state": { + "numlock": true, + "capslock": true, + "format": "{name} {icon}", + "format-icons": { + "locked": "", + "unlocked": "" + } + }, + "custom/spotify": { + "exec": "/usr/bin/python3 ~/.config/waybar/scripts/mediaplayer.py --player spotify", + "format": "{} ", + "return-type": "json", + "escape": true, + "on-click": "playerctl play-pause", + "on-scroll-up": "playerctl next", + "on-scroll-down": "playerctl previous" + }, + "custom/dunst": { + "exec": "~/.config/waybar/scripts/dunst.sh", + "return-type": "json", + "on-click": "dunstctl set-paused toggle", + "on-click-middle": "dunstctl context", + "on-click-right": "dunstctl history-pop", + "restart-interval": 1 + }, + "custom/wifionice": { + "exec": "~/.config/waybar/scripts/wifionice.sh", + "escape": true + }, + "idle_inhibitor": { + "format": "{icon}", + "format-icons": { + "activated": "", + "deactivated": "" + } + }, + "tray": { + // "icon-size": 21, + "spacing": 10 + }, + "clock": { + // "timezone": "America/New_York", + "tooltip-format": "{:%Y %B}\n{calendar}", + "format-alt": "{:%Y-%m-%d}" + }, + "cpu": { + "format": "{usage}% ", + "tooltip": false + }, + "memory": { + "format": "{}% " + }, + "temperature": { + // "thermal-zone": 2, + // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input", + "critical-threshold": 80, + // "format-critical": "{temperatureC}°C {icon}", + "format": "{temperatureC}°C {icon}", + "format-icons": ["", "", ""] + }, + "backlight": { + // "device": "acpi_video1", + "format": "{percent}% {icon}", + "format-icons": ["", "", "", "", "", "", "", "", ""] + }, + "battery": { + "states": { + // "good": 95, + "warning": 30, + "critical": 15 + }, + "format": "{capacity}% {icon}", + "format-charging": "{capacity}% ", + "format-plugged": "{capacity}% ", + "format-alt": "{time} {icon}", + // "format-good": "", // An empty format will hide the module + // "format-full": "", + "format-icons": ["", "", "", "", ""] + }, + "battery#bat2": { + "bat": "BAT2" + }, + "custom/network": { + "format": "{}", + "max-length": 40, + "escape": true, + "exec": "$HOME/.config/hypr/hooks/networkmanager.sh 2> /dev/null" // Script in resources folder + }, + "pulseaudio": { + // "scroll-step": 1, // %, can be a float + "format": "{volume}% {icon} {format_source}", + "format-bluetooth": "{volume}% {icon} {format_source}", + "format-bluetooth-muted": " {icon} {format_source}", + "format-muted": " {format_source}", + "format-source": "{volume}% ", + "format-source-muted": "", + "format-icons": { + "headphone": "", + "hands-free": "", + "headset": "", + "phone": "", + "portable": "", + "car": "", + "default": ["", "", ""] + }, + "on-click": "pavucontrol" + }, + "custom/media": { + "format": "{icon} {}", + "return-type": "json", + "max-length": 40, + "format-icons": { + "spotify": "", + "default": "🎜" + }, + "escape": true, + "exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null" // Script in resources folder + // "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name + } +} + diff --git a/waybar/mocha.css b/waybar/mocha.css new file mode 100644 index 0000000..98e218a --- /dev/null +++ b/waybar/mocha.css @@ -0,0 +1,37 @@ +/* +* +* Catppuccin Mocha palette +* Maintainer: rubyowo +* +*/ + +@define-color base #1e1e2e; +@define-color mantle #181825; +@define-color crust #11111b; + +@define-color text #cdd6f4; +@define-color subtext0 #a6adc8; +@define-color subtext1 #bac2de; + +@define-color surface0 #313244; +@define-color surface1 #45475a; +@define-color surface2 #585b70; + +@define-color overlay0 #6c7086; +@define-color overlay1 #7f849c; +@define-color overlay2 #9399b2; + +@define-color blue #89b4fa; +@define-color lavender #b4befe; +@define-color sapphire #74c7ec; +@define-color sky #89dceb; +@define-color teal #94e2d5; +@define-color green #a6e3a1; +@define-color yellow #f9e2af; +@define-color peach #fab387; +@define-color maroon #eba0ac; +@define-color red #f38ba8; +@define-color mauve #cba6f7; +@define-color pink #f5c2e7; +@define-color flamingo #f2cdcd; +@define-color rosewater #f5e0dc; diff --git a/waybar/scripts/dunst.sh b/waybar/scripts/dunst.sh new file mode 100755 index 0000000..d2ce884 --- /dev/null +++ b/waybar/scripts/dunst.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -euo pipefail + +readonly ENABLED=' ' +readonly DISABLED=' ' +dbus-monitor path='/org/freedesktop/Notifications',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged' --profile | + while read -r _; do + PAUSED="$(dunstctl is-paused)" + if [ "$PAUSED" == 'false' ]; then + CLASS="enabled" + TEXT="$ENABLED" + else + CLASS="disabled" + TEXT="$DISABLED" + COUNT="$(dunstctl count waiting)" + if [ "$COUNT" != '0' ]; then + TEXT="$DISABLED ($COUNT)" + fi + fi + printf '{"text": "%s", "class": "%s"}\n' "$TEXT" "$CLASS" + done diff --git a/waybar/scripts/mediaplayer.py b/waybar/scripts/mediaplayer.py new file mode 100644 index 0000000..51a4837 --- /dev/null +++ b/waybar/scripts/mediaplayer.py @@ -0,0 +1,182 @@ +#!/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): + 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.init_players() + + def init_players(self): + for player in self.manager.props.player_names: + 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) + + # 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}") + player = PlayerManager(arguments.player) + player.run() + + +if __name__ == "__main__": + main() diff --git a/waybar/scripts/wifionice.sh b/waybar/scripts/wifionice.sh new file mode 100755 index 0000000..11aa20a --- /dev/null +++ b/waybar/scripts/wifionice.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +icon="🚄" +path_pid="/tmp/polybar-network-wifionice.pid" + +print_train_info() { + while true; do + if [ "$(iwgetid -r)" = "WIFIonICE" ] || [ "$(iwgetid -r)" = "WIFI@DB" ]; then + wifionice=$(curl -sLf https://iceportal.de/api1/rs/status) + + if [ "$(echo "$wifionice" | jq .connection)" = "true" ]; then + wifionice_speed=$(echo "$wifionice" | jq .speed) + wifionice_conn=$( echo "$wifionice" | jq -r .connectivity.currentState ) + wifionice_nextConn=$( echo "$wifionice" | jq -r .connectivity.nextState ) + wifionice_connTime=$( echo "$wifionice" | jq -r .connectivity.remainingTimeSeconds | awk '{printf "%d m", $1/60}' ) + if [ "$wifionice_speed" -ne 0 ]; then + wifionice_speed=" - $wifionice_speed km/h" + else + wifionice_speed="" + fi + + station=$(curl -sLf https://iceportal.de/api1/rs/tripInfo/trip | jq '[.[].stops[]? | select(.info.passed == false)][0]') + + station_name=$(echo "$station" | jq -r '.station.name') + + station_track=$(echo "$station" | jq -r '.track.actual') + + station_arrival=$(echo "$station" | jq -r '.timetable.scheduledArrivalTime') + station_arrival=$(date --date="@$((station_arrival / 1000))" +%H:%M) + + station_delay=$(echo "$station" | jq -r '.timetable.arrivalDelay') + if [ -n "$station_delay" ]; then + station_delay=" ($station_delay)" + else + station_delay="" + fi + + if [ "${wifionice_conn}" = "NO_INFO" ]; then + net_text="Net: 🤷" + else + net_text="Net: $wifionice_conn → $wifionice_nextConn ($wifionice_connTime)" + fi + + echo "$icon $station_arrival$station_delay - $station_name, Gl. $station_track$wifionice_speed | $net_text" + fi + sleep 10 + else + echo "" # hidden + sleep 600 & + wait + fi + done +} + +network_update() { + pid=$(cat "$path_pid") + if [ "$pid" != "" ]; then + kill -USR1 "$pid" + fi +} + +case "$1" in + --update) + network_update + ;; + *) + echo $$ > $path_pid + trap exit INT + trap "echo" USR1 + print_train_info # Does not return + ;; +esac diff --git a/waybar/style.css b/waybar/style.css new file mode 100644 index 0000000..1225c87 --- /dev/null +++ b/waybar/style.css @@ -0,0 +1,244 @@ +@import "mocha.css"; + +* { + /* `otf-font-awesome` is required to be installed for icons */ + font-family: FontAwesome, Roboto, Helvetica, Arial, sans-serif; + font-size: 13px; +} + +window#waybar { + background-color: shade(@base, 0.9); + border: 2px solid alpha(@mantle, 0.3); + border-bottom: 3px solid alpha(@crust, 0.5); + color: @text; + transition-property: background-color; + transition-duration: .5s; +} + +window#waybar.hidden { + opacity: 0.2; +} + +/* +window#waybar.empty { + background-color: transparent; +} +window#waybar.solo { + background-color: #FFFFFF; +} +*/ + +window#waybar.termite { + background-color: #3F3F3F; +} + +window#waybar.chromium { + background-color: #000000; + border: none; +} + +button { + /* Use box-shadow instead of border so the text isn't offset */ + box-shadow: inset 0 -3px transparent; + /* Avoid rounded borders under each button name */ + border: none; + border-radius: 0; +} + +/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */ +button:hover { + background: inherit; + box-shadow: inset 0 -3px #ffffff; +} + +#workspaces button { + padding: 0 5px; + background-color: transparent; + color: #ffffff; +} + +#submap { + padding: 0 10px; + margin: 0 4px; + background-color: @peach; + color: black; +} + +#dunst { + +} + +#workspaces button:hover { + background: alpha(@overlay0, 0.2); +} + +#workspaces button.active { + background-color: @overlay1; + box-shadow: inset 0 -3px #ffffff; +} + +#workspaces button.urgent { + background-color: @red; +} + +#mode { + background-color: #64727D; + border-bottom: 3px solid #ffffff; +} + +#clock, +#battery, +#cpu, +#memory, +#disk, +#temperature, +#backlight, +#custom-network, +#pulseaudio, +#wireplumber, +#custom-media, +#custom-wifionice, +#tray, +#mode, +#idle_inhibitor, +#custom-spotify, +#scratchpad, +#mpd { + padding: 0 10px; + color: #ffffff; + border-bottom: 3px solid alpha(@crust, 0.5); +} +#window, +#workspaces { + margin: 0 4px; +} + +/* If workspaces is the leftmost module, omit left margin */ +.modules-left > widget:first-child > #workspaces { + margin-left: 0; +} + +/* If workspaces is the rightmost module, omit right margin */ +.modules-right > widget:last-child > #workspaces { + margin-right: 0; +} + +#clock { + background-color: #64727D; +} + +#battery { + background-color: @yellow; + color: black; +} + +#battery.charging, #battery.plugged { + color: black; + background-color: @green; +} + +@keyframes blink { + to { + background-color: @yellow; + color: black; + } +} + +#battery.critical:not(.charging) { + background-color: @red; + color: black; + animation-name: blink; + animation-duration: 0.5s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate; +} + +#custom-wifionice { + background-color: @red; + color: black; +} + +label:focus { + background-color: black; +} + +#cpu { + background-color: @green; + color: black; +} + +#memory { + background-color: @lavender; +} + +#disk { + background-color: @lavender; +} + +#backlight { + background-color: @overlay1; +} + +#custom-network { + background-color: @sky; + color: black; +} + +#network.disconnected { + background-color: @overlay2; +} + +#pulseaudio { + background-color: @yellow; + color: black; +} + +#pulseaudio.muted { + background-color: @overlay2; + color: @text; +} + +#custom-media { + background-color: @base; + color: @text; + min-width: 100px; +} + +#custom-spotify { + padding: 0 10px; + margin: 0 4px; + background-color: @green; + color: black; +} + +#temperature { + background-color: @peach; + color: black; +} + +#temperature.critical { + background-color: @red; +} + +#tray { + background-color: @base; +} + +#tray > .passive { + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: @red; +} + +#idle_inhibitor { + background-color: @base; +} + +#idle_inhibitor.activated { + background-color: @lavender; + color: #2d3436; +}