/** @file Apple Event typing. Copyright (c) 2021, Mike Beaton. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause **/ #include #include #include #include #include #include #include #include #include STATIC APPLE_EVENT_PROTOCOL *mProtocol; STATIC VOID EFIAPI HandleKeyEvent ( IN APPLE_EVENT_INFORMATION *Information, IN OC_TYPING_CONTEXT *Context ) { APPLE_KEY_CODE AppleKeyCode; CHAR16 UnicodeChar; UINTN NewHead; AppleKeyCode = 0; UnicodeChar = L'\0'; if (Information->EventData.KeyData != NULL) { AppleKeyCode = Information->EventData.KeyData->AppleKeyCode; UnicodeChar = Information->EventData.KeyData->InputKey.UnicodeChar; } DEBUG ((OC_TRACE_TYPING, "OCTY: HandleKeyEvent[%p] %x:%d[%x] %c\n", &HandleKeyEvent, Information->EventType, Information->Modifiers, AppleKeyCode, UnicodeChar)); // // We now handle modifiers immediately, rather than in the queue. By comparison this reduces // memory footprint, simplifies queue and dequeue code and removes any possibility of // stuck modifiers if queue fills. It also still behaves at least as well from end user pov. // Difference is that modifiers held when action occurs are what apply, not modifiers // held when key which caused action was pressed - but it is not at all clear that // this is incorrect, it may even be more correct. // if ((Information->EventType & APPLE_EVENT_TYPE_MODIFIER_UP) != 0) { Context->CurrentModifiers &= ~Information->Modifiers; return; } else if ((Information->EventType & APPLE_EVENT_TYPE_MODIFIER_DOWN) != 0) { Context->CurrentModifiers |= Information->Modifiers; return; } else if ((Information->EventType & APPLE_EVENT_TYPE_KEY_DOWN) == 0) { return; } NewHead = Context->Head + 1; if (NewHead >= OC_TYPING_BUFFER_SIZE) { NewHead = 0; } // // Now that we are returning key presses only (not modifiers) in the queue, just drop any over queue length // if (NewHead == Context->Tail) { return; } Context->Head = NewHead; DEBUG_CODE_BEGIN (); ASSERT (AppleKeyCode != 0); DEBUG_CODE_END (); DEBUG ((OC_TRACE_TYPING, "OCTY: Writing to %d\n", Context->Head)); Context->Buffer[Context->Head].AppleKeyCode = AppleKeyCode; Context->Buffer[Context->Head].UnicodeChar = UnicodeChar; #if defined (OC_TRACE_KEY_TIMES) DEBUG_CODE_BEGIN (); Context->KeyTimes[Context->Head] = GetPerformanceCounter (); DEBUG_CODE_END (); #endif } EFI_STATUS OcRegisterTypingHandler ( OUT OC_TYPING_CONTEXT **Context ) { EFI_STATUS Status; DEBUG ((OC_TRACE_TYPING, "OCTY: OcRegisterTypingHandler\n")); *Context = NULL; Status = gBS->LocateProtocol ( &gAppleEventProtocolGuid, NULL, (VOID **)&mProtocol ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "OCTY: Failed to locate apple event protocol - %r\n", Status)); return Status; } DEBUG ((OC_TRACE_TYPING, "OCTY: About to allocate %d x %d for buffer, plus rest of context = %d\n", OC_TYPING_BUFFER_SIZE, sizeof ((*Context)->Buffer[0]), sizeof (**Context))); *Context = AllocatePool (sizeof (**Context)); if (*Context == NULL) { Status = EFI_OUT_OF_RESOURCES; DEBUG ((DEBUG_ERROR, "OCTY: Failed to allocate context - %r\n", Status)); return Status; } (*Context)->KeyTimes = NULL; #if defined (OC_TRACE_KEY_TIMES) DEBUG_CODE_BEGIN (); DEBUG ((OC_TRACE_TYPING, "OCTY: About to allocate %d x %d for key times buffer\n", OC_TYPING_BUFFER_SIZE, sizeof ((*Context)->KeyTimes[0]))); (*Context)->KeyTimes = AllocatePool (OC_TYPING_BUFFER_SIZE * sizeof ((*Context)->KeyTimes[0])); if ((*Context)->KeyTimes == NULL) { DEBUG ((DEBUG_ERROR, "OCTY: Failed to allocate typing context key times buffer - %r\n", Status)); FreePool (*Context); *Context = NULL; Status = EFI_OUT_OF_RESOURCES; return Status; } DEBUG_CODE_END (); #endif DEBUG ((OC_TRACE_TYPING, "OCTY: RegisterHandler (F, [%p], %p, %p)\n", &HandleKeyEvent, &(*Context)->Handle, *Context)); Status = mProtocol->RegisterHandler ( APPLE_ALL_KEYBOARD_EVENTS, (APPLE_EVENT_NOTIFY_FUNCTION)&HandleKeyEvent, &(*Context)->Handle, *Context ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "OCTY: Failed to register handler - %r\n", Status)); #if defined (OC_TRACE_KEY_TIMES) DEBUG_CODE_BEGIN (); FreePool ((*Context)->KeyTimes); DEBUG_CODE_END (); #endif FreePool (*Context); *Context = NULL; return Status; } // // Init // OcFlushTypingBuffer (*Context); DEBUG ((OC_TRACE_TYPING, "OCTY: reg c=%p h=%p k=%p\n", *Context, (*Context)->Handle, (*Context)->KeyTimes)); DEBUG ((DEBUG_INFO, "OCTY: Registered handler\n")); return EFI_SUCCESS; } EFI_STATUS OcUnregisterTypingHandler ( IN OC_TYPING_CONTEXT **Context ) { EFI_STATUS Status; ASSERT (Context != NULL); if (*Context == NULL) { return EFI_SUCCESS; } DEBUG ((OC_TRACE_TYPING, "OCTY: unreg c=%p h=%p k=%p\n", *Context, (*Context)->Handle, (*Context)->KeyTimes)); if ((*Context)->Handle == NULL) { Status = EFI_NOT_STARTED; } else { Status = mProtocol->UnregisterHandler ((*Context)->Handle); (*Context)->Handle = NULL; if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "OCTY: Failed to unregister handler - %r\n", Status)); } else { // show this here, before freeing the pools; any stray events in log after this point would be a problem DEBUG ((DEBUG_INFO, "OCTY: Unregistered handler\n")); } } #if defined (OC_TRACE_KEY_TIMES) DEBUG_CODE_BEGIN (); if ((*Context)->KeyTimes != NULL) { FreePool ((*Context)->KeyTimes); (*Context)->KeyTimes = NULL; } DEBUG_CODE_END (); #endif FreePool (*Context); *Context = NULL; // // Prevent chars which have queued up in ConIn (which is not used by us) affecting subsequent applications. // OcConsoleFlush (); return Status; } VOID OcGetNextKeystroke ( IN OC_TYPING_CONTEXT *Context, OUT APPLE_MODIFIER_MAP *Modifiers, OUT APPLE_KEY_CODE *AppleKeyCode, OUT CHAR16 *UnicodeChar ) { UINTN NewTail; UINT64 CurrentMillis; CurrentMillis = 0; ASSERT (Context != NULL); DEBUG ((OC_TRACE_TYPING, "OCTY: OcGetNextKeystroke(%p, M, K)\n", Context)); *Modifiers = Context->CurrentModifiers; *AppleKeyCode = 0; *UnicodeChar = L'\0'; // // Buffer empty. // if (Context->Tail == Context->Head) { return; } DEBUG ((OC_TRACE_TYPING, "OCTY: %d != %d\n", Context->Tail, Context->Head)); // // Write can potentially occur in the middle of read, so don't update // Context->Tail until data is read. // NewTail = Context->Tail + 1; if (NewTail >= OC_TYPING_BUFFER_SIZE) { NewTail = 0; } DEBUG ((OC_TRACE_TYPING, "OCTY: Reading from %d\n", NewTail)); *AppleKeyCode = Context->Buffer[NewTail].AppleKeyCode; *UnicodeChar = Context->Buffer[NewTail].UnicodeChar; Context->Tail = NewTail; #if defined (OC_TRACE_KEY_TIMES) DEBUG_CODE_BEGIN (); CurrentMillis = DivU64x64Remainder (GetTimeInNanoSecond (Context->KeyTimes[Context->Tail]), 1000000ULL, NULL); DEBUG ((DEBUG_INFO, "OCTY: OcGetNextKeystroke @%d %d[%x] %,Lu\n", Context->Tail, *Modifiers, *AppleKeyCode, CurrentMillis)); DEBUG_CODE_END (); #else DEBUG ((OC_TRACE_TYPING, "OCTY: OcGetNextKeystroke @%d %d[%x] %,Lu\n", Context->Tail, *Modifiers, *AppleKeyCode, CurrentMillis)); #endif } VOID OcFlushTypingBuffer ( IN OC_TYPING_CONTEXT *Context ) { Context->Tail = 0; Context->Head = 0; Context->CurrentModifiers = 0; DEBUG ((OC_TRACE_TYPING, "OCTY: OcFlushTypingBuffer %d %d %d\n", Context->Tail, Context->Head, Context->CurrentModifiers)); }