diff --git a/isotest/enable-displays.py b/isotest/enable-displays.py index c7cbe64..aad9a1f 100755 --- a/isotest/enable-displays.py +++ b/isotest/enable-displays.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 """ Enable multiple displays on a SPICE VM by sending monitor configuration. -Uses SpiceMainChannel.set_display() to configure displays directly. +Retries until the SPICE agent in the guest is connected. """ import gi import sys +import time gi.require_version('SpiceClientGLib', '2.0') from gi.repository import SpiceClientGLib, GLib @@ -15,114 +16,150 @@ CHANNEL_MAIN = 1 CHANNEL_DISPLAY = 2 class SpiceDisplayEnabler: - def __init__(self, uri, num_displays=3, width=1920, height=1080): + def __init__(self, uri, num_displays=3, width=1920, height=1080, timeout=60): self.uri = uri self.num_displays = num_displays self.width = width self.height = height + self.timeout = timeout self.session = None self.main_channel = None self.display_channels = [] self.loop = GLib.MainLoop() self.configured = False + self.agent_connected = False + self.config_sent = False def on_channel_new(self, session, channel): """Handle new channel creation""" channel_type = channel.get_property('channel-type') channel_id = channel.get_property('channel-id') - print(f"New channel: type={channel_type}, id={channel_id}") if channel_type == CHANNEL_MAIN: self.main_channel = channel channel.connect_after('channel-event', self.on_channel_event) + # Check agent status periodically + GLib.timeout_add(500, self.check_agent_and_configure) elif channel_type == CHANNEL_DISPLAY: self.display_channels.append((channel_id, channel)) - print(f" Display channel {channel_id} added") def on_channel_event(self, channel, event): """Handle channel events""" - print(f"Channel event: {event}") if event == SpiceClientGLib.ChannelEvent.OPENED: - print("Main channel opened, configuring displays...") - GLib.timeout_add(2000, self.configure_monitors) + # Start checking for agent + GLib.timeout_add(100, self.check_agent_and_configure) + + def check_agent_and_configure(self): + """Check if agent is connected and configure if ready""" + if self.config_sent: + return False # Stop checking + + if not self.main_channel: + return True # Keep checking + + self.agent_connected = self.main_channel.get_property('agent-connected') + + if self.agent_connected and not self.config_sent: + print(f"Agent connected! Configuring {self.num_displays} displays...") + self.configure_monitors() + return False # Stop checking + + return True # Keep checking def configure_monitors(self): """Configure multiple monitors via SPICE protocol""" - print(f"\n=== Configuring {self.num_displays} displays ===") - print(f"Display channels available: {len(self.display_channels)}") + if self.config_sent: + return if not self.main_channel: print("No main channel!") - GLib.timeout_add(1000, self.quit) - return False + return # Enable and configure each display for i in range(self.num_displays): x = i * self.width # Position displays side by side y = 0 - print(f"Setting display {i}: {self.width}x{self.height} at ({x}, {y})") - try: - # Enable the display using update_display_enabled (not set_display_enabled) self.main_channel.update_display_enabled(i, True, False) - - # Set display geometry (id, x, y, width, height) using update_display self.main_channel.update_display(i, x, y, self.width, self.height, False) except Exception as e: print(f" Error setting display {i}: {e}") - # Send the configuration immediately - print("\nSending monitor config to guest...") + # Send the configuration try: self.main_channel.send_monitor_config() + self.config_sent = True self.configured = True - print("Monitor config sent!") + print(f"Configured {self.num_displays} displays at {self.width}x{self.height}") except Exception as e: print(f"Error sending config: {e}") - # Wait a bit then check agent status and quit - GLib.timeout_add(3000, self.check_and_quit) - return False - - def check_and_quit(self): - """Check final status and quit""" - if self.main_channel: - agent_connected = self.main_channel.get_property('agent-connected') - print(f"\nAgent connected: {agent_connected}") - self.quit() - return False + # Quit after a short delay + GLib.timeout_add(1000, self.quit) def quit(self): self.loop.quit() return False + def on_timeout(self): + """Handle overall timeout""" + if not self.configured: + print(f"Timeout after {self.timeout}s - agent not connected") + self.quit() + return False + def run(self): print(f"Connecting to {self.uri}...") - print(f"Target: {self.num_displays} displays at {self.width}x{self.height}") + print(f"Waiting up to {self.timeout}s for agent...") self.session = SpiceClientGLib.Session() self.session.set_property('uri', self.uri) self.session.connect_after('channel-new', self.on_channel_new) if not self.session.connect(): - print("Failed to connect") + print("Failed to connect to SPICE server") return False - # Fallback timeout to configure monitors - GLib.timeout_add(5000, self.configure_monitors) - GLib.timeout_add(15000, self.quit) + # Set overall timeout + GLib.timeout_add(self.timeout * 1000, self.on_timeout) + self.loop.run() - print(f"\n=== Result ===") - print(f"Configured: {self.configured}") - print(f"Display channels: {len(self.display_channels)}") + if self.configured: + print(f"Success: {self.num_displays} displays enabled") + else: + print("Failed: Could not enable displays") + return self.configured -if __name__ == '__main__': - uri = sys.argv[1] if len(sys.argv) > 1 else 'spice://localhost:5930' - num_displays = int(sys.argv[2]) if len(sys.argv) > 2 else 3 - enabler = SpiceDisplayEnabler(uri, num_displays) +def main(): + import argparse + parser = argparse.ArgumentParser(description='Enable SPICE VM displays') + parser.add_argument('uri', nargs='?', default='spice://localhost:5930', + help='SPICE URI (default: spice://localhost:5930)') + parser.add_argument('num_displays', nargs='?', type=int, default=3, + help='Number of displays to enable (default: 3)') + parser.add_argument('--timeout', '-t', type=int, default=60, + help='Timeout in seconds (default: 60)') + parser.add_argument('--width', '-W', type=int, default=1920, + help='Display width (default: 1920)') + parser.add_argument('--height', '-H', type=int, default=1080, + help='Display height (default: 1080)') + + args = parser.parse_args() + + enabler = SpiceDisplayEnabler( + args.uri, + args.num_displays, + args.width, + args.height, + args.timeout + ) success = enabler.run() sys.exit(0 if success else 1) + + +if __name__ == '__main__': + main() diff --git a/isotest/run-test.sh b/isotest/run-test.sh index 7f1b320..b70def8 100755 --- a/isotest/run-test.sh +++ b/isotest/run-test.sh @@ -2,6 +2,17 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +# Parse arguments +AUTO_MODE=false +for arg in "$@"; do + case $arg in + --auto) + AUTO_MODE=true + shift + ;; + esac +done PROJECT_ROOT="$SCRIPT_DIR/.." VM_DIR="$PROJECT_ROOT/.nogit/vm" ISO_PATH="$PROJECT_ROOT/.nogit/iso/ecoos.iso" @@ -192,15 +203,12 @@ echo "" echo "=== Press Ctrl-C to stop ===" echo "" -# Wait for eco-vdagent to be ready in the guest, then enable all 3 displays -echo "Waiting for eco-vdagent to be ready (10s)..." -sleep 10 - -# Enable all 3 displays via SPICE protocol +# Enable all 3 displays via SPICE protocol (waits for agent automatically) +# Using 300s timeout since ISO boot can take several minutes if [ -f "$SCRIPT_DIR/enable-displays.py" ]; then - echo "Enabling all 3 displays via SPICE protocol..." - python3 "$SCRIPT_DIR/enable-displays.py" "spice://localhost:5930" 3 2>&1 || true - echo "" + echo "Enabling displays (waiting for SPICE agent, up to 5 minutes)..." + python3 "$SCRIPT_DIR/enable-displays.py" --timeout 300 2>&1 & + ENABLE_PID=$! fi echo "Tips:" @@ -209,5 +217,39 @@ echo " - http://localhost:3006 - Management UI" echo " - socat - UNIX-CONNECT:.nogit/vm/serial.sock - Serial console (login: ecouser/ecouser)" echo "" -# Wait for either process to exit -wait $QEMU_PID 2>/dev/null || true +if [ "$AUTO_MODE" = true ]; then + echo "=== Auto mode: waiting for display setup ===" + + # Wait for enable-displays.py to complete + if [ -n "$ENABLE_PID" ]; then + wait $ENABLE_PID + ENABLE_EXIT=$? + if [ $ENABLE_EXIT -ne 0 ]; then + echo "FAIL: Could not enable displays (exit code: $ENABLE_EXIT)" + exit 1 + fi + fi + + # Take screenshot + echo "Taking screenshot..." + "$SCRIPT_DIR/screenshot.sh" + + # Verify screenshot dimensions (should be 5760x1080 for 3 displays) + SCREENSHOT="$PROJECT_ROOT/.nogit/screenshots/latest.png" + if [ -f "$SCREENSHOT" ]; then + WIDTH=$(identify -format "%w" "$SCREENSHOT" 2>/dev/null || echo "0") + if [ "$WIDTH" -ge 5760 ]; then + echo "SUCCESS: Multi-display test passed (width: ${WIDTH}px)" + exit 0 + else + echo "FAIL: Screenshot width is ${WIDTH}px, expected >= 5760px" + exit 1 + fi + else + echo "FAIL: Screenshot not found" + exit 1 + fi +else + # Interactive mode - wait for QEMU to exit + wait $QEMU_PID 2>/dev/null || true +fi