diff --git a/.config/colors/hyprland.conf b/.config/colors/hyprland.conf index a49eee5..e563860 100644 --- a/.config/colors/hyprland.conf +++ b/.config/colors/hyprland.conf @@ -1,2 +1,3 @@ -$active_color = rgba(98C1D9FF) +#$active_color = rgba(f55c20ff) +$active_color = rgba(de3c3cff) $inactive_color = rgba(595959aa) diff --git a/.config/hypr/config/environment.conf b/.config/hypr/config/environment.conf index f0b8fdf..4a2e550 100644 --- a/.config/hypr/config/environment.conf +++ b/.config/hypr/config/environment.conf @@ -5,10 +5,11 @@ # See https://wiki.hyprland.org/Configuring/Keywords/ # Set programs that you use -$terminal = kitty +$terminal = foot #$fileManager = cosmic-files $fileManager = nautilus -$menu = albert toggle +# $menu = albert toggle +$menu = rofi -show drun $notificationManager = swaync-client -t diff --git a/.config/hypr/config/input.conf b/.config/hypr/config/input.conf index 36248c9..ca3b6c4 100644 --- a/.config/hypr/config/input.conf +++ b/.config/hypr/config/input.conf @@ -18,9 +18,7 @@ input { } # https://wiki.hyprland.org/Configuring/Variables/#gestures -gestures { - workspace_swipe = true -} +gesture = 3, horizontal, workspace # Example per-device config # See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more @@ -55,7 +53,7 @@ bind = $mainMod, X, exec, $terminal # Absolutely not because I dislocated my arm bind = $mainMod, SPACE, exec, $menu bind = $mainMod, E, exec, $fileManager bind = $mainMod, A, exec, $notificationManager -bindel = , XF86Launch1, exec, kitty vim ~/.config/hypr/config +bindel = , XF86Launch1, exec, $terminal hx ~/.config/hypr/config # Replace by default editor # Move focus with mainMod + arrow keys bind = $mainMod, left, movefocus, l @@ -63,6 +61,12 @@ bind = $mainMod, right, movefocus, r bind = $mainMod, up, movefocus, u bind = $mainMod, down, movefocus, d +# Move windows with mainMod + shift + arrow keys +bind = $mainMod SHIFT, left, movewindow, l +bind = $mainMod SHIFT, right, movewindow, r +bind = $mainMod SHIFT, up, movewindow, u +bind = $mainMod SHIFT, down, movewindow, d + # Layout bind = $mainMod, F, fullscreen, bind = $mainMod, V, togglesplit, # Horizontal/vertical split diff --git a/.config/hypr/hyprlock.conf b/.config/hypr/hyprlock.conf index de83a38..03b7e53 100644 --- a/.config/hypr/hyprlock.conf +++ b/.config/hypr/hyprlock.conf @@ -1,12 +1,24 @@ # BACKGROUND +## Old config +# background { +# monitor = +# path = ~/.config/hypr/images/lockscreen.jpg +# blur_passes = 0 +# #contrast = 0.8916 +# #brightness = 0.8172 +# #vibrancy = 0.1696 +# #vibrancy_darkness = 0.0 +# } background { monitor = - path = ~/.config/hypr/images/lockscreen.jpg - blur_passes = 0 - #contrast = 0.8916 - #brightness = 0.8172 - #vibrancy = 0.1696 - #vibrancy_darkness = 0.0 + path = screenshot # 'screenshot' = auto-capture screen and blur it + blur_passes = 3 + blur_size = 4 + noise = 0.0 + contrast = 0.7 + brightness = 0.8172 + vibrancy = 0.2 + vibrancy_darkness = 0.0 } # GENERAL @@ -31,7 +43,7 @@ input-field { font_color = rgb(200, 200, 200) fade_on_empty = false font_family = DejaVu Sans - placeholder_text = Enter Pass + placeholder_text = Enter password hide_input = false position = 0, -350 halign = center @@ -83,16 +95,40 @@ label { #} # USER -#label { -# monitor = -# text = Hi, Guillem #$USER -# color = rgba(216, 222, 233, 0.70) -# font_size = 20 -# font_family = DejaVu Sans -# position = 0, -200 -# halign = center -# valign = center -#} +label { + monitor = + text = $USER + color = rgba(216, 222, 233, 0.70) + font_size = 20 + font_family = JetbrainsMonoNL NF + position = 0, -200 + halign = center + valign = center +} + +# Timer +label { + monitor = + text = cmd[update:0] echo "Locked since $TIME" + color = rgba(216, 222, 233, 0.40) + font_size = 20 + font_family = JetbrainsMonoNL NF + position = 0, -250 + halign = center + valign = center +} + +# # Failed login attempts +# label { +# monitor = +# text = cmd[update:1000] echo "$ATTEMPTS Failed attempts" +# color = rgba(216, 222, 233, 0.40) +# font_size = 20 +# font_family = JetbrainsMonoNL NF +# position = 0, -250 +# halign = center +# valign = center +# } # CURRENT SONG label { @@ -100,7 +136,7 @@ label { text = cmd[update:1000] echo "$(~/.config/hypr/scripts/songdetails.sh)" color = rgba(255, 255, 255, 0.7) font_size = 16 - font_family = DejaVu Sans + font_family = JetbrainsMonoNL NF position = 0, 20 halign = center valign = bottom diff --git a/.config/hypr/hyprpaper.conf b/.config/hypr/hyprpaper.conf index 51bf3ef..246b059 100644 --- a/.config/hypr/hyprpaper.conf +++ b/.config/hypr/hyprpaper.conf @@ -1,2 +1,2 @@ -preload = ~/.config/hypr/images/wallpaper.JPG -wallpaper = , ~/.config/hypr/images/wallpaper.JPG +preload = ~/.config/hypr/images/wallpaper.jpg +wallpaper = , ~/.config/hypr/images/wallpaper.jpg diff --git a/.config/hypr/images/lockscreen.jpg b/.config/hypr/images/lockscreen.jpg deleted file mode 100644 index c9c6b41..0000000 Binary files a/.config/hypr/images/lockscreen.jpg and /dev/null differ diff --git a/.config/hypr/images/wallpaper.jpg b/.config/hypr/images/wallpaper.jpg index 4f3b069..4167f89 100644 Binary files a/.config/hypr/images/wallpaper.jpg and b/.config/hypr/images/wallpaper.jpg differ diff --git a/.config/kitty/seventy-nine.conf b/.config/kitty/seventy-nine.conf index 0d75685..6b93975 100644 --- a/.config/kitty/seventy-nine.conf +++ b/.config/kitty/seventy-nine.conf @@ -7,7 +7,8 @@ #: The basic colors foreground #adbac7 -background #14171F +background #0d1117 +#background #14171F #background #111317 selection_foreground #EE6C4D selection_background #3D5A80 diff --git a/.config/rofi/config.rasi b/.config/rofi/config.rasi new file mode 100644 index 0000000..4c9d9d5 --- /dev/null +++ b/.config/rofi/config.rasi @@ -0,0 +1 @@ +@theme "~/.local/share/rofi/themes/boussole.rasi" diff --git a/.config/rofi/wifi/config.rasi b/.config/rofi/wifi/config.rasi new file mode 100644 index 0000000..7c698e0 --- /dev/null +++ b/.config/rofi/wifi/config.rasi @@ -0,0 +1,5 @@ +/* @theme "./themes/rosepine.rasi" */ +@theme "~/.local/share/rofi/themes/boussole.rasi" + configuration { + terminal: "foot"; + } diff --git a/.config/waybar/Atlas/config.jsonc b/.config/waybar/Atlas/config.jsonc new file mode 100644 index 0000000..a147e99 --- /dev/null +++ b/.config/waybar/Atlas/config.jsonc @@ -0,0 +1,508 @@ +{ + "layer": "top", + "position": "right", + "margin": "1 1 1 2", + "reload_style_on_change": true, + + // Modules display + + "modules-left": [ + //"custom/updates", + "hyprland/workspaces", + "hyprland/submap" + //"group/info", + //"hyprland/window" + ], + + "modules-right": [ + "custom/recorder", + "privacy", + "group/brightness", + "group/sound", + "group/connection", + "tray", + "group/together", + //"group/cnoti", + "group/power" + ], + + + // Modules definition + + // Up + + "hyprland/workspaces": { + "format": "{icon}", + "on-click": "activate", + "all-outputs": true, + "format-icons": { + "1": "1", + "2": "2", + "3": "3", + "4": "4", + "5": "5", + "6": "6", + "7": "7", + "8": "8", + "9": "9", + "10": "10" + } + }, + "hyprland/submap": { + "format": "󰇘", + "max-length": 8, + "tooltip": true + }, + "group/info": { + "orientation": "inherit", + "drawer": { + "transition-duration": 500, + "transition-left-to-right": false + }, + "modules": [ + "custom/dmark", + "group/gcpu", + "memory", + "disk", + "hyprland/window" + ] + }, + "hyprland/window": { + "orientation": "vertical", + "format":"{class}", + "spearate-outputs": true, + "icon": false + }, + "custom/dmark": { + "format": "", + "tooltip": false + }, + "group/gcpu": { + "orientation": "inherit", + "modules": [ + "custom/cpu-icon", + "custom/cputemp", + "cpu" + ] + }, + "custom/cpu-icon": { + "format": "󰻠", + "tooltip": false + }, + "custom/cputemp": { + "format": "{}", + "exec": "~/.config/waybar/bin/cputemp", + "interval": 10, + "return-type": "json" + }, + "cpu": { + "format": "{usage}󱉸", + "on-click": "foot btop" + }, + "memory": { + "format": "  \n{:2}󱉸" + }, + "disk": { + "interval": 600, + "format": " 󰋊 \n{percentage_used}󱉸", + "path": "/" + }, + + // Bottom + + "custom/recorder": { + "format": "{}", + "interval": "once", + "exec": "echo ''", + "tooltip": "false", + "exec-if": "pgrep 'wl-screenrec'", + "on-click": "recorder", + "signal": 4 + }, + "privacy": { + "orientation": "vertical", + "icon-spacing": 4, + "icon-size": 14, + "transition-duration": 250, + "modules": [ + { + "type": "screenshare", + "tooltip": true, + "tooltip-icon-size": 24 + } + ] + }, + "group/brightness": { + "orientation": "inherit", + "drawer": { + "transition-duration": 500, + "transition-left-to-right": false + }, + "modules": [ + "backlight" + //"backlight/slider" + ] + }, + "backlight": { + "device": "intel_backlight", + "format": "{icon}", + "format-icons": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "on-scroll-down": "brightnessctl s 5%-", + "on-scroll-up": "brightnessctl s +5%", + "tooltip": true, + "tooltip-format": "Brightness: {percent}% ", + "smooth-scrolling-threshold": 1 + }, + "backlight/slider": { + "min": 1, + "max": 100, + "orientation": "vertical", + "device": "intel_backlight" + }, + "group/sound": { + "orientation": "inherit", + "modules": [ + "group/audio", + "custom/notifications" + ] + }, + "group/audio": { + "orientation": "inherit", + "drawer": { + "transition-duration": 500, + "transition-left-to-right": false + }, + "modules": [ + "pulseaudio", + "pulseaudio#mic", + "pulseaudio/slider" + ] + }, + "group/cnoti": { + "orientation": "inherit", + "modules": [ + "custom/github" + ] + }, + "group/connection": { + "orientation": "inherit", + "modules": [ + //"custom/vpn", + "custom/hotspot", + "group/network" + //"group/bluetooth" + ] + }, + "group/together": { + "orientation": "inherit", + "modules": [ + "group/utils", + "clock" + ] + }, + "group/utils": { + "orientation": "inherit", + "drawer": { + "transition-duration": 500, + "transition-left-to-right": true + }, + "modules": [ + "custom/mark", + "custom/weather", + "custom/colorpicker", + "custom/hyprshade", + "idle_inhibitor" + //"custom/hyprkill" + ] + }, + "group/network": { + "orientation": "inherit", + "drawer": { + "transition-duration": 500, + "transition-left-to-right": false + }, + "modules": [ + "network", + "network#speed" + ] + }, + "group/bluetooth": { + "orientation": "inherit", + "drawer": { + "transition-duration": 500, + "transition-left-to-right": true + }, + "modules": [ + "bluetooth", + "bluetooth#status" + ] + }, + "group/battery": { + "orientation":"vertical", + "modules": [ + "battery", + "custom/battery_percentage", + ] + }, + "group/power": { + "orientation": "inherit", + "drawer": { + "transition-duration": 500, + "transition-left-to-right": false + }, + "modules": [ + "group/battery", + "power-profiles-daemon", + ] + }, + "tray": { + "icon-size": 18, + "spacing": 10 + }, + "pulseaudio": { + "format": "{icon}", + "format-bluetooth": "{icon}", + "tooltip-format": "{volume}% {icon} | {desc}", + "format-muted": "󰖁", + "format-icons": { + "headphones": "", + "handsfree": "󱡏", + "headset": "", + "phone": "", + "portable": "", + "car": " ", + "default": [ + "󰕿", + "󰖀", + "󰕾" + ] + }, + "on-click": "volume mute", + //"on-click-middle": "pavucontrol", + "on-scroll-up": "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+", + "on-scroll-down": "wpctl set-volume @DEFAULT_AUDIO_SINK@ -5%", + "smooth-scrolling-threshold": 1 + }, + "pulseaudio#mic": { + "format": "{format_source}", + "format-source": "󰍬", + "format-source-muted": "󰍭", + "tooltip-format": "{volume}% {format_source} ", + "on-click": "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle", + "on-scroll-down": "wpctl set-volume @DEFAULT_SOURCE@ 5%-", + "on-scroll-up": "wpctl set-volume -l 1 @DEFAULT_SOURCE@ 5%+" + }, + "pulseaudio/slider": { + "min": 0, + "max": 100, + "orientation": "vertical" + }, + "network": { + "format": "{icon}", + "format-icons": { + "wifi": [ + "󰤨" + ], + "ethernet": [ + "" + ], + "disconnected": [ + "󰤮" + ] + }, + "format-wifi": "󰤨", + "format-ethernet": "", + "format-disconnected": "󰤮", + "format-linked": "󰈁", + "tooltip": false, + //"on-click": "pgrep -x rofi &>/dev/null && notify-send rofi || networkmanager_dmenu" + "on-click": "kitty nmtui" + }, + "network#speed": { + "format": " {bandwidthDownBits} ", + "rotate": 90, + "interval": 5, + "tooltip-format": "{ipaddr}", + "tooltip-format-wifi": "{essid} ({signalStrength}%)  \n{ipaddr} | {frequency} MHz{icon} ", + "tooltip-format-ethernet": "{ifname} 󰈀 \n{ipaddr} | {frequency} MHz{icon} ", + "tooltip-format-disconnected": "Not Connected to any type of Network", + "tooltip": true, + "on-click": "pgrep -x rofi &>/dev/null && notify-send rofi || networkmanager_dmenu" + }, + "bluetooth": { + "format-on": "󰂯", + "format-off": "󰂲", + "format-disabled": "", + "format-connected": "󰂱", + "tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected", + "tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}", + "tooltip-format-enumerate-connected": "{device_alias}\t{device_address}", + "tooltip-format-enumerate-connected-battery": "{device_alias}\t{device_address}\t{device_battery_percentage}%", + //"on-click": "rofi-bluetooth -config ~/.config/rofi/menu.d/network.rasi -i" + "on-click": "toggle-bluetooth" + }, + "bluetooth#status": { + "format-on": "", + "format-off": "", + "format-disabled": "", + "format-connected": "{num_connections}", + "format-connected-battery": "{device_battery_percentage}%", + "tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected", + "tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}", + "tooltip-format-enumerate-connected": "{device_alias}\t{device_address}", + "tooltip-format-enumerate-connected-battery": "{device_alias}\t{device_address}\t{device_battery_percentage}%", + "on-click": "rofi-bluetooth -config ~/.config/rofi/menu.d/network.rasi -i" + }, + "battery": { + "rotate": 270, + "states": { + "good": 95, + "warning": 16, + "critical": 8 + }, + "format": "{icon}", + "interval": 1, + "format-charging": "󰂄", + "format-full": "{icon}", + "format-icons": [ + "󰁻", + "󰁼", + "󰁾", + "󰂀", + "󰂂", + "󰁹" + ], + "tooltip-format": "{timeTo} {capacity} % | {power} W" + }, + "custom/battery_percentage": { + "format": "{}", + //"exec": "cat /sys/class/power_supply/BAT*/capacity", + + // Makes battery like if 85% was the maximum capacity (100%). + // Useful for people that limit charge up to a certain percentage but like having real percentage + "exec": "echo $(( $(cat /sys/class/power_supply/BAT*/capacity) * 100 / 85 ))", + "interval": 10, + "tooltip": false + }, + "clock": { + "format": "{:%H\n%M}", + "tooltip-format": "{calendar}", + "calendar": { + "mode": "month", + "mode-mon-col": 3, + "weeks-pos": "right", + "on-scroll": 1, + "on-click-right": "mode", + "on-click": "swaync-client -t", + "format": { + "today": "{}" + } + } + }, + "power-profiles-daemon": { + "format": "{icon}", + "tooltip-format": "Power profile: {profile}\nDriver: {driver}", + "tooltip": true, + "format-icons": { + "default": "", + "performance": "", + "balanced": "", + "power-saver": "" + + } + }, + "custom/hyprshade": { + "format": "{}", + "tooltip": true, + "signal": 11, + "exec": "toggle-hyprshade status", + "on-click": "toggle-hyprshade", + "return-type": "json" + }, + "custom/weather": { + "format": "{}", + "tooltip": true, + "interval": 3600, + "exec": "wttrbar --custom-indicator '{ICON}\n{temp_C}' --location noida", + "return-type": "json" + }, + "custom/updates": { + "format": "{}", + "interval": 10800, + "exec": "~/.config/waybar/bin/updatecheck", + "return-type": "json", + "exec-if": "exit 0", + "signal": 8 + }, + "custom/vpn": { + "format": "{} ", + "exec": "~/.config/waybar/bin/vpn", + "return-type": "json", + "interval": 5 + }, + "custom/hotspot": { + "format": "{} ", + "exec": "~/.config/waybar/bin/hotspot", + "return-type": "json", + "on-click": "hash wihotspot && wihotspot", + "interval": 5 + }, + "custom/mark": { + "format": "", + "tooltip": false + }, + "custom/colorpicker": { + "format": "{}", + "interval": "once", + "on-click": "hyprpicker", + "signal": 1 + }, + "custom/hyprkill": { + "format": "{}", + "interval": "once", + "exec": "echo '󰱝\nKill clients using hyrpctl kill'", + "on-click": "sleep 1 && hyprctl kill" + }, + "custom/notifications": { + "format": "{} ", + "exec": "noti-cycle -j", + "on-click": "noti-cycle", + "on-click-right": "noti-cycle rofi", + "return-type": "json", + "interval": "once", + "signal": 2 + }, + "custom/github": { + "format": "{}", + "return-type": "json", + "interval": 3600, + "signal": 9, + "exec": "$HOME/.config/waybar/bin/github.sh", + "on-click": "xdg-open https://github.com/notifications;pkill -RTMIN+9 waybar" + }, + "idle_inhibitor": { + "format": "{icon}", + "tooltip-format-activated": "Idle Inhibitor is active", + "tooltip-format-deactivated": "Idle Inhibitor is not active", + "format-icons": { + "activated": "󰅶", + "deactivated": "󰾪" + } + } +} diff --git a/.config/waybar/Atlas/style.css b/.config/waybar/Atlas/style.css new file mode 100644 index 0000000..93653d9 --- /dev/null +++ b/.config/waybar/Atlas/style.css @@ -0,0 +1,305 @@ +@import "colors.css"; +@define-color active @foreground; + +* { + font-size: 17px; + font-family: "JetBrainsMono Nerd Font Propo"; + min-width: 8px; + min-height: 0px; + border: none; + border-radius: 0; + box-shadow: none; + text-shadow: none; + padding: 0px; + +} + +window#waybar { + transition-property: background-color; + transition-duration: 0.5s; + border-radius: 4px; + border: 1px solid alpha(@active, 0.2); + background: @background; + background: alpha(@background, 0.8); + color: @foreground; +} + +menu, +tooltip { + border-radius: 2px; + padding: 2px; + border: 1px solid @active; + background: @background; + + color: @foreground; +} + +menu label, +tooltip label { + font-size: 14px; + color: @foreground; +} + +#submap, +#tray>.needs-attention { + animation-name: blink-active; + animation-duration: 1s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate; +} + +.modules-right { + margin: 0px 6px 4px 6px; + border-radius: 4px; + background: alpha(@background, 0); + color: @foreground; +} + +.modules-left { + transition-property: background-color; + transition-duration: 0.5s; + margin: 6px 5px 6px 5px; /* Testing with 5 pixels on left/right */ + border-radius: 4px; + background: alpha(@background, 0.5); + color: @foreground; + border: 1px solid alpha(@active, 0.1); +} + +#gcpu, +#custom-github, +#memory, +#disk, +#together, +#submap, +#custom-weather, +#custom-recorder, +#connection, +#cnoti, +#brightness, +#power, +#custom-updates, +#tray, +/*#audio,*/ /* Duplicate with #sound */ +#sound, +#privacy { /*Controls all the right modules for some reason*/ + border-radius: 0.15em; + margin: 2px 1px 3px 1px; + background: alpha(darker(@active), 0.25); + border: 1px solid alpha(darker(@active), 0.0); +} + +/* Override specific parameters*/ + +#brightness, +#sound { + padding: 1px 0px; +} + +#custom-notifications { + padding-left: 4px; +} + +#custom-hotspot, +#custom-github, +#custom-notifications { + font-size: 14px; +} + +#custom-vpn, +#custom-hotspot { + background: alpha(darker(@active), 0.3); +} + +#privacy-item { + padding: 6px 0px 6px 6px; +} + +#gcpu { + padding: 8px 0px 8px 0px; +} + +#custom-cpu-icon { + font-size: 25px; +} + +#custom-cputemp, +#disk, +#memory, +#cpu { + font-size: 14px; + font-weight: bold; +} + +#custom-github { + padding-top: 2px; +} + +#custom-dmark { + color: alpha(@foreground, 0.3); +} + +#submap { + margin-bottom: 0px; +} + +#workspaces { + margin: 0px 2px; + padding: 2px 0px; + border-radius: 8px; +} + +#workspaces button { + transition-property: background-color; + transition-duration: 0.1s; + color: @foreground; + background: transparent; + border-radius: 4px; + color: alpha(@foreground, 0.3); + padding: 2px 0px; +} + +#workspaces button.urgent { + font-weight: bold; + color: @foreground; +} + +#workspaces button.active { + padding: 2px 0px; + background: alpha(@active, 0.4); + color: @foreground; + border-radius: 2px; +} + +#network.wifi { + margin: 2px; +} + +#network.disconnected { + margin: 2px; +} + +#network.ethernet { + margin: 2px; +} + +#submap { + min-width: 0px; + margin: 4px 6px 4px 6px; +} + +#custom-weather, +#tray { + padding: 4px 0px 4px 0px; +} + +#bluetooth { + padding-top: 2px; +} + +#group-battery { + /* border-radius: 8px; */ + /* padding: 4px 0px; */ + margin: 4px 2px 4px 2px; +} + +#battery { + font-size: 1.5rem; + border-radius: 3px; + padding: 4px 0px; + margin: 0 0px; +} + +#battery.charging { + color: @charging; +} + +#battery.discharging.warning { + background-color: #cf9022; + /*animation-name: blink-yellow; + animation-duration: 1s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate; */ +} + +#battery.discharging.critical { + background-color: #c64d4f; + /*animation-name: blink-red; + animation-duration: 1s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate;*/ +} + +#custom-battery_percentage { + font-size: 1.1rem; + border-radius: 100px; + /* padding: 0 2px; */ + margin: 1px 0; + /* margin: 4px 2px 4px 2px; */ +} + +#clock { + font-weight: bold; + padding: 4px 2px 2px 2px; +} + +#pulseaudio.mic { + border-radius: 4px; + color: @foreground; + padding-left: 2px; +} + +#backlight-slider slider, +#pulseaudio-slider slider { + background-color: transparent; + box-shadow: none; +} + +#backlight-slider trough, +#pulseaudio-slider trough { + margin-top: 4px; + min-width: 6px; + min-height: 60px; + border-radius: 8px; + background-color: alpha(@background, 0.6); +} + +#backlight-slider highlight, +#pulseaudio-slider highlight { + border-radius: 8px; + background-color: @foreground; +} + +#bluetooth.discoverable, +#bluetooth.discovering, +#bluetooth.pairable { + border-radius: 8px; + animation-name: blink-active; + animation-duration: 1s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate; +} + +@keyframes blink-active { + to { + background-color: @active; + color: @foreground; + } +} + +@keyframes blink-red { + to { + background-color: #c64d4f; + color: @foreground; + } +} + +@keyframes blink-yellow { + to { + background-color: #cf9022; + color: @foreground; + } +} diff --git a/.config/waybar/style_minimal.css b/.config/waybar/Atlas/style_minimal.css similarity index 100% rename from .config/waybar/style_minimal.css rename to .config/waybar/Atlas/style_minimal.css diff --git a/.config/waybar/Boussole/config.jsonc b/.config/waybar/Boussole/config.jsonc new file mode 100644 index 0000000..b24f6cd --- /dev/null +++ b/.config/waybar/Boussole/config.jsonc @@ -0,0 +1,224 @@ +{ + "layer": "top", + "position": "bottom", + "height": 24, + "spacing": 5, + + "modules-left": ["hyprland/workspaces"], + "modules-center": ["clock"], + "modules-right": ["tray", "group/audio", "bluetooth", "group/network_grp", "idle_inhibitor", "group/power" ], + + + "group/network_grp": { + "orientation": "inherit", + "drawer": { + "transition-duration": 300, + "transition-left-to-right": false + }, + "modules": [ + "network", + "network#speed" + ] + }, + + "group/audio": { + "orientation": "inherit", + "drawer": { + "transition-duration": 300, + "transition-left-to-right": false + }, + "modules": [ + "pulseaudio", + "pulseaudio#mic", + "pulseaudio#volume", + ] + }, + + "group/power": { + "orientation": "inherit", + "drawer": { + "transition-duration": 300, + "transition-left-to-right": true + }, + "modules": [ + "battery", + "custom/battery_percentage", + "power-profiles-daemon", + ] + }, + + "hyprland/workspaces": { + "format": "{icon}", + "on-click": "activate", + "format-icons": { + "active": "\uf444", + "default": "\uf4c3" + }, + "icon-size": 10, + "sort-by-number": true, + "persistent-workspaces": { + "1": [], + "2": [], + "3": [], + "4": [], + "5": [], + } + }, + + "clock": { + "format": "{:%d.%m.%Y | %H:%M}" + }, + + //"wireplumber": { + // "format": "\udb81\udd7e {volume}%", + // "max-volume": 100, + // "scroll-step": 5 + //}, + + "pulseaudio": { + "format": "{icon} ", + "format-bluetooth": "{icon}", + "tooltip-format": "{volume}% {icon} | {desc}", + "format-muted": "󰖁", + "format-icons": { + "headphones": "", + "handsfree": "󱡏", + "headset": "", + "phone": "", + "portable": "", + "car": " ", + "default": [ + "󰕿", + "󰖀", + "󰕾" + ] + }, + "on-click": "volume mute", + //"on-click-middle": "pavucontrol", + "on-scroll-up": "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+", + "on-scroll-down": "wpctl set-volume @DEFAULT_AUDIO_SINK@ -5%", + "smooth-scrolling-threshold": 1 + }, + "pulseaudio#volume": { + "format": " {volume}% " + }, + // TODO missing + "pulseaudio#mic": { + "format": "{format_source}", + "format-source": "󰍬", + "format-source-muted": "󰍭", + "tooltip-format": "{volume}% {format_source} ", + "on-click": "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle", + "on-scroll-down": "wpctl set-volume @DEFAULT_SOURCE@ 5%-", + "on-scroll-up": "wpctl set-volume -l 1 @DEFAULT_SOURCE@ 5%+" + }, + +// "battery": { +// "bat": "BAT1", +// "interval": 60, +// "format": "{icon} {capacity}%", +// "format-icons": ["\uf244", "\uf243", "\uf242", "\uf241", "\uf240"], +// }, + "battery": { + "rotate": 270, + "states": { + "good": 95, + "warning": 16, + "critical": 8 + }, + "format": "{icon}", + "interval": 1, + "format-charging": "󰂄", + "format-full": "{icon}", + "format-icons": [ + "󰁻", + "󰁼", + "󰁾", + "󰂀", + "󰂂", + "󰁹" + ], + "tooltip-format": "{timeTo} {capacity} % | {power} W" + }, + "custom/battery_percentage": { + "format": "{}", + //"exec": "cat /sys/class/power_supply/BAT*/capacity", + + // Makes battery like if 85% was the maximum capacity (100%). + // Useful for people that limit charge up to a certain percentage but like having real percentage + "exec": "echo $(( $(cat /sys/class/power_supply/BAT*/capacity) * 100 / 85 ))%", + "interval": 10, + "tooltip": false + }, + + + "memory": { + "interval": 30, + "format": "\uf4bc {used:0.1f}G" + }, + + "temperature": { + "format": "{temperatureC}°C" + }, + + "network": { + "format": "", + "format-ethernet": "\udb83\udc9d", + "format-wifi": "{icon}", + "format-disconnected": "\udb83\udc9c", + "format-icons": ["\udb82\udd2f", "\udb82\udd1f", "\udb82\udd22", "\udb82\udd25", "\udb82\udd28"], + "tooltip-format-wifi": "{essid} ({signalStrength}%)", + "tooltip-format-ethernet": "{ifname}", + "tooltip-format-disconnected": "Disconnected", + "on-click": "python ~/.local/bin/rofi-wifi", + }, + "network#speed": { + "format": "{bandwidthDownBits}", + "interval": 5, + "tooltip-format": "{ipaddr}", + "tooltip-format-wifi": "{essid} ({signalStrength}%)  \n{ipaddr} | {frequency} MHz{icon} ", + "tooltip-format-ethernet": "{ifname} 󰈀 \n{ipaddr} | {frequency} MHz{icon}", + "tooltip-format-disconnected": "Disconnected", + "tooltip": true + }, + + "bluetooth": { + "format": "\udb80\udcaf", + "format-disabled": "\udb80\udcb2", + "format-connected": "\udb80\udcb1", + "tooltip-format": "{controller_alias}\t{controller_address}", + "tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{device_enumerate}", + "tooltip-format-enumerate-connected": "{device_alias}\t{device_address}", + "on-click": "rofi-bluetooth" + }, + + "power-profiles-daemon": { + "format": "{icon}", + "tooltip-format": "Power profile: {profile}\nDriver: {driver}", + "tooltip": true, + "format-icons": { + "default": "", + "performance": "", + "balanced": "", + "power-saver": "" + + } + }, + + "hyprland/language": { + "format": "{short}" + }, + + "tray": { + "icon-size": 16, + "spacing": 16 + }, + + "idle_inhibitor": { + "format": "{icon}", + "format-icons": { + "activated": "\udb80\udd76", + "deactivated": "\udb83\udfaa" + } + } +} diff --git a/.config/waybar/Boussole/style.css b/.config/waybar/Boussole/style.css new file mode 100644 index 0000000..ed52bfa --- /dev/null +++ b/.config/waybar/Boussole/style.css @@ -0,0 +1,60 @@ +@define-color foreground #eeeeee; +@define-color foreground-inactive #aaaaaa; +@define-color background #000000; + +* { + font-family: JetBrainsMono Nerd Font Propo; + font-size: 17px; + padding: 0; + margin: 0; +} + +#waybar { + color: @foreground; + background-color: @background; +} + +#workspaces button { + color: @foreground; + padding: 0 0.2em; +} + +#workspaces button.empty { + color: @foreground-inactive; +} + +#memory, +#wireplumber, +#audio, +#language, +#network, +#bluetooth + { + padding-right: 1em +} + +#custom-battery_percentage, +#battery { + padding-right: 0.3em +} + +#power-profiles-daemon { + padding-right: 0.7em; + padding-left: 0.4em +} + +#idle_inhibitor { + padding-right: 0.6em +} + +#tray { + padding-right: 1em +} + +#network_grp { + padding: 0em +} + +#pulseaudio.mic { + padding-left: .7em +} diff --git a/.config/waybar/config.jsonc b/.config/waybar/config.jsonc deleted file mode 100644 index a147e99..0000000 --- a/.config/waybar/config.jsonc +++ /dev/null @@ -1,508 +0,0 @@ -{ - "layer": "top", - "position": "right", - "margin": "1 1 1 2", - "reload_style_on_change": true, - - // Modules display - - "modules-left": [ - //"custom/updates", - "hyprland/workspaces", - "hyprland/submap" - //"group/info", - //"hyprland/window" - ], - - "modules-right": [ - "custom/recorder", - "privacy", - "group/brightness", - "group/sound", - "group/connection", - "tray", - "group/together", - //"group/cnoti", - "group/power" - ], - - - // Modules definition - - // Up - - "hyprland/workspaces": { - "format": "{icon}", - "on-click": "activate", - "all-outputs": true, - "format-icons": { - "1": "1", - "2": "2", - "3": "3", - "4": "4", - "5": "5", - "6": "6", - "7": "7", - "8": "8", - "9": "9", - "10": "10" - } - }, - "hyprland/submap": { - "format": "󰇘", - "max-length": 8, - "tooltip": true - }, - "group/info": { - "orientation": "inherit", - "drawer": { - "transition-duration": 500, - "transition-left-to-right": false - }, - "modules": [ - "custom/dmark", - "group/gcpu", - "memory", - "disk", - "hyprland/window" - ] - }, - "hyprland/window": { - "orientation": "vertical", - "format":"{class}", - "spearate-outputs": true, - "icon": false - }, - "custom/dmark": { - "format": "", - "tooltip": false - }, - "group/gcpu": { - "orientation": "inherit", - "modules": [ - "custom/cpu-icon", - "custom/cputemp", - "cpu" - ] - }, - "custom/cpu-icon": { - "format": "󰻠", - "tooltip": false - }, - "custom/cputemp": { - "format": "{}", - "exec": "~/.config/waybar/bin/cputemp", - "interval": 10, - "return-type": "json" - }, - "cpu": { - "format": "{usage}󱉸", - "on-click": "foot btop" - }, - "memory": { - "format": "  \n{:2}󱉸" - }, - "disk": { - "interval": 600, - "format": " 󰋊 \n{percentage_used}󱉸", - "path": "/" - }, - - // Bottom - - "custom/recorder": { - "format": "{}", - "interval": "once", - "exec": "echo ''", - "tooltip": "false", - "exec-if": "pgrep 'wl-screenrec'", - "on-click": "recorder", - "signal": 4 - }, - "privacy": { - "orientation": "vertical", - "icon-spacing": 4, - "icon-size": 14, - "transition-duration": 250, - "modules": [ - { - "type": "screenshare", - "tooltip": true, - "tooltip-icon-size": 24 - } - ] - }, - "group/brightness": { - "orientation": "inherit", - "drawer": { - "transition-duration": 500, - "transition-left-to-right": false - }, - "modules": [ - "backlight" - //"backlight/slider" - ] - }, - "backlight": { - "device": "intel_backlight", - "format": "{icon}", - "format-icons": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "on-scroll-down": "brightnessctl s 5%-", - "on-scroll-up": "brightnessctl s +5%", - "tooltip": true, - "tooltip-format": "Brightness: {percent}% ", - "smooth-scrolling-threshold": 1 - }, - "backlight/slider": { - "min": 1, - "max": 100, - "orientation": "vertical", - "device": "intel_backlight" - }, - "group/sound": { - "orientation": "inherit", - "modules": [ - "group/audio", - "custom/notifications" - ] - }, - "group/audio": { - "orientation": "inherit", - "drawer": { - "transition-duration": 500, - "transition-left-to-right": false - }, - "modules": [ - "pulseaudio", - "pulseaudio#mic", - "pulseaudio/slider" - ] - }, - "group/cnoti": { - "orientation": "inherit", - "modules": [ - "custom/github" - ] - }, - "group/connection": { - "orientation": "inherit", - "modules": [ - //"custom/vpn", - "custom/hotspot", - "group/network" - //"group/bluetooth" - ] - }, - "group/together": { - "orientation": "inherit", - "modules": [ - "group/utils", - "clock" - ] - }, - "group/utils": { - "orientation": "inherit", - "drawer": { - "transition-duration": 500, - "transition-left-to-right": true - }, - "modules": [ - "custom/mark", - "custom/weather", - "custom/colorpicker", - "custom/hyprshade", - "idle_inhibitor" - //"custom/hyprkill" - ] - }, - "group/network": { - "orientation": "inherit", - "drawer": { - "transition-duration": 500, - "transition-left-to-right": false - }, - "modules": [ - "network", - "network#speed" - ] - }, - "group/bluetooth": { - "orientation": "inherit", - "drawer": { - "transition-duration": 500, - "transition-left-to-right": true - }, - "modules": [ - "bluetooth", - "bluetooth#status" - ] - }, - "group/battery": { - "orientation":"vertical", - "modules": [ - "battery", - "custom/battery_percentage", - ] - }, - "group/power": { - "orientation": "inherit", - "drawer": { - "transition-duration": 500, - "transition-left-to-right": false - }, - "modules": [ - "group/battery", - "power-profiles-daemon", - ] - }, - "tray": { - "icon-size": 18, - "spacing": 10 - }, - "pulseaudio": { - "format": "{icon}", - "format-bluetooth": "{icon}", - "tooltip-format": "{volume}% {icon} | {desc}", - "format-muted": "󰖁", - "format-icons": { - "headphones": "", - "handsfree": "󱡏", - "headset": "", - "phone": "", - "portable": "", - "car": " ", - "default": [ - "󰕿", - "󰖀", - "󰕾" - ] - }, - "on-click": "volume mute", - //"on-click-middle": "pavucontrol", - "on-scroll-up": "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+", - "on-scroll-down": "wpctl set-volume @DEFAULT_AUDIO_SINK@ -5%", - "smooth-scrolling-threshold": 1 - }, - "pulseaudio#mic": { - "format": "{format_source}", - "format-source": "󰍬", - "format-source-muted": "󰍭", - "tooltip-format": "{volume}% {format_source} ", - "on-click": "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle", - "on-scroll-down": "wpctl set-volume @DEFAULT_SOURCE@ 5%-", - "on-scroll-up": "wpctl set-volume -l 1 @DEFAULT_SOURCE@ 5%+" - }, - "pulseaudio/slider": { - "min": 0, - "max": 100, - "orientation": "vertical" - }, - "network": { - "format": "{icon}", - "format-icons": { - "wifi": [ - "󰤨" - ], - "ethernet": [ - "" - ], - "disconnected": [ - "󰤮" - ] - }, - "format-wifi": "󰤨", - "format-ethernet": "", - "format-disconnected": "󰤮", - "format-linked": "󰈁", - "tooltip": false, - //"on-click": "pgrep -x rofi &>/dev/null && notify-send rofi || networkmanager_dmenu" - "on-click": "kitty nmtui" - }, - "network#speed": { - "format": " {bandwidthDownBits} ", - "rotate": 90, - "interval": 5, - "tooltip-format": "{ipaddr}", - "tooltip-format-wifi": "{essid} ({signalStrength}%)  \n{ipaddr} | {frequency} MHz{icon} ", - "tooltip-format-ethernet": "{ifname} 󰈀 \n{ipaddr} | {frequency} MHz{icon} ", - "tooltip-format-disconnected": "Not Connected to any type of Network", - "tooltip": true, - "on-click": "pgrep -x rofi &>/dev/null && notify-send rofi || networkmanager_dmenu" - }, - "bluetooth": { - "format-on": "󰂯", - "format-off": "󰂲", - "format-disabled": "", - "format-connected": "󰂱", - "tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected", - "tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}", - "tooltip-format-enumerate-connected": "{device_alias}\t{device_address}", - "tooltip-format-enumerate-connected-battery": "{device_alias}\t{device_address}\t{device_battery_percentage}%", - //"on-click": "rofi-bluetooth -config ~/.config/rofi/menu.d/network.rasi -i" - "on-click": "toggle-bluetooth" - }, - "bluetooth#status": { - "format-on": "", - "format-off": "", - "format-disabled": "", - "format-connected": "{num_connections}", - "format-connected-battery": "{device_battery_percentage}%", - "tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected", - "tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}", - "tooltip-format-enumerate-connected": "{device_alias}\t{device_address}", - "tooltip-format-enumerate-connected-battery": "{device_alias}\t{device_address}\t{device_battery_percentage}%", - "on-click": "rofi-bluetooth -config ~/.config/rofi/menu.d/network.rasi -i" - }, - "battery": { - "rotate": 270, - "states": { - "good": 95, - "warning": 16, - "critical": 8 - }, - "format": "{icon}", - "interval": 1, - "format-charging": "󰂄", - "format-full": "{icon}", - "format-icons": [ - "󰁻", - "󰁼", - "󰁾", - "󰂀", - "󰂂", - "󰁹" - ], - "tooltip-format": "{timeTo} {capacity} % | {power} W" - }, - "custom/battery_percentage": { - "format": "{}", - //"exec": "cat /sys/class/power_supply/BAT*/capacity", - - // Makes battery like if 85% was the maximum capacity (100%). - // Useful for people that limit charge up to a certain percentage but like having real percentage - "exec": "echo $(( $(cat /sys/class/power_supply/BAT*/capacity) * 100 / 85 ))", - "interval": 10, - "tooltip": false - }, - "clock": { - "format": "{:%H\n%M}", - "tooltip-format": "{calendar}", - "calendar": { - "mode": "month", - "mode-mon-col": 3, - "weeks-pos": "right", - "on-scroll": 1, - "on-click-right": "mode", - "on-click": "swaync-client -t", - "format": { - "today": "{}" - } - } - }, - "power-profiles-daemon": { - "format": "{icon}", - "tooltip-format": "Power profile: {profile}\nDriver: {driver}", - "tooltip": true, - "format-icons": { - "default": "", - "performance": "", - "balanced": "", - "power-saver": "" - - } - }, - "custom/hyprshade": { - "format": "{}", - "tooltip": true, - "signal": 11, - "exec": "toggle-hyprshade status", - "on-click": "toggle-hyprshade", - "return-type": "json" - }, - "custom/weather": { - "format": "{}", - "tooltip": true, - "interval": 3600, - "exec": "wttrbar --custom-indicator '{ICON}\n{temp_C}' --location noida", - "return-type": "json" - }, - "custom/updates": { - "format": "{}", - "interval": 10800, - "exec": "~/.config/waybar/bin/updatecheck", - "return-type": "json", - "exec-if": "exit 0", - "signal": 8 - }, - "custom/vpn": { - "format": "{} ", - "exec": "~/.config/waybar/bin/vpn", - "return-type": "json", - "interval": 5 - }, - "custom/hotspot": { - "format": "{} ", - "exec": "~/.config/waybar/bin/hotspot", - "return-type": "json", - "on-click": "hash wihotspot && wihotspot", - "interval": 5 - }, - "custom/mark": { - "format": "", - "tooltip": false - }, - "custom/colorpicker": { - "format": "{}", - "interval": "once", - "on-click": "hyprpicker", - "signal": 1 - }, - "custom/hyprkill": { - "format": "{}", - "interval": "once", - "exec": "echo '󰱝\nKill clients using hyrpctl kill'", - "on-click": "sleep 1 && hyprctl kill" - }, - "custom/notifications": { - "format": "{} ", - "exec": "noti-cycle -j", - "on-click": "noti-cycle", - "on-click-right": "noti-cycle rofi", - "return-type": "json", - "interval": "once", - "signal": 2 - }, - "custom/github": { - "format": "{}", - "return-type": "json", - "interval": 3600, - "signal": 9, - "exec": "$HOME/.config/waybar/bin/github.sh", - "on-click": "xdg-open https://github.com/notifications;pkill -RTMIN+9 waybar" - }, - "idle_inhibitor": { - "format": "{icon}", - "tooltip-format-activated": "Idle Inhibitor is active", - "tooltip-format-deactivated": "Idle Inhibitor is not active", - "format-icons": { - "activated": "󰅶", - "deactivated": "󰾪" - } - } -} diff --git a/.config/waybar/config.jsonc b/.config/waybar/config.jsonc new file mode 120000 index 0000000..1b9ff12 --- /dev/null +++ b/.config/waybar/config.jsonc @@ -0,0 +1 @@ +Boussole/config.jsonc \ No newline at end of file diff --git a/.config/waybar/style.css b/.config/waybar/style.css deleted file mode 100644 index 93653d9..0000000 --- a/.config/waybar/style.css +++ /dev/null @@ -1,305 +0,0 @@ -@import "colors.css"; -@define-color active @foreground; - -* { - font-size: 17px; - font-family: "JetBrainsMono Nerd Font Propo"; - min-width: 8px; - min-height: 0px; - border: none; - border-radius: 0; - box-shadow: none; - text-shadow: none; - padding: 0px; - -} - -window#waybar { - transition-property: background-color; - transition-duration: 0.5s; - border-radius: 4px; - border: 1px solid alpha(@active, 0.2); - background: @background; - background: alpha(@background, 0.8); - color: @foreground; -} - -menu, -tooltip { - border-radius: 2px; - padding: 2px; - border: 1px solid @active; - background: @background; - - color: @foreground; -} - -menu label, -tooltip label { - font-size: 14px; - color: @foreground; -} - -#submap, -#tray>.needs-attention { - animation-name: blink-active; - animation-duration: 1s; - animation-timing-function: linear; - animation-iteration-count: infinite; - animation-direction: alternate; -} - -.modules-right { - margin: 0px 6px 4px 6px; - border-radius: 4px; - background: alpha(@background, 0); - color: @foreground; -} - -.modules-left { - transition-property: background-color; - transition-duration: 0.5s; - margin: 6px 5px 6px 5px; /* Testing with 5 pixels on left/right */ - border-radius: 4px; - background: alpha(@background, 0.5); - color: @foreground; - border: 1px solid alpha(@active, 0.1); -} - -#gcpu, -#custom-github, -#memory, -#disk, -#together, -#submap, -#custom-weather, -#custom-recorder, -#connection, -#cnoti, -#brightness, -#power, -#custom-updates, -#tray, -/*#audio,*/ /* Duplicate with #sound */ -#sound, -#privacy { /*Controls all the right modules for some reason*/ - border-radius: 0.15em; - margin: 2px 1px 3px 1px; - background: alpha(darker(@active), 0.25); - border: 1px solid alpha(darker(@active), 0.0); -} - -/* Override specific parameters*/ - -#brightness, -#sound { - padding: 1px 0px; -} - -#custom-notifications { - padding-left: 4px; -} - -#custom-hotspot, -#custom-github, -#custom-notifications { - font-size: 14px; -} - -#custom-vpn, -#custom-hotspot { - background: alpha(darker(@active), 0.3); -} - -#privacy-item { - padding: 6px 0px 6px 6px; -} - -#gcpu { - padding: 8px 0px 8px 0px; -} - -#custom-cpu-icon { - font-size: 25px; -} - -#custom-cputemp, -#disk, -#memory, -#cpu { - font-size: 14px; - font-weight: bold; -} - -#custom-github { - padding-top: 2px; -} - -#custom-dmark { - color: alpha(@foreground, 0.3); -} - -#submap { - margin-bottom: 0px; -} - -#workspaces { - margin: 0px 2px; - padding: 2px 0px; - border-radius: 8px; -} - -#workspaces button { - transition-property: background-color; - transition-duration: 0.1s; - color: @foreground; - background: transparent; - border-radius: 4px; - color: alpha(@foreground, 0.3); - padding: 2px 0px; -} - -#workspaces button.urgent { - font-weight: bold; - color: @foreground; -} - -#workspaces button.active { - padding: 2px 0px; - background: alpha(@active, 0.4); - color: @foreground; - border-radius: 2px; -} - -#network.wifi { - margin: 2px; -} - -#network.disconnected { - margin: 2px; -} - -#network.ethernet { - margin: 2px; -} - -#submap { - min-width: 0px; - margin: 4px 6px 4px 6px; -} - -#custom-weather, -#tray { - padding: 4px 0px 4px 0px; -} - -#bluetooth { - padding-top: 2px; -} - -#group-battery { - /* border-radius: 8px; */ - /* padding: 4px 0px; */ - margin: 4px 2px 4px 2px; -} - -#battery { - font-size: 1.5rem; - border-radius: 3px; - padding: 4px 0px; - margin: 0 0px; -} - -#battery.charging { - color: @charging; -} - -#battery.discharging.warning { - background-color: #cf9022; - /*animation-name: blink-yellow; - animation-duration: 1s; - animation-timing-function: linear; - animation-iteration-count: infinite; - animation-direction: alternate; */ -} - -#battery.discharging.critical { - background-color: #c64d4f; - /*animation-name: blink-red; - animation-duration: 1s; - animation-timing-function: linear; - animation-iteration-count: infinite; - animation-direction: alternate;*/ -} - -#custom-battery_percentage { - font-size: 1.1rem; - border-radius: 100px; - /* padding: 0 2px; */ - margin: 1px 0; - /* margin: 4px 2px 4px 2px; */ -} - -#clock { - font-weight: bold; - padding: 4px 2px 2px 2px; -} - -#pulseaudio.mic { - border-radius: 4px; - color: @foreground; - padding-left: 2px; -} - -#backlight-slider slider, -#pulseaudio-slider slider { - background-color: transparent; - box-shadow: none; -} - -#backlight-slider trough, -#pulseaudio-slider trough { - margin-top: 4px; - min-width: 6px; - min-height: 60px; - border-radius: 8px; - background-color: alpha(@background, 0.6); -} - -#backlight-slider highlight, -#pulseaudio-slider highlight { - border-radius: 8px; - background-color: @foreground; -} - -#bluetooth.discoverable, -#bluetooth.discovering, -#bluetooth.pairable { - border-radius: 8px; - animation-name: blink-active; - animation-duration: 1s; - animation-timing-function: linear; - animation-iteration-count: infinite; - animation-direction: alternate; -} - -@keyframes blink-active { - to { - background-color: @active; - color: @foreground; - } -} - -@keyframes blink-red { - to { - background-color: #c64d4f; - color: @foreground; - } -} - -@keyframes blink-yellow { - to { - background-color: #cf9022; - color: @foreground; - } -} diff --git a/.config/waybar/style.css b/.config/waybar/style.css new file mode 120000 index 0000000..e7230cf --- /dev/null +++ b/.config/waybar/style.css @@ -0,0 +1 @@ +Boussole/style.css \ No newline at end of file diff --git a/.config/waybar/wifi_menu/config.ini b/.config/waybar/wifi_menu/config.ini new file mode 100644 index 0000000..fa5fcb7 --- /dev/null +++ b/.config/waybar/wifi_menu/config.ini @@ -0,0 +1,28 @@ +[dmenu] +dmenu_command = rofi -dmenu -theme ~/.config/rofi/wifi/config.rasi -i -no-history -matching fuzzy -no-tokenize -hover-select +# compact = # (Default: False). Remove extra spacing from display +# pinentry = # (Default: None) e.g. `pinentry-gtk` +pinentry = pinentry-gtk +wifi_icons = 󰤯󰤟󰤢󰤥󰤨 +# format = +format = {name} {icon} +list_saved=False +[dmenu_passphrase] +# # Uses the -password flag for Rofi, -x for bemenu. For dmenu, sets -nb and +# # -nf to the same color or uses -P if the dmenu password patch is applied +# # https://tools.suckless.org/dmenu/patches/password/ +# obscure = True +# obscure_color = #222222 + +[pinentry] +# description = (Default: Get network password) +prompt = Password: + +[editor] +terminal = kitty +# terminal = +# gui_if_available = (Default: True) +# gui = (Default: nm-connection-editor) + +[nmdm] +# rescan_delay = # (seconds to wait after a wifi rescan before redisplaying the results) diff --git a/.config/waybar/wifi_menu/networkmanager/config.ini b/.config/waybar/wifi_menu/networkmanager/config.ini new file mode 100644 index 0000000..fa5fcb7 --- /dev/null +++ b/.config/waybar/wifi_menu/networkmanager/config.ini @@ -0,0 +1,28 @@ +[dmenu] +dmenu_command = rofi -dmenu -theme ~/.config/rofi/wifi/config.rasi -i -no-history -matching fuzzy -no-tokenize -hover-select +# compact = # (Default: False). Remove extra spacing from display +# pinentry = # (Default: None) e.g. `pinentry-gtk` +pinentry = pinentry-gtk +wifi_icons = 󰤯󰤟󰤢󰤥󰤨 +# format = +format = {name} {icon} +list_saved=False +[dmenu_passphrase] +# # Uses the -password flag for Rofi, -x for bemenu. For dmenu, sets -nb and +# # -nf to the same color or uses -P if the dmenu password patch is applied +# # https://tools.suckless.org/dmenu/patches/password/ +# obscure = True +# obscure_color = #222222 + +[pinentry] +# description = (Default: Get network password) +prompt = Password: + +[editor] +terminal = kitty +# terminal = +# gui_if_available = (Default: True) +# gui = (Default: nm-connection-editor) + +[nmdm] +# rescan_delay = # (seconds to wait after a wifi rescan before redisplaying the results) diff --git a/.local/bin/rofi-wifi b/.local/bin/rofi-wifi new file mode 100755 index 0000000..4256251 --- /dev/null +++ b/.local/bin/rofi-wifi @@ -0,0 +1,1073 @@ +#!/usr/bin/env python3 +# encoding:utf8 +"""NetworkManager command line dmenu script. + +To add new connections or enable/disable networking requires policykit +permissions setup per: +https://wiki.archlinux.org/index.php/NetworkManager#Set_up_PolicyKit_permissions + +OR running the script as root + +Add dmenu options and default terminal if desired to +~/.config/networkmanager-dmenu/config.ini + +""" +import pathlib +import struct +import configparser +import locale +import os +from os.path import basename, expanduser +import shlex +from shutil import which +import sys +from time import sleep +import uuid +import subprocess + +import gi +gi.require_version('NM', '1.0') +from gi.repository import GLib, NM # noqa pylint: disable=wrong-import-position + +ENV = os.environ.copy() +ENC = locale.getpreferredencoding() + +CONF = configparser.ConfigParser() +CONF.read(expanduser("~/.config/waybar/wifi_menu/config.ini")) + + +def cli_args(): + """ Don't override dmenu_cmd function arguments with CLI args. Removes -l + and -p if those are passed on the command line. + + Exception: if -l is passed and dmenu_command is not defined, assume that the + user wants to switch dmenu to the vertical layout and include -l. + + Returns: List of additional CLI arguments + + """ + args = sys.argv[1:] + cmd = CONF.get('dmenu', 'dmenu_command', fallback=False) + if "-l" in args or "-p" in args: + for nope in ['-l', '-p'] if cmd is not False else ['-p']: + try: + nope_idx = args.index(nope) + del args[nope_idx] + del args[nope_idx] + except ValueError: + pass + return args + + +def dmenu_pass(command, color): + """Check if dmenu passphrase patch is applied and return the correct command + line arg list + + Args: command - string + color - obscure color string + Returns: list or None + + """ + if command != 'dmenu': + return None + try: + # Check for dmenu password patch + dm_patch = b'P' in subprocess.run(["dmenu", "-h"], + capture_output=True, + check=False).stderr + except FileNotFoundError: + dm_patch = False + return ["-P"] if dm_patch else ["-nb", color, "-nf", color] + + +def dmenu_cmd(num_lines, prompt="Networks", active_lines=None): + """Parse config.ini for menu options + + Args: args - num_lines: number of lines to display + prompt: prompt to show + active_lines: list of line numbers to tag as active + Returns: command invocation (as a list of strings) for example + ["dmenu", "-l", "", "-p", "", "-i"] + + """ + # Create command string + commands = {"dmenu": ["-p", str(prompt)], + "rofi": ["-dmenu", "-p", str(prompt)], + "bemenu": ["-p", str(prompt)], + "wofi": ["-p", str(prompt)], + "fuzzel": ["-p", str(prompt), "--log-level", "none"]} + command = shlex.split(CONF.get('dmenu', 'dmenu_command', fallback="dmenu")) + cmd_base = basename(command[0]) + command.extend(cli_args()) + command.extend(commands.get(cmd_base, [])) + # Highlighting + highlight = CONF.getboolean('dmenu', 'highlight', fallback=False) + if highlight is True: + # Rofi + if cmd_base == "rofi" and active_lines: + command.extend(["-a", ",".join([str(num) for num in active_lines])]) + # Wofi + if cmd_base == "wofi" and active_lines: + # add '-q' to prevent tag name and properties of pango markup from searchable + command.extend(["-m", "-q"]) + # Passphrase prompts + obscure = CONF.getboolean('dmenu_passphrase', 'obscure', fallback=False) + if prompt == "Passphrase" and obscure is True: + obscure_color = CONF.get('dmenu_passphrase', 'obscure_color', fallback='#222222') + pass_prompts = {"dmenu": dmenu_pass(cmd_base, obscure_color), + "rofi": ['-password'], + "bemenu": ['-x'], + "wofi": ['-P'], + "fuzzel": ['--password']} + command.extend(pass_prompts.get(cmd_base, [])) + return command + + +def choose_adapter(client): + """If there is more than one wifi adapter installed, ask which one to use + + """ + devices = client.get_devices() + devices = [i for i in devices if i.get_device_type() == NM.DeviceType.WIFI] + if not devices: + return None + if len(devices) == 1: + return devices[0] + device_names = "\n".join([d.get_iface() for d in devices]) + sel = subprocess.run(dmenu_cmd(len(devices), "CHOOSE ADAPTER:"), + capture_output=True, + check=False, + env=ENV, + input=device_names, + encoding=ENC).stdout + if not sel.strip(): + sys.exit() + devices = [i for i in devices if i.get_iface() == sel.strip()] + if len(devices) != 1: + raise ValueError(f"Selection was ambiguous: '{str(sel.strip())}'") + return devices[0] + + +def is_installed(cmd): + """Check if a utility is installed""" + return which(cmd) is not None + + +def is_running(cmd): + try: + subprocess.check_output(["pidof", cmd]) + except subprocess.CalledProcessError: + return False + else: + return True + + +def bluetooth_get_enabled(): + """Check if bluetooth is enabled. Try bluetoothctl first, then rfkill. + + Returns None if no bluetooth device was found. + """ + if is_installed('bluetoothctl') and is_running('bluetoothd'): + # Times out in 2 seconds, otherwise bluetoothctl will hang if bluetooth + # service isn't running. + try: + res = subprocess.run(['bluetoothctl', 'show'], + timeout=2, + capture_output=True, + text=True) + return "Powered: yes" in res.stdout + except subprocess.TimeoutExpired: + pass + # See https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-class-rfkill + for path in pathlib.Path('/sys/class/rfkill/').glob('rfkill*'): + if (path / 'type').read_text().strip() == 'bluetooth': + return (path / 'soft').read_text().strip() == '0' + return None + + +def create_other_actions(client): + """Return list of other actions that can be taken + + """ + networking_enabled = client.networking_get_enabled() + networking_action = "Disable" if networking_enabled else "Enable" + + wifi_enabled = client.wireless_get_enabled() + wifi_action = "Disable" if wifi_enabled else "Enable" + + bluetooth_enabled = bluetooth_get_enabled() + bluetooth_action = "Disable" if bluetooth_enabled else "Enable" + + actions = [Action(f"{wifi_action} Wifi", toggle_wifi, + not wifi_enabled), + Action(f"{networking_action} Networking", + toggle_networking, not networking_enabled)] + if bluetooth_enabled is not None: + actions.append(Action(f"{bluetooth_action} Bluetooth", + toggle_bluetooth, not bluetooth_enabled)) + actions += [Action("Launch Connection Manager", launch_connection_editor), + Action("Delete a Connection", delete_connection)] + if wifi_enabled: + actions.append(Action("Rescan Wifi Networks", rescan_wifi)) + actions.append(Action("Show WiFi password", show_wifi_password)) + return actions + + +def rescan_wifi(): + """ + Rescan Wifi Access Points + """ + delay = CONF.getint('nmdm', 'rescan_delay', fallback=5) + for dev in CLIENT.get_devices(): + if isinstance(dev, gi.repository.NM.DeviceWifi): + try: + dev.request_scan_async(None, rescan_cb, None) + LOOP.run() + sleep(delay) + notify("Wifi scan complete") + main() + except gi.repository.GLib.Error as err: + # Too frequent rescan error + notify("Wifi rescan failed", urgency="critical") + if not err.code == 6: # pylint: disable=no-member + raise err + + +def rescan_cb(dev, res, data): + """Callback for rescan_wifi. Just for notifications + + """ + if dev.request_scan_finish(res) is True: + notify("Wifi scan running...") + else: + notify("Wifi scan failed", urgency="critical") + LOOP.quit() + + +def ssid_to_utf8(nm_ap): + """ Convert binary ssid to utf-8 """ + ssid = nm_ap.get_ssid() + if not ssid: + return "" + ret = NM.utils_ssid_to_utf8(ssid.get_data()) + return ret + + +def prompt_saved(saved_cons): + """Prompt for a saved connection.""" + actions = create_saved_actions(saved_cons) + sel = get_selection(actions) + sel() + + +def ap_security(nm_ap): + """Parse the security flags to return a string with 'WPA2', etc. """ + flags = nm_ap.get_flags() + wpa_flags = nm_ap.get_wpa_flags() + rsn_flags = nm_ap.get_rsn_flags() + sec_str = "" + if ((flags & getattr(NM, '80211ApFlags').PRIVACY) and + (wpa_flags == 0) and (rsn_flags == 0)): + sec_str = " WEP" + if wpa_flags: + sec_str = " WPA1" + if rsn_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_PSK: + sec_str += " WPA2" + if rsn_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_SAE: + sec_str += " WPA3" + if ((wpa_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_802_1X) or + (rsn_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_802_1X)): + sec_str += " 802.1X" + if ((wpa_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_OWE) or + (rsn_flags & getattr(NM, '80211ApSecurityFlags').KEY_MGMT_OWE)): + sec_str += " OWE" + + # If there is no security use "--" + if sec_str == "": + sec_str = "--" + return sec_str.lstrip() + + +class Action(): # pylint: disable=too-few-public-methods + """Helper class to execute functions from a string variable""" + def __init__(self, + name, + func, + args=None, + active=False): + self.name = name + self.func = func + self.is_active = active + if args is None: + self.args = None + elif isinstance(args, list): + self.args = args + else: + self.args = [args] + + def __str__(self): + return self.name + + def __call__(self): + if self.args is None: + self.func() + else: + self.func(*self.args) + + +def conn_matches_adapter(conn, adapter): + """Return True if the connection is applicable for the given adapter. + + There seem to be two ways for a connection specify what interface it belongs + to: + + - By setting 'mac-address' in [wifi] to the adapter's MAC + - By setting 'interface-name` in [connection] to the adapter's name. + + Depending on how the connection was added, it seems like either + 'mac-address', 'interface-name' or neither of both is set. + """ + # [wifi] mac-address + setting_wireless = conn.get_setting_wireless() + mac = setting_wireless.get_mac_address() + if mac is not None: + return mac == adapter.get_permanent_hw_address() + + # [connection] interface-name + setting_connection = conn.get_setting_connection() + interface = setting_connection.get_interface_name() + if interface is not None: + return interface == adapter.get_iface() + + # Neither is set, let's assume this connection is for multiple/all adapters. + return True + + +def process_ap(nm_ap, is_active, adapter): + """Activate/Deactivate a connection and get password if required""" + if is_active: + CLIENT.deactivate_connection_async(nm_ap, None, deactivate_cb, nm_ap) + LOOP.run() + else: + conns_cur = [i for i in CONNS if + i.get_setting_wireless() is not None and + conn_matches_adapter(i, adapter)] + con = nm_ap.filter_connections(conns_cur) + if len(con) > 1: + raise ValueError("There are multiple connections possible") + + if len(con) == 1: + CLIENT.activate_connection_async(con[0], adapter, nm_ap.get_path(), + None, activate_cb, nm_ap) + LOOP.run() + else: + if ap_security(nm_ap) != "--": + password = get_passphrase() + else: + password = "" + set_new_connection(nm_ap, password, adapter) + + +def activate_cb(dev, res, data): + """Notification if activate connection completed successfully + + """ + try: + conn = dev.activate_connection_finish(res) + except GLib.Error: + conn = None + if conn is not None: + notify(f"Activated {conn.get_id()}") + else: + notify(f"Problem activating {data.get_id()}", urgency="critical") + LOOP.quit() + + +def deactivate_cb(dev, res, data): + """Notification if deactivate connection completed successfully + + """ + if dev.deactivate_connection_finish(res) is True: + notify(f"Deactivated {data.get_id()}") + else: + notify(f"Problem deactivating {data.get_id()}", urgency="critical") + LOOP.quit() + + +def process_vpngsm(con, activate): + """Activate/deactive VPN or GSM connections""" + if activate: + CLIENT.activate_connection_async(con, None, None, + None, activate_cb, con) + else: + CLIENT.deactivate_connection_async(con, None, deactivate_cb, con) + LOOP.run() + +def strength_bars(signal_strength): + bars = NM.utils_wifi_strength_bars(signal_strength) + wifi_chars = CONF.get("dmenu", "wifi_chars", fallback=False) + if wifi_chars: + bars = "".join([wifi_chars[i] for i, j in enumerate(bars) if j == '*']) + return bars + + +def strength_icon(signal_strength): + wifi_icons = CONF.get("dmenu", "wifi_icons", fallback=False) + if wifi_icons: + return wifi_icons[round(signal_strength / 100 * (len(wifi_icons) - 1))] + return "" + + +def create_ap_actions(aps, active_ap, active_connection, adapter): # noqa pylint: disable=too-many-locals,line-too-long + """For each AP in a list, create the string and its attached function + (activate/deactivate) + + """ + active_ap_bssid = active_ap.get_bssid() if active_ap is not None else "" + + names = [ssid_to_utf8(ap) for ap in aps] + max_len_name = max([len(name) for name in names]) if names else 0 + secs = [ap_security(ap) for ap in aps] + max_len_sec = max([len(sec) for sec in secs]) if secs else 0 + + ap_actions = [] + + if CONF.getboolean("dmenu", "compact", fallback=False): + format = CONF.get("dmenu", "format", fallback="{name} {sec} {bars}") + else: + format = CONF.get("dmenu", "format", fallback="{name:<{max_len_name}s} {sec:<{max_len_sec}s} {bars:>4}") + + for nm_ap, name, sec in zip(aps, names, secs): + is_active = nm_ap.get_bssid() == active_ap_bssid + signal_strength = nm_ap.get_strength() + bars = strength_bars(signal_strength) + icon = strength_icon(signal_strength) + action_name = format.format(name=name, sec=sec, signal=signal_strength, bars=bars, icon=icon, + max_len_name=max_len_name, max_len_sec=max_len_sec) + if is_active: + ap_actions.append(Action(action_name, process_ap, + [active_connection, True, adapter], + active=True)) + else: + ap_actions.append(Action(action_name, process_ap, + [nm_ap, False, adapter])) + return ap_actions + + +def create_hotspot_actions(hotspots, active): + """Create the list of strings to display with associated function + (activate/deactivate) for NetworkManager Hotspots. + + """ + active_hotspot = [i for i in active if i.get_connection_type() == "802-11-wireless" + and i.get_connection().get_setting_wireless().get_mode() == "ap"] + return _create_vpngsm_actions(hotspots, active_hotspot, "Hotspot") + + +def create_vpn_actions(vpns, active): + """Create the list of strings to display with associated function + (activate/deactivate) for VPN connections. + + """ + active_vpns = [i for i in active if i.get_vpn()] + return _create_vpngsm_actions(vpns, active_vpns, "VPN") + + +def create_vlan_actions(vlans, active): + """Create the list of strings to display with associated function + (activate/deactivate) for VLAN connections. + + """ + active_vlans = [i for i in active if "vlan" == i.get_connection_type()] + return _create_vpngsm_actions(vlans, active_vlans, "VLAN") + + +def create_wireguard_actions(wgs, active): + """Create the list of strings to display with associated function + (activate/deactivate) for Wireguard connections. + + """ + active_wgs = [i for i in active if i.get_connection_type() == "wireguard"] + return _create_vpngsm_actions(wgs, active_wgs, "Wireguard") + + +def create_eth_actions(eths, active): + """Create the list of strings to display with associated function + (activate/deactivate) for Ethernet connections. + + """ + active_eths = [i for i in active if 'ethernet' in i.get_connection_type()] + return _create_vpngsm_actions(eths, active_eths, "Eth") + + +def create_gsm_actions(gsms, active): + """Create the list of strings to display with associated function + (activate/deactivate) GSM connections.""" + active_gsms = [i for i in active if + i.get_connection() is not None and + i.get_connection().is_type(NM.SETTING_GSM_SETTING_NAME)] + return _create_vpngsm_actions(gsms, active_gsms, "GSM") + + +def create_blue_actions(blues, active): + """Create the list of strings to display with associated function + (activate/deactivate) Bluetooth connections.""" + active_blues = [i for i in active if + i.get_connection() is not None and + i.get_connection().is_type(NM.SETTING_BLUETOOTH_SETTING_NAME)] + return _create_vpngsm_actions(blues, active_blues, "Bluetooth") + + +def create_saved_actions(saved): + """Create the list of strings to display with associated function + (activate/deactivate) for VPN connections. + + """ + return _create_vpngsm_actions(saved, [], "SAVED") + + +def _create_vpngsm_actions(cons, active_cons, label): + active_con_ids = [a.get_id() for a in active_cons] + actions = [] + for con in cons: + is_active = con.get_id() in active_con_ids + try: + # Get hotspot ssid + if con.get_setting_wireless().get_mode() == 'ap': + hotspot = f" '{ssid_to_utf8(con.get_setting_wireless())}'" + else: + hotspot = "" + except AttributeError: + hotspot = "" + action_name = f"{con.get_id()}{hotspot}:{label}" + if is_active: + active_connection = [a for a in active_cons + if a.get_id() == con.get_id()] + if len(active_connection) != 1: + raise ValueError(f"Multiple active connections match {con.get_id()}") + active_connection = active_connection[0] + + actions.append(Action(action_name, process_vpngsm, + [active_connection, False], active=True)) + else: + actions.append(Action(action_name, process_vpngsm, + [con, True])) + return actions + + +def create_wwan_actions(client): + """Create WWWAN actions + + """ + wwan_enabled = client.wwan_get_enabled() + wwan_action = "Disable" if wwan_enabled else "Enable" + return [Action(f"{wwan_action} WWAN", toggle_wwan, not wwan_enabled)] + + +def combine_actions(eths, aps, vlans, vpns, wgs, gsms, blues, wwan, hotspots, others, saved): + # pylint: disable=too-many-arguments + """Combine all given actions into a list of actions. + + Args: args - eths: list of Actions + aps: list of Actions + vpns: list of Actions + gsms: list of Actions + blues: list of Actions + wwan: list of Actions + hotspots: list of Actions + others: list of Actions + """ + compact = CONF.getboolean("dmenu", "compact", fallback=False) + empty_action = [Action('', None)] if not compact else [] + all_actions = [] + all_actions += eths + empty_action if eths else [] + all_actions += aps + empty_action if aps else [] + all_actions += vlans + empty_action if vlans else [] + all_actions += vpns + empty_action if vpns else [] + all_actions += wgs + empty_action if wgs else [] + all_actions += gsms + empty_action if (gsms and wwan) else [] + all_actions += blues + empty_action if blues else [] + all_actions += wwan + empty_action if wwan else [] + all_actions += hotspots + empty_action if hotspots else [] + all_actions += others + empty_action if others else [] + all_actions += saved + empty_action if saved else [] + return all_actions + + +def get_wofi_highlight_markup(action): + highlight_fg = CONF.get('dmenu', 'highlight_fg', fallback=None) + highlight_bg = CONF.get('dmenu', 'highlight_bg', fallback=None) + highlight_bold = CONF.getboolean('dmenu', 'highlight_bold', fallback=True) + + style = "" + if highlight_fg: + style += f'foreground="{highlight_fg}" ' + if highlight_bg: + style += f'background="{highlight_bg}" ' + if highlight_bold: + style += 'weight="bold" ' + + return f"" + str(action) + "" + + +def get_selection(all_actions): + """Spawn dmenu for selection and execute the associated action.""" + command = shlex.split(CONF.get("dmenu", "dmenu_command", fallback="dmenu")) + cmd_base = basename(command[0]) + active_chars = CONF.get("dmenu", "active_chars", fallback="==") + highlight = CONF.getboolean("dmenu", "highlight", fallback=False) + inp = [] + + if highlight is True and cmd_base == "rofi": + inp = [str(action) for action in all_actions] + elif highlight is True and cmd_base == "wofi": + inp = [get_wofi_highlight_markup(action) if action.is_active else str(action) + for action in all_actions] + else: + inp = [(active_chars if action.is_active else " " * len(active_chars)) + " " + str(action) + for action in all_actions] + active_lines = [index for index, action in enumerate(all_actions) + if action.is_active] + + command = dmenu_cmd(len(inp), active_lines=active_lines) + sel = subprocess.run(command, + capture_output=True, + check=False, + input="\n".join(inp), + encoding=ENC, + env=ENV).stdout + + if not sel.rstrip(): + sys.exit() + + if highlight is True and cmd_base == "rofi": + action = [i for i in all_actions if str(i).strip() == sel.strip()] + elif highlight is True and cmd_base == "wofi": + action = [i for i in all_actions + if str(i).strip() == sel.strip() or + get_wofi_highlight_markup(i) == sel.strip()] + else: + action = [i for i in all_actions + if ((str(i).strip() == str(sel.strip()) + and not i.is_active) or + (active_chars + " " + str(i) == str(sel.rstrip('\n')) + and i.is_active))] + if len(action) != 1: + raise ValueError(f"Selection was ambiguous: '{str(sel.strip())}'") + return action[0] + + +def toggle_networking(enable): + """Enable/disable networking + + Args: enable - boolean + + """ + toggle = GLib.Variant.new_tuple(GLib.Variant.new_boolean(enable)) + try: + CLIENT.dbus_call(NM.DBUS_PATH, NM.DBUS_INTERFACE, "Enable", toggle, + None, -1, None, None, None) + except AttributeError: + # Workaround for older versions of python-gobject + CLIENT.networking_set_enabled(enable) + notify(f"Networking {'enabled' if enable is True else 'disabled'}") + + +def toggle_wifi(enable): + """Enable/disable Wifi + + Args: enable - boolean + + """ + toggle = GLib.Variant.new_boolean(enable) + try: + CLIENT.dbus_set_property(NM.DBUS_PATH, NM.DBUS_INTERFACE, "WirelessEnabled", toggle, + -1, None, None, None) + except AttributeError: + # Workaround for older versions of python-gobject + CLIENT.wireless_set_enabled(enable) + notify(f"Wifi {'enabled' if enable is True else 'disabled'}") + + +def toggle_wwan(enable): + """Enable/disable WWAN + + Args: enable - boolean + + """ + toggle = GLib.Variant.new_boolean(enable) + try: + CLIENT.dbus_set_property(NM.DBUS_PATH, NM.DBUS_INTERFACE, "WwanEnabled", toggle, + -1, None, None, None) + except AttributeError: + # Workaround for older versions of python-gobject + CLIENT.wwan_set_enabled(enable) + notify(f"Wwan {'enabled' if enable is True else 'disabled'}") + + +def toggle_bluetooth(enable): + """Enable/disable Bluetooth + + Try bluetoothctl first, then drop to rfkill if it's not installed or + bluetooth service isn't running. + + Args: enable - boolean + + References: + https://github.com/blueman-project/blueman/blob/master/blueman/plugins/mechanism/RfKill.py + https://www.kernel.org/doc/html/latest/driver-api/rfkill.html + https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/uapi/linux/rfkill.h?h=v5.8.9 + + """ + if is_installed('bluetoothctl') and is_running('bluetoothd'): + # Times out in 2 seconds, otherwise bluetoothctl will hang if bluetooth + # service isn't running. + try: + res = subprocess.run(['bluetoothctl', 'power', 'on' if enable is True else 'off'], + timeout=2, + capture_output=True) + except subprocess.TimeoutExpired: + pass + try: + res = subprocess.run(['bluetoothctl', 'show'], + timeout=2, + capture_output=True, + text=True) + if "Powered: yes" in res.stdout: + notify("Bluetooth enabled") + return + except subprocess.TimeoutExpired: + pass + # Now try using rfkill + type_bluetooth = 2 + op_change_all = 3 + idx = 0 + soft_state = 0 if enable else 1 + hard_state = 0 + + data = struct.pack("IBBBB", idx, type_bluetooth, op_change_all, + soft_state, hard_state) + + try: + with open('/dev/rfkill', 'r+b', buffering=0) as rff: + rff.write(data) + except PermissionError: + notify("Lacking permission to write to /dev/rfkill.", + "Check README for configuration options.", + urgency="critical") + else: + notify(f"Bluetooth {'enabled' if enable else 'disabled'}") + + +def launch_connection_editor(): + """Launch nmtui or the gui nm-connection-editor + + """ + terminal = shlex.split(CONF.get("editor", "terminal", fallback="xterm")) + gui_if_available = CONF.getboolean("editor", "gui_if_available", fallback=True) + gui = CONF.get("editor", "gui", fallback="nm-connection-editor") + if gui_if_available is True: + if is_installed(gui): + subprocess.run(gui, check=False) + return + if is_installed("nmtui"): + subprocess.run(terminal + ["-e", "nmtui"], check=False) + return + notify("No network connection editor installed", urgency="critical") + + +def show_wifi_password(): + """Run `nmcli device wifi show-password` for current connection + + """ + terminal = CONF.get("editor", "terminal", fallback="xterm") + subprocess.run([terminal, "-e", "nmcli", "device", "wifi", "show-password;", + "exec", "bash"], check=False) + + +def get_passphrase(): + """Get a password + + Returns: string + + """ + pinentry = CONF.get("dmenu", "pinentry", fallback=None) + if pinentry: + description = CONF.get("pinentry", "description", fallback="Get network password") + prompt = CONF.get("pinentry", "prompt", fallback="Password: ") + pin = "" + out = subprocess.run(pinentry, + capture_output=True, + check=False, + encoding=ENC, + input=f"setdesc {description}\nsetprompt {prompt}\ngetpin\n").stdout + if out: + res = [i for i in out.split("\n") if i.startswith("D ")] + if res and res[0].startswith("D "): + pin = res[0].split("D ")[1] + return pin + return subprocess.run(dmenu_cmd(0, "Passphrase"), + stdin=subprocess.DEVNULL, + capture_output=True, + check=False, + encoding=ENC).stdout + + +def delete_connection(): + """Display list of NM connections and delete the selected one + + """ + conn_acts = [Action(i.get_id(), i.delete_async, args=[None, delete_cb, None]) for i in CONNS] + conn_names = "\n".join([str(i) for i in conn_acts]) + sel = subprocess.run(dmenu_cmd(len(conn_acts), "CHOOSE CONNECTION TO DELETE:"), + capture_output=True, + check=False, + input=conn_names, + encoding=ENC, + env=ENV).stdout + if not sel.strip(): + sys.exit() + action = [i for i in conn_acts if str(i) == sel.rstrip("\n")] + if len(action) != 1: + raise ValueError(f"Selection was ambiguous: {str(sel)}") + action[0]() + LOOP.run() + + +def delete_cb(dev, res, data): + """Notification if delete completed successfully + + """ + if dev.delete_finish(res) is True: + notify(f"Deleted {dev.get_id()}") + else: + notify(f"Problem deleting {dev.get_id()}", urgency="critical") + LOOP.quit() + + +def set_new_connection(nm_ap, nm_pw, adapter): + """Setup a new NetworkManager connection + + Args: ap - NM.AccessPoint + pw - string + + """ + nm_pw = str(nm_pw).strip() + profile = create_wifi_profile(nm_ap, nm_pw, adapter) + CLIENT.add_and_activate_connection_async(profile, adapter, nm_ap.get_path(), + None, verify_conn, profile) + LOOP.run() + + +def create_wifi_profile(nm_ap, password, adapter): + # pylint: disable=line-too-long + # noqa From https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/examples/python/gi/add_connection.py + # noqa and https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/examples/python/dbus/add-wifi-psk-connection.py + # pylint: enable=line-too-long + """Create the NM profile given the AP and passphrase""" + ap_sec = ap_security(nm_ap) + profile = NM.SimpleConnection.new() + + s_con = NM.SettingConnection.new() + s_con.set_property(NM.SETTING_CONNECTION_ID, ssid_to_utf8(nm_ap)) + s_con.set_property(NM.SETTING_CONNECTION_UUID, str(uuid.uuid4())) + s_con.set_property(NM.SETTING_CONNECTION_TYPE, "802-11-wireless") + profile.add_setting(s_con) + + s_wifi = NM.SettingWireless.new() + s_wifi.set_property(NM.SETTING_WIRELESS_SSID, nm_ap.get_ssid()) + s_wifi.set_property(NM.SETTING_WIRELESS_MODE, 'infrastructure') + s_wifi.set_property(NM.SETTING_WIRELESS_MAC_ADDRESS, adapter.get_permanent_hw_address()) + profile.add_setting(s_wifi) + + s_ip4 = NM.SettingIP4Config.new() + s_ip4.set_property(NM.SETTING_IP_CONFIG_METHOD, "auto") + profile.add_setting(s_ip4) + + s_ip6 = NM.SettingIP6Config.new() + s_ip6.set_property(NM.SETTING_IP_CONFIG_METHOD, "auto") + profile.add_setting(s_ip6) + + if ap_sec != "--": + s_wifi_sec = NM.SettingWirelessSecurity.new() + if "WPA" in ap_sec: + if "WPA3" in ap_sec: + s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_KEY_MGMT, + "sae") + else: + s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-psk") + s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_AUTH_ALG, + "open") + s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_PSK, password) + elif "WEP" in ap_sec: + s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_KEY_MGMT, + "None") + s_wifi_sec.set_property(NM.SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, + NM.WepKeyType.PASSPHRASE) + s_wifi_sec.set_wep_key(0, password) + profile.add_setting(s_wifi_sec) + + return profile + + +def verify_conn(client, result, data): + """Callback function for add_and_activate_connection_async + + Check if connection completes successfully. Delete the connection if there + is an error. + + """ + try: + act_conn = client.add_and_activate_connection_finish(result) + conn = act_conn.get_connection() + if not all([conn.verify(), + conn.verify_secrets(), + data.verify(), + data.verify_secrets()]): + raise GLib.Error + notify(f"Added {conn.get_id()}") + except GLib.Error: + try: + notify(f"Connection to {conn.get_id()} failed", + urgency="critical") + conn.delete_async(None, None, None) + except UnboundLocalError: + pass + finally: + LOOP.quit() + + +def create_ap_list(adapter, active_connections): + """Generate list of access points. Remove duplicate APs and hotspots, + keeping strongest ones and the active AP + + Args: adapter + active_connections - list of all active connections + Returns: aps - list of access points + active_ap - active AP + active_ap_con - active Connection + adapter + + """ + aps = [] + ap_names = [] + active_ap = adapter.get_active_access_point() + aps_all = sorted(adapter.get_access_points(), + key=lambda a: a.get_strength(), reverse=True) + if adapter.get_mode() == getattr(NM, "80211Mode").AP: + # Remove active hotspot from AP list + aps_all.remove(active_ap) + active_ap = None + conns_cur = [i for i in CONNS if + i.get_setting_wireless() is not None and + i.get_setting_wireless().get_mode() != "ap" and # filter out hotspots + conn_matches_adapter(i, adapter)] + try: + ap_conns = active_ap.filter_connections(conns_cur) + active_ap_name = ssid_to_utf8(active_ap) + active_ap_con = [active_conn for active_conn in active_connections + if active_conn.get_connection() in ap_conns] + except AttributeError: + active_ap_name = None + active_ap_con = [] + if len(active_ap_con) > 1: + raise ValueError("Multiple connection profiles match" + " the wireless AP") + active_ap_con = active_ap_con[0] if active_ap_con else None + for nm_ap in aps_all: + ap_name = ssid_to_utf8(nm_ap) + if nm_ap != active_ap and ap_name == active_ap_name: + # Skip adding AP if it's not active but same name as active AP + continue + if ap_name not in ap_names: + ap_names.append(ap_name) + aps.append(nm_ap) + return aps, active_ap, active_ap_con, adapter + + +def notify(message, details=None, urgency="low"): + """Use notify-send if available for notifications + + """ + delay = CONF.getint('nmdm', 'rescan_delay', fallback=5) + args = ["-u", urgency, "-a", "networkmanager-dmenu", + "-t", str(delay * 1000), message] + if details is not None: + args.append(details) + if is_installed("notify-send"): + subprocess.run(["notify-send"] + args, check=False) + + +def run(): # pylint: disable=too-many-locals + """Main script entrypoint""" + try: + subprocess.check_output(["pidof", "NetworkManager"]) + except subprocess.CalledProcessError: + notify("WARNING: NetworkManager don't seems to be running") + print("WARNING: NetworkManager don't seems to be running") + active = CLIENT.get_active_connections() + adapter = choose_adapter(CLIENT) + if adapter: + ap_actions = create_ap_actions(*create_ap_list(adapter, active)) + else: + ap_actions = [] + + vpns = [i for i in CONNS if i.is_type(NM.SETTING_VPN_SETTING_NAME)] + try: + wgs = [i for i in CONNS if i.is_type(NM.SETTING_WIREGUARD_SETTING_NAME)] + except AttributeError: + # Workaround for older versions of python-gobject with no wireguard support + wgs = [] + hotspots = [i for i in CONNS if i.get_setting_wireless() and + i.get_setting_wireless().get_mode() == "ap"] + eths = [i for i in CONNS if i.is_type(NM.SETTING_WIRED_SETTING_NAME)] + vlans = [i for i in CONNS if i.is_type(NM.SETTING_VLAN_SETTING_NAME)] + blues = [i for i in CONNS if i.is_type(NM.SETTING_BLUETOOTH_SETTING_NAME)] + + vpn_actions = create_vpn_actions(vpns, active) + wg_actions = create_wireguard_actions(wgs, active) + eth_actions = create_eth_actions(eths, active) + vlan_actions = create_vlan_actions(vlans, active) + blue_actions = create_blue_actions(blues, active) + hotspot_actions = create_hotspot_actions(hotspots, active) + other_actions = create_other_actions(CLIENT) + wwan_installed = is_installed("ModemManager") + if wwan_installed: + gsms = [i for i in CONNS if i.is_type(NM.SETTING_GSM_SETTING_NAME)] + gsm_actions = create_gsm_actions(gsms, active) + wwan_actions = create_wwan_actions(CLIENT) + else: + gsm_actions = [] + wwan_actions = [] + + list_saved = CONF.getboolean('dmenu', 'list_saved', fallback=False) + saved_cons = [i for i in CONNS if i not in vpns + wgs + eths + blues] + if list_saved: + saved_actions = create_saved_actions(saved_cons) + else: + saved_actions = [Action("Saved connections", prompt_saved, [saved_cons])] + + + actions = combine_actions(eth_actions, ap_actions, vlan_actions, vpn_actions, + wg_actions, gsm_actions, blue_actions, wwan_actions, + hotspot_actions, other_actions, saved_actions) + sel = get_selection(actions) + sel() + + +def main(): + """Main. Enables script to be re-run after a wifi rescan + + """ + global CLIENT, CONNS, LOOP # noqa pylint: disable=global-variable-undefined + CLIENT = NM.Client.new(None) + LOOP = GLib.MainLoop() + CONNS = CLIENT.get_connections() + run() + + +if __name__ == '__main__': + main() + +# vim: set et ts=4 sw=4 : diff --git a/.local/share/rofi/themes/boussole.rasi b/.local/share/rofi/themes/boussole.rasi new file mode 100644 index 0000000..1b12a3a --- /dev/null +++ b/.local/share/rofi/themes/boussole.rasi @@ -0,0 +1,114 @@ +/* Boussole theme by Gu://em_*/ +/* Based on work done by */ +/* Newman Sanchez (https://github.com/newmanls) */ + +configuration { + + modi: "drun,run"; + display-drun: "Applications"; + display-run: "Run"; + drun-display-format: "{icon} {name}"; + sort: true; + sorting-method: "fzf"; + + /* Icons */ + show-icons: true; + /* icon-theme: "Gruvbox-Plus-Dark"; */ +} + +* { + /*font: "FiraCode Nerd Font Medium 12";*/ + font: "JetBrainsMono Nerd Font Propo Regular 12"; + + bg0: #0d1117; + bg1: #1a2330; + fg0: #eeffff; + + accent-color: #e64e4e; + urgent-color: #ffcb6b; + + background-color: transparent; + text-color: @fg0; + + margin: 0; + padding: 0; + spacing: 0; +} + +window { + location: center; + width: 600; + + background-color: @bg0; + + /*border-radius: 4px;*/ +} + +inputbar { + spacing: 8px; + padding: 8px; + + background-color: @bg1; +} + +prompt, entry, element-icon, element-text { + vertical-align: 0.5; +} + +prompt { + text-color: @accent-color; +} + +textbox { + padding: 8px; + background-color: @bg1; +} + +listview { + padding: 4px 0; + lines: 8; + columns: 1; + + fixed-height: false; +} + +element { + padding: 8px; + spacing: 8px; +} + +element normal normal { + text-color: @fg0; +} + +element normal urgent { + text-color: @urgent-color; +} + +element normal active { + text-color: @accent-color; +} + +element alternate active { + text-color: @accent-color; +} + +element selected { + text-color: @bg0; +} + +element selected normal, element selected active { + background-color: @accent-color; +} + +element selected urgent { + background-color: @urgent-color; +} + +element-icon { + size: 0.8em; +} + +element-text { + text-color: inherit; +} diff --git a/.zshrc b/.zshrc index b838f64..805c5d8 100644 --- a/.zshrc +++ b/.zshrc @@ -89,6 +89,8 @@ alias hx='helix' alias cg='cargo' alias rqr='rust-quick-run.sh' alias vm='quickemu --vm *.conf' +alias paclist="pacman -Qq | fzf --preview 'pacman -Qil {}' --layout=reverse --bind 'enter:execute(pacman -Qil {} | less)' +" # Power management alias pprof='cat /sys/firmware/acpi/platform_profile' # Get current performance profile alias pprof-list='cat /sys/firmware/acpi/platform_profile_choices' # Lists the available performance profiles