; ============================================================================= ; Модуль : VMMDev Seamless Mode Service ; Назначение : Получение запросов на seamless mode от хоста ; Файл : services/seamless/seamless.inc ; Протокол : VMMDevReq_GetSeamlessChangeRequest (73) ; ; Seamless mode = «бесшовные окна»: окна гостя рисуются как окна хоста, ; без рамки VM. Хост периодически спрашивает гостя: «какие области экрана ; заняты окнами?» (visible region). ; ; Для KolibriOS полный seamless пока невозможен (нет API получения ; регионов окон), но мы: ; 1. Сообщаем хосту VMMDEV_GUEST_SUPPORTS_SEAMLESS → кнопка ; «View → Seamless Mode» в VBox GUI станет активной ; 2. Получаем события SEAMLESS_MODE_CHANGE через IRQ ; 3. Запрашиваем текущий режим через GetSeamlessChangeRequest ; 4. При включении seamless → отправляем visible region = весь экран ; (один прямоугольник 0,0 → width,height) ; ; Архитектура (как display): ; IRQ event → seamless_handle_event → pending=1, timestamp ; Timer tick → seamless_tick → если pending и debounce прошёл → обработать ; ============================================================================= svc_seamless_name db "SEAMLESS", 0 ; --- Data --- align 4 vbox_seamless_enabled dd 0 vbox_seamless_current_mode dd 0 ; VMMDEV_SEAMLESS_* vbox_seamless_pending dd 0 vbox_seamless_event_time dd 0 ; Debounce: 500ms (50 тиков * 10ms) SEAMLESS_DELAY_TICKS equ 50 ; --- Пакет GetSeamlessChangeRequest --- align 4 vmmdev_seamless_change_s VMMDEV_SEAMLESS_CHANGE_REQUEST \ , \ 0, \ VMMDEV_EVENT_SEAMLESS_MODE_CHANGE ; eventAck align 4 seamless_change_virt dd 0 seamless_change_phys dd 0 ; --- Пакет VideoSetVisibleRegion (1 прямоугольник) --- align 4 vmmdev_visible_region_s VMMDEV_VIDEO_SET_VISIBLE_REGION \ , \ 1, \ <0, 0, 0, 0> ; rect0: заполняется при вызове align 4 visible_region_virt dd 0 visible_region_phys dd 0 ; seamless_init — Инициализация proc seamless_init uses ebx DEBUGF 2, "[VBoxGuest] [Seamless] Initializing...\n" ; --- Физические адреса пакетов --- mov eax, vmmdev_seamless_change_s mov [seamless_change_virt], eax invoke GetPhysAddr test eax, eax jz .phys_failed mov [seamless_change_phys], eax mov eax, vmmdev_visible_region_s mov [visible_region_virt], eax invoke GetPhysAddr test eax, eax jz .phys_failed mov [visible_region_phys], eax DEBUGF __DEBUG_SEAMLESS__, "[VBoxGuest] [Seamless] SeamlessChange pkt: virt=0x%x phys=0x%x\n", \ [seamless_change_virt], [seamless_change_phys] DEBUGF __DEBUG_SEAMLESS__, "[VBoxGuest] [Seamless] VisibleRegion pkt: virt=0x%x phys=0x%x\n", \ [visible_region_virt], [visible_region_phys] mov dword [vbox_seamless_enabled], 1 mov dword [vbox_seamless_current_mode], VMMDEV_SEAMLESS_DISABLED ; Сразу запросить текущий режим (может быть уже включен) call seamless_query_mode DEBUGF 2, "[VBoxGuest] [Seamless] Initialized OK (mode=%d)\n", \ [vbox_seamless_current_mode] xor eax, eax ret .phys_failed: DEBUGF 2, "[VBoxGuest] [Seamless] GetPhysAddr failed\n" mov eax, VERR_GENERAL_FAILURE ret endp ; seamless_handle_event — Из IRQ контекста proc seamless_handle_event test eax, VMMDEV_EVENT_SEAMLESS_MODE_CHANGE jz .done cmp dword [vbox_seamless_enabled], 0 je .done invoke GetTimerTicks mov [vbox_seamless_event_time], eax mov dword [vbox_seamless_pending], 1 DEBUGF __DEBUG_SEAMLESS__, "[VBoxGuest] [Seamless] Event queued\n" .done: ret endp ; seamless_tick — Из таймера, debounce proc seamless_tick cmp dword [vbox_seamless_pending], 0 je .done invoke GetTimerTicks sub eax, [vbox_seamless_event_time] cmp eax, SEAMLESS_DELAY_TICKS jb .done mov dword [vbox_seamless_pending], 0 DEBUGF __DEBUG_SEAMLESS__, "[VBoxGuest] [Seamless] Processing mode change...\n" call seamless_query_mode .done: ret endp ; seamless_query_mode — Запросить текущий seamless mode у хоста (req 73) proc seamless_query_mode uses ebx edi mov edi, [seamless_change_virt] test edi, edi jz .bad_ptr ; Заполнить запрос mov dword [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.header.rc], 0 mov dword [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.mode], 0 mov dword [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.eventAck], \ VMMDEV_EVENT_SEAMLESS_MODE_CHANGE ; Отправить stdcall vmmdev_send_request, [seamless_change_phys] ; Проверить rc mov eax, [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.header.rc] test eax, eax jnz .error ; Прочитать режим mov eax, [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.mode] mov ebx, [vbox_seamless_current_mode] mov [vbox_seamless_current_mode], eax DEBUGF 2, "[VBoxGuest] [Seamless] Mode: %d → %d\n", ebx, eax ; Обработать смену режима cmp eax, VMMDEV_SEAMLESS_DISABLED je .mode_disabled cmp eax, VMMDEV_SEAMLESS_VISIBLE_REGION je .mode_visible_region cmp eax, VMMDEV_SEAMLESS_HOST_WINDOW je .mode_host_window DEBUGF 2, "[VBoxGuest] [Seamless] Unknown mode: %d\n", eax ret .mode_disabled: DEBUGF 2, "[VBoxGuest] [Seamless] Seamless DISABLED\n" ret .mode_visible_region: DEBUGF 2, "[VBoxGuest] [Seamless] Seamless ENABLED (visible region)\n" ; Отправить видимую область = весь экран call seamless_send_fullscreen_region ret .mode_host_window: DEBUGF 2, "[VBoxGuest] [Seamless] Seamless ENABLED (host window)\n" call seamless_send_fullscreen_region ret .bad_ptr: DEBUGF 2, "[VBoxGuest] [Seamless] Bad pointer\n" ret .error: DEBUGF 2, "[VBoxGuest] [Seamless] GetSeamlessChange failed: rc=0x%x\n", eax ret endp ; seamless_send_fullscreen_region — Отправить visible region = весь экран proc seamless_send_fullscreen_region uses ebx edi mov edi, [visible_region_virt] test edi, edi jz .bad_ptr ; Получить текущее разрешение из KOS invoke GetDisplay test eax, eax jz .no_display mov ebx, eax mov ecx, [ebx + DISPLAY.width] mov edx, [ebx + DISPLAY.height] DEBUGF 1, "[VBoxGuest] [Seamless] Sending region: 0,0 → %d,%d\n", ecx, edx ; Заполнить заголовок mov dword [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.header.rc], 0 ; Заполнить прямоугольник mov dword [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.cRect], 1 mov dword [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.rect0.xLeft], 0 mov dword [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.rect0.yTop], 0 mov [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.rect0.xRight], ecx mov [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.rect0.yBottom], edx ; Отправить stdcall vmmdev_send_request, [visible_region_phys] mov eax, [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.header.rc] test eax, eax jnz .error DEBUGF 1, "[VBoxGuest] [Seamless] Region sent OK\n" xor eax, eax ret .bad_ptr: DEBUGF 2, "[VBoxGuest] [Seamless] Bad pointer\n" ret .no_display: DEBUGF 2, "[VBoxGuest] [Seamless] No display\n" ret .error: DEBUGF 2, "[VBoxGuest] [Seamless] SetVisibleRegion failed: rc=0x%x\n", eax ret endp ; seamless_disable — Выключить seamless proc seamless_disable DEBUGF 2, "[VBoxGuest] [Seamless] Disabling...\n" mov dword [vbox_seamless_enabled], 0 mov dword [vbox_seamless_pending], 0 mov dword [vbox_seamless_current_mode], VMMDEV_SEAMLESS_DISABLED xor eax, eax ret endp REGISTER_SERVICE svc_seamless_name, SEAMLESS_EVENT_MASK, VMMDEV_GUEST_SUPPORTS_SEAMLESS, \ seamless_init, 0, seamless_disable, seamless_handle_event, seamless_tick, AUTOSTART_SEAMLESS