Skip to content

Commit ac4b75f

Browse files
committed
feat(quickemu): enable local SPICE over Unix sockets with VirGL
- Use Unix sockets for local --display spice to allow GL/VirGL acceleration - Use egl-headless backend to provide GL context for virtio-gpu-gl; do not enable SPICE gl=on (avoids blocking QEMU main loop) - Add -vga none for SPICE modes to prevent duplicate scanouts - Update start_viewer() to support spice+unix:// and spice://localhost:port connection modes for spicy and remote-viewer clients - Clean up Unix socket (.sock) files alongside existing .spice file cleanup Provides local GL-accelerated guest 3D via VirGL while keeping remote access over TCP unchanged. Signed-off-by: Martin Wimpress <martin@wimpress.org>
1 parent a89b658 commit ac4b75f

File tree

1 file changed

+162
-58
lines changed

1 file changed

+162
-58
lines changed

quickemu

Lines changed: 162 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -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() {
14011423
function 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

21172173
function 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

21522255
function 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

Comments
 (0)