383 lines
12 KiB
HTML
383 lines
12 KiB
HTML
; =============================================================================
|
||
; Модуль : VMMDev Mouse Service (Absolute Pointer)
|
||
; Назначение : Поддержка абсолютных координат мыши VirtualBox
|
||
; Файл : services/mouse/mouse.inc
|
||
;
|
||
; Использует расширенный протокол GetMouseStatusEx (type=223) если хост
|
||
; поддерживает FULL_STATE_PROTOCOL — кнопки и скролл приходят от VMMDev.
|
||
; Если хост не поддерживает — fallback на стандартный протокол + PS/2.
|
||
; =============================================================================
|
||
|
||
svc_mouse_name db "MOUSE", 0
|
||
|
||
; --- Mouse Data ---
|
||
align 4
|
||
vbox_mouse_enabled dd 0
|
||
vbox_mouse_x dd 0
|
||
vbox_mouse_y dd 0
|
||
vbox_mouse_buttons dd 0
|
||
vbox_mouse_dz dd 0 ; vertical scroll delta
|
||
vbox_mouse_dw dd 0 ; horizontal scroll delta
|
||
vbox_mouse_host_features dd 0
|
||
vbox_mouse_use_ext dd 0 ; 1 = extended protocol (buttons+scroll from VMMDev)
|
||
|
||
; Статический буфер для запроса (48 байт: 36 standard + 12 extended fields)
|
||
align 4
|
||
vmmdev_mouse_status_s VMMDEV_REQ_MOUSE_STATUS \
|
||
<sizeof.VMMDEV_REQ_MOUSE_STATUS, \
|
||
VMMDEV_REQUEST_HEADER_VERSION, \
|
||
VMMDEV_REQ_SET_MOUSE_STATUS, \
|
||
0, 0, VMMDEV_REQUESTOR_KERNEL>, \
|
||
VBOX_MOUSE_GUEST_FEATURES, 0, 0
|
||
; Extended fields (immediately after VMMDEV_REQ_MOUSE_STATUS in memory)
|
||
; VMMDev заполнит их при GET_MOUSE_STATUS_EX (type=223)
|
||
vmmdev_mouse_ext_dz dd 0 ; offset +36: vertical scroll
|
||
vmmdev_mouse_ext_dw dd 0 ; offset +40: horizontal scroll
|
||
vmmdev_mouse_ext_buttons dd 0 ; offset +44: button state
|
||
|
||
SIZEOF_MOUSE_STATUS_EX = sizeof.VMMDEV_REQ_MOUSE_STATUS + 12
|
||
MOUSE_EXT_OFS_DZ = sizeof.VMMDEV_REQ_MOUSE_STATUS
|
||
MOUSE_EXT_OFS_DW = sizeof.VMMDEV_REQ_MOUSE_STATUS + 4
|
||
MOUSE_EXT_OFS_BUTTONS = sizeof.VMMDEV_REQ_MOUSE_STATUS + 8
|
||
|
||
; vmmdev_mouse_init — Инициализация подсистемы мыши
|
||
proc vmmdev_mouse_init
|
||
; Guard against double initialization (fn_init = fn_enable in REGISTER_SERVICE)
|
||
cmp dword [vbox_mouse_enabled], 1
|
||
je .already_initialized
|
||
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Initializing...\n"
|
||
|
||
; Получить физический адрес буфера
|
||
mov eax, vmmdev_mouse_status_s
|
||
mov [vbox_device.mouse_virt], eax
|
||
invoke GetPhysAddr
|
||
mov [vbox_device.mouse_phys], eax
|
||
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Buffer: virt=0x%x, phys=0x%x, ext_size=%d\n", \
|
||
vmmdev_mouse_status_s, eax, SIZEOF_MOUSE_STATUS_EX
|
||
|
||
; Запросить текущий статус (стандартный GET, т.к. use_ext=0)
|
||
call mouse_query_status
|
||
test eax, eax
|
||
jnz .failed
|
||
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Host features: 0x%x\n", [vbox_mouse_host_features]
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Host position: X=%d, Y=%d\n", [vbox_mouse_x], [vbox_mouse_y]
|
||
|
||
; Проверить поддержку расширенного протокола
|
||
test dword [vbox_mouse_host_features], VMMDEV_MOUSE_HOST_SUPPORTS_FULL_STATE_PROTOCOL
|
||
jz .no_ext
|
||
|
||
; === Расширенный протокол ===
|
||
mov dword [vbox_mouse_use_ext], 1
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Host supports FULL_STATE_PROTOCOL — using extended mode\n"
|
||
|
||
call mouse_send_status
|
||
test eax, eax
|
||
jnz .failed
|
||
|
||
; Отключить PS/2 IRQ12 — VMMDev сам сообщает кнопки+скролл.
|
||
; Если PS/2 остаётся активным, SetMouseData(buttons=0) от VMMDev
|
||
; сбрасывает нажатые кнопки из PS/2 на каждом движении (glitch при drag).
|
||
stdcall i8042_mouse_control, 0
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] PS/2 IRQ12 disabled (buttons from VMMDev)\n"
|
||
|
||
jmp .ok
|
||
|
||
.no_ext:
|
||
; === Стандартный протокол (fallback) ===
|
||
mov dword [vbox_mouse_use_ext], 0
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] WARNING: Host does NOT support FULL_STATE_PROTOCOL\n"
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Fallback: standard protocol, PS/2 stays enabled for buttons\n"
|
||
|
||
call mouse_send_status
|
||
test eax, eax
|
||
jnz .failed
|
||
|
||
.ok:
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Initialized OK (ext=%d)\n", [vbox_mouse_use_ext]
|
||
xor eax, eax
|
||
ret
|
||
|
||
.already_initialized:
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Already initialized (skip reinit)\n"
|
||
xor eax, eax
|
||
ret
|
||
|
||
.failed:
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Init failed: 0x%x\n", eax
|
||
ret
|
||
endp
|
||
|
||
; mouse_query_status — Запросить статус у хоста
|
||
proc mouse_query_status
|
||
push ebx edi
|
||
|
||
mov edi, [vbox_device.mouse_virt]
|
||
test edi, edi
|
||
jz .bad_ptr
|
||
|
||
; Очистить rc
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc], 0
|
||
|
||
; Выбрать тип запроса
|
||
cmp dword [vbox_mouse_use_ext], 0
|
||
je .setup_standard
|
||
|
||
; === Extended GET (type=223, size=48) ===
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.size], SIZEOF_MOUSE_STATUS_EX
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.request_type], VMMDEV_REQ_GET_MOUSE_STATUS_EX
|
||
jmp .send
|
||
|
||
.setup_standard:
|
||
; === Standard GET (type=1, size=36) ===
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.size], sizeof.VMMDEV_REQ_MOUSE_STATUS
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.request_type], VMMDEV_REQ_GET_MOUSE_STATUS
|
||
|
||
.send:
|
||
mov ebx, [vbox_device.mouse_phys]
|
||
stdcall vmmdev_send_request, ebx
|
||
|
||
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc]
|
||
test eax, eax
|
||
js .error
|
||
|
||
; === Сохранить общие данные ===
|
||
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.mouse_features]
|
||
mov [vbox_mouse_host_features], eax
|
||
|
||
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.pointer_x_pos]
|
||
mov [vbox_mouse_x], eax
|
||
|
||
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.pointer_y_pos]
|
||
mov [vbox_mouse_y], eax
|
||
|
||
; === Если extended — сохранить кнопки и скролл ===
|
||
cmp dword [vbox_mouse_use_ext], 0
|
||
je .no_ext
|
||
|
||
mov eax, [edi + MOUSE_EXT_OFS_BUTTONS]
|
||
and eax, 0x1F ; биты 0-4: left, right, middle, X1, X2
|
||
mov [vbox_mouse_buttons], eax
|
||
|
||
mov eax, [edi + MOUSE_EXT_OFS_DZ]
|
||
mov [vbox_mouse_dz], eax
|
||
|
||
mov eax, [edi + MOUSE_EXT_OFS_DW]
|
||
mov [vbox_mouse_dw], eax
|
||
|
||
.no_ext:
|
||
DEBUGF __DEBUG_MOUSE__, "[VBoxGuest] [Mouse] VMMDev: feat=0x%x, X=%d, Y=%d, btn=0x%x, dz=%d\n", \
|
||
[vbox_mouse_host_features], [vbox_mouse_x], [vbox_mouse_y], \
|
||
[vbox_mouse_buttons], [vbox_mouse_dz]
|
||
|
||
; Восстановить header для SET запросов
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.size], sizeof.VMMDEV_REQ_MOUSE_STATUS
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.request_type], VMMDEV_REQ_SET_MOUSE_STATUS
|
||
|
||
xor eax, eax
|
||
pop edi ebx
|
||
ret
|
||
|
||
.error:
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Query failed: rc=0x%x\n", eax
|
||
; Восстановить header даже при ошибке
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.size], sizeof.VMMDEV_REQ_MOUSE_STATUS
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.request_type], VMMDEV_REQ_SET_MOUSE_STATUS
|
||
pop edi ebx
|
||
ret
|
||
|
||
.bad_ptr:
|
||
mov eax, VERR_INVALID_POINTER
|
||
pop edi ebx
|
||
ret
|
||
endp
|
||
|
||
; mouse_send_status — Отправить guest capabilities хосту
|
||
proc mouse_send_status
|
||
push ebx edi
|
||
|
||
mov edi, [vbox_device.mouse_virt]
|
||
mov ebx, [vbox_device.mouse_phys]
|
||
test edi, edi
|
||
jz .bad_ptr
|
||
test ebx, ebx
|
||
jz .bad_ptr
|
||
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc], 0
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.pointer_x_pos], 0
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.pointer_y_pos], 0
|
||
|
||
; Выбрать guest features
|
||
cmp dword [vbox_mouse_use_ext], 0
|
||
je .std_feats
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.mouse_features], VBOX_MOUSE_GUEST_FEATURES_EXT
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Sending guest features: 0x%x (extended)\n", VBOX_MOUSE_GUEST_FEATURES_EXT
|
||
jmp .send
|
||
.std_feats:
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.mouse_features], VBOX_MOUSE_GUEST_FEATURES
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Sending guest features: 0x%x (standard)\n", VBOX_MOUSE_GUEST_FEATURES
|
||
.send:
|
||
stdcall vmmdev_send_request, ebx
|
||
|
||
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc]
|
||
test eax, eax
|
||
js .error
|
||
|
||
mov dword [vbox_mouse_enabled], 1
|
||
xor eax, eax
|
||
pop edi ebx
|
||
ret
|
||
|
||
.error:
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Send status failed: 0x%x\n", eax
|
||
pop edi ebx
|
||
ret
|
||
|
||
.bad_ptr:
|
||
mov eax, VERR_INVALID_POINTER
|
||
pop edi ebx
|
||
ret
|
||
endp
|
||
|
||
; mouse_handle_event — Обработка событий мыши (вызывается из IRQ)
|
||
proc mouse_handle_event
|
||
push eax
|
||
|
||
DEBUGF __DEBUG_MOUSE__, "[VBoxGuest] [Mouse] Event: mask=0x%x\n", eax
|
||
|
||
; Любое из двух событий — нужен свежий статус
|
||
call mouse_query_status
|
||
test eax, eax
|
||
jnz .query_fail
|
||
|
||
pop eax
|
||
push eax
|
||
|
||
; Для position changed — обновить координаты в системе
|
||
test eax, VMMDEV_EVENT_MOUSE_POSITION_CHANGED
|
||
jz .done
|
||
|
||
call mouse_update_system_position
|
||
jmp .done
|
||
|
||
.query_fail:
|
||
DEBUGF __DEBUG_MOUSE__, "[VBoxGuest] [Mouse] Query failed in event handler: 0x%x\n", eax
|
||
|
||
.done:
|
||
pop eax
|
||
ret
|
||
endp
|
||
|
||
; mouse_update_system_position — Отправить позицию + кнопки + скролл в KolibriOS
|
||
proc mouse_update_system_position
|
||
push eax ebx ecx edx
|
||
|
||
; VirtualBox: 0-65535, KolibriOS: 0-32767
|
||
mov ecx, [vbox_mouse_x]
|
||
shr ecx, 1 ; X / 2
|
||
|
||
mov edx, [vbox_mouse_y]
|
||
shr edx, 1 ; Y / 2
|
||
|
||
; Флаги: bit31=X_abs, bit30=Y_abs + кнопки (bits 0-4)
|
||
mov eax, 0xC0000000
|
||
or eax, [vbox_mouse_buttons]
|
||
|
||
DEBUGF __DEBUG_MOUSE__, "[VBoxGuest] [Mouse] SetMouseData: flags=0x%x, X=%d, Y=%d, btn=0x%x, dz=%d, dw=%d\n", \
|
||
eax, ecx, edx, [vbox_mouse_buttons], [vbox_mouse_dz], [vbox_mouse_dw]
|
||
|
||
invoke SetMouseData, eax, ecx, edx, [vbox_mouse_dz], [vbox_mouse_dw]
|
||
|
||
; Сбросить дельты скролла (это дельты, не состояние)
|
||
mov dword [vbox_mouse_dz], 0
|
||
mov dword [vbox_mouse_dw], 0
|
||
|
||
pop edx ecx ebx eax
|
||
ret
|
||
endp
|
||
|
||
; mouse_disable — Отключить мышь
|
||
proc mouse_disable
|
||
push ebx edi
|
||
|
||
DEBUGF 2, "[VBoxGuest] [Mouse] Disabling...\n"
|
||
|
||
mov dword [vbox_mouse_enabled], 0
|
||
|
||
mov edi, [vbox_device.mouse_virt]
|
||
test edi, edi
|
||
jz .done
|
||
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc], 0
|
||
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.mouse_features], 0
|
||
|
||
mov ebx, [vbox_device.mouse_phys]
|
||
stdcall vmmdev_send_request, ebx
|
||
|
||
; Восстановить PS/2 при выключении
|
||
stdcall i8042_mouse_control, 1
|
||
|
||
.done:
|
||
xor eax, eax
|
||
pop edi ebx
|
||
ret
|
||
endp
|
||
|
||
|
||
; i8042_mouse_control — Включить/выключить IRQ12 PS/2 мыши
|
||
proc i8042_mouse_control stdcall, enable:dword
|
||
call .wait_input
|
||
mov al, 0x20
|
||
out 0x64, al
|
||
|
||
call .wait_output
|
||
in al, 0x60
|
||
|
||
and al, not 0x02
|
||
cmp [enable], 0
|
||
je @f
|
||
or al, 0x02
|
||
@@:
|
||
mov ah, al
|
||
|
||
call .wait_input
|
||
mov al, 0x60
|
||
out 0x64, al
|
||
|
||
call .wait_input
|
||
mov al, ah
|
||
out 0x60, al
|
||
ret
|
||
|
||
.wait_input:
|
||
push ecx
|
||
mov ecx, 0xFFFF
|
||
@@:
|
||
in al, 0x64
|
||
test al, 0x02
|
||
jz @f
|
||
dec ecx
|
||
jnz @b
|
||
@@:
|
||
pop ecx
|
||
ret
|
||
|
||
.wait_output:
|
||
push ecx
|
||
mov ecx, 0xFFFF
|
||
@@:
|
||
in al, 0x64
|
||
test al, 0x01
|
||
jnz @f
|
||
dec ecx
|
||
jnz @b
|
||
@@:
|
||
pop ecx
|
||
ret
|
||
endp
|
||
|
||
REGISTER_SERVICE svc_mouse_name, MOUSE_EVENT_MASK, VMMDEV_GUEST_SUPPORTS_MOUSE, \
|
||
vmmdev_mouse_init, vmmdev_mouse_init, mouse_disable, mouse_handle_event, 0, AUTOSTART_MOUSE
|