[ 'label' => 'Standard VGA (std)', 'description' => 'Compatible with most operating systems. Recommended fallback option.', 'supports_memory' => false, ], 'virtio' => [ 'label' => 'VirtIO-GPU', 'description' => 'Modern paravirtualized GPU with low overhead and better performance.', 'supports_memory' => true, ], 'vmware' => [ 'label' => 'VMware Compatible (vmware)', 'description' => 'Useful when guest OS expects VMware virtual hardware.', 'supports_memory' => false, ], 'cirrus' => [ 'label' => 'Legacy Cirrus (cirrus)', 'description' => 'Legacy compatibility mode for older operating systems.', 'supports_memory' => false, ], 'serial0' => [ 'label' => 'Serial Console (serial0)', 'description' => 'Disables graphical console and enables serial output only.', 'supports_memory' => false, ], 'none' => [ 'label' => 'No Display (none)', 'description' => 'Detach the VGA device entirely (headless).', 'supports_memory' => false, ], ]; public string $selectedDisplay = 'std'; public ?int $displayMemory = null; public ?string $currentRawValue = null; public bool $tabletEnabled = true; public array $original = []; public function mount(Server $server): void { parent::mount($server); $this->loadDisplayConfiguration(false); } /** * Reload the display configuration from Proxmox. */ public function loadDisplayConfiguration(bool $notify = true): void { try { $proxmox = new Proxmox; $config = $proxmox->request( '/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/config' )->json()['data'] ?? []; [$type, $memory] = $this->parseDisplayString($config['vga'] ?? null); $this->selectedDisplay = $type; $this->displayMemory = $memory; $this->currentRawValue = $config['vga'] ?? null; $this->tabletEnabled = !array_key_exists('tablet', $config) || (bool) $config['tablet']; // Always update original values to reflect current Proxmox state for synced comparison $this->original = [ 'selectedDisplay' => $this->selectedDisplay, 'displayMemory' => $this->displayMemory, 'tabletEnabled' => $this->tabletEnabled, ]; if ($notify) { $this->notify('Display configuration refreshed.', 'success'); } } catch (\Exception $e) { if ($notify) { $this->notify('Failed to load display settings: ' . $e->getMessage(), 'error'); } } } /** * Save the chosen display configuration. */ public function saveDisplaySettings(): void { $validated = $this->validate($this->rules(), $this->messages()); $payload = $validated['selectedDisplay']; if ($this->supportsMemory($validated['selectedDisplay']) && isset($validated['displayMemory'])) { $payload .= ',memory=' . $validated['displayMemory']; } try { $proxmox = new Proxmox; $proxmox->request( '/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/config', 'put', [ 'vga' => $payload, 'tablet' => $this->tabletEnabled ? 1 : 0, ] ); $this->currentRawValue = $payload; // Update original values after successful save $this->original = [ 'selectedDisplay' => $this->selectedDisplay, 'displayMemory' => $this->displayMemory, 'tabletEnabled' => $this->tabletEnabled, ]; $this->notify('Display configuration updated successfully. Reboot the VPS for changes to take effect.', 'success'); } catch (\Exception $e) { $this->notify('Unable to update display settings: ' . $e->getMessage(), 'error'); } } /** * Validation rules. */ protected function rules(): array { return [ 'selectedDisplay' => [ 'required', Rule::in(array_keys($this->displayOptions)), ], 'displayMemory' => [ function ($attribute, $value, $fail) { if (!$this->supportsMemory($this->selectedDisplay)) { return; } if ($value === null || $value === '') { return $fail('Select the amount of video memory to allocate.'); } if (!is_numeric($value) || (int) $value != $value) { return $fail('Video memory must be a whole number.'); } $intValue = (int) $value; if ($intValue < 16 || $intValue > 256) { return $fail('Video memory must be between 16 MB and 256 MB.'); } }, ], 'tabletEnabled' => [ 'boolean', ], ]; } protected function messages(): array { return [ 'selectedDisplay.in' => 'The selected display adapter is not available.', ]; } /** * Handle Livewire updates when display type changes. */ public function updatedSelectedDisplay(string $value): void { if (!$this->supportsMemory($value)) { $this->displayMemory = null; } elseif ($this->displayMemory === null) { $this->displayMemory = 64; } } /** * Derived list of display options that ensures the current selection is available. */ public function getDisplayOptionsProperty(): array { $options = $this->displayPresets; // Only add the original loaded display type as a custom option, not player-controlled input $originalDisplay = $this->original['selectedDisplay'] ?? null; if ($originalDisplay && !isset($options[$originalDisplay])) { $options[$originalDisplay] = [ 'label' => strtoupper($originalDisplay) . ' (custom)', 'description' => 'Currently configured in Proxmox. You can keep it or choose another option.', 'supports_memory' => false, ]; } return $options; } public function getSupportsMemoryProperty(): bool { return $this->supportsMemory($this->selectedDisplay); } protected function supportsMemory(?string $type): bool { return $type !== null && ($this->displayOptions[$type]['supports_memory'] ?? false); } /** * Parse Proxmox display string into [type, memory]. */ protected function parseDisplayString(?string $value): array { if (!$value || trim($value) === '') { return ['std', null]; } $segments = array_map('trim', explode(',', $value)); $type = array_shift($segments) ?: 'std'; $memory = null; foreach ($segments as $segment) { if (!str_contains($segment, '=')) { continue; } [$key, $val] = array_map('trim', explode('=', $segment, 2)); if (strtolower($key) === 'memory' && is_numeric($val)) { $memory = min(256, max(16, (int) $val)); } } if (!$this->supportsMemory($type)) { $memory = null; } elseif ($memory === null) { $memory = 64; } return [$type, $memory]; } /** * Check if Display Adapter settings are synced with the original values. */ public function getDisplayAdapterSyncedProperty(): bool { // If original hasn't been loaded yet, consider synced if (!isset($this->original['selectedDisplay'])) { return true; } $originalSelectedDisplay = (string)($this->original['selectedDisplay'] ?? 'std'); $currentSelectedDisplay = (string)$this->selectedDisplay; $displayMatch = $currentSelectedDisplay === $originalSelectedDisplay; $originalDisplayMemory = $this->original['displayMemory'] ?? null; $currentDisplayMemory = $this->displayMemory; // Both null means no memory setting, which matches $memoryMatch = ($originalDisplayMemory === null && $currentDisplayMemory === null) || ($originalDisplayMemory === $currentDisplayMemory); $originalTabletEnabled = (bool)($this->original['tabletEnabled'] ?? true); $currentTabletEnabled = (bool)$this->tabletEnabled; $tabletMatch = $originalTabletEnabled === $currentTabletEnabled; return $displayMatch && $memoryMatch && $tabletMatch; } public function render() { return view('proxmox::display')->layoutData([ 'sidebar' => true, ]); } }