-
-
Notifications
You must be signed in to change notification settings - Fork 76
Description
Is your feature request related to a problem? Please describe.
I've been working on Lima PRs to enhance VZ display and audio support (#4476, #4480). The current StartGraphicApplication() API bundles everything together — creates a window, wraps the view in a scroll view, and blocks on the event loop. I had to swizzle NSScrollView to disable the scrollbars (they're hardcoded to YES and cut off part of the display). There were concerns from @AkihiroSuda about shipping Objective-C runtime hacks in Lima, so I thought I would add support for custom GUI UX to this project.
Describe the solution you'd like
A pluggable window API that separates window creation from the event loop, enabling multi-VM support and custom UI integration. Minimal Go API surface that maintains backward compatibility while eliminating the need for runtime hacks. Demonstrated through a gui-linux example with VM registry, lifecycle management, and custom menus. Working implementation: stuffbucket/vz@v0.0.1-pluggable-window-api. I considered splitting the API between Go and Objective-C — exposing raw types and letting developers handle the platform layer themselves. That works for applications willing to write Objective-C, but this is a Go library. The approach here surfaces a minimal Go API that allows pluggable UX while maintaining backward compatibility. Applications that need deeper customization can still drop into Objective-C, but common patterns work from Go.
Describe alternatives you've considered
Fork vz for Lima — Adds maintenance burden and diverges from upstream.
Swizzling in Lima — What I have now in #4476. It works, but it's fragile. The swizzle intercepts every NSScrollView.setDocumentView: call and checks if the view is a VZVirtualMachineView.
Add scrollbar option only — Solves the immediate Lima problem but not the broader limitation.
Expose view via callback — Pass a callback to StartGraphicApplication() that receives the view before the event loop starts. Doesn't help with multi-VM.
Additional context
Working implementation: stuffbucket/vz@v0.0.1-pluggable-window-api
Diff against upstream: roughly +2100/-1300 lines, though much of that is moving code between files.
- Separate window creation from event loop
New Go API:
CreateWindow(width, height, opts...) — shows the window, returns immediately
CreateVMView() — returns the raw VZVirtualMachineView* for custom embedding
RunApplication() — runs the event loop when you're ready
StartGraphicApplication() — unchanged, now calls CreateWindow() + RunApplication() internally
Applications can create multiple VM windows before starting the event loop, or use their own event loop.
2. Reorganize Objective-C infrastructure
Moved app lifecycle code from virtualization_12.m into new files:
virtualization_default_app.h — public interface for app infrastructure
virtualization_default_app.m — VMWindowController, AppDelegate, default menus
The library provides standard macOS menus (App menu with About/Quit, Window menu, Help menu). Applications needing custom menus add them in their own Objective-C code. VMWindowController now supports multiple instances tracked by AppDelegate.
3. Updated gui-linux example
Multi-VM pattern demonstration:
VM registry persisted to ~/GUI Linux VM/registry.json
CLI subcommands: start [name], create , list, delete
Custom File menu in app_menu.m with "Start VM…" and "Create New VM…" dialogs
Go exports that Objective-C menu handlers call back into
Testing
Built and ran on macOS 15.2 (Sequoia) and macOS 26.2 (Tahoe):
Library tests: make test passes (code-signed test binary with entitlements)
Single VM: ./virtualization start launches default VM, window works as before
Multi-VM: Started three Fedora 41 live ISO instances concurrently via File menu. Each gets its own window, independent lifecycle. Windows menu shows all three.
CLI workflow: Created VMs via ./virtualization create, listed them, started specific ones, deleted them
Backward compatibility: StartGraphicApplication() still blocks and behaves identically to upstream
The registry tracks VM state across runs — ISOs associated with VMs, bundle directory structure.