/** @file AppleKeyMapAggregator edge detection Copyright (c) 2021, Mike Beaton. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause **/ #include #include #include #include #include #include #include #include #include #include #include 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; }