diff --git a/files/.local/bin/.gitignore b/files/.local/bin/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/files/.local/bin/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/files/.local/bin/apod b/files/.local/bin/apod new file mode 100755 index 0000000..38da388 --- /dev/null +++ b/files/.local/bin/apod @@ -0,0 +1,27 @@ +#!/bin/bash -e + +api_response="$(curl -sS https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY)" +filename="$HOME/Pictures/apod/$(echo -n $api_response | jq --join-output '.date+" "+.title').jpg" +wallpaper_filename="$HOME/Pictures/wallpaper" +url="$(echo -n $api_response | jq --raw-output '.hdurl')" +screen_size="$(xrandr --screen 0 | grep '^Screen' | grep -Eo 'current [0-9]+ x [0-9]+' | sed -E 's/current ([0-9]+) x ([0-9]+)/\1x\2/g')" + +mkdir -p "$(dirname $filename)" + +if [[ -f "$filename" ]]; then + echo "Image already downloaded" +else + curl -sS -o "$filename" "$url" +fi + +if [[ "$1" == "--wallpaper" ]]; then + [[ -f "$wallpaper_filename" ]] && rm "$wallpaper_filename" + img_size="$(identify "$filename" | grep -Eo '[0-9]+x[0-9]+' | head -n 1)" + if [[ "$(echo "$img_size" | grep -Eo '^[0-9]+')" -lt "$(echo "$screen_size" | grep -Eo '^[0-9]+')" + || "$(echo "$img_size" | grep -Eo '[0-9]+$')" -lt "$(echo "$screen_size" | grep -Eo '[0-9]+$')" ]]; then + convert "$filename" -filter Lanczos -gravity center -unsharp 0x1 -resize "$screen_size^" "$wallpaper_filename" + else + ln -s "$filename" "$wallpaper_filename" + fi + feh --bg-fill "$wallpaper_filename" +fi diff --git a/files/.local/bin/brightness-control b/files/.local/bin/brightness-control new file mode 100755 index 0000000..9017170 --- /dev/null +++ b/files/.local/bin/brightness-control @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# You can call this script like this: +# $ ./brightnessControl.sh up +# $ ./brightnessControl.sh down + +# Script inspired by these wonderful people: +# https://github.com/dastorm/volume-notification-dunst/blob/master/volume.sh +# https://gist.github.com/sebastiencs/5d7227f388d93374cebdf72e783fbd6a + +function get_brightness { + brightnessctl -m | grep -o '[0-9]\+%' | head -c-2 +} + +function send_notification { + icon="notification-display-brightness" + echo $(get_brightness) >"$XDG_RUNTIME_DIR/wobpipe" +} + +case $1 in + up) + # increase the backlight by 5% + brightnessctl set +5% + send_notification + ;; + down) + if [[ $(get_brightness) -lt 5 ]]; then + # avoid 0% brightness + brightnessctl set 1% + else + # decrease the backlight by 5% + brightnessctl set 5%- + fi + send_notification + ;; +esac diff --git a/files/.local/bin/lsiommu b/files/.local/bin/lsiommu new file mode 100755 index 0000000..9f293d6 --- /dev/null +++ b/files/.local/bin/lsiommu @@ -0,0 +1,8 @@ +#!/bin/bash +shopt -s nullglob +for d in /sys/kernel/iommu_groups/*/devices/*; do + n=${d#*/iommu_groups/*}; n=${n%%/*} + printf 'IOMMU Group %s ' "$n" + lspci -nns "${d##*/}" +done; + diff --git a/files/.local/bin/motd b/files/.local/bin/motd new file mode 100755 index 0000000..e452b05 --- /dev/null +++ b/files/.local/bin/motd @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 + +import math +import shutil +import sys +import os +import re + +from subprocess import check_call, DEVNULL, CalledProcessError + +try: + import psutil +except: + pass + +try: + from pyfiglet import figlet_format +except ImportError: + def figlet_format(string, *args, **kwargs): + return string + '\n' + +term_r, term_c = os.popen('stty size', 'r').read().split() + +class Termutils: + BLACK = '\33[30m' + RED = '\33[31m' + GREEN = '\33[32m' + YELLOW = '\33[33m' + BLUE = '\33[34m' + MAGENTA = '\33[35m' + CYAN = '\33[36m' + WHITE = '\33[37m' + GREY = '\33[30;1m' + DISABLE = '\033[0m' + + HOSTNAME_COLOR = '\033[38;5;%sm' % os.environ.get('ZSH_THEME_HOSTNAME_COLOR', '004') + + PADDING = ' ' + + @staticmethod + def print_padded(*arg, **kwarg): + print(Termutils.PADDING, end='') + print(*arg, **kwarg) + + @staticmethod + def print_progress_bar(percent, label='', length=60): + percent_color = Termutils.GREEN + bar = '[' + for i in range(0, length): + if i >= math.ceil(percent * length): + bar += Termutils.GREY + elif i == 0: + bar += Termutils.GREEN + elif i == math.ceil(length * 0.6): + bar += Termutils.YELLOW + percent_color = Termutils.YELLOW + elif i == math.ceil(length * 0.9): + bar += Termutils.RED + percent_color = Termutils.RED + + bar += '=' + bar += '{}]{}{:7.2%}{} {}'.format( + Termutils.DISABLE, + percent_color, + percent, + Termutils.DISABLE, + label + ) + Termutils.print_padded(bar) + +def to_gb(bytes_count): + return bytes_count/1024/1024/1024 + +def print_hostname(): + print( + Termutils.HOSTNAME_COLOR + + figlet_format(os.uname()[1], 'slant') + + Termutils.DISABLE + + '\n' + + os.popen('uptime').read().strip() + + '\n' + ) + +def print_services(): + if 'MOTD_SERVICES' in os.environ: + print('Services:') + services = list(set(os.environ['MOTD_SERVICES'].split())) + services.sort() + for service in services: + Termutils.print_padded('{:<15.15}'.format(service), end='') + try: + check_call(['systemctl', 'is-active', service], stdout=DEVNULL, stderr=DEVNULL) + print('{} ● active{}'.format(Termutils.GREEN, Termutils.DISABLE)) + except CalledProcessError: + print('{} ● inactive{}'.format(Termutils.RED, Termutils.DISABLE)) + print('') + + +def print_mem(): + try: + memory = psutil.virtual_memory() + print('{:<18} {:6.2f}G used {:6.2f}G free {:6.2f}G total'.format( + 'Memory:', + to_gb(memory.used), + to_gb(memory.free), + to_gb(memory.total) + )) + Termutils.print_progress_bar(memory.used / memory.total) + print('') + except NameError: + pass + + +def print_fs(): + print('Filesystems:') + with open('/etc/mtab') as mtab: + for line in mtab: + line = line.strip() + if line.startswith('/'): + mount_point = line.split()[1] + usage = shutil.disk_usage(mount_point) + Termutils.print_padded('{:<15.15} {:7.2f}G used {:7.2f}G free {:7.2f}G total'.format( + mount_point, + to_gb(usage.used), + to_gb(usage.free), + to_gb(usage.total) + )) + Termutils.print_progress_bar( + percent=(usage.used / usage.total) + ) + print('') + +def print_tmux(): + try: + check_call(['tmux', 'info'], stdout=DEVNULL, stderr=DEVNULL) + print('tmux:') + tmux_session = os.popen('tmux display-message -p "#S"', 'r').read().strip() + with os.popen('tmux list-sessions', 'r') as tmux_ls: + for line in tmux_ls: + line = line.strip() + if line.startswith(tmux_session) and 'TMUX' in os.environ: + print(Termutils.GREEN, end='') + Termutils.print_padded( + line + + Termutils.DISABLE) + print('') + except CalledProcessError: + pass + + +print_hostname() +print_services() +print_mem() +print_fs() +print_tmux() diff --git a/files/.local/bin/sshot b/files/.local/bin/sshot new file mode 100755 index 0000000..e5523ee --- /dev/null +++ b/files/.local/bin/sshot @@ -0,0 +1,41 @@ +#!/bin/bash + +## sshot - Take a screenshot + +if [[ "$1" == "--partial" ]]; then + ARG_PARTIAL=1 +elif [[ "$1" == "--pin" ]]; then + ARG_PARTIAL=1 + ARG_PIN=1 +elif [[ "$1" == "--window" ]]; then + ARG_WINDOW=1 +fi + +file="$HOME/Pictures/screenshots/$(date --iso-8601=seconds).png" +mkdir -p "$(dirname $file)" 2>/dev/null + +if [[ -n "$ARG_PARTIAL" ]]; then + # prompt the user for the area to take a screenshot from + geometry="$(slurp)" + if [ -z "$geometry" ]; then + exit 1 + fi +elif [[ -n $ARG_WINDOW ]]; then + # get the active window geometry + geometry="$(get-focused-window-geometry)" +fi + +# take a screenshot +grim -g "$geometry" -- "$file" + +# place to image in the clipboard +wl-copy < "$file" + +if [[ -n "$ARG_PIN" ]]; then + # pin the screenshot + feh --title "Pinned screenshot" --geometry "$(echo -n "$geometry" | sed -E 's/([0-9]+),([0-9]+) ([0-9]+)x([0-9]+)/\3x\4+\1+\2/')" "$file" & +fi + +# print the file name +echo "$file" + diff --git a/files/.local/bin/sway b/files/.local/bin/sway new file mode 100755 index 0000000..ec2d24c --- /dev/null +++ b/files/.local/bin/sway @@ -0,0 +1,64 @@ +#!/bin/bash + +import-gsettings() { + # usage: import-gsettings : : ... + expression="" + for pair in "$@"; do + IFS=:; set -- $pair + expressions="$expressions -e 's:^$2=(.*)$:gsettings set org.gnome.desktop.interface $1 \1:e'" + done + IFS= + eval sed -E $expressions "${XDG_CONFIG_HOME:-$HOME/.config}"/gtk-3.0/settings.ini >/dev/null +} + +export XDG_CURRENT_DESKTOP=sway +export XDG_SESSION_TYPE=wayland + +# Qt +#export DESKTOP_SESSION=gnome +export QT_STYLE_OVERRIDE=gtk +export QT_QPA_PLATFORMTHEME=gtk2 +export QT_AUTO_SCREEN_SCALE_FACTOR=0 +export QT_QPA_PLATFORM=wayland-egl +export QT_WAYLAND_DISABLE_WINDOWDECORATION=1 + +# GTK +export CLUTTER_BACKEND=wayland +export GTK_CSD=0 +if [[ -f /usr/lib/libgtk3-nocsd.so.0 ]]; then + export LD_PRELOAD=/usr/lib/libgtk3-nocsd.so.0 +fi +import-gsettings \ + gtk-theme:gtk-theme-name \ + icon-theme:gtk-icon-theme-name \ + cursor-theme:gtk-cursor-theme-name + +# Elementary/EFL +export ECORE_EVAS_ENGINE=wayland_egl +export ELM_ENGINE=wayland_egl + +# SDL +export SDL_VIDEODRIVER=wayland + +# Java +export _JAVA_AWT_WM_NONREPARENTING=1 + +# Firefox +export MOZ_ENABLE_WAYLAND=1 + +# wlroot rdp +wlroot_rdp_cache="$HOME/.cache/wlr-rdp" +wlroot_rdp_cert="$wlroot_rdp_cache/tls.crt" +wlroot_rdp_key="$wlroot_rdp_cache/tls.key" +wlroot_rdp_csr="$wlroot_rdp_cache/tls.csr" +mkdir "$wlroot_rdp_cache" 2>/dev/null +if [[ ! -e "$wlroot_rdp_cert" ]] || [[ ! -e "$wlroot_rdp_key" ]]; then + openssl genrsa -out "$wlroot_rdp_key" 2048 + openssl req -new -key "$wlroot_rdp_key" -out "$wlroot_rdp_csr" + openssl x509 -req -days 365 -signkey "$wlroot_rdp_key" -in "$wlroot_rdp_csr" -out "$wlroot_rdp_cert" +fi + +# start sway +WLR_RDP_TLS_CERT_PATH="$wlroot_rdp_cert" \ +WLR_RDP_TLS_KEY_PATH="$wlroot_rdp_key" \ +exec /usr/bin/sway $@ diff --git a/files/.local/bin/sway-focused-window-geometry b/files/.local/bin/sway-focused-window-geometry new file mode 100755 index 0000000..9345feb --- /dev/null +++ b/files/.local/bin/sway-focused-window-geometry @@ -0,0 +1,3 @@ +#!/bin/bash + +get-focused-window-properties | jq --raw-output '.rect|((.x|tostring)+","+(.y|tostring)+" "+(.width|tostring)+"x"+(.height|tostring))' diff --git a/files/.local/bin/sway-focused-window-properties b/files/.local/bin/sway-focused-window-properties new file mode 100755 index 0000000..c42ac84 --- /dev/null +++ b/files/.local/bin/sway-focused-window-properties @@ -0,0 +1,3 @@ +#!/bin/bash + +swaymsg -t get_tree | jq --raw-output '..|select(.focused? == true)' diff --git a/files/.local/bin/to7zip b/files/.local/bin/to7zip new file mode 100755 index 0000000..48be5c2 --- /dev/null +++ b/files/.local/bin/to7zip @@ -0,0 +1,154 @@ +#!/bin/bash + +print_help() { + echo "" + echo "Convert an archive from one format to another" + echo "" + echo "Usage: to7zip [options] ...files" + echo "" + echo "Options:" + echo " -h, --help Print this help text" + echo " -d, --delete-old Delete converted file" + echo " -f, --force Replace the file with the converted file even if the file exists" + echo " -r, --rezip Extract and archive again even if the converted file is already a 7zip archive" + echo " -F, --format The format of the output" + echo " -t, --thread Set the number of threads for archive extraction and creation (default: $thread_count)" + echo "" +} + +short_args=hdfrF:t: +long_args=help,delete-old,force,rezip,format:,thread: + +# default +rezip= +delete_old= +force= +zip_args= +thread_count=8 +params="" + +# Parse arguments +parsed_args=$(getopt --options $short_args --longoptions $long_args --name "$0" -- "$@") +if [[ $? -ne 0 ]]; then + echo "Failed to parse arguments" + exit 2 +fi +eval set -- "$parsed_args" +while true; do + case "$1" in + -h|--help) + print_help + exit 0 + ;; + -d|--delete-old) + delete_old=y + shift + ;; + -f|--force) + force=y + shift + ;; + -r|--rezip) + rezip=y + shift + ;; + -F|--format) + case "$2" in + 7z) + ;; + zip) + zip_args="-tzip" + output_ext="zip" + ;; + *) + echo "Unknown format: $2" + exit 1 + esac + shift 2 + ;; + -t|--thread) + thread_count="$2" + shift 2 + ;; + --) + shift + break + ;; + *) + break + ;; + esac +done + +out_dir="$PWD" + +if [[ -z "$zip_args" ]]; then + zip_args="-t7z -m0=lzma2 -ms=on" + output_ext=7z +fi + +for f in "$@"; do + tmp_dir="$(mktemp -d "$PWD/to7zip.XXX")" + output="$(echo "$f" | sed -E 's/\.(7z|tar\.gz|zip|rar)$//g').$output_ext" + backup_file="$output".backup + do_backup= + + if [[ ! -r "$f" ]]; then + echo "$f : No such file or directory" + continue + fi + + echo "$f -> $output" + + if [[ -f "$output" ]]; then + if [[ -n "$force" ]] || [[ "$f" =~ .7z$ && -n "$rezip" ]]; then + do_backup=y + else + echo "$output : file exists, refusing to continue (-f to force)" + continue + fi + fi + + if [[ "$f" =~ \.tar[^/]*$ ]]; then + if ! tar -xf "$f" -C "$tmp_dir" >/dev/null; then + echo "$f : extract failed" + continue + fi + elif [[ "$f" =~ \.tar|gz|bz2|zip|7z$ ]]; then + if ! 7z e -mmt="$thread_count" -o"$tmp_dir" "$f" >/dev/null; then + echo "$f : extract failed" + continue + fi + elif [[ "$f" =~ \.rar$ ]]; then + if ! unrar x "$f" "$tmp_dir" >/dev/null; then + echo "$f : unrar failed" + continue + fi + else + cp -r "$f" "$tmp_dir" + fi + + if [[ -n "$do_backup" ]]; then + mv "$output" "$backup_file" + fi + + pushd "$tmp_dir" >/dev/null + 7z a $zip_args -mx=9 -mmt="$thread_count" "$out_dir/$output" >/dev/null + return_val=$? + popd >/dev/null + if [[ $return_val -eq 0 ]]; then + if [[ -n "$do_backup" ]]; then + rm -r "$backup_file" + elif [[ -n "$delete_old" ]]; then + rm -r "$f" + fi + else + if [[ -n "$do_backup" ]]; then + mv "$backup_file" "$output" + fi + echo "$f : 7z archive failed" + fi + + rm -r "$tmp_dir" +done + diff --git a/files/.local/bin/until-success b/files/.local/bin/until-success new file mode 100755 index 0000000..db09a68 --- /dev/null +++ b/files/.local/bin/until-success @@ -0,0 +1,16 @@ +#!/bin/bash + +function stop() { + echo "Stopping" + exit 0 +} + +while true; do + echo "Starting $1" + trap - SIGINT + $@ + trap stop SIGINT + echo -e "\nPress enter to restart $1..." + read +done + diff --git a/files/.local/bin/virt-usb b/files/.local/bin/virt-usb new file mode 100755 index 0000000..a9f3c01 --- /dev/null +++ b/files/.local/bin/virt-usb @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK + +import argparse +import logging +import re +import subprocess +import tempfile + +def main(**kwargs): + if kwargs.get('detach'): + virsh_subcommand = 'detach-device' + virsh_action = 'detach' + else: + virsh_subcommand = 'attach-device' + virsh_action = 'attach' + + for device in kwargs.get('device_ids'): + bus, dev = device.split(':') + with tempfile.NamedTemporaryFile('w+') as f: + f.write(f""" + + + + + + + """) + f.flush() + logging.info('%s %s',virsh_action, device) + subprocess.run(['virsh', virsh_subcommand, kwargs.get('domain'), f.name, '--live'], check=False) + +def regex_arg_type(regex): + pat = re.compile(regex) + def regex_type(value): + if pat.match(value): + return value + else: + raise argparse.ArgumentTypeError('argument "%s" must match regex "%s"' % (value, regex)) + return regex_type + +def get_libvirt_domains(): + sp = subprocess.run(['virsh', 'list', '--name'], capture_output=True, encoding='utf8', check=True) + for line in sp.stdout.split('\n'): + if line: + yield line.strip() + +def get_usb_devices(): + sp = subprocess.run(['lsusb'], capture_output=True, encoding='utf8', check=True) + for line in sp.stdout.split('\n'): + if line: + yield line.split()[5] + +if __name__ == '__main__': + logging.basicConfig(format='%(message)s', level=logging.INFO) + parser = argparse.ArgumentParser() + action_group = parser.add_mutually_exclusive_group() + action_group.add_argument('--attach', + action='store_true', + help='attach the usb devices (default)') + action_group.add_argument('--detach', + action='store_true', + help='detach the usb devices') + parser.add_argument('-d', '--domain', + choices=list(get_libvirt_domains()), + default=next(get_libvirt_domains(), None), + help='the libvirt domain on which to attach/detach the usb devices (default: %(default)s)') + parser.add_argument('device_ids', + metavar='device_ids', + nargs='+', + choices=list(get_usb_devices()), + help='a list of usb vendor:product ids in hexadecimal to attach/detach to the vm') + try: + import argcomplete + argcomplete.autocomplete(parser) + except ImportError: + pass + + args = parser.parse_args() + main(**args.__dict__) diff --git a/files/.local/bin/volume-control b/files/.local/bin/volume-control new file mode 100755 index 0000000..d6864b7 --- /dev/null +++ b/files/.local/bin/volume-control @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# You can call this script like this: +# $ ./volumeControl.sh up +# $ ./volumeControl.sh down +# $ ./volumeControl.sh mute + +# Script modified from these wonderful people: +# https://github.com/dastorm/volume-notification-dunst/blob/master/volume.sh +# https://gist.github.com/sebastiencs/5d7227f388d93374cebdf72e783fbd6a + +function get_volume { + amixer get Master | grep '%' | head -n 1 | cut -d '[' -f 2 | cut -d '%' -f 1 +} + +function is_mute { + amixer get Master | grep '%' | grep -oE '[^ ]+$' | grep off > /dev/null +} + +function send_notification { + iconSound="notification-audio-volume-high" + iconMuted="notification-audio-volume-muted" + if is_mute ; then + echo 0 >"$XDG_RUNTIME_DIR/wobpipe" + else + echo "$(get_volume)" >"$XDG_RUNTIME_DIR/wobpipe" + fi +} + +case $1 in + up) + # set the volume on (if it was muted) + amixer -D pulse set Master on > /dev/null + # up the volume (+ 5%) + amixer -D pulse sset Master 5%+ > /dev/null + send_notification + ;; + down) + amixer -D pulse set Master on > /dev/null + amixer -D pulse sset Master 5%- > /dev/null + send_notification + ;; + mute) + # toggle mute + amixer -D pulse set Master 1+ toggle > /dev/null + send_notification + ;; +esac diff --git a/files/.local/bin/wob b/files/.local/bin/wob new file mode 100755 index 0000000..a6b1d40 --- /dev/null +++ b/files/.local/bin/wob @@ -0,0 +1,4 @@ +#!/bin/bash + +mkfifo "$XDG_RUNTIME_DIR/wobpipe" +tail -f "$XDG_RUNTIME_DIR/wobpipe" | /bin/wob