Files
VBoxGuest/services/display/display.inc
2026-03-04 22:03:47 +03:00

207 lines
6.5 KiB
HTML

; =============================================================================
; Модуль : VMMDev Display Service (Auto-resize)
; Назначение : Автоматическое изменение разрешения экрана при resize окна VBox
; Файл : services/display/display.inc
;
; Логика задержки (паттерн heartbeat):
; Event → запомнить время, поставить флаг pending
; Tick → если pending и прошло 2 секунды → display_change
; Каждое новое событие сбрасывает таймер (debounce)
; =============================================================================
svc_display_name db "DISPLAY", 0
; --- Display Data ---
align 4
vbox_display_enabled dd 0
kos_display_ptr dd 0
; Отложенное обновление (debounce)
vbox_display_pending dd 0 ; 1 = есть отложенный запрос
vbox_display_event_time dd 0 ; timer_ticks момента последнего события
DISPLAY_DELAY_TICKS equ 50 ; 500ms (50 * 10ms)
; Статический буфер для запроса GetDisplayChangeRequest2
align 4
vmmdev_display_change_s VMMDEV_GET_DISPLAY_CHANGE_REQUEST2 \
<sizeof.VMMDEV_GET_DISPLAY_CHANGE_REQUEST2, \
VMMDEV_REQUEST_HEADER_VERSION, \
VMMDEV_REQ_GET_DISPLAY_CHANGE_2, \
0, 0, VMMDEV_REQUESTOR_KERNEL>, \
0, 0, 0, 1, 0 ; x_res, y_res, bpp, event_ack=1, display=0
; --- BGA Macros ---
macro bga_write index_val, data_val {
mov eax, index_val
mov dx, VBE_DISPI_IOPORT_INDEX
out dx, ax
mov eax, data_val
mov dx, VBE_DISPI_IOPORT_DATA
out dx, ax
}
macro bga_set_mode w, h, bpp {
bga_write VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED
bga_write VBE_DISPI_INDEX_XRES, w
bga_write VBE_DISPI_INDEX_YRES, h
bga_write VBE_DISPI_INDEX_BPP, bpp
bga_write VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED or VBE_DISPI_LFB_ENABLED
}
; vmmdev_display_init — Инициализация подсистемы дисплея
proc vmmdev_display_init
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Initializing...\n"
; Получить указатель на DISPLAY структуру ядра KolibriOS
invoke GetDisplay
mov [kos_display_ptr], eax
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] KOS display struct: 0x%x\n", eax
; Получить физический адрес буфера запроса
mov eax, vmmdev_display_change_s
mov [vbox_device.display_virt], eax
invoke GetPhysAddr
mov [vbox_device.display_phys], eax
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Buffer: virt=0x%x, phys=0x%x\n", \
vmmdev_display_change_s, eax
mov dword [vbox_display_enabled], 1
; Сразу обработать текущее разрешение (без задержки)
call display_change
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Initialized OK\n"
xor eax, eax
ret
endp
; display_handle_event — Обработка события (из IRQ)
proc display_handle_event
test eax, VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST
jz .done
cmp dword [vbox_display_enabled], 0
je .done
; Запомнить время события (сбросить таймер при повторных)
invoke GetTimerTicks
mov [vbox_display_event_time], eax
; Поставить флаг
mov dword [vbox_display_pending], 1
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Event queued, will apply in 2s\n"
.done:
ret
endp
; display_tick — Вызывается периодически из timer tick
proc display_tick
cmp dword [vbox_display_pending], 0
je .done
invoke GetTimerTicks
sub eax, [vbox_display_event_time]
cmp eax, DISPLAY_DELAY_TICKS
jb .done
; Сбросить флаг ДО обработки (новое событие во время display_change
; не потеряется — оно поставит pending заново)
mov dword [vbox_display_pending], 0
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Delay elapsed, applying...\n"
call display_change
.done:
ret
endp
; display_change — Запросить и применить новое разрешение
proc display_change uses ebx ecx edx esi edi
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] display_change\n"
mov ebx, [vbox_device.display_virt]
test ebx, ebx
jz .skip
mov dword [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.header.rc], 0
mov dword [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.event_ack], 1
mov dword [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.display], 0
mov eax, [vbox_device.display_phys]
stdcall vmmdev_send_request, eax
mov ebx, [vbox_device.display_virt]
mov edi, [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.x_res]
mov esi, [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.y_res]
mov ecx, [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.bpp]
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Requested: %dx%d %dbpp\n", edi, esi, ecx
; Валидация
cmp edi, DISP_W_MIN
jb .skip
cmp esi, DISP_H_MIN
jb .skip
cmp edi, DISP_W_MAX
ja .skip
cmp esi, DISP_H_MAX
ja .skip
cmp ecx, 32
je .valid
cmp ecx, 24
je .valid
cmp ecx, 16
je .valid
cmp ecx, 8
jne .skip
.valid:
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Setting %dx%d %dbpp\n", edi, esi, ecx
; BGA под cli (как в v5)
cli
bga_set_mode edi, esi, ecx
sti
; Обновить DISPLAY структуру ядра
mov eax, [kos_display_ptr]
mov [eax + DISPLAY.width], edi
mov [eax + DISPLAY.height], esi
mov [eax + DISPLAY.bits_per_pixel], ecx
; pitch = (bpp / 8) * width
push ebx
mov ebx, ecx
shr ecx, 3
imul ecx, edi
mov [eax + DISPLAY.pitch], ecx
pop ebx
; SetScreen(width-1, height-1)
mov eax, edi
mov edx, esi
dec eax
dec edx
invoke SetScreen
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Resolution set successfully\n"
.skip:
ret
endp
; display_disable — Отключить подсистему дисплея
proc display_disable
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Disabling...\n"
mov dword [vbox_display_enabled], 0
mov dword [vbox_display_pending], 0
xor eax, eax
ret
endp
REGISTER_SERVICE svc_display_name, DISPLAY_EVENT_MASK, VMMDEV_GUEST_SUPPORTS_GRAPHICS, \
vmmdev_display_init, 0, display_disable, display_handle_event, display_tick, AUTOSTART_DISPLAY