Add all files
This commit is contained in:
288
extensions/Servers/Proxmox/Livewire/Display.php
Normal file
288
extensions/Servers/Proxmox/Livewire/Display.php
Normal file
@@ -0,0 +1,288 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Servers\Proxmox\Livewire;
|
||||
|
||||
use Illuminate\Validation\Rule;
|
||||
use Paymenter\Extensions\Servers\Proxmox\Models\Server;
|
||||
use Paymenter\Extensions\Servers\Proxmox\Proxmox;
|
||||
|
||||
class Display extends Component
|
||||
{
|
||||
/**
|
||||
* Map of supported display devices and their metadata.
|
||||
*/
|
||||
protected array $displayPresets = [
|
||||
'std' => [
|
||||
'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;
|
||||
|
||||
if (!isset($options[$this->selectedDisplay])) {
|
||||
$options[$this->selectedDisplay] = [
|
||||
'label' => strtoupper($this->selectedDisplay) . ' (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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user