aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md40
-rw-r--r--assets/brightness.svg39
-rw-r--r--assets/corner.pngbin1118 -> 0 bytes
-rw-r--r--assets/mute.pngbin5911 -> 0 bytes
-rw-r--r--assets/trans.pngbin169 -> 0 bytes
-rw-r--r--assets/vol.pngbin5607 -> 0 bytes
-rwxr-xr-xclear-screen3
-rwxr-xr-xhotspots121
-rwxr-xr-xlayer.sh24
-rwxr-xr-xlockscreen-wallpaper5
-rwxr-xr-xlow-bat-notifier66
-rwxr-xr-xmove-qemu.sh74
-rwxr-xr-xopacity-change.sh34
-rwxr-xr-xplank.sh5
-rwxr-xr-xpowermenu38
-rwxr-xr-xqemu-helper.sh172
-rwxr-xr-xrandom-wall128
-rwxr-xr-xrandom_data.py153
-rwxr-xr-xroot.sh54
-rwxr-xr-xscreenshot38
-rwxr-xr-xsession21
-rwxr-xr-xspec73
-rwxr-xr-xsystray17
-rw-r--r--test/.editorconfig14
-rwxr-xr-xtest/unhidewindow10
-rwxr-xr-xtitle-bar137
-rwxr-xr-xtoggle-control53
-rwxr-xr-xunix/sys/battery_alert.sh43
-rwxr-xr-xunix/sys/gsettings.sh (renamed from gsettings.sh)13
-rwxr-xr-xunix/sys/keep_guest_awake.sh84
-rwxr-xr-xunix/sys/low-bat-notifier79
-rwxr-xr-xunix/sys/session_manager.sh (renamed from session_manager.sh)0
-rwxr-xr-xunix/sys/shutdown_watchdog11
-rwxr-xr-xunix/utils/backlight_default.sh32
-rwxr-xr-xunix/utils/colors.sh (renamed from colors.sh)0
-rwxr-xr-xunix/utils/cryptocheck (renamed from cryptocheck)0
-rwxr-xr-xunix/utils/cryptonotify (renamed from cryptonotify)0
-rwxr-xr-xunix/utils/disable_meta_key_guest.sh94
-rwxr-xr-xunix/utils/heads-up-display (renamed from heads-up-display)8
-rwxr-xr-xunix/utils/hex2rgb.sh3
-rwxr-xr-xunix/utils/kill-notify (renamed from kill-notify)0
-rwxr-xr-xunix/utils/kill-process (renamed from kill-process)0
-rwxr-xr-xunix/utils/mnt (renamed from mnt)0
-rwxr-xr-xunix/utils/move_terminal65
-rwxr-xr-xunix/utils/neovim.sh (renamed from neovim.sh)19
-rwxr-xr-xunix/utils/pack (renamed from scratchpad)50
-rwxr-xr-xunix/utils/rofi-network-manager.sh (renamed from rofi-network-manager.sh)0
-rwxr-xr-xunix/utils/root.sh152
-rwxr-xr-xunix/utils/run_with_display.sh19
-rwxr-xr-xunix/utils/scratchpad147
-rwxr-xr-xunix/utils/screenshot102
-rwxr-xr-xunix/utils/sendkeys.awk86
-rwxr-xr-xunix/utils/sext47
-rwxr-xr-xunix/utils/track-books.sh (renamed from track-books.sh)0
-rwxr-xr-xunix/utils/umnt (renamed from umnt)0
-rwxr-xr-xunix/utils/waypipe_app16
-rwxr-xr-xunix/utils/wayvnc_session10
-rwxr-xr-xunix/utils/window_manager_name.sh29
-rwxr-xr-xunix/utils/xtouch (renamed from xtouch)0
-rwxr-xr-xunix/virt/checksum.sh36
-rwxr-xr-xunix/virt/dos.sh998
-rwxr-xr-xunix/virt/server.sh128
-rwxr-xr-xunix/virt/ubuntu222
-rwxr-xr-xunix/virt/windows.sh1156
-rwxr-xr-xupdate-title26
-rw-r--r--win-nvim.bat37
-rw-r--r--win-nvim.ps139
67 files changed, 3662 insertions, 1408 deletions
diff --git a/README.md b/README.md
index 458b3cc..3e15d2d 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,41 @@
# scripts
+
+```
+.scripts/
+├── assets/ # Images, icons, templates, and other resource files
+├── env/ # OS-specific environment setups
+│ ├── linux/ # Linux-specific configurations
+│ │ ├── autorun/ # Auto-start and background services
+│ │ ├── storage/ # Disk operations, file management, backups
+│ │ └── utils/ # Linux utility scripts
+│ ├── unix/ # macOS/BSD-specific configurations
+│ ├── virt/ # Virtualization/Containerization (Docker, KVM, LXC)
+│ └── windows/ # Windows-specific configurations
+│ ├── autorun/ # Auto-start and background services
+│ ├── storage/ # Disk operations, file management, backups
+│ └── utils/ # Windows utility scripts
+├── media/ # Gaming, social media, video, streaming automation
+├── network/ # Networking-related scripts
+│ ├── dns/ # DNS configuration, resolver tools
+│ ├── monitoring/ # Network monitoring, packet capture, alerts
+│ ├── ssh/ # SSH key management, tunnels, jump hosts
+│ └── vpn/ # VPN configuration and scripts
+├── security/ # Security-related scripts
+│ ├── audit/ # Security auditing, log analysis, compliance checks
+│ ├── defensive/ # Hardening, honeypots, firewall rules
+│ ├── forensics/ # Incident response, digital forensics tools
+│ └── offensive/ # Exploitation, penetration testing
+├── sysadmin/ # Administrative tasks
+│ ├── active_directory/ # Active Directory management
+│ ├── automation/ # Automated processes (cron, schedule tasks, startup scripts)
+│ ├── databases/ # Database-related scripts (backups, migrations)
+│ └── sysinfo/ # System monitoring, hardware info/diagnostics, logs, device management
+├── test/ # Testing, debugging, script validation
+├── tools/ # General utilities and helper scripts
+├── .editorconfig # Define consistent editing/formatting styles
+├── .gitignore # Specify files to ignore/not shared
+├── .gitmodules # Track external repositories
+├── LICENSE # Usage and distribution terms
+└── README.md # Documentation
+
+```
diff --git a/assets/brightness.svg b/assets/brightness.svg
deleted file mode 100644
index 85c44c1..0000000
--- a/assets/brightness.svg
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?>
-<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
-<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
- viewBox="0 0 512 512" xml:space="preserve">
-<g>
- <path style="fill:#F2D23D;" d="M256.007,423.724c-0.004,0-0.004,0-0.007,0c-14.626,0-26.483,11.857-26.483,26.483v35.31
- C229.517,500.143,241.374,512,256,512c0.004,0,0.004,0,0.007,0c14.622-0.004,26.476-11.859,26.476-26.483v-35.31
- C282.483,435.583,270.629,423.728,256.007,423.724z"/>
- <path style="fill:#F2D23D;" d="M256.007,0c-0.004,0-0.004,0-0.007,0c-14.626,0-26.483,11.857-26.483,26.483v35.31
- c0,14.626,11.857,26.483,26.483,26.483c0.004,0,0.004,0,0.007,0c14.622-0.004,26.476-11.859,26.476-26.483v-35.31
- C282.483,11.859,270.629,0.004,256.007,0z"/>
- <path style="fill:#F2D23D;" d="M256.007,141.241h-0.004c-63.276,0-114.755,51.481-114.755,114.759s51.479,114.759,114.755,114.759
- h0.004c63.278-0.002,114.759-51.481,114.759-114.759C370.766,192.724,319.285,141.243,256.007,141.241z"/>
-</g>
-<g>
- <path style="fill:#EEBF00;" d="M256.004,141.241c-63.276,0-114.755,51.481-114.755,114.76c0,63.278,51.479,114.757,114.755,114.757
- h0.004V141.241H256.004z"/>
- <path style="fill:#EEBF00;" d="M256.007,88.276V0c-0.004,0-0.004,0-0.007,0c-14.626,0-26.483,11.857-26.483,26.483v35.31
- c0,14.626,11.857,26.483,26.483,26.483C256.004,88.276,256.004,88.276,256.007,88.276z"/>
- <path style="fill:#EEBF00;" d="M256,423.724c-14.626,0-26.483,11.857-26.483,26.483v35.31C229.517,500.143,241.374,512,256,512
- c0.004,0,0.004,0,0.007,0v-88.276C256.004,423.724,256.004,423.724,256,423.724z"/>
-</g>
-<path style="fill:#F2D23D;" d="M423.724,256c0,14.626,11.857,26.483,26.483,26.483h35.31C500.143,282.483,512,270.626,512,256
- c0-14.626-11.857-26.483-26.483-26.483h-35.31C435.581,229.517,423.724,241.374,423.724,256z"/>
-<path style="fill:#EEBF00;" d="M26.483,282.483h35.31c14.626,0,26.483-11.857,26.483-26.483c0-14.626-11.857-26.483-26.483-26.483
- h-35.31C11.857,229.517,0,241.374,0,256C0,270.626,11.857,282.483,26.483,282.483z"/>
-<path style="fill:#F2D23D;" d="M399.567,74.98l-24.97,24.968c-10.342,10.342-10.342,27.11,0,37.452
- c5.171,5.171,11.949,7.758,18.725,7.758s13.556-2.586,18.725-7.756l24.97-24.968c10.342-10.342,10.342-27.11,0-37.452
- C426.68,64.637,409.911,64.637,399.567,74.98z"/>
-<path style="fill:#EEBF00;" d="M99.949,374.597l-24.968,24.968c-10.342,10.342-10.342,27.109,0,37.452
- c5.171,5.171,11.949,7.756,18.725,7.756s13.556-2.586,18.725-7.756l24.97-24.968c10.342-10.342,10.342-27.11,0-37.452
- C127.061,364.256,110.292,364.254,99.949,374.597z"/>
-<path style="fill:#F2D23D;" d="M399.567,437.018c5.171,5.171,11.949,7.756,18.725,7.756c6.778,0,13.556-2.585,18.727-7.756
- c10.342-10.342,10.342-27.11,0-37.452l-24.97-24.968c-10.341-10.341-27.11-10.341-37.452,0.002
- c-10.342,10.342-10.342,27.109,0,37.452L399.567,437.018z"/>
-<path style="fill:#EEBF00;" d="M99.949,137.401c5.171,5.171,11.949,7.756,18.727,7.756c6.778,0,13.556-2.586,18.725-7.758
- c10.342-10.342,10.342-27.11,0-37.452L112.433,74.98c-10.341-10.341-27.11-10.341-37.452,0c-10.342,10.342-10.342,27.11,0,37.452
- L99.949,137.401z"/>
-</svg> \ No newline at end of file
diff --git a/assets/corner.png b/assets/corner.png
deleted file mode 100644
index c0239d8..0000000
--- a/assets/corner.png
+++ /dev/null
Binary files differ
diff --git a/assets/mute.png b/assets/mute.png
deleted file mode 100644
index 41cf959..0000000
--- a/assets/mute.png
+++ /dev/null
Binary files differ
diff --git a/assets/trans.png b/assets/trans.png
deleted file mode 100644
index 56c8a67..0000000
--- a/assets/trans.png
+++ /dev/null
Binary files differ
diff --git a/assets/vol.png b/assets/vol.png
deleted file mode 100644
index 7e388f6..0000000
--- a/assets/vol.png
+++ /dev/null
Binary files differ
diff --git a/clear-screen b/clear-screen
deleted file mode 100755
index 90debb0..0000000
--- a/clear-screen
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env sh
-
-tput reset
diff --git a/hotspots b/hotspots
deleted file mode 100755
index 8454bac..0000000
--- a/hotspots
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/usr/bin/env bash
-#
-# Hotspots for Xorg
-# - Corners: BL, BR, TL, TR
-# - Edges: B, T, L, R
-# - Events: onEnter, onExit
-#
-# Dependencies: xdotool
-#
-# TODO:
-# - better multimonitor support
-
-# Hotspot size
-SIZE=2
-
-# Active hotspot dimensions (width, height)
-B=(1100 140)
-T=(700 "$SIZE")
-L=(460 700)
-R=(140 700)
-BL=(400 400)
-BR=("$SIZE" "$SIZE")
-TL=(400 300)
-TR=("$SIZE" "$SIZE")
-
-# Variables to store the timestamp when the cursor entered a zone
-ENTER_TIMESTAMP=0
-
-# Function to reset the timestamp
-function resetTimestamp() {
- ENTER_TIMESTAMP=0
-}
-
-# Function to handle onEnter with a delay
-function onEnterWithDelay() {
- local zone=$1
- local current_time=$(date +%s)
-
- if ((ENTER_TIMESTAMP == 0)); then
- # Cursor has just entered the zone, store the timestamp
- ENTER_TIMESTAMP=$current_time
- elif ((current_time - ENTER_TIMESTAMP >= 2)); then
- # Cursor has been in the zone for 2 seconds or more, trigger onEnter
- onEnter "$zone"
- resetTimestamp
- fi
-}
-
-# Function to handle onEnter event
-function onEnter() {
- ZONE=$1
- case "$1" in
- 'T') eww open status &>/dev/null ;;
- #'R') eww open bar &>/dev/null ;;
- esac
-}
-
-# Function to handle onExit event
-function onExit() {
- ZONE=''
- case "$1" in
- 'T') eww close status &>/dev/null ;;
- 'R') eww close bar &>/dev/null ;;
- esac
-}
-
-# Get screen dimensions (returns: WIDTH, HEIGHT)
-eval "$(xdotool getdisplaygeometry --shell)"
-
-# Calculate coordinates
-BLY=$((HEIGHT - BL[1]))
-BRX=$((WIDTH - BR[0]))
-BRY=$((HEIGHT - BR[1]))
-TRX=$((WIDTH - TR[0]))
-BX1=$(((WIDTH - B[0]) / 2))
-BX2=$(((WIDTH + B[0]) / 2))
-BY=$((HEIGHT - B[1]))
-LY1=$(((HEIGHT - L[1]) / 2))
-LY2=$(((HEIGHT + L[1]) / 2))
-RX=$((WIDTH - R[0]))
-RY1=$(((HEIGHT - R[1]) / 2))
-RY2=$(((HEIGHT + R[1]) / 2))
-H=$((HEIGHT - SIZE))
-W=$((WIDTH - SIZE))
-
-# Watch mouse
-while :; do
- sleep 0.1
-
- # Get mouse location (returns: X, Y)
- eval "$(xdotool getmouselocation --shell)"
-
- if [ "$ZONE" = "" ]; then
- if [[ "$Y" -ge "$H" ]]; then
- if [[ "$X" -lt "$SIZE" ]]; then
- onEnterWithDelay BL
- elif [[ "$X" -gt "$BX1" && "$X" -lt "$BX2" ]]; then
- onEnterWithDelay B
- elif [[ "$X" -gt "$W" ]]; then
- onEnterWithDelay BR
- fi
- elif [[ "$Y" -lt "$SIZE" && "$X" -gt $((WIDTH / 3)) && "$X" -lt $((2 * WIDTH / 3)) ]]; then
- onEnterWithDelay T
- elif [[ "$X" -lt "$SIZE" && "$Y" -gt "$LY1" && "$Y" -lt "$LY2" ]]; then
- onEnterWithDelay L
- elif [[ "$X" -gt "$W" && "$Y" -gt "$RY1" && "$Y" -lt "$RY2" ]]; then
- onEnterWithDelay R
- fi
- else
- case "$ZONE" in
- 'B') [[ "$X" -lt "$BX1" || "$X" -gt "$BX2" || "$Y" -lt "$BY" ]] && onExit B ;;
- 'L') [[ "$X" -ge "${L[0]}" || "$Y" -lt "$LY1" || "$Y" -gt "$LY2" ]] && onExit L ;;
- 'R') [[ "$X" -le "$RX" || "$Y" -lt "$RY1" || "$Y" -gt "$RY2" ]] && onExit R ;;
- 'T') [[ "$X" -lt "$BX1" || "$X" -gt "$BX2" || "$Y" -gt "${T[1]}" ]] && onExit T ;;
- 'BL') [[ "$X" -ge "${BL[0]}" || "$Y" -lt "$BLY" ]] && onExit BL ;;
- 'BR') [[ "$X" -le "$BRX" || "$Y" -lt "$BRY" ]] && onExit BR ;;
- 'TL') [[ "$X" -ge "${TL[0]}" || "$Y" -ge "${TL[1]}" ]] && onExit TL ;;
- 'TR') [[ "$X" -le "$TRX" || "$Y" -ge "${TR[1]}" ]] && onExit TR ;;
- esac
- fi
-done
diff --git a/layer.sh b/layer.sh
deleted file mode 100755
index 4b17ed1..0000000
--- a/layer.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-
-current_layer="$(bspc query -T -n | jq -r '.client.layer')"
-case $1 in
- +|-)
- declare -A _layers=( [below]=0 [normal]=1 [above]=2 )
- layers=( below normal above )
- maxl=$(( ${#layers[@]} - 1 ))
- current_layer="$(bspc query -T -n | jq -r '.client.layer')"
- i=$(( ${_layers[$current_layer]} $1 1 ))
- if [[ $i -lt 0 ]]; then
- i=0
- elif [[ $i -gt $maxl ]]; then
- i=$maxl
- fi
- #cycle? nah
- #i=$(( (${_layers[$current_layer]} + ${#layers[@]} ${1} 1) % ${#layers[@]} ))
- new_layer="${layers[$i]}"
- ;;
- *)
- new_layer="$(bspc query -T -n | jq -r '.client.lastLayer')"
- ;;
-esac
-[[ "$current_layer" != "$new_layer" ]] && bspc node -l "$new_layer"
diff --git a/lockscreen-wallpaper b/lockscreen-wallpaper
deleted file mode 100755
index f191a7a..0000000
--- a/lockscreen-wallpaper
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-# use nitrogen wallpaper.
-wallpaper=$(cat ~/.config/nitrogen/bg-saved.cfg | grep file= | sed 's/file=//')
-betterlockscreen -u $wallpaper
diff --git a/low-bat-notifier b/low-bat-notifier
deleted file mode 100755
index be346f4..0000000
--- a/low-bat-notifier
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/bash
-
-### VARIABLES
-
-POLL_INTERVAL=120 # seconds at which to check battery level
-LOW_BAT=33 # lesser than this is considered low battery
-
-# If BAT0 doesn't work for you, check available devices with command below
-#
-# $ 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
-
-### END OF VARIABLES
-
-kill_running() { # stop older instances to not get multiple notifications
- local mypid=$$
-
- declare pids=($(pgrep -f ${0##*/}))
-
- for pid in ${pids[@]/$mypid/}; do
- kill $pid
- sleep 1
- done
-}
-
-launched=0
-
-# 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 ))
-
- if [[ $bat_percent -lt $LOW_BAT && "$bs" = "Discharging" && $launched -lt 3 ]]
- then
- notify-send --urgency=critical "$bat_percent% : Low Battery!"
- launched=$((launched+1))
- elif [[ "$bs" = "Charging" ]]
- then
- launched=0
- fi
- sleep $POLL_INTERVAL
- done
-fi
diff --git a/move-qemu.sh b/move-qemu.sh
deleted file mode 100755
index 7583cff..0000000
--- a/move-qemu.sh
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/bin/bash
-
-#
-#function move_qemu_window {
-# find_window_title() {
-# hyprctl clients | grep -q "QEMU (nixos) - noVNC — Mozilla Firefox"
-# }
-#
-# # Function to move the window to workspace 3
-# move_window() {
-# local window_title="$1"
-# if [[ -n $window_title ]]; then
-# hyprctl dispatch movetoworkspace 3,title:"$window_title"
-# echo "Moved window to workspace 3."
-# else
-# echo "Failed to find window title."
-# fi
-# }
-#
-# # Function to handle socket input
-# handle_socket() {
-# while read -r line; do
-# case "$line" in
-# *"QEMU (nixos) - noVNC — Mozilla Firefox"*)
-# echo "Socket message received: "$line
-# if find_window_title; then
-# window_title="^QEMU \(.*\) - noVNC — Mozilla Firefox*"
-# move_window ""$window_title
-# else
-# echo "Failed to find window title."
-# fi
-# ;;
-# *)
-# echo "Ignoring socket message: "$line
-# ;;
-# esac
-# done
-# }
-#
-# # Wait for the socket and handle messages
-# echo "Waiting for socket messages..."
-# socat - "UNIX-CONNECT:/tmp/hypr/"$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | handle_socket
-#}
-#
-#move_qemu_window
-
-function move_qemu_window {
- find_window_title() {
- hyprctl clients | grep -q "QEMU (nixos) - noVNC — Mozilla Firefox"
- }
-
- # Function to move the window to workspace 3
- move_window() {
- local window_title="$1"
- if [[ -n $window_title ]]; then
- hyprctl dispatch movetoworkspace 3,title:"$window_title"
- echo "Moved window to workspace 3."
- else
- echo "Failed to find window title."
- fi
- }
-
- # Wait for the window to appear
- echo "Waiting for window..."
- while true; do
- if find_window_title; then
- window_title="^QEMU \(.*\) - noVNC — Mozilla Firefox*"
- move_window "$window_title"
- fi
- sleep 1 # Check every second
- done
-}
-
-move_qemu_window
diff --git a/opacity-change.sh b/opacity-change.sh
deleted file mode 100755
index b509936..0000000
--- a/opacity-change.sh
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env bash
-set -eu
-[[ -n ${DEBUG:-} ]] && set -x
-
-#### Example alacritty.yml usage
-#key_bindings:
-# - { key: N, mods: Control|Shift, action: SpawnNewInstance }
-# - { key: O, mods: Control|Shift, command: { program: "opacity-change.sh", args: ["-"] } }
-# - { key: P, mods: Control|Shift, command: { program: "opacity-change.sh", args: ["+"] } }
-
-
-operation="${1:-}${2:-}" # Arg #1 & #2 (in case the user misinterpreted a space in the usage), Default ''
-step="${operation:1}" # Substring from char index 1
-step="${step:-1}" # Default '1'
-operation="${operation:0:1}" # Substring from char index 0 length of 1
-config_file="$HOME/.config/alacritty/alacritty.yml"
-config_field="opacity"
-tmp_file="/tmp/$(basename $config_file).$(date +%s)"
-current_value=$(sed 's/#.*//g; /\b'"$config_field"':/!d; s/.*: \?//' < "$config_file")
-
-case $operation in
-"-")
- verb="Decreasing" ;;
-"+")
- verb="Increasing" ;;
-*)
- echo "Usage: ${BASH_SOURCE[0]} (-|+)[int]"; exit 255 ;;
-esac
-
-new_value="$(awk '{n=$1+$2/10; print (n<0 ? 0 : n>1 ? 1 : n)}' <<<"$current_value $operation$step")"
-echo "$verb $config_field from $current_value to $new_value" >&2
-cp "$config_file" "$tmp_file"
-sed "s/\b$config_field:.*/$config_field: $new_value/" "$tmp_file" > "$config_file"
-
diff --git a/plank.sh b/plank.sh
deleted file mode 100755
index f389c3a..0000000
--- a/plank.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-bspc subscribe node_add | while read -r _; do
- xdo raise -N Plank
-done
diff --git a/powermenu b/powermenu
deleted file mode 100755
index 7bd913e..0000000
--- a/powermenu
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/bash
-
-# display a power menu to: shutdown, reboot,
-# lock, logout, and suspend. This script can be
-# executed by clicking on the polybar powermenu module
-# or with a keyboard shortcut
-
-# options to be displayed
-shutdown=" Shutdown"
-reboot=" Reboot"
-lock=" Lock"
-logout=" Logout"
-suspend=" Suspend"
-
-uptime=$(uptime -p | sed -e 's/up //g')
-
-# options passed into variable
-options="$shutdown\n$reboot\n$lock\n$logout\n$suspend"
-
-chosen="$(echo -e "$options" | rofi -theme ~/.config/rofi/styles/powermenu.rasi -lines 5 -dmenu -p "$uptime")"
-
-case $chosen in
-$shutdown)
- systemctl poweroff
- ;;
-$reboot)
- systemctl reboot
- ;;
-$lock)
- betterlockscreen --lock dimblur
- ;;
-$logout)
- bspc quit
- ;;
-$suspend)
- systemctl suspend
- ;;
-esac
diff --git a/qemu-helper.sh b/qemu-helper.sh
deleted file mode 100755
index 0d38aba..0000000
--- a/qemu-helper.sh
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/bin/bash
-
-# Created By: srdusr
-# Created On: Wed 02 Aug 2023 16:16:21 PM CAT
-# Project: QEMU setup/opener helper wrapper script
-
-# Set global variables for VM parameters
-ram_size="4G"
-
-# Function to prompt user for VM parameters
-function get_vm_parameters() {
- read -p "Enter VM name (default: vm): " vm_name
- vm_name=${vm_name:-vm}
-
- # Set the default ISO file path to ~/machines/images
- default_iso_path="$HOME/machines/images"
-
- # Generate completions for ISO and IMG files in the images directory
- COMPREPLY=()
- local files=$(compgen -G "$default_iso_path/*.{iso,img}" -o plusdirs)
- for file in $files; do
- COMPREPLY+=("$file")
- done
-
- # Use read with -i and -e options for tab-completion
- read -ep "Enter ISO file path (default: $default_iso_path): " -i "$default_iso_path" iso_path
-
- # Manually expand the ~ to the user's home directory
- iso_path=$(eval echo "$iso_path")
-
- # Validate the user input
- while [ ! -f "$iso_path" ]; do
- read -ep "Invalid file path. Enter a valid ISO file path: " iso_path
- done
-
- # Check if the selected file is an IMG file
- if [[ "$iso_path" == *.img ]]; then
- guest_os="windows"
- else
- guest_os="linux"
- fi
-
- # Show available disk space before asking for disk image size
- echo "Available disk space:"
- df -h "$vm_images_path"
-
- read -p "Enter disk image size in GB (default: 10G): " disk_size
- disk_size=${disk_size:-10G}
-
- read -p "Enter RAM size in GB (default: 4G): " ram_size
- ram_size=${ram_size:-4G}
-
- # Check if the RAM size is in the correct format (e.g., "4G")
- while ! [[ $ram_size =~ ^[0-9]+[kKmMgGtTpPeE]$ ]]; do
- read -p "Invalid RAM size format. Enter RAM size in GB (e.g., 4G): " ram_size
- done
-
- read -p "Enter number of CPU cores (default: 2): " cpu_cores
- cpu_cores=${cpu_cores:-2}
-}
-
-
-# Function to list available VMs
-function list_vms() {
- echo "Available VMs:"
- for vm_file in "$vm_images_path"/*.qcow2; do
- vm=$(basename "$vm_file" .qcow2)
- echo " - $vm"
- done
-}
-
-# Function to list available ISO and IMG files in the images directory
-function list_iso_img_files() {
- echo "Available ISO and IMG files in $iso_images_path:"
- iso_img_files=()
- while IFS= read -r -d $'\0' file; do
- iso_img_files+=("$file")
- done < <(find "$iso_images_path" -type f \( -iname \*.iso -o -iname \*.img \) -print0)
-
- for ((i = 0; i < ${#iso_img_files[@]}; i++)); do
- echo " $((i + 1)). ${iso_img_files[i]##*/}"
- done
-}
-
-# Function to check if VM is already running
-function is_vm_running() {
- vm_name=$1
- if ps aux | grep -v grep | grep -q "[q]emu-system-x86_64.*$vm_name"; then
- return 0
- else
- return 1
- fi
-}
-
-# Function to start VM
-function start_vm() {
- vm_name=$1
- is_vm_running "$vm_name"
- if [ $? -eq 0 ]; then
- echo "VM '$vm_name' is already running."
- return
- fi
-
- # VM parameters
- qemu_cmd="qemu-system-x86_64 -enable-kvm -machine type=q35 -m $ram_size -cpu host -smp 2 -vga virtio"
- qemu_cmd+=" -device qemu-xhci -device usb-tablet -device usb-kbd -device virtio-net,netdev=user0 -netdev user,id=user0,hostfwd=tcp::5555-:22"
- qemu_cmd+=" -cdrom \"$iso_path\" -drive file=\"$vm_images_path/$vm_name.qcow2\",index=0,media=disk,if=virtio"
-
- if [[ $guest_os == "windows" ]]; then
- qemu_cmd+=" -drive file=\"$iso_images_path/virtio-win.iso\",index=3,media=cdrom"
- fi
-
- qemu_cmd+=" -boot menu=on"
- qemu_cmd+=" -net nic -net user,hostname=$vm_name -name \"$vm_name\""
-
- echo "Starting VM: $vm_name"
- eval "$qemu_cmd"
-}
-
-# Main script starts here
-vm_images_path="$HOME/machines/vm"
-iso_images_path="$HOME/machines/images"
-
-# Check if directories exist
-mkdir -p "$vm_images_path"
-mkdir -p "$iso_images_path"
-
-# List available VMs
-list_vms
-
-# List available ISO and IMG files in the images directory
-list_iso_img_files
-
-# Ask the user if they want to use an existing VM or create a new one
-read -p "Do you want to use an existing VM? (y/n): " use_existing_vm
-if [[ $use_existing_vm =~ ^[Yy]$ ]]; then
- read -p "Enter the name of the existing VM: " existing_vm_name
- while [ ! -f "$vm_images_path/$existing_vm_name.qcow2" ]; do
- echo "VM '$existing_vm_name' does not exist."
- read -p "Enter a valid existing VM name: " existing_vm_name
- done
- vm_name=$existing_vm_name
-else
- # Prompt user for VM parameters
- get_vm_parameters
-
- # Check if VM already exists
- if [ -f "$vm_images_path/$vm_name.qcow2" ]; then
- read -p "VM '$vm_name' already exists. Do you want to start it? (y/n): " start_vm_choice
- if [[ $start_vm_choice =~ ^[Yy]$ ]]; then
- start_vm "$vm_name"
- exit 0
- fi
- else
- # Create new VM
- echo "Creating new VM: $vm_name"
- qemu-img create -f qcow2 "$vm_images_path/$vm_name.qcow2" "$disk_size"
- start_vm "$vm_name"
- exit 0
- fi
-fi
-
-# If an existing VM is selected, ask if the user wants to modify its parameters
-read -p "Do you want to modify the VM parameters? (y/n): " modify_vm_params
-if [[ $modify_vm_params =~ ^[Yy]$ ]]; then
- get_vm_parameters
-fi
-
-# Start the VM
-start_vm "$vm_name"
-
-echo "Script execution completed."
diff --git a/random-wall b/random-wall
deleted file mode 100755
index bd23983..0000000
--- a/random-wall
+++ /dev/null
@@ -1,128 +0,0 @@
-#!/bin/bash
-
-WPDIR="$HOME/pictures/wallpapers"
-LAST_WP_FILE="$HOME/.config/random-wall_last_wp.txt"
-CURRENT_WP_FILE="$HOME/.config/nitrogen/bg-saved.cfg"
-
-random=true
-apply=true
-wpfile=""
-
-function usage {
- local stream exitcode
- if [ "$1" -eq 1 ]; then
- stream=2
- exitcode=255
- else
- stream=1
- exitcode=0
- fi
- echo "Usage: $(basename "$0") [-n|--noapply] [-h|--help] [wallpaper_location]" >&"$stream"
- echo "If wallpaper location is not given, a random wallpaper from $WPDIR will be chosen" >&"$stream"
- exit "$exitcode"
-}
-
-# Check if last wallpaper file exists and get its content
-if [ -f "$LAST_WP_FILE" ]; then
- LAST_WP=$(<"$LAST_WP_FILE")
-else
- LAST_WP=""
-fi
-
-# Function to get a random wallpaper from $WPDIR, excluding the last and current wallpapers
-function get_random_wallpaper {
- local wallpapers
- shopt -s nullglob
- wallpapers=("$WPDIR"/*.jpg)
- if [ ${#wallpapers[@]} -eq 0 ]; then
- echo "No wallpapers found in $WPDIR" >&2
- exit 1
- fi
-
- # If there is only one wallpaper, return it
- if [ ${#wallpapers[@]} -eq 1 ]; then
- echo "${wallpapers[0]}"
- return
- fi
-
- local exclude=()
- if [ "$LAST_WP" != "" ]; then
- exclude+=("$LAST_WP")
- fi
- if [ "$CURRENT_WP" != "" ]; then
- exclude+=("$CURRENT_WP")
- fi
-
- local random_wallpaper
- while true; do
- random_wallpaper="${wallpapers[RANDOM % ${#wallpapers[@]}]}"
- if [[ ! " ${exclude[@]} " =~ " ${random_wallpaper} " ]]; then
- echo "$random_wallpaper"
- return
- fi
- done
-}
-
-# handle arguments
-while [ $# -gt 0 ]; do
- case "$1" in
- "--help" | "-h")
- usage 0
- ;;
- "--noapply" | "-n")
- apply=false
- ;;
- *)
- if ! "$random"; then
- usage 1
- elif [ ! -f "$1" ]; then
- echo "File '$1' not found" >&2
- exit 1
- fi
- random=false
- wpfile="$1"
- ;;
- esac
- shift
-done
-
-# Choose a random wallpaper if necessary
-if "$random"; then
- wpfile=$(get_random_wallpaper)
- echo "Chose: $wpfile" >&2
-fi
-
-# Set the desktop wallpaper
-cat >"$HOME/.config/nitrogen/bg-saved.cfg" <<EOF
-[xin_-1]
-file=$wpfile
-mode=4
-bgcolor=#000000
-EOF
-
-# Set the lock screen wallpaper
-"$HOME/.scripts/lockscreen-wallpaper" "$wpfile" &
-
-# Set the LightDM wallpaper
-cp "$wpfile" "/usr/share/lightdm-webkit/themes/glorious/assets/bg.jpg"
-
-# Save the last chosen wallpaper
-echo "$wpfile" >"$LAST_WP_FILE"
-
-# Sync wallpaper using AccountsService D-Bus
-if [ "$wpfile" != "" ]; then
- dbus-send \
- --print-reply \
- --system \
- --dest=org.freedesktop.Accounts \
- /org/freedesktop/Accounts/User"$(id -u)" \
- org.freedesktop.DBus.Properties.Set \
- string:org.freedesktop.DisplayManager.AccountsService \
- string:BackgroundFile \
- variant:string:"$wpfile"
-fi
-
-# Apply the desktop wallpaper if requested
-if "$apply"; then
- nitrogen --restore
-fi
diff --git a/random_data.py b/random_data.py
deleted file mode 100755
index 071ab7c..0000000
--- a/random_data.py
+++ /dev/null
@@ -1,153 +0,0 @@
-#!/usr/bin/env python3
-
-import os
-import random
-import string
-import json
-import datetime
-import csv
-
-
-def generate_random_string(length, charset=string.ascii_letters):
- """Generate a random string of given length and character set."""
- return ''.join(random.choice(charset) for _ in range(length))
-
-
-def generate_random_number(min_value, max_value):
- """Generate a random number within the specified range."""
- return random.randint(min_value, max_value)
-
-
-def generate_random_date(start_date, end_date):
- """Generate a random date within the specified range."""
- time_between_dates = end_date - start_date
- days_between_dates = time_between_dates.days
- random_number_of_days = random.randrange(days_between_dates)
- random_date = start_date + datetime.timedelta(days=random_number_of_days)
- return random_date.strftime("%Y-%m-%d")
-
-
-def generate_sql_insert(table_name, columns, num_records):
- """Generate SQL INSERT statements for populating a table."""
- sql_statements = []
- for _ in range(num_records):
- values = [f"'{generate_random_string(int(input(f'Enter length for {column}: ')))}'" for column in columns]
- sql = f"INSERT INTO {table_name} ({', '.join(columns)}) VALUES ({', '.join(values)});"
- sql_statements.append(sql)
- return sql_statements
-
-
-def generate_placeholder_data(num_records, data_format):
- """Generate placeholder data based on user-defined format."""
- placeholder_data = []
- for _ in range(num_records):
- record = {}
- for field, field_data in data_format.items():
- data_type = field_data['type']
- if data_type == 'string':
- length = field_data['length']
- charset = field_data['charset']
- record[field] = generate_random_string(length, charset)
- elif data_type == 'number':
- min_value = field_data['min']
- max_value = field_data['max']
- record[field] = generate_random_number(min_value, max_value)
- elif data_type == 'date':
- start_date = datetime.datetime.strptime(field_data['start_date'], "%Y-%m-%d")
- end_date = datetime.datetime.strptime(field_data['end_date'], "%Y-%m-%d")
- record[field] = generate_random_date(start_date, end_date)
- elif data_type == 'boolean':
- record[field] = random.choice([True, False])
- placeholder_data.append(record)
- return placeholder_data
-
-
-def get_data_format_from_user():
- """Prompt the user for data format preferences."""
- data_format = {}
- while True:
- field = input("Enter field name (or 'done' to finish): ").strip()
- if field.lower() == 'done':
- break
-
- data_type = input(f"Enter data type for '{field}' (string/number/date/boolean): ").strip()
- if data_type not in ['string', 'number', 'date', 'boolean']:
- print("Invalid data type. Please enter 'string', 'number', 'date', or 'boolean'.")
- continue
-
- if data_type == 'string':
- length = int(input(f"Enter length for '{field}' (integer): "))
- charset = input(f"Enter character set for '{field}' (optional, press Enter for default): ").strip()
- if not charset:
- charset = string.ascii_letters
- data_format[field] = {'type': 'string', 'length': length, 'charset': charset}
- elif data_type == 'number':
- min_value = int(input(f"Enter minimum value for '{field}' (integer): "))
- max_value = int(input(f"Enter maximum value for '{field}' (integer): "))
- data_format[field] = {'type': 'number', 'min': min_value, 'max': max_value}
- elif data_type == 'date':
- start_date = input(f"Enter start date for '{field}' (YYYY-MM-DD): ")
- end_date = input(f"Enter end date for '{field}' (YYYY-MM-DD): ")
- data_format[field] = {'type': 'date', 'start_date': start_date, 'end_date': end_date}
- elif data_type == 'boolean':
- data_format[field] = {'type': 'boolean'}
-
- return data_format
-
-
-def get_file_type_from_user():
- """Prompt the user for the desired file type (e.g., JSON, CSV, SQL, TXT, MD, HTML)."""
- while True:
- file_type = input("Enter the desired file type for saving the data (json/csv/sql/txt/md/html): ").strip().lower()
- if file_type in ['json', 'csv', 'sql', 'txt', 'md', 'html']:
- return file_type
- else:
- print("Invalid file type. Please enter 'json', 'csv', 'sql', 'txt', 'md', or 'html'.")
-
-
-def save_data_to_file(data, file_type):
- """Save the generated data to a file of the specified type."""
- if file_type == 'json':
- with open('placeholder_data.json', 'w') as json_file:
- json.dump(data, json_file, indent=4)
- elif file_type == 'csv':
- with open('placeholder_data.csv', 'w', newline='') as csv_file:
- fieldnames = data[0].keys()
- writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
- writer.writeheader()
- for record in data:
- writer.writerow(record)
- elif file_type == 'sql':
- table_name = input("Enter the SQL table name: ")
- columns = input("Enter column names separated by commas: ").split(',')
- sql_statements = generate_sql_insert(table_name, columns, len(data))
- with open('generated_data.sql', 'w') as sql_file:
- sql_file.write('\n'.join(sql_statements))
- elif file_type == 'txt':
- with open('placeholder_data.txt', 'w') as txt_file:
- for record in data:
- txt_file.write(str(record) + '\n')
- elif file_type == 'md':
- with open('placeholder_data.md', 'w') as md_file:
- for record in data:
- md_file.write('- ' + ', '.join([f"{key}: {value}" for key, value in record.items()]) + '\n')
- elif file_type == 'html':
- with open('placeholder_data.html', 'w') as html_file:
- html_file.write('<html>\n<head>\n<title>Placeholder Data</title>\n</head>\n<body>\n')
- for record in data:
- html_file.write('<ul>\n')
- for key, value in record.items():
- html_file.write(f'<li>{key}: {value}</li>\n')
- html_file.write('</ul>\n')
-
-
-if __name__ == "__main__":
- num_records = int(input("Enter the number of records to generate: "))
- data_format = get_data_format_from_user()
- file_type = get_file_type_from_user()
-
- placeholder_data = generate_placeholder_data(num_records, data_format)
-
- save_data_to_file(placeholder_data, file_type)
-
- print(f"Data will be saved to: {os.path.abspath('generated_data.sql')}")
diff --git a/root.sh b/root.sh
deleted file mode 100755
index 7706bce..0000000
--- a/root.sh
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/bash
-
-# Define the source base directory
-BASE_DIR="$HOME/extras"
-
-# Check if the script is running with superuser privileges
-if [ "$EUID" -ne 0 ]; then
- echo "Please run as root or with sudo"
- exit 1
-fi
-
-# Function to backup existing files
-backup_existing() {
- local dest=$1
- if [ -e "$dest" ]; then
- echo "Backing up $dest"
- mv "$dest" "$dest.bak"
- fi
-}
-
-# Function to copy directories, backup, and change permissions
-copy_and_set_permissions() {
- local src=$1
- local dest=$2
-
- if [ -d "$src" ]; then
- echo "Processing directory $src"
-
- for file in "$src"/*; {
- dest_file="$dest/$(basename "$file")"
-
- backup_existing "$dest_file"
-
- echo "Copying $file to $dest"
- cp -rp "$file" "$dest"
-
- echo "Setting permissions for $dest_file"
- chown root:root "$dest_file"
- chmod 644 "$dest_file"
- }
- else
- echo "Source directory $src does not exist."
- fi
-}
-
-# Iterate over all directories in the extras directory
-for dir in "$BASE_DIR"/*; do
- if [ -d "$dir" ]; then
- dest_dir="/${dir##*/}"
- copy_and_set_permissions "$dir" "$dest_dir"
- fi
-done
-
-echo "Files copied and permissions set successfully."
diff --git a/screenshot b/screenshot
deleted file mode 100755
index 25fc3bd..0000000
--- a/screenshot
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/bash
-
-DATE="$(date +'%d-%m-%y_%H:%M:%S')"
-DIR="$HOME/pictures/screenshots"
-PIC="$DIR/$DATE.jpg"
-
-[ ! -d "$DIR" ] && mkdir -pv "$DIR"
-
-abort() {
- notify-send -a "Warn" -i dialog-error "Screenshot" "aborted"
- exit 1
-}
-
-notification() {
- NOTIFY=$(notify-send -A open=Open -A delete=Delete -a Screenshot -i "$PIC" "Screenshot" "$PIC")
- if [[ $NOTIFY == "open" ]]; then
- viewnior "$PIC"
- elif [[ $NOTIFY == "delete" ]]; then
- rm -rf "$PIC"
- else
- exit 0
- fi
-}
-
-case "$1" in
- full)
- flameshot full -p "$PIC" || abort
- #flameshot full -p ~/pictures/screenshots
- xclip -selection primary <"$PIC"
- notification
- ;;
- crop)
- flameshot gui -p "$PIC" || abort
- #flameshot gui -p ~/pictures/screenshots
- xclip -selection primary <"$PIC"
- notification
- ;;
-esac
diff --git a/session b/session
deleted file mode 100755
index c798403..0000000
--- a/session
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-
-# Check if the desired session is provided as an argument
-if [ "$1" ]; then
- case "$1" in
- bspwm | sway | Hyprland)
- echo "$1" >~/.session
- echo "Session choice set to: $1"
- ;;
- *)
- echo "Unsupported session: $1"
- ;;
- esac
-
- # Kill the current X session or logout the user
- pkill -KILL -u "$USER"
-else
- echo "Usage: switch_session.sh <session_name>"
- echo "Example: switch_session.sh bspwm"
- exit 1
-fi
diff --git a/spec b/spec
deleted file mode 100755
index 19810fc..0000000
--- a/spec
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/env bash
-
-# Created By: srdusr
-# Created On: Wed 18 Oct 2023 20:19:03 CAT
-# Project: Create Spectrograms of audio files
-
-# Dependencies: sox
-
-# Define the timestamp function
-timestamp() {
- date +%Y%m%d%H%M%S
-}
-
-spec() {
-
- if [[ $# -eq 0 ]]; then
- echo "No audio files provided."
- return
- fi
-
- local outdir
-
- if [[ -d "$HOME/pictures" ]]; then
- outdir="$HOME/pictures/spectrograms"
- elif [[ -d "$HOME/Pictures" ]]; then
- outdir="$HOME/Pictures/Spectrograms"
- elif [[ -d "$HOME/Images" ]]; then
- outdir="$HOME/Images/Spectrograms"
- else
- outdir="./spectrograms" # Save to the current directory if none of the expected directories exist
- fi
-
- for file in "$@"; do
- if [[ -f "$file" ]]; then
- local filename
- filename=$(basename "$file")
- local filename_no_extension="${filename%.*}"
- local target_dir="$outdir"
- local outname="$target_dir/sox-spec-$(timestamp)-${filename_no_extension}.png"
-
- if [[ ! -d "$target_dir" ]]; then
- mkdir -p "$target_dir" # Create the directory if it doesn't exist
- fi
-
- sox "$file" -S -n spectrogram -o "$outname"
-
- # Add the generated spectrogram file name to the array
- spectrogram_files+=("$outname")
- else
- echo "File not found: $file"
- fi
- done
-
- if [[ ${#} -gt 0 ]]; then
- read -p "Do you want to open the spectrogram(s)? (y/n): " open_choice
- case "$open_choice" in
- [Yy])
- for file in "${spectrogram_files[@]}"; do
- xdg-open "$file" # Open the spectrogram images generated from the provided audio files
- done
- ;;
- [Nn])
- echo "Not opening the spectrogram(s)."
- ;;
- *)
- echo "Invalid choice. Not opening the spectrogram(s)."
- ;;
- esac
- fi
-}
-
-# Call the spec function with provided arguments
-spec "$@"
diff --git a/systray b/systray
deleted file mode 100755
index 0fbc768..0000000
--- a/systray
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-
-hidden=/tmp/syshide.lock
-file="$HOME/.config/polybar/config.ini"
-
-if [[ ! -e $hidden ]]; then
- polybar-msg action "#systray.hook.1"
- xdo hide -n stalonetray
- touch "$hidden"
- sed -i 's/systray\ninitial=.*/systray\ninitial=2/g' "$file"
-else
- polybar-msg action "#systray.hook.0"
- xdo show -n stalonetray
- xdo raise -n stalonetray
- rm "$hidden"
- sed -i 's/systray\ninitial=.*/systray\ninitial=1/g' "$file"
-fi
diff --git a/test/.editorconfig b/test/.editorconfig
deleted file mode 100644
index db601e5..0000000
--- a/test/.editorconfig
+++ /dev/null
@@ -1,14 +0,0 @@
-
-root = true
-
-
-[*]
-charset = utf-8
-indent_style = tab
-indent_size = 4
-max_line_length = 10000
-
-# Python config
-[**.py]
-max_line_length = 10000
-indent_size = 4
diff --git a/test/unhidewindow b/test/unhidewindow
deleted file mode 100755
index 7e276df..0000000
--- a/test/unhidewindow
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/env bash
-# dependency: xtitle
-
-ids=($(bspc query -N -n .hidden.window)) || exit
-options="$(xtitle "${ids[@]}")"
-
-id_index="$(<<< "$options" rofi -dmenu -format i)"
-if [[ -n "${id_index}" ]]; then
- bspc node "${ids[${id_index}]}" -g hidden=off -f
-fi
diff --git a/title-bar b/title-bar
deleted file mode 100755
index 596d426..0000000
--- a/title-bar
+++ /dev/null
@@ -1,137 +0,0 @@
-#!/bin/bash
-
-# Lock file for preventing multiple instances
-lockfile="/tmp/title-bar.lock"
-
-# Check if the lock file exists and obtain the PID
-if [ -e "$lockfile" ]; then
- # If the lock file exists, check if the process is still running
- lock_pid=$(cat "$lockfile")
- if ps -p "$lock_pid" >/dev/null; then
- echo "title-bar is already running. Exiting."
- exit 1
- else
- # If the process is not running, remove the stale lock file
- rm "$lockfile"
- fi
-fi
-
-# Create a new lock file with the current PID
-echo "$$" >"$lockfile"
-
-# Debug log file
-DEBUG_LOG="/tmp/title-bar_debug.log"
-
-# Debug function
-debug_echo() {
- echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >>"$DEBUG_LOG"
-}
-
-debug_echo "Script execution started."
-
-# Background image path
-bg_path="/tmp/bg_${bgw}.png"
-
-# Function to create background images
-create_background_image() {
- echo -e "\e[48;2;255;165;0m" # Orange color
-}
-
-# Kill existing lemonbar instances and update script
-pkill -f "lemonbar"
-pkill -f "update-title"
-
-# Get the focused window information
-focused_info="$(bspc query -T --names -M)"
-
-# Get the focused window ID
-focused_id="$(bspc query -N -n focused)"
-
-# Check if the window ID is empty
-if [ "$focused_id" = "" ]; then
- echo "No focused window found."
- exit
-fi
-
-# Check if the focused window is in fullscreen mode
-if bspc query -T -n "$focused_id" | grep -q '"state":"fullscreen"'; then
- echo "Focused window is in fullscreen mode. Exiting."
- rm -f "$lockfile" # Remove the lock file
- exit
-fi
-
-# Get the WM_CLASS of the focused window
-focused_class="$(xprop WM_CLASS -id "$focused_id" | awk -F '"' '{print $2}')"
-
-debug_echo "Focused Class: $focused_class"
-
-# Exclude certain window classes
-case "$focused_class" in
- "Nwg-menu" | "nwg-menu" | "Steam" | "Qmmp" | "Wrapper-1.0" | "xfce4-panel")
- debug_echo "Excluded window class: $focused_class. Exiting script."
- rm -f "$lockfile" # Remove the lock file
- exit
- ;;
-esac
-
-# Get focused window geometry
-read -r x y w h < <(xwininfo -id "$focused_id" |
- awk '/Absolute upper-left X:/ { x=$4 }
- /Absolute upper-left Y:/ { y=$4 }
- /Width:/ { w=$2 }
- /Height:/ { h=$2 }
-END { print x, y, w, h }')
-
-#geoBar=$(echo "$w 13 $x $y" | awk '{print $1+4"x13+"$3"+"$4-8}')
-#geoBar=$(echo "$w 13 $x $y" | awk '{print $1-7"x10+"$3+5"+"$4-4}')
-geoBar=$(echo "$w 13 $x $y" | awk '{print $1+7"x10+"$3"+"$4}')
-
-# Create background image based on window width
-create_background_image
-
-# Launch lemonbar with consistent background color
-echo -e "%{c}%{I:${bg_path}:}%{l}%{I#FF000000:$HOME/.scripts/assets/corner.png:}%{r}%{I:$HOME/.scripts/assets/corner.png:}" |
-lemonbar -d -p -B "#000000" -U "#FFFFFF" -F "#FFFFFF" -f "Jetbrains Mono:bold:size=10" -g "$geoBar" &
-#lemonbar -d -p -B "#FFFFFF" -U "#000000" -F "#000000" -f "Jetbrains Mono:bold:size=10" -g "$geoBar" &
-#lemonbar -d -p -B "$HOME/.scripts/assets/trans.png" -U "#77dd77" -F "#4e585d" -f "Terminus:size=1" -g "$geoBar" &
-sleep 0.05
-
-# Launch update-title for the focused window
-~/.scripts/update-title "$focused_id" | lemonbar -d -g "$geoBar" -f "Jetbrains Mono:bold:size=6" -f "Jetbrains Mono:bold:size=6" -p | bash &
-
-# Wait for lemonbar to start
-sleep 0.1
-
-# Adjust stacking order for the focused window
-barinfos="$(xwininfo -tree -root | grep "$focused_id")"
-bgid="$(echo "$barinfos" | tail -n 1 | awk '{print $1}')"
-xdo below -t "$focused_id" "$bgid"
-xdo above -t "$bgid" "$(echo "$barinfos" | head -n 1 | awk '{print $1}')"
-
-debug_echo "Script execution completed."
-
-# Function to handle updates triggered by BSPWM events
-handle_bspwm_events() {
- while read -r line; do
- event_type=$(echo "$line" | jq -r '.type')
-
- case "$event_type" in
- focus_changed)
- focused_node=$(echo "$line" | jq -r '.node')
- ~/.scripts/update-title "$focused_node" | lemonbar -p -g "$geoBar" -f "Jetbrains Mono:size=6" -f "Jetbrains Mono:size=6" | bash &
- ;;
- node_add | node_remove | node_flag | node_geometry)
- # Handle other node-related events
- ;;
- *)
- # Handle other events as needed
- ;;
- esac
- done
-}
-
-# Subscribe to BSPWM events and pass them to the handler function
-bspc subscribe | handle_bspwm_events &
-
-# Remove the lock file when the script exits
-trap 'rm -f "$lockfile"; pkill -f "title-bar"; exit 0' EXIT
diff --git a/toggle-control b/toggle-control
deleted file mode 100755
index 8de5200..0000000
--- a/toggle-control
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/bin/bash
-
-EWW=$(which eww)
-STATE_FILE="$HOME/.eww_module_state"
-
-# Function to close the control module
-close_control_module() {
- "$EWW" close control &
-}
-
-# Function to open the control module
-open_control_module() {
- touch "$STATE_FILE"
- "$EWW" open control &
-}
-
-# Function to check if the cursor is inside the control window
-is_cursor_inside_control() {
- local mouse_x=$(xdotool getmouselocation --shell | awk -F '=' '/X/{print $2}')
- local mouse_y=$(xdotool getmouselocation --shell | awk -F '=' '/Y/{print $2}')
-
- local control_x=1110
- local control_y=0
- local control_width=240
- local control_height=360
-
- [ "$mouse_x" -ge "$control_x" -a "$mouse_x" -le "$((control_x + control_width))" -a \
- "$mouse_y" -ge "$control_y" -a "$mouse_y" -le "$((control_y + control_height))" ]
-}
-
-# Function to close the control module if cursor is outside the control window
-close_control_on_leave() {
- while true; do
- sleep 0.1
- if [ -e "$STATE_FILE" ] && ! is_cursor_inside_control; then
- close_control_module
- rm "$STATE_FILE"
- break
- fi
- done
-}
-
-# Check if the module is currently running
-if [ -e "$STATE_FILE" ]; then
- # If the file exists, the module is running, so close it when cursor leaves
- close_control_on_leave
-else
- # If the file doesn't exist, the module is not running, so start it
- open_control_module
-
- # Start monitoring cursor position to close the control window if it leaves
- close_control_on_leave &
-fi
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/gsettings.sh b/unix/sys/gsettings.sh
index 0cd28c2..9054344 100755
--- a/gsettings.sh
+++ b/unix/sys/gsettings.sh
@@ -25,3 +25,16 @@ gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/or
# 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/session_manager.sh b/unix/sys/session_manager.sh
index b6a6b03..b6a6b03 100755
--- a/session_manager.sh
+++ b/unix/sys/session_manager.sh
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/colors.sh b/unix/utils/colors.sh
index fc1c10c..fc1c10c 100755
--- a/colors.sh
+++ b/unix/utils/colors.sh
diff --git a/cryptocheck b/unix/utils/cryptocheck
index 02ba42d..02ba42d 100755
--- a/cryptocheck
+++ b/unix/utils/cryptocheck
diff --git a/cryptonotify b/unix/utils/cryptonotify
index 47883c3..47883c3 100755
--- a/cryptonotify
+++ b/unix/utils/cryptonotify
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/heads-up-display b/unix/utils/heads-up-display
index 8857675..54f5de1 100755
--- a/heads-up-display
+++ b/unix/utils/heads-up-display
@@ -34,7 +34,7 @@
# Set the environment variables to x11 to allow working in Wayland
export GDK_BACKEND=x11
export QT_QPA_PLATFORM=xcb
-export WAYLAND_DISPLAY=""
+#export WAYLAND_DISPLAY=""
export WINIT_UNIX_BACKEND=x11
# Supported terminals and dropdown class
@@ -62,9 +62,9 @@ if [ "$my_term" = "" ]; then
# 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 & ;;
- "kitty") kitty --class heads-up-display tmux new-session -A -s HUD -e bash & ;;
- "alacritty") alacritty --class heads-up-display -e tmux new-session -A -s HUD -e bash & ;;
+ "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
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/kill-notify b/unix/utils/kill-notify
index f7f749e..f7f749e 100755
--- a/kill-notify
+++ b/unix/utils/kill-notify
diff --git a/kill-process b/unix/utils/kill-process
index 5247e5f..5247e5f 100755
--- a/kill-process
+++ b/unix/utils/kill-process
diff --git a/mnt b/unix/utils/mnt
index dc19ca9..dc19ca9 100755
--- a/mnt
+++ b/unix/utils/mnt
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/neovim.sh b/unix/utils/neovim.sh
index 39554cb..c9adcca 100755
--- a/neovim.sh
+++ b/unix/utils/neovim.sh
@@ -11,7 +11,7 @@ RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
-# Function to handle errors
+# Handle errors
handle_error() {
local message="$1"
printf "${RED}Error: $message${NC}\n"
@@ -65,15 +65,16 @@ check_neovim_installed() {
# Nightly version
nightly_version() {
- local url="https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage"
+ 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+')"
+ 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/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+')"
@@ -88,13 +89,13 @@ specific_version() {
version="v$version"
fi
- local url="https://github.com/neovim/neovim/releases/download/$version/nvim.appimage"
+ 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+')"
+ version_id="Version $version $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+\.\d+')"
}
-# Function to download a file using wget or curl
+# Download a file using wget or curl
download_file() {
local url="$1"
local output="$2"
@@ -346,7 +347,7 @@ else
esac
fi
-# Function to check for updates and display breaking changes
+# 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=""
@@ -368,7 +369,7 @@ check_version_updates() {
fi
}
-# Function to display breaking changes for a specific version
+# 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"
diff --git a/scratchpad b/unix/utils/pack
index 430e71e..4b8760c 100755
--- a/scratchpad
+++ b/unix/utils/pack
@@ -1,19 +1,40 @@
-#!/bin/bash
+#!/bin/sh
# 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
+# 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
+
-# 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 WAYLAND_DISPLAY=""
export WINIT_UNIX_BACKEND=x11
# Supported terminals and dropdown class
@@ -21,7 +42,7 @@ 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
+ if pgrep -f "$term.*--class pack" >/dev/null; then
my_term="$term"
break
fi
@@ -41,14 +62,15 @@ if [ "$my_term" = "" ]; then
# 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 & ;;
+ "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 scratchpad)"
+id="$(xdo id -N pack)"
# Toggle scratchpad terminal visibility
if [ "$id" != "" ]; then
diff --git a/rofi-network-manager.sh b/unix/utils/rofi-network-manager.sh
index 61106d2..61106d2 100755
--- a/rofi-network-manager.sh
+++ b/unix/utils/rofi-network-manager.sh
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/track-books.sh b/unix/utils/track-books.sh
index f13add8..f13add8 100755
--- a/track-books.sh
+++ b/unix/utils/track-books.sh
diff --git a/umnt b/unix/utils/umnt
index 6d9b788..6d9b788 100755
--- a/umnt
+++ b/unix/utils/umnt
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 <app>
+# Example: ./waypipe_app user@remote gedit
+
+if [ $# -lt 2 ]; then
+ echo "Usage: $0 user@remote <app> [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/xtouch b/unix/utils/xtouch
index a5eb23b..a5eb23b 100755
--- a/xtouch
+++ b/unix/utils/xtouch
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 <file> <website>"
+ 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 '<option value="[0-9]+">Windows' | cut -d '"' -f 2 | head -n 1 | tr -cd '0-9' | head -c 16)"
+
+ if [[ -z "$product_edition_id" ]]; then
+ print_error "Failed to extract product edition ID."
+ print_error "Please download Windows $windows_version ISO manually from $url"
+ return 1
+ fi
+
+ print_success "Product Edition ID: $product_edition_id"
+
+ # Step 3: Register session ID
+ print_info "Registering session ID: $session_id"
+ curl --disable --silent --output /dev/null --user-agent "$user_agent" \
+ --header "Accept:" --max-filesize 100K --fail --proto =https --tlsv1.2 \
+ --http1.1 -- "https://vlscppe.microsoft.com/tags?org_id=y6jn8c31&session_id=$session_id" || {
+ print_error "Failed to register session ID."
+ return 1
+ }
+
+ # Step 4: Get language SKU ID
+ print_info "Getting language SKU ID..."
+ local language_skuid_table_json
+ language_skuid_table_json="$(curl --disable -s --fail --max-filesize 100K --proto =https --tlsv1.2 --http1.1 \
+ "https://www.microsoft.com/software-download-connector/api/getskuinformationbyproductedition?profile=${profile}&ProductEditionId=${product_edition_id}&SKU=undefined&friendlyFileName=undefined&Locale=en-US&sessionID=${session_id}")" || {
+ handle_curl_error $?
+ print_error "Failed to get language SKU information."
+ return 1
+ }
+
+ # Extract SKU ID for selected language
+ local sku_id
+
+ # Try with jq if available (more reliable)
+ if command -v jq >/dev/null 2>&1; then
+ sku_id="$(echo "$language_skuid_table_json" | jq -r '.Skus[] | select(.LocalizedLanguage=="'"$language"'" or .Language=="'"$language"'").Id')"
+ else
+ # Fallback to grep/cut if jq not available
+ sku_id="$(echo "$language_skuid_table_json" | grep -o '"Id":"[^"]*","Language":"'"$language"'"' | cut -d'"' -f4)"
+
+ if [[ -z "$sku_id" ]]; then
+ # Try alternative extraction method
+ sku_id="$(echo "$language_skuid_table_json" | grep -o '"LocalizedLanguage":"'"$language"'","Id":"[^"]*"' | cut -d'"' -f6)"
+ fi
+ fi
+
+ if [[ -z "$sku_id" ]]; then
+ print_error "Failed to extract SKU ID for $language."
+ return 1
+ fi
+
+ print_success "SKU ID: $sku_id"
+
+ # Step 5: Get ISO download link
+ print_info "Getting ISO download link..."
+ local iso_download_link_json
+ iso_download_link_json="$(curl --disable -s --fail --referer "$url" \
+ "https://www.microsoft.com/software-download-connector/api/GetProductDownloadLinksBySku?profile=${profile}&productEditionId=undefined&SKU=${sku_id}&friendlyFileName=undefined&Locale=en-US&sessionID=${session_id}")"
+
+ local failed=0
+
+ if [[ -z "$iso_download_link_json" ]]; then
+ print_error "Microsoft servers gave an empty response to the download request."
+ failed=1
+ fi
+
+ if echo "$iso_download_link_json" | grep -q "Sentinel marked this request as rejected."; then
+ print_error "Microsoft blocked the automated download request based on your IP address."
+ failed=1
+ fi
+
+ if [[ "$failed" -eq 1 ]]; then
+ print_warning "Please manually download the Windows $windows_version ISO using a web browser from: $url"
+ print_warning "Save the downloaded ISO to: $WIN_ISO_DIR"
+ return 1
+ fi
+
+ # Extract 64-bit ISO download URL
+ local iso_download_link
+
+ # Try with jq if available
+ if command -v jq >/dev/null 2>&1; then
+ iso_download_link="$(echo "$iso_download_link_json" | jq -r '.ProductDownloadOptions[].Uri' | grep x64 | head -n 1)"
+ else
+ # Fallback to grep/cut if jq not available
+ iso_download_link="$(echo "$iso_download_link_json" | grep -o '"Uri":"[^"]*x64[^"]*"' | cut -d'"' -f4 | head -n 1)"
+ fi
+
+ if [[ -z "$iso_download_link" ]]; then
+ print_error "Failed to extract the download link from Microsoft's response."
+ print_warning "Manually download the Windows $windows_version ISO using a web browser from: $url"
+ return 1
+ fi
+
+ print_success "Got download link: ${iso_download_link%%\?*}"
+
+ # Extract filename from URL
+ local file_name="$(echo "$iso_download_link" | cut -d'?' -f1 | rev | cut -d'/' -f1 | rev)"
+
+ # If filename couldn't be extracted, use default
+ if [[ -z "$file_name" || "$file_name" == "$iso_download_link" ]]; then
+ file_name="windows-$windows_version.iso"
+ fi
+
+ # Step 6: Download the ISO
+ print_info "Downloading Windows $windows_version ISO to $WIN_ISO_DIR/$file_name. This may take a while..."
+
+ # Direct curl download
+ curl --disable --progress-bar --fail --location --proto '=https' --tlsv1.2 --http1.1 \
+ --retry 3 --retry-delay 3 --connect-timeout 30 \
+ --output "$WIN_ISO_DIR/$file_name" "$iso_download_link" || {
+ handle_curl_error $?
+ return 1
+ }
+
+ if [[ $? -ne 0 ]]; then
+ print_error "Failed to download the Windows $windows_version ISO."
+ return 1
+ fi
+
+ print_success "Successfully downloaded Windows $windows_version ISO to $WIN_ISO_DIR/$file_name"
+
+ # Return the downloaded filename so calling code can use it
+ echo "$file_name"
+ return 0
+}
+
+# Create unattended installation ISO with proper Windows 11 bypass
+create_unattended_iso() {
+ print_info "Creating unattended installation ISO..."
+
+ # Create enhanced autounattend.xml with Windows 11 TPM/Secure Boot bypass
+ if [ ! -f "$WIN_ISO_DIR/unattended/autounattend.xml" ]; then
+ print_info "Creating enhanced autounattend.xml with Windows 11 bypass..."
+ mkdir -p "$WIN_ISO_DIR/unattended"
+ cat >"$WIN_ISO_DIR/unattended/autounattend.xml" <<'EOF'
+<?xml version="1.0" encoding="utf-8"?>
+<unattend xmlns="urn:schemas-microsoft-com:unattend">
+ <settings pass="windowsPE">
+ <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SetupUILanguage>
+ <UILanguage>en-US</UILanguage>
+ </SetupUILanguage>
+ <InputLocale>en-US</InputLocale>
+ <SystemLocale>en-US</SystemLocale>
+ <UILanguage>en-US</UILanguage>
+ <UILanguageFallback>en-US</UILanguageFallback>
+ <UserLocale>en-US</UserLocale>
+ </component>
+ <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <DiskConfiguration>
+ <Disk wcm:action="add">
+ <CreatePartitions>
+ <CreatePartition wcm:action="add">
+ <Order>1</Order>
+ <Type>Primary</Type>
+ <Size>100</Size>
+ </CreatePartition>
+ <CreatePartition wcm:action="add">
+ <Order>2</Order>
+ <Type>EFI</Type>
+ <Size>100</Size>
+ </CreatePartition>
+ <CreatePartition wcm:action="add">
+ <Order>3</Order>
+ <Type>MSR</Type>
+ <Size>16</Size>
+ </CreatePartition>
+ <CreatePartition wcm:action="add">
+ <Order>4</Order>
+ <Type>Primary</Type>
+ <Extend>true</Extend>
+ </CreatePartition>
+ </CreatePartitions>
+ <ModifyPartitions>
+ <ModifyPartition wcm:action="add">
+ <Order>1</Order>
+ <PartitionID>1</PartitionID>
+ <Label>WINRE</Label>
+ <Format>NTFS</Format>
+ <TypeID>DE94BBA4-06D1-4D40-A16A-BFD50179D6AC</TypeID>
+ </ModifyPartition>
+ <ModifyPartition wcm:action="add">
+ <Order>2</Order>
+ <PartitionID>2</PartitionID>
+ <Label>System</Label>
+ <Format>FAT32</Format>
+ </ModifyPartition>
+ <ModifyPartition wcm:action="add">
+ <Order>3</Order>
+ <PartitionID>3</PartitionID>
+ </ModifyPartition>
+ <ModifyPartition wcm:action="add">
+ <Order>4</Order>
+ <PartitionID>4</PartitionID>
+ <Label>Windows</Label>
+ <Format>NTFS</Format>
+ </ModifyPartition>
+ </ModifyPartitions>
+ <DiskID>0</DiskID>
+ <WillWipeDisk>true</WillWipeDisk>
+ </Disk>
+ </DiskConfiguration>
+ <ImageInstall>
+ <OSImage>
+ <InstallTo>
+ <DiskID>0</DiskID>
+ <PartitionID>4</PartitionID>
+ </InstallTo>
+ <InstallToAvailablePartition>false</InstallToAvailablePartition>
+ <WillShowUI>OnError</WillShowUI>
+ <InstallFrom>
+ <MetaData wcm:action="add">
+ <Key>/IMAGE/INDEX</Key>
+ <Value>6</Value>
+ </MetaData>
+ </InstallFrom>
+ </OSImage>
+ </ImageInstall>
+ <UserData>
+ <AcceptEula>true</AcceptEula>
+ <FullName>Windows User</FullName>
+ <Organization>Windows</Organization>
+ <ProductKey>
+ <WillShowUI>Never</WillShowUI>
+ </ProductKey>
+ </UserData>
+ <!-- Windows 11 Requirements Bypass -->
+ <RunSynchronous>
+ <RunSynchronousCommand wcm:action="add">
+ <Order>1</Order>
+ <Path>reg add HKLM\SYSTEM\Setup\LabConfig /v BypassTPMCheck /t REG_DWORD /d 1 /f</Path>
+ </RunSynchronousCommand>
+ <RunSynchronousCommand wcm:action="add">
+ <Order>2</Order>
+ <Path>reg add HKLM\SYSTEM\Setup\LabConfig /v BypassSecureBootCheck /t REG_DWORD /d 1 /f</Path>
+ </RunSynchronousCommand>
+ <RunSynchronousCommand wcm:action="add">
+ <Order>3</Order>
+ <Path>reg add HKLM\SYSTEM\Setup\LabConfig /v BypassRAMCheck /t REG_DWORD /d 1 /f</Path>
+ </RunSynchronousCommand>
+ <RunSynchronousCommand wcm:action="add">
+ <Order>4</Order>
+ <Path>reg add HKLM\SYSTEM\Setup\LabConfig /v BypassStorageCheck /t REG_DWORD /d 1 /f</Path>
+ </RunSynchronousCommand>
+ <RunSynchronousCommand wcm:action="add">
+ <Order>5</Order>
+ <Path>reg add HKLM\SYSTEM\Setup\LabConfig /v BypassCPUCheck /t REG_DWORD /d 1 /f</Path>
+ </RunSynchronousCommand>
+ </RunSynchronous>
+ <EnableFirewall>false</EnableFirewall>
+ <EnableNetwork>true</EnableNetwork>
+ </component>
+ </settings>
+ <settings pass="specialize">
+ <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <InputLocale>en-US</InputLocale>
+ <SystemLocale>en-US</SystemLocale>
+ <UILanguage>en-US</UILanguage>
+ <UILanguageFallback>en-US</UILanguageFallback>
+ <UserLocale>en-US</UserLocale>
+ </component>
+ <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <ComputerName>DESKTOP-PC</ComputerName>
+ <TimeZone>UTC</TimeZone>
+ </component>
+ <component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <SkipAutoActivation>true</SkipAutoActivation>
+ </component>
+ </settings>
+ <settings pass="oobeSystem">
+ <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <AutoLogon>
+ <Password>
+ <Value>password</Value>
+ <PlainText>true</PlainText>
+ </Password>
+ <LogonCount>1</LogonCount>
+ <Username>Administrator</Username>
+ <Enabled>true</Enabled>
+ </AutoLogon>
+ <OOBE>
+ <HideEULAPage>true</HideEULAPage>
+ <HideLocalAccountScreen>true</HideLocalAccountScreen>
+ <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
+ <HideOnlineAccountScreens>true</HideOnlineAccountScreens>
+ <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
+ <NetworkLocation>Home</NetworkLocation>
+ <ProtectYourPC>1</ProtectYourPC>
+ <SkipMachineOOBE>true</SkipMachineOOBE>
+ <SkipUserOOBE>true</SkipUserOOBE>
+ </OOBE>
+ <UserAccounts>
+ <AdministratorPassword>
+ <Value>password</Value>
+ <PlainText>true</PlainText>
+ </AdministratorPassword>
+ <LocalAccounts>
+ <LocalAccount wcm:action="add">
+ <Password>
+ <Value>password</Value>
+ <PlainText>true</PlainText>
+ </Password>
+ <Name>User</Name>
+ <Group>Administrators</Group>
+ <DisplayName>User</DisplayName>
+ </LocalAccount>
+ </LocalAccounts>
+ </UserAccounts>
+ <!-- Additional Windows 11 bypass commands -->
+ <FirstLogonCommands>
+ <SynchronousCommand wcm:action="add">
+ <Order>1</Order>
+ <CommandLine>reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU /v NoAutoUpdate /t REG_DWORD /d 1 /f</CommandLine>
+ </SynchronousCommand>
+ <SynchronousCommand wcm:action="add">
+ <Order>2</Order>
+ <CommandLine>reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f</CommandLine>
+ </SynchronousCommand>
+ <SynchronousCommand wcm:action="add">
+ <Order>3</Order>
+ <CommandLine>powershell -Command "Set-ExecutionPolicy Unrestricted -Force"</CommandLine>
+ </SynchronousCommand>
+ </FirstLogonCommands>
+ </component>
+ </settings>
+</unattend>
+EOF
+ fi
+
+ # Create the unattended ISO
+ if command -v genisoimage >/dev/null 2>&1; then
+ print_info "Creating unattended ISO using genisoimage..."
+ genisoimage -J -r -o "$WIN_ISO_DIR/unattended.iso" "$WIN_ISO_DIR/unattended" 2>/dev/null
+ return $?
+ elif command -v mkisofs >/dev/null 2>&1; then
+ print_info "Creating unattended ISO using mkisofs..."
+ mkisofs -J -r -o "$WIN_ISO_DIR/unattended.iso" "$WIN_ISO_DIR/unattended" 2>/dev/null
+ return $?
+ elif command -v xorriso >/dev/null 2>&1; then
+ print_info "Creating unattended ISO using xorriso..."
+ xorriso -as genisoimage -J -r -o "$WIN_ISO_DIR/unattended.iso" "$WIN_ISO_DIR/unattended" 2>/dev/null
+ return $?
+ else
+ print_warning "No ISO creation tool found (genisoimage, mkisofs, or xorriso)"
+ print_warning "Installing genisoimage..."
+ if command -v apt-get >/dev/null 2>&1; then
+ sudo apt-get update && sudo apt-get install -y genisoimage
+ elif command -v yum >/dev/null 2>&1; then
+ sudo yum install -y genisoimage
+ elif command -v pacman >/dev/null 2>&1; then
+ sudo pacman -S --noconfirm cdrtools
+ fi
+
+ # Try again after installation
+ if command -v genisoimage >/dev/null 2>&1; then
+ print_info "Creating unattended ISO using newly installed genisoimage..."
+ genisoimage -J -r -o "$WIN_ISO_DIR/unattended.iso" "$WIN_ISO_DIR/unattended" 2>/dev/null
+ return $?
+ else
+ print_error "Failed to install ISO creation tools"
+ return 1
+ fi
+ fi
+}
+
+# Download or locate essential files
+prepare_files() {
+ # Find Windows ISO
+ WINDOWS_ISO_PATH=$(find_windows_iso | tail -n 1 | xargs)
+ local iso_found=false
+
+ # Check if we found a valid ISO
+ if [[ -n "$WINDOWS_ISO_PATH" && -f "$WINDOWS_ISO_PATH" ]]; then
+ print_success "Using Windows ISO: $WINDOWS_ISO_PATH"
+ iso_found=true
+ else
+ print_warning "Windows ISO not found, attempting to download..."
+ if download_windows_iso; then
+ # Check again after download attempt
+ WINDOWS_ISO_PATH=$(find_windows_iso | tail -n 1 | xargs)
+ if [[ -n "$WINDOWS_ISO_PATH" && -f "$WINDOWS_ISO_PATH" ]]; then
+ print_success "Using downloaded Windows ISO: $WINDOWS_ISO_PATH"
+ iso_found=true
+ fi
+ fi
+ fi
+
+ # If we still don't have an ISO, exit
+ if [[ "$iso_found" != "true" ]]; then
+ print_error "Could not find or download Windows ISO"
+ print_info "Please place your Windows ISO file in: $WIN_ISO_DIR"
+ exit 1
+ fi
+
+ # Download VirtIO drivers if missing (optional for stealth mode)
+ if [[ ! -f "$ISO_VIRTIO" ]]; then
+ print_info "VirtIO drivers ISO not found, downloading (for fallback)..."
+ download_file "$VIRTIO_ISO_URL" "$ISO_VIRTIO" "" "true"
+ else
+ print_success "VirtIO drivers ISO already exists: $ISO_VIRTIO"
+ fi
+
+ # Create unattended ISO if it doesn't exist
+ if [[ ! -f "$ISO_UNATTENDED" ]]; then
+ create_unattended_iso
+ if [[ $? -eq 0 ]]; then
+ print_success "Created unattended ISO: $ISO_UNATTENDED"
+ else
+ print_error "Failed to create unattended ISO"
+ exit 1
+ fi
+ else
+ print_success "Unattended ISO already exists: $ISO_UNATTENDED"
+ fi
+}
+
+# Locate OVMF firmware files
+locate_ovmf() {
+ OVMF_DIRS=(
+ "/usr/share/OVMF"
+ "/usr/share/qemu"
+ "/usr/lib/qemu"
+ "/usr/share/edk2"
+ "/usr/lib/edk2"
+ "/usr/share/edk2/ovmf"
+ "/usr/share/edk2-ovmf"
+ "/usr/share/qemu/edk2-x86_64-code.fd"
+ )
+
+ OVMF_CODE=""
+ OVMF_VARS=""
+
+ for dir in "${OVMF_DIRS[@]}"; do
+ if [[ -f "$dir" ]]; then
+ # Handle direct file paths
+ if [[ "$dir" == *"code.fd" ]]; then
+ OVMF_CODE="$dir"
+ OVMF_VARS="$(dirname "$dir")/edk2-x86_64-vars.fd"
+ fi
+ elif [[ -d "$dir" ]]; then
+ # Handle directories
+ [[ -z "$OVMF_CODE" ]] && OVMF_CODE=$(find "$dir" -type f -name "OVMF_CODE.fd" -o -name "edk2-x86_64-code.fd" 2>/dev/null | head -n 1)
+ [[ -z "$OVMF_VARS" ]] && OVMF_VARS=$(find "$dir" -type f -name "OVMF_VARS.fd" -o -name "edk2-x86_64-vars.fd" 2>/dev/null | head -n 1)
+ fi
+ [[ -n "$OVMF_CODE" && -n "$OVMF_VARS" ]] && break
+ done
+
+ # Try package-specific locations
+ if [[ -z "$OVMF_CODE" || -z "$OVMF_VARS" ]]; then
+ # Ubuntu/Debian locations
+ [[ -z "$OVMF_CODE" ]] && OVMF_CODE="/usr/share/OVMF/OVMF_CODE_4M.fd"
+ [[ -z "$OVMF_VARS" && -f "/usr/share/OVMF/OVMF_VARS_4M.fd" ]] && OVMF_VARS="/usr/share/OVMF/OVMF_VARS_4M.fd"
+
+ # Arch Linux locations
+ [[ -z "$OVMF_CODE" ]] && OVMF_CODE="/usr/share/edk2-ovmf/x64/OVMF_CODE.fd"
+ [[ -z "$OVMF_VARS" && -f "/usr/share/edk2-ovmf/x64/OVMF_VARS.fd" ]] && OVMF_VARS="/usr/share/edk2-ovmf/x64/OVMF_VARS.fd"
+ fi
+
+ # Ensure a writable copy of OVMF_VARS.fd
+ local original_ovmf_vars="$OVMF_VARS"
+ OVMF_VARS="$FIRMWARE_DIR/OVMF_VARS.fd"
+
+ if [[ ! -f "$OVMF_VARS" && -f "$original_ovmf_vars" ]]; then
+ print_info "Copying OVMF_VARS.fd to $OVMF_VARS"
+ cp "$original_ovmf_vars" "$OVMF_VARS" 2>/dev/null || {
+ print_error "Failed to copy OVMF_VARS.fd!"
+ print_info "Trying to install OVMF firmware..."
+
+ # Try to install OVMF
+ if command -v apt-get >/dev/null 2>&1; then
+ sudo apt-get update && sudo apt-get install -y ovmf
+ elif command -v yum >/dev/null 2>&1; then
+ sudo yum install -y edk2-ovmf
+ elif command -v pacman >/dev/null 2>&1; then
+ sudo pacman -S --noconfirm edk2-ovmf
+ fi
+
+ # Try to locate again after installation
+ locate_ovmf
+ return
+ }
+ fi
+
+ # Check if required files exist
+ if [[ -z "$OVMF_CODE" || ! -f "$OVMF_CODE" ]]; then
+ print_error "OVMF_CODE.fd not found!"
+ print_info "Please install OVMF firmware package:"
+ print_info " Ubuntu/Debian: sudo apt install ovmf"
+ print_info " RHEL/CentOS: sudo yum install edk2-ovmf"
+ print_info " Arch: sudo pacman -S edk2-ovmf"
+ exit 1
+ fi
+ if [[ ! -f "$OVMF_VARS" ]]; then
+ print_error "OVMF_VARS.fd not found or could not be copied!"
+ exit 1
+ fi
+
+ print_success "Found OVMF firmware: $OVMF_CODE"
+ print_success "Using OVMF vars: $OVMF_VARS"
+}
+
+# Create VM disk image
+create_disk() {
+ # Check if the qcow2 image file exists; if not, create it
+ if [[ ! -f "$QCOW2_FILE" ]]; then
+ print_info "Creating $QCOW2_FILE with a size of $VM_SIZE"
+ qemu-img create -f qcow2 "$QCOW2_FILE" "$VM_SIZE" || {
+ print_error "Failed to create qcow2 image!"
+ exit 1
+ }
+ print_success "Created VM disk image: $QCOW2_FILE"
+ else
+ print_success "VM disk image already exists: $QCOW2_FILE"
+ fi
+}
+
+# Helper function to convert QCOW2 to RAW for stealth
+convert_to_raw() {
+ if [[ -f "$QCOW2_FILE" && ! -f "$RAW_FILE" ]]; then
+ print_info "Converting QCOW2 to RAW format for stealth..."
+ qemu-img convert -f qcow2 -O raw "$QCOW2_FILE" "$RAW_FILE" || {
+ print_error "Failed to convert to RAW format"
+ exit 1
+ }
+ print_success "Successfully converted to RAW format: $RAW_FILE"
+ elif [[ ! -f "$RAW_FILE" ]]; then
+ print_info "Creating RAW disk image..."
+ qemu-img create -f raw "$RAW_FILE" "$VM_SIZE" || {
+ print_error "Failed to create RAW disk image"
+ exit 1
+ }
+ print_success "Created RAW disk image: $RAW_FILE"
+ else
+ print_success "RAW disk image already exists: $RAW_FILE"
+ fi
+}
+
+# Check dependencies
+check_dependencies() {
+ local missing_deps=()
+
+ # Check for essential tools
+ command -v qemu-system-x86_64 >/dev/null 2>&1 || missing_deps+=("qemu-system-x86_64")
+ command -v swtpm >/dev/null 2>&1 || missing_deps+=("swtpm")
+ command -v curl >/dev/null 2>&1 || missing_deps+=("curl")
+ command -v uuidgen >/dev/null 2>&1 || missing_deps+=("uuidgen")
+ command -v openssl >/dev/null 2>&1 || missing_deps+=("openssl")
+
+ if [[ ${#missing_deps[@]} -gt 0 ]]; then
+ print_error "Missing required dependencies: ${missing_deps[*]}"
+ print_info "Please install them using your package manager:"
+ print_info " Ubuntu/Debian: sudo apt install qemu-system-x86 swtpm curl uuid-runtime openssl"
+ print_info " RHEL/CentOS: sudo yum install qemu-kvm swtpm curl util-linux openssl"
+ print_info " Arch: sudo pacman -S qemu swtpm curl util-linux openssl"
+ exit 1
+ fi
+}
+
+start_vm() {
+ # Verify that we have a Windows ISO
+ if [[ -z "$WINDOWS_ISO_PATH" || ! -f "$WINDOWS_ISO_PATH" ]]; then
+ print_error "Windows ISO file not found. Cannot start VM."
+ print_info "Please download Windows 11 ISO and save it to: $WIN_ISO_DIR"
+ exit 1
+ fi
+
+ # Anti-detection: Generate realistic hardware identifiers
+ REAL_MAC="00:1A:79:$(openssl rand -hex 3 | sed 's/../&:/g; s/:$//')" # Dell OUI
+ 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"
+
+ # Stop any existing swtpm process for this VM
+ if [[ -f "$TPM_SOCKET" ]]; then
+ print_info "Cleaning up existing TPM socket..."
+ rm -f "$TPM_SOCKET"
+ fi
+
+ # Start swtpm (TPM emulator)
+ print_info "Starting TPM emulator..."
+ /sbin/swtpm socket \
+ --ctrl type=unixio,path="$TPM_SOCKET" \
+ --terminate \
+ --tpmstate dir="$TPM_DIR" \
+ --tpm2 &
+
+ # Give swtpm a moment to create the socket
+ sleep 1
+
+ # Verify TPM socket exists
+ if [[ ! -S "$TPM_SOCKET" ]]; then
+ print_error "TPM socket not created: $TPM_SOCKET"
+ exit 1
+ fi
+
+ print_info "Starting stealth Windows 11 VM..."
+ print_info "Using Windows ISO: $WINDOWS_ISO_PATH"
+ print_info "VM will boot from unattended installation ISO"
+ print_info "Default login: Username=User, Password=password"
+ print_info "Anti-detection measures enabled"
+
+ # Build qemu arguments in an array for safety and readability
+ QEMU_ARGS=(
+ # Basic VM configuration
+ -name "$REAL_PRODUCT",process="$VM_NAME"
+ -machine q35,hpet=off,smm=on,vmport=off,accel=kvm
+
+ # Global optimizations
+ -global kvm-pit.lost_tick_policy=discard
+ -global ICH9-LPC.disable_s3=1
+
+ # CPU configuration (stealth)
+ -cpu host,-hypervisor,+invtsc,+ssse3,l3-cache=on,migratable=no
+ -smp "$SMP_CONFIG"
+ -m "$VM_RAM"
+
+ # SMBIOS spoofing for anti-detection
+ -smbios type=0,vendor="$REAL_VENDOR",version="$REAL_VERSION",date="03/15/2023"
+ -smbios type=1,manufacturer="$REAL_VENDOR",product="$REAL_PRODUCT",version="$REAL_VERSION",serial="$REAL_SERIAL",uuid="$REAL_UUID",family="$REAL_FAMILY"
+ -smbios type=2,manufacturer="$REAL_VENDOR",product="0NK70N",version="A00",serial="$REAL_SERIAL"
+ -smbios type=3,manufacturer="$REAL_VENDOR",version="$REAL_VERSION",serial="$REAL_SERIAL"
+ -smbios type=4,manufacturer="Intel(R) Corporation",version="11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz"
+
+ # Process management
+ -pidfile "$VM_DIR/$VM_NAME.pid"
+ -rtc base=localtime,clock=host,driftfix=slew
+
+ # Display and graphics
+ -vga qxl
+ -display sdl
+
+ # Boot configuration
+ -boot menu=on,splash-time=0,order=d,reboot-timeout=5000
+
+ # Random number generator
+ -object rng-random,id=rng0,filename=/dev/urandom
+ -device i82801b11-bridge,id=pci.1
+
+ # usb
+ -device usb-ehci,id=usb
+ -device usb-kbd,bus=usb.0
+ -device usb-tablet,bus=usb.0
+ -k en-us
+
+ # audio (pipewire)
+ -audiodev pipewire,id=audio0
+ -device AC97,audiodev=audio0
+
+ # Network (realistic NIC with stealth MAC)
+ -device rtl8139,netdev=nic,mac="$REAL_MAC"
+ -netdev user,hostname="$VM_NAME",hostfwd=tcp::"$HOST_PORT"-:"$GUEST_PORT",smb="$SHARED_DIR",id=nic
+
+ # UEFI firmware
+ -global driver=cfi.pflash01,property=secure,value=on
+ -drive if=pflash,format=raw,unit=0,file="$OVMF_CODE",readonly=on
+ -drive if=pflash,format=raw,unit=1,file="$OVMF_VARS"
+
+ # Main storage (AHCI for compatibility)
+ -device ahci,id=ahci
+ -drive if=none,id=disk0,format=raw,file="$RAW_FILE",cache=writeback
+ -device ide-hd,bus=ahci.0,drive=disk0
+
+ # CD-ROM drives (Windows ISO + Unattended)
+ -drive media=cdrom,index=0,file="$WINDOWS_ISO_PATH",if=ide
+ -drive media=cdrom,index=1,file="$ISO_UNATTENDED",if=ide
+
+ # TPM 2.0
+ -chardev socket,id=chrtpm,path="$TPM_SOCKET"
+ -tpmdev emulator,id=tpm0,chardev=chrtpm
+ -device tpm-tis,tpmdev=tpm0
+
+ # Monitoring and management sockets
+ -monitor unix:"$SOCKET_DIR/$VM_NAME-monitor.socket",server,nowait
+ -serial unix:"$SOCKET_DIR/$VM_NAME-serial.socket",server,nowait
+ )
+
+ # Add VirtIO ISO if it exists (as fallback)
+ if [[ -f "$ISO_VIRTIO" ]]; then
+ QEMU_ARGS+=(-drive media=cdrom,index=2,file="$ISO_VIRTIO",if=ide,readonly=on)
+ fi
+
+ print_info "Starting QEMU with stealth configuration..."
+ print_info "Monitor socket: $SOCKET_DIR/$VM_NAME-monitor.socket"
+ print_info "Serial socket: $SOCKET_DIR/$VM_NAME-serial.socket"
+ print_info "SSH port forwarding: localhost:$HOST_PORT -> VM:$GUEST_PORT"
+
+ # Execute qemu
+ exec qemu-system-x86_64 "${QEMU_ARGS[@]}"
+
+}
+
+# Helper function to convert QCOW2 to RAW for stealth
+convert_to_raw() {
+ if [[ -f "$QCOW2_FILE" && ! -f "$RAW_FILE" ]]; then
+ print_info "Converting QCOW2 to RAW format for stealth..."
+ qemu-img convert -f qcow2 -O raw "$QCOW2_FILE" "$RAW_FILE"
+ if [[ $? -eq 0 ]]; then
+ print_success "Successfully converted to RAW format"
+ else
+ print_error "Failed to convert to RAW format"
+ exit 1
+ fi
+ elif [[ ! -f "$RAW_FILE" ]]; then
+ print_info "Creating RAW disk image..."
+ qemu-img create -f raw "$RAW_FILE" "$VM_SIZE"
+ fi
+}
+
+# Anti-detection validation commands
+show_validation_commands() {
+ cat << 'EOF'
+=== VM DETECTION VALIDATION COMMANDS ===
+
+Run these commands inside Windows to verify stealth:
+
+# PowerShell - Check hardware info:
+Get-WmiObject -Class Win32_ComputerSystem | Select-Object Manufacturer, Model, TotalPhysicalMemory
+Get-WmiObject -Class Win32_BIOS | Select-Object Manufacturer, Version, SerialNumber
+Get-WmiObject -Class Win32_BaseBoard | Select-Object Manufacturer, Product, SerialNumber
+Get-WmiObject -Class Win32_Processor | Select-Object Name, Manufacturer, MaxClockSpeed
+
+# Check for hypervisor presence:
+Get-WmiObject -Class Win32_ComputerSystem | Select-Object HypervisorPresent
+bcdedit /enum | findstr hypervisorlaunchtype
+
+# Registry checks for VM artifacts:
+reg query "HKLM\HARDWARE\DESCRIPTION\System" /v SystemBiosVersion
+reg query "HKLM\HARDWARE\DESCRIPTION\System\BIOS" /v SystemManufacturer
+reg query "HKLM\SYSTEM\CurrentControlSet\Services" | findstr -i "vbox\|vmware\|qemu\|virtio"
+
+# Check network adapter:
+Get-WmiObject -Class Win32_NetworkAdapter | Where-Object {$_.NetConnectionStatus -eq 2} | Select-Object Name, MACAddress, Manufacturer
+
+# Check PCI devices for VM signatures:
+Get-WmiObject -Class Win32_PnPEntity | Where-Object {$_.Name -match "VirtIO|QEMU|VMware|VirtualBox|Hyper-V"} | Select-Object Name, DeviceID
+
+# Check running services:
+Get-Service | Where-Object {$_.Name -match "vbox|vmware|qemu|virtio|spice"}
+
+# Check for VM-specific processes:
+Get-Process | Where-Object {$_.ProcessName -match "vbox|vmware|qemu|virtio"}
+
+# Additional checks:
+systeminfo | findstr /C:"System Manufacturer" /C:"System Model" /C:"BIOS Version"
+wmic computersystem get manufacturer,model,name,systemtype
+
+EOF
+}
+
+# Cleanup function
+cleanup() {
+ print_info "Cleaning up..."
+
+ # Kill swtpm if running
+ if [[ -f "$TPM_SOCKET" ]]; then
+ pkill -f "swtpm.*$TPM_SOCKET" 2>/dev/null || true
+ rm -f "$TPM_SOCKET" 2>/dev/null || true
+ fi
+
+ # Remove PID file
+ if [[ -f "$VM_DIR/$VM_NAME.pid" ]]; then
+ rm -f "$VM_DIR/$VM_NAME.pid" 2>/dev/null || true
+ fi
+}
+
+# Trap cleanup on exit
+trap cleanup EXIT
+
+# Main execution function
+main() {
+ print_info "=== Windows 11 Stealth VM Setup ==="
+ print_info "VM Name: $VM_NAME"
+ print_info "VM Size: $VM_SIZE"
+ print_info "VM RAM: $VM_RAM"
+ print_info "VM CPUs: $VM_CPU"
+ print_info "Host Port: $HOST_PORT"
+
+ check_dependencies
+ prepare_files
+ locate_ovmf
+ create_disk
+ convert_to_raw
+ show_validation_commands
+
+ print_success "Setup complete! Starting VM..."
+ start_vm
+}
+
+# Run main function
+main "$@"
diff --git a/unix/virt/server.sh b/unix/virt/server.sh
new file mode 100755
index 0000000..6d937fd
--- /dev/null
+++ b/unix/virt/server.sh
@@ -0,0 +1,128 @@
+#!/usr/bin/env bash
+
+# Set variables
+HOST_DIR="machines"
+VM_NAME="proxmox"
+VM_DIR="$HOME/machines/vm"
+IMAGE_DIR="$HOME/machines/images"
+SOCKET_DIR="$VM_DIR"
+#ISO_FILE="$IMAGE_DIR/proxmox-ve_8.3-1.iso"
+ISO_FILE=$(find "$IMAGE_DIR" -type f -iname "proxmox*.iso" -exec stat --format="%Y %n" {} \; | sort -n | tail -n 1 | cut -d' ' -f2-)
+QCOW2_FILE="$VM_DIR/$VM_NAME.qcow2"
+HOST_PORT=22220
+GUEST_PORT=22
+SHARED_DIR="$HOME/machines/shared"
+FIRMWARE_DIR="$HOME/machines/firmware"
+VM_SIZE="300G" # Disk size in GB
+VM_RAM="12G" # RAM size
+VM_CPU="6" # Number of virtual CPUs
+CORES=$((VM_CPU / 2))
+THREADS_PER_CORE=2
+SOCKETS=1
+
+# Set SMP configuration
+SMP_CONFIG="cores=$CORES,threads=$THREADS_PER_CORE,sockets=$SOCKETS"
+
+# Ensure necessary directories exist
+mkdir -p "$HOME"/"$HOST_DIR"
+mkdir -p "$VM_DIR" "$IMAGE_DIR" "$SHARED_DIR" "$FIRMWARE_DIR"
+
+# Locate OVMF firmware files
+OVMF_DIRS=(
+ "/usr/share/OVMF"
+ "/usr/share/qemu"
+ "/usr/lib/qemu"
+ "/usr/share/edk2"
+ "/usr/lib/edk2"
+)
+
+OVMF_CODE=""
+OVMF_VARS=""
+
+for dir in "${OVMF_DIRS[@]}"; do
+ [[ -z "$OVMF_CODE" ]] && OVMF_CODE=$(find "$dir" -type f -name "OVMF_CODE.fd" -o -name "edk2-x86_64-code.fd" 2>/dev/null | head -n 1)
+ [[ -z "$OVMF_VARS" ]] && OVMF_VARS=$(find "$dir" -type f -name "OVMF_VARS.fd" 2>/dev/null | head -n 1)
+ [[ -n "$OVMF_CODE" && -n "$OVMF_VARS" ]] && break
+done
+
+# Ensure a writable copy of OVMF_VARS.fd
+OVMF_VARS="$FIRMWARE_DIR/OVMF_VARS.fd"
+
+if [[ ! -f "$OVMF_VARS" ]]; then
+ echo "Copying OVMF_VARS.fd to $OVMF_VARS"
+ cp /usr/share/edk2/OvmfX64/OVMF_VARS.fd "$OVMF_VARS" 2>/dev/null || {
+ echo "Error: Failed to copy OVMF_VARS.fd!" >&2
+ exit 1
+ }
+fi
+
+# Check if required files exist
+if [[ -z "$OVMF_CODE" ]]; then
+ echo "Error: OVMF_CODE.fd not found!" >&2
+ exit 1
+fi
+if [[ ! -f "$OVMF_VARS" ]]; then
+ echo "Error: OVMF_VARS.fd not found or could not be copied!" >&2
+ exit 1
+fi
+if [[ ! -f "$ISO_FILE" ]]; then
+ echo "Warning: $ISO_FILE ISO not found at $IMAGE_DIR"
+fi
+
+# Check if the qcow2 image file exists; if not, create it
+if [[ ! -f "$QCOW2_FILE" ]]; then
+ echo "Creating $QCOW2_FILE with a size of $VM_SIZE"
+ qemu-img create -f qcow2 "$QCOW2_FILE" "$VM_SIZE" || {
+ echo "Error: Failed to create qcow2 image!" >&2
+ exit 1
+ }
+else
+ echo ""
+fi
+
+# Run QEMU
+/sbin/qemu-system-x86_64 \
+ -name "$VM_NAME",process="$VM_NAME" \
+ -machine q35,smm=off,vmport=off,accel=kvm \
+ -global kvm-pit.lost_tick_policy=discard \
+ -cpu host \
+ -smp "$SMP_CONFIG" \
+ -m "$VM_RAM" \
+ -device virtio-balloon \
+ -pidfile "$VM_DIR/$VM_NAME.pid" \
+ -rtc base=utc,clock=host \
+ -vga none \
+ -device virtio-vga-gl,xres=1280,yres=800 \
+ -display sdl,gl=on \
+ -device virtio-rng-pci,rng=rng0 \
+ -object rng-random,id=rng0,filename=/dev/urandom \
+ -device qemu-xhci,id=spicepass \
+ -chardev spicevmc,id=usbredirchardev1,name=usbredir \
+ -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1 \
+ -chardev spicevmc,id=usbredirchardev2,name=usbredir \
+ -device usb-redir,chardev=usbredirchardev2,id=usbredirdev2 \
+ -chardev spicevmc,id=usbredirchardev3,name=usbredir \
+ -device usb-redir,chardev=usbredirchardev3,id=usbredirdev3 \
+ -device pci-ohci,id=smartpass \
+ -device usb-ccid \
+ -device usb-ehci,id=input \
+ -device usb-kbd,bus=input.0 \
+ -k en-us \
+ -device usb-tablet,bus=input.0 \
+ -audiodev pipewire,id=audio0 \
+ -device intel-hda \
+ -device hda-micro,audiodev=audio0 \
+ -device virtio-net,netdev=nic \
+ -netdev user,hostname="$VM_NAME",hostfwd=tcp::"$HOST_PORT"-:"$GUEST_PORT",id=nic \
+ -fsdev local,id=fsdev0,path="$SHARED_DIR",security_model=mapped-xattr \
+ -device virtio-9p-pci,fsdev=fsdev0,mount_tag="SharedDir" \
+ -global driver=cfi.pflash01,property=secure,value=on \
+ -drive if=pflash,format=raw,unit=0,file="$OVMF_CODE",readonly=on \
+ -drive if=pflash,format=raw,unit=1,file="$OVMF_VARS" \
+ -drive media=cdrom,index=0,file="$ISO_FILE" \
+ -device virtio-blk-pci,drive=SystemDisk \
+ -drive id=SystemDisk,if=none,format=qcow2,file="$QCOW2_FILE" \
+ -monitor unix:"$SOCKET_DIR/$VM_NAME-monitor.socket",server,nowait \
+ -serial unix:"$SOCKET_DIR/$VM_NAME-serial.socket",server,nowait
+#-serial unix:"$SOCKET_DIR/$VM_NAME-serial.socket",server,nowait 2>/dev/null
+#-netdev user,hostname="$VM_NAME",hostfwd=tcp::"$HOST_PORT"-:"$GUEST_PORT",smb="$SHARED_DIR",id=nic \
diff --git a/unix/virt/ubuntu b/unix/virt/ubuntu
new file mode 100755
index 0000000..c93941d
--- /dev/null
+++ b/unix/virt/ubuntu
@@ -0,0 +1,222 @@
+#!/usr/bin/env bash
+
+# Set variables
+HOST_DIR="$HOME/virt"
+VM_DIR="$HOME/virt/machines"
+IMAGE_DIR="$HOME/virt/images"
+SOCKET_DIR="$VM_DIR"
+
+BASE_NAME=$(basename "$0" .sh | sed 's/[0-9]*$//')
+VM_INDEX=$(basename "$0" .sh | grep -o '[0-9]*$')
+VM_INDEX=${VM_INDEX:-1} # default to 1 if no number
+
+VM_NAME="${BASE_NAME}${VM_INDEX}"
+QCOW2_FILE="$VM_DIR/$VM_NAME.qcow2"
+
+# If the file exists, use it
+if [[ -f "$QCOW2_FILE" ]]; then
+ echo "Using existing VM image: $QCOW2_FILE"
+else
+ # Loop to find first available index if not
+ while [[ -f "$QCOW2_FILE" ]]; do
+ ((VM_INDEX++))
+ VM_NAME="${BASE_NAME}${VM_INDEX}"
+ QCOW2_FILE="$VM_DIR/$VM_NAME.qcow2"
+ done
+ echo "Creating new VM image: $QCOW2_FILE"
+ qemu-img create -f qcow2 "$QCOW2_FILE" "$VM_SIZE" || {
+ echo "Error: Failed to create qcow2 image!" >&2
+ exit 1
+ }
+fi
+
+#ISO_FILE=$(ls -1t "$IMAGE_DIR"/ubuntu-*-desktop-*-amd64.iso 2>/dev/null | head -n1)
+#if [[ -z "$ISO_FILE" ]]; then
+# echo "Error: No Ubuntu ISO found in $IMAGE_DIR" >&2
+# exit 1
+#fi
+#echo "Using ISO: $ISO_FILE"
+
+# Directory to remember ISO choices
+CHOICES_DIR="$VM_DIR/.iso_choices"
+mkdir -p "$CHOICES_DIR"
+
+CHOICE_FILE="$CHOICES_DIR/$BASE_NAME.choice"
+
+if [[ -f "$CHOICE_FILE" ]]; then
+ # Already have a saved ISO for this VM
+ ISO_FILE=$(<"$CHOICE_FILE")
+ echo "Using previously selected ISO: $ISO_FILE"
+else
+ # List all ISO files newest first
+ mapfile -t ISO_LIST < <(ls -1t "$IMAGE_DIR"/*.iso 2>/dev/null)
+ if [[ ${#ISO_LIST[@]} -eq 0 ]]; then
+ echo "Error: No ISO files found in $IMAGE_DIR" >&2
+ exit 1
+ fi
+
+ echo "Available ISOs:"
+ PS3="Select an ISO to boot (default: 1): "
+ select ISO_FILE in "${ISO_LIST[@]}"; do
+ ISO_FILE=${ISO_FILE:-${ISO_LIST[0]}}
+ echo "Using ISO: $ISO_FILE"
+ # Save choice so we don’t ask again
+ echo "$ISO_FILE" > "$CHOICE_FILE"
+ break
+ done
+fi
+
+QCOW2_FILE="$VM_DIR/$VM_NAME.qcow2"
+GUEST_PORT=22
+SHARED_DIR="$HOST_DIR/shared"
+VM_SIZE="60G" # 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
+SHARED_DIR="$HOST_DIR/shared"
+FIRMWARE_DIR="$HOST_DIR/firmware"
+
+SMP_CONFIG="cores=$CORES,threads=$THREADS_PER_CORE,sockets=$SOCKETS"
+
+# Set SPICE_NOGRAB environment variable
+export SPICE_NOGRAB=1
+
+# 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
+
+# Ensure necessary directories exist
+mkdir -p "$HOST_DIR"
+mkdir -p "$VM_DIR" "$IMAGE_DIR" "$SHARED_DIR" "$FIRMWARE_DIR"
+
+# Locate OVMF firmware files
+OVMF_DIRS=(
+ "/usr/share/OVMF"
+ "/usr/share/qemu"
+ "/usr/lib/qemu"
+ "/usr/share/edk2"
+ "/usr/lib/edk2"
+)
+
+OVMF_CODE=""
+OVMF_VARS=""
+
+for dir in "${OVMF_DIRS[@]}"; do
+ [[ -z "$OVMF_CODE" ]] && OVMF_CODE=$(find "$dir" -type f -name "OVMF_CODE.fd" -o -name "edk2-x86_64-code.fd" 2>/dev/null | head -n 1)
+ [[ -z "$OVMF_VARS" ]] && OVMF_VARS=$(find "$dir" -type f -name "OVMF_VARS.fd" 2>/dev/null | head -n 1)
+ [[ -n "$OVMF_CODE" && -n "$OVMF_VARS" ]] && break
+done
+
+# Ensure a writable copy of OVMF_VARS.fd
+OVMF_VARS="$IMAGE_DIR/OVMF_VARS.fd"
+
+if [[ ! -f "$OVMF_VARS" ]]; then
+ echo "Copying OVMF_VARS.fd to $OVMF_VARS"
+ cp /usr/share/edk2/OvmfX64/OVMF_VARS.fd "$OVMF_VARS" 2>/dev/null || {
+ echo "Error: Failed to copy OVMF_VARS.fd!" >&2
+ exit 1
+ }
+fi
+
+# Check if required files exist
+if [[ -z "$OVMF_CODE" ]]; then
+ echo "Error: OVMF_CODE.fd not found!" >&2
+ exit 1
+fi
+if [[ ! -f "$OVMF_VARS" ]]; then
+ echo "Error: OVMF_VARS.fd not found or could not be copied!" >&2
+ exit 1
+fi
+if [[ ! -f "$ISO_FILE" ]]; then
+ echo "Warning: $ISO_FILE ISO not found at $IMAGE_DIR"
+fi
+
+# Check if the qcow2 image file exists; if not, create it
+if [[ ! -f "$QCOW2_FILE" ]]; then
+ echo "Creating $QCOW2_FILE with a size of $VM_SIZE"
+ qemu-img create -f qcow2 "$QCOW2_FILE" "$VM_SIZE" || {
+ echo "Error: Failed to create qcow2 image!" >&2
+ exit 1
+ }
+else
+ echo ""
+fi
+
+# Run QEMU
+/sbin/qemu-system-x86_64 \
+ -name "$VM_NAME",process="$VM_NAME" \
+ -machine q35,smm=off,vmport=off,accel=kvm \
+ -enable-kvm \
+ -global kvm-pit.lost_tick_policy=discard \
+ -cpu host \
+ -smp "$SMP_CONFIG" \
+ -m "$VM_RAM" \
+ -device virtio-balloon \
+ -pidfile "$VM_DIR/$VM_NAME.pid" \
+ -rtc base=utc,clock=host \
+ -vga none \
+ -device virtio-vga-gl,xres=1280,yres=800 \
+ -display sdl,gl=on \
+ -device virtio-rng-pci,rng=rng0 \
+ -device virtio-serial \
+ -object rng-random,id=rng0,filename=/dev/urandom \
+ -device qemu-xhci,id=spicepass \
+ -chardev spicevmc,id=usbredirchardev1,name=usbredir \
+ -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1 \
+ -chardev spicevmc,id=usbredirchardev2,name=usbredir \
+ -device usb-redir,chardev=usbredirchardev2,id=usbredirdev2 \
+ -chardev spicevmc,id=usbredirchardev3,name=usbredir \
+ -device usb-redir,chardev=usbredirchardev3,id=usbredirdev3 \
+ -device pci-ohci,id=smartpass \
+ -device usb-ccid \
+ -device usb-ehci,id=input \
+ -device usb-kbd,bus=input.0 \
+ -k en-us \
+ -device usb-tablet,bus=input.0 \
+ -audiodev pipewire,id=audio0 \
+ -device intel-hda \
+ -device hda-micro,audiodev=audio0 \
+ -device virtio-net,netdev=nic \
+ -netdev user,hostname="$VM_NAME",hostfwd=tcp::"$HOST_PORT"-:"$GUEST_PORT",id=nic \
+ -device virtio-9p-pci,fsdev=fsdev0,mount_tag="Public-$(whoami)" \
+ -global driver=cfi.pflash01,property=secure,value=on \
+ -drive if=pflash,format=raw,unit=0,file="$OVMF_CODE",readonly=on \
+ -drive if=pflash,format=raw,unit=1,file="$OVMF_VARS" \
+ -drive media=cdrom,index=0,file="$ISO_FILE" \
+ -device virtio-blk-pci,drive=SystemDisk \
+ -drive id=SystemDisk,if=none,format=qcow2,file="$QCOW2_FILE" \
+ -fsdev local,id=fsdev0,path="$SHARED_DIR",security_model=mapped-xattr \
+ -monitor unix:"$SOCKET_DIR/$VM_NAME-monitor.socket",server,nowait \
+ -serial unix:"$SOCKET_DIR/$VM_NAME-serial.socket",server,nowait
+
+#-display sdl,gl=on \
+#-display gtk,gl=on \
+#-display gtk,grab-on-hover=on,grab-mod=none \
+#-qmp unix:/tmp/qmp-sock,server,nowait \
+#-qmp unix:"$SOCKET_DIR/$VM_NAME-qmp.socket",server,nowait \
+#-chardev socket,path="$VM_DIR/$VM_NAME-qga.sock",server=on,wait=off,id=qga0 \
+
+# Network Isolation:
+#-netdev user,hostname="$VM_NAME",restrict=yes,id=nic \
+
+# No file-sharing:
+#-netdev user,hostname="$VM_NAME",hostfwd=tcp::"$HOST_PORT"-:"$GUEST_PORT",id=nic \
+
+# File-sharing and networking:
+#-netdev user,hostname="$VM_NAME",hostfwd=tcp::"$HOST_PORT"-:"$GUEST_PORT",smb="$SHARED_DIR",id=nic \
+#-device virtio-9p-pci,fsdev=fsdev0,mount_tag="Public-$(whoami)" \
diff --git a/unix/virt/windows.sh b/unix/virt/windows.sh
new file mode 100755
index 0000000..1863f8a
--- /dev/null
+++ b/unix/virt/windows.sh
@@ -0,0 +1,1156 @@
+#!/usr/bin/env bash
+
+# Windows VM Creation Script
+# Description: Creates and manages Windows virtual machines using QEMU/KVM
+# Features:
+# - Supports Windows 10, 11, and Server
+# - Automatic dependency checking
+# - Multiple VM instance support
+# - Cache management
+# - Unattended installation
+# - SMB sharing support
+# - Network control
+
+# Usage: ./windows.sh [VERSION] [OPTIONS]
+# Versions:
+# 10 Windows 10
+# 11 Windows 11 (default)
+# server Windows Server 2022
+# server2019 Windows Server 2019
+# server2016 Windows Server 2016
+
+# Options:
+# --help Show this help message
+# --clean Clean dependency cache
+# --check-deps Only check dependencies and exit
+# --download-iso Download Windows ISO and exit
+# --name NAME Set custom VM name
+# --ram SIZE Set RAM size (default: 8G)
+# --cpu NUM Set number of CPUs (default: 6)
+# --disk SIZE Set disk size (default: 80G)
+# --enable-smb Enable SMB sharing (default: disabled)
+# --disable-net Disable network connectivity (default: enabled)
+
+# Parse command line arguments
+WINDOWS_VERSION="11"
+CLEAN_CACHE=false
+CHECK_DEPS_ONLY=false
+DOWNLOAD_ISO_ONLY=false
+VM_NAME="windows-11"
+VM_RAM="8G"
+VM_CPU="6"
+VM_SIZE="80G"
+ENABLE_SMB=false
+DISABLE_NET=false
+
+# Handle version argument if it's the first argument
+if [[ $# -gt 0 && ! $1 =~ ^-- ]]; then
+ case "$1" in
+ "10")
+ WINDOWS_VERSION="10"
+ VM_NAME="windows-10"
+ ;;
+ "server" | "server2022")
+ WINDOWS_VERSION="server2022"
+ VM_NAME="windows-server-2022"
+ ;;
+ "server2019")
+ WINDOWS_VERSION="server2019"
+ VM_NAME="windows-server-2019"
+ ;;
+ "server2016")
+ WINDOWS_VERSION="server2016"
+ VM_NAME="windows-server-2016"
+ ;;
+ *)
+ echo "Unknown Windows version: $1"
+ echo "Use --help for usage information"
+ exit 1
+ ;;
+ esac
+ shift
+fi
+
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --help)
+ echo "Windows VM Creation Script"
+ echo "Usage: $0 [VERSION] [OPTIONS]"
+ echo
+ echo "Versions:"
+ echo " 10 Windows 10"
+ echo " 11 Windows 11 (default)"
+ echo " server Windows Server 2022"
+ echo " server2019 Windows Server 2019"
+ echo " server2016 Windows Server 2016"
+ echo
+ echo "Options:"
+ echo " --help Show this help message"
+ echo " --clean Clean dependency cache"
+ echo " --check-deps Only check dependencies and exit"
+ echo " --download-iso Download Windows ISO and exit"
+ echo " --name NAME Set custom VM name"
+ echo " --ram SIZE Set RAM size (default: 8G)"
+ echo " --cpu NUM Set number of CPUs (default: 6)"
+ echo " --disk SIZE Set disk size (default: 80G)"
+ echo " --enable-smb Enable SMB sharing (default: disabled)"
+ echo " --disable-net Disable network connectivity (default: enabled)"
+ exit 0
+ ;;
+ --clean)
+ CLEAN_CACHE=true
+ shift
+ ;;
+ --check-deps)
+ CHECK_DEPS_ONLY=true
+ shift
+ ;;
+ --download-iso)
+ DOWNLOAD_ISO_ONLY=true
+ shift
+ ;;
+ --name)
+ VM_NAME="$2"
+ shift 2
+ ;;
+ --ram)
+ VM_RAM="$2"
+ shift 2
+ ;;
+ --cpu)
+ VM_CPU="$2"
+ shift 2
+ ;;
+ --disk)
+ VM_SIZE="$2"
+ shift 2
+ ;;
+ --enable-smb)
+ ENABLE_SMB=true
+ shift
+ ;;
+ --disable-net)
+ DISABLE_NET=true
+ shift
+ ;;
+ *)
+ echo "Unknown option: $1"
+ echo "Use --help for usage information"
+ exit 1
+ ;;
+ esac
+done
+
+# Set variables
+HOST_DIR="$HOME/virt-new-new"
+VM_DIR="$HOST_DIR/machines"
+IMAGE_DIR="$HOST_DIR/images"
+WIN_ISO_DIR="${IMAGE_DIR}/${VM_NAME}" # Directory for Windows ISO
+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"
+
+# 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
+CORES=$((VM_CPU / 2))
+THREADS_PER_CORE=2
+SOCKETS=1
+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"
+SPICE_WEBDAVD_URL="https://www.spice-space.org/download/windows/spice-webdavd/spice-webdavd-x64-latest.msi"
+SPICE_VDAGENT_URL="https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-latest.msi"
+SPICE_VDAGENT_FALLBACK_URL="https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-0.10.0.msi"
+USBDK_URL="https://www.spice-space.org/download/windows/usbdk/UsbDk_1.0.22_x64.msi"
+
+# Fido download URL (Windows ISO downloader)
+#FIDO_URL="https://github.com/pbatard/Fido/raw/master/Fido.ps1"
+#FIDO_PATH="$WIN_DIR/Fido.ps1"
+
+# 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
+}
+
+# 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 '<option value="[0-9]+">Windows' | cut -d '"' -f 2 | head -n 1 | tr -cd '0-9' | head -c 16)"
+
+ if [[ -z "$product_edition_id" ]]; then
+ print_error "Failed to extract product edition ID."
+ print_error "Please download Windows $windows_version ISO manually from $url"
+ return 1
+ fi
+
+ print_success "Product Edition ID: $product_edition_id"
+
+ # Step 3: Register session ID
+ print_info "Registering session ID: $session_id"
+ curl --disable --silent --output /dev/null --user-agent "$user_agent" \
+ --header "Accept:" --max-filesize 100K --fail --proto =https --tlsv1.2 \
+ --http1.1 -- "https://vlscppe.microsoft.com/tags?org_id=y6jn8c31&session_id=$session_id" || {
+ print_error "Failed to register session ID."
+ return 1
+ }
+
+ # Step 4: Get language SKU ID
+ print_info "Getting language SKU ID..."
+ local language_skuid_table_json
+ language_skuid_table_json="$(curl --disable -s --fail --max-filesize 100K --proto =https --tlsv1.2 --http1.1 \
+ "https://www.microsoft.com/software-download-connector/api/getskuinformationbyproductedition?profile=${profile}&ProductEditionId=${product_edition_id}&SKU=undefined&friendlyFileName=undefined&Locale=en-US&sessionID=${session_id}")" || {
+ handle_curl_error $?
+ print_error "Failed to get language SKU information."
+ return 1
+ }
+
+ # Extract SKU ID for selected language
+ local sku_id
+
+ # Try with jq if available (more reliable)
+ if command -v jq >/dev/null 2>&1; then
+ sku_id="$(echo "$language_skuid_table_json" | jq -r '.Skus[] | select(.LocalizedLanguage=="'"$language"'" or .Language=="'"$language"'").Id')"
+ else
+ # Fallback to grep/cut if jq not available
+ sku_id="$(echo "$language_skuid_table_json" | grep -o '"Id":"[^"]*","Language":"'"$language"'"' | cut -d'"' -f4)"
+
+ if [[ -z "$sku_id" ]]; then
+ # Try alternative extraction method
+ sku_id="$(echo "$language_skuid_table_json" | grep -o '"LocalizedLanguage":"'"$language"'","Id":"[^"]*"' | cut -d'"' -f6)"
+ fi
+ fi
+
+ if [[ -z "$sku_id" ]]; then
+ print_error "Failed to extract SKU ID for $language."
+ return 1
+ fi
+
+ print_success "SKU ID: $sku_id"
+
+ # Step 5: Get ISO download link
+ print_info "Getting ISO download link..."
+ local iso_download_link_json
+ iso_download_link_json="$(curl --disable -s --fail --referer "$url" \
+ "https://www.microsoft.com/software-download-connector/api/GetProductDownloadLinksBySku?profile=${profile}&productEditionId=undefined&SKU=${sku_id}&friendlyFileName=undefined&Locale=en-US&sessionID=${session_id}")"
+
+ local failed=0
+
+ if [[ -z "$iso_download_link_json" ]]; then
+ print_error "Microsoft servers gave an empty response to the download request."
+ failed=1
+ fi
+
+ if echo "$iso_download_link_json" | grep -q "Sentinel marked this request as rejected."; then
+ print_error "Microsoft blocked the automated download request based on your IP address."
+ failed=1
+ fi
+
+ if [[ "$failed" -eq 1 ]]; then
+ print_warning "Please manually download the Windows $windows_version ISO using a web browser from: $url"
+ print_warning "Save the downloaded ISO to: $WIN_ISO_DIR"
+ return 1
+ fi
+
+ # Extract 64-bit ISO download URL
+ local iso_download_link
+
+ # Try with jq if available
+ if command -v jq >/dev/null 2>&1; then
+ iso_download_link="$(echo "$iso_download_link_json" | jq -r '.ProductDownloadOptions[].Uri' | grep x64 | head -n 1)"
+ else
+ # Fallback to grep/cut if jq not available
+ iso_download_link="$(echo "$iso_download_link_json" | grep -o '"Uri":"[^"]*x64[^"]*"' | cut -d'"' -f4 | head -n 1)"
+ fi
+
+ if [[ -z "$iso_download_link" ]]; then
+ print_error "Failed to extract the download link from Microsoft's response."
+ print_warning "Manually download the Windows $windows_version ISO using a web browser from: $url"
+ return 1
+ fi
+
+ print_success "Got download link: ${iso_download_link%%\?*}"
+
+ # Extract filename from URL
+ local file_name="$(echo "$iso_download_link" | cut -d'?' -f1 | rev | cut -d'/' -f1 | rev)"
+
+ # If filename couldn't be extracted, use default
+ if [[ -z "$file_name" || "$file_name" == "$iso_download_link" ]]; then
+ file_name="windows-$windows_version.iso"
+ fi
+
+ # Step 6: Download the ISO
+ print_info "Downloading Windows $windows_version ISO to $WIN_ISO_DIR/$file_name. This may take a while..."
+
+ # Check which download function to use
+ if type web_get >/dev/null 2>&1; then
+ web_get "$iso_download_link" "$WIN_ISO_DIR" "$file_name"
+ else
+ # Fallback to direct curl download
+ curl --disable --progress-bar --fail --location --proto '=https' --tlsv1.2 --http1.1 \
+ --retry 3 --retry-delay 3 --connect-timeout 30 \
+ --output "$WIN_ISO_DIR/$file_name" "$iso_download_link" || {
+ handle_curl_error $?
+ return 1
+ }
+ fi
+
+ if [[ $? -ne 0 ]]; then
+ print_error "Failed to download the Windows $windows_version ISO."
+ return 1
+ fi
+
+ print_success "Successfully downloaded Windows $windows_version ISO to $WIN_ISO_DIR/$file_name"
+
+ # Return the downloaded filename so calling code can use it
+ echo "$file_name"
+ return 0
+}
+
+# Create unattended installation ISO
+create_unattended_iso() {
+ print_info "Creating unattended installation ISO..."
+
+ # Create basic autounattend.xml if it doesn't exist
+ if [ ! -f "$WIN_ISO_DIR/unattended/autounattend.xml" ]; then
+ print_info "Creating autounattend.xml..."
+ cat >"$WIN_ISO_DIR/unattended/autounattend.xml" <<'EOF'
+<?xml version="1.0" encoding="utf-8"?>
+<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
+ <settings pass="windowsPE">
+ <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+ <SetupUILanguage>
+ <UILanguage>en-US</UILanguage>
+ </SetupUILanguage>
+ <InputLocale>0409:00000409</InputLocale>
+ <SystemLocale>en-US</SystemLocale>
+ <UILanguage>en-US</UILanguage>
+ <UserLocale>en-US</UserLocale>
+ </component>
+ <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+ <ImageInstall>
+ <OSImage>
+ <Compact>false</Compact>
+ <InstallTo>
+ <DiskID>0</DiskID>
+ <PartitionID>3</PartitionID>
+ </InstallTo>
+ </OSImage>
+ </ImageInstall>
+ <UserData>
+ <AcceptEula>true</AcceptEula>
+ </UserData>
+ <UseConfigurationSet>false</UseConfigurationSet>
+ <RunSynchronous>
+ <RunSynchronousCommand wcm:action="add">
+ <Order>1</Order>
+ <Path>cmd.exe /c "&gt;&gt;"X:\diskpart.txt" (echo SELECT DISK=0&amp;echo CLEAN&amp;echo CONVERT GPT&amp;echo CREATE PARTITION EFI SIZE=300&amp;echo FORMAT QUICK FS=FAT32 LABEL="System"&amp;echo CREATE PARTITION MSR SIZE=16)"</Path>
+ </RunSynchronousCommand>
+ <RunSynchronousCommand wcm:action="add">
+ <Order>2</Order>
+ <Path>cmd.exe /c "&gt;&gt;"X:\diskpart.txt" (echo CREATE PARTITION PRIMARY&amp;echo SHRINK MINIMUM=1000&amp;echo FORMAT QUICK FS=NTFS LABEL="Windows"&amp;echo CREATE PARTITION PRIMARY&amp;echo FORMAT QUICK FS=NTFS LABEL="Recovery")"</Path>
+ </RunSynchronousCommand>
+ <RunSynchronousCommand wcm:action="add">
+ <Order>3</Order>
+ <Path>cmd.exe /c "&gt;&gt;"X:\diskpart.txt" (echo SET ID="de94bba4-06d1-4d40-a16a-bfd50179d6ac"&amp;echo GPT ATTRIBUTES=0x8000000000000001)"</Path>
+ </RunSynchronousCommand>
+ <RunSynchronousCommand wcm:action="add">
+ <Order>4</Order>
+ <Path>cmd.exe /c "diskpart.exe /s "X:\diskpart.txt" &gt;&gt;"X:\diskpart.log" || ( type "X:\diskpart.log" &amp; echo diskpart encountered an error. &amp; pause &amp; exit /b 1 )"</Path>
+ </RunSynchronousCommand>
+ </RunSynchronous>
+ </component>
+ </settings>
+ <settings pass="specialize">
+ <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+ <ComputerName>Win11-VM</ComputerName>
+ <TimeZone>UTC</TimeZone>
+ </component>
+ </settings>
+ <settings pass="oobeSystem">
+ <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+ <InputLocale>0409:00000409</InputLocale>
+ <SystemLocale>en-US</SystemLocale>
+ <UILanguage>en-US</UILanguage>
+ <UserLocale>en-US</UserLocale>
+ </component>
+ <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+ <UserAccounts>
+ <LocalAccounts>
+ <LocalAccount wcm:action="add">
+ <Name>Admin</Name>
+ <DisplayName></DisplayName>
+ <Group>Administrators</Group>
+ <Password>
+ <Value>password</Value>
+ <PlainText>true</PlainText>
+ </Password>
+ </LocalAccount>
+ <LocalAccount wcm:action="add">
+ <Name>User</Name>
+ <DisplayName></DisplayName>
+ <Group>Users</Group>
+ <Password>
+ <Value>password</Value>
+ <PlainText>true</PlainText>
+ </Password>
+ </LocalAccount>
+ </LocalAccounts>
+ </UserAccounts>
+ <AutoLogon>
+ <Username>Admin</Username>
+ <Enabled>true</Enabled>
+ <LogonCount>1</LogonCount>
+ <Password>
+ <Value>password</Value>
+ <PlainText>true</PlainText>
+ </Password>
+ </AutoLogon>
+ <OOBE>
+ <ProtectYourPC>3</ProtectYourPC>
+ <HideEULAPage>true</HideEULAPage>
+ <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
+ <HideOnlineAccountScreens>true</HideOnlineAccountScreens>
+ <HideLocalAccountScreen>true</HideLocalAccountScreen>
+ <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
+ <SkipMachineOOBE>true</SkipMachineOOBE>
+ <SkipUserOOBE>true</SkipUserOOBE>
+ </OOBE>
+ </component>
+ </settings>
+</unattend>
+EOF
+ fi
+
+ # Create the unattended ISO
+ local iso_tool=""
+
+ if command -v genisoimage >/dev/null 2>&1; then
+ iso_tool="genisoimage"
+ elif command -v mkisofs >/dev/null 2>&1; then
+ iso_tool="mkisofs"
+ elif command -v xorriso >/dev/null 2>&1; then
+ iso_tool="xorriso"
+ fi
+
+ if [ "$iso_tool" != "" ]; then
+ print_info "Creating unattended ISO using $iso_tool..."
+
+ if [ "$iso_tool" = "xorriso" ]; then
+ xorriso -as genisoimage -J -r -o "$WIN_ISO_DIR/unattended.iso" "$WIN_ISO_DIR/unattended"
+ else
+ "$iso_tool" -J -r -o "$WIN_ISO_DIR/unattended.iso" "$WIN_ISO_DIR/unattended"
+ fi
+
+ return $?
+ else
+ print_warning "No ISO creation tool found (genisoimage, mkisofs, or xorriso)"
+ print_warning "Creating empty ISO file as fallback"
+ touch "$WIN_ISO_DIR/unattended.iso"
+ return 1
+ fi
+}
+
+# Download or locate essential files
+prepare_files() {
+ # Find Windows ISO
+ WINDOWS_ISO_PATH=$(find_windows_iso | tail -n 1 | xargs)
+ local iso_found=false
+
+ # Check if we found a valid ISO
+ if [[ -n "$WINDOWS_ISO_PATH" && -f "$WINDOWS_ISO_PATH" ]]; then
+ print_success "Using Windows ISO: $WINDOWS_ISO_PATH"
+ iso_found=true
+ else
+ print_warning "Windows ISO not found, attempting to download..."
+ if download_windows_iso; then
+ # Check again after download attempt
+ WINDOWS_ISO_PATH=$(find_windows_iso | tail -n 1 | xargs)
+ if [[ -n "$WINDOWS_ISO_PATH" && -f "$WINDOWS_ISO_PATH" ]]; then
+ print_success "Using downloaded Windows ISO: $WINDOWS_ISO_PATH"
+ iso_found=true
+ fi
+ fi
+ fi
+
+ # If we still don't have an ISO, exit
+ if [[ "$iso_found" != "true" ]]; then
+ print_error "Could not find or download Windows ISO"
+ print_info "Please place your Windows ISO file in: $WIN_ISO_DIR"
+ exit 1
+ fi
+
+ # Download VirtIO drivers if missing
+ if [[ ! -f "$ISO_VIRTIO" ]]; then
+ print_info "VirtIO drivers ISO not found, downloading..."
+ download_file "$VIRTIO_ISO_URL" "$ISO_VIRTIO"
+ else
+ print_success "VirtIO drivers ISO already exists: $ISO_VIRTIO"
+ fi
+
+ # Define the directory containing the MSI files
+ UNATTENDED_DIR="$WIN_ISO_DIR/unattended"
+ mkdir -p "$UNATTENDED_DIR"
+
+ # Find the latest Spice WebDAVD MSI
+ SPICE_WEBDAVD_MSI=$(find "$UNATTENDED_DIR" -type f -iname "spice-webdavd-x64*.msi" -exec stat --format="%Y %n" {} \; | sort -n | tail -n 1 | cut -d' ' -f2-)
+
+ # Find the latest Spice VD Agent MSI
+ SPICE_VDAGENT_MSI=$(find "$UNATTENDED_DIR" -type f -iname "spice-vdagent-x64*.msi" -exec stat --format="%Y %n" {} \; | sort -n | tail -n 1 | cut -d' ' -f2-)
+
+ # Find the latest UsbDk MSI
+ USBDK_MSI=$(find "$UNATTENDED_DIR" -type f -iname "UsbDk_*_x64.msi" -exec stat --format="%Y %n" {} \; | sort -n | tail -n 1 | cut -d' ' -f2-)
+
+ # Spice Guest Tools + UsbDk (only if missing)
+ if [[ ! -f "$SPICE_WEBDAVD_MSI" ]]; then
+ print_info "Downloading Spice WebDAVD..."
+ curl -L -o "$UNATTENDED_DIR/spice-webdavd-x64-latest.msi" "$SPICE_WEBDAVD_URL" || {
+ print_error "Failed to download Spice WebDAVD"
+ return 1
+ }
+ else
+ print_success "Spice WebDAVD MSI already exists: $SPICE_WEBDAVD_MSI"
+ fi
+
+ if [[ ! -f "$SPICE_VDAGENT_MSI" ]]; then
+ print_info "Downloading Spice VD Agent..."
+ # Try multiple mirrors for spice-vdagent
+ local spice_mirrors=(
+ "https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-latest.msi"
+ "https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-0.10.0.msi"
+ "https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-0.9.0.msi"
+ "https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-0.8.0.msi"
+ "https://www.spice-space.org/download/windows/spice-vdagent/spice-vdagent-x64-0.7.0.msi"
+ )
+
+ local download_success=false
+ for mirror in "${spice_mirrors[@]}"; do
+ print_info "Trying mirror: $mirror"
+ if curl -L -o "$UNATTENDED_DIR/spice-vdagent-x64-latest.msi" "$mirror"; then
+ # Verify the downloaded file
+ if [[ -s "$UNATTENDED_DIR/spice-vdagent-x64-latest.msi" ]]; then
+ download_success=true
+ break
+ else
+ print_warning "Downloaded file is empty, trying next mirror..."
+ rm -f "$UNATTENDED_DIR/spice-vdagent-x64-latest.msi"
+ fi
+ fi
+ done
+
+ if [[ "$download_success" == "false" ]]; then
+ print_error "Failed to download Spice VD Agent from all mirrors"
+ return 1
+ fi
+ else
+ print_success "Spice VD Agent MSI already exists: $SPICE_VDAGENT_MSI"
+ fi
+
+ if [[ ! -f "$USBDK_MSI" ]]; then
+ print_info "Downloading UsbDk..."
+ curl -L -o "$UNATTENDED_DIR/UsbDk_1.0.22_x64.msi" "$USBDK_URL" || {
+ print_error "Failed to download UsbDk"
+ return 1
+ }
+ else
+ print_success "UsbDk MSI already exists: $USBDK_MSI"
+ fi
+
+ # Create unattended ISO if it doesn't exist
+ if [[ ! -f "$ISO_UNATTENDED" ]]; then
+ create_unattended_iso
+ else
+ print_success "Unattended ISO already exists: $ISO_UNATTENDED"
+ fi
+}
+
+# Locate OVMF firmware files
+locate_ovmf() {
+ OVMF_DIRS=(
+ "/usr/share/OVMF"
+ "/usr/share/qemu"
+ "/usr/lib/qemu"
+ "/usr/share/edk2"
+ "/usr/lib/edk2"
+ "/usr/share/edk2/ovmf"
+ "/usr/share/edk2-ovmf"
+ )
+
+ OVMF_CODE=""
+ OVMF_VARS=""
+
+ for dir in "${OVMF_DIRS[@]}"; do
+ [[ -z "$OVMF_CODE" ]] && OVMF_CODE=$(find "$dir" -type f -name "OVMF_CODE.fd" -o -name "edk2-x86_64-code.fd" 2>/dev/null | head -n 1)
+ [[ -z "$OVMF_VARS" ]] && OVMF_VARS=$(find "$dir" -type f -name "OVMF_VARS.fd" 2>/dev/null | head -n 1)
+ [[ -n "$OVMF_CODE" && -n "$OVMF_VARS" ]] && break
+ done
+
+ # Ensure a writable copy of OVMF_VARS.fd
+ local original_ovmf_vars="$OVMF_VARS"
+ OVMF_VARS="$FIRMWARE_DIR/OVMF_VARS.fd"
+
+ if [[ ! -f "$OVMF_VARS" && -f "$original_ovmf_vars" ]]; then
+ print_info "Copying OVMF_VARS.fd to $OVMF_VARS"
+ cp "$original_ovmf_vars" "$OVMF_VARS" 2>/dev/null || {
+ print_error "Failed to copy OVMF_VARS.fd!"
+ exit 1
+ }
+ fi
+
+ # Check if required files exist
+ if [[ -z "$OVMF_CODE" || ! -f "$OVMF_CODE" ]]; then
+ print_error "OVMF_CODE.fd not found!"
+ exit 1
+ fi
+ if [[ ! -f "$OVMF_VARS" ]]; then
+ print_error "OVMF_VARS.fd not found or could not be copied!"
+ exit 1
+ fi
+ #}
+}
+
+# Create VM disk image
+create_disk() {
+ # Check if the qcow2 image file exists; if not, create it
+ if [[ ! -f "$QCOW2_FILE" ]]; then
+ print_info "Creating $QCOW2_FILE with a size of $VM_SIZE"
+ qemu-img create -f qcow2 "$QCOW2_FILE" "$VM_SIZE" || {
+ print_error "Failed to create qcow2 image!"
+ exit 1
+ }
+ else
+ print_success "VM disk image already exists: $QCOW2_FILE"
+ fi
+}
+
+# Generate unique PID file for this instance
+generate_pid_file() {
+ local base_pid_file="$VM_DIR/$VM_NAME.pid"
+ local pid_file="$base_pid_file"
+ local counter=1
+
+ # If the base PID file exists, try to find an available number
+ while [[ -f "$pid_file" ]]; do
+ pid_file="${base_pid_file%.pid}-${counter}.pid"
+ ((counter++))
+ done
+
+ echo "$pid_file"
+}
+
+# Start the VM
+start_vm() {
+ # Verify that we have a Windows ISO
+ if [[ -z "$WINDOWS_ISO_PATH" || ! -f "$WINDOWS_ISO_PATH" ]]; then
+ print_error "Windows ISO file not found. Cannot start VM."
+ print_info "Please download Windows 11 ISO and save it to: $WIN_ISO_DIR"
+ exit 1
+ fi
+
+ # Start swtpm
+ print_info "Starting TPM emulator..."
+ /sbin/swtpm socket \
+ --ctrl type=unixio,path="$TPM_SOCKET" \
+ --terminate \
+ --tpmstate dir="$TPM_DIR" \
+ --tpm2 &
+
+ # Wait for swtpm socket
+ sleep 1
+
+ print_info "Starting Windows 11 VM..."
+ print_info "Using Windows ISO: $WINDOWS_ISO_PATH"
+
+ # Build network options
+ local network_opts=()
+ if [[ "$DISABLE_NET" == "true" ]]; then
+ network_opts=(
+ "-netdev" "none,id=nic"
+ "-device" "virtio-net,netdev=nic"
+ )
+ else
+ if [[ "$ENABLE_SMB" == "true" ]]; then
+ network_opts=(
+ "-netdev" "user,id=nic,hostname=$VM_NAME,hostfwd=tcp::$HOST_PORT-:$GUEST_PORT,smb=$SHARED_DIR"
+ "-device" "virtio-net,netdev=nic"
+ )
+ else
+ network_opts=(
+ "-netdev" "user,id=nic,hostname=$VM_NAME,hostfwd=tcp::$HOST_PORT-:$GUEST_PORT"
+ "-device" "virtio-net,netdev=nic"
+ )
+ fi
+ fi
+
+ # Run QEMU
+ /sbin/qemu-system-x86_64 \
+ -name "$VM_NAME",process="$VM_NAME" \
+ -machine q35,hpet=off,smm=on,vmport=off,accel=kvm \
+ -global kvm-pit.lost_tick_policy=discard \
+ -global ICH9-LPC.disable_s3=1 \
+ -cpu host,+hypervisor,+invtsc,l3-cache=on,migratable=no,hv_passthrough \
+ -smp "$SMP_CONFIG" \
+ -m "$VM_RAM" \
+ -device virtio-balloon \
+ -pidfile "$PID_FILE" \
+ -rtc base=localtime,clock=host,driftfix=slew \
+ -vga none \
+ -device virtio-vga-gl,xres=1280,yres=800 \
+ -display sdl,gl=on \
+ -boot menu=on,splash-time=0,order=d,reboot-timeout=5000 \
+ -device virtio-rng-pci,rng=rng0 \
+ -object rng-random,id=rng0,filename=/dev/urandom \
+ -device qemu-xhci,id=spicepass \
+ -chardev spicevmc,id=usbredirchardev1,name=usbredir \
+ -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1 \
+ -chardev spicevmc,id=usbredirchardev2,name=usbredir \
+ -device usb-redir,chardev=usbredirchardev2,id=usbredirdev2 \
+ -chardev spicevmc,id=usbredirchardev3,name=usbredir \
+ -device usb-redir,chardev=usbredirchardev3,id=usbredirdev3 \
+ -device pci-ohci,id=smartpass \
+ -device usb-ccid \
+ -chardev spicevmc,id=ccid,name=smartcard \
+ -device ccid-card-passthru,chardev=ccid \
+ -device usb-ehci,id=input \
+ -device usb-kbd,bus=input.0 \
+ -k en-us \
+ -device usb-tablet,bus=input.0 \
+ -audiodev pipewire,id=audio0 \
+ -device intel-hda \
+ -device hda-micro,audiodev=audio0 \
+ "${network_opts[@]}" \
+ -global driver=cfi.pflash01,property=secure,value=on \
+ -drive if=pflash,format=raw,unit=0,file="$OVMF_CODE",readonly=on \
+ -drive if=pflash,format=raw,unit=1,file="$OVMF_VARS" \
+ -drive media=cdrom,index=1,file="$ISO_UNATTENDED" \
+ -drive media=cdrom,index=0,file="$WINDOWS_ISO_PATH" \
+ -drive media=cdrom,index=2,file="$ISO_VIRTIO" \
+ -device virtio-blk-pci,drive=SystemDisk \
+ -drive id=SystemDisk,if=none,format=qcow2,file="$QCOW2_FILE" \
+ -chardev socket,id=chrtpm,path="$TPM_SOCKET" \
+ -tpmdev emulator,id=tpm0,chardev=chrtpm \
+ -device tpm-tis,tpmdev=tpm0 \
+ -monitor unix:"$SOCKET_DIR/$VM_NAME-monitor.socket",server,nowait \
+ -serial unix:"$SOCKET_DIR/$VM_NAME-serial.socket",server,nowait
+}
+
+# Check dependencies
+check_dependencies() {
+ # Cache file for dependency checks and VM configuration
+ local cache_name="windows-vm-${VM_NAME}-${VM_RAM}-${VM_CPU}-${VM_SIZE}"
+ local cache_file="${HOME}/.cache/${cache_name}.cache"
+ local cache_dir=$(dirname "$cache_file")
+ local cache_valid=false
+ local missing_deps=()
+ local pkg_manager=""
+ local pkg_install_cmd=""
+ local privilege_cmd=""
+
+ # Clean cache if requested
+ if [[ "$CLEAN_CACHE" == "true" ]]; then
+ print_info "Cleaning dependency cache..."
+ rm -f "$cache_file"
+ fi
+
+ # Create cache directory if it doesn't exist
+ mkdir -p "$cache_dir"
+
+ # Check if cache is valid (less than 24 hours old)
+ if [[ -f "$cache_file" ]]; then
+ local cache_age=$(($(date +%s) - $(stat -c %Y "$cache_file")))
+ if [[ $cache_age -lt 86400 ]]; then # 24 hours in seconds
+ # Read cached configuration
+ local cached_config
+ cached_config=$(head -n 1 "$cache_file" 2>/dev/null)
+ # Compare with current configuration
+ if [[ "$cached_config" == "${VM_NAME}-${VM_RAM}-${VM_CPU}-${VM_SIZE}" ]]; then
+ cache_valid=true
+ else
+ print_info "VM configuration changed, invalidating cache..."
+ rm -f "$cache_file"
+ fi
+ fi
+ fi
+
+ # Check KVM group membership
+ if ! groups | grep -q -E 'kvm|qemu'; then
+ print_warning "User is not a member of kvm or qemu group"
+ print_info "You may need to add your user to the kvm group:"
+ print_info "sudo usermod -aG kvm $USER"
+ fi
+
+ # Detect package manager and privilege command
+ if command -v emerge >/dev/null 2>&1; then
+ pkg_manager="emerge"
+ pkg_install_cmd="emerge --ask --noreplace"
+ privilege_cmd="sudo"
+ # Gentoo package mapping
+ declare -A pkg_map=(
+ ["qemu-system-x86_64"]="app-emulation/qemu"
+ ["qemu-img"]="app-emulation/qemu"
+ ["swtpm"]="app-crypt/swtpm"
+ ["genisoimage"]="app-cdr/cdrtools"
+ ["curl"]="net-misc/curl"
+ ["uuidgen"]="sys-apps/util-linux"
+ ["jq"]="app-misc/jq"
+ ["glxinfo"]="x11-apps/mesa-progs"
+ ["lspci"]="sys-apps/pciutils"
+ ["ps"]="sys-process/procps"
+ ["python3"]="dev-lang/python"
+ ["mkisofs"]="app-cdr/cdrtools"
+ ["lsusb"]="sys-apps/usbutils"
+ ["socat"]="net-misc/socat"
+ ["spicy"]="app-emulation/spice"
+ ["xrandr"]="x11-apps/xrandr"
+ ["zsync"]="net-misc/zsync"
+ ["unzip"]="app-arch/unzip"
+ )
+ elif command -v apt-get >/dev/null 2>&1; then
+ pkg_manager="apt-get"
+ pkg_install_cmd="apt-get install -y"
+ privilege_cmd="sudo"
+ # Debian/Ubuntu package mapping
+ declare -A pkg_map=(
+ ["qemu-system-x86_64"]="qemu-system-x86"
+ ["qemu-img"]="qemu-utils"
+ ["swtpm"]="swtpm-tools"
+ ["genisoimage"]="genisoimage"
+ ["curl"]="curl"
+ ["uuidgen"]="uuid-runtime"
+ ["jq"]="jq"
+ ["glxinfo"]="mesa-utils"
+ ["lspci"]="pciutils"
+ ["ps"]="procps"
+ ["python3"]="python3"
+ ["mkisofs"]="genisoimage"
+ ["lsusb"]="usbutils"
+ ["socat"]="socat"
+ ["spicy"]="spice-client-gtk"
+ ["xrandr"]="x11-xserver-utils"
+ ["zsync"]="zsync"
+ ["unzip"]="unzip"
+ )
+ elif command -v dnf >/dev/null 2>&1; then
+ pkg_manager="dnf"
+ pkg_install_cmd="dnf install -y"
+ privilege_cmd="sudo"
+ # Fedora package mapping
+ declare -A pkg_map=(
+ ["qemu-system-x86_64"]="qemu-system-x86"
+ ["qemu-img"]="qemu-img"
+ ["swtpm"]="swtpm"
+ ["genisoimage"]="genisoimage"
+ ["curl"]="curl"
+ ["uuidgen"]="util-linux"
+ ["jq"]="jq"
+ ["glxinfo"]="mesa-demos"
+ ["lspci"]="pciutils"
+ ["ps"]="procps-ng"
+ ["python3"]="python3"
+ ["mkisofs"]="genisoimage"
+ ["lsusb"]="usbutils"
+ ["socat"]="socat"
+ ["spicy"]="spice-gtk-tools"
+ ["xrandr"]="xorg-x11-server-utils"
+ ["zsync"]="zsync"
+ ["unzip"]="unzip"
+ )
+ elif command -v pacman >/dev/null 2>&1; then
+ pkg_manager="pacman"
+ pkg_install_cmd="pacman -S --noconfirm"
+ privilege_cmd="sudo"
+ # Arch package mapping
+ declare -A pkg_map=(
+ ["qemu-system-x86_64"]="qemu"
+ ["qemu-img"]="qemu"
+ ["swtpm"]="swtpm"
+ ["genisoimage"]="cdrtools"
+ ["curl"]="curl"
+ ["uuidgen"]="util-linux"
+ ["jq"]="jq"
+ ["glxinfo"]="mesa-utils"
+ ["lspci"]="pciutils"
+ ["ps"]="procps-ng"
+ ["python3"]="python"
+ ["mkisofs"]="cdrtools"
+ ["lsusb"]="usbutils"
+ ["socat"]="socat"
+ ["spicy"]="spice-gtk"
+ ["xrandr"]="xorg-xrandr"
+ ["zsync"]="zsync"
+ ["unzip"]="unzip"
+ )
+ fi
+
+ # List of required commands and their package names
+ local deps=(
+ "qemu-system-x86_64"
+ "qemu-img"
+ "swtpm"
+ "genisoimage"
+ "curl"
+ "uuidgen"
+ "jq"
+ "glxinfo"
+ "lspci"
+ "ps"
+ "python3"
+ "mkisofs"
+ "lsusb"
+ "socat"
+ "spicy"
+ "xrandr"
+ "zsync"
+ "unzip"
+ )
+
+ # If cache is valid, read from it
+ if [[ "$cache_valid" == "true" ]]; then
+ print_info "Using cached dependency information..."
+ # Skip the first line (configuration) and read dependencies
+ tail -n +2 "$cache_file" | while IFS= read -r dep; do
+ if ! command -v "$dep" >/dev/null 2>&1; then
+ missing_deps+=("$dep")
+ fi
+ done
+ else
+ # Check each dependency and cache the results
+ print_info "Checking dependencies..."
+
+ # Clear cache file and write current configuration
+ echo "${VM_NAME}-${VM_RAM}-${VM_CPU}-${VM_SIZE}" >"$cache_file"
+
+ for dep in "${deps[@]}"; do
+ # Special case for genisoimage/mkisofs
+ if [[ "$dep" == "genisoimage" ]]; then
+ if ! command -v genisoimage >/dev/null 2>&1 && ! command -v mkisofs >/dev/null 2>&1; then
+ missing_deps+=("$dep")
+ echo "$dep" >>"$cache_file"
+ fi
+ # Special case for xdg-user-dirs
+ elif [[ "$dep" == "xdg-user-dirs" ]]; then
+ if ! pkg-config --exists xdg-user-dirs; then
+ missing_deps+=("$dep")
+ echo "$dep" >>"$cache_file"
+ fi
+ # Normal case for other dependencies
+ elif ! command -v "$dep" >/dev/null 2>&1; then
+ missing_deps+=("$dep")
+ echo "$dep" >>"$cache_file"
+ fi
+ done
+ fi
+
+ if [[ ${#missing_deps[@]} -gt 0 ]]; then
+ print_warning "Missing dependencies:"
+ for dep in "${missing_deps[@]}"; do
+ if [[ -n "${pkg_map[$dep]}" ]]; then
+ print_warning "- $dep (package: ${pkg_map[$dep]})"
+ else
+ print_warning "- $dep"
+ fi
+ done
+
+ if [[ -n "$pkg_manager" ]]; then
+ print_info "Detected package manager: $pkg_manager"
+ read -p "Would you like to install the missing dependencies? (y/N) " -n 1 -r
+ echo
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
+ # Check for privilege command
+ if ! command -v "$privilege_cmd" >/dev/null 2>&1; then
+ print_error "Privilege command ($privilege_cmd) not found"
+ print_info "Please install the missing packages manually"
+ else
+ # Install missing packages
+ print_info "Installing missing dependencies..."
+ local pkgs_to_install=()
+ for dep in "${missing_deps[@]}"; do
+ if [[ -n "${pkg_map[$dep]}" ]]; then
+ pkgs_to_install+=("${pkg_map[$dep]}")
+ fi
+ done
+ "$privilege_cmd" "$pkg_install_cmd" "${pkgs_to_install[@]}"
+
+ # Clear cache after installation
+ rm -f "$cache_file"
+ fi
+ else
+ print_warning "Continuing without installing dependencies. Some features may not work correctly."
+ fi
+ else
+ print_warning "No supported package manager found"
+ print_info "Please install the missing packages manually"
+ fi
+ else
+ print_success "All required dependencies are installed"
+ fi
+}
+
+# Main execution
+check_dependencies
+
+if [[ "$CHECK_DEPS_ONLY" == "true" ]]; then
+ exit 0
+fi
+
+if [[ "$DOWNLOAD_ISO_ONLY" == "true" ]]; then
+ download_windows_iso "$WINDOWS_VERSION"
+ exit 0
+fi
+
+# Generate unique PID file for this instance
+PID_FILE=$(generate_pid_file)
+print_info "Using PID file: $PID_FILE"
+
+prepare_files
+locate_ovmf
+create_disk
+start_vm
diff --git a/update-title b/update-title
deleted file mode 100755
index e01e34f..0000000
--- a/update-title
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-
-while true; do
- bg_color="#000000" # Initial background color (white)
- #bg_color="#FFFFFF" # Initial background color (white)
- text_color="#FFFFFF" # Initial text color (black)
- #text_color="#000000" # Initial text color (black)
- font="%{T1}" # Initial font setting
-
- active_node=$(xdotool getactivewindow)
-
- if [[ "$1" -eq "$active_node" ]]; then
- bg_color="#000000" # Change the background color when the window is active (white)
- #bg_color="#FFFFFF" # Change the background color when the window is active (white)
- text_color="#FFFFFF" # Change the text color when the window is active (black)
- #text_color="#000000" # Change the text color when the window is active (black)
- font="%{T2}" # Change the font setting when the window is active
- fi
-
- title=$(xtitle "$1")
-
- echo -e "%{B$bg_color}%{F$text_color}$font%{c}%{A1:bspc node $1 -f:}%{A2:bspc node $1 -c:}%{A5:bspc node $1 -d last:}%{A4:bspc node $1 -s biggest && ~/.scripts/title-bar:} $title%{A}%{A}%{A}%{A}%{F#FFFFFF}%{r}%{A:bspc node $1 -c:}x %{A}"
- #echo -e "%{B$bg_color}%{F$text_color}$font%{c}%{A1:bspc node $1 -f:}%{A2:bspc node $1 -c:}%{A5:bspc node $1 -d last:}%{A4:bspc node $1 -s biggest && ~/.scripts/title-bar:}$title%{A}%{A}%{A}%{A}%{F#000000}%{r}%{A:bspc node $1 -c:}x %{A}"
-
- sleep 0.5
-done
diff --git a/win-nvim.bat b/win-nvim.bat
deleted file mode 100644
index c99374d..0000000
--- a/win-nvim.bat
+++ /dev/null
@@ -1,37 +0,0 @@
-@echo off
-
-REM Install NeoVim with winget, if not already present on the system
-where nvim >nul 2>nul
-if %errorlevel% neq 0 (
- winget install Neovim.Neovim -q
-)
-
-REM Clone my dotfiles repo
-set dotFilesRoot=%USERPROFILE%\dotfiles
-if not exist "%dotFilesRoot%\." (
- git clone git@github.com:srdusr/dotfiles.git "%dotFilesRoot%"
-)
-
-REM Link NeoVim configuration
-set localConfiguration=%LOCALAPPDATA%\nvim
-set dotfilesConfiguration=%dotFilesRoot%\.config\nvim
-
-if not exist "%localConfiguration%\." (
- mklink /D "%localConfiguration%" "%dotfilesConfiguration%"
-)
-
-REM Clone Packer.nvim, if not already present on the system
-set localPacker=%LOCALAPPDATA%\nvim-data\site\pack\packer\start\packer.nvim
-
-if not exist "%localPacker%\." (
- git clone https://github.com/wbthomason/packer.nvim "%localPacker%"
-)
-
-REM Run the script by using this command in the same existing directory: win-nvim.bat
-
-@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://aka.ms/install-winget'))"
-iex ((new-object net.webclient).DownloadString('https://aka.ms/install-winget'))
-curl -o winget-cli.appxbundle https://aka.ms/winget-cli-appxbundle
-
-powershell Add-AppxPackage -Path "winget-cli.appxbundle"
-
diff --git a/win-nvim.ps1 b/win-nvim.ps1
deleted file mode 100644
index ca67755..0000000
--- a/win-nvim.ps1
+++ /dev/null
@@ -1,39 +0,0 @@
-# Install NeoVim with winget, if not already present on the system
-if (!(Get-Command nvim -ErrorAction SilentlyContinue)) {
- winget install Neovim.Neovim
-}
-
-# Clone my dotfiles repo
-$dotFilesRoot = Join-Path $HOME "dotfiles"
-
-if (!(Test-Path $dotFilesRoot -PathType Container)) {
- git clone https://github.com/srdusr/dotfiles.git $dotFilesRoot
-}
-
-# Link NeoVim configuration
-$localConfiguration = Join-Path $env:LOCALAPPDATA "nvim"
-$dotfilesConfiguration = Join-Path $dotFilesRoot ".config" "nvim"
-
-if (!(Test-Path $localConfiguration -PathType Container)) {
- Start-Process -FilePath "cmd.exe" -ArgumentList "/c mklink /D $localConfiguration $dotfilesConfiguration" -Verb runas
-}
-
-# Clone Packer.nvim, if not already present on the system
-$localPacker = Join-Path $env:LOCALAPPDATA "nvim-data" "site" "pack" "packer" "start" "packer.nvim"
-
-if (!(Test-Path $localPacker -PathType Container)) {
- git clone https://github.com/wbthomason/packer.nvim $localPacker
-}
-
-# To allow script execution, run the following command in PowerShell as an administrator:
-# Set-ExecutionPolicy RemoteSigned
-# Then run the script by using this command in the same existing directory:
-# ./win-nvim.ps1
-#curl -o winget-cli.appxbundle https://aka.ms/winget-cli-appxbundle
-#powershell Add-AppxPackage -Path "winget-cli.appxbundle"
-#Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
-#use `-y` or consider: choco feature enable -n allowGlobalConfirmation
-#choco install git
-#- Refresh the environment
-#Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1
-#refreshenv