From 318d80de38649fa6f26c1a4baedf0849ef11f33f Mon Sep 17 00:00:00 2001 From: clsty Date: Wed, 12 Nov 2025 21:07:21 +0800 Subject: [PATCH] Update subcmd virtmon --- sdata/lib/functions.sh | 20 +++++-- sdata/subcmd-virtmon/0.run.sh | 95 ++++++++++++++++----------------- sdata/subcmd-virtmon/options.sh | 36 ++++++++++--- 3 files changed, 91 insertions(+), 60 deletions(-) diff --git a/sdata/lib/functions.sh b/sdata/lib/functions.sh index 28e97ae3d..96ddb7eb4 100644 --- a/sdata/lib/functions.sh +++ b/sdata/lib/functions.sh @@ -350,7 +350,10 @@ function install_cmds(){ local pkgs=() for cmd in "$@";do # For package name which is not cmd name, use "case" syntax to replace - pkgs+=($cmd) + case $cmd in + ip) pkgs+=(iproute2);; + *) pkgs+=($cmd) ;; + esac done v sudo pacman -Syu v sudo pacman -S --noconfirm --needed "${pkgs[@]}" @@ -359,7 +362,10 @@ function install_cmds(){ local pkgs=() for cmd in "$@";do # For package name which is not cmd name, use "case" syntax to replace - pkgs+=($cmd) + case $cmd in + ip) pkgs+=(iproute2);; + *) pkgs+=($cmd) ;; + esac done v sudo apt update -y v sudo apt install -y "${pkgs[@]}" @@ -368,7 +374,10 @@ function install_cmds(){ local pkgs=() for cmd in "$@";do # For package name which is not cmd name, use "case" syntax to replace - pkgs+=($cmd) + case $cmd in + ip) pkgs+=(iproute);; + *) pkgs+=($cmd) ;; + esac done v sudo dnf install -y "${pkgs[@]}" ;; @@ -376,7 +385,10 @@ function install_cmds(){ local pkgs=() for cmd in "$@";do # For package name which is not cmd name, use "case" syntax to replace - pkgs+=($cmd) + case $cmd in + ip) pkgs+=(iproute2);; + *) pkgs+=($cmd) ;; + esac done v sudo zypper refresh v sudo zypper -n install "${pkgs[@]}" diff --git a/sdata/subcmd-virtmon/0.run.sh b/sdata/subcmd-virtmon/0.run.sh index 70cd3e0b4..446978e62 100644 --- a/sdata/subcmd-virtmon/0.run.sh +++ b/sdata/subcmd-virtmon/0.run.sh @@ -3,70 +3,67 @@ # shellcheck shell=bash -readarray -t vmons < <(hyprctl -j monitors all | jq -r '.[] | select(.name | test("^HEADLESS-")) | .name') +ensure_cmds wayvnc lsof jq ip -ensure_cmds wayvnc lsof jq +readarray -t vmon_ids < <(hyprctl -j monitors all | jq -r '.[] | select(.name | test("^TESTER-")) | .name | sub("^TESTER-"; "")') -if [[ "${CLEAN_VIRTUAL_MONITORS}" = true ]]; then - echo "Cleaning virtual monitors..." - for i in "${vmons[@]}"; do +if [[ "${CLEAN_TESTER_MONITORS}" = true ]]; then + echo "Cleaning tester monitors..." + for i in "TESTER-${vmon_ids[@]}"; do x hyprctl output remove "$i" done + echo "Cleaning tester wayvnc sessions..." + for tester_socket in /tmp/wayvncctl_tester_* ; do + x wayvncctl --socket=$tester_socket -r wayvnc-exit + done + echo "Cleaning complete, exit..." exit 0 fi -if [ "${#vmons[@]}" -gt 0 ]; then - echo "headless monitors found:" - printf '%s\n' "${vmons[@]}" - if [[ ! "${KEEP_VIRTUAL_MONITORS}" = true ]]; then - echo "Cleaning..." - for i in "${vmons[@]}"; do - x hyprctl output remove "$i" - done - fi -else - echo "No headless monitors found." -fi -echo "Creating tester monitor..." -readarray -t vmons_old < <(hyprctl -j monitors all | jq -r '.[] | select(.name | test("^HEADLESS-")) | .name') -x hyprctl output create headless -readarray -t vmons_new < <(hyprctl -j monitors all | jq -r '.[] | select(.name | test("^HEADLESS-")) | .name') -declare -A seen -for e in "${vmons_old[@]}"; do - seen["$e"]=1 -done -deltas=() -for e in "${vmons_new[@]}"; do - if [[ -z "${seen[$e]+_}" ]]; then - deltas+=("$e") - fi -done -if (( ${#deltas[@]} == 1 )); then - vmon_tester="${deltas[0]}" - echo "tester monitor found: $vmons_tester" -elif (( ${#deltas[@]} == 0 )); then - echo "Error: No tester monitor found" - exit 1 -else - echo "Error: multiple tester monitor found: ${deltas[*]}" - exit 1 -fi -# TODO: Implement setting geometry -#echo "Setting geometry..." -#${vmon_tester} -echo "Using wayvnc to share monitor $vmons_tester..." +echo "Finding an unused port..." for port in {5900..5999}; do if ! lsof -nP -iTCP:"$port" -sTCP:LISTEN -t >/dev/null 2>&1; then vnc_port="$port" break fi done -# TODO: Allow running in background and implement --stop to stop it if [ -z "$vnc_port" ];then echo "No available port for vnc server, aborting..."; exit 1 fi -wayvnc -S -o=${vmon_tester} --log-level=trace 0.0.0.0 $vnc_port +# The name of tester_socket can be anything, the following just borrows $vnc_port as ID +tester_socket=/tmp/wayvncctl_tester_$vnc_port +# In case this exists for some reason +try rm $tester_socket +vmon_tester=TESTER-$vnc_port;break -echo "Cleaning the tester monitor..." -hyprctl output remove "${vmon_tester}" +echo "Creating tester monitor..." +x hyprctl output create headless ${vmon_tester} + +echo "Setting geometry..." +x hyprctl keyword monitor ${vmon_tester},${VMON_RESOLUTION}@${VMON_FPS},${VMON_POSITION},${VMON_SCALE}${VMON_EXTRA} + +echo "=========================================" +echo "Use a VNC client to connect to the virtual monitor." +echo " Port: $vnc_port" +echo " IP: choose a suitable one from below:" +ip -o addr show up | grep -v -E 'docker|veth|virbr' | awk '{split($4,a,"/"); print $2"\t"a[1]}' +echo "The status of the virtual monitor:" +echo " Resolution: ${VMON_RESOLUTION}" +echo " Frame rate: ${VMON_FPS}" +echo "Hint:" +echo " The VNC client will ask you about server address," +echo " either joined as : or separately." +echo " As for username and password, just leave them as empty." +echo "=========================================" +echo "Launching wayvnc..." + +if [ "$RUNNING_IN_BACKGROUND" = true ];then + nohup wayvnc --socket=$tester_socket -f=${VMON_FPS} -o=${vmon_tester} --log-level=${WAYVNC_LOGLEVEL} 0.0.0.0 $vnc_port > $(mktemp) 2>&1 & + disown +else + echo "wayvnc now running, press Ctrl-C to quit." + wayvnc --socket=$tester_socket -f=${VMON_FPS} -o=${vmon_tester} --log-level=${WAYVNC_LOGLEVEL} 0.0.0.0 $vnc_port + echo "wayvnc stopped. Cleaning..." + hyprctl output remove "${vmon_tester}" +fi diff --git a/sdata/subcmd-virtmon/options.sh b/sdata/subcmd-virtmon/options.sh index 485abbb6c..6fd721617 100644 --- a/sdata/subcmd-virtmon/options.sh +++ b/sdata/subcmd-virtmon/options.sh @@ -1,21 +1,38 @@ # Handle args for subcmd: checkdeps # shellcheck shell=bash +VMON_RESOLUTION=1920x1080 +VMON_FPS=60 +VMON_POSITION=auto +VMON_SCALE=1 +VMON_EXTRA="" +WAYVNC_LOGLEVEL=${WAYVNC_LOGLEVEL:-quiet} + showhelp(){ echo -e "Syntax: $0 virtmon [OPTIONS] Create virtual monitor for testing multi-monitors. +Note: + The virtual monitor will be served via wayvnc. + You need a VNC client to connect to it. + Options: - -h, --help Show this help message - -c, --clean Clean all virtual monitors and exit - -k, --keep Do not remove virtual monitors + -h, --help Show this help message and exit + -c, --clean Clean all tester monitors and wayvnc sessions and exit + + -d, --daemon Running in background + --res Resolution, by default $VMON_RESOLUTION + --fps Refresh rate and FPS, by default $VMON_FPS + --pos Position, by default $VMON_POSITION + --sca Scale, by default $VMON_SCALE + --ext Extra args, e.g. \"transform, 1\" " } # `man getopt` to see more para=$(getopt \ - -o hck \ - -l help,clean,keep \ + -o hcd \ + -l help,clean,daemon,res:,fps:,pos:,sca:,ext: \ -n "$0" -- "$@") [ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1 ##################################################################################### @@ -31,8 +48,13 @@ done eval set -- "$para" while true ; do case "$1" in - -c|--clean) CLEAN_VIRTUAL_MONITORS=true;shift;; - -k|--keep) KEEP_VIRTUAL_MONITORS=true;shift;; + -c|--clean) CLEAN_TESTER_MONITORS=true;shift;; + -d|--daemon) RUNNING_IN_BACKGROUND=true;shift;; + --res) VMON_RESOLUTION="$2";shift 2;; + --fps) VMON_FPS="$2";shift 2;; + --pos) VMON_POSITION="$2";shift 2;; + --sca) VMON_SCALE="$2";shift 2;; + --ext) VMON_EXTRA=", $2";shift 2;; --) shift;break ;; *) sleep 0 ;; esac