From 553cb2204b0bf27afe13c6332f5679bbd47172a0 Mon Sep 17 00:00:00 2001 From: srdusr Date: Wed, 24 Sep 2025 05:01:20 +0200 Subject: Update/Overhaul --- unix/sys/battery_alert.sh | 43 ++ unix/sys/gsettings.sh | 40 ++ unix/sys/keep_guest_awake.sh | 84 +++ unix/sys/low-bat-notifier | 79 +++ unix/sys/session_manager.sh | 72 +++ unix/sys/shutdown_watchdog | 11 + unix/utils/backlight_default.sh | 32 + unix/utils/colors.sh | 78 +++ unix/utils/cryptocheck | 31 + unix/utils/cryptonotify | 19 + unix/utils/disable_meta_key_guest.sh | 94 +++ unix/utils/heads-up-display | 88 +++ unix/utils/hex2rgb.sh | 3 + unix/utils/kill-notify | 4 + unix/utils/kill-process | 6 + unix/utils/mnt | 27 + unix/utils/move_terminal | 65 ++ unix/utils/neovim.sh | 422 +++++++++++++ unix/utils/pack | 88 +++ unix/utils/rofi-network-manager.sh | 252 ++++++++ unix/utils/root.sh | 152 +++++ unix/utils/run_with_display.sh | 19 + unix/utils/scratchpad | 147 +++++ unix/utils/screenshot | 102 +++ unix/utils/sendkeys.awk | 86 +++ unix/utils/sext | 47 ++ unix/utils/track-books.sh | 19 + unix/utils/umnt | 22 + unix/utils/waypipe_app | 16 + unix/utils/wayvnc_session | 10 + unix/utils/window_manager_name.sh | 29 + unix/utils/xtouch | 38 ++ unix/virt/checksum.sh | 36 ++ unix/virt/dos.sh | 998 +++++++++++++++++++++++++++++ unix/virt/server.sh | 128 ++++ unix/virt/ubuntu | 222 +++++++ unix/virt/windows.sh | 1156 ++++++++++++++++++++++++++++++++++ 37 files changed, 4765 insertions(+) create mode 100755 unix/sys/battery_alert.sh create mode 100755 unix/sys/gsettings.sh create mode 100755 unix/sys/keep_guest_awake.sh create mode 100755 unix/sys/low-bat-notifier create mode 100755 unix/sys/session_manager.sh create mode 100755 unix/sys/shutdown_watchdog create mode 100755 unix/utils/backlight_default.sh create mode 100755 unix/utils/colors.sh create mode 100755 unix/utils/cryptocheck create mode 100755 unix/utils/cryptonotify create mode 100755 unix/utils/disable_meta_key_guest.sh create mode 100755 unix/utils/heads-up-display create mode 100755 unix/utils/hex2rgb.sh create mode 100755 unix/utils/kill-notify create mode 100755 unix/utils/kill-process create mode 100755 unix/utils/mnt create mode 100755 unix/utils/move_terminal create mode 100755 unix/utils/neovim.sh create mode 100755 unix/utils/pack create mode 100755 unix/utils/rofi-network-manager.sh create mode 100755 unix/utils/root.sh create mode 100755 unix/utils/run_with_display.sh create mode 100755 unix/utils/scratchpad create mode 100755 unix/utils/screenshot create mode 100755 unix/utils/sendkeys.awk create mode 100755 unix/utils/sext create mode 100755 unix/utils/track-books.sh create mode 100755 unix/utils/umnt create mode 100755 unix/utils/waypipe_app create mode 100755 unix/utils/wayvnc_session create mode 100755 unix/utils/window_manager_name.sh create mode 100755 unix/utils/xtouch create mode 100755 unix/virt/checksum.sh create mode 100755 unix/virt/dos.sh create mode 100755 unix/virt/server.sh create mode 100755 unix/virt/ubuntu create mode 100755 unix/virt/windows.sh (limited to 'unix') diff --git a/unix/sys/battery_alert.sh b/unix/sys/battery_alert.sh new file mode 100755 index 0000000..47fa556 --- /dev/null +++ b/unix/sys/battery_alert.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# Send a notification if the laptop battery is either low or is fully charged. + +# Battery percentage at which to notify +WARNING_LEVEL=78 +CRITICAL_LEVEL=5 +BATTERY_DISCHARGING=$(acpi -b | grep "Battery 0" | grep -c "Discharging") +BATTERY_LEVEL=$(acpi -b | grep "Battery 0" | grep -P -o '[0-9]+(?=%)') + +# Use files to store whether we've shown a notification or not (to prevent multiple notifications) +FULL_FILE=/tmp/batteryfull +EMPTY_FILE=/tmp/batteryempty +CRITICAL_FILE=/tmp/batterycritical + +# Reset notifications if the computer is charging/discharging +if [ "$BATTERY_DISCHARGING" -eq 1 ]; then + # Battery is discharging + if [ -f "$FULL_FILE" ]; then + command rm "$FULL_FILE" + fi + if [ "$BATTERY_LEVEL" -le "$WARNING_LEVEL" ] && [ ! -f "$EMPTY_FILE" ]; then + notify-send "Low Battery" "${BATTERY_LEVEL}% of battery remaining." -u critical -i "battery-low-symbolic" -r 9991 + touch "$EMPTY_FILE" + fi + if [ "$BATTERY_LEVEL" -le "$CRITICAL_LEVEL" ] && [ ! -f "$CRITICAL_FILE" ]; then + notify-send "Battery Critical" "The computer will shut down soon." -u critical -i "battery-caution-symbolic" -r 9991 + touch "$CRITICAL_FILE" + fi +elif [ "$BATTERY_DISCHARGING" -eq 0 ]; then + # Battery is charging + if [ "$BATTERY_LEVEL" -gt 99 ] && [ ! -f "$FULL_FILE" ]; then + notify-send "Battery Charged" "Battery is fully charged." -i "battery-full-symbolic" -r 9991 + touch "$FULL_FILE" + fi + # Reset empty/critical notifications when charging begins + if [ -f "$EMPTY_FILE" ]; then + command rm "$EMPTY_FILE" + fi + if [ -f "$CRITICAL_FILE" ]; then + command rm "$CRITICAL_FILE" + fi +fi diff --git a/unix/sys/gsettings.sh b/unix/sys/gsettings.sh new file mode 100755 index 0000000..9054344 --- /dev/null +++ b/unix/sys/gsettings.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Disable screen lock +gsettings set org.gnome.desktop.screensaver lock-enabled false +gsettings set org.gnome.desktop.session idle-delay 0 + +# Mutter Overlay Key +gsettings set org.gnome.mutter overlay-key '' + +# Disable update notification +#gsettings set org.gnome.software download-updates false +#gsettings set com.ubuntu.update-notifier no-show-notifications true +#sudo rm /etc/xdg/autostart/upg-notifier-autostart.desktop + +#sudo mv /etc/xdg/autostart/update-notifier.desktop /etc/xdg/autostart/update-notifier.desktop.old +#sudo mv /etc/xdg/autostart/gnome-software-service.desktop /etc/xdg/autostart/gnome-software-service.desktop.old + +# Custom Keybinding Names +gsettings set org.gnome.settings-daemon.plugins.media-keys custom-keybindings "['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/']" + +# Custom Keybinding 0 +gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ binding "T" +gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ command "scratchpad" +gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ name "scratchpad" + +# Disable keyboard plugin +gsettings set org.gnome.settings-daemon.plugins.keyboard active false + +# Prefer dark mode +gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' + +# Default terminal +gsettings set org.cinnamon.desktop.default-applications.terminal exec wezterm + +# Disable screensaver +gsettings set org.gnome.desktop.session idle-delay 0 +gsettings set org.gnome.desktop.screensaver lock-enabled false + +# Hack +#gsettings set org.gnome.desktop.app-folders folder-children "['Red', 'SysAdmin', 'Blue']" diff --git a/unix/sys/keep_guest_awake.sh b/unix/sys/keep_guest_awake.sh new file mode 100755 index 0000000..13d839b --- /dev/null +++ b/unix/sys/keep_guest_awake.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +# Host-side script to keep QEMU guest display awake without triggering anything +# and handle Windows key logic (disable it in the guest, map Caps Lock to Windows key). + +MONITOR_SOCKET_DIR="$HOME/machines/vm" +QEMU_CLASS="qemu-system-x86_64" +SEND_CMD_MONITOR="sendkey f15" +SEND_CMD_XDO="key --clearmodifiers F15" +DISABLE_WINDOWS_KEY_CMD="sendkey meta_l NoSymbol" # Using ctrl_l as a placeholder +CAPS_LOCK_CMD="sendkey meta_l" # Remapped Caps Lock to Super_L (Windows key) + +## Function to disable the Windows key and remap Caps Lock to Super_L +#disable_windows_key_and_remap_caps_lock() { +# shopt -s nullglob +# sockets=("$MONITOR_SOCKET_DIR"/*.socket) +# shopt -u nullglob +# +# for socket in "${sockets[@]}"; do +# # Get VM name from socket file +# VM_NAME=$(basename "$socket" .socket) +# VM_NAME=${VM_NAME%-serial} +# +# # Send QMP command to disable Windows key (Super_L key press/remap) +# qmp_command='{"execute": "device_key_press", "arguments": {"dev": "virtio-keyboard-pci", "key": "capslock"}}' +# echo "$qmp_command" | socat - "UNIX-CONNECT:$socket" >/dev/null +# +# # Send QMP command to remap Caps Lock to Super_L (Windows key) +# qmp_command='{"execute": "guest-execute", "arguments": {"path": "/usr/bin/xmodmap", "arg": ["-e", "keycode 66 = Super_L"]}}' +# echo "$qmp_command" | socat - "UNIX-CONNECT:$socket" >/dev/null +# +# # Optional: Disable Super_L key (Windows key) +# qmp_command='{"execute": "guest-execute", "arguments": {"path": "/usr/bin/xmodmap", "arg": ["-e", "keycode 133 = NoSymbol"]}}' +# echo "$qmp_command" | socat - "UNIX-CONNECT:$socket" >/dev/null +# done +#} + +# Function to keep the guest display awake by sending F15 key press +keep_guest_display_awake() { + shopt -s nullglob + sockets=("$MONITOR_SOCKET_DIR"/*.socket) + shopt -u nullglob + + if [ ${#sockets[@]} -eq 0 ]; then + sleep 30 + return + fi + + focused_qemu=false + + if command -v xdotool >/dev/null 2>&1; then + active_win_id=$(xdotool getwindowfocus 2>/dev/null) + if [ "$active_win_id" != "" ]; then + active_class=$(xdotool getwindowclassname "$active_win_id" 2>/dev/null) + if [[ "$active_class" == "$QEMU_CLASS" ]]; then + focused_qemu=true + fi + fi + fi + + if "$focused_qemu"; then + # QEMU is focused β†’ send F15 via xdotool + #xdotool "$SEND_CMD_XDO" + echo "" + + else + # QEMU is not focused β†’ send F15 via monitor + for socket in "${sockets[@]}"; do + echo "$SEND_CMD_MONITOR" | socat - "UNIX-CONNECT:$socket" + done + fi +} + +# Main loop +while true; do + # Handle Windows key remapping and Caps Lock disablement + #disable_windows_key_and_remap_caps_lock + + # Keep the guest display awake + keep_guest_display_awake + + # Sleep before next cycle + sleep 30 +done diff --git a/unix/sys/low-bat-notifier b/unix/sys/low-bat-notifier new file mode 100755 index 0000000..a67b25d --- /dev/null +++ b/unix/sys/low-bat-notifier @@ -0,0 +1,79 @@ +#!/bin/bash + +### VARIABLES + +POLL_INTERVAL=120 # seconds at which to check battery level +LAST_NOTIFIED=-1 # track last notified battery percentage to avoid repeated notifications + +# If BAT0 doesn't work for you, check available devices with: +# $ ls -1 /sys/class/power_supply/ +BAT_PATH=/sys/class/power_supply/BAT0 +BAT_STAT=$BAT_PATH/status + +if [[ -f $BAT_PATH/charge_full ]]; then + BAT_FULL=$BAT_PATH/charge_full + BAT_NOW=$BAT_PATH/charge_now +elif [[ -f $BAT_PATH/energy_full ]]; then + BAT_FULL=$BAT_PATH/energy_full + BAT_NOW=$BAT_PATH/energy_now +else + exit +fi + +kill_running() { + local mypid=$$ + local pids + mapfile -t pids < <(pgrep -f "${0##*/}") + + for pid in "${pids[@]}"; do + if [[ $pid -ne $mypid ]]; then + kill "$pid" + sleep 1 + fi + done +} + +get_battery_icon() { + local percent=$1 + if ((percent > 80)); then + echo "πŸ”‹" # Full battery + elif ((percent > 60)); then + echo "πŸ”‹" # High battery + elif ((percent > 40)); then + echo "πŸ”‹" # Medium battery + elif ((percent > 20)); then + echo "πŸͺ«" # Low battery + else + echo "⚠️" # Critical battery + fi +} + +# Run only if battery is detected +if ls -1qA /sys/class/power_supply/ | grep -q BAT; then + + kill_running + + while true; do + bf=$(cat "$BAT_FULL") + bn=$(cat "$BAT_NOW") + bs=$(cat "$BAT_STAT") + + bat_percent=$((100 * $bn / $bf)) + bat_icon=$(get_battery_icon "$bat_percent") + + # Notify only at exact levels: 20%, 15%, and 10% + if [[ ($bat_percent -eq 20 || $bat_percent -eq 15 || $bat_percent -eq 10) && "$bs" = "Discharging" && $bat_percent -ne $LAST_NOTIFIED ]]; then + notify-send --urgency=critical "$bat_icon $bat_percent% : $([[ $bat_percent -eq 10 ]] && echo 'Critical Battery! Shutting down in 1 minute...' || echo 'Low Battery!')" + LAST_NOTIFIED=$bat_percent + + if [[ $bat_percent -eq 10 ]]; then + sleep 60 + shutdown now + fi + elif [[ "$bs" = "Charging" ]]; then + LAST_NOTIFIED=-1 + fi + + sleep "$POLL_INTERVAL" + done +fi diff --git a/unix/sys/session_manager.sh b/unix/sys/session_manager.sh new file mode 100755 index 0000000..b6a6b03 --- /dev/null +++ b/unix/sys/session_manager.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +cd ~ + +# Default session to be executed +unset DISPLAY XAUTHORITY DBUS_SESSION_BUS_ADDRESS + +session="" + +# Function to display and start the selected session +display() { + # Default list of sessions in priority order + default_sessions=("Hyprland" "bspwm" "sway") + + # Check conditions and set session command + if [ "$DISPLAY" = "" ] && [ "$XDG_VTNR" -eq 1 ]; then + if [ -f ~/.session ]; then + session=$(cat ~/.session) + rm ~/.session # Remove the session file after reading + fi + + if [ "$session" != "" ]; then + case "$session" in + bspwm ) + export XDG_SESSION_TYPE="x11" + session="startx /usr/bin/bspwm" + ;; + Hyprland | sway) + session="dbus-launch --sh-syntax --exit-with-session $session" + ;; + *) + echo "Session $session is not supported." + session="" + ;; + esac + else + # Iterate through default sessions to find a suitable one + for wm in "${default_sessions[@]}"; do + if command -v "$wm" >/dev/null 2>&1; then + case "$wm" in + bspwm ) + export XDG_SESSION_TYPE="x11" + session="startx /usr/bin/$wm" + break + ;; + Hyprland | sway) + session="dbus-launch --sh-syntax --exit-with-session $wm >/dev/null 2>&1 && exit" + #show_animation.sh + clear + break + ;; + esac + fi + done + fi + + # Execute the session command if session is set + if [ "$session" != "" ]; then + #echo "Starting session: $session" + eval "$session" + else + echo "No suitable window manager found or conditions not met." + fi + fi +} + +# Main function +main() { + display +} + +main "$@" diff --git a/unix/sys/shutdown_watchdog b/unix/sys/shutdown_watchdog new file mode 100755 index 0000000..79bc7bf --- /dev/null +++ b/unix/sys/shutdown_watchdog @@ -0,0 +1,11 @@ +#!/bin/sh + +LOGFILE="/tmp/shutdown_watchdog.log" + +while true; do + echo "--- $(date) ---" >> "$LOGFILE" + uptime >> "$LOGFILE" + sensors >> "$LOGFILE" 2>/dev/null + acpi -V >> "$LOGFILE" 2>/dev/null + sleep 10 +done diff --git a/unix/utils/backlight_default.sh b/unix/utils/backlight_default.sh new file mode 100755 index 0000000..b79e680 --- /dev/null +++ b/unix/utils/backlight_default.sh @@ -0,0 +1,32 @@ +#!/bin/sh +set -e + +backlight_sys_dir="/sys/class/backlight/intel_backlight" + +read -r max_brightness < "${backlight_sys_dir}/max_brightness" +read -r curr_brightness < "${backlight_sys_dir}/brightness" + +if ! groups | grep -q backlight; then + echo "User is not in the backlight group" + exit 1 +fi + +if [ "$#" -eq 0 ] ; then + # set to half that of 'max_brightness' + echo $((max_brightness / 2)) > "$backlight_sys_dir"/brightness + exit 0 +fi + +case "$1" in + up) increment="+ 10" ;; + down) increment="- 10" ;; + *) exit 1 ;; +esac + +new_brightness=$(($curr_brightness $increment)) + +if $((new_brightness < 1)) || $((new_brightness > $max_brightness)); then + exit 1 +else + echo "$new_brightness" > "$backlight_sys_dir"/brightness +fi diff --git a/unix/utils/colors.sh b/unix/utils/colors.sh new file mode 100755 index 0000000..fc1c10c --- /dev/null +++ b/unix/utils/colors.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +colors=$@ +for (( n=0; n < $colors; n++ )) do + printf " [%d] $(tput setaf $n)%s$(tput sgr0)" $n "Hello World! +" +done +PADDING='Padding' + +main() { + local xterm_start=0 \ + xterm_width=8 \ + xterm_height=2 + + local cube_start=$((xterm_start + xterm_width * xterm_height)) \ + cube_width=6 \ + cube_height=$((6 * 6)) + + local greys_start=$((cube_start + cube_width * cube_height)) \ + greys_width=8 \ + greys_height=3 + + color_block $xterm_start $xterm_width $xterm_height + color_block $cube_start $cube_width $cube_height use_padding + color_block $greys_start $greys_width $greys_height + echo +} + +color_block() { + local start=$1 width=$2 height=$3 use_padding=$4 + local max s color_nums colors + + max=$((start + width * height - 1)) + + echo + for s in $(seq $start $width $max); do + color_nums=$(seq $s $((s + width - 1))) + colors="${use_padding:+$PADDING }${color_nums}${use_padding:+ $PADDING}" + + printf '%s%s %s%s\n' \ + "$(fg_bars $colors)" $ansi_reset \ + "$(bg_bars $colors)" $ansi_reset + done +} + +fg_bars() { + for color in $@; do + color_bar ansi_fg $color '' + done +} + +bg_bars() { + for color in $@; do + color_bar ansi_bg $color ' ' + done +} + +color_bar() { + local ansi=$1 color=$2 trail=$3 + + if [ "$color" == $PADDING ]; then + printf '%s %s' $ansi_reset "$trail" + else + local color_seq=$($ansi $color) + printf '%s %03d%s' $color_seq $color "$trail" + fi +} + +ansi_reset=$'\033[0m' + +ansi_fg() { + printf '\033[38;5;%dm' $1 +} + +ansi_bg() { + printf '\033[48;5;%dm' $1 +} + +main diff --git a/unix/utils/cryptocheck b/unix/utils/cryptocheck new file mode 100755 index 0000000..02ba42d --- /dev/null +++ b/unix/utils/cryptocheck @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +if [ ! -d ~/.cache/crypto ]; then + mkdir ~/.cache/crypto +fi +ticker=(BTC ETH ADA DOT SOL XMR) + +for currency in "${ticker[@]}"; do + echo "$currency" +done | while read coin + do + price=$(curl rate.sx/1$coin) + if [ $coin = "BTC" ]; then + icon=σ° “ + elif [ $coin = "ETH" ]; then + icon=σ°‘ͺ + elif [ $coin = "ADA" ]; then + icon=󰝨 + elif [ $coin = "DOT" ]; then + icon=󰐇 + elif [ $coin = "SOL" ]; then + icon=σ°˜™ + elif [ $coin = "XMR" ]; then + icon=󰝴 + fi + + echo "$icon $coin: $price" > ~/.cache/crypto/$coin + + done + +date > ~/.cache/crypto/time + diff --git a/unix/utils/cryptonotify b/unix/utils/cryptonotify new file mode 100755 index 0000000..47883c3 --- /dev/null +++ b/unix/utils/cryptonotify @@ -0,0 +1,19 @@ +#!/bin/sh +source cryptocheck +Output= +for file in ~/.cache/crypto/* +do + if [ ! -z "$Output" ]; then + if [ ! $(basename $file) = "time" ]; then + Output="$Output\n$(cat $file)" + fi + else + if [ ! $(basename $file) = "time" ]; then + Output="$Output$(cat $file)" + fi + fi + +done + +Output="$Output\n$(cat ~/.cache/crypto/time)" +notify-send "Crypto Prices" "$Output" diff --git a/unix/utils/disable_meta_key_guest.sh b/unix/utils/disable_meta_key_guest.sh new file mode 100755 index 0000000..acbbdf5 --- /dev/null +++ b/unix/utils/disable_meta_key_guest.sh @@ -0,0 +1,94 @@ +ain "$@" +#!/usr/bin/env bash + +# Function to find the QEMU monitor socket dynamically and extract the VM name +get_socket_path() { + local socket_dir="$HOME/machines/vm" # Directory where your sockets are stored + local socket_file=$(find "$socket_dir" -type s -name "*-monitor.socket" | head -n 1) + + echo "[DEBUG] Found socket file: $socket_file" # Debugging line to check the socket file + + if [[ -z "$socket_file" ]]; then + echo "Error: No QEMU monitor socket found in $socket_dir." >&2 + exit 1 + fi + + # Extract the VM name from the socket file name + local vm_name=$(basename "$socket_file" -monitor.socket) + echo "[DEBUG] VM name detected: $vm_name" # Debugging line to check VM name + + echo "$socket_file" # Return the full socket path + echo "$vm_name" # Return the VM name +} + +# Function to check if sendkeys.awk is in the user's PATH or fall back to specific directories +find_sendkeys_awk() { + # Check if sendkeys.awk is in the user's PATH + if command -v sendkeys.awk &>/dev/null; then + echo "$(command -v sendkeys.awk)" + return 0 + fi + + # Otherwise, check in specific fallback directories + local possible_paths=( + "$HOME/.scripts/env/linux/utils/sendkeys.awk" + "$HOME/.scripts/sendkeys.awk" + ) + + for path in "${possible_paths[@]}"; do + if [[ -f "$path" ]]; then + echo "$path" + return 0 + fi + done + + echo "sendkeys.awk not found in the user's PATH or known directories." >&2 + exit 1 +} + +send_guest_command() { + local cmd="$1" + local socket="$2" + echo "$cmd" | awk -f "$SENDKEYS_AWK" | socat - UNIX-CONNECT:"$socket" +} + +main() { + # Get the QEMU socket and VM name dynamically + SOCKET=$(get_socket_path) + VM_NAME=$(basename "$SOCKET" -monitor.socket) + + # Debugging output to verify the socket and VM name + echo "[DEBUG] Using socket: $SOCKET" + echo "[DEBUG] VM name: $VM_NAME" + + # If no socket file is found, exit + if [[ ! -S "$SOCKET" ]]; then + echo "Error: QEMU monitor socket for $VM_NAME does not exist or is not available." + exit 1 + fi + + # Try to find the sendkeys.awk file + SENDKEYS_AWK=$(find_sendkeys_awk) + + echo "[*] Attempting Caps Lock to Super remapping on all known platforms..." + + # === X11 === + send_guest_command "setxkbmap -option caps:super" "$SOCKET" + send_guest_command "xmodmap -e 'remove Mod4 = Super_L Super_R'" "$SOCKET" + send_guest_command "xmodmap -e 'keycode 133 = NoSymbol'" "$SOCKET" + send_guest_command "xmodmap -e 'keycode 134 = NoSymbol'" "$SOCKET" + + # === Wayland (note: this just gives a reminder) === + send_guest_command "gsettings set org.gnome.desktop.input-sources xkb-options \"['caps:super']\"" "$SOCKET" + send_guest_command "echo 'Wayland? Try remapping via wlroots/wlr-keyboard'" "$SOCKET" + + # === Windows (registry scancode map) === + send_guest_command "powershell -Command \"Set-ItemProperty -Path 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout' -Name 'Scancode Map' -Value ([byte[]](0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x3A,0x00,0x5B,0x00,0x00,0x00))\"" "$SOCKET" + + # === macOS (hidutil remap) === + send_guest_command "hidutil property --set '{\"UserKeyMapping\":[{\"HIDKeyboardModifierMappingSrc\":0x700000039,\"HIDKeyboardModifierMappingDst\":0x7000000E3}]}'" "$SOCKET" + + echo "[*] Done sending remapping commands to guest." +} + +main "$@" diff --git a/unix/utils/heads-up-display b/unix/utils/heads-up-display new file mode 100755 index 0000000..54f5de1 --- /dev/null +++ b/unix/utils/heads-up-display @@ -0,0 +1,88 @@ +#!/bin/sh + +# Created By: srdusr +# Created On: Wed 05 Feb 2023 01:24:37 AM CAT +# Project: bspwm scratchpad (Heads-Up-Display) with tmux session + +### Alternative method + +#if id="$(xdo id -N heads-up-display)"; then +# bspc node "$id" -g hidden -f +#else +# #kitty --class "heads-up-display" -e tmux new-session -A -s HUD -e bash >/dev/null 2>&1 & +# wezterm start --class "heads-up-display" -e tmux new-session -A -s HUD -e bash >/dev/null 2>&1 & +#fi + +#- - - - - - - - - - + +### Alternative method + +#id=$(xdotool search --class heads-up-display) +#if [ "$id" = "" ]; then +# #kitty --class "Heads-Up-Display" -e tmux new-session -A -s HUD -e bash > /dev/null 2>&1 & +# alacritty --class "heads-up-display" -e tmux new-session -A -s HUD -e bash >/dev/null 2>&1 & +#else +# if [ ! -f /tmp/hide_hud ]; then +# touch /tmp/hide_hud && xdo hide "$id" +# elif [ -f /tmp/hide_hud ]; then +# rm /tmp/hide_hud && xdo show "$id" +# fi +#fi + + + +# Set the environment variables to x11 to allow working in Wayland +export GDK_BACKEND=x11 +export QT_QPA_PLATFORM=xcb +#export WAYLAND_DISPLAY="" +export WINIT_UNIX_BACKEND=x11 + +# Supported terminals and dropdown class +supported_terminals=("wezterm" "kitty" "alacritty") + +# Check if any supported terminal with scratchpad class is running +for term in "${supported_terminals[@]}"; do + if pgrep -f "$term.*--class heads-up-display" >/dev/null; then + my_term="$term" + break + fi +done + +# If no supported terminal is running, start the first available one +if [ "$my_term" = "" ]; then + for term in "${supported_terminals[@]}"; do + if command -v "$term" >/dev/null 2>&1; then + my_term="$term" + break + fi + done + if [ "$my_term" = "" ]; then + echo "No supported terminal found." && exit 1 + fi + + # Start terminal with scratchpad class + case "$my_term" in + "wezterm") wezterm start --class heads-up-display -e tmux new-session -A -s HUD -e bash htop & ;; + "kitty") kitty --class heads-up-display tmux new-session -A -s HUD -e bash htop & ;; + "alacritty") alacritty --class heads-up-display -e tmux new-session -A -s HUD -e bash htop & ;; + + esac +fi + +# Get the window ID of the scratchpad terminal +id="$(xdo id -N heads-up-display)" + +# Toggle scratchpad terminal visibility +if [ "$id" != "" ]; then + if xwininfo -id "$id" | grep "Map State: IsViewable" >/dev/null; then + # Scratchpad is visible, hide it + dimensions="$(xwininfo -id "$id" | awk '/Width:|Height:/ { printf("%s=%s;", tolower($1), $2) }')" + xdo hide "$id" 2>/dev/null + else + # Scratchpad is hidden, show it and restore dimensions + xdo show "$id" + xdotool windowsize "$id" "$(echo "$dimensions" | tr ';' ' ')" 2>/dev/null + xdotool windowactivate "$id" + xdotool windowfocus "$id" + fi +fi diff --git a/unix/utils/hex2rgb.sh b/unix/utils/hex2rgb.sh new file mode 100755 index 0000000..a8ccc38 --- /dev/null +++ b/unix/utils/hex2rgb.sh @@ -0,0 +1,3 @@ +#!/bin/bash +hex="${1}" +printf "R: %d G: %d B: %d\n" 0x"${hex:0:2}" 0x"${hex:2:2}" 0x"${hex:4:2}" diff --git a/unix/utils/kill-notify b/unix/utils/kill-notify new file mode 100755 index 0000000..f7f749e --- /dev/null +++ b/unix/utils/kill-notify @@ -0,0 +1,4 @@ +#!/bin/sh +# Kills an application and sends a notification that it's been killed + +killall "$1" && notify-send "Killed $1" diff --git a/unix/utils/kill-process b/unix/utils/kill-process new file mode 100755 index 0000000..5247e5f --- /dev/null +++ b/unix/utils/kill-process @@ -0,0 +1,6 @@ +#!/bin/sh +# pipes list of processes into rofi/dmenu and kills the selection + +proc=$(ps -u $USER -o pid,%mem,%cpu,comm | sort -b -k2 -r | sed -n '1!p' | dmenu -i -p "Kill" | awk '{print $1,$4}') + +[ -z "$proc" ] || (kill -15 $(echo $proc | awk '{print $1}') 2>/dev/null && notify-send "$(echo $proc | awk '{print $2}') killed") diff --git a/unix/utils/mnt b/unix/utils/mnt new file mode 100755 index 0000000..dc19ca9 --- /dev/null +++ b/unix/utils/mnt @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# mount disk +set -e +. ~/etc/colors/current + +d() case $LABEL in +Windows) echo "/win" ;; +*) echo "${1:-$HOME}/dev/$NAME" ;; +esac + +lsblk -Po NAME,SIZE,MOUNTPOINT,FSTYPE,LABEL | while read -r a; do + eval "$a" + + [ "$FSTYPE" ] && [ ! "$MOUNTPOINT" ] && + printf "%-4s:%s:%s:-> %s\n" \ + "$NAME" \ + "$SIZE" \ + "${LABEL:-unnamed}" \ + "$(d \~)" + +done | column -ts':' -o' ' | menu -p mount | { + read -r NAME _ + eval "$(lsblk -Polabel "/dev/$NAME")" + mkdir -p "$(d)" + sudo mount -o "umask=000" "/dev/$NAME" "$(d)" + notify-send summary "$NAME: $LABEL\n$(d \~)" +} diff --git a/unix/utils/move_terminal b/unix/utils/move_terminal new file mode 100755 index 0000000..2b447d0 --- /dev/null +++ b/unix/utils/move_terminal @@ -0,0 +1,65 @@ +#!/bin/bash + +SCRATCHPAD_CLASSES=("scratchpad" "pack" "heads-up-display") +PRIMARY_MONITOR="eDP-1" +SECOND_MONITOR="HDMI-A-2" + +PRIMARY_WORKSPACES=(2 4 5 6) +SECOND_WORKSPACES=(1 3) + +# Check if second monitor exists +if ! hyprctl monitors -j | jq -e ".[] | select(.name == \"$SECOND_MONITOR\")" >/dev/null; then + # No second monitor, do nothing + exit 0 +fi + +# Check if list of workspaces has any windows +has_windows_in_workspaces() { + local workspaces=("$@") + for ws in "${workspaces[@]}"; do + local count + count=$(hyprctl clients -j | jq "[.[] | select(.workspace.id == $ws)] | length") + if ((count > 0)); then + return 0 + fi + done + return 1 +} + +# Check window presence +primary_has_windows=false +second_has_windows=false + +if has_windows_in_workspaces "${PRIMARY_WORKSPACES[@]}"; then + primary_has_windows=true +fi +if has_windows_in_workspaces "${SECOND_WORKSPACES[@]}"; then + second_has_windows=true +fi + +# Decide target monitor +if [[ "$primary_has_windows" == true && "$second_has_windows" == false ]]; then + TARGET_MONITOR="$SECOND_MONITOR" +elif [[ "$primary_has_windows" == false && "$second_has_windows" == true ]]; then + TARGET_MONITOR="$PRIMARY_MONITOR" +else + TARGET_MONITOR="$SECOND_MONITOR" # both busy or both empty β†’ second monitor +fi + +# Get workspace id of the target monitor +TARGET_WORKSPACE=$(hyprctl monitors -j | jq ".[] | select(.name == \"$TARGET_MONITOR\") | .activeWorkspace.id") + +# Wait for scratchpad window to appear and move it +for _ in {1..20}; do + for class in "${SCRATCHPAD_CLASSES[@]}"; do + client=$(hyprctl clients -j | jq -r ".[] | select(.class == \"$class\") | .address") + if [[ -n "$client" ]]; then + hyprctl dispatch movetoworkspacesilent "$TARGET_WORKSPACE,address:$client" + hyprctl dispatch focuswindow address:"$client" + exit 0 + fi + done + sleep 0.1 +done + +exit 1 diff --git a/unix/utils/neovim.sh b/unix/utils/neovim.sh new file mode 100755 index 0000000..c9adcca --- /dev/null +++ b/unix/utils/neovim.sh @@ -0,0 +1,422 @@ +#!/bin/bash + +# Created By: srdusr +# Created On: Sat 12 Aug 2023 13:11:39 CAT +# Project: Install/update/uninstall/change version Neovim script, primarily for Linux but may work in other platforms + +# Dependencies: wget/curl, fuse + +# Color definitions +RED='\033[0;31m' +GREEN='\033[0;32m' +NC='\033[0m' # No Color + +# Handle errors +handle_error() { + local message="$1" + printf "${RED}Error: $message${NC}\n" +} + +# Check if necessary dependencies are installed +check_dependencies() { + if [ -x "$(command -v wget)" ]; then + DOWNLOAD_COMMAND="wget" + elif [ -x "$(command -v curl)" ]; then + DOWNLOAD_COMMAND="curl" + else + printf "${RED}Error: Neither wget nor curl found. Please install one of them to continue!${NC}\n" + exit 1 + fi +} + +# Check for privilege escalation tools +check_privilege_tools() { + if [ -x "$(command -v sudo)" ]; then + PRIVILEGE_TOOL="sudo" + elif [ -x "$(command -v doas)" ]; then + PRIVILEGE_TOOL="doas" + elif [ -x "$(command -v pkexec)" ]; then + PRIVILEGE_TOOL="pkexec" + elif [ -x "$(command -v dzdo)" ]; then + PRIVILEGE_TOOL="dzdo" + elif [ "$(id -u)" -eq 0 ]; then + PRIVILEGE_TOOL="" # root + else + PRIVILEGE_TOOL="" # No privilege escalation mechanism found + printf "\n${RED}Error: No privilege escalation tool (sudo, doas, pkexec, dzdo, or root privileges) found. You may not have sufficient permissions to run this script.${NC}\n" + printf "\nAttempt to continue Installation (might fail without a privilege escalation tool)? [yes/no] " + read continue_choice + case $continue_choice in + [Yy] | [Yy][Ee][Ss]) ;; + [Nn] | [Nn][Oo]) exit ;; + *) handle_error "Invalid choice. Exiting..." && exit ;; + esac + fi +} + +# Check if Neovim is already installed +check_neovim_installed() { + if [ -x "$(command -v nvim)" ]; then + return 0 # Neovim is installed + else + return 1 # Neovim is not installed + fi +} + +# Nightly version +nightly_version() { + local url="https://github.com/neovim/neovim/releases/download/nightly/nvim-linux-x86_64.appimage" + install_neovim "$url" + local version_output=$(nvim --version) + version_id="Nightly $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+\.\d+')" +} + +# Stable version +stable_version() { + #local url="https://github.com/neovim/neovim/releases/download/stable/nvim.appimage" + local url="https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.appimage" + install_neovim "$url" + local version_output=$(nvim --version) + version_id="Stable $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+')" +} + +# Specific version +specific_version() { + local version="$1" + + # Add 'v' prefix if not present + if [[ $version != v* ]]; then + version="v$version" + fi + + local url="https://github.com/neovim/neovim/releases/download/$version/nvim-linux-x86_64.appimage" + install_neovim "$url" + local version_output=$(nvim --version) + version_id="Version $version $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+\.\d+')" +} + +# Download a file using wget or curl +download_file() { + local url="$1" + local output="$2" + + if [ "$DOWNLOAD_COMMAND" = "wget" ]; then + if ! "$DOWNLOAD_COMMAND" -q --show-progress -O "$output" "$url"; then + handle_error "Download failed. Exiting..." + exit 1 + fi + elif [ "$DOWNLOAD_COMMAND" = "curl" ]; then + if ! "$DOWNLOAD_COMMAND" --progress-bar -# -o "$output" "$url"; then + handle_error "Download failed. Exiting..." + exit 1 + fi + else + echo "Unsupported download command: $DOWNLOAD_COMMAND" + exit 1 + fi +} + +# Check if a specific version of Neovim exists +version_exists() { + local version="$1" + + # Add 'v' prefix if not present + if [[ $version != v* ]]; then + version="v$version" + fi + + # Fetch all the release tags from GitHub + ALL_TAGS=$(curl -s "https://api.github.com/repos/neovim/neovim/tags" | grep '"name":' | cut -d '"' -f 4) + + # Check if the desired version is in the list of release tags + if echo "$ALL_TAGS" | grep -q "$version"; then + return 0 # Version exists + else + return 1 # Version does not exist + fi +} + +# Update Neovim to the latest version (nightly/stable) +update_version() { + valid_choice=false + while [ "$valid_choice" = false ]; do + # Determine which version to update to (nightly/stable) + printf "Select version to install/update to:\n" + printf " 1. Nightly\n" + printf " 2. Stable\n" + printf " 3. Choose specific version by tag\n" + printf "Enter the number corresponding to your choice (1/2/3): " + read update_choice + + case $update_choice in + 1) + version="Nightly" + nightly_version + valid_choice=true + ;; + 2) + version="Stable" + stable_version + valid_choice=true + ;; + 3) + # Ask user for specific version + read -p "Enter the specific version (e.g., v0.1.0): " version + # Normalize version + if [[ $version != v* ]]; then + version="v$version" + fi + # Check if the specific version exists on GitHub releases + if version_exists "$version"; then + # Install specific version + specific_version "$version" # Pass the normalized version to the function + valid_choice=true + else + printf "${RED}The specified version $version does not exist.${NC}\n" + fi + ;; + + *) + handle_error "Invalid choice. Please enter a valid option (1, 2 or 3)." + ;; + esac + done + +} + +# Install Neovim +install_neovim() { + local url="$1" + local install_action="$3" + + if [ "$install_action" = "installed" ]; then + printf "Downloading and installing Neovim $version...\n" + else + printf "${GREEN}Updating Neovim to the latest version ($version)...${NC}\n" + fi + + # Determine the platform-specific installation steps + case "$(uname -s)" in + Linux) + printf "Detected Linux OS.\n" + if [ -x "$(command -v fusermount)" ]; then + printf "FUSE is available. Downloading and running the AppImage...\n" + download_file "$url" "nvim.appimage" + chmod u+x nvim.appimage + "$PRIVILEGE_TOOL" cp nvim.appimage /usr/local/bin/nvim + "$PRIVILEGE_TOOL" mv nvim.appimage /usr/bin/nvim + else + printf "FUSE is not available. Downloading and extracting the AppImage...\n" + download_file "$url" "nvim.appimage" + chmod u+x nvim.appimage + ./nvim.appimage --appimage-extract + "$PRIVILEGE_TOOL" cp squashfs-root/usr/bin/nvim /usr/local/bin + "$PRIVILEGE_TOOL" mv squashfs-root/usr/bin/nvim /usr/bin + fi + ;; + + Darwin) + printf "Detected macOS.\n" + download_file "$url" "nvim-macos.tar.gz" + xattr -c ./nvim-macos.tar.gz + tar xzvf nvim-macos.tar.gz + "$PRIVILEGE_TOOL" cp nvim-macos/bin/nvim /usr/local/bin + "$PRIVILEGE_TOOL" mv nvim-macos/bin/nvim /usr/bin/nvim + ;; + + MINGW*) + printf "Detected Windows.\n" + download_file "$url" "nvim.appimage" + chmod +x nvim.appimage + if [ "$PRIVILEGE_TOOL" = "sudo" ]; then + "$PRIVILEGE_TOOL" cp nvim.appimage /usr/local/bin/nvim + "$PRIVILEGE_TOOL" mv /usr/local/bin/nvim /usr/bin + elif [ "$PRIVILEGE_TOOL" = "" ]; then + cp nvim.appimage /usr/local/bin/nvim + mv /usr/local/bin/nvim /usr/bin + else + printf "No privilege escalation tool found. Cannot install Neovim on Windows.\n" + fi + ;; + + *) + printf "Unsupported operating system.\n" + exit 1 + ;; + esac + # Check if the installation was successful + if [ $? -eq 0 ]; then + if [ "$install_action" = "installed" ]; then + printf "${GREEN}Neovim $version has been installed successfully!${NC}\n" + else + printf "${GREEN}Neovim has been updated successfully to $version!${NC}\n" + fi + else + printf "${RED}Error: Neovim installation/update failed.${NC}\n" + exit 1 + fi +} + +# Uninstall Neovim +uninstall_neovim() { + printf "${RED}Uninstalling Neovim...${NC}\n" + + # Detect the operating system to determine the appropriate uninstallation method + case "$(uname -s)" in + Linux) + printf "Detected Linux OS.\n" + "$PRIVILEGE_TOOL" rm /usr/local/bin/nvim + "$PRIVILEGE_TOOL" rm /usr/bin/nvim + ;; + + Darwin) + printf "Detected macOS.\n" + "$PRIVILEGE_TOOL" rm /usr/local/bin/nvim + "$PRIVILEGE_TOOL" rm /usr/bin/nvim + ;; + + MINGW*) + printf "Detected Windows.\n" + if [ "$PRIVILEGE_TOOL" = "sudo" ]; then + "$PRIVILEGE_TOOL" rm /usr/local/bin/nvim + "$PRIVILEGE_TOOL" rm /usr/bin/nvim + else + [ "$PRIVILEGE_TOOL" = "" ] + rm /usr/local/bin/nvim + rm /usr/bin/nvim + fi + ;; + *) + printf "Unsupported operating system.\n" + ;; + esac + + printf "${GREEN}Neovim has been uninstalled successfully!${NC}\n" +} + +# Check if Neovim is running +check_neovim_running() { + if pgrep nvim >/dev/null; then + printf "${RED}Error: Neovim is currently running. Please close Neovim before proceeding.${NC}\n" + read -p "Do you want to forcefully terminate Neovim and continue? [yes/no] " terminate_choice + + case $terminate_choice in + [Yy] | [Yy][Ee][Ss]) + pkill nvim # Forcefully terminate Neovim + ;; + [Nn] | [Nn][Oo]) + echo "Exiting..." + exit 1 + ;; + *) + handle_error "Invalid choice." + ;; + esac + fi +} + +check_neovim_running + +# Define the variable to control the prompt +SHOW_PROMPT=1 + +# Check if necessary dependencies are installed +check_dependencies + +# Check for privilege escalation tools +check_privilege_tools + +# Check if Neovim is already installed and ask the user if want to install it +if check_neovim_installed; then + printf "${GREEN}Neovim is already installed!${NC}\n" +else + printf "${RED}Neovim is not installed.${NC}\n" + read -p "Install Neovim? (y/n): " install_choice + + case $install_choice in + [Yy]) + update_version + ;; + [Nn]) + echo "Exiting..." + exit + ;; + *) + handle_error "Invalid choice. Please enter 'y' for yes or 'n' for no." + ;; + esac +fi + +# Check for updates and display breaking changes +check_version_updates() { + local latest_version_url="https://api.github.com/repos/neovim/neovim/releases/latest" + local latest_version="" + + if [ -x "$(command -v curl)" ]; then + latest_version=$(curl -sSL "$latest_version_url" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + elif [ -x "$(command -v wget)" ]; then + latest_version=$(wget -qO - "$latest_version_url" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + else + printf "${RED}Error: Neither curl nor wget found. Please install one of them to continue!${NC}\n" + exit 1 + fi + + if version_exists "$latest_version"; then + printf "${GREEN}An update is available!${NC}\n" + display_breaking_changes "$latest_version" + else + printf "You have the latest version of Neovim.\n" + fi +} + +# To display breaking changes for a specific version +display_breaking_changes() { + local version="$1" + local changelog_url="https://github.com/neovim/neovim/releases/tag/$version" + local changelog="" + + if [ -x "$(command -v curl)" ]; then + changelog=$(curl -sSL "$changelog_url" | grep -oE '

Breaking Changes.*?' | sed 's/<[^>]*>//g') + elif [ -x "$(command -v wget)" ]; then + changelog=$(wget -qO - "$changelog_url" | grep -oE '

Breaking Changes.*?' | sed 's/<[^>]*>//g') + else + printf "${RED}Error: Neither curl nor wget found. Please install one of them to continue!${NC}\n" + exit 1 + fi + + printf "\nBreaking Changes in Neovim $version:\n" + printf "$changelog\n" +} + +# Main loop +while [ "$SHOW_PROMPT" -gt 0 ]; do + printf "Select an option:\n" + printf " 1. Install/update Neovim\n" + printf " 2. Check for updates\n" + printf " 3. Uninstall Neovim\n" + printf " 4. Run Neovim\n" + printf " 5. Quit\n" + read -p "Enter a number or press 'q' to quit: " choice + + case $choice in + 1) + update_version + ;; + 2) + check_version_updates + ;; + 3) + uninstall_neovim + ;; + 4) + nvim + ;; + 5 | [Qq]) + echo "Exiting..." + exit + ;; + *) + handle_error "Invalid choice. Please choose a valid option by entering the corresponding number or press 'q' to 'quit'." + ;; + esac +done diff --git a/unix/utils/pack b/unix/utils/pack new file mode 100755 index 0000000..4b8760c --- /dev/null +++ b/unix/utils/pack @@ -0,0 +1,88 @@ +#!/bin/sh + +# Created By: srdusr +# Created On: Wed 05 Feb 2023 01:24:37 AM CAT +# Project: bspwm scratchpad (pack) with tmux session + +### Alternative method + +#if id="$(xdo id -N pack)"; then +# bspc node "$id" -g hidden -f +#else +# #kitty --class "pack" -e tmux new-session -A -s pack -e bash >/dev/null 2>&1 & +# wezterm start --class "pack" -e tmux new-session -A -s pack -e bash >/dev/null 2>&1 & +#fi + +#- - - - - - - - - - + +### Alternative method + +#id=$(xdotool search --class pack) +#if [ "$id" = "" ]; then +# #kitty --class "pack" -e tmux new-session -A -s pack -e bash > /dev/null 2>&1 & +# alacritty --class "pack" -e tmux new-session -A -s pack -e bash >/dev/null 2>&1 & +#else +# if [ ! -f /tmp/hide_hud ]; then +# touch /tmp/hide_hud && xdo hide "$id" +# elif [ -f /tmp/hide_hud ]; then +# rm /tmp/hide_hud && xdo show "$id" +# fi +#fi + + + +# Set the environment variables to x11 to allow working in Wayland +export GDK_BACKEND=x11 +export QT_QPA_PLATFORM=xcb +#export WAYLAND_DISPLAY="" +export WINIT_UNIX_BACKEND=x11 + +# Supported terminals and dropdown class +supported_terminals=("wezterm" "kitty" "alacritty") + +# Check if any supported terminal with scratchpad class is running +for term in "${supported_terminals[@]}"; do + if pgrep -f "$term.*--class pack" >/dev/null; then + my_term="$term" + break + fi +done + +# If no supported terminal is running, start the first available one +if [ "$my_term" = "" ]; then + for term in "${supported_terminals[@]}"; do + if command -v "$term" >/dev/null 2>&1; then + my_term="$term" + break + fi + done + if [ "$my_term" = "" ]; then + echo "No supported terminal found." && exit 1 + fi + + # Start terminal with scratchpad class + case "$my_term" in + "wezterm") wezterm start --class pack -e tmux new-session -A -s pack -e bash & ;; + "kitty") kitty --class pack tmux new-session -A -s pack -e bash & ;; + "alacritty") alacritty --class pack -e tmux new-session -A -s pack -e bash & ;; + + esac +fi + +# Get the window ID of the scratchpad terminal +id="$(xdo id -N pack)" + +# Toggle scratchpad terminal visibility +if [ "$id" != "" ]; then + if xwininfo -id "$id" | grep "Map State: IsViewable" >/dev/null; then + # Scratchpad is visible, hide it + dimensions="$(xwininfo -id "$id" | awk '/Width:|Height:/ { printf("%s=%s;", tolower($1), $2) }')" + xdo hide "$id" 2>/dev/null + else + # Scratchpad is hidden, show it and restore dimensions + xdo show "$id" + xdotool windowsize "$id" "$(echo "$dimensions" | tr ';' ' ')" 2>/dev/null + xdotool windowactivate "$id" + xdotool windowfocus "$id" + fi +fi diff --git a/unix/utils/rofi-network-manager.sh b/unix/utils/rofi-network-manager.sh new file mode 100755 index 0000000..61106d2 --- /dev/null +++ b/unix/utils/rofi-network-manager.sh @@ -0,0 +1,252 @@ +#!/bin/bash + +# Credit: https://github.com/P3rf/rofi-network-manager + +# Default Values +LOCATION=0 +QRCODE_LOCATION=$LOCATION +Y_AXIS=0 +X_AXIS=0 +NOTIFICATIONS="off" +QRCODE_DIR="/tmp/" +WIDTH_FIX_MAIN=1 +WIDTH_FIX_STATUS=10 +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PASSWORD_ENTER="if connection is stored,hit enter/esc." +WIRELESS_INTERFACES=("$(nmcli device | awk '$2=="wifi" {print $1}')") +WIRELESS_INTERFACES_PRODUCT=() +WLAN_INT=0 +WIRED_INTERFACES=("$(nmcli device | awk '$2=="ethernet" {print $1}')") +WIRED_INTERFACES_PRODUCT=() +ASCII_OUT=false +CHANGE_BARS=false +SIGNAL_STRENGTH_0="0" +SIGNAL_STRENGTH_1="1" +SIGNAL_STRENGTH_2="12" +SIGNAL_STRENGTH_3="123" +SIGNAL_STRENGTH_4="1234" +VPN_PATTERN='(wireguard|vpn)' +function initialization() { + # Try to source configuration files from .config/rofi/ + if [[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/rofi/rofi-network-manager.conf" ]]; then + source "${XDG_CONFIG_HOME:-$HOME/.config}/rofi/rofi-network-manager.conf" + RASI_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/rofi/rofi-network-manager.rasi" + elif [[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/rofi/rofi-network-manager.rasi" ]]; then + source "${XDG_CONFIG_HOME:-$HOME/.config}/rofi/rofi-network-manager.rasi" + else + # Fallback to using files in the script's directory + source "$DIR/rofi-network-manager.conf" || source "$DIR/rofi-network-manager.conf" + [[ -f "$DIR/rofi-network-manager.rasi" ]] && RASI_DIR="$DIR/rofi-network-manager.rasi" + fi + for i in "${WIRELESS_INTERFACES[@]}"; do WIRELESS_INTERFACES_PRODUCT+=("$(nmcli -f general.product device show "$i" | awk '{print $2}')"); done + for i in "${WIRED_INTERFACES[@]}"; do WIRED_INTERFACES_PRODUCT+=("$(nmcli -f general.product device show "$i" | awk '{print $2}')"); done + wireless_interface_state && ethernet_interface_state +} +function notification() { + [[ "$NOTIFICATIONS" == "true" && -x "$(command -v notify-send)" ]] && notify-send -r "5" -u "normal" "$1" "$2" +} +function wireless_interface_state() { + + [[ ${#WIRELESS_INTERFACES[@]} -eq "0" ]] || { + ACTIVE_SSID=$(nmcli device status | grep "^${WIRELESS_INTERFACES[WLAN_INT]}." | awk '{print $4}') + WIFI_CON_STATE=$(nmcli device status | grep "^${WIRELESS_INTERFACES[WLAN_INT]}." | awk '{print $3}') + { [[ "$WIFI_CON_STATE" == "unavailable" ]] && WIFI_LIST="***Wi-Fi Disabled***" && WIFI_SWITCH="~Wi-Fi On" && OPTIONS="${WIFI_LIST}\n${WIFI_SWITCH}\n~Scan\n"; } || { [[ "$WIFI_CON_STATE" =~ "connected" ]] && { + PROMPT=${WIRELESS_INTERFACES_PRODUCT[WLAN_INT]}[${WIRELESS_INTERFACES[WLAN_INT]}] + WIFI_LIST=$(nmcli --fields SSID,SECURITY,BARS device wifi list ifname "${WIRELESS_INTERFACES[WLAN_INT]}") + wifi_list + [[ "$ACTIVE_SSID" == "--" ]] && WIFI_SWITCH="~Scan\n~Manual/Hidden\n~Wi-Fi Off" || WIFI_SWITCH="~Scan\n~Disconnect\n~Manual/Hidden\n~Wi-Fi Off" + OPTIONS="${WIFI_LIST}\n${WIFI_SWITCH}\n" + }; } + } +} +function ethernet_interface_state() { + [[ ${#WIRED_INTERFACES[@]} -eq "0" ]] || { + WIRED_CON_STATE=$(nmcli device status | grep "ethernet" | head -1 | awk '{print $3}') + { [[ "$WIRED_CON_STATE" == "disconnected" ]] && WIRED_SWITCH="~Eth On"; } || { [[ "$WIRED_CON_STATE" == "connected" ]] && WIRED_SWITCH="~Eth Off"; } || { [[ "$WIRED_CON_STATE" == "unavailable" ]] && WIRED_SWITCH="***Wired Unavailable***"; } || { [[ "$WIRED_CON_STATE" == "connecting" ]] && WIRED_SWITCH="***Wired Initializing***"; } + OPTIONS="${OPTIONS}${WIRED_SWITCH}\n" + } +} +function rofi_menu() { + { [[ ${#WIRELESS_INTERFACES[@]} -gt "1" ]] && OPTIONS="${OPTIONS}~Change Wifi Interface\n~More Options"; } || { OPTIONS="${OPTIONS}~More Options"; } + { [[ "$WIRED_CON_STATE" == "connected" ]] && PROMPT="${WIRED_INTERFACES_PRODUCT}[$WIRED_INTERFACES]"; } || PROMPT="${WIRELESS_INTERFACES_PRODUCT[WLAN_INT]}[${WIRELESS_INTERFACES[WLAN_INT]}]" + SELECTION=$(echo -e "$OPTIONS" | rofi_cmd "$OPTIONS" "$WIDTH_FIX_MAIN" "-a 0") + SSID=$(echo "$SELECTION" | sed "s/\s\{2,\}/\|/g" | awk -F "|" '{print $1}') + selection_action +} +function rofi_cmd() { + { [[ -n "${1}" ]] && WIDTH=$(echo -e "$1" | awk '{print length}' | sort -n | tail -1) && ((WIDTH += $2)) && ((WIDTH = WIDTH / 2)); } || { ((WIDTH = $2 / 2)); } + rofi -dmenu -i -location "$LOCATION" -yoffset "$Y_AXIS" -xoffset "$X_AXIS" "$3" -theme "$RASI_DIR" -theme-str 'window{width: '"$WIDTH"'em;}textbox-prompt-colon{str:"'"$PROMPT"':";}'"$4"'' +} +function change_wireless_interface() { + { [[ ${#WIRELESS_INTERFACES[@]} -eq "2" ]] && { [[ $WLAN_INT -eq "0" ]] && WLAN_INT=1 || WLAN_INT=0; }; } || { + LIST_WLAN_INT="" + for i in "${!WIRELESS_INTERFACES[@]}"; do LIST_WLAN_INT=("${LIST_WLAN_INT[@]}${WIRELESS_INTERFACES_PRODUCT[$i]}[${WIRELESS_INTERFACES[$i]}]\n"); done + LIST_WLAN_INT[-1]=${LIST_WLAN_INT[-1]::-2} + CHANGE_WLAN_INT=$(echo -e "${LIST_WLAN_INT[@]}" | rofi_cmd "${LIST_WLAN_INT[@]}" "$WIDTH_FIX_STATUS") + for i in "${!WIRELESS_INTERFACES[@]}"; do [[ $CHANGE_WLAN_INT == "${WIRELESS_INTERFACES_PRODUCT[$i]}[${WIRELESS_INTERFACES[$i]}]" ]] && WLAN_INT=$i && break; done + } + wireless_interface_state && ethernet_interface_state + rofi_menu +} +function scan() { + [[ "$WIFI_CON_STATE" =~ "unavailable" ]] && change_wifi_state "Wi-Fi" "Enabling Wi-Fi connection" "on" && sleep 2 + notification "-t 0 Wifi" "Please Wait Scanning" + WIFI_LIST=$(nmcli --fields SSID,SECURITY,BARS device wifi list ifname "${WIRELESS_INTERFACES[WLAN_INT]}" --rescan yes) + wifi_list + wireless_interface_state && ethernet_interface_state + notification "-t 1 Wifi" "Please Wait Scanning" + rofi_menu +} +function wifi_list() { + WIFI_LIST=$(echo -e "$WIFI_LIST" | awk -F' +' '{ if (!seen[$1]++) print}' | awk '$1!="--" {print}' | awk '$1 !~ "^'"$ACTIVE_SSID"'"') + [[ $ASCII_OUT == "true" ]] && WIFI_LIST=$(echo -e "$WIFI_LIST" | sed 's/\(..*\)\*\{4,4\}/\1β–‚β–„β–†β–ˆ/g' | sed 's/\(..*\)\*\{3,3\}/\1β–‚β–„β–†_/g' | sed 's/\(..*\)\*\{2,2\}/\1β–‚β–„__/g' | sed 's/\(..*\)\*\{1,1\}/\1β–‚___/g') + [[ $CHANGE_BARS == "true" ]] && WIFI_LIST=$(echo -e "$WIFI_LIST" | sed 's/\(.*\)β–‚β–„β–†β–ˆ/\1'"$SIGNAL_STRENGTH_4"'/' | sed 's/\(.*\)β–‚β–„β–†_/\1'"$SIGNAL_STRENGTH_3"'/' | sed 's/\(.*\)β–‚β–„__/\1'"$SIGNAL_STRENGTH_2"'/' | sed 's/\(.*\)β–‚___/\1'"$SIGNAL_STRENGTH_1"'/' | sed 's/\(.*\)____/\1'"$SIGNAL_STRENGTH_0"'/') +} +function change_wifi_state() { + notification "$1" "$2" + nmcli radio wifi "$3" +} +function change_wired_state() { + notification "$1" "$2" + nmcli device "$3" "$4" +} +function net_restart() { + notification "$1" "$2" + nmcli networking off && sleep 3 && nmcli networking on +} +function disconnect() { + ACTIVE_SSID=$(nmcli -t -f GENERAL.CONNECTION dev show "${WIRELESS_INTERFACES[WLAN_INT]}" | cut -d ':' -f2) + notification "$1" "You're now disconnected from Wi-Fi network '$ACTIVE_SSID'" + nmcli con down id "$ACTIVE_SSID" +} +function check_wifi_connected() { + [[ "$(nmcli device status | grep "^${WIRELESS_INTERFACES[WLAN_INT]}." | awk '{print $3}')" == "connected" ]] && disconnect "Connection_Terminated" +} +function connect() { + check_wifi_connected + notification "-t 0 Wi-Fi" "Connecting to $1" + { [[ $(nmcli dev wifi con "$1" password "$2" ifname "${WIRELESS_INTERFACES[WLAN_INT]}" | grep -c "successfully activated") -eq "1" ]] && notification "Connection_Established" "You're now connected to Wi-Fi network '$1'"; } || notification "Connection_Error" "Connection can not be established" +} +function enter_passwword() { + PROMPT="Enter_Password" && PASS=$(echo "$PASSWORD_ENTER" | rofi_cmd "$PASSWORD_ENTER" 4 "-password") +} +function enter_ssid() { + PROMPT="Enter_SSID" && SSID=$(rofi_cmd "" 40) +} +function stored_connection() { + check_wifi_connected + notification "-t 0 Wi-Fi" "Connecting to $1" + { [[ $(nmcli dev wifi con "$1" ifname "${WIRELESS_INTERFACES[WLAN_INT]}" | grep -c "successfully activated") -eq "1" ]] && notification "Connection_Established" "You're now connected to Wi-Fi network '$1'"; } || notification "Connection_Error" "Connection can not be established" +} +function ssid_manual() { + enter_ssid + [[ -n $SSID ]] && { + enter_passwword + { [[ -n "$PASS" ]] && [[ "$PASS" != "$PASSWORD_ENTER" ]] && connect "$SSID" "$PASS"; } || stored_connection "$SSID" + } +} +function ssid_hidden() { + enter_ssid + [[ -n $SSID ]] && { + enter_passwword && check_wifi_connected + [[ -n "$PASS" ]] && [[ "$PASS" != "$PASSWORD_ENTER" ]] && { + nmcli con add type wifi con-name "$SSID" ssid "$SSID" ifname "${WIRELESS_INTERFACES[WLAN_INT]}" + nmcli con modify "$SSID" wifi-sec.key-mgmt wpa-psk + nmcli con modify "$SSID" wifi-sec.psk "$PASS" + } || [[ $(nmcli -g NAME con show | grep -c "$SSID") -eq "0" ]] && nmcli con add type wifi con-name "$SSID" ssid "$SSID" ifname "${WIRELESS_INTERFACES[WLAN_INT]}" + notification "-t 0 Wifi" "Connecting to $SSID" + { [[ $(nmcli con up id "$SSID" | grep -c "successfully activated") -eq "1" ]] && notification "Connection_Established" "You're now connected to Wi-Fi network '$SSID'"; } || notification "Connection_Error" "Connection can not be established" + } +} +function interface_status() { + local -n INTERFACES="$1" && local -n INTERFACES_PRODUCT="$2" + for i in "${!INTERFACES[@]}"; do + CON_STATE=$(nmcli device status | grep "^${INTERFACES[$i]}." | awk '{print $3}') + INT_NAME=${INTERFACES_PRODUCT[$i]}[${INTERFACES[$i]}] + [[ "$CON_STATE" == "connected" ]] && STATUS="$INT_NAME:\n\t$(nmcli -t -f GENERAL.CONNECTION dev show "${INTERFACES[$i]}" | awk -F '[:]' '{print $2}') ~ $(nmcli -t -f IP4.ADDRESS dev show "${INTERFACES[$i]}" | awk -F '[:/]' '{print $2}')" || STATUS="$INT_NAME: ${CON_STATE^}" + echo -e "$STATUS" + done +} +function status() { + OPTIONS="" + [[ ${#WIRED_INTERFACES[@]} -ne "0" ]] && ETH_STATUS="$(interface_status WIRED_INTERFACES WIRED_INTERFACES_PRODUCT)" && OPTIONS="${OPTIONS}${ETH_STATUS}" + [[ ${#WIRELESS_INTERFACES[@]} -ne "0" ]] && WLAN_STATUS="$(interface_status WIRELESS_INTERFACES WIRELESS_INTERFACES_PRODUCT)" && { [[ -n ${OPTIONS} ]] && OPTIONS="${OPTIONS}\n${WLAN_STATUS}" || OPTIONS="${OPTIONS}${WLAN_STATUS}"; } + ACTIVE_VPN=$(nmcli -g NAME,TYPE con show --active | awk '/:'"$VPN_PATTERN"'/ {sub(/:'"$VPN_PATTERN"'.*/, ""); print}') + [[ -n $ACTIVE_VPN ]] && OPTIONS="${OPTIONS}\n${ACTIVE_VPN}[VPN]: $(nmcli -g ip4.address con show "$ACTIVE_VPN" | awk -F '[:/]' '{print $1}')" + echo -e "$OPTIONS" | rofi_cmd "$OPTIONS" "$WIDTH_FIX_STATUS" "" "mainbox{children:[listview];}" +} +function share_pass() { + SSID=$(nmcli dev wifi show-password | grep -oP '(?<=SSID: ).*' | head -1) + PASSWORD=$(nmcli dev wifi show-password | grep -oP '(?<=Password: ).*' | head -1) + OPTIONS="SSID: ${SSID}\nPassword: ${PASSWORD}" + [[ -x "$(command -v qrencode)" ]] && OPTIONS="${OPTIONS}\n~QrCode" + SELECTION=$(echo -e "$OPTIONS" | rofi_cmd "$OPTIONS" "$WIDTH_FIX_STATUS" "-a -1" "mainbox{children:[listview];}") + selection_action +} +function gen_qrcode() { + DIRECTIONS=("Center" "Northwest" "North" "Northeast" "East" "Southeast" "South" "Southwest" "West") + TMP_SSID="${SSID// /_}" + [[ -e $QRCODE_DIR$TMP_SSID.png ]] || qrencode -t png -o "$QRCODE_DIR$TMP_SSID".png -l H -s 25 -m 2 --dpi=192 "WIFI:S:""$SSID"";T:""$(nmcli dev wifi show-password | grep -oP '(?<=Security: ).*' | head -1)"";P:""$PASSWORD"";;" + rofi_cmd "" "0" "" "entry{enabled:false;}window{location:""${DIRECTIONS[QRCODE_LOCATION]}"";border-radius:6mm;padding:1mm;width:100mm;height:100mm; + background-image:url(\"$QRCODE_DIR$TMP_SSID.png\",both);}" +} +function manual_hidden() { + OPTIONS="~Manual\n~Hidden" && SELECTION=$(echo -e "$OPTIONS" | rofi_cmd "$OPTIONS" "$WIDTH_FIX_STATUS" "" "mainbox{children:[listview];}") + selection_action +} +function vpn() { + ACTIVE_VPN=$(nmcli -g NAME,TYPE con show --active | awk '/:'"$VPN_PATTERN"'/ {sub(/:'"$VPN_PATTERN"'.*/, ""); print}') + [[ $ACTIVE_VPN ]] && OPTIONS="~Deactive $ACTIVE_VPN" || OPTIONS="$(nmcli -g NAME,TYPE connection | awk '/:'"$VPN_PATTERN"'/ {sub(/:'"$VPN_PATTERN"'.*/, ""); print}')" + VPN_ACTION=$(echo -e "$OPTIONS" | rofi_cmd "$OPTIONS" "$WIDTH_FIX_STATUS" "" "mainbox {children:[listview];}") + [[ -n "$VPN_ACTION" ]] && { { [[ "$VPN_ACTION" =~ "~Deactive" ]] && nmcli connection down "$ACTIVE_VPN" && notification "VPN_Deactivated" "$ACTIVE_VPN"; } || { + notification "-t 0 Activating_VPN" "$VPN_ACTION" + VPN_OUTPUT=$(nmcli connection up "$VPN_ACTION" 2>/dev/null) + { [[ $(echo "$VPN_OUTPUT" | grep -c "Connection successfully activated") -eq "1" ]] && notification "VPN_Successfully_Activated" "$VPN_ACTION"; } || notification "Error_Activating_VPN" "Check your configuration for $VPN_ACTION" + }; } +} +function more_options() { + OPTIONS="" + [[ "$WIFI_CON_STATE" == "connected" ]] && OPTIONS="~Share Wifi Password\n" + OPTIONS="${OPTIONS}~Status\n~Restart Network" + [[ $(nmcli -g NAME,TYPE connection | awk '/:'"$VPN_PATTERN"'/ {sub(/:'"$VPN_PATTERN"'.*/, ""); print}') ]] && OPTIONS="${OPTIONS}\n~VPN" + [[ -x "$(command -v nm-connection-editor)" ]] && OPTIONS="${OPTIONS}\n~Open Connection Editor" + SELECTION=$(echo -e "$OPTIONS" | rofi_cmd "$OPTIONS" "$WIDTH_FIX_STATUS" "" "mainbox {children:[listview];}") + selection_action +} +function selection_action() { + case "$SELECTION" in + "~Disconnect") disconnect "Connection_Terminated" ;; + "~Scan") scan ;; + "~Status") status ;; + "~Share Wifi Password") share_pass ;; + "~Manual/Hidden") manual_hidden ;; + "~Manual") ssid_manual ;; + "~Hidden") ssid_hidden ;; + "~Wi-Fi On") change_wifi_state "Wi-Fi" "Enabling Wi-Fi connection" "on" ;; + "~Wi-Fi Off") change_wifi_state "Wi-Fi" "Disabling Wi-Fi connection" "off" ;; + "~Eth Off") change_wired_state "Ethernet" "Disabling Wired connection" "disconnect" "$WIRED_INTERFACES" ;; + "~Eth On") change_wired_state "Ethernet" "Enabling Wired connection" "connect" "$WIRED_INTERFACES" ;; + "***Wi-Fi Disabled***") ;; + "***Wired Unavailable***") ;; + "***Wired Initializing***") ;; + "~Change Wifi Interface") change_wireless_interface ;; + "~Restart Network") net_restart "Network" "Restarting Network" ;; + "~QrCode") gen_qrcode ;; + "~More Options") more_options ;; + "~Open Connection Editor") nm-connection-editor ;; + "~VPN") vpn ;; + *) + [[ -n "$SELECTION" ]] && [[ "$WIFI_LIST" =~ .*"$SELECTION".* ]] && { + [[ "$SSID" == "*" ]] && SSID=$(echo "$SELECTION" | sed "s/\s\{2,\}/\|/g " | awk -F "|" '{print $3}') + { [[ "$ACTIVE_SSID" == "$SSID" ]] && nmcli con up "$SSID" ifname "${WIRELESS_INTERFACES[WLAN_INT]}"; } || { + [[ "$SELECTION" =~ "WPA2" ]] || [[ "$SELECTION" =~ "WEP" ]] && enter_passwword + { [[ -n "$PASS" ]] && [[ "$PASS" != "$PASSWORD_ENTER" ]] && connect "$SSID" "$PASS"; } || stored_connection "$SSID" + } + } + ;; + esac +} +function main() { + initialization && rofi_menu +} +main diff --git a/unix/utils/root.sh b/unix/utils/root.sh new file mode 100755 index 0000000..a7807bc --- /dev/null +++ b/unix/utils/root.sh @@ -0,0 +1,152 @@ +#!/bin/bash + +# Created By: srdusr +# Created On: Mon 19 Feb 2025 14:18:00 PM CAT +# Project: Backup and restore system files to/from home extras (system dotfiles) directory + +# Dependencies: None +# NOTE: The backups will be stored in the ~/extras directory, preserving the original file structure. Run as sudo or be prompted for password +# Example usage: +# To backup a specific file: root.sh --backup /some_directory/some_file.conf +# To restore a specific file: root.sh --restore ~/extras/some_directory/some_file.conf +# To restore all files: root.sh --restore +# + +# Use $SUDO_USER to get the original user when run with sudo, or fall back to the current user if not +BASE_DIR="/home/${SUDO_USER:-$(whoami)}/extras" + +if [ "$EUID" -eq 0 ] && [ "$SUDO_USER" = "" ]; then + echo "You are running this script directly as root, not through sudo!" + exit 1 +fi + +if [ "$EUID" -ne 0 ]; then + echo "Elevating to sudo..." + exec sudo "$0" "$@" # Re-run the script with sudo +fi + +# Create directories if they do not exist +create_directory() { + local dir=$1 + if [ ! -d "$dir" ]; then + echo "Creating directory: $dir" + mkdir -p "$dir" + else + echo "Directory already exists: $dir" + fi +} + +# Backup files +backup_to_extras() { + local src=$1 + + # Ensure the file or directory exists + if [ -e "$src" ]; then + # Strip the leading / from src to avoid double slashes + local stripped_src="${src#/}" + + # Determine the destination path + dest_dir="$BASE_DIR/$(dirname "$stripped_src")" # Get the directory part of the source + dest_file="$BASE_DIR/$stripped_src" # Get the full destination file path + + # Debug: Print paths + echo "Source file: $src" + echo "Destination directory: $dest_dir" + echo "Destination file: $dest_file" + + # Create the necessary directories in extras if they don't exist + create_directory "$dest_dir" + + # Backup the file to extras + echo "Backing up $src to $dest_file" + cp -p "$src" "$dest_file" + + # Set permission to user + chown "$SUDO_USER:$SUDO_USER" "$dest_file" + + echo "Backup of $src completed." + else + echo "Error: The file or directory '$src' does not exist." + fi +} + +# Restore files +restore_from_extras() { + local src=$1 + + # Ensure the file or directory exists in extras + if [ -e "$src" ]; then + # Strip the leading / from src to avoid double slashes + local stripped_src="${src#/}" + + # Determine the destination path + dest_dir="/$(dirname "$stripped_src")" # Get the directory part of the source + dest_file="/$stripped_src" # Get the full destination file path + + # Debug: Print paths + echo "Source file: $src" + echo "Destination directory: $dest_dir" + echo "Destination file: $dest_file" + + # Create the necessary directories in the system if they don't exist + create_directory "$dest_dir" + + # Backup the file if it exists before restoring + if [ -e "$dest_file" ]; then + echo "File $dest_file exists, creating a backup..." + mv "$dest_file" "$dest_file.bak" + echo "Backup created at $dest_file.bak" + fi + + # Restore the file from extras + echo "Restoring $src to $dest_file" + cp -p "$BASE_DIR/$stripped_src" "$dest_file" + + # Set permissions for the restored file + chmod 644 "$dest_file" + + echo "Restore of $src completed." + else + echo "Error: The file or directory '$src' does not exist in extras." + fi +} + +# Restore all files from extras +restore_all_from_extras() { + echo "Restoring all files and directories from extras..." + + # Loop over all files and directories in BASE_DIR and restore them + find "$BASE_DIR" -type f | while read -r file; do + restore_from_extras "$file" + done + + echo "Restore completed." +} + +# Backup system files based on user input +if [ "$1" == "--backup" ]; then + if [ "$2" = "" ]; then + echo "Error: Please specify the file or directory to backup." + exit 1 + fi + + # Perform the backup + echo "Backing up system files to extras..." + backup_to_extras "$2" + echo "Backup completed." + + # Restore system files based on user input +elif [ "$1" == "--restore" ]; then + if [ "$2" = "" ]; then + # If no specific file is provided, restore everything + restore_all_from_extras + else + # Restore a specific file or directory + echo "Restoring system files from extras..." + restore_from_extras "$2" + echo "Restore completed." + fi + +else + echo "Invalid option. Use '--backup' to backup or '--restore' to restore." +fi diff --git a/unix/utils/run_with_display.sh b/unix/utils/run_with_display.sh new file mode 100755 index 0000000..5f1f3a0 --- /dev/null +++ b/unix/utils/run_with_display.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +run_with_display() { + output=$("$@" 2>&1) + exit_status=$? + + if [[ $exit_status -ne 0 && ("$output" =~ "cannot open display" || "$output" =~ "DISPLAY environment variable is missing") ]]; then + DISPLAY=:0 "$@" + else + echo "$output" + return $exit_status + fi +} + +# Call this script with any command you want to run +command=$1 +shift +run_with_display "$command" "$@" + diff --git a/unix/utils/scratchpad b/unix/utils/scratchpad new file mode 100755 index 0000000..d47dafe --- /dev/null +++ b/unix/utils/scratchpad @@ -0,0 +1,147 @@ +#!/bin/bash + +# Created By: srdusr +# Created On: Tue 07 Mar 2023 15:06:47 PM CAT +# Project: Agnostic scratchpad/dropdown terminal that works on most window managers + +# Dependencies: wmctrl, xprop, xdo, xdotool +# NOTE: Ensure script is included in system's path and can therefore be invoked with the command 'scratchpad'. +# Furthermore make sure the terminal is using x11 as a backend in wayland to allow this to work. +# Example: wezterm.lua: enable_wayland = false, +# kitty.conf: linux_display_server x11 + +# Set the environment variables to x11 to allow working in Wayland +#export GDK_BACKEND=x11 +#export QT_QPA_PLATFORM=xcb +#export WAYLAND_DISPLAY="" +#export WINIT_UNIX_BACKEND=x11 +#export WAYLAND_DISPLAY=wayland-0 + +# Supported terminals and dropdown class +supported_terminals=("wezterm" "kitty" "alacritty") + +# Check if any supported terminal with scratchpad class is running +for term in "${supported_terminals[@]}"; do + if pgrep -f "$term.*--class scratchpad" >/dev/null; then + my_term="$term" + break + fi +done + +# If no supported terminal is running, start the first available one +if [ "$my_term" = "" ]; then + for term in "${supported_terminals[@]}"; do + if command -v "$term" >/dev/null 2>&1; then + my_term="$term" + break + fi + done + if [ "$my_term" = "" ]; then + echo "No supported terminal found." && exit 1 + fi + + # Start terminal with scratchpad class + case "$my_term" in + "wezterm") wezterm start --class scratchpad -e tmux new-session -A -s tmux -e bash & ;; + "kitty") kitty --class scratchpad tmux new-session -A -s tmux -e bash & ;; + "alacritty") alacritty --class scratchpad -e tmux new-session -A -s tmux -e bash & ;; + esac +fi + +# Get the window ID of the scratchpad terminal +id="$(xdo id -N scratchpad)" +# Get the ID of the currently focused window +focused_id="$(xdotool getwindowfocus)" + +# Get class of the currently focused window +focused_class="$(xprop -id "$focused_id" WM_CLASS 2>/dev/null | awk -F '"' '{print $4}')" + +# Toggle visible/hide and smart focus +if [ "$id" != "" ]; then + if xwininfo -id "$id" | grep "Map State: IsViewable" >/dev/null; then + if [ "$focused_id" = "$id" ] || [ "$focused_class" = "scratchpad" ]; then + # Scratchpad is focused, hide it + dimensions="$(xwininfo -id "$id" | awk '/Width:|Height:/ { printf("%s=%s;", tolower($1), $2) }')" + xdo hide "$id" 2>/dev/null + else + # Scratchpad is visible but not focused + xdotool windowactivate "$id" + xdotool windowfocus "$id" + hyprctl dispatch focuswindow scratchpad + fi + else + # Scratchpad is hidden, show and focus it + xdo show "$id" + xdotool windowsize "$id" "$(echo "$dimensions" | tr ';' ' ')" 2>/dev/null + xdotool windowactivate "$id" + xdotool windowfocus "$id" + hyprctl dispatch focuswindow scratchpad + fi +fi + +## Get the window ID of the PiP window by title +#pip_id="$(xdo id -n "Picture-in-Picture")" +# +## Toggle scratchpad terminal visibility +#if [ "$id" != "" ]; then +# if xwininfo -id "$id" | grep "Map State: IsViewable" >/dev/null; then +# # Scratchpad is visible, hide it +# dimensions="$(xwininfo -id "$id" | awk '/Width:|Height:/ { printf("%s=%s;", tolower($1), $2) }')" +# xdo hide "$id" 2>/dev/null +# else +# # Scratchpad is hidden, show it and restore dimensions +# xdo show "$id" +# xdotool windowsize "$id" "$(echo "$dimensions" | tr ';' ' ')" 2>/dev/null +# xdotool windowactivate "$id" +# xdotool windowfocus "$id" +# +# # Adjust layer based on PiP window presence +# if [ "$pip_id" != "" ]; then +# hyprctl dispatch layer "$id" 1 +# else +# hyprctl dispatch layer "$id" 0 +# fi +# +# hyprctl dispatch focuswindow scratchpad +# fi +#fi + +## Get the window ID of the scratchpad terminal +#id=$(hyprctl clients -j | jq -r '.[] | select(.class=="scratchpad").address') +# +## Toggle scratchpad terminal visibility +#if [ -n "$id" ]; then +# focused=$(hyprctl activewindow -j | jq -r '.address') +# if [ "$focused" = "$id" ]; then +# # Scratchpad is focused, minimize it +# hyprctl dispatch move "address:$id workspace silent 0" +# else +# # Show scratchpad, set above normal windows but below PiP +# hyprctl dispatch movetoworkspace current address:$id +# hyprctl dispatch focuswindow address:$id +# hyprctl dispatch layer "address:$id above" +# hyprctl dispatch focuswindow address:$id +# fi +#fi + +## Get the window ID of the PiP window by title +#pip_id=$(hyprctl clients -j | jq -r '.[] | select(.title=="Picture-in-Picture").address') +# +## Toggle scratchpad terminal visibility +#if [ -n "$id" ]; then +# focused=$(hyprctl activewindow -j | jq -r '.address') +# if [ "$focused" = "$id" ]; then +# # Scratchpad is focused, minimize it +# hyprctl dispatch move "address:$id workspace silent 0" +# else +# # Show scratchpad, set above normal windows but below PiP +# hyprctl dispatch movetoworkspace current address:$id +# hyprctl dispatch focuswindow address:$id +# if [ -n "$pip_id" ]; then +# hyprctl dispatch layer "address:$id below" +# else +# hyprctl dispatch layer "address:$id above" +# fi +# hyprctl dispatch focuswindow address:$id +# fi +#fi diff --git a/unix/utils/screenshot b/unix/utils/screenshot new file mode 100755 index 0000000..b209574 --- /dev/null +++ b/unix/utils/screenshot @@ -0,0 +1,102 @@ +#!/bin/sh + +# === Config: directory for QEMU sockets === +QEMU_SOCK_DIR="/home/$(whoami)/virt/machines" + +# === Send 'print' key to QEMU monitor === +send_print_to_qemu() { + monitor_socket=$(find "$QEMU_SOCK_DIR" -maxdepth 1 -type s -name '*-monitor.socket' | head -n1) + if [ "$monitor_socket" = "" ]; then + echo "No QEMU monitor socket found." + return 1 + fi + echo "Using QEMU monitor socket: $monitor_socket" + if ! printf "sendkey print\n" | socat - UNIX-CONNECT:"$monitor_socket"; then + echo "Failed to send key via socat." + return 1 + fi + echo "Sent 'sendkey print' to QEMU successfully." + return 0 +} + +# === Detect if current window is QEMU (X11 only) === +if [ "$DISPLAY" != "" ] && command -v xprop >/dev/null 2>&1; then + win_id=$(xprop -root _NET_ACTIVE_WINDOW | awk -F' ' '{print $5}') + class=$(xprop -id "$win_id" WM_CLASS 2>/dev/null | awk -F'"' '{print $4}') + if [ "$class" = "qemu-system-x86_64" ]; then + send_print_to_qemu && exit 0 + # if sending fails, optionally fallback or exit anyway + exit 1 + fi +fi +#if command -v hyprctl >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then +# focused_class="$(hyprctl activewindow -j | jq -r '.class' 2>/dev/null)" +# if [ "$focused_class" = "qemu-system-x86_64" ]; then +# send_print_to_qemu && exit 0 +# fi +#fi + +DIR="$HOME/pictures/screenshots" +OUTPUT_DIR="$HOME/documents/ocr_output" + +# Create the directories if they don't exist +[ ! -d "$DIR" ] && mkdir -pv "$DIR" +[ ! -d "$OUTPUT_DIR" ] && mkdir -pv "$OUTPUT_DIR" + +file="$DIR/screenshot_$(date '+%Y-%m-%d_%H-%M-%S').png" +text_file="$OUTPUT_DIR/ocr_output_$(date '+%Y-%m-%d_%H-%M-%S')" + +copy_to_clipboard() { + if [ "$WAYLAND_DISPLAY" != "" ]; then + wl-copy <"$1" + elif [ "$DISPLAY" != "" ]; then + xclip -selection clipboard -i <"$1" + else + echo "No display server detected. Cannot copy to clipboard." + return 1 + fi +} + +case "$1" in +screen) grim "$file" ;; +output) slurp -o -r | grim -g - "$file" ;; +area) slurp | grim -g - "$file" ;; +output-area) slurp -o | grim -g - "$file" ;; +ocr) slurp | grim -g - "$file" && tesseract "$file" "$text_file" ;; +ocr-clipboard) + slurp | grim -g - "$file" && tesseract "$file" "$text_file" + if [ -f "$text_file.txt" ]; then + copy_to_clipboard "$text_file.txt" + CLIP_STATUS=$? + if [ "$CLIP_STATUS" -eq 0 ]; then + command rm -f "$text_file.txt" + notify-send -t 10000 --app-name "Screenshot" "OCR to Clipboard" "Text copied to clipboard." + echo "OCR output copied to clipboard and file deleted." + else + notify-send -t 10000 --app-name "Screenshot" "Clipboard Copy Failed" "Failed to copy text to clipboard." + echo "Failed to copy text to clipboard." + fi + else + notify-send -t 10000 --app-name "Screenshot" "OCR Error" "OCR process failed." + exit 1 + fi + ;; +*) + echo "Invalid argument" + notify-send -t 10000 --app-name "Screenshot" "Screenshot" "Something went wrong." + exit 1 + ;; +esac + +if [ "$1" = "ocr" ]; then + if [ -f "$text_file" ]; then + notify-send -t 10000 --app-name "Screenshot" "OCR Complete" "Text saved to $text_file" + echo "OCR output saved to: $text_file" + else + notify-send -t 10000 --app-name "Screenshot" "OCR Error" "OCR process failed." + exit 1 + fi +else + notify-send -t 10000 --app-name "Screenshot" "Screenshot" "Saved as $file" + echo "$file" +fi diff --git a/unix/utils/sendkeys.awk b/unix/utils/sendkeys.awk new file mode 100755 index 0000000..16a3fa9 --- /dev/null +++ b/unix/utils/sendkeys.awk @@ -0,0 +1,86 @@ +#!/usr/bin/env awk -f +# +# AWK script to send multiple `sendkey` commands to a QEMU virtual machine. +# It writes at a rate of roughly 40 keys per second, due to lower delays +# resulting in garbage output. +# +# It makes use of a TCP client created by an external utility, such as OpenBSD +# Netcat, to interact with QEMU's monitor and send a stream of `sendkey` +# commands. This is a practical way to transfer a small file or to script +# interactions with a terminal user interface. + +BEGIN { + # Set default delay if not provided via command-line args + if (!delay) { + delay = 0.025 + } + + # Define key mappings for common characters and symbols + key["#"] = "backspace" + key[" "] = "tab" + key[" "] = "spc" + key["!"] = "shift-1" + key["\""] = "shift-apostrophe" + key["#"] = "shift-3" + key["$"] = "shift-4" + key["%"] = "shift-5" + key["&"] = "shift-7" + key["'"] = "apostrophe" + key["("] = "shift-9" + key[")"] = "shift-0" + key["*"] = "shift-8" + key["+"] = "shift-equal" + key[","] = "comma" + key["-"] = "minus" + key["."] = "dot" + key["/"] = "slash" + key[":"] = "shift-semicolon" + key[";"] = "semicolon" + key["<"] = "shift-comma" + key["="] = "equal" + key[">"] = "shift-dot" + key["?"] = "shift-slash" + key["@"] = "shift-2" + + # Map numbers + for (i = 48; i < 48 + 10; ++i) { + number = sprintf("%c", i) + key[number] = number + } + + # Map letters A-Z, including shift + for (i = 65; i < 65 + 26; ++i) { + key[sprintf("%c", i)] = sprintf("shift-%c", i + 32) + } + + # Other symbols + key["["] = "bracket_left" + key["\\"] = "backslash" + key["]"] = "bracket_right" + key["^"] = "shift-6" + key["_"] = "shift-minus" + key["`"] = "grave_accent" + key["{"] = "shift-bracket_left" + key["|"] = "shift-backslash" + key["}"] = "shift-bracket_right" + key["~"] = "shift-grave_accent" + key[""] = "delete" + + # Handle Super and Caps Lock key mappings (for remapping Caps to Super) + key["capslock"] = "super" + key["super"] = "super" + + # Handle other keys if needed +} + +{ + split($0, chars, "") + for (i = 1; i <= length($0); i++) { + # Print sendkey command for the character, mapping it through the key[] array + if (key[chars[i]] != "") { + printf("sendkey %s\n", key[chars[i]]) + } + system("sleep " delay) # Sleep for the defined delay + } + printf "sendkey ret\n" # Send "return" (enter) key at the end +} diff --git a/unix/utils/sext b/unix/utils/sext new file mode 100755 index 0000000..b120929 --- /dev/null +++ b/unix/utils/sext @@ -0,0 +1,47 @@ +#!/bin/bash + +#Sext (Shutdown Exit) +# List of package managers to check for +package_managers=("emerge" "apt" "dnf" "pacman" "zypper") + +# Set how often to check (in seconds) +check_interval=30 + +# Check if any package manager is running +is_package_manager_running() { + for pm in "${package_managers[@]}"; do + if pgrep -x "$pm" >/dev/null; then + return 0 # Return 0 (true) if any package manager is running + fi + done + return 1 # Return 1 (false) if no package manager is running +} + +# Safely shutdown the system +safe_shutdown() { + # Try using shutdown without sudo first + if shutdown -h now; then + echo "Shutdown initiated successfully using 'shutdown'." + # If shutdown fails, try using poweroff + elif poweroff; then + echo "Shutdown initiated successfully using 'poweroff'." + # If poweroff fails, try using sudo poweroff + elif sudo poweroff; then + echo "Shutdown initiated successfully with 'sudo poweroff'." + # If both shutdown and poweroff fail, try with sudo shutdown + elif sudo shutdown -h now; then + echo "Shutdown initiated successfully with 'sudo shutdown'." + else + echo "Shutdown command failed. Please check your system configuration." + fi +} + +# Loop until no package manager process is running +while is_package_manager_running; do + echo "Package manager is still running. Checking again in $check_interval seconds..." + sleep "$check_interval" +done + +# Once the process completes, initiate a safe shutdown +echo "Package manager has finished. Attempting to shutdown..." +safe_shutdown diff --git a/unix/utils/track-books.sh b/unix/utils/track-books.sh new file mode 100755 index 0000000..f13add8 --- /dev/null +++ b/unix/utils/track-books.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Created By: srdusr +# Created On: Wed 25 Oct 2023 13:45:52 CAT +# Project: Simple script to track most recent books opened, mainly for neovim usage. + +# Dependencies: inotify-tools + +books_directory="$HOME/documents/books" +recent_books_file="$HOME/.config/nvim/tmp/recent_books.txt" + +inotifywait -m -e CREATE -e OPEN -r "$books_directory" | + while read -r path action file; do + if [[ $file == *.pdf || $file == *.epub ]]; then + echo "$path/$file" >>"$recent_books_file" + # Remove duplicates and overwrite the recent_books_file + sort -u -o "$recent_books_file" "$recent_books_file" + fi + done diff --git a/unix/utils/umnt b/unix/utils/umnt new file mode 100755 index 0000000..6d9b788 --- /dev/null +++ b/unix/utils/umnt @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# unmount disk +set -e +. ~/etc/colors/current + +lsblk -Po NAME,SIZE,MOUNTPOINT,FSTYPE,LABEL | while read -r a; do + eval "$a" + + [ "$MOUNTPOINT" ] && ! grep -iq "\s$MOUNTPOINT\s" /etc/fstab && + printf "%-4s:%s:%s:<- %s\n" \ + "$NAME" \ + "$SIZE" \ + "${LABEL:-unnamed}" \ + "${MOUNTPOINT//$HOME/\~}" + + done | column -ts':' -o' ' | menu -p unmount | { + read -r NAME _ + eval "$(lsblk -Po LABEL,MOUNTPOINT "/dev/$NAME")" + notify-send summary "$NAME: $LABEL\n${MOUNTPOINT/$HOME/\~}" + sudo umount "$MOUNTPOINT" + sudo rmdir "$HOME/dev/$NAME" || : +} diff --git a/unix/utils/waypipe_app b/unix/utils/waypipe_app new file mode 100755 index 0000000..a8520bc --- /dev/null +++ b/unix/utils/waypipe_app @@ -0,0 +1,16 @@ +#!/bin/sh + +# Usage: ./waypipe_app user@remote +# Example: ./waypipe_app user@remote gedit + +if [ $# -lt 2 ]; then + echo "Usage: $0 user@remote [args...]" + exit 1 +fi + +REMOTE=$1 +shift +APP="$@" + +# Run the app remotely, forward display over SSH with waypipe +waypipe ssh "$REMOTE" $APP diff --git a/unix/utils/wayvnc_session b/unix/utils/wayvnc_session new file mode 100755 index 0000000..1f2f30b --- /dev/null +++ b/unix/utils/wayvnc_session @@ -0,0 +1,10 @@ +#!/bin/sh + +# Usage: ./wayvnc_session [address] [port] +# Default: 0.0.0.0 5900 + +ADDR=${1:-0.0.0.0} +PORT=${2:-5900} + +echo "Starting Wayland VNC server on $ADDR:$PORT" +wayvnc $ADDR $PORT diff --git a/unix/utils/window_manager_name.sh b/unix/utils/window_manager_name.sh new file mode 100755 index 0000000..cfff9ee --- /dev/null +++ b/unix/utils/window_manager_name.sh @@ -0,0 +1,29 @@ +#! /bin/bash + + +windowManagerName () { + local window=$( + xprop -root -notype + ) + + local identifier=$( + echo "${window}" | + awk '$1=="_NET_SUPPORTING_WM_CHECK:"{print $5}' + ) + + local attributes=$( + xprop -id "${identifier}" -notype -f _NET_WM_NAME 8t + ) + + local name=$( + echo "${attributes}" | + grep "_NET_WM_NAME = " | + cut --delimiter=' ' --fields=3 | + cut --delimiter='"' --fields=2 + ) + + echo "${name}" +} + + +windowManagerName diff --git a/unix/utils/xtouch b/unix/utils/xtouch new file mode 100755 index 0000000..a5eb23b --- /dev/null +++ b/unix/utils/xtouch @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +check_valid() { + if [ -f "$1" ]; then + echo "The file "$1" already exists!" + exit 1 + fi +} + +create_script() { + touch "$1" + chmod +x "$1" + echo "$0: $2 script file $1 created with exec permissions" + echo -e '#!/usr/bin/env '"$2" > "$1" +} + +usage() { + echo -e "Quickly create executable script\n" + echo "Usage:" + echo " xtouch [ -w FILE LANG ]" + echo -e " xtouch ( -h | --help )\n" + echo "Arguments:" + echo "FILE Name to give the scripts" + echo -e "LANG Language in which the script will be written\n" + echo "Options:" + echo " -w FILE LANG Creates a executable script file named ." + echo " -h --help Show this screen." +} + +case "$1" in + '-w') + check_valid "$2" + create_script "$2" "$3" + ;; + *) + usage + ;; +esac diff --git a/unix/virt/checksum.sh b/unix/virt/checksum.sh new file mode 100755 index 0000000..adb6a24 --- /dev/null +++ b/unix/virt/checksum.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Check if the correct number of arguments are provided +if [[ $# -ne 2 ]]; then + echo "Usage: checksum " + exit 1 +fi + +FILE=$1 +CHECKSUM_PAGE=$2 + +# Fetch the HTML content of the page +HTML_CONTENT=$(curl -s "$CHECKSUM_PAGE") + +# Try searching for checksums with a more targeted approach, like matching a checksum pattern or look for files with checksum labels +CHECKSUM=$(echo "$HTML_CONTENT" | grep -oP '([a-f0-9]{64})' | head -n 1) # Try specifically looking for SHA256 checksum patterns + +# Check if any checksum was found +if [[ -z "$CHECKSUM" ]]; then + echo "Checksum not found on the page." + exit 1 +fi + +# Calculate the checksum of the file locally +LOCAL_CHECKSUM=$(sha256sum "$FILE" | awk '{print $1}') + +echo "Local checksum: $LOCAL_CHECKSUM" +echo "Remote checksum: $CHECKSUM" + +# Compare the local checksum with the one from the website +if [[ "$LOCAL_CHECKSUM" == "$CHECKSUM" ]]; then + echo "The checksums match! The file is verified." + +else + echo "The checksums do not match. The file may be corrupted or tampered with." +fi diff --git a/unix/virt/dos.sh b/unix/virt/dos.sh new file mode 100755 index 0000000..0b50c4b --- /dev/null +++ b/unix/virt/dos.sh @@ -0,0 +1,998 @@ +#!/usr/bin/env bash + +# Set variables +HOST_DIR="virt" +VM_NAME="dos" +VM_SIZE="80G" # Disk size in GB +VM_RAM="8G" # RAM size +VM_CPU="6" # Number of virtual CPUs +CORES=$((VM_CPU / 2)) +THREADS_PER_CORE=2 +SOCKETS=1 + +VM_DIR="$HOST_DIR/machines" +IMAGE_DIR="$HOST_DIR/images" +WIN_ISO_DIR="${IMAGE_DIR}/${VM_NAME}" # Directory for Windows ISO +VM_DIR="$WIN_ISO_DIR" +SOCKET_DIR="$VM_DIR" +SHARED_DIR="${HOST_DIR}/shared" +FIRMWARE_DIR="${HOST_DIR}/firmware" +TPM_DIR="$WIN_ISO_DIR" +TPM_SOCKET="${WIN_ISO_DIR}/${VM_NAME}.swtpm-sock" +GUEST_PORT=22 +QCOW2_FILE="${VM_DIR}/${VM_NAME}.qcow2" +RAW_FILE="${VM_DIR}/${VM_NAME}.raw" + +# Anti-detection: Generate realistic hardware identifiers +REAL_MAC="00:1A:2B:3C:4D:5E" # Example Dell MAC - replace with your choice +REAL_SERIAL="$(openssl rand -hex 8 | tr '[:lower:]' '[:upper:]')" +REAL_UUID="$(uuidgen)" +REAL_VENDOR="Dell Inc." +REAL_PRODUCT="OptiPlex 7090" +REAL_VERSION="01" +REAL_FAMILY="OptiPlex" + +# Try to find an available host port starting from 22220 +HOST_PORT_START=22220 +HOST_PORT_END=22300 + +for ((port = HOST_PORT_START; port <= HOST_PORT_END; port++)); do + if ! ss -tuln | grep -q ":$port\b"; then + HOST_PORT=$port + echo "Using available port: $HOST_PORT" + break + fi +done + +if [[ $port -gt $HOST_PORT_END ]]; then + echo "Error: No available ports found between $HOST_PORT_START and $HOST_PORT_END" >&2 + exit 1 +fi + +# Set SMP configuration +SMP_CONFIG="cores=$CORES,threads=$THREADS_PER_CORE,sockets=$SOCKETS" + +# Create necessary directories +mkdir -p "${HOME}/${HOST_DIR}" +mkdir -p "$IMAGE_DIR" "$SHARED_DIR" "$FIRMWARE_DIR" +mkdir -p "$WIN_ISO_DIR" "$VM_DIR" +mkdir -p "${WIN_ISO_DIR}/unattended" + +# Define ISO paths and URLs +ISO_VIRTIO="${WIN_ISO_DIR}/virtio-win.iso" +ISO_UNATTENDED="${WIN_ISO_DIR}/unattended.iso" + +# Find Windows ISO with flexible pattern matching +find_windows_iso() { + # Check if directory exists + if [[ ! -d "$WIN_ISO_DIR" ]]; then + mkdir -p "$WIN_ISO_DIR" + fi + + # Try to find any Windows ISO using case-insensitive patterns + local found_iso + found_iso=$(find "$WIN_ISO_DIR" -maxdepth 1 -type f \( \ + -iname "*win11*.iso" -o \ + -iname "*win*11*.iso" -o \ + -iname "Win*.iso" -o \ + -iname "Win11*.iso" -o \ + -iname "Win*11*.iso" -o \ + -iname "*windows*11*.iso" -o \ + -iname "*windows11*.iso" \ + \) -exec stat --format="%Y %n" {} \; | sort -n | tail -n 1 | cut -d' ' -f2-) + + if [[ -n "$found_iso" && -f "$found_iso" ]]; then + echo "$found_iso" + return 0 + fi + + return 1 +} + +# Define download URLs +VIRTIO_ISO_URL="https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" + +# Print colored messages +print_info() { echo -e "\033[1;34m[INFO]\033[0m $1" >&2; } +print_success() { echo -e "\033[1;32m[SUCCESS]\033[0m $1" >&2; } +print_warning() { echo -e "\033[1;33m[WARNING]\033[0m $1" >&2; } +print_error() { echo -e "\033[1;31m[ERROR]\033[0m $1" >&2; } + +# Helper: verify file integrity +verify_file() { + local file="$1" + local expected_sha256="$2" + + if [[ ! -f "$file" ]]; then + return 1 + fi + + if [[ -n "$expected_sha256" ]]; then + local actual_sha256 + actual_sha256=$(sha256sum "$file" | cut -d' ' -f1) + if [[ "$actual_sha256" != "$expected_sha256" ]]; then + print_error "File integrity check failed for $file" + return 1 + fi + fi + + return 0 +} + +# Helper: download file with verification +download_file() { + local url="$1" + local dest="$2" + local expected_sha256="$3" + local allow_failure="$4" + + # Check if file exists and is valid + if [[ -f "$dest" ]]; then + if verify_file "$dest" "$expected_sha256"; then + print_info "File $dest already exists and verified." + return 0 + else + print_warning "File $dest exists but failed verification. Redownloading..." + rm -f "$dest" + fi + fi + + print_info "Downloading $url..." + if ! curl -fL --progress-bar -o "$dest" "$url"; then + print_error "Failed to download $url." + if [[ "$allow_failure" != "true" ]]; then + return 1 + fi + else + # Verify downloaded file + if ! verify_file "$dest" "$expected_sha256"; then + print_error "Downloaded file failed verification" + rm -f "$dest" + return 1 + fi + print_success "Successfully downloaded and verified $dest" + fi + + return 0 +} + +# Handle curl errors +handle_curl_error() { + local exit_code=$1 + case $exit_code in + 6) print_error "Couldn't resolve host" ;; + 7) print_error "Failed to connect to host" ;; + 22) print_error "HTTP page not retrieved (404, etc.)" ;; + 28) print_error "Operation timeout" ;; + *) print_error "Curl failed with exit code $exit_code" ;; + esac +} + +# Download Windows 11 ISO using Microsoft's API +download_windows_iso() { + local windows_version="11" # Default to Windows 11 + local language="English (United States)" # Default language + + # Parse arguments if provided + if [[ -n "$1" ]]; then + windows_version="$1" + fi + + print_info "Attempting to download Windows $windows_version ISO from Microsoft..." + + # Set required variables + local user_agent="Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0" + local session_id="$(uuidgen)" + local profile="606624d44113" + local url="https://www.microsoft.com/en-us/software-download/windows$windows_version" + + # Add ISO to URL for Windows 10 + case "$windows_version" in + 10) url="${url}ISO" ;; + esac + + # Step 1: Get download page HTML + print_info "Fetching download page: $url" + local iso_download_page_html + iso_download_page_html="$(curl --disable --silent --user-agent "$user_agent" --header "Accept:" --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url")" || { + handle_curl_error $? + print_error "Failed to fetch the download page. Please download Windows $windows_version ISO manually from $url" + return 1 + } + + # Step 2: Extract Product Edition ID + print_info "Getting Product Edition ID..." + local product_edition_id + product_edition_id="$(echo "$iso_download_page_html" | grep -Eo '