289 lines
9.6 KiB
C

/** @file
AppleKeyMapAggregator edge detection
Copyright (c) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include <Uefi.h>
#include <Protocol/AppleKeyMapAggregator.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcAppleKeyMapLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcMiscLib.h>
#include <Library/OcTimerLib.h>
#include <Library/TimerLib.h>
VOID
OcFreeKeyRepeatContext (
OC_KEY_REPEAT_CONTEXT **Context
)
{
ASSERT (Context != NULL);
if (*Context == NULL) {
return;
}
DEBUG ((DEBUG_INFO, "OCKM: Freeing key repeat context %p %p %p\n", *Context, (*Context)->KeysHeld, (*Context)->KeyHeldTimes));
FreePool ((*Context)->KeyHeldTimes);
FreePool ((*Context)->KeysHeld);
FreePool (*Context);
*Context = NULL;
}
EFI_STATUS
OcInitKeyRepeatContext (
OUT OC_KEY_REPEAT_CONTEXT **Context,
IN APPLE_KEY_MAP_AGGREGATOR_PROTOCOL *KeyMap,
IN UINTN MaxKeysHeld,
IN UINT64 InitialDelay,
IN UINT64 SubsequentDelay,
IN BOOLEAN PreventInitialRepeat
)
{
EFI_STATUS Status;
APPLE_MODIFIER_MAP Modifiers;
UINTN NumKeysUp;
UINTN NumKeysDown;
DEBUG ((OC_TRACE_UPDOWNKEYS, "OCKM: Allocating %d for repeat context\n", sizeof (OC_KEY_REPEAT_CONTEXT)));
*Context = AllocatePool (sizeof (OC_KEY_REPEAT_CONTEXT));
if (*Context == NULL) {
DEBUG ((DEBUG_ERROR, "OCKM: Cannot allocate repeat context\n"));
return EFI_OUT_OF_RESOURCES;
}
if (MaxKeysHeld == 0) {
(*Context)->KeysHeld = NULL;
} else {
DEBUG ((OC_TRACE_UPDOWNKEYS, "OCKM: Allocating %d for keys held\n", MaxKeysHeld * sizeof ((*Context)->KeysHeld[0])));
(*Context)->KeysHeld = AllocatePool (MaxKeysHeld * sizeof ((*Context)->KeysHeld[0]));
if ((*Context)->KeysHeld == NULL) {
DEBUG ((DEBUG_ERROR, "OCKM: Cannot allocate keys held\n"));
FreePool (*Context);
*Context = NULL;
return EFI_OUT_OF_RESOURCES;
}
DEBUG ((OC_TRACE_UPDOWNKEYS, "OCKM: Allocating %d for key held times\n", MaxKeysHeld * sizeof ((*Context)->KeyHeldTimes[0])));
(*Context)->KeyHeldTimes = AllocatePool (MaxKeysHeld * sizeof ((*Context)->KeyHeldTimes[0]));
if ((*Context)->KeyHeldTimes == NULL) {
DEBUG ((DEBUG_ERROR, "OCKM: Cannot allocate key held times\n"));
FreePool ((*Context)->KeysHeld);
FreePool (*Context);
*Context = NULL;
return EFI_OUT_OF_RESOURCES;
}
}
(*Context)->KeyMap = KeyMap;
(*Context)->NumKeysHeld = 0;
(*Context)->MaxKeysHeld = MaxKeysHeld;
(*Context)->InitialDelay = InitialDelay;
(*Context)->SubsequentDelay = SubsequentDelay;
(*Context)->PreviousTime = 0;
if ((MaxKeysHeld == 0) || (PreventInitialRepeat == FALSE)) {
Status = EFI_SUCCESS;
} else {
//
// Prevent any keys which are down at init from appearring as immediate down key strokes or even repeating until released and then pressed again
//
NumKeysDown = MaxKeysHeld;
Status = OcGetUpDownKeys (
*Context,
&Modifiers,
&NumKeysUp,
NULL,
&NumKeysDown,
NULL,
MAX_INT64 >> 1 // == far future, creates massive but manageable negative delta on next call
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "OCKM: InitKeyRepeatContext initial GetUpDownKeys call - %r\n", Status));
OcFreeKeyRepeatContext (Context);
return Status;
}
}
DEBUG ((DEBUG_INFO, "OCKM: Allocated key repeat context %p %p %p\n", *Context, (*Context)->KeysHeld, (*Context)->KeyHeldTimes));
return Status;
}
EFI_STATUS
EFIAPI
OcGetUpDownKeys (
IN OUT OC_KEY_REPEAT_CONTEXT *RepeatContext,
OUT APPLE_MODIFIER_MAP *Modifiers,
IN OUT UINTN *NumKeysUp,
OUT APPLE_KEY_CODE *KeysUp OPTIONAL,
IN OUT UINTN *NumKeysDown,
OUT APPLE_KEY_CODE *KeysDown OPTIONAL,
IN UINT64 CurrentTime
)
{
EFI_STATUS Status;
UINTN NumRawKeys;
APPLE_KEY_CODE RawKeys[OC_KEY_MAP_DEFAULT_SIZE];
UINTN NumKeysHeldInCopy;
UINTN MaxKeysHeldInCopy;
APPLE_KEY_CODE KeysHeldCopy[OC_HELD_KEYS_DEFAULT_SIZE];
INT64 KeyHeldTimesCopy[OC_HELD_KEYS_DEFAULT_SIZE];
UINTN Index;
UINTN Index2;
APPLE_KEY_CODE Key;
INT64 KeyTime;
UINT64 DeltaTime;
BOOLEAN FoundHeldKey;
ASSERT (Modifiers != NULL);
ASSERT (NumKeysUp != NULL);
ASSERT (NumKeysDown != NULL);
ASSERT (RepeatContext != NULL);
ASSERT (RepeatContext->KeyMap != NULL);
ASSERT (RepeatContext->NumKeysHeld <= RepeatContext->MaxKeysHeld);
ASSERT_EQUALS (RepeatContext->KeysHeld == NULL, RepeatContext->KeyHeldTimes == NULL);
DeltaTime = CurrentTime - RepeatContext->PreviousTime;
RepeatContext->PreviousTime = CurrentTime;
if (RepeatContext->KeysHeld != NULL) {
//
// All held keys could potentially go into keys up
//
if ((KeysUp != NULL) && (RepeatContext->MaxKeysHeld > *NumKeysUp)) {
DEBUG ((DEBUG_ERROR, "OCKM: MaxKeysHeld %d exceeds NumKeysUp %d\n", RepeatContext->MaxKeysHeld, *NumKeysUp));
return EFI_UNSUPPORTED;
}
//
// All requested keys could potentially go into held keys
// (NumKeysDown is always used as the requested number of keys to scan, even if there is no KeysDown buffer to return down keycodes)
//
if ((KeysDown != NULL) && (*NumKeysDown > RepeatContext->MaxKeysHeld)) {
DEBUG ((DEBUG_ERROR, "OCKM: Number of keys requested %d exceeds MaxKeysHeld %d\n", *NumKeysDown, RepeatContext->MaxKeysHeld));
return EFI_UNSUPPORTED;
}
//
// Clone live entries of KeysHeld buffer
//
MaxKeysHeldInCopy = ARRAY_SIZE (KeysHeldCopy);
if (RepeatContext->MaxKeysHeld > MaxKeysHeldInCopy) {
DEBUG ((DEBUG_ERROR, "OCKM: MaxKeysHeld %d exceeds supported copy space %d\n", RepeatContext->MaxKeysHeld, MaxKeysHeldInCopy));
return EFI_UNSUPPORTED;
}
CopyMem (KeysHeldCopy, RepeatContext->KeysHeld, RepeatContext->NumKeysHeld * sizeof (RepeatContext->KeysHeld[0]));
CopyMem (KeyHeldTimesCopy, RepeatContext->KeyHeldTimes, RepeatContext->NumKeysHeld * sizeof (RepeatContext->KeyHeldTimes[0]));
NumKeysHeldInCopy = RepeatContext->NumKeysHeld;
} else {
NumKeysHeldInCopy = 0;
}
NumRawKeys = ARRAY_SIZE (RawKeys);
if (*NumKeysDown > NumRawKeys) {
DEBUG ((DEBUG_ERROR, "OCKM: Number of keys requested %d exceeds supported raw key bufsize %d\n", *NumKeysDown, NumRawKeys));
return EFI_UNSUPPORTED;
}
Status = RepeatContext->KeyMap->GetKeyStrokes (
RepeatContext->KeyMap,
Modifiers,
&NumRawKeys,
RawKeys
);
if (EFI_ERROR (Status)) {
return Status;
}
if ((KeysDown != NULL) && (NumRawKeys > *NumKeysDown)) {
return EFI_BUFFER_TOO_SMALL;
}
DEBUG ((OC_TRACE_UPDOWNKEYS, "OCKM: [%p] %ld %ld %ld\n", RepeatContext, RepeatContext->InitialDelay, RepeatContext->SubsequentDelay, DeltaTime));
DEBUG ((OC_TRACE_UPDOWNKEYS, "OCKM: [%p] I u:%d d:%d n:%d h:%d r:%d m:%d\n", RepeatContext, *NumKeysUp, *NumKeysDown, RepeatContext->MaxKeysHeld, RepeatContext->NumKeysHeld, NumRawKeys, *Modifiers));
*NumKeysUp = 0;
*NumKeysDown = 0;
RepeatContext->NumKeysHeld = 0;
//
// Loop through all keys which are currently down
//
for (Index = 0; Index < NumRawKeys; ++Index) {
Key = RawKeys[Index];
if (RepeatContext->KeysHeld != NULL) {
RepeatContext->KeysHeld[RepeatContext->NumKeysHeld] = Key;
}
FoundHeldKey = FALSE;
for (Index2 = 0; Index2 < NumKeysHeldInCopy; Index2++) {
if (KeysHeldCopy[Index2] == Key) {
FoundHeldKey = TRUE;
KeyTime = KeyHeldTimesCopy[Index2] + DeltaTime;
KeysHeldCopy[Index2] = 0; // Mark held key as still down
DEBUG ((OC_TRACE_UPDOWNKEYS, "OCKM: [%p] Still down 0x%X %ld\n", RepeatContext, Key, KeyTime));
if ((RepeatContext->InitialDelay != 0) && (KeyTime >= 0)) {
if (KeysDown != NULL) {
KeysDown[*NumKeysDown] = Key;
}
(*NumKeysDown)++;
KeyTime -= RepeatContext->SubsequentDelay;
DEBUG ((OC_TRACE_UPDOWNKEYS, "OCKM: [%p] Repeating 0x%X %ld\n", RepeatContext, Key, KeyTime));
}
break;
}
}
if (!FoundHeldKey) {
if (KeysDown != NULL) {
KeysDown[*NumKeysDown] = Key;
}
(*NumKeysDown)++;
KeyTime = -(INT64)RepeatContext->InitialDelay;
DEBUG ((OC_TRACE_UPDOWNKEYS, "OCKM: [%p] New down 0x%X %ld\n", RepeatContext, Key, KeyTime));
}
if (RepeatContext->KeysHeld != NULL) {
RepeatContext->KeyHeldTimes[RepeatContext->NumKeysHeld] = KeyTime;
RepeatContext->NumKeysHeld++;
}
}
//
// Process remaining held keys
//
for (Index = 0; Index < NumKeysHeldInCopy; Index++) {
Key = KeysHeldCopy[Index];
if (Key != 0) {
DEBUG ((OC_TRACE_UPDOWNKEYS, "OCKM: [%p] Gone up 0x%X %ld\n", RepeatContext, Key, KeyHeldTimesCopy[Index]));
if (KeysUp != NULL) {
KeysUp[*NumKeysUp] = Key;
}
(*NumKeysUp)++;
}
}
DEBUG ((OC_TRACE_UPDOWNKEYS, "OCKM: [%p] O u:%d d:%d n:%d h:%d r:%d\n", RepeatContext, *NumKeysUp, *NumKeysDown, RepeatContext->MaxKeysHeld, RepeatContext->NumKeysHeld, NumRawKeys));
return Status;
}