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

383 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
; =============================================================================
; Модуль : 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