@@ -166,11 +166,13 @@ function kill_vm() {
166166 echo " - ${VMNAME} is not running."
167167 rm -f " ${VMDIR} /${VMNAME} .pid"
168168 rm -f " ${VMDIR} /${VMNAME} .spice"
169+ rm -f " ${VMDIR} /${VMNAME} .sock"
169170 elif [ -n " ${VM_PID} " ]; then
170171 if kill -9 " ${VM_PID} " > /dev/null 2>&1 ; then
171172 echo " - ${VMNAME} (${VM_PID} ) killed."
172173 rm -f " ${VMDIR} /${VMNAME} .pid"
173174 rm -f " ${VMDIR} /${VMNAME} .spice"
175+ rm -f " ${VMDIR} /${VMNAME} .sock"
174176 else
175177 echo " - ${VMNAME} (${VM_PID} ) was not killed."
176178 fi
@@ -1324,7 +1326,20 @@ function configure_display() {
13241326 gtk) DISPLAY_RENDER=" ${display} ,grab-on-hover=on,zoom-to-fit=off,gl=${gl} " ;;
13251327 none) DISPLAY_RENDER=" none"
13261328 gl=" off" ;; # No display backend means no GL context
1327- spice) DISPLAY_RENDER=" none" ;;
1329+ spice)
1330+ # For local SPICE with GL, use egl-headless to provide the GL context
1331+ # for VirGL. Don't use gl=on in SPICE itself as it blocks the main loop.
1332+ if [ -z " ${ACCESS} " ] || [ " ${ACCESS} " == " local" ]; then
1333+ if [ " ${gl} " == " on" ]; then
1334+ DISPLAY_RENDER=" egl-headless,rendernode=/dev/dri/renderD128"
1335+ else
1336+ DISPLAY_RENDER=" none"
1337+ fi
1338+ else
1339+ # Remote access cannot use GL
1340+ DISPLAY_RENDER=" none"
1341+ gl=" off"
1342+ fi ;;
13281343 sdl) DISPLAY_RENDER=" ${display} ,gl=${gl} " ;;
13291344 spice-app) DISPLAY_RENDER=" ${display} ,gl=${gl} " ;;
13301345 * ) DISPLAY_RENDER=" ${display} " ;;
@@ -1349,8 +1364,15 @@ function configure_display() {
13491364 echo -n " - Display: ${display^^} , ${DISPLAY_DEVICE} , GL (${gl} ), VirGL (off)"
13501365 fi
13511366
1367+ # Disable default VGA for SPICE modes to prevent duplicate scanouts
1368+ # SPICE creates its own display output; the default VGA would create a second one
1369+ case ${display} in
1370+ none|spice|spice-app) VGA=" -vga none" ;;
1371+ * ) VGA=" " ;;
1372+ esac
1373+
13521374 # Build the video configuration
1353- VIDEO=" -device ${DISPLAY_DEVICE} "
1375+ VIDEO=" ${VGA : + ${VGA} } -device ${DISPLAY_DEVICE} "
13541376
13551377 # ARM64 needs ramfb for UEFI boot display before virtio-gpu driver loads
13561378 if [ " ${ARCH_VM} " == " aarch64" ]; then
@@ -1401,6 +1423,7 @@ function configure_audio() {
14011423function configure_ports() {
14021424 echo -n " " > " ${VMDIR} /${VMNAME} .ports"
14031425 rm -f " ${VMDIR} /${VMNAME} .spice"
1426+ rm -f " ${VMDIR} /${VMNAME} .sock"
14041427
14051428 if [ -z " ${ssh_port} " ]; then
14061429 # Find a free port to expose ssh to the guest
@@ -1429,49 +1452,81 @@ function configure_ports() {
14291452
14301453 if [ " ${display} " == " none" ] || [ " ${display} " == " spice" ] || [ " ${display} " == " spice-app" ]; then
14311454 SPICE=" disable-ticketing=on"
1432- # gl=on can be use with 'spice' too, but only over local connections (not tcp ports)
1455+
14331456 if [ " ${display} " == " spice-app" ]; then
1457+ # spice-app uses QEMU's built-in viewer with GL support
14341458 SPICE+=" ,gl=${gl} "
1435- fi
1459+ echo " - SPICE: Enabled"
1460+ elif [ " ${display} " == " spice" ]; then
1461+ # For spice display, use Unix socket for local or TCP for remote
1462+ if [ -z " ${ACCESS} " ] || [ " ${ACCESS} " == " local" ]; then
1463+ # Unix socket mode for local access
1464+ # GL context is provided by egl-headless display, not SPICE
1465+ SPICE+=" ,unix=on,addr=${VMDIR} /${VMNAME} .sock"
1466+ echo " unix,${VMDIR} /${VMNAME} .sock" >> " ${VMDIR} /${VMNAME} .ports"
1467+ echo " ${VMDIR} /${VMNAME} .sock" > " ${VMDIR} /${VMNAME} .spice"
1468+ echo -n " - SPICE: On host: spicy --uri=\" spice+unix://${VMDIR} /${VMNAME} .sock\" --title \" ${VMNAME} \" "
1469+ if [ " ${guest_os} " != " macos" ] && [ -n " ${PUBLIC} " ]; then
1470+ echo -n " --spice-shared-dir ${PUBLIC} "
1471+ fi
1472+ echo " ${FULLSCREEN} "
1473+ else
1474+ # TCP mode for remote access (no GL support)
1475+ if [ -z " ${spice_port} " ]; then
1476+ spice_port=$( get_port 5930 9)
1477+ fi
14361478
1437- # TODO: Don't use ports so local-only connections can be used with gl=on
1438- if [ -z " ${spice_port} " ] ; then
1439- # Find a free port for spice
1440- spice_port= $( get_port 5930 9 )
1441- fi
1479+ if [ " ${ACCESS} " == " remote " ] ; then
1480+ SPICE_ADDR= " "
1481+ else
1482+ SPICE_ADDR= " ${ACCESS} "
1483+ fi
14421484
1443- # ALLOW REMOTE ACCESS TO SPICE OVER LAN RATHER THAN JUST LOCALHOST
1444- if [ -z " ${ACCESS} " ]; then
1445- SPICE_ADDR=" 127.0.0.1"
1446- else
1447- if [ " ${ACCESS} " == " remote" ]; then
1485+ if [ -z " ${spice_port} " ]; then
1486+ echo " - SPICE: All SPICE ports have been exhausted."
1487+ echo " ERROR! Requested SPICE display, but no SPICE ports are free."
1488+ exit 1
1489+ fi
1490+
1491+ SPICE+=" ,port=${spice_port} ,addr=${SPICE_ADDR} "
1492+ echo " spice,${spice_port} " >> " ${VMDIR} /${VMNAME} .ports"
1493+ echo " ${spice_port} " > " ${VMDIR} /${VMNAME} .spice"
1494+ echo -n " - SPICE: On host: spicy --title \" ${VMNAME} \" --port ${spice_port} "
1495+ if [ " ${guest_os} " != " macos" ] && [ -n " ${PUBLIC} " ]; then
1496+ echo -n " --spice-shared-dir ${PUBLIC} "
1497+ fi
1498+ echo " ${FULLSCREEN} "
1499+ fi
1500+ elif [ " ${display} " == " none" ]; then
1501+ # display=none with SPICE for headless VMs - use TCP for remote access
1502+ if [ -z " ${spice_port} " ]; then
1503+ spice_port=$( get_port 5930 9)
1504+ fi
1505+
1506+ if [ -z " ${ACCESS} " ]; then
1507+ SPICE_ADDR=" 127.0.0.1"
1508+ elif [ " ${ACCESS} " == " remote" ]; then
14481509 SPICE_ADDR=" "
14491510 elif [ " ${ACCESS} " == " local" ]; then
14501511 SPICE_ADDR=" 127.0.0.1"
14511512 else
14521513 SPICE_ADDR=" ${ACCESS} "
14531514 fi
1454- fi
14551515
1456- if [ -z " ${spice_port} " ]; then
1457- echo " - SPICE: All SPICE ports have been exhausted."
1458- if [ " ${display} " == " none" ] || [ " ${display} " == " spice" ] || [ " ${display} " == " spice-app" ]; then
1516+ if [ -z " ${spice_port} " ]; then
1517+ echo " - SPICE: All SPICE ports have been exhausted."
14591518 echo " ERROR! Requested SPICE display, but no SPICE ports are free."
14601519 exit 1
14611520 fi
1462- else
1463- if [ " ${display} " == " spice-app" ]; then
1464- echo " - SPICE: Enabled"
1465- else
1466- echo " spice,${spice_port} " >> " ${VMDIR} /${VMNAME} .ports"
1467- echo " ${spice_port} " > " ${VMDIR} /${VMNAME} .spice"
1468- echo -n " - SPICE: On host: spicy --title \" ${VMNAME} \" --port ${spice_port} "
1469- if [ " ${guest_os} " != " macos" ] && [ -n " ${PUBLIC} " ]; then
1470- echo -n " --spice-shared-dir ${PUBLIC} "
1471- fi
1472- echo " ${FULLSCREEN} "
1473- SPICE=" ${SPICE} ,port=${spice_port} ,addr=${SPICE_ADDR} "
1521+
1522+ SPICE+=" ,port=${spice_port} ,addr=${SPICE_ADDR} "
1523+ echo " spice,${spice_port} " >> " ${VMDIR} /${VMNAME} .ports"
1524+ echo " ${spice_port} " > " ${VMDIR} /${VMNAME} .spice"
1525+ echo -n " - SPICE: On host: spicy --title \" ${VMNAME} \" --port ${spice_port} "
1526+ if [ " ${guest_os} " != " macos" ] && [ -n " ${PUBLIC} " ]; then
1527+ echo -n " --spice-shared-dir ${PUBLIC} "
14741528 fi
1529+ echo " ${FULLSCREEN} "
14751530 fi
14761531 fi
14771532}
@@ -2108,45 +2163,93 @@ function vm_boot() {
21082163 echo " - Process: ERROR! Failed to start ${VM} as ${VMNAME} "
21092164 rm -f " ${VMDIR} /${VMNAME} .pid"
21102165 rm -f " ${VMDIR} /${VMNAME} .spice"
2166+ rm -f " ${VMDIR} /${VMNAME} .sock"
21112167 echo && cat " ${VMDIR} /${VMNAME} .log"
21122168 exit 1
21132169 fi
21142170 fi
21152171}
21162172
21172173function start_viewer {
2118- errno=0
2119- if [ " ${viewer} " != " none" ]; then
2120- # If output is 'none' then SPICE was requested.
2121- if [ " ${display} " == " spice" ]; then
2122- if [ " ${viewer} " == " remote-viewer" ]; then
2123- # show via viewer: remote-viewer
2124- if [ -n " ${PUBLIC} " ]; then
2125- echo " - Viewer: ${viewer} --title \" ${VMNAME} \" --spice-shared-dir \" ${PUBLIC} \" ${FULLSCREEN} \" spice://localhost:${spice_port} \" >/dev/null 2>&1 &"
2126- ${viewer} --title " ${VMNAME} " --spice-shared-dir " ${PUBLIC} " ${FULLSCREEN} " spice://localhost:${spice_port} " > /dev/null 2>&1 &
2127- errno=$?
2128- else
2129- echo " - Viewer: ${viewer} --title \" ${VMNAME} \" ${FULLSCREEN} \" spice://localhost:${spice_port} \" >/dev/null 2>&1 &"
2130- ${viewer} --title " ${VMNAME} " ${FULLSCREEN} " spice://localhost:${spice_port} " > /dev/null 2>&1 &
2131- errno=$?
2132- fi
2133- elif [ " ${viewer} " == " spicy" ]; then
2134- # show via viewer: spicy
2135- if [ -n " ${PUBLIC} " ]; then
2136- echo " - Viewer: ${viewer} --title \" ${VMNAME} \" --port \" ${spice_port} \" --spice-shared-dir \" ${PUBLIC} \" \" ${FULLSCREEN} \" >/dev/null 2>&1 &"
2137- ${viewer} --title " ${VMNAME} " --port " ${spice_port} " --spice-shared-dir " ${PUBLIC} " " ${FULLSCREEN} " > /dev/null 2>&1 &
2138- errno=$?
2139- else
2140- echo " - Viewer: ${viewer} --title \" ${VMNAME} \" --port \" ${spice_port} \" \" ${FULLSCREEN} \" >/dev/null 2>&1 &"
2141- ${viewer} --title " ${VMNAME} " --port " ${spice_port} " " ${FULLSCREEN} " > /dev/null 2>&1 &
2142- errno=$?
2143- fi
2174+ # Exit early if viewer is disabled or display is not SPICE
2175+ if [ " ${viewer} " == " none" ] || [ " ${display} " != " spice" ]; then
2176+ return
2177+ fi
2178+
2179+ # Build viewer arguments based on connection mode (Unix socket or TCP)
2180+ local viewer_args=()
2181+ local viewer_uri=" "
2182+ local errno=0
2183+
2184+ # Determine connection mode from .spice file content
2185+ # - Unix socket mode: file contains a path (with /)
2186+ # - TCP mode: file contains just a port number
2187+ local spice_info=" "
2188+ if [ -r " ${VMDIR} /${VMNAME} .spice" ]; then
2189+ spice_info=$( cat " ${VMDIR} /${VMNAME} .spice" )
2190+ fi
2191+
2192+ if [[ " ${spice_info} " == * /* ]]; then
2193+ # Unix socket mode (path contains /)
2194+ local SPICE_SOCKET=" ${spice_info} "
2195+ if [ " ${viewer} " == " spicy" ]; then
2196+ viewer_args+=(" --uri=spice+unix://${SPICE_SOCKET} " )
2197+ else
2198+ viewer_uri=" spice+unix://${SPICE_SOCKET} "
2199+ fi
2200+ elif [ -n " ${spice_info} " ]; then
2201+ # TCP mode (port number)
2202+ if [ " ${viewer} " == " spicy" ]; then
2203+ viewer_args+=(" --port" " ${spice_info} " )
2204+ else
2205+ viewer_uri=" spice://localhost:${spice_info} "
2206+ fi
2207+ else
2208+ # Fallback: no .spice file, use ACCESS variable to determine mode
2209+ if [ -z " ${ACCESS} " ] || [ " ${ACCESS} " == " local" ]; then
2210+ # Unix socket mode
2211+ local SPICE_SOCKET=" ${VMDIR} /${VMNAME} .sock"
2212+ if [ " ${viewer} " == " spicy" ]; then
2213+ viewer_args+=(" --uri=spice+unix://${SPICE_SOCKET} " )
2214+ else
2215+ viewer_uri=" spice+unix://${SPICE_SOCKET} "
21442216 fi
2145- if [ ${errno} -ne 0 ]; then
2146- echo " WARNING! Could not start viewer (${viewer} ) Err: ${errno} "
2217+ else
2218+ # TCP mode for remote access
2219+ if [ " ${viewer} " == " spicy" ]; then
2220+ viewer_args+=(" --port" " ${spice_port} " )
2221+ else
2222+ viewer_uri=" spice://localhost:${spice_port} "
21472223 fi
21482224 fi
21492225 fi
2226+
2227+ # Add common arguments
2228+ viewer_args+=(" --title" " ${VMNAME} " )
2229+
2230+ # Add shared directory if configured (not for macOS guests)
2231+ if [ " ${guest_os} " != " macos" ] && [ -n " ${PUBLIC} " ]; then
2232+ viewer_args+=(" --spice-shared-dir" " ${PUBLIC} " )
2233+ fi
2234+
2235+ # Add fullscreen if requested
2236+ if [ -n " ${FULLSCREEN} " ]; then
2237+ viewer_args+=(" ${FULLSCREEN} " )
2238+ fi
2239+
2240+ # Add URI for remote-viewer (spicy uses --uri= in the args already)
2241+ if [ " ${viewer} " == " remote-viewer" ] && [ -n " ${viewer_uri} " ]; then
2242+ viewer_args+=(" ${viewer_uri} " )
2243+ fi
2244+
2245+ # Launch the viewer
2246+ echo " - Viewer: ${viewer} ${viewer_args[*]} >/dev/null 2>&1 &"
2247+ " ${viewer} " " ${viewer_args[@]} " > /dev/null 2>&1 &
2248+ errno=$?
2249+
2250+ if [ ${errno} -ne 0 ]; then
2251+ echo " WARNING! Could not start viewer (${viewer} ) Err: ${errno} "
2252+ fi
21502253}
21512254
21522255function shortcut_create {
@@ -2726,6 +2829,7 @@ if [ -n "${VM}" ] && [ -e "${VM}" ]; then
27262829 VM_PID=" "
27272830 rm -f " ${VMDIR} /${VMNAME} .pid"
27282831 rm -f " ${VMDIR} /${VMNAME} .spice"
2832+ rm -f " ${VMDIR} /${VMNAME} .sock"
27292833 fi
27302834 fi
27312835
0 commit comments