configuration form the arch linux stored
This commit is contained in:
313
rofi/scripts/bluetooth.sh
Executable file
313
rofi/scripts/bluetooth.sh
Executable file
@@ -0,0 +1,313 @@
|
||||
#!/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)
|
||||
|
||||
# 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 ''
|
||||
|
||||
mapfile -t paired_devices < <(bluetoothctl paired-devices | 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 [[ $device_alias != "Bluetooth Mouse M336/M337/M535" ]]; then
|
||||
if [ $counter -gt 0 ]; then
|
||||
printf ", %s" "$device_alias"
|
||||
else
|
||||
printf " %s" "$device_alias"
|
||||
fi
|
||||
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 "")"
|
||||
|
||||
# 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 -dmenu -no-fixed-num-lines -yoffset -100 -i -p"
|
||||
|
||||
case "$1" in
|
||||
--status)
|
||||
print_status
|
||||
;;
|
||||
*)
|
||||
show_menu
|
||||
;;
|
||||
esac
|
||||
8
rofi/scripts/bookmarks/bookmarks.sh
Executable file
8
rofi/scripts/bookmarks/bookmarks.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
# cat $(dirname $0)/bookmarks.txt | rofi -dmenu -i -p "" | cut -d\- -f2- | xclip -selection clipboard
|
||||
ADDRESS=$(cat $(dirname $0)/bookmarks.txt | rofi -dmenu -i -p "" | cut -d\- -f2-)
|
||||
if [ -n "$ADDRESS" ]; then
|
||||
brave -e $ADDRESS
|
||||
i3-msg workspace number 2
|
||||
fi
|
||||
18
rofi/scripts/bookmarks/bookmarks.txt
Normal file
18
rofi/scripts/bookmarks/bookmarks.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
br - https://search.brave.com
|
||||
pm - https://mail.proton.me
|
||||
gt - https://translate.google.com
|
||||
gs - https://scholar.google.co.jp/?hl=en
|
||||
yt - https://www.youtube.com
|
||||
gm - https://www.google.com/maps
|
||||
ch - https://chat.openai.com
|
||||
wi - https://www.wikipedia.org
|
||||
git - https://github.com
|
||||
aur - https://aur.archlinux.org
|
||||
gr - https://app.grammarly.com
|
||||
fb - https://www.facebook.com
|
||||
sp - https://open.spotify.com
|
||||
angry - https://www.angryreviewer.com
|
||||
deepl - https://www.deepl.com/write
|
||||
typing - https://www.keybr.com
|
||||
overleaf - https://www.overleaf.com/project
|
||||
lang - https://www.languagetool.org
|
||||
71
rofi/scripts/books-search.sh
Executable file
71
rofi/scripts/books-search.sh
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Info:
|
||||
# author: Miroslav Vidovic
|
||||
# file: books-search.sh
|
||||
# created: 13.08.2017.-08:06:54
|
||||
# revision: ---
|
||||
# version: 1.0
|
||||
# -----------------------------------------------------------------------------
|
||||
# Requirements:
|
||||
# rofi
|
||||
# Description:
|
||||
# Use rofi to search my books.
|
||||
# Usage:
|
||||
# books-search.sh
|
||||
# -----------------------------------------------------------------------------
|
||||
# Script:
|
||||
|
||||
# Books directory
|
||||
BOOKS_DIR=~/Library
|
||||
mkdir -p ~/Library
|
||||
|
||||
# Save find result to F_ARRAY
|
||||
readarray -t F_ARRAY <<< "$(find "$BOOKS_DIR" -type f -name '*.pdf')"
|
||||
|
||||
# Associative array for storing books
|
||||
# key => book name
|
||||
# value => absolute path to the file
|
||||
# BOOKS['filename']='path'
|
||||
declare -A BOOKS
|
||||
|
||||
# Add elements to BOOKS array
|
||||
get_books() {
|
||||
if [[ ! -z ${F_ARRAY[@]} ]]; then
|
||||
for i in "${!F_ARRAY[@]}"
|
||||
do
|
||||
path=${F_ARRAY[$i]}
|
||||
file=$(basename "${F_ARRAY[$i]}")
|
||||
BOOKS+=(["$file"]="$path")
|
||||
done
|
||||
else
|
||||
echo "$BOOKS_DIR is empty!"
|
||||
echo "Please put some books in it."
|
||||
echo "Only .pdf files are accepted."
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
# List for rofi
|
||||
gen_list(){
|
||||
for i in "${!BOOKS[@]}"
|
||||
do
|
||||
echo "$i"
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
get_books
|
||||
book=$( (gen_list) | rofi -dmenu -i -matching normal -no-custom -location 0 -p " " )
|
||||
|
||||
if [ -n "$book" ]; then
|
||||
echo "$book" | sed -e "s/^.... - //" -e "s/\ .*//" | xclip -selection clipboard
|
||||
# xdg-open "${BOOKS[$book]}"
|
||||
zathura "${BOOKS[$book]}"
|
||||
fi
|
||||
}
|
||||
|
||||
main
|
||||
|
||||
exit 0
|
||||
13
rofi/scripts/launch_on_current_workspace.sh
Executable file
13
rofi/scripts/launch_on_current_workspace.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the currently focused workspace
|
||||
workspace=$(i3-msg -t get_workspaces | jq '.[] | select(.focused==true).name' | tr -d '"')
|
||||
|
||||
# Run the provided command
|
||||
$1 &
|
||||
|
||||
# Get the window ID of the most recently launched program
|
||||
win_id=$(xdotool getactivewindow)
|
||||
|
||||
# Move the program to the initially focused workspace
|
||||
i3-msg [id="$win_id"] move container to workspace $workspace
|
||||
99
rofi/scripts/monitor-layout.sh
Executable file
99
rofi/scripts/monitor-layout.sh
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
|
||||
XRANDR=$(which xrandr)
|
||||
|
||||
MONITORS=( $( ${XRANDR} | awk '( $2 == "connected" ){ print $1 }' ) )
|
||||
|
||||
|
||||
NUM_MONITORS=${#MONITORS[@]}
|
||||
|
||||
TITLES=()
|
||||
COMMANDS=()
|
||||
|
||||
|
||||
function gen_xrandr_only()
|
||||
{
|
||||
selected=$1
|
||||
|
||||
cmd="xrandr --output ${MONITORS[$selected]} --auto "
|
||||
|
||||
for entry in $(seq 0 $((${NUM_MONITORS}-1)))
|
||||
do
|
||||
if [ $selected != $entry ]
|
||||
then
|
||||
cmd="$cmd --output ${MONITORS[$entry]} --off"
|
||||
fi
|
||||
done
|
||||
|
||||
echo $cmd
|
||||
}
|
||||
|
||||
|
||||
|
||||
declare -i index=0
|
||||
TILES[$index]="Cancel"
|
||||
COMMANDS[$index]="true"
|
||||
index+=1
|
||||
|
||||
|
||||
for entry in $(seq 0 $((${NUM_MONITORS}-1)))
|
||||
do
|
||||
TILES[$index]="Only ${MONITORS[$entry]}"
|
||||
COMMANDS[$index]=$(gen_xrandr_only $entry)
|
||||
index+=1
|
||||
done
|
||||
|
||||
##
|
||||
# Dual screen options
|
||||
##
|
||||
for entry_a in $(seq 0 $((${NUM_MONITORS}-1)))
|
||||
do
|
||||
for entry_b in $(seq 0 $((${NUM_MONITORS}-1)))
|
||||
do
|
||||
if [ $entry_a != $entry_b ]
|
||||
then
|
||||
TILES[$index]="Dual Screen ${MONITORS[$entry_a]} -> ${MONITORS[$entry_b]}"
|
||||
COMMANDS[$index]="xrandr --output ${MONITORS[$entry_a]} --auto \
|
||||
--output ${MONITORS[$entry_b]} --auto --left-of ${MONITORS[$entry_a]}"
|
||||
|
||||
index+=1
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
|
||||
##
|
||||
# Clone monitors
|
||||
##
|
||||
for entry_a in $(seq 0 $((${NUM_MONITORS}-1)))
|
||||
do
|
||||
for entry_b in $(seq 0 $((${NUM_MONITORS}-1)))
|
||||
do
|
||||
if [ $entry_a != $entry_b ]
|
||||
then
|
||||
TILES[$index]="Clone Screen ${MONITORS[$entry_a]} -> ${MONITORS[$entry_b]}"
|
||||
COMMANDS[$index]="xrandr --output ${MONITORS[$entry_a]} --auto \
|
||||
--output ${MONITORS[$entry_b]} --auto --same-as ${MONITORS[$entry_a]}"
|
||||
|
||||
index+=1
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
|
||||
##
|
||||
# Generate entries, where first is key.
|
||||
##
|
||||
function gen_entries()
|
||||
{
|
||||
for a in $(seq 0 $(( ${#TILES[@]} -1 )))
|
||||
do
|
||||
echo $a ${TILES[a]}
|
||||
done
|
||||
}
|
||||
|
||||
# Call menu
|
||||
SEL=$( gen_entries | rofi -dmenu -p "Monitor Setup:" -a 0 -no-custom | awk '{print $1}' )
|
||||
|
||||
# Call xrandr
|
||||
$( ${COMMANDS[$SEL]} )
|
||||
857
rofi/scripts/networkmanager
Normal file
857
rofi/scripts/networkmanager
Normal file
@@ -0,0 +1,857 @@
|
||||
#!/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 formatting options and default terminal if desired to
|
||||
~/.config/networkmanager-dmenu/config.ini
|
||||
"""
|
||||
import pathlib
|
||||
import struct
|
||||
import configparser
|
||||
import itertools
|
||||
import locale
|
||||
import os
|
||||
from os.path import expanduser
|
||||
import shlex
|
||||
import sys
|
||||
import uuid
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
import gi
|
||||
gi.require_version('NM', '1.0')
|
||||
from gi.repository import GLib, NM # pylint: disable=wrong-import-position
|
||||
|
||||
ENV = os.environ.copy()
|
||||
ENV['LC_ALL'] = 'C'
|
||||
ENC = locale.getpreferredencoding()
|
||||
|
||||
CLIENT = NM.Client.new(None)
|
||||
LOOP = GLib.MainLoop()
|
||||
CONNS = CLIENT.get_connections()
|
||||
|
||||
CONF = configparser.ConfigParser()
|
||||
CONF.read(expanduser("~/.config/networkmanager-dmenu/config.ini"))
|
||||
|
||||
def dmenu_cmd(num_lines, prompt="", active_lines=None): # pylint: disable=too-many-branches
|
||||
"""Parse config.ini if it exists and add options to the dmenu command
|
||||
Args: args - num_lines: number of lines to display
|
||||
prompt: prompt to show
|
||||
Returns: command invocation (as a list of strings) for
|
||||
dmenu -l <num_lines> -p <prompt> -i ...
|
||||
"""
|
||||
dmenu_command = "dmenu"
|
||||
if not CONF.sections():
|
||||
res = [dmenu_command, "-i", "-l", str(num_lines), "-p", str(prompt)]
|
||||
res.extend(sys.argv[1:])
|
||||
return res
|
||||
if CONF.has_section('dmenu'):
|
||||
args = CONF.items('dmenu')
|
||||
args_dict = dict(args)
|
||||
dmenu_args = []
|
||||
if "dmenu_command" in args_dict:
|
||||
command = shlex.split(args_dict["dmenu_command"])
|
||||
dmenu_command = command[0]
|
||||
dmenu_args = command[1:]
|
||||
del args_dict["dmenu_command"]
|
||||
if "p" in args_dict and prompt == "":
|
||||
prompt = args_dict["p"]
|
||||
del args_dict["p"]
|
||||
elif "p" in args_dict:
|
||||
del args_dict["p"]
|
||||
if "rofi" in dmenu_command:
|
||||
lines = "-i -dmenu -lines"
|
||||
# rofi doesn't support 0 length line, it requires at least -lines=1
|
||||
# see https://github.com/DaveDavenport/rofi/issues/252
|
||||
num_lines = num_lines or 1
|
||||
else:
|
||||
lines = "-i -l"
|
||||
if "l" in args_dict:
|
||||
# rofi doesn't support 0 length line, it requires at least -lines=1
|
||||
# see https://github.com/DaveDavenport/rofi/issues/252
|
||||
if "rofi" in dmenu_command:
|
||||
args_dict['l'] = min(num_lines, int(args_dict['l'])) or 1
|
||||
lines = "{} {}".format(lines, args_dict['l'])
|
||||
del args_dict['l']
|
||||
else:
|
||||
lines = "{} {}".format(lines, num_lines)
|
||||
if "pinentry" in args_dict:
|
||||
del args_dict["pinentry"]
|
||||
if "compact" in args_dict:
|
||||
del args_dict["compact"]
|
||||
if "wifi_chars" in args_dict:
|
||||
del args_dict["wifi_chars"]
|
||||
rofi_highlight = CONF.getboolean('dmenu', 'rofi_highlight', fallback=False)
|
||||
if CONF.has_option('dmenu', 'rofi_highlight'):
|
||||
del args_dict["rofi_highlight"]
|
||||
if rofi_highlight is True and "rofi" in dmenu_command:
|
||||
if active_lines:
|
||||
dmenu_args.extend(["-a", ",".join([str(num)
|
||||
for num in active_lines])])
|
||||
if prompt == "Passphrase":
|
||||
if CONF.has_section('dmenu_passphrase'):
|
||||
args = CONF.items('dmenu_passphrase')
|
||||
args_dict.update(args)
|
||||
rofi_obscure = CONF.getboolean('dmenu_passphrase', 'rofi_obscure', fallback=True)
|
||||
if CONF.has_option('dmenu_passphrase', 'rofi_obscure'):
|
||||
del args_dict["rofi_obscure"]
|
||||
if rofi_obscure is True and "rofi" in dmenu_command:
|
||||
dmenu_args.extend(["-password"])
|
||||
dmenu_password = CONF.getboolean('dmenu_passphrase', 'dmenu_password', fallback=False)
|
||||
if CONF.has_option('dmenu_passphrase', 'dmenu_password'):
|
||||
del args_dict["dmenu_password"]
|
||||
if dmenu_password is True:
|
||||
dmenu_args.extend(["-P"])
|
||||
extras = (["-" + str(k), str(v)] for (k, v) in args_dict.items())
|
||||
res = [dmenu_command, "-p", str(prompt)]
|
||||
res.extend(dmenu_args)
|
||||
res += list(itertools.chain.from_iterable(extras))
|
||||
res[1:1] = lines.split()
|
||||
res = list(filter(None, res)) # Remove empty list elements
|
||||
res.extend(sys.argv[1:])
|
||||
return res
|
||||
|
||||
|
||||
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: # pylint: disable=no-else-return
|
||||
return None
|
||||
elif len(devices) == 1:
|
||||
return devices[0]
|
||||
device_names = "\n".join([d.get_iface() for d in devices]).encode(ENC)
|
||||
sel = Popen(dmenu_cmd(len(devices), "CHOOSE ADAPTER:"),
|
||||
stdin=PIPE,
|
||||
stdout=PIPE,
|
||||
env=ENV).communicate(input=device_names)[0].decode(ENC)
|
||||
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_modemmanager_installed():
|
||||
"""Check if ModemManager is installed"""
|
||||
with open(os.devnull) as devnull:
|
||||
try:
|
||||
Popen(["ModemManager"], stdout=devnull, stderr=devnull).communicate()
|
||||
except OSError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
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("{} Wifi".format(wifi_action), toggle_wifi,
|
||||
not wifi_enabled),
|
||||
Action("{} Networking".format(networking_action),
|
||||
toggle_networking, not networking_enabled)]
|
||||
if bluetooth_enabled is not None:
|
||||
actions.append(Action("{} Bluetooth".format(bluetooth_action),
|
||||
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
|
||||
"""
|
||||
for dev in CLIENT.get_devices():
|
||||
if gi.repository.NM.DeviceWifi == type(dev):
|
||||
try:
|
||||
dev.request_scan_async(None, rescan_cb, None)
|
||||
LOOP.run()
|
||||
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 complete")
|
||||
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 != 0:
|
||||
sec_str += " WPA1"
|
||||
if rsn_flags != 0:
|
||||
sec_str += " WPA2"
|
||||
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 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)
|
||||
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)
|
||||
else:
|
||||
if ap_security(nm_ap) != "--":
|
||||
password = get_passphrase()
|
||||
else:
|
||||
password = ""
|
||||
set_new_connection(nm_ap, password, adapter)
|
||||
LOOP.run()
|
||||
|
||||
|
||||
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("Activated {}".format(conn.get_id()))
|
||||
else:
|
||||
notify("Problem activating {}".format(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("Deactivated {}".format(data.get_id()))
|
||||
else:
|
||||
notify("Problem deactivating {}".format(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): # pylint: disable=too-many-locals
|
||||
"""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 = u"{} {} {}".format(name, sec, bars)
|
||||
else:
|
||||
action_name = u"{:<{}s} {:<{}s} {}".format(name, max_len_name, sec,
|
||||
max_len_sec, bars)
|
||||
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 = u"{}:{}".format(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(u"Multiple active connections match"
|
||||
" the connection: {}".format(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("{} WWAN".format(wwan_action), toggle_wwan, not wwan_enabled)]
|
||||
|
||||
|
||||
def combine_actions(eths, aps, vpns, wgs, gsms, blues, wwan, others, saved):
|
||||
"""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]
|
||||
|
||||
inp_bytes = "\n".join(inp).encode(ENC)
|
||||
command = dmenu_cmd(len(inp), active_lines=active_lines)
|
||||
sel = Popen(command, stdin=PIPE, stdout=PIPE,
|
||||
env=ENV).communicate(input=inp_bytes)[0].decode(ENC)
|
||||
|
||||
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, \
|
||||
u"Selection was ambiguous: '{}'".format(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("Networking {}".format("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("Wifi {}".format("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("Wwan {}".format("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.",
|
||||
"Maybe you need to add your user to the 'rfkill' group?",
|
||||
urgency="critical")
|
||||
else:
|
||||
notify("Bluetooth {}".format("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)
|
||||
if gui_if_available is True:
|
||||
try:
|
||||
Popen(["gnome-control-center", "network"]).communicate()
|
||||
except OSError:
|
||||
try:
|
||||
Popen(["nm-connection-editor"]).communicate()
|
||||
except OSError:
|
||||
Popen([terminal, "-e", "nmtui"]).communicate()
|
||||
else:
|
||||
Popen([terminal, "-e", "nmtui"]).communicate()
|
||||
|
||||
|
||||
def get_passphrase():
|
||||
"""Get a password
|
||||
Returns: string
|
||||
"""
|
||||
pinentry = CONF.get("dmenu", "pinentry", fallback=None)
|
||||
if pinentry:
|
||||
pin = ""
|
||||
out = Popen(pinentry,
|
||||
stdout=PIPE,
|
||||
stdin=PIPE).communicate(input=b'setdesc Get network password\ngetpin\n')[0]
|
||||
if out:
|
||||
res = out.decode(ENC).split("\n")[2]
|
||||
if res.startswith("D "):
|
||||
pin = res.split("D ")[1]
|
||||
return pin
|
||||
return Popen(dmenu_cmd(0, "Passphrase"),
|
||||
stdin=PIPE, stdout=PIPE).communicate()[0].decode(ENC)
|
||||
|
||||
|
||||
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]).encode(ENC)
|
||||
sel = Popen(dmenu_cmd(len(conn_acts), "CHOOSE CONNECTION TO DELETE:"),
|
||||
stdin=PIPE,
|
||||
stdout=PIPE,
|
||||
env=ENV).communicate(input=conn_names)[0].decode(ENC)
|
||||
if not sel.strip():
|
||||
sys.exit()
|
||||
action = [i for i in conn_acts if str(i) == sel.rstrip("\n")]
|
||||
assert len(action) == 1, u"Selection was ambiguous: {}".format(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("Deleted {}".format(dev.get_id()))
|
||||
else:
|
||||
notify("Problem deleting {}".format(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=C0301
|
||||
# From https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/examples/python/gi/add_connection.py
|
||||
# and https://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/examples/python/dbus/add-wifi-psk-connection.py
|
||||
# pylint: enable=C0301
|
||||
"""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:
|
||||
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("Added {}".format(conn.get_id()))
|
||||
except GLib.Error: # pylint: disable=catching-non-exception
|
||||
try:
|
||||
notify("Connection to {} failed".format(conn.get_id()),
|
||||
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
|
||||
"""
|
||||
args = ["-u", urgency, message]
|
||||
if details is not None:
|
||||
args.append(details)
|
||||
|
||||
try:
|
||||
Popen(["notify-send"] + args,
|
||||
stdout=PIPE, stderr=PIPE).communicate()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
def run():
|
||||
"""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_modemmanager_installed()
|
||||
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()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
|
||||
# vim: set et ts=4 sw=4 :
|
||||
169
rofi/scripts/nordvpn.sh
Executable file
169
rofi/scripts/nordvpn.sh
Executable file
@@ -0,0 +1,169 @@
|
||||
#!/bin/sh
|
||||
# __ _ _
|
||||
# _ __ ___ / _(_) _ __ ___ _ __ __| |_ ___ __ _ __
|
||||
# | '__/ _ \| |_| |_____| '_ \ / _ \| '__/ _` \ \ / / '_ \| '_ \
|
||||
# | | | (_) | _| |_____| | | | (_) | | | (_| |\ V /| |_) | | | |
|
||||
# |_| \___/|_| |_| |_| |_|\___/|_| \__,_| \_/ | .__/|_| |_|
|
||||
# |_|
|
||||
|
||||
# exit when any command fails
|
||||
set -e
|
||||
|
||||
echoexit() {
|
||||
# Print to stderr and exit
|
||||
printf "%s\n" "$@" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Checking dependencies:
|
||||
whereis nordvpn > /dev/null || echoexit "'nordvpn' not found."
|
||||
|
||||
menu() {
|
||||
# Menu command, should read from stdin and write to stdout.
|
||||
|
||||
rofi -dmenu -i -p "Select" -no-custom
|
||||
}
|
||||
|
||||
usage() {
|
||||
printf "Dynamic menu interface for nordvpn.
|
||||
Usage:
|
||||
rofi-nordvpn [-h] [-s LABEL]
|
||||
-h Display this help message.
|
||||
-s Display current vpn status, useful for status bars.
|
||||
"
|
||||
}
|
||||
|
||||
init_menu() {
|
||||
# Initial menu.
|
||||
|
||||
local choices
|
||||
choices="connect\ndisconnect\nstatus\nsettings"
|
||||
printf "%b" "$choices" | menu
|
||||
}
|
||||
|
||||
connect() {
|
||||
# nordvpn connect options.
|
||||
|
||||
local choices
|
||||
choices="default\ncountries\ncities\np2p\nonion"
|
||||
printf "%b" "$choices" | menu
|
||||
}
|
||||
|
||||
countries() {
|
||||
# Country selection.
|
||||
|
||||
local choices
|
||||
choices="$(nordvpn countries | tr -d '\r,-' | tr -s "[:blank:]" "\n" | sed '/^\s*$/d' | sort)"
|
||||
printf "%s" "$choices" | menu
|
||||
}
|
||||
|
||||
cities() {
|
||||
# City selection.
|
||||
|
||||
local choices
|
||||
choices="$(nordvpn cities "$1" | tr -d '\r,-' | tr -s "[:blank:]" "\n" | sed '/^\s*$/d' | sort)"
|
||||
printf "%s" "$choices" | menu
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
# disconnect
|
||||
|
||||
nordvpn disconnect
|
||||
}
|
||||
|
||||
status() {
|
||||
# Echo the status.
|
||||
|
||||
local status
|
||||
status="$(nordvpn status | tr -d '\r -')"
|
||||
if [ -n "${status##*Connected*}" ]; then
|
||||
printf "Off"
|
||||
else
|
||||
printf "%s" "$(printf "%s" "$status" | grep "City" | cut -d ":" -f 2)"
|
||||
fi
|
||||
}
|
||||
|
||||
vpn_status() {
|
||||
# Show vpn status.
|
||||
|
||||
local choices
|
||||
choices="$(nordvpn status | tr -d '\r-' | sed 's/^ *//')"
|
||||
printf "%s" "$choices" | menu
|
||||
}
|
||||
|
||||
settings() {
|
||||
# Show vpn settings.
|
||||
|
||||
local choices
|
||||
choices="$(nordvpn settings | tr -d '\r-' | sed 's/^ *//')"
|
||||
printf "%s" "$choices" | menu
|
||||
}
|
||||
|
||||
# Parse options to the `rofi-nordvpn` command
|
||||
while getopts ":hs" opt; do
|
||||
case ${opt} in
|
||||
h )
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
s )
|
||||
status
|
||||
exit 0
|
||||
;;
|
||||
\? )
|
||||
printf "Invalid Option: -%s\n" "$OPTARG" 1>&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$(init_menu)" in
|
||||
"connect")
|
||||
case $(connect) in
|
||||
"default")
|
||||
nordvpn connect
|
||||
;;
|
||||
"countries")
|
||||
country="$(countries)"
|
||||
[ -n "$country" ] && nordvpn connect "$country"
|
||||
;;
|
||||
"cities")
|
||||
country="$(countries)"
|
||||
[ -n "$country" ] && city="$(cities "$country")"
|
||||
[ -n "$city" ] && nordvpn connect "$country" "$city"
|
||||
;;
|
||||
"p2p")
|
||||
nordvpn connect p2p
|
||||
;;
|
||||
"onion")
|
||||
nordvpn connect onion_over_vpn
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
"disconnect")
|
||||
disconnect
|
||||
;;
|
||||
"status")
|
||||
vpn_status
|
||||
;;
|
||||
"settings")
|
||||
settings
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
© 2021 GitHub, Inc.
|
||||
Terms
|
||||
Privacy
|
||||
Security
|
||||
Status
|
||||
Docs
|
||||
Contact GitHub
|
||||
Pricing
|
||||
API
|
||||
Training
|
||||
Blog
|
||||
About
|
||||
247
rofi/scripts/powermenu.sh
Executable file
247
rofi/scripts/powermenu.sh
Executable file
@@ -0,0 +1,247 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script defines just a mode for rofi instead of being a self-contained
|
||||
# executable that launches rofi by itself. This makes it more flexible than
|
||||
# running rofi inside this script as now the user can call rofi as one pleases.
|
||||
# For instance:
|
||||
#
|
||||
# rofi -show powermenu -modi powermenu:./rofi-power-menu
|
||||
#
|
||||
# See README.md for more information.
|
||||
|
||||
set -e
|
||||
set -u
|
||||
|
||||
# All supported choices
|
||||
all=(shutdown reboot suspend logout)
|
||||
|
||||
# By default, show all (i.e., just copy the array)
|
||||
show=("${all[@]}")
|
||||
|
||||
declare -A texts
|
||||
# texts[lockscreen]="lock screen"
|
||||
texts[switchuser]="switch user"
|
||||
texts[logout]="log out"
|
||||
texts[suspend]="suspend"
|
||||
# texts[hibernate]="hibernate"
|
||||
texts[reboot]="reboot"
|
||||
texts[shutdown]="shut down"
|
||||
|
||||
declare -A icons
|
||||
# icons[lockscreen]=""
|
||||
icons[switchuser]=" "
|
||||
icons[logout]=" "
|
||||
icons[suspend]=" "
|
||||
# icons[hibernate]=" "
|
||||
icons[reboot]=" "
|
||||
icons[shutdown]=" "
|
||||
icons[cancel]=" "
|
||||
|
||||
declare -A actions
|
||||
# actions[lockscreen]="loginctl lock-session ${XDG_SESSION_ID-}"
|
||||
# actions[lockscreen]="i3lock -d -u -e -c 000000 -i ~/Pictures/lock.png"
|
||||
#actions[switchuser]="???"
|
||||
actions[logout]="loginctl terminate-session ${XDG_SESSION_ID-}"
|
||||
actions[suspend]="systemctl suspend"
|
||||
# actions[hibernate]="systemctl hibernate"
|
||||
actions[reboot]="systemctl reboot"
|
||||
actions[shutdown]="systemctl poweroff"
|
||||
|
||||
# By default, ask for confirmation for actions that are irreversible
|
||||
confirmations=(reboot shutdown logout)
|
||||
|
||||
# By default, no dry run
|
||||
dryrun=false
|
||||
showsymbols=true
|
||||
|
||||
function check_valid {
|
||||
option="$1"
|
||||
shift 1
|
||||
for entry in "${@}"
|
||||
do
|
||||
if [ -z "${actions[$entry]+x}" ]
|
||||
then
|
||||
echo "Invalid choice in $1: $entry" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Parse command-line options
|
||||
parsed=$(getopt --options=h --longoptions=help,dry-run,confirm:,choices:,choose:,symbols,no-symbols --name "$0" -- "$@")
|
||||
if [ $? -ne 0 ]; then
|
||||
echo 'Terminating...' >&2
|
||||
exit 1
|
||||
fi
|
||||
eval set -- "$parsed"
|
||||
unset parsed
|
||||
while true; do
|
||||
case "$1" in
|
||||
"-h"|"--help")
|
||||
echo "rofi-power-menu - a power menu mode for Rofi"
|
||||
echo
|
||||
echo "Usage: rofi-power-menu [--choices CHOICES] [--confirm CHOICES]"
|
||||
echo " [--choose CHOICE] [--dry-run] [--symbols|--no-symbols]"
|
||||
echo
|
||||
echo "Use with Rofi in script mode. For instance, to ask for shutdown or reboot:"
|
||||
echo
|
||||
echo " rofi -show menu -modi \"menu:rofi-power-menu --choices=shutdown/reboot\""
|
||||
echo
|
||||
echo "Available options:"
|
||||
echo " --dry-run Don't perform the selected action but print it to stderr."
|
||||
echo " --choices CHOICES Show only the selected choices in the given order. Use / "
|
||||
echo " as the separator. Available choices are lockscreen, logout,"
|
||||
echo " suspend, hibernate, reboot and shutdown. By default, all"
|
||||
echo " available choices are shown."
|
||||
echo " --confirm CHOICES Require confirmation for the gives choices only. Use / as"
|
||||
echo " the separator. Available choices are lockscreen, logout,"
|
||||
echo " suspend, hibernate, reboot and shutdown. By default, only"
|
||||
echo " irreversible actions logout, reboot and shutdown require"
|
||||
echo " confirmation."
|
||||
echo " --choose CHOICE Preselect the given choice and only ask for a confirmation"
|
||||
echo " (if confirmation is set to be requested). It is strongly"
|
||||
echo " recommended to combine this option with --confirm=CHOICE"
|
||||
echo " if the choice wouldn't require confirmation by default."
|
||||
echo " Available choices are lockscreen, logout, suspend,"
|
||||
echo " hibernate, reboot and shutdown."
|
||||
echo " --[no-]symbols Show Unicode symbols or not. Requires a font with support"
|
||||
echo " for the symbols. Use, for instance, fonts from the"
|
||||
echo " Nerdfonts collection. By default, they are shown"
|
||||
echo " -h,--help Show this help text."
|
||||
exit 0
|
||||
;;
|
||||
"--dry-run")
|
||||
dryrun=true
|
||||
shift 1
|
||||
;;
|
||||
"--confirm")
|
||||
IFS='/' read -ra confirmations <<< "$2"
|
||||
check_valid "$1" "${confirmations[@]}"
|
||||
shift 2
|
||||
;;
|
||||
"--choices")
|
||||
IFS='/' read -ra show <<< "$2"
|
||||
check_valid "$1" "${show[@]}"
|
||||
shift 2
|
||||
;;
|
||||
"--choose")
|
||||
# Check that the choice is valid
|
||||
check_valid "$1" "$2"
|
||||
selectionID="$2"
|
||||
shift 2
|
||||
;;
|
||||
"--symbols")
|
||||
showsymbols=true
|
||||
shift 1
|
||||
;;
|
||||
"--no-symbols")
|
||||
showsymbols=false
|
||||
shift 1
|
||||
;;
|
||||
"--")
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "Internal error" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Define the messages after parsing the CLI options so that it is possible to
|
||||
# configure them in the future.
|
||||
|
||||
function write_message {
|
||||
icon="<span font_size=\"medium\">$1</span>"
|
||||
text="<span font_size=\"medium\">$2</span>"
|
||||
if [ "$showsymbols" = "true" ]
|
||||
then
|
||||
echo -n "\u200e$icon \u2068$text\u2069"
|
||||
else
|
||||
echo -n "$text"
|
||||
fi
|
||||
}
|
||||
|
||||
function print_selection {
|
||||
echo -e "$1" | $(read -r -d '' entry; echo "echo $entry")
|
||||
}
|
||||
|
||||
declare -A messages
|
||||
declare -A confirmationMessages
|
||||
for entry in "${all[@]}"
|
||||
do
|
||||
messages[$entry]=$(write_message "${icons[$entry]}" "${texts[$entry]^}")
|
||||
done
|
||||
for entry in "${all[@]}"
|
||||
do
|
||||
confirmationMessages[$entry]=$(write_message "${icons[$entry]}" "Yes")
|
||||
done
|
||||
confirmationMessages[cancel]=$(write_message "${icons[cancel]}" "No")
|
||||
|
||||
if [ $# -gt 0 ]
|
||||
then
|
||||
# If arguments given, use those as the selection
|
||||
selection="${@}"
|
||||
else
|
||||
# Otherwise, use the CLI passed choice if given
|
||||
if [ -n "${selectionID+x}" ]
|
||||
then
|
||||
selection="${messages[$selectionID]}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Don't allow custom entries
|
||||
echo -e "\0no-custom\x1ftrue"
|
||||
# Use markup
|
||||
echo -e "\0markup-rows\x1ftrue"
|
||||
|
||||
if [ -z "${selection+x}" ]
|
||||
then
|
||||
echo -e "\0prompt\x1f⏻"
|
||||
for entry in "${show[@]}"
|
||||
do
|
||||
echo -e "${messages[$entry]}\0icon\x1f${icons[$entry]}"
|
||||
done
|
||||
else
|
||||
for entry in "${show[@]}"
|
||||
do
|
||||
if [ "$selection" = "$(print_selection "${messages[$entry]}")" ]
|
||||
then
|
||||
# Check if the selected entry is listed in confirmation requirements
|
||||
for confirmation in "${confirmations[@]}"
|
||||
do
|
||||
if [ "$entry" = "$confirmation" ]
|
||||
then
|
||||
# Ask for confirmation
|
||||
echo -e "\0prompt\x1fAre you sure?"
|
||||
echo -e "${confirmationMessages[$entry]}\0icon\x1f${icons[$entry]}"
|
||||
echo -e "${confirmationMessages[cancel]}\0icon\x1f${icons[cancel]}"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
# If not, then no confirmation is required, so mark confirmed
|
||||
selection=$(print_selection "${confirmationMessages[$entry]}")
|
||||
fi
|
||||
if [ "$selection" = "$(print_selection "${confirmationMessages[$entry]}")" ]
|
||||
then
|
||||
if [ $dryrun = true ]
|
||||
then
|
||||
# Tell what would have been done
|
||||
echo "Selected: $entry" >&2
|
||||
else
|
||||
# Perform the action
|
||||
${actions[$entry]}
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
if [ "$selection" = "$(print_selection "${confirmationMessages[cancel]}")" ]
|
||||
then
|
||||
# Do nothing
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
# The selection didn't match anything, so raise an error
|
||||
echo "Invalid selection: $selection" >&2
|
||||
exit 1
|
||||
fi
|
||||
184
rofi/scripts/rofi-screenshot.sh
Executable file
184
rofi/scripts/rofi-screenshot.sh
Executable file
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
## rofi-screenshot
|
||||
## Author: ceuk @ github
|
||||
## Licence: WTFPL
|
||||
## Usage:
|
||||
## show the menu with rofi-screenshot
|
||||
## stop recording with rofi-screenshot -s
|
||||
|
||||
# Screenshot directory
|
||||
screenshot_directory="$HOME/Pictures/Screenshots"
|
||||
|
||||
# set ffmpeg defaults
|
||||
ffmpeg() {
|
||||
command ffmpeg -hide_banner -loglevel error -nostdin "$@"
|
||||
}
|
||||
|
||||
video_to_gif() {
|
||||
ffmpeg -i "$1" -vf palettegen -f image2 -c:v png - |
|
||||
ffmpeg -i "$1" -i - -filter_complex paletteuse "$2"
|
||||
}
|
||||
|
||||
countdown() {
|
||||
notify-send "Screenshot" "Recording in 3 seconds" -t 1000
|
||||
sleep 1
|
||||
notify-send "Screenshot" "Recording in 2 seconds" -t 1000
|
||||
sleep 1
|
||||
notify-send "Screenshot" "Recording in 1 seconds" -t 1000
|
||||
sleep 1
|
||||
}
|
||||
|
||||
crtc() {
|
||||
notify-send "Screenshot" "Select a region to capture"
|
||||
ffcast -q $(slop -n -f '-g %g ') png /tmp/screenshot_clip.png
|
||||
xclip -selection clipboard -t image/png /tmp/screenshot_clip.png
|
||||
rm /tmp/screenshot_clip.png
|
||||
notify-send "Screenshot" "Region copied to Clipboard"
|
||||
}
|
||||
|
||||
crtf() {
|
||||
notify-send "Screenshot" "Select a region to capture"
|
||||
dt=$(date '+%d-%m-%Y %H:%M:%S')
|
||||
ffcast -q $(slop -n -f '-g %g ') png "$screenshot_directory/$dt.png"
|
||||
notify-send "Screenshot" "Region saved to $screenshot_directory"
|
||||
}
|
||||
|
||||
cstc() {
|
||||
ffcast -q png /tmp/screenshot_clip.png
|
||||
xclip -selection clipboard -t image/png /tmp/screenshot_clip.png
|
||||
rm /tmp/screenshot_clip.png
|
||||
notify-send "Screenshot" "Screenshot copied to Clipboard"
|
||||
}
|
||||
|
||||
cstf() {
|
||||
dt=$(date '+%d-%m-%Y %H:%M:%S')
|
||||
ffcast -q png "$screenshot_directory/$dt.png"
|
||||
notify-send "Screenshot" "Screenshot saved to $screenshot_directory"
|
||||
}
|
||||
|
||||
rgrtf() {
|
||||
notify-send "Screenshot" "Select a region to record"
|
||||
dt=$(date '+%d-%m-%Y %H:%M:%S')
|
||||
ffcast -q $(slop -n -f '-g %g ' && countdown) rec /tmp/screenshot_gif.mp4
|
||||
notify-send "Screenshot" "Converting to gif... (this can take a while)"
|
||||
video_to_gif /tmp/screenshot_gif.mp4 "$screenshot_directory/$dt.gif"
|
||||
rm /tmp/screenshot_gif.mp4
|
||||
notify-send "Screenshot" "Recording saved to $screenshot_directory"
|
||||
}
|
||||
|
||||
rgstf() {
|
||||
countdown
|
||||
dt=$(date '+%d-%m-%Y %H:%M:%S')
|
||||
ffcast -q rec /tmp/screenshot_gif.mp4
|
||||
notify-send "Screenshot" "Converting to gif... (this can take a while)"
|
||||
video_to_gif /tmp/screenshot_gif.mp4 "$screenshot_directory/$dt.gif"
|
||||
rm /tmp/screenshot_gif.mp4
|
||||
notify-send "Screenshot" "Recording saved to $screenshot_directory"
|
||||
}
|
||||
|
||||
rvrtf() {
|
||||
notify-send "Screenshot" "Select a region to record"
|
||||
dt=$(date '+%d-%m-%Y %H:%M:%S')
|
||||
ffcast -q $(slop -n -f '-g %g ' && countdown) rec "$screenshot_directory/$dt.mp4"
|
||||
notify-send "Screenshot" "Recording saved to $screenshot_directory"
|
||||
}
|
||||
|
||||
rvstf() {
|
||||
countdown
|
||||
dt=$(date '+%d-%m-%Y %H:%M:%S')
|
||||
ffcast -q rec "$screenshot_directory/$dt.mp4"
|
||||
notify-send "Screenshot" "Recording saved to $screenshot_directory"
|
||||
}
|
||||
|
||||
get_options() {
|
||||
echo " Capture Region Clip"
|
||||
echo " Capture Region File"
|
||||
echo " Capture Screen Clip"
|
||||
echo " Capture Screen File"
|
||||
echo " Record Region File (GIF)"
|
||||
echo " Record Screen File (GIF)"
|
||||
echo " Record Region File (MP4)"
|
||||
echo " Record Screen File (MP4)"
|
||||
}
|
||||
|
||||
check_deps() {
|
||||
if ! hash $1 2>/dev/null; then
|
||||
echo "Error: This script requires $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
# check dependencies
|
||||
check_deps slop
|
||||
check_deps ffcast
|
||||
check_deps ffmpeg
|
||||
check_deps xclip
|
||||
check_deps rofi
|
||||
|
||||
if [[ $1 == '--help' ]] || [[ $1 = '-h' ]]
|
||||
then
|
||||
echo ### rofi-screenshot
|
||||
echo USAGE: rofi-screenshot [OPTION]
|
||||
echo \(no option\)
|
||||
echo " show the screenshot menu"
|
||||
echo -s, --stop
|
||||
echo " stop recording"
|
||||
echo -h, --help
|
||||
echo " this screen"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $1 = '--stop' ]] || [[ $1 = '-s' ]]
|
||||
then
|
||||
pkill -fxn '(/\S+)*ffmpeg\s.*\sx11grab\s.*'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get choice from rofi
|
||||
choice=$( (get_options) | rofi -dmenu -i -fuzzy -p "Screenshot" )
|
||||
|
||||
# If user has not picked anything, exit
|
||||
if [[ -z "${choice// }" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# run the selected command
|
||||
case $choice in
|
||||
' Capture Region Clip')
|
||||
crtc
|
||||
;;
|
||||
' Capture Region File')
|
||||
crtf
|
||||
;;
|
||||
' Capture Screen Clip')
|
||||
cstc
|
||||
;;
|
||||
' Capture Screen File')
|
||||
cstf
|
||||
;;
|
||||
' Record Region File (GIF)')
|
||||
rgrtf
|
||||
;;
|
||||
' Record Screen File (GIF)')
|
||||
rgstf
|
||||
;;
|
||||
' Record Region File (MP4)')
|
||||
rvrtf
|
||||
;;
|
||||
' Record Screen File (MP4)')
|
||||
rvstf
|
||||
;;
|
||||
esac
|
||||
|
||||
# done
|
||||
set -e
|
||||
}
|
||||
|
||||
main $1 &
|
||||
|
||||
exit 0
|
||||
|
||||
!/bin/bash
|
||||
|
||||
163
rofi/scripts/rofitr.sh
Executable file
163
rofi/scripts/rofitr.sh
Executable file
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (C) 2019 Yusuf Aktepe <yusuf@yusufaktepe.com>
|
||||
|
||||
## Set defaults
|
||||
SOURCE="ja" # source language code
|
||||
TARGET="en" # translation language code
|
||||
# ENGINE="google" # google, yandex or bing
|
||||
# LOCALE="en" # translator language ($LANG var. by default)
|
||||
# SPEAK_SOURCE="false" # speak the source (true/false)
|
||||
# SPEAK_TRANSLATION="false" # speak the translation (true/false)
|
||||
|
||||
## Rofi general options
|
||||
# leave these empty or comment out to use system defaults:
|
||||
# FONT="mono 12"
|
||||
WIDTH="50"
|
||||
LOCATION="0"
|
||||
# YOFFSET="-20"
|
||||
|
||||
# use alternative config and theme
|
||||
# CONFIG="~/.config/rofi/translate-config.rasi"
|
||||
# THEME="/usr/share/rofi/themes/lb.rasi"
|
||||
|
||||
## Rofi required options
|
||||
HIST_LINES="5" # lines to show for history mode
|
||||
RES_LINES="100" # limit output to screen height
|
||||
PROMPT_TR=" "
|
||||
PROMPT_HIST=" history"
|
||||
PROMPT_DEL=" delete"
|
||||
CLR_RESULT="#ffffff" # text color for translation
|
||||
CLR1="#5294e2"
|
||||
CLR2="#1d2021"
|
||||
|
||||
HIST="${XDG_DATA_HOME:-~/.local/share}/rofi/rofitr_history"
|
||||
mkdir -p "$(dirname "$HIST")"
|
||||
|
||||
help() {
|
||||
cat <<EOF
|
||||
# <u>Translate with defaults</u>
|
||||
> <span background="$CLR1" foreground="$CLR2"><b><i>text</i></b></span>
|
||||
# <u>Override default SOURCE and TARGET</u>
|
||||
> <span background="$CLR1" foreground="$CLR2"><b>en:es <i>text</i></b></span>
|
||||
# <u>Auto-detect SOURCE</u>
|
||||
> <span background="$CLR1" foreground="$CLR2"><b>:en <i>text</i></b></span>
|
||||
<s> </s>
|
||||
<u>Actions:</u>
|
||||
<span foreground="$CLR1"><b>!e</b></span> <i>word</i> => show examples for "word"
|
||||
<span foreground="$CLR1"><b>!s</b></span> <i>text</i> => speak the "text"
|
||||
<span foreground="$CLR1"><b>!!</b></span> => show last translation
|
||||
<span foreground="$CLR1"><b>!!e</b></span> => show examples for last translation
|
||||
<span foreground="$CLR1"><b>!!s</b></span> => speak last translation
|
||||
<span foreground="$CLR1"><b>!</b></span> => select and translate from history
|
||||
<span foreground="$CLR1"><b>!d</b></span> => select and remove from history
|
||||
<span foreground="$CLR1"><b>!dd</b></span> => clear history (in delete mode)
|
||||
<s> </s>
|
||||
<u>Command line:</u>
|
||||
$ rofitr :ru <i>text</i> <span foreground="$CLR1">=> </span>Translate into Russian
|
||||
$ rofitr -s <span foreground="$CLR1">=> </span>Translate from primary selection
|
||||
EOF
|
||||
}
|
||||
|
||||
rofi_cmd() {
|
||||
[ -n "$CONFIG" ] && params+=(-config "$CONFIG")
|
||||
[ -n "$THEME" ] && params+=(-theme "$THEME")
|
||||
[ -n "$WIDTH" ] && params+=(-width "$WIDTH")
|
||||
[ -n "$LOCATION" ] && params+=(-location "$LOCATION")
|
||||
[ -n "$YOFFSET" ] && params+=(-yoffset "$YOFFSET")
|
||||
[ -n "$FONT" ] && params+=(-font "$FONT")
|
||||
|
||||
rofi -dmenu -i "${params[@]}" "$@"
|
||||
}
|
||||
|
||||
crow_cmd() {
|
||||
[ -n "$ENGINE" ] && crowparams+=(-e "$ENGINE")
|
||||
[ -n "$LOCALE" ] && crowparams+=(-l "$LOCALE")
|
||||
[ "$SPEAK_TRANSLATION" = "true" ] && crowparams+=(-p)
|
||||
[ "$SPEAK_SOURCE" = "true" ] && crowparams+=(-u)
|
||||
|
||||
crow "${crowparams[@]}" "$@"
|
||||
}
|
||||
|
||||
format() {
|
||||
clean="s/^[\t]$//;/^$/d;s/\&/\&\;/g;s/</\<\;/g;s/>/\>\;/g;/ - translation options:$/d"
|
||||
bold="s/^\(auxiliary verb\|abbreviation\|adjective\|adverb\|article\|conjunction\|contraction\|exclamation\|interjection\|noun\|particle\|prefix\|preposition\|pronoun\|suffix\|symbol\|verb\)$/<b>&<\/b>/"
|
||||
trl="s/^\[ .*-\>\;.* \]$/<span foreground=\"$CLR1\"><i>&<\/i><\/span>/"
|
||||
case "$1" in
|
||||
ex) sed -e "$clean" -e "$bold" -ne '/- examples:/,$p' | head -n $RES_LINES ;;
|
||||
sel) sed -e "$clean" -e '/ - examples:$/d' -e "$bold" -e "$trl" | sed -z 's/\n*(/ (/' | head -n $RES_LINES ;;
|
||||
*) sed -e "$clean" -e '/- examples:$/Q' -e "$bold" -e "$trl" | sed -z 's/\n*(/ (/' | head -n $RES_LINES
|
||||
esac
|
||||
}
|
||||
|
||||
cklang() {
|
||||
lslang=(
|
||||
"af" "sq" "am" "ar" "hy" "az" "eu" "ba" "be" "bn" "bs" "bg" "ca" "yue" "ceb"
|
||||
"zh-CN" "zh-TW" "co" "hr" "cs" "da" "nl" "en" "eo" "et" "fj" "fil" "fi" "fr"
|
||||
"fy" "gl" "ka" "de" "el" "gu" "ht" "ha" "haw" "he" "mrj" "hi" "hmn" "hu" "is"
|
||||
"ig" "id" "ga" "it" "ja" "jw" "kn" "kk" "km" "tlh" "tlh-Qaak" "ko" "ku" "ky" "lo"
|
||||
"la" "lv" "apc" "lt" "lb" "mk" "mg" "ms" "ml" "mt" "mi" "mr" "mhr" "mn" "my" "ne"
|
||||
"no" "ny" "pap" "ps" "fa" "pl" "pt" "pa" "otq" "ro" "ru" "sm" "gd" "sr-Cyrl"
|
||||
"sr-Latin" "st" "sn" "sd" "si" "sk" "sl" "so" "es" "su" "sw" "sv" "tl" "ty" "tg"
|
||||
"ta" "tt" "te" "th" "to" "tr" "udm" "uk" "ur" "uz" "vi" "cy" "xh" "yi" "yo" "yua"
|
||||
)
|
||||
printf '%s\n' "${lslang[@]}" | grep -x "$1"
|
||||
}
|
||||
|
||||
esc() { sed 's|[][\\/.*^$]|\\&|g' <<< "$1" ;}
|
||||
|
||||
append_hist() {
|
||||
sed -i "/^$(esc "$input")$/d" "$HIST"
|
||||
printf '%s\n' "$input" >> "$HIST"
|
||||
}
|
||||
|
||||
return_result() {
|
||||
input="$(rofi_cmd -p "$PROMPT_TR" -l 0 -mesg "<span color='$CLR_RESULT'>$result</span>")"
|
||||
}
|
||||
|
||||
[ -n "$*" ] && input="$*" || input="$(rofi_cmd -p "$PROMPT_TR" -l 0)"
|
||||
|
||||
while [ -n "$input" ]; do
|
||||
case "$input" in
|
||||
"?") input="$(rofi_cmd -p "Usage" -l 0 -mesg "<span color='$CLR_RESULT'>$(help)</span>")" ;;
|
||||
!) input="$(tac "$HIST" | rofi_cmd -p "$PROMPT_HIST" -l "$HIST_LINES")"
|
||||
[ "$input" = "!" ] && input="$(rofi_cmd -p "$PROMPT_TR" -l 0)" ;;
|
||||
!!) input=$(tail -n1 "$HIST") ;;
|
||||
!!e) input=$(printf "!e %s" "$(tail -n1 "$HIST")") ;;
|
||||
!!s) input=$(printf "!s %s" "$(tail -n1 "$HIST")") ;;
|
||||
!d) pattern="$(tac "$HIST" | rofi_cmd -p "$PROMPT_DEL" -l "$HIST_LINES")"
|
||||
case "$pattern" in
|
||||
!) input="$(rofi_cmd -p "$PROMPT_TR" -l 0)" ;;
|
||||
!dd) printf '' > "$HIST" && exit 0 ;;
|
||||
"") exit 0 ;;
|
||||
*) sed -i "/^$(esc "$pattern")$/d" "$HIST"
|
||||
esac ;;
|
||||
??*:??*)
|
||||
SOURCE=$(cklang "$(echo "$input" | awk -F':' '{print $1}')")
|
||||
TARGET=$(cklang "$(echo "$input" | awk -F'[:| ]' '{print $2}')")
|
||||
input=$(echo "$input" | cut -d' ' -f2-)
|
||||
result=$(crow_cmd -s "${SOURCE:-en}" -t "${TARGET:-tr}" -- "$input" | format)
|
||||
unset SOURCE TARGET
|
||||
append_hist; return_result ;;
|
||||
:??*)
|
||||
TARGET=$(cklang "$(echo "$input" | awk -F'[:| ]' '{print $2}')")
|
||||
input=$(echo "$input" | cut -d' ' -f2-)
|
||||
result=$(crow_cmd -t "${TARGET:-tr}" -- "$input" | format)
|
||||
unset TARGET
|
||||
append_hist; return_result ;;
|
||||
!e*)
|
||||
input=$(echo "$input" | cut -d' ' -f2-)
|
||||
result=$(crow_cmd -- "$input" | format ex)
|
||||
append_hist; return_result ;;
|
||||
!s*)
|
||||
input=$(echo "$input" | cut -d' ' -f2-)
|
||||
result=$(SPEAK_SOURCE="true" crow_cmd -- "$input" | format sel)
|
||||
append_hist; return_result ;;
|
||||
-s)
|
||||
input=$(xclip -o | tr -d '\n'); [ -z "$input" ] && exit
|
||||
result=$(crow_cmd -t "${TARGET:-tr}" -- "$input" | format)
|
||||
append_hist; return_result ;;
|
||||
*)
|
||||
result=$(crow_cmd -s "${SOURCE:-en}" -t "${TARGET:-tr}" -- "$input" | format)
|
||||
append_hist; return_result ;;
|
||||
esac
|
||||
done
|
||||
12
rofi/scripts/scholar-search.sh
Executable file
12
rofi/scripts/scholar-search.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
query=$( (echo ) | rofi -dmenu -i -lines 0 -matching fuzzy -location 0 -p " " )
|
||||
|
||||
if [[ -n "$query" ]]; then
|
||||
url=https://scholar.google.com/scholar?q=$query
|
||||
xdg-open "$url"
|
||||
else
|
||||
exit
|
||||
fi
|
||||
|
||||
exit 0
|
||||
3
rofi/scripts/symbol-finder/rofi_symbol_finder.sh
Executable file
3
rofi/scripts/symbol-finder/rofi_symbol_finder.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
cat $(dirname $0)/symbol-list.txt | rofi -dmenu -i -p "Icon" | cut -f 1 | xclip -selection clipboard
|
||||
62
rofi/scripts/symbol-finder/symbol-list.txt
Normal file
62
rofi/scripts/symbol-finder/symbol-list.txt
Normal file
@@ -0,0 +1,62 @@
|
||||
α alpha
|
||||
β beta
|
||||
γ gamma
|
||||
δ delta
|
||||
ε epsilon
|
||||
ζ zeta
|
||||
η eta
|
||||
θ theta
|
||||
ι iota
|
||||
κ kappa
|
||||
λ lambda
|
||||
μ mu
|
||||
ν nu
|
||||
ξ xi
|
||||
ο omicron
|
||||
π pi
|
||||
ρ rho
|
||||
σ sigma
|
||||
τ tau
|
||||
υ upsilon
|
||||
φ phi
|
||||
χ chi
|
||||
ψ psi
|
||||
ω omega
|
||||
Α Alpha
|
||||
Β Beta
|
||||
Γ Gamma
|
||||
Δ Delta
|
||||
Ε Epsilon
|
||||
Ζ Zeta
|
||||
Η Eta
|
||||
Θ Theta
|
||||
Ι Iota
|
||||
Κ Kappa
|
||||
Λ Lambda
|
||||
Μ Mu
|
||||
Ν Nu
|
||||
Ξ Xi
|
||||
Ο Omicron
|
||||
Π Pi
|
||||
Ρ Rho
|
||||
Σ Sigma
|
||||
Τ Tau
|
||||
Υ Upsilon
|
||||
Φ Phi
|
||||
Χ Chi
|
||||
Ψ Psi
|
||||
Ω Omega
|
||||
|
||||
~
|
||||
=
|
||||
≠
|
||||
≈
|
||||
>
|
||||
<
|
||||
≥
|
||||
≤
|
||||
±
|
||||
×
|
||||
·
|
||||
÷
|
||||
—
|
||||
63
rofi/scripts/virtual-machines.sh
Executable file
63
rofi/scripts/virtual-machines.sh
Executable file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# rofi-virtualbox: manage virtualbox machines with rofi
|
||||
# Originally by Oliver Kraitschy <okraits@arcor.de> (https://github.com/okraits/rofi-tools)
|
||||
# With modifications by Alexander Pushkov <hey@ale.sh>
|
||||
|
||||
OPTIONS="Start machine\nStart headless\nSend ACPI shutdown signal\nClone machine\nDelete machine"
|
||||
|
||||
kb_start="Control-Return"
|
||||
|
||||
args=(
|
||||
-dmenu
|
||||
-kb-custom-1 "${kb_start}"
|
||||
-kb-accept-custom ""
|
||||
)
|
||||
|
||||
while true; do
|
||||
# select machine to control
|
||||
vm=$(vboxmanage list vms | awk -F '"' '{print $2}' | rofi "${args[@]}" 'Select machine:')
|
||||
rofi_exit=$?
|
||||
if [[ $rofi_exit -eq 1 ]]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
case "${rofi_exit}" in
|
||||
0) # default
|
||||
# just pass through to the menu
|
||||
;;
|
||||
10) # -kb-custom-1
|
||||
vboxmanage startvm "$vm"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
|
||||
# select action to be executed
|
||||
option=$(echo -e $OPTIONS | rofi "${args[@]}" 'Select action:')
|
||||
rofi_exit=$?
|
||||
if [[ $rofi_exit -eq 1 ]]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
case "$option" in
|
||||
"Start machine")
|
||||
vboxmanage startvm "$vm"
|
||||
exit
|
||||
;;
|
||||
"Start headless")
|
||||
vboxmanage startvm "$vm" --type headless
|
||||
;;
|
||||
"Send ACPI shutdown signal")
|
||||
vboxmanage controlvm "$vm" acpipowerbutton --type headless
|
||||
;;
|
||||
"Clone machine")
|
||||
vboxmanage clonevm "$vm" --mode machine --register --options keepallmacs
|
||||
;;
|
||||
"Delete machine")
|
||||
vboxmanage unregistervm "$vm" --delete
|
||||
;;
|
||||
*)
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
14
rofi/scripts/web-search.sh
Executable file
14
rofi/scripts/web-search.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
query=$( (echo ) | rofi -dmenu -i -lines 0 -matching fuzzy -location 0 -p " " )
|
||||
|
||||
if [[ -n "$query" ]]; then
|
||||
# https://scholar.google.com/scholar?hl=en&q=
|
||||
# url=https://www.duckduckgo.com/?q=$query
|
||||
url=https://search.brave.com/search?q=$query
|
||||
xdg-open "$url"
|
||||
else
|
||||
exit
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user