/** @file
This file is part of OpenCanopy, OpenCore GUI.
Copyright (c) 2018-2019, Download-Fritz. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../OpenCanopy.h"
#include "../GuiIo.h"
struct GUI_POINTER_CONTEXT_ {
APPLE_EVENT_PROTOCOL *AppleEvent;
EFI_SIMPLE_POINTER_PROTOCOL *Pointer;
EFI_ABSOLUTE_POINTER_PROTOCOL *AbsPointer;
APPLE_EVENT_HANDLE AppleEventHandle;
UINT32 MaxX;
UINT32 MaxY;
UINT32 PrevX;
UINT32 PrevY;
UINT32 PrevRealX;
UINT32 PrevRealY;
UINT32 X;
UINT32 Y;
UINT8 LockedBy;
BOOLEAN PrimaryDown;
BOOLEAN SecondaryDown;
};
enum {
PointerUnlocked,
PointerLockedSimple,
PointerLockedAbsolute
};
STATIC
UINT32
InternalClipPointerSimple (
IN UINT32 OldCoord,
IN INT64 DeltaCoord,
IN UINT32 MaxCoord
)
{
BOOLEAN Result;
INT64 NewCoord;
Result = OcOverflowAddS64 (OldCoord, DeltaCoord, &NewCoord);
if (!Result) {
if (NewCoord <= 0) {
return 0;
}
if (NewCoord > MaxCoord) {
return MaxCoord;
}
return (UINT32) NewCoord;
}
if (DeltaCoord < 0) {
return 0;
}
return MaxCoord;
}
STATIC
INT64
InternalGetInterpolatedValue (
IN INT32 Value
)
{
INTN Bit;
//
// For now this produces most natural speed.
//
STATIC CONST INT8 AccelerationNumbers[] = {2};
if (Value != 0) {
Bit = HighBitSet32 (ABS (Value));
return (INT64) Value * AccelerationNumbers[
MIN (Bit, (INTN) ARRAY_SIZE (AccelerationNumbers) - 1)
];
}
return 0;
}
STATIC
VOID
EFIAPI
InternalAppleEventNotification (
IN APPLE_EVENT_INFORMATION *Information,
IN VOID *NotifyContext
)
{
APPLE_POINTER_EVENT_TYPE EventType;
GUI_POINTER_CONTEXT *Context;
Context = NotifyContext;
//
// Should not happen but just in case.
//
if ((Information->EventType & APPLE_ALL_MOUSE_EVENTS) == 0) {
return;
}
EventType = Information->EventData.PointerEventType;
if ((EventType & APPLE_EVENT_TYPE_MOUSE_MOVED) != 0) {
Context->X = MIN (
(UINT32) Information->PointerPosition.Horizontal,
Context->MaxX
);
Context->Y = MIN (
(UINT32) Information->PointerPosition.Vertical,
Context->MaxY
);
}
if ((EventType & APPLE_EVENT_TYPE_MOUSE_DOWN) != 0) {
if ((EventType & APPLE_EVENT_TYPE_LEFT_BUTTON) != 0) {
Context->PrimaryDown = TRUE;
} else if ((Information->EventType & APPLE_EVENT_TYPE_RIGHT_BUTTON) != 0) {
Context->SecondaryDown = TRUE;
}
} else if ((EventType & APPLE_EVENT_TYPE_MOUSE_UP) != 0) {
if ((EventType & APPLE_EVENT_TYPE_LEFT_BUTTON) != 0) {
Context->PrimaryDown = FALSE;
} else if ((EventType & APPLE_EVENT_TYPE_RIGHT_BUTTON) != 0) {
Context->SecondaryDown = FALSE;
}
}
}
STATIC
EFI_STATUS
InternalUpdateStateSimpleAppleEvent (
IN OUT GUI_POINTER_CONTEXT *Context,
OUT GUI_POINTER_STATE *State
)
{
INT64 Difference;
INT32 PrevX;
INT32 PrevY;
EFI_TPL OldTpl;
ASSERT (Context->AppleEvent != NULL);
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
PrevX = (INT32) Context->PrevX;
PrevY = (INT32) Context->PrevY;
Context->PrevX = Context->X;
Context->PrevY = Context->Y;
if (Context->X == 0 || Context->X == Context->MaxX) {
State->X = Context->X;
} else {
Difference = InternalGetInterpolatedValue ((INT32) Context->X - PrevX);
State->X = InternalClipPointerSimple (Context->PrevRealX, Difference, Context->MaxX);
}
if (Context->Y == 0 || Context->Y == Context->MaxY) {
State->Y = Context->Y;
} else {
Difference = InternalGetInterpolatedValue ((INT32) Context->Y - PrevY);
State->Y = InternalClipPointerSimple (Context->PrevRealY, Difference, Context->MaxY);
}
Context->PrevRealX = State->X;
Context->PrevRealY = State->Y;
State->PrimaryDown = Context->PrimaryDown;
State->SecondaryDown = Context->SecondaryDown;
gBS->RestoreTPL (OldTpl);
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
InternalUpdateStateSimplePointer (
IN OUT GUI_POINTER_CONTEXT *Context,
OUT GUI_POINTER_STATE *State
)
{
EFI_STATUS Status;
EFI_SIMPLE_POINTER_STATE PointerState;
INT64 InterpolatedX;
INT64 InterpolatedY;
ASSERT (Context->Pointer != NULL);
Status = Context->Pointer->GetState (Context->Pointer, &PointerState);
if (EFI_ERROR (Status)) {
return Status;
}
InterpolatedX = DivS64x64Remainder (
InternalGetInterpolatedValue (PointerState.RelativeMovementX),
Context->Pointer->Mode->ResolutionX,
NULL
);
InterpolatedY = DivS64x64Remainder (
InternalGetInterpolatedValue (PointerState.RelativeMovementY),
Context->Pointer->Mode->ResolutionY,
NULL
);
if (InterpolatedX == 0) {
if (PointerState.RelativeMovementX > 0) {
InterpolatedX = 1;
} else if (PointerState.RelativeMovementX < 0) {
InterpolatedX = -1;
}
}
if (InterpolatedY == 0) {
if (PointerState.RelativeMovementY > 0) {
InterpolatedY = 1;
} else if (PointerState.RelativeMovementY < 0) {
InterpolatedY = -1;
}
}
Context->X = InternalClipPointerSimple (
Context->X,
InterpolatedX,
Context->MaxX
);
State->X = Context->X;
Context->Y = InternalClipPointerSimple (
Context->Y,
InterpolatedY,
Context->MaxY
);
State->Y = Context->Y;
State->PrimaryDown = PointerState.LeftButton;
State->SecondaryDown = PointerState.RightButton;
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
InternalUpdateStateSimple (
IN OUT GUI_POINTER_CONTEXT *Context,
OUT GUI_POINTER_STATE *State
)
{
ASSERT (Context != NULL);
ASSERT (State != NULL);
if (Context->AppleEvent != NULL) {
return InternalUpdateStateSimpleAppleEvent (
Context,
State
);
}
if (Context->Pointer != NULL) {
return InternalUpdateStateSimplePointer (
Context,
State
);
}
return EFI_UNSUPPORTED;
}
STATIC
EFI_STATUS
InternalUpdateStateAbsolute (
IN OUT GUI_POINTER_CONTEXT *Context,
OUT GUI_POINTER_STATE *State
)
{
EFI_STATUS Status;
EFI_ABSOLUTE_POINTER_STATE PointerState;
UINT64 NewX;
UINT64 NewY;
ASSERT (Context != NULL);
ASSERT (State != NULL);
if (Context->AbsPointer == NULL) {
return EFI_UNSUPPORTED;
}
Status = Context->AbsPointer->GetState (Context->AbsPointer, &PointerState);
if (EFI_ERROR (Status)) {
return Status;
}
NewX = PointerState.CurrentX - Context->AbsPointer->Mode->AbsoluteMinX;
NewX *= Context->MaxX + 1;
NewX = DivU64x32 (NewX, (UINT32) (Context->AbsPointer->Mode->AbsoluteMaxX - Context->AbsPointer->Mode->AbsoluteMinX));
NewY = PointerState.CurrentY - Context->AbsPointer->Mode->AbsoluteMinY;
NewY *= Context->MaxY + 1;
NewY = DivU64x32 (NewY, (UINT32) (Context->AbsPointer->Mode->AbsoluteMaxY - Context->AbsPointer->Mode->AbsoluteMinY));
State->X = (UINT32)NewX;
State->Y = (UINT32)NewY;
State->PrimaryDown = (PointerState.ActiveButtons & EFI_ABSP_TouchActive) != 0;
State->SecondaryDown = (PointerState.ActiveButtons & EFI_ABS_AltActive) != 0;
//
// Adapt X/Y based on touch position so the cursor will not jump when
// changing from touch to mouse.
//
Context->X = (UINT32)NewX;
Context->Y = (UINT32)NewY;
return EFI_SUCCESS;
}
VOID
GuiPointerReset (
IN OUT GUI_POINTER_CONTEXT *Context
)
{
EFI_SIMPLE_POINTER_STATE SimpleState;
EFI_ABSOLUTE_POINTER_STATE AbsoluteState;
ASSERT (Context != NULL);
if (Context->Pointer != NULL) {
Context->Pointer->GetState (Context->Pointer, &SimpleState);
}
if (Context->AbsPointer != NULL) {
Context->AbsPointer->GetState (Context->AbsPointer, &AbsoluteState);
}
Context->LockedBy = PointerUnlocked;
}
EFI_STATUS
GuiPointerGetState (
IN OUT GUI_POINTER_CONTEXT *Context,
OUT GUI_POINTER_STATE *State
)
{
EFI_STATUS Status;
EFI_STATUS Status2;
ASSERT (Context != NULL);
ASSERT (State != NULL);
Status = EFI_NOT_READY;
switch (Context->LockedBy) {
case PointerUnlocked:
{
Status = InternalUpdateStateSimple (Context, State);
if (!EFI_ERROR (Status)
&& (State->PrimaryDown || State->SecondaryDown)) {
Context->LockedBy = PointerLockedSimple;
break;
}
Status2 = InternalUpdateStateAbsolute (Context, State);
if (!EFI_ERROR (Status2)) {
if (State->PrimaryDown || State->SecondaryDown) {
Context->LockedBy = PointerLockedAbsolute;
}
Status = Status2;
break;
}
break;
}
case PointerLockedSimple:
{
Status = InternalUpdateStateSimple (Context, State);
if (!EFI_ERROR (Status)
&& !State->PrimaryDown && !State->SecondaryDown) {
Context->LockedBy = PointerUnlocked;
}
break;
}
case PointerLockedAbsolute:
{
Status = InternalUpdateStateAbsolute (Context, State);
if (!EFI_ERROR (Status)
&& !State->PrimaryDown && !State->SecondaryDown) {
Context->LockedBy = PointerUnlocked;
}
break;
}
default:
{
ASSERT (FALSE);
break;
}
}
return Status;
}
GUI_POINTER_CONTEXT *
GuiPointerConstruct (
IN OC_PICKER_CONTEXT *PickerContext,
IN UINT32 DefaultX,
IN UINT32 DefaultY,
IN UINT32 Width,
IN UINT32 Height
)
{
// TODO: alloc on the fly?
STATIC GUI_POINTER_CONTEXT Context;
EFI_STATUS Status;
EFI_STATUS Status2;
EFI_TPL OldTpl;
DIMENSION Dimension;
ASSERT (DefaultX < Width);
ASSERT (DefaultY < Height);
ASSERT (Width <= MAX_INT32);
ASSERT (Height <= MAX_INT32);
Context.MaxX = Width - 1;
Context.MaxY = Height - 1;
Context.PrevX = DefaultX;
Context.PrevY = DefaultY;
Context.PrevRealX = DefaultX;
Context.PrevRealY = DefaultY;
Context.X = DefaultX;
Context.Y = DefaultY;
Status = OcHandleProtocolFallback (
gST->ConsoleInHandle,
&gAppleEventProtocolGuid,
(VOID **)&Context.AppleEvent
);
if (!EFI_ERROR (Status)) {
if (Context.AppleEvent->Revision >= APPLE_EVENT_PROTOCOL_REVISION) {
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
Status = Context.AppleEvent->RegisterHandler (
APPLE_ALL_MOUSE_EVENTS,
InternalAppleEventNotification,
&Context.AppleEventHandle,
&Context
);
Dimension.Horizontal = (INT32) DefaultX;
Dimension.Vertical = (INT32) DefaultY;
Context.AppleEvent->SetCursorPosition (
&Dimension
);
gBS->RestoreTPL (OldTpl);
} else {
Status = EFI_UNSUPPORTED;
}
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_WARN,
"OCUI: AppleEvent %u is unsupported - %r\n",
Context.AppleEvent->Revision,
Status
));
Context.AppleEvent = NULL;
}
}
if (EFI_ERROR (Status)) {
Status = OcHandleProtocolFallback (
gST->ConsoleInHandle,
&gEfiSimplePointerProtocolGuid,
(VOID **)&Context.Pointer
);
}
Status2 = OcHandleProtocolFallback (
gST->ConsoleInHandle,
&gEfiAbsolutePointerProtocolGuid,
(VOID **)&Context.AbsPointer
);
if (EFI_ERROR (Status) && EFI_ERROR (Status2)) {
return NULL;
}
return &Context;
}
VOID
GuiPointerDestruct (
IN GUI_POINTER_CONTEXT *Context
)
{
ASSERT (Context != NULL);
if (Context->AppleEvent != NULL) {
Context->AppleEvent->UnregisterHandler (
Context->AppleEventHandle
);
}
ZeroMem (Context, sizeof (*Context));
}