Files
eco_os/isotest/enable-displays.py

166 lines
5.4 KiB
Python
Raw Normal View History

2026-01-09 23:28:33 +00:00
#!/usr/bin/env python3
"""
Enable multiple displays on a SPICE VM by sending monitor configuration.
2026-01-10 08:42:37 +00:00
Retries until the SPICE agent in the guest is connected.
2026-01-09 23:28:33 +00:00
"""
import gi
import sys
2026-01-10 08:42:37 +00:00
import time
2026-01-09 23:28:33 +00:00
gi.require_version('SpiceClientGLib', '2.0')
from gi.repository import SpiceClientGLib, GLib
# Channel types (from spice-protocol)
CHANNEL_MAIN = 1
CHANNEL_DISPLAY = 2
class SpiceDisplayEnabler:
2026-01-10 08:42:37 +00:00
def __init__(self, uri, num_displays=3, width=1920, height=1080, timeout=60):
2026-01-09 23:28:33 +00:00
self.uri = uri
self.num_displays = num_displays
self.width = width
self.height = height
2026-01-10 08:42:37 +00:00
self.timeout = timeout
2026-01-09 23:28:33 +00:00
self.session = None
self.main_channel = None
self.display_channels = []
self.loop = GLib.MainLoop()
self.configured = False
2026-01-10 08:42:37 +00:00
self.agent_connected = False
self.config_sent = False
2026-01-09 23:28:33 +00:00
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')
if channel_type == CHANNEL_MAIN:
self.main_channel = channel
channel.connect_after('channel-event', self.on_channel_event)
2026-01-10 08:42:37 +00:00
# Check agent status periodically
GLib.timeout_add(500, self.check_agent_and_configure)
2026-01-09 23:28:33 +00:00
elif channel_type == CHANNEL_DISPLAY:
self.display_channels.append((channel_id, channel))
def on_channel_event(self, channel, event):
"""Handle channel events"""
if event == SpiceClientGLib.ChannelEvent.OPENED:
2026-01-10 08:42:37 +00:00
# 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
2026-01-09 23:28:33 +00:00
def configure_monitors(self):
"""Configure multiple monitors via SPICE protocol"""
2026-01-10 08:42:37 +00:00
if self.config_sent:
return
2026-01-09 23:28:33 +00:00
if not self.main_channel:
print("No main channel!")
2026-01-10 08:42:37 +00:00
return
2026-01-09 23:28:33 +00:00
# Enable and configure each display
for i in range(self.num_displays):
x = i * self.width # Position displays side by side
y = 0
try:
self.main_channel.update_display_enabled(i, True, False)
self.main_channel.update_display(i, x, y, self.width, self.height, False)
2026-01-09 23:28:33 +00:00
except Exception as e:
print(f" Error setting display {i}: {e}")
2026-01-10 08:42:37 +00:00
# Send the configuration
2026-01-09 23:28:33 +00:00
try:
self.main_channel.send_monitor_config()
2026-01-10 08:42:37 +00:00
self.config_sent = True
2026-01-09 23:28:33 +00:00
self.configured = True
2026-01-10 08:42:37 +00:00
print(f"Configured {self.num_displays} displays at {self.width}x{self.height}")
2026-01-09 23:28:33 +00:00
except Exception as e:
print(f"Error sending config: {e}")
2026-01-10 08:42:37 +00:00
# Quit after a short delay
GLib.timeout_add(1000, self.quit)
2026-01-09 23:28:33 +00:00
def quit(self):
self.loop.quit()
return False
2026-01-10 08:42:37 +00:00
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
2026-01-09 23:28:33 +00:00
def run(self):
print(f"Connecting to {self.uri}...")
2026-01-10 08:42:37 +00:00
print(f"Waiting up to {self.timeout}s for agent...")
2026-01-09 23:28:33 +00:00
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():
2026-01-10 08:42:37 +00:00
print("Failed to connect to SPICE server")
2026-01-09 23:28:33 +00:00
return False
2026-01-10 08:42:37 +00:00
# Set overall timeout
GLib.timeout_add(self.timeout * 1000, self.on_timeout)
2026-01-09 23:28:33 +00:00
self.loop.run()
2026-01-10 08:42:37 +00:00
if self.configured:
print(f"Success: {self.num_displays} displays enabled")
else:
print("Failed: Could not enable displays")
2026-01-09 23:28:33 +00:00
return self.configured
2026-01-10 08:42:37 +00:00
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
)
2026-01-09 23:28:33 +00:00
success = enabler.run()
sys.exit(0 if success else 1)
2026-01-10 08:42:37 +00:00
if __name__ == '__main__':
main()