Hyprland config

This commit is contained in:
2024-10-23 11:53:39 +02:00
parent bf80b5054a
commit db55bcedb8
91 changed files with 6543 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## launch alacritty with alt config
CONFIG="$HOME/.config/hypr/alacritty/alacritty.toml"
if [ "$1" == "-f" ]; then
shift 1
alacritty --class 'alacritty-float,alacritty-float' --config-file "$CONFIG" $@
elif [ "$1" == "-F" ]; then
shift 1
alacritty --class 'alacritty-fullscreen,alacritty-fullscreen' --config-file "$CONFIG" \
-o window.startup_mode="'Fullscreen'" \
window.padding.x=30 window.padding.y=30 \
window.opacity=0.95 font.size=14 $@
else
alacritty --config-file "$CONFIG" $@
fi
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
# hypr directory
DIR="$HOME/.config/hypr"
# rofi sudo askpass helper
export SUDO_ASKPASS="$DIR"/scripts/rofi_askpass
# execute the application
sudo -E -A $1
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## Script To Manage Brightness For Archcraft (in Wayland).
iDIR="$HOME/.config/hypr/mako/icons"
# Get brightness
get_backlight() {
LIGHT=$(printf "%.0f\n" `light -G`)
echo "${LIGHT}%"
}
# Get icons
get_icon() {
backlight="$(get_backlight)"
current="${backlight%%%}"
if [[ ("$current" -ge "0") && ("$current" -le "20") ]]; then
icon="$iDIR"/brightness-20.png
elif [[ ("$current" -ge "20") && ("$current" -le "40") ]]; then
icon="$iDIR"/brightness-40.png
elif [[ ("$current" -ge "40") && ("$current" -le "60") ]]; then
icon="$iDIR"/brightness-60.png
elif [[ ("$current" -ge "60") && ("$current" -le "80") ]]; then
icon="$iDIR"/brightness-80.png
elif [[ ("$current" -ge "80") && ("$current" -le "100") ]]; then
icon="$iDIR"/brightness-100.png
fi
}
# Notify
notify_user() {
notify-send -h string:x-canonical-private-synchronous:sys-notify-backlight -u low -i "$icon" "Brightness : $(get_backlight)"
}
# Increase brightness
inc_backlight() {
light -A 5 && get_icon && notify_user
}
# Decrease brightness
dec_backlight() {
light -U 5 && get_icon && notify_user
}
# Execute accordingly
if [[ "$1" == "--get" ]]; then
get_backlight
elif [[ "$1" == "--inc" ]]; then
inc_backlight
elif [[ "$1" == "--dec" ]]; then
dec_backlight
else
get_backlight
fi
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## Simple Script To Pick Color Quickly.
#color=$(grim -g "`slurp -b 00000000 -p`" -t ppm - | convert - -format '%[pixel:p{0,0}]' txt:- | tail -n1 | cut -d' ' -f4)
color=`$HOME/.config/hypr/scripts/hyprpicker`
image=/tmp/${color}.png
main() {
if [[ "$color" ]]; then
# copy color code to clipboard
echo $color | tr -d "\n" | wl-copy
# generate preview
convert -size 48x48 xc:"$color" ${image}
# notify about it
notify-send -h string:x-canonical-private-synchronous:sys-notify-picker -u low -i ${image} "$color, copied to clipboard."
fi
}
# Run the script
main
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## launch terminal with alt config
# Get Colors
DIR="$HOME/.config/hypr"
background="`cat $DIR/rofi/shared/colors.rasi | grep 'background:' | cut -d':' -f2 | tr -d ' '\;`"
accent="`cat $DIR/rofi/shared/colors.rasi | grep 'selected:' | cut -d':' -f2 | tr -d ' '\;`"
CONFIG="$HOME/.config/hypr/foot/foot.ini"
if [ "$1" == "-f" ]; then
foot --app-id='foot-float' --config="$CONFIG"
elif [ "$1" == "-F" ]; then
foot --fullscreen --app-id='foot-full' --font="Iosevka Nerd Font:size=14" --override=pad=35x35 --config="$CONFIG"
elif [ "$1" == "-s" ]; then
foot --app-id='foot-float' --config="$CONFIG" \
--window-size-pixels=$(slurp -b ${background:1}CC -c ${accent:1}ff -s ${accent:1}0D -w 2 -f "%wx%h")
else
foot --config="$CONFIG"
fi
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## Set GTK Themes, Icons, Cursor and Fonts
THEME='Manhattan'
ICONS='Luv-Folders-Dark'
FONT='Noto Sans 9'
CURSOR='Qogirr-Dark'
SCHEMA='gsettings set org.gnome.desktop.interface'
apply_themes () {
${SCHEMA} gtk-theme "$THEME"
${SCHEMA} icon-theme "$ICONS"
${SCHEMA} cursor-theme "$CURSOR"
${SCHEMA} font-name "$FONT"
}
apply_themes
BIN
View File
Binary file not shown.
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
# Source Theme file
DIR="$HOME/.config/hypr"
source "$DIR"/theme/current.bash
# Colors
bg=${background:1} fg=${foreground:1}
red=${color1:1} green=${color2:1} yellow=${color3:1}
blue=${color4:1} magenta=${color5:1} cyan=${color6:1}
alpha='00000000'
# CMD
swaylock -f \
\
`# General` \
--ignore-empty-password \
--show-failed-attempts \
--hide-keyboard-layout \
--indicator-caps-lock \
\
`# Appearance` \
--color ${bg}E6 \
--font 'JetBrainsMono Nerd Font' --font-size 18 \
\
`# Backspace Key` \
--key-hl-color ${green} \
--caps-lock-key-hl-color ${blue} \
--bs-hl-color ${red} \
--caps-lock-bs-hl-color ${red} \
\
`# Indicator` \
--indicator-radius 120 \
--indicator-thickness 10 \
\
`# Inside Circle Colors` \
--inside-color ${alpha} \
--inside-clear-color ${alpha} \
--inside-caps-lock-color ${alpha} \
--inside-ver-color ${blue} \
--inside-wrong-color ${red} \
\
`# Layout Colors` \
--layout-bg-color ${cyan} \
--layout-border-color ${cyan} \
--layout-text-color ${bg} \
\
`# Line Colors` \
--line-color ${bg} \
--line-clear-color ${red} \
--line-caps-lock-color ${bg} \
--line-ver-color ${bg} \
--line-wrong-color ${bg} \
\
`# Ring Colors` \
--ring-color ${cyan} \
--ring-clear-color ${bg} \
--ring-caps-lock-color ${magenta} \
--ring-ver-color ${blue} \
--ring-wrong-color ${red} \
\
`# Separator Color` \
--separator-color ${bg} \
\
`# Text Colors` \
--text-color ${fg} \
--text-clear-color ${fg} \
--text-caps-lock-color ${fg} \
--text-ver-color ${bg} \
--text-wrong-color ${bg}
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## launch mako with alt config
CONFIG="$HOME/.config/hypr/mako/config"
if [[ ! `pidof mako` ]]; then
mako --config ${CONFIG}
fi
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# Import Current Theme
DIR="$HOME/.config/hypr"
RASI="$DIR/rofi/askpass.rasi"
# Rofi text dialog to get password
rofi -dmenu \
-password \
-i \
-p "Root" \
-theme ${RASI}
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# Import Current Theme
DIR="$HOME/.config/hypr"
RASI="$DIR/rofi/asroot.rasi"
ASROOT="$DIR/scripts/asroot"
USER=`whoami`
# Theme Elements
prompt='Root'
mesg="Run Applications As Root"
term="alacritty --class alacritty-float,alacritty-float --config-file /home/$USER/.config/hypr/alacritty/alacritty.toml"
# Options
layout=`cat ${RASI} | grep 'USE_ICON' | cut -d'=' -f2`
if [[ "$layout" == 'NO' ]]; then
option_1=" Alacritty"
option_2=" Thunar"
option_3=" Geany"
option_4=" Ranger"
option_5=" Vim"
else
option_1=""
option_2=""
option_3=""
option_4=""
option_5=""
fi
# Rofi CMD
rofi_cmd() {
rofi -dmenu \
-p "$prompt" \
-mesg "$mesg" \
-markup-rows \
-theme ${RASI}
}
# Pass variables to rofi dmenu
run_rofi() {
echo -e "$option_1\n$option_2\n$option_3\n$option_4\n$option_5" | rofi_cmd
}
# Execute Command
run_cmd() {
if [[ "$1" == '--opt1' ]]; then
${ASROOT} "$term"
elif [[ "$1" == '--opt2' ]]; then
${ASROOT} 'dbus-run-session thunar'
elif [[ "$1" == '--opt3' ]]; then
${ASROOT} geany
elif [[ "$1" == '--opt4' ]]; then
${ASROOT} "$term -e ranger"
elif [[ "$1" == '--opt5' ]]; then
${ASROOT} "$term -e vim"
fi
}
# Actions
chosen="$(run_rofi)"
case ${chosen} in
$option_1)
run_cmd --opt1
;;
$option_2)
run_cmd --opt2
;;
$option_3)
run_cmd --opt3
;;
$option_4)
run_cmd --opt4
;;
$option_5)
run_cmd --opt5
;;
esac
+321
View File
@@ -0,0 +1,321 @@
#!/usr/bin/env bash
# __ _ _ _ _ _ _
# _ __ ___ / _(_) | |__ | |_ _ ___| |_ ___ ___ | |_| |__
# | '__/ _ \| |_| |_____| '_ \| | | | |/ _ \ __/ _ \ / _ \| __| '_ \
# | | | (_) | _| |_____| |_) | | |_| | __/ || (_) | (_) | |_| | | |
# |_| \___/|_| |_| |_.__/|_|\__,_|\___|\__\___/ \___/ \__|_| |_|
#
# Author: Nick Clyde (clydedroid)
#
# A script that generates a rofi menu that uses bluetoothctl to
# connect to bluetooth devices and display status info.
#
# Inspired by networkmanager-dmenu (https://github.com/firecat53/networkmanager-dmenu)
# Thanks to x70b1 (https://github.com/polybar/polybar-scripts/tree/master/polybar-scripts/system-bluetooth-bluetoothctl)
#
# Depends on:
# Arch repositories: rofi, bluez-utils (contains bluetoothctl)
# Import Current Theme
DIR="$HOME/.config/hypr"
RASI="$DIR/rofi/bluetooth.rasi"
# Constants
divider="---------"
goback="Back"
# Checks if bluetooth controller is powered on
power_on() {
if bluetoothctl show | grep -q "Powered: yes"; then
return 0
else
return 1
fi
}
# Toggles power state
toggle_power() {
if power_on; then
bluetoothctl power off
show_menu
else
if rfkill list bluetooth | grep -q 'blocked: yes'; then
rfkill unblock bluetooth && sleep 3
fi
bluetoothctl power on
show_menu
fi
}
# Checks if controller is scanning for new devices
scan_on() {
if bluetoothctl show | grep -q "Discovering: yes"; then
echo "Scan: on"
return 0
else
echo "Scan: off"
return 1
fi
}
# Toggles scanning state
toggle_scan() {
if scan_on; then
kill $(pgrep -f "bluetoothctl scan on")
bluetoothctl scan off
show_menu
else
bluetoothctl scan on &
echo "Scanning..."
sleep 5
show_menu
fi
}
# Checks if controller is able to pair to devices
pairable_on() {
if bluetoothctl show | grep -q "Pairable: yes"; then
echo "Pairable: on"
return 0
else
echo "Pairable: off"
return 1
fi
}
# Toggles pairable state
toggle_pairable() {
if pairable_on; then
bluetoothctl pairable off
show_menu
else
bluetoothctl pairable on
show_menu
fi
}
# Checks if controller is discoverable by other devices
discoverable_on() {
if bluetoothctl show | grep -q "Discoverable: yes"; then
echo "Discoverable: on"
return 0
else
echo "Discoverable: off"
return 1
fi
}
# Toggles discoverable state
toggle_discoverable() {
if discoverable_on; then
bluetoothctl discoverable off
show_menu
else
bluetoothctl discoverable on
show_menu
fi
}
# Checks if a device is connected
device_connected() {
device_info=$(bluetoothctl info "$1")
if echo "$device_info" | grep -q "Connected: yes"; then
return 0
else
return 1
fi
}
# Toggles device connection
toggle_connection() {
if device_connected "$1"; then
bluetoothctl disconnect "$1"
device_menu "$device"
else
bluetoothctl connect "$1"
device_menu "$device"
fi
}
# Checks if a device is paired
device_paired() {
device_info=$(bluetoothctl info "$1")
if echo "$device_info" | grep -q "Paired: yes"; then
echo "Paired: yes"
return 0
else
echo "Paired: no"
return 1
fi
}
# Toggles device paired state
toggle_paired() {
if device_paired "$1"; then
bluetoothctl remove "$1"
device_menu "$device"
else
bluetoothctl pair "$1"
device_menu "$device"
fi
}
# Checks if a device is trusted
device_trusted() {
device_info=$(bluetoothctl info "$1")
if echo "$device_info" | grep -q "Trusted: yes"; then
echo "Trusted: yes"
return 0
else
echo "Trusted: no"
return 1
fi
}
# Toggles device connection
toggle_trust() {
if device_trusted "$1"; then
bluetoothctl untrust "$1"
device_menu "$device"
else
bluetoothctl trust "$1"
device_menu "$device"
fi
}
# Prints a short string with the current bluetooth status
# Useful for status bars like polybar, etc.
print_status() {
if power_on; then
printf '󰂯'
paired_devices_cmd="devices Paired"
# Check if an outdated version of bluetoothctl is used to preserve backwards compatibility
if (( $(echo "$(bluetoothctl version | cut -d ' ' -f 2) < 5.65" | bc -l) )); then
paired_devices_cmd="paired-devices"
fi
mapfile -t paired_devices < <(bluetoothctl $paired_devices_cmd | grep Device | cut -d ' ' -f 2)
counter=0
for device in "${paired_devices[@]}"; do
if device_connected "$device"; then
device_alias=$(bluetoothctl info "$device" | grep "Alias" | cut -d ' ' -f 2-)
if [ $counter -gt 0 ]; then
printf ", %s" "$device_alias"
else
printf " %s" "$device_alias"
fi
((counter++))
fi
done
printf "\n"
else
echo "󰂲"
fi
}
# A submenu for a specific device that allows connecting, pairing, and trusting
device_menu() {
device=$1
# Get device name and mac address
device_name=$(echo "$device" | cut -d ' ' -f 3-)
mac=$(echo "$device" | cut -d ' ' -f 2)
# Build options
if device_connected "$mac"; then
connected="Connected: yes"
else
connected="Connected: no"
fi
paired=$(device_paired "$mac")
trusted=$(device_trusted "$mac")
options="$connected\n$paired\n$trusted\n$divider\n$goback\nExit"
# Open rofi menu, read chosen option
chosen="$(echo -e "$options" | $rofi_command "$device_name")"
# Match chosen option to command
case "$chosen" in
"" | "$divider")
echo "No option chosen."
;;
"$connected")
toggle_connection "$mac"
;;
"$paired")
toggle_paired "$mac"
;;
"$trusted")
toggle_trust "$mac"
;;
"$goback")
show_menu
;;
esac
}
# Opens a rofi menu with current bluetooth status and options to connect
show_menu() {
# Get menu options
if power_on; then
power="Power: on"
# Human-readable names of devices, one per line
# If scan is off, will only list paired devices
devices=$(bluetoothctl devices | grep Device | cut -d ' ' -f 3-)
# Get controller flags
scan=$(scan_on)
pairable=$(pairable_on)
discoverable=$(discoverable_on)
# Options passed to rofi
options="$devices\n$divider\n$power\n$scan\n$pairable\n$discoverable\nExit"
else
power="Power: off"
options="$power\nExit"
fi
# Open rofi menu, read chosen option
chosen="$(echo -e "$options" | $rofi_command "Bluetooth")"
# Match chosen option to command
case "$chosen" in
"" | "$divider")
echo "No option chosen."
;;
"$power")
toggle_power
;;
"$scan")
toggle_scan
;;
"$discoverable")
toggle_discoverable
;;
"$pairable")
toggle_pairable
;;
*)
device=$(bluetoothctl devices | grep "$chosen")
# Open a submenu if a device is selected
if [[ $device ]]; then device_menu "$device"; fi
;;
esac
}
# Rofi command to pipe into, can add any options here
rofi_command="rofi -theme ${RASI} -dmenu $* -p"
case "$1" in
--status)
print_status
;;
*)
show_menu
;;
esac
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
# Import Current Theme
DIR="$HOME/.config/hypr"
RASI="$DIR/rofi/launcher.rasi"
# Run
rofi \
-show drun \
-theme ${RASI}
+120
View File
@@ -0,0 +1,120 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
# Import Current Theme
DIR="$HOME/.config/hypr"
RASI="$DIR/rofi/music.rasi"
# Theme Elements
status="`mpc status`"
if [[ -z "$status" ]]; then
prompt='Offline'
mesg="MPD is Offline"
else
prompt="`mpc -f "%artist%" current`"
mesg="`mpc -f "%title%" current` :: `mpc status | grep "#" | awk '{print $3}'`"
fi
# Options
layout=`cat ${RASI} | grep 'USE_ICON' | cut -d'=' -f2`
if [[ "$layout" == 'NO' ]]; then
if [[ ${status} == *"[playing]"* ]]; then
option_1=" Pause"
else
option_1=" Play"
fi
option_2=" Stop"
option_3=" Previous"
option_4=" Next"
option_5=" Repeat"
option_6=" Random"
else
if [[ ${status} == *"[playing]"* ]]; then
option_1=""
else
option_1=""
fi
option_2=""
option_3=""
option_4=""
option_5=""
option_6=""
fi
# Toggle Actions
active=''
urgent=''
# Repeat
if [[ ${status} == *"repeat: on"* ]]; then
active="-a 4"
elif [[ ${status} == *"repeat: off"* ]]; then
urgent="-u 4"
else
option_5=" Parsing Error"
fi
# Random
if [[ ${status} == *"random: on"* ]]; then
[ -n "$active" ] && active+=",5" || active="-a 5"
elif [[ ${status} == *"random: off"* ]]; then
[ -n "$urgent" ] && urgent+=",5" || urgent="-u 5"
else
option_6=" Parsing Error"
fi
# Rofi CMD
rofi_cmd() {
rofi -dmenu \
-p "$prompt" \
-mesg "$mesg" \
${active} ${urgent} \
-markup-rows \
-theme ${RASI}
}
# Pass variables to rofi dmenu
run_rofi() {
echo -e "$option_1\n$option_2\n$option_3\n$option_4\n$option_5\n$option_6" | rofi_cmd
}
# Execute Command
iDIR="$HOME/.config/hypr/mako/icons"
notify_song="notify-send -h string:x-canonical-private-synchronous:sys-notify-song -u low -i ${iDIR}/music.png"
run_cmd() {
if [[ "$1" == '--opt1' ]]; then
mpc -q toggle && ${notify_song} "`mpc -f "%artist%" current`"
elif [[ "$1" == '--opt2' ]]; then
mpc -q stop
elif [[ "$1" == '--opt3' ]]; then
mpc -q prev && ${notify_song} "`mpc -f "%artist%" current`"
elif [[ "$1" == '--opt4' ]]; then
mpc -q next && ${notify_song} "`mpc -f "%artist%" current`"
elif [[ "$1" == '--opt5' ]]; then
mpc -q repeat
elif [[ "$1" == '--opt6' ]]; then
mpc -q random
fi
}
# Actions
chosen="$(run_rofi)"
case ${chosen} in
$option_1)
run_cmd --opt1
;;
$option_2)
run_cmd --opt2
;;
$option_3)
run_cmd --opt3
;;
$option_4)
run_cmd --opt4
;;
$option_5)
run_cmd --opt5
;;
$option_6)
run_cmd --opt6
;;
esac
+924
View File
@@ -0,0 +1,924 @@
#!/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/hypr/rofi/networkmenu_config.ini
"""
import pathlib
import struct
import configparser
import locale
import os
from os.path import expanduser
import shlex
from shutil import which
import sys
from time import sleep
import uuid
import subprocess
# pylint: disable=import-error
import gi
gi.require_version('NM', '1.0')
from gi.repository import GLib, NM # noqa pylint: disable=wrong-import-position
# pylint: enable=import-error
ENV = os.environ.copy()
ENV['LC_ALL'] = 'C'
ENC = locale.getpreferredencoding()
CONF = configparser.ConfigParser()
CONF.read(expanduser("~/.config/hypr/rofi/networkmenu_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 nmd.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", "<num_lines>", "-p", "<prompt>", "-i"]
"""
# Create command string
commands = {"dmenu": ["-p", str(prompt)],
"rofi": ["-dmenu", "-p", str(prompt), "-l", str(num_lines)],
"bemenu": ["-p", str(prompt)],
"wofi": ["-p", str(prompt)]}
command = shlex.split(CONF.get('dmenu', 'dmenu_command', fallback="dmenu"))
command.extend(cli_args())
command.extend(commands.get(command[0], []))
# Rofi Highlighting
rofi_highlight = CONF.getboolean('dmenu', 'rofi_highlight', fallback=False)
if rofi_highlight is True and command[0] == "rofi" and active_lines:
command.extend(["-a", ",".join([str(num) for num in active_lines])])
# 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(command[0], obscure_color),
"rofi": ['-password'],
"bemenu": ['-x'],
"wofi": ['-P']}
command.extend(pass_prompts.get(command[0], []))
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()]
assert len(devices) == 1
return devices[0]
def is_installed(cmd):
"""Check if a utility is installed"""
return which(cmd) is not None
def bluetooth_get_enabled():
"""Check if bluetooth is enabled via rfkill.
Returns None if no bluetooth device was found.
"""
# 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))
return actions
def rescan_wifi():
"""
Rescan Wifi Access Points
"""
delay = CONF.getint('nmdm', 'rescan_delay', fallback=5)
for dev in CLIENT.get_devices():
if gi.repository.NM.DeviceWifi == type(dev):
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 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 = []
for nm_ap, name, sec in zip(aps, names, secs):
bars = NM.utils_wifi_strength_bars(nm_ap.get_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 == '*'])
is_active = nm_ap.get_bssid() == active_ap_bssid
compact = CONF.getboolean("dmenu", "compact", fallback=False)
if compact:
action_name = f"{name} {sec} {bars}"
else:
action_name = f"{name:<{max_len_name}s} {sec:<{max_len_sec}s} {bars:>4}"
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_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_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
action_name = f"{con.get_id()}:{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, vpns, wgs, gsms, blues, wwan, 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
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 += 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 += others + empty_action if others else []
all_actions += saved + empty_action if saved else []
return all_actions
def get_selection(all_actions):
"""Spawn dmenu for selection and execute the associated action."""
rofi_highlight = CONF.getboolean('dmenu', 'rofi_highlight', fallback=False)
inp = []
if rofi_highlight is True:
inp = [str(action) for action in all_actions]
else:
inp = [('== ' if action.is_active else ' ') + 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 rofi_highlight is False:
action = [i for i in all_actions
if ((str(i).strip() == str(sel.strip())
and not i.is_active) or
('== ' + str(i) == str(sel.rstrip('\n'))
and i.is_active))]
else:
action = [i for i in all_actions if str(i).strip() == sel.strip()]
assert len(action) == 1, 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
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
"""
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 = CONF.get("editor", "terminal", fallback="xterm")
gui_if_available = CONF.getboolean("editor", "gui_if_available", fallback=True)
guis = ["gnome-control-center", "nm-connection-editor"]
if gui_if_available is True:
for gui in guis:
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 get_passphrase():
"""Get a password
Returns: string
"""
pinentry = CONF.get("dmenu", "pinentry", fallback=None)
if pinentry:
pin = ""
out = subprocess.run(pinentry,
capture_output=True,
check=False,
encoding=ENC,
input='setdesc Get network password\ngetpin\n').stdout
if out:
res = out.split("\n")[2]
if res.startswith("D "):
pin = res.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")]
assert len(action) == 1, 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 , 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)
conns_cur = [i for i in CONNS if
i.get_setting_wireless() is not None and
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"""
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 = []
eths = [i for i in CONNS if i.is_type(NM.SETTING_WIRED_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)
blue_actions = create_blue_actions(blues, 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, vpn_actions, wg_actions,
gsm_actions, blue_actions, wwan_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 :
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
# Import Current Theme
DIR="$HOME/.config/hypr"
RASI="$DIR/rofi/powermenu.rasi"
CNFR="$DIR/rofi/confirm.rasi"
# Theme Elements
prompt="`hostname` (`echo $DESKTOP_SESSION`)"
mesg="Uptime : `uptime -p | sed -e 's/up //g'`"
# Options
layout=`cat ${RASI} | grep 'USE_ICON' | cut -d'=' -f2`
if [[ "$layout" == 'NO' ]]; then
option_1=" Lock"
option_2=" Logout"
option_3=" Suspend"
option_4=" Hibernate"
option_5=" Reboot"
option_6=" Shutdown"
else
option_1=""
option_2=""
option_3=""
option_4=""
option_5=""
option_6=""
fi
cnflayout=`cat ${CNFR} | grep 'USE_ICON' | cut -d'=' -f2`
if [[ "$cnflayout" == 'NO' ]]; then
yes=' Yes'
no=' No'
else
yes=''
no=''
fi
# Rofi CMD
rofi_cmd() {
rofi -dmenu \
-p "$prompt" \
-mesg "$mesg" \
-markup-rows \
-theme ${RASI}
}
# Pass variables to rofi dmenu
run_rofi() {
echo -e "$option_1\n$option_2\n$option_3\n$option_4\n$option_5\n$option_6" | rofi_cmd
}
# Confirmation CMD
confirm_cmd() {
rofi -dmenu \
-p 'Confirmation' \
-mesg 'Are you Sure?' \
-theme ${CNFR}
}
# Ask for confirmation
confirm_exit() {
echo -e "$yes\n$no" | confirm_cmd
}
# Confirm and execute
confirm_run () {
selected="$(confirm_exit)"
if [[ "$selected" == "$yes" ]]; then
${1} && ${2} && ${3} && ${4}
else
exit
fi
}
# Execute Command
run_cmd() {
if [[ "$1" == '--opt1' ]]; then
hyprlock
elif [[ "$1" == '--opt2' ]]; then
confirm_run 'hyprctl dispatch exit 0'
elif [[ "$1" == '--opt3' ]]; then
confirm_run 'mpc -q pause' 'pulsemixer --mute' 'systemctl suspend'
elif [[ "$1" == '--opt4' ]]; then
confirm_run 'systemctl hibernate'
elif [[ "$1" == '--opt5' ]]; then
confirm_run 'systemctl reboot'
elif [[ "$1" == '--opt6' ]]; then
confirm_run 'systemctl poweroff'
fi
}
# Actions
chosen="$(run_rofi)"
case ${chosen} in
$option_1)
run_cmd --opt1
;;
$option_2)
run_cmd --opt2
;;
$option_3)
run_cmd --opt3
;;
$option_4)
run_cmd --opt4
;;
$option_5)
run_cmd --opt5
;;
$option_6)
run_cmd --opt6
;;
esac
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
# Import Current Theme
DIR="$HOME/.config/hypr"
RASI="$DIR/rofi/runner.rasi"
# Run
rofi \
-show run \
-theme ${RASI}
+143
View File
@@ -0,0 +1,143 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
# Get Colors
DIR="$HOME/.config/hypr"
background="`cat $DIR/rofi/shared/colors.rasi | grep 'background:' | cut -d':' -f2 | tr -d ' '\;`"
accent="`cat $DIR/rofi/shared/colors.rasi | grep 'selected:' | cut -d':' -f2 | tr -d ' '\;`"
# Import Current Theme
DIR="$HOME/.config/hypr"
RASI="$DIR/rofi/screenshot.rasi"
# Theme Elements
prompt='Screenshot'
mesg="Directory :: `xdg-user-dir PICTURES`/Screenshots"
# Options
layout=`cat ${RASI} | grep 'USE_ICON' | cut -d'=' -f2`
if [[ "$layout" == 'NO' ]]; then
option_1=" Capture Desktop"
option_2=" Capture Area"
option_3=" Capture Window"
option_4=" Capture in 5s"
option_5=" Capture in 10s"
else
option_1=""
option_2=""
option_3=""
option_4=""
option_5=""
fi
# Rofi CMD
rofi_cmd() {
rofi -dmenu \
-p "$prompt" \
-mesg "$mesg" \
-markup-rows \
-theme ${RASI}
}
# Pass variables to rofi dmenu
run_rofi() {
echo -e "$option_1\n$option_2\n$option_3\n$option_4\n$option_5" | rofi_cmd
}
# Screenshot
time=`date +%Y-%m-%d-%H-%M-%S`
dir="`xdg-user-dir PICTURES`/Screenshots"
file="Screenshot_${time}_${geometry}.png"
# Directory
if [[ ! -d "$dir" ]]; then
mkdir -p "$dir"
fi
# notify and view screenshot
iDIR="$HOME/.config/hypr/mako/icons"
notify_view() {
notify_cmd_shot="notify-send -h string:x-canonical-private-synchronous:sys-notify-shot -u low -i ${iDIR}/picture.png"
${notify_cmd_shot} "Copied to clipboard."
paplay /usr/share/sounds/freedesktop/stereo/screen-capture.oga &>/dev/null &
viewnior ${dir}/"$file"
if [[ -e "$dir/$file" ]]; then
${notify_cmd_shot} "Screenshot Saved."
else
${notify_cmd_shot} "Screenshot Deleted."
fi
}
# countdown
countdown () {
for sec in `seq $1 -1 1`; do
notify-send -h string:x-canonical-private-synchronous:sys-notify-count -t 1000 -i "$iDIR"/timer.png "Taking shot in : $sec"
sleep 1
done
}
# take shots
shotnow () {
cd ${dir} && sleep 0.5 && grim - | tee "$file" | wl-copy
notify_view
}
shot5 () {
countdown '5'
sleep 1 && cd ${dir} && grim - | tee "$file" | wl-copy
notify_view
}
shot10 () {
countdown '10'
sleep 1 && cd ${dir} && grim - | tee "$file" | wl-copy
notify_view
}
shotwin () {
w_pos=`hyprctl activewindow | grep 'at:' | cut -d':' -f2 | tr -d ' ' | tail -n1`
w_size=`hyprctl activewindow | grep 'size:' | cut -d':' -f2 | tr -d ' ' | tail -n1 | sed s/,/x/g`
cd ${dir} && sleep 0.3 && grim -g "$w_pos $w_size" - | tee "$file" | wl-copy
notify_view
}
shotarea () {
cd ${dir} && grim -g "$(slurp -b ${background:1}CC -c ${accent:1}ff -s ${accent:1}0D -w 2 && sleep 0.3)" - | tee "$file" | wl-copy
notify_view
}
# Execute Command
run_cmd() {
if [[ "$1" == '--opt1' ]]; then
shotnow
elif [[ "$1" == '--opt2' ]]; then
shotarea
elif [[ "$1" == '--opt3' ]]; then
shotwin
elif [[ "$1" == '--opt4' ]]; then
shot5
elif [[ "$1" == '--opt5' ]]; then
shot10
fi
}
# Actions
chosen="$(run_rofi)"
case ${chosen} in
$option_1)
run_cmd --opt1
;;
$option_2)
run_cmd --opt2
;;
$option_3)
run_cmd --opt3
;;
$option_4)
run_cmd --opt4
;;
$option_5)
run_cmd --opt5
;;
esac
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# Import Current Theme
DIR="$HOME/.config/hypr"
RASI="$DIR/rofi/runner.rasi"
TERMINAL="$DIR/scripts/alacritty"
# Run
rofi \
-show ssh \
-terminal ${TERMINAL} \
-theme ${RASI}
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
# Import Current Theme
DIR="$HOME/.config/hypr"
RASI="$DIR/rofi/runner.rasi"
# Run
rofi \
-show window \
-theme ${RASI}
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## Script to take screenshots with grim, slurp (in Wayland)
# Get Colors
DIR="$HOME/.config/hypr"
background="`cat $DIR/rofi/shared/colors.rasi | grep 'background:' | cut -d':' -f2 | tr -d ' '\;`"
accent="`cat $DIR/rofi/shared/colors.rasi | grep 'selected:' | cut -d':' -f2 | tr -d ' '\;`"
iDIR="$HOME/.config/hypr/mako/icons"
time=`date +%Y-%m-%d-%H-%M-%S`
dir="`xdg-user-dir PICTURES`/Screenshots"
file="Screenshot_${time}_${RANDOM}.png"
# notify and view screenshot
notify_cmd_shot="notify-send -h string:x-canonical-private-synchronous:sys-notify-shot -u low -i ${iDIR}/picture.png"
notify_view () {
${notify_cmd_shot} "Copied to clipboard."
paplay /usr/share/sounds/freedesktop/stereo/screen-capture.oga &>/dev/null &
viewnior ${dir}/"$file"
if [[ -e "$dir/$file" ]]; then
${notify_cmd_shot} "Screenshot Saved."
else
${notify_cmd_shot} "Screenshot Deleted."
fi
}
# countdown
countdown () {
for sec in `seq $1 -1 1`; do
notify-send -h string:x-canonical-private-synchronous:sys-notify-count -t 1000 -i "$iDIR"/timer.png "Taking shot in : $sec"
sleep 1
done
}
# take shots
shotnow () {
cd ${dir} && sleep 0.5 && grim - | tee "$file" | wl-copy
notify_view
}
shot5 () {
countdown '5'
sleep 1 && cd ${dir} && grim - | tee "$file" | wl-copy
notify_view
}
shot10 () {
countdown '10'
sleep 1 && cd ${dir} && grim - | tee "$file" | wl-copy
notify_view
}
shotwin () {
w_pos=`hyprctl activewindow | grep 'at:' | cut -d':' -f2 | tr -d ' ' | tail -n1`
w_size=`hyprctl activewindow | grep 'size:' | cut -d':' -f2 | tr -d ' ' | tail -n1 | sed s/,/x/g`
cd ${dir} && grim -g "$w_pos $w_size" - | tee "$file" | wl-copy
notify_view
}
shotarea () {
cd ${dir} && grim -g "$(slurp -b ${background:1}CC -c ${accent:1}ff -s ${accent:1}0D -w 2 && sleep 0.3)" - | tee "$file" | wl-copy
notify_view
}
if [[ ! -d "$dir" ]]; then
mkdir -p "$dir"
fi
if [[ "$1" == "--now" ]]; then
shotnow
elif [[ "$1" == "--in5" ]]; then
shot5
elif [[ "$1" == "--in10" ]]; then
shot10
elif [[ "$1" == "--win" ]]; then
shotwin
elif [[ "$1" == "--area" ]]; then
shotarea
else
echo -e "Available Options : --now --in5 --in10 --win --area"
fi
exit 0
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## Autostart Programs
# Kill already running process
_ps=(waybar mako)
for _prs in "${_ps[@]}"; do
if [[ `pidof ${_prs}` ]]; then
killall -9 ${_prs}
fi
done
# Polkit agent
if [[ ! `pidof xfce-polkit` ]]; then
/usr/lib/xfce-polkit/xfce-polkit &
fi
# Set wallpaper
~/.config/hypr/scripts/wallpaper &
# Apply themes
~/.config/hypr/scripts/gtkthemes &
# Lauch notification daemon (mako)
~/.config/hypr/scripts/notifications &
# Lauch statusbar (waybar)
~/.config/hypr/scripts/statusbar &
# Start network manager applet
nm-applet --indicator &
# Start mpd
exec mpd &
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## launch waybar with alt config
CONFIG="$HOME/.config/hypr/waybar/config"
STYLE="$HOME/.config/hypr/waybar/style.css"
if [[ ! `pidof waybar` ]]; then
waybar --bar main-bar --log-level error --config ${CONFIG} --style ${STYLE}
fi
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## Script To Manage Speaker Volume For Archcraft (in Wayland).
iDIR="$HOME/.config/hypr/mako/icons"
notify_cmd='notify-send -h string:x-canonical-private-synchronous:sys-notify-volume -u low'
# Get Volume
get_volume() {
echo "`pulsemixer --get-volume | cut -d' ' -f1`"
}
# Get icons
get_icon() {
current="$(get_volume)"
if [[ "$current" -eq "0" ]]; then
icon="$iDIR/volume-mute.png"
elif [[ ("$current" -ge "0") && ("$current" -le "30") ]]; then
icon="$iDIR/volume-low.png"
elif [[ ("$current" -ge "30") && ("$current" -le "60") ]]; then
icon="$iDIR/volume-mid.png"
elif [[ ("$current" -ge "60") && ("$current" -le "100") ]]; then
icon="$iDIR/volume-high.png"
fi
}
# Notify
notify_user() {
${notify_cmd} -i "$icon" "Volume : $(get_volume)%"
}
# Increase Volume
inc_volume() {
[[ `pulsemixer --get-mute` == 1 ]] && pulsemixer --unmute
pulsemixer --max-volume 100 --change-volume +5 && get_icon && notify_user
}
# Decrease Volume
dec_volume() {
[[ `pulsemixer --get-mute` == 1 ]] && pulsemixer --unmute
pulsemixer --max-volume 100 --change-volume -5 && get_icon && notify_user
}
# Toggle Mute
toggle_mute() {
if [[ `pulsemixer --get-mute` == 0 ]]; then
pulsemixer --toggle-mute && ${notify_cmd} -i "$iDIR/volume-mute.png" "Mute"
else
pulsemixer --toggle-mute && get_icon && ${notify_cmd} -i "$icon" "Unmute"
fi
}
# Toggle Mic
toggle_mic() {
ID="`pulsemixer --list-sources | grep 'Default' | cut -d',' -f1 | cut -d' ' -f3`"
if [[ `pulsemixer --id $ID --get-mute` == 0 ]]; then
pulsemixer --id ${ID} --toggle-mute && ${notify_cmd} -i "$iDIR/microphone-mute.png" "Microphone Switched OFF"
else
pulsemixer --id ${ID} --toggle-mute && ${notify_cmd} -i "$iDIR/microphone.png" "Microphone Switched ON"
fi
}
# Execute accordingly
if [[ -x `which pulsemixer` ]]; then
if [[ "$1" == "--get" ]]; then
get_volume
elif [[ "$1" == "--inc" ]]; then
inc_volume
elif [[ "$1" == "--dec" ]]; then
dec_volume
elif [[ "$1" == "--toggle" ]]; then
toggle_mute
elif [[ "$1" == "--toggle-mic" ]]; then
toggle_mic
else
echo $(get_volume)%
fi
else
${notify_cmd} "'pulsemixer' is not installed."
fi
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## Set Wallpaper in Hyprland
# WALLPAPER='/home/legrems/Pictures/wallpapers/wallpaper_3.jpg'
WALLPAPER='/home/legrems/Pictures/wallpapers/wallpaper_3.jpg'
swaybg --output '*' --mode fill --image "$WALLPAPER"
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## wlogout with alt layout and style file
LAYOUT="$HOME/.config/hypr/wlogout/layout"
STYLE="$HOME/.config/hypr/wlogout/style.css"
if [[ ! `pidof wlogout` ]]; then
wlogout --layout ${LAYOUT} --css ${STYLE} \
--buttons-per-row 5 \
--column-spacing 50 \
--row-spacing 50 \
--margin-top 390 \
--margin-bottom 390 \
--margin-left 150 \
--margin-right 150
else
pkill wlogout
fi
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## launch wofi with alt config
CONFIG="$HOME/.config/hypr/wofi/config"
STYLE="$HOME/.config/hypr/wofi/style.css"
if [[ ! `pidof wofi` ]]; then
wofi --show drun --prompt 'Search...' --conf ${CONFIG} --style ${STYLE}
else
pkill wofi
fi
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
## Copyright (C) 2020-2024 Aditya Shakya <adi1090x@gmail.com>
##
## Wofi Powermenu
## Files
CONFIG="$HOME/.config/hypr/wofi/config"
STYLE="$HOME/.config/hypr/wofi/style.css"
## Wofi Command
wofi_command="wofi --show dmenu \
--conf ${CONFIG} --style ${STYLE} \
--width=300 --height=215 \
--cache-file=/dev/null \
--hide-scroll --no-actions \
--define=matching=fuzzy"
uptime=$(uptime -p | sed -e 's/up //g')
## Entries
shutdown=" Shutdown"
reboot=" Restart"
lock=" Lock"
suspend=" Sleep"
logout=" Logout"
# Ask for confirmation
cdialog () {
yad --title='Confirm?' --borders=15 --center --fixed --undecorated --button=Yes:0 --button=No:1 --text="Are you sure?" --text-align=center
}
# Variable passed to rofi
open_menu () {
options="$lock\n$suspend\n$logout\n$reboot\n$shutdown"
chosen="$(echo -e "$options" | $wofi_command --prompt "UP - $uptime")"
case $chosen in
$shutdown)
cdialog
if [[ "$?" == 0 ]]; then
systemctl poweroff
else
exit
fi
;;
$reboot)
cdialog
if [[ "$?" == 0 ]]; then
systemctl reboot
else
exit
fi
;;
$lock)
~/.config/hypr/scripts/lockscreen
;;
$suspend)
cdialog
if [[ "$?" == 0 ]]; then
mpc -q pause
pulsemixer --mute
~/.config/hypr/scripts/lockscreen
systemctl suspend
else
exit
fi
;;
$logout)
cdialog
if [[ "$?" == 0 ]]; then
hyprctl dispatch exit 0
else
exit
fi
;;
esac
}
if [[ ! `pidof wofi` ]]; then
open_menu
else
pkill wofi
fi