mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
439 lines
12 KiB
C
439 lines
12 KiB
C
/** @file
|
|
This file is part of OpenCanopy, OpenCore GUI.
|
|
|
|
Copyright (c) 2018-2019, Download-Fritz. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-3-Clause
|
|
**/
|
|
|
|
#include <Uefi.h>
|
|
|
|
#include <Protocol/AppleEvent.h>
|
|
#include <Protocol/AbsolutePointer.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/BaseOverflowLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/AppleEventLib.h>
|
|
#include <Library/OcCpuLib.h>
|
|
#include <Library/OcMiscLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiLib.h>
|
|
|
|
#include "../OpenCanopy.h"
|
|
#include "../GuiIo.h"
|
|
|
|
#define ABS_DOUBLE_CLICK_RADIUS 25U
|
|
#define IS_POWER_2(x) (((x) & ((x) - 1)) == 0 && (x) != 0)
|
|
|
|
#define POINTER_SCALE 1U
|
|
|
|
struct GUI_POINTER_CONTEXT_ {
|
|
APPLE_EVENT_PROTOCOL *AppleEvent;
|
|
EFI_ABSOLUTE_POINTER_PROTOCOL *AbsPointer;
|
|
APPLE_EVENT_HANDLE AppleEventHandle;
|
|
EFI_EVENT AbsPollEvent;
|
|
UINT32 MaxXPlus1;
|
|
UINT32 MaxYPlus1;
|
|
GUI_PTR_POSITION CurPos;
|
|
GUI_PTR_POSITION AbsLastDownPos;
|
|
UINT32 OldEventExScale;
|
|
BOOLEAN AbsPrimaryDown;
|
|
BOOLEAN AbsDoubleClick;
|
|
UINT8 UiScale;
|
|
UINT8 LockedBy;
|
|
UINT8 EventQueueHead;
|
|
UINT8 EventQueueTail;
|
|
GUI_PTR_EVENT EventQueue[16];
|
|
};
|
|
|
|
enum {
|
|
PointerUnlocked,
|
|
PointerLockedSimple,
|
|
PointerLockedAbsolute
|
|
};
|
|
|
|
STATIC
|
|
VOID
|
|
InternalQueuePointerEvent (
|
|
IN OUT GUI_POINTER_CONTEXT *Context,
|
|
IN UINT8 Type,
|
|
IN UINT32 X,
|
|
IN UINT32 Y
|
|
)
|
|
{
|
|
UINT32 Tail;
|
|
|
|
//
|
|
// Due to the modulus, wraparounds do not matter. The size of the queue must
|
|
// be a power of two for this to hold.
|
|
//
|
|
STATIC_ASSERT (
|
|
IS_POWER_2 (ARRAY_SIZE (Context->EventQueue)),
|
|
"The pointer event queue must have a power of two length."
|
|
);
|
|
|
|
Tail = Context->EventQueueTail % ARRAY_SIZE (Context->EventQueue);
|
|
Context->EventQueue[Tail].Type = Type;
|
|
Context->EventQueue[Tail].Pos.Pos.X = X;
|
|
Context->EventQueue[Tail].Pos.Pos.Y = Y;
|
|
++Context->EventQueueTail;
|
|
}
|
|
|
|
BOOLEAN
|
|
GuiPointerGetEvent (
|
|
IN OUT GUI_POINTER_CONTEXT *Context,
|
|
OUT GUI_PTR_EVENT *Event
|
|
)
|
|
{
|
|
UINT32 Head;
|
|
|
|
if (Context->EventQueueHead == Context->EventQueueTail) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Due to the modulus, wraparounds do not matter. The size of the queue must
|
|
// be a power of two for this to hold.
|
|
//
|
|
STATIC_ASSERT (
|
|
IS_POWER_2 (ARRAY_SIZE (Context->EventQueue)),
|
|
"The pointer event queue must have a power of two length."
|
|
);
|
|
|
|
Head = Context->EventQueueHead % ARRAY_SIZE (Context->EventQueue);
|
|
Event->Type = Context->EventQueue[Head].Type;
|
|
Event->Pos.Pos.X = Context->EventQueue[Head].Pos.Pos.X;
|
|
Event->Pos.Pos.Y = Context->EventQueue[Head].Pos.Pos.Y;
|
|
++Context->EventQueueHead;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
EFIAPI
|
|
InternalAppleEventNotification (
|
|
IN APPLE_EVENT_INFORMATION *Information,
|
|
IN VOID *NotifyContext
|
|
)
|
|
{
|
|
APPLE_POINTER_EVENT_TYPE EventType;
|
|
GUI_POINTER_CONTEXT *Context;
|
|
|
|
Context = NotifyContext;
|
|
|
|
ASSERT ((Information->EventType & APPLE_ALL_MOUSE_EVENTS) != 0);
|
|
//
|
|
// Discard simple pointer updates when absolute pointer locked.
|
|
//
|
|
if (Context->LockedBy == PointerLockedAbsolute) {
|
|
return;
|
|
}
|
|
|
|
EventType = Information->EventData.PointerEventType;
|
|
|
|
if ((EventType & APPLE_EVENT_TYPE_MOUSE_MOVED) != 0) {
|
|
Context->CurPos.Pos.X = (UINT32)Information->PointerPosition.Horizontal;
|
|
Context->CurPos.Pos.Y = (UINT32)Information->PointerPosition.Vertical;
|
|
}
|
|
|
|
if ((EventType & APPLE_EVENT_TYPE_LEFT_BUTTON) != 0) {
|
|
if ((EventType & APPLE_EVENT_TYPE_MOUSE_DOWN) != 0) {
|
|
ASSERT (Context->LockedBy == PointerUnlocked);
|
|
InternalQueuePointerEvent (
|
|
Context,
|
|
GuiPointerPrimaryDown,
|
|
(UINT32)Information->PointerPosition.Horizontal,
|
|
(UINT32)Information->PointerPosition.Vertical
|
|
);
|
|
Context->LockedBy = PointerLockedSimple;
|
|
} else if ((EventType & APPLE_EVENT_TYPE_MOUSE_UP) != 0) {
|
|
ASSERT (Context->LockedBy == PointerLockedSimple);
|
|
InternalQueuePointerEvent (
|
|
Context,
|
|
GuiPointerPrimaryUp,
|
|
(UINT32)Information->PointerPosition.Horizontal,
|
|
(UINT32)Information->PointerPosition.Vertical
|
|
);
|
|
Context->LockedBy = PointerUnlocked;
|
|
} else if ((EventType & APPLE_EVENT_TYPE_MOUSE_DOUBLE_CLICK) != 0) {
|
|
InternalQueuePointerEvent (
|
|
Context,
|
|
GuiPointerPrimaryDoubleClick,
|
|
(UINT32)Information->PointerPosition.Horizontal,
|
|
(UINT32)Information->PointerPosition.Vertical
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
EFIAPI
|
|
InternalUpdateContextAbsolute (
|
|
IN EFI_EVENT Event,
|
|
IN OUT VOID *NotifyContext
|
|
)
|
|
{
|
|
GUI_POINTER_CONTEXT *Context;
|
|
EFI_STATUS Status;
|
|
EFI_ABSOLUTE_POINTER_STATE PointerState;
|
|
UINT64 NewX;
|
|
UINT64 NewY;
|
|
GUI_PTR_POSITION NewPos;
|
|
|
|
ASSERT (NotifyContext != NULL);
|
|
|
|
Context = NotifyContext;
|
|
ASSERT (Context->AbsPointer != NULL);
|
|
//
|
|
// Discard absolute pointer updates when simple pointer locked.
|
|
//
|
|
if (Context->LockedBy == PointerLockedSimple) {
|
|
return;
|
|
}
|
|
|
|
Status = Context->AbsPointer->GetState (Context->AbsPointer, &PointerState);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
NewX = PointerState.CurrentX - Context->AbsPointer->Mode->AbsoluteMinX;
|
|
NewX *= Context->MaxXPlus1;
|
|
NewPos.Pos.X = (UINT32)DivU64x32 (
|
|
NewX,
|
|
(UINT32)(Context->AbsPointer->Mode->AbsoluteMaxX - Context->AbsPointer->Mode->AbsoluteMinX)
|
|
);
|
|
|
|
NewY = PointerState.CurrentY - Context->AbsPointer->Mode->AbsoluteMinY;
|
|
NewY *= Context->MaxYPlus1;
|
|
NewPos.Pos.Y = (UINT32)DivU64x32 (
|
|
NewY,
|
|
(UINT32)(Context->AbsPointer->Mode->AbsoluteMaxY - Context->AbsPointer->Mode->AbsoluteMinY)
|
|
);
|
|
|
|
Context->CurPos.Uint64 = NewPos.Uint64;
|
|
//
|
|
// Cancel double click when the finger is moved too far away.
|
|
//
|
|
ASSERT (Context->UiScale > 0);
|
|
|
|
if (Context->AbsDoubleClick) {
|
|
if ( (ABS ((INT64)NewPos.Pos.X - Context->AbsLastDownPos.Pos.X) > ABS_DOUBLE_CLICK_RADIUS * Context->UiScale)
|
|
|| (ABS ((INT64)NewPos.Pos.Y - Context->AbsLastDownPos.Pos.Y) > ABS_DOUBLE_CLICK_RADIUS * Context->UiScale))
|
|
{
|
|
Context->AbsDoubleClick = FALSE;
|
|
}
|
|
}
|
|
|
|
if (Context->AbsPrimaryDown != ((PointerState.ActiveButtons & EFI_ABSP_TouchActive) != 0)) {
|
|
Context->AbsPrimaryDown = ((PointerState.ActiveButtons & EFI_ABSP_TouchActive) != 0);
|
|
if (Context->AbsPrimaryDown) {
|
|
ASSERT (Context->LockedBy == PointerUnlocked);
|
|
InternalQueuePointerEvent (
|
|
Context,
|
|
GuiPointerPrimaryDown,
|
|
NewPos.Pos.X,
|
|
NewPos.Pos.Y
|
|
);
|
|
Context->LockedBy = PointerLockedAbsolute;
|
|
|
|
Context->AbsLastDownPos.Uint64 = NewPos.Uint64;
|
|
Context->AbsDoubleClick = TRUE;
|
|
} else {
|
|
ASSERT (Context->LockedBy == PointerLockedAbsolute);
|
|
InternalQueuePointerEvent (
|
|
Context,
|
|
GuiPointerPrimaryUp,
|
|
NewPos.Pos.X,
|
|
NewPos.Pos.Y
|
|
);
|
|
Context->LockedBy = PointerUnlocked;
|
|
|
|
if (Context->AbsDoubleClick) {
|
|
InternalQueuePointerEvent (
|
|
Context,
|
|
GuiPointerPrimaryDoubleClick,
|
|
NewPos.Pos.X,
|
|
NewPos.Pos.Y
|
|
);
|
|
Context->AbsDoubleClick = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
GuiPointerReset (
|
|
IN OUT GUI_POINTER_CONTEXT *Context
|
|
)
|
|
{
|
|
ASSERT (Context != NULL);
|
|
|
|
Context->EventQueueHead = 0;
|
|
Context->EventQueueTail = 0;
|
|
|
|
Context->LockedBy = PointerUnlocked;
|
|
}
|
|
|
|
VOID
|
|
GuiPointerGetPosition (
|
|
IN OUT GUI_POINTER_CONTEXT *Context,
|
|
OUT GUI_PTR_POSITION *Position
|
|
)
|
|
{
|
|
EFI_TPL OldTpl;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Position != NULL);
|
|
//
|
|
// Prevent pointer updates during state retrieval.
|
|
// On 64+-bit systems, the operation is atomic.
|
|
//
|
|
if (sizeof (UINTN) < sizeof (UINT64)) {
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
}
|
|
|
|
//
|
|
// Return the current pointer position.
|
|
//
|
|
Position->Uint64 = Context->CurPos.Uint64;
|
|
|
|
if (sizeof (UINTN) < sizeof (UINT64)) {
|
|
gBS->RestoreTPL (OldTpl);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
GuiPointerSetPosition (
|
|
IN OUT GUI_POINTER_CONTEXT *Context,
|
|
IN CONST GUI_PTR_POSITION *Position
|
|
)
|
|
{
|
|
EFI_TPL OldTpl;
|
|
DIMENSION ApplePosition;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Position != NULL);
|
|
|
|
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
ApplePosition.Horizontal = (INT32)Position->Pos.X;
|
|
ApplePosition.Vertical = (INT32)Position->Pos.Y;
|
|
Context->AppleEvent->SetCursorPosition (
|
|
&ApplePosition
|
|
);
|
|
|
|
//
|
|
// Return the current pointer position.
|
|
//
|
|
Context->CurPos.Uint64 = Position->Uint64;
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
}
|
|
|
|
GUI_POINTER_CONTEXT *
|
|
GuiPointerConstruct (
|
|
IN UINT32 DefaultX,
|
|
IN UINT32 DefaultY,
|
|
IN UINT32 Width,
|
|
IN UINT32 Height,
|
|
IN UINT8 UiScale
|
|
)
|
|
{
|
|
// TODO: alloc on the fly?
|
|
STATIC GUI_POINTER_CONTEXT Context;
|
|
|
|
EFI_STATUS Status;
|
|
EFI_STATUS Status2;
|
|
DIMENSION Dimension;
|
|
|
|
ASSERT (DefaultX <= MAX_INT32);
|
|
ASSERT (DefaultY <= MAX_INT32);
|
|
|
|
Context.MaxXPlus1 = Width;
|
|
Context.MaxYPlus1 = Height;
|
|
Context.CurPos.Pos.X = DefaultX;
|
|
Context.CurPos.Pos.Y = DefaultY;
|
|
Context.UiScale = UiScale;
|
|
|
|
Context.AppleEvent = OcGetProtocol (
|
|
&gAppleEventProtocolGuid,
|
|
DEBUG_WARN,
|
|
"GuiPointerConstruct",
|
|
"AppleEvent"
|
|
);
|
|
|
|
Status = EFI_UNSUPPORTED;
|
|
if (Context.AppleEvent != NULL) {
|
|
if (Context.AppleEvent->Revision >= APPLE_EVENT_PROTOCOL_REVISION) {
|
|
Dimension.Horizontal = (INT32)DefaultX;
|
|
Dimension.Vertical = (INT32)DefaultY;
|
|
Context.AppleEvent->SetCursorPosition (
|
|
&Dimension
|
|
);
|
|
|
|
//
|
|
// Do not register 'Click' as its behaviour does not follow OS behaviour.
|
|
//
|
|
Status = Context.AppleEvent->RegisterHandler (
|
|
APPLE_ALL_MOUSE_EVENTS & ~APPLE_EVENT_TYPE_MOUSE_CLICK,
|
|
InternalAppleEventNotification,
|
|
&Context.AppleEventHandle,
|
|
&Context
|
|
);
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
DEBUG_WARN,
|
|
"OCUI: AppleEvent %u is unsupported - %r\n",
|
|
Context.AppleEvent->Revision,
|
|
Status
|
|
));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
Status2 = OcHandleProtocolFallback (
|
|
gST->ConsoleInHandle,
|
|
&gEfiAbsolutePointerProtocolGuid,
|
|
(VOID **)&Context.AbsPointer
|
|
);
|
|
if (!EFI_ERROR (Status2)) {
|
|
Context.AbsPollEvent = EventLibCreateNotifyTimerEvent (
|
|
InternalUpdateContextAbsolute,
|
|
&Context,
|
|
EFI_TIMER_PERIOD_MILLISECONDS (10),
|
|
TRUE
|
|
);
|
|
if (Context.AbsPollEvent == NULL) {
|
|
Status2 = EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
if (EFI_ERROR (Status) && EFI_ERROR (Status2)) {
|
|
return NULL;
|
|
}
|
|
|
|
return &Context;
|
|
}
|
|
|
|
VOID
|
|
GuiPointerDestruct (
|
|
IN GUI_POINTER_CONTEXT *Context
|
|
)
|
|
{
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Context->AppleEvent != NULL);
|
|
|
|
Context->AppleEvent->UnregisterHandler (Context->AppleEventHandle);
|
|
|
|
if (Context->AbsPollEvent != NULL) {
|
|
EventLibCancelEvent (Context->AbsPollEvent);
|
|
}
|
|
|
|
ZeroMem (Context, sizeof (*Context));
|
|
}
|