820 lines
20 KiB
C

/** @file
AppleEventDxe
Copyright (c) 2018, vit9696
All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include <AppleMacEfi.h>
#include <IndustryStandard/AppleHid.h>
#include <Protocol/AppleKeyMapAggregator.h>
#include <Protocol/ConsoleControl.h>
#include <Library/AppleEventLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include "AppleEventInternal.h"
// KEY_STROKE_DELAY
#define KEY_STROKE_DELAY 5
// KEY_STROKE_POLL_FREQUENCY
#define KEY_STROKE_POLL_FREQUENCY EFI_TIMER_PERIOD_MILLISECONDS (10)
// KEY_STROKE_INFORMATION
typedef struct {
APPLE_KEY_CODE AppleKeyCode; ///<
UINTN NumberOfStrokes; ///<
BOOLEAN CurrentStroke; ///<
} KEY_STROKE_INFORMATION;
// mCLockOn
STATIC BOOLEAN mCLockOn = FALSE;
// mKeyStrokePollEvent
STATIC EFI_EVENT mKeyStrokePollEvent = NULL;
// mModifiers
STATIC APPLE_MODIFIER_MAP mModifiers = 0;
// mPreviousModifiers
STATIC APPLE_MODIFIER_MAP mPreviousModifiers = 0;
// mInitialized
STATIC BOOLEAN mInitialized = FALSE;
// mKeyInformation
STATIC KEY_STROKE_INFORMATION mKeyStrokeInfo[10];
// mCLockChanged
STATIC BOOLEAN mCLockChanged = FALSE;
// mKeyInitialDelay
// mKeySubsequentDelay
// Apple implementation default values
STATIC UINTN mKeyInitialDelay = 50;
STATIC UINTN mKeySubsequentDelay = 5;
// mGraphicsInputMirroring
STATIC BOOLEAN mGraphicsInputMirroring = FALSE;
// mAppleKeyMapAggregator
STATIC APPLE_KEY_MAP_AGGREGATOR_PROTOCOL *mKeyMapAggregator = NULL;
// InternalSetKeyBehaviour
VOID
InternalSetKeyBehaviour (
IN BOOLEAN CustomDelays,
IN UINT16 KeyInitialDelay,
IN UINT16 KeySubsequentDelay,
IN BOOLEAN GraphicsInputMirroring
)
{
if (CustomDelays) {
//
// Zero is meaningful
//
mKeyInitialDelay = KeyInitialDelay;
//
// Zero is meaningless (also div by zero expception): warn and use 1
//
if (KeySubsequentDelay == 0) {
KeySubsequentDelay = 1;
DEBUG ((DEBUG_WARN, "OCAE: Illegal KeySubsequentDelay value 0, using 1\n"));
}
mKeySubsequentDelay = KeySubsequentDelay;
DEBUG ((DEBUG_INFO, "OCAE: Using key delays %d (%d0ms) and %d (%d0ms)\n", mKeyInitialDelay, mKeyInitialDelay, mKeySubsequentDelay, mKeySubsequentDelay));
}
mGraphicsInputMirroring = GraphicsInputMirroring;
}
// InternalGetAppleKeyStrokes
STATIC
EFI_STATUS
InternalGetAppleKeyStrokes (
OUT APPLE_MODIFIER_MAP *Modifiers,
OUT UINTN *NumberOfKeyCodes,
OUT APPLE_KEY_CODE **KeyCodes
)
{
EFI_STATUS Status;
DEBUG ((DEBUG_VERBOSE, "InternalGetAppleKeyStrokes\n"));
Status = EFI_UNSUPPORTED;
if (mKeyMapAggregator != NULL) {
Status = EFI_INVALID_PARAMETER;
if ( (Modifiers != NULL)
&& (NumberOfKeyCodes != NULL)
&& (KeyCodes != NULL))
{
*NumberOfKeyCodes = 0;
*KeyCodes = NULL;
Status = mKeyMapAggregator->GetKeyStrokes (
mKeyMapAggregator,
Modifiers,
NumberOfKeyCodes,
NULL
);
if (!EFI_ERROR (Status) || (Status == EFI_BUFFER_TOO_SMALL)) {
if (*NumberOfKeyCodes == 0) {
*KeyCodes = NULL;
} else {
*KeyCodes = AllocateZeroPool (
*NumberOfKeyCodes * sizeof (**KeyCodes)
);
if (*KeyCodes == NULL) {
*NumberOfKeyCodes = 0;
Status = EFI_OUT_OF_RESOURCES;
DEBUG ((DEBUG_VERBOSE, "InternalGetAppleKeyStrokes alloc failure\n"));
} else {
Status = mKeyMapAggregator->GetKeyStrokes (
mKeyMapAggregator,
Modifiers,
NumberOfKeyCodes,
*KeyCodes
);
if (EFI_ERROR (Status)) {
FreePool ((VOID *)*KeyCodes);
*KeyCodes = NULL;
*NumberOfKeyCodes = 0;
}
}
}
}
}
}
return Status;
}
// InternalGetModifierStrokes
APPLE_MODIFIER_MAP
InternalGetModifierStrokes (
VOID
)
{
APPLE_MODIFIER_MAP Modifiers;
EFI_STATUS Status;
UINTN NumberOfKeyCodes;
APPLE_KEY_CODE *KeyCodes;
DEBUG ((DEBUG_VERBOSE, "InternalGetModifierStrokes\n"));
Status = InternalGetAppleKeyStrokes (
&Modifiers,
&NumberOfKeyCodes,
&KeyCodes
);
if (!EFI_ERROR (Status)) {
if (KeyCodes != NULL) {
FreePool ((VOID *)KeyCodes);
}
} else {
Modifiers = 0;
}
return Modifiers;
}
// InternalAppleKeyEventDataFromInputKey
STATIC
EFI_STATUS
InternalAppleKeyEventDataFromInputKey (
OUT APPLE_EVENT_DATA *EventData,
IN APPLE_KEY_CODE *AppleKeyCode,
IN EFI_INPUT_KEY *InputKey
)
{
EFI_STATUS Status;
APPLE_KEY_EVENT_DATA *KeyEventData;
DEBUG ((DEBUG_VERBOSE, "InternalAppleKeyEventDataFromInputKey\n"));
Status = EFI_INVALID_PARAMETER;
if ((EventData != NULL) && (AppleKeyCode != NULL) && (InputKey != NULL)) {
KeyEventData = AllocateZeroPool (sizeof (*KeyEventData));
Status = EFI_OUT_OF_RESOURCES;
if (KeyEventData != NULL) {
KeyEventData->NumberOfKeyPairs = 1;
KeyEventData->InputKey = *InputKey;
CopyMem (
(VOID *)&KeyEventData->AppleKeyCode,
(VOID *)AppleKeyCode,
sizeof (*AppleKeyCode)
);
EventData->KeyData = KeyEventData;
Status = EFI_SUCCESS;
}
}
return Status;
}
// InternalGetAndRemoveReleasedKeys
STATIC
UINTN
InternalGetAndRemoveReleasedKeys (
IN CONST UINTN *NumberOfKeyCodes,
IN CONST APPLE_KEY_CODE *KeyCodes,
OUT APPLE_KEY_CODE **ReleasedKeys
)
{
UINTN NumberOfReleasedKeys;
UINTN Index;
UINTN Index2;
APPLE_KEY_CODE ReleasedKeysBuffer[12];
UINTN ReleasedKeysSize;
DEBUG ((DEBUG_VERBOSE, "InternalGetAndRemoveReleasedKeys\n"));
NumberOfReleasedKeys = 0;
for (Index = 0; Index < ARRAY_SIZE (mKeyStrokeInfo); ++Index) {
for (Index2 = 0; Index2 < *NumberOfKeyCodes; ++Index2) {
if (mKeyStrokeInfo[Index].AppleKeyCode == KeyCodes[Index2]) {
break;
}
}
if (*NumberOfKeyCodes == Index2) {
if (mKeyStrokeInfo[Index].AppleKeyCode != 0) {
ReleasedKeysBuffer[NumberOfReleasedKeys] = mKeyStrokeInfo[Index].AppleKeyCode;
++NumberOfReleasedKeys;
}
ZeroMem (
&mKeyStrokeInfo[Index],
sizeof (mKeyStrokeInfo[Index])
);
}
}
// Add CLock to released keys if applicable and set bool to FALSE.
if (mCLockChanged) {
for (Index = 0; Index < *NumberOfKeyCodes; ++Index) {
if (KeyCodes[Index] == AppleHidUsbKbUsageKeyCLock) {
break;
}
}
if (*NumberOfKeyCodes == Index) {
mCLockChanged = FALSE;
ReleasedKeysBuffer[NumberOfReleasedKeys] = AppleHidUsbKbUsageKeyCLock;
++NumberOfReleasedKeys;
}
}
// Allocate a heap buffer to return.
*ReleasedKeys = NULL;
if (NumberOfReleasedKeys > 0) {
ReleasedKeysSize = (sizeof (**ReleasedKeys) * NumberOfReleasedKeys);
*ReleasedKeys = AllocateZeroPool (ReleasedKeysSize);
if (*ReleasedKeys != NULL) {
CopyMem (
(VOID *)*ReleasedKeys,
(VOID *)&ReleasedKeysBuffer[0],
ReleasedKeysSize
);
} else {
NumberOfReleasedKeys = 0;
}
}
return NumberOfReleasedKeys;
}
// InternalIsCLockOn
STATIC
BOOLEAN
InternalIsCLockOn (
IN CONST UINTN *NumberOfKeyCodes,
IN CONST APPLE_KEY_CODE *KeyCodes
)
{
BOOLEAN CLockOn;
UINTN Index;
UINTN Index2;
KEY_STROKE_INFORMATION *KeyInfo;
DEBUG ((DEBUG_VERBOSE, "InternalIsCLockOn\n"));
//
// Check against invalid usage
//
if ( (NumberOfKeyCodes == NULL)
|| ((*NumberOfKeyCodes != 0) && (KeyCodes == NULL)))
{
return FALSE;
}
//
// Return the previous value by default
//
CLockOn = mCLockOn;
for (Index = 0; Index < *NumberOfKeyCodes; ++Index) {
KeyInfo = NULL;
for (Index2 = 0; Index2 < ARRAY_SIZE (mKeyStrokeInfo); ++Index2) {
if (mKeyStrokeInfo[Index2].AppleKeyCode == KeyCodes[Index]) {
KeyInfo = &mKeyStrokeInfo[Index2];
break;
}
}
if ( (KeyInfo == NULL)
&& (KeyCodes[Index] == AppleHidUsbKbUsageKeyCLock)
&& !mCLockChanged)
{
CLockOn = (BOOLEAN)mCLockOn == FALSE;
break;
}
}
return CLockOn;
}
// InternalGetCurrentStroke
STATIC
KEY_STROKE_INFORMATION *
InternalGetCurrentStroke (
VOID
)
{
KEY_STROKE_INFORMATION *KeyInfo;
UINTN Index;
DEBUG ((DEBUG_VERBOSE, "InternalGetCurrentStroke\n"));
KeyInfo = NULL;
for (Index = 0; Index < ARRAY_SIZE (mKeyStrokeInfo); ++Index) {
if (mKeyStrokeInfo[Index].CurrentStroke) {
KeyInfo = &mKeyStrokeInfo[Index];
break;
}
}
return KeyInfo;
}
// InternalGetCurrentKeyStroke
STATIC
EFI_STATUS
InternalGetCurrentKeyStroke (
IN APPLE_MODIFIER_MAP Modifiers,
IN OUT UINTN *NumberOfKeyCodes,
IN OUT APPLE_KEY_CODE *KeyCodes,
IN OUT EFI_INPUT_KEY *Key
)
{
EFI_STATUS Status;
KEY_STROKE_INFORMATION *KeyInfo;
UINTN Index;
UINTN NumberOfReleasedKeys;
APPLE_KEY_CODE *ReleasedKeys;
BOOLEAN CLockOn;
APPLE_MODIFIER_MAP AppleModifiers;
BOOLEAN ShiftPressed;
EFI_INPUT_KEY InputKey;
APPLE_EVENT_DATA AppleEventData;
UINTN NewKeyIndex;
BOOLEAN AcceptStroke;
BOOLEAN Shifted;
DEBUG ((DEBUG_VERBOSE, "InternalGetCurrentKeyStroke\n"));
if (mModifiers != Modifiers) {
for (Index = 0; Index < ARRAY_SIZE (mKeyStrokeInfo); ++Index) {
mKeyStrokeInfo[Index].CurrentStroke = FALSE;
}
}
NumberOfReleasedKeys = InternalGetAndRemoveReleasedKeys (
NumberOfKeyCodes,
KeyCodes,
&ReleasedKeys
);
CLockOn = InternalIsCLockOn (NumberOfKeyCodes, KeyCodes);
AppleModifiers = Modifiers;
if (CLockOn) {
AppleModifiers |= APPLE_MODIFIERS_SHIFT;
}
ShiftPressed = (BOOLEAN)((AppleModifiers & APPLE_MODIFIERS_SHIFT) != 0);
for (Index = 0; Index < NumberOfReleasedKeys; ++Index) {
EventInputKeyFromAppleKeyCode (
ReleasedKeys[Index],
&InputKey,
ShiftPressed
);
AppleEventData.KeyData = NULL;
Status = InternalAppleKeyEventDataFromInputKey (
&AppleEventData,
&ReleasedKeys[Index],
&InputKey
);
if (EFI_ERROR (Status)) {
break;
}
EventCreateEventQueue (
AppleEventData,
APPLE_EVENT_TYPE_KEY_UP,
AppleModifiers
);
}
if (ReleasedKeys != NULL) {
FreePool ((VOID *)ReleasedKeys);
}
if (CLockOn != mCLockOn) {
mCLockOn = CLockOn;
mCLockChanged = TRUE;
}
//
// Increase the number of strokes for all currently pressed keys.
//
for (NewKeyIndex = 0; NewKeyIndex < *NumberOfKeyCodes; ++NewKeyIndex) {
KeyInfo = NULL;
for (Index = 0; Index < ARRAY_SIZE (mKeyStrokeInfo); ++Index) {
if (mKeyStrokeInfo[Index].AppleKeyCode == KeyCodes[NewKeyIndex]) {
KeyInfo = &mKeyStrokeInfo[Index];
KeyInfo->NumberOfStrokes++;
break;
}
}
//
// Indicates a key has been pressed which is not part of mKeyInformation.
//
if (KeyInfo == NULL) {
//
// If a new key is pressed, cancel all previous inputs.
//
for (Index = 0; Index < ARRAY_SIZE (mKeyStrokeInfo); ++Index) {
mKeyStrokeInfo[Index].CurrentStroke = FALSE;
}
//
// Insert the new key into a buffer
//
for (Index = 0; Index < ARRAY_SIZE (mKeyStrokeInfo); ++Index) {
if (mKeyStrokeInfo[Index].AppleKeyCode == 0) {
KeyInfo = &mKeyStrokeInfo[Index];
KeyInfo->AppleKeyCode = KeyCodes[NewKeyIndex];
KeyInfo->CurrentStroke = TRUE;
KeyInfo->NumberOfStrokes = 0;
break;
}
}
// CHECKME: is it intentional?
break;
}
}
KeyInfo = InternalGetCurrentStroke ();
Status = EFI_NOT_READY;
if ((KeyInfo != NULL) || (mModifiers != Modifiers)) {
mModifiers = Modifiers;
//
// Verify the timeframe the key has been pressed.
//
if (KeyInfo != NULL) {
AcceptStroke = (BOOLEAN)(
(KeyInfo->NumberOfStrokes < mKeyInitialDelay)
? (KeyInfo->NumberOfStrokes == 0)
: (((KeyInfo->NumberOfStrokes - mKeyInitialDelay) % mKeySubsequentDelay) == 0)
);
if (AcceptStroke) {
*NumberOfKeyCodes = 1;
*KeyCodes = KeyInfo->AppleKeyCode;
Shifted = (BOOLEAN)(
(IS_APPLE_KEY_LETTER (KeyInfo->AppleKeyCode) && CLockOn)
!= ((mModifiers & APPLE_MODIFIERS_SHIFT) != 0)
);
EventInputKeyFromAppleKeyCode (
KeyInfo->AppleKeyCode,
Key,
Shifted
);
Status = EFI_SUCCESS;
}
} else {
//
// Report that no keys were pressed.
//
*NumberOfKeyCodes = 0;
Status = EFI_SUCCESS;
}
}
return Status;
}
// CreateAppleKeyCodeDescriptorsFromKeyStrokes
STATIC
EFI_STATUS
InternalAppleEventDataFromCurrentKeyStroke (
IN OUT APPLE_EVENT_DATA *EventData,
IN OUT APPLE_MODIFIER_MAP *Modifiers
)
{
EFI_STATUS Status;
EFI_INPUT_KEY InputKey;
APPLE_KEY_CODE *KeyCodes;
APPLE_MODIFIER_MAP AppleModifiers;
UINTN NumberOfKeyCodes;
EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl;
EFI_CONSOLE_CONTROL_SCREEN_MODE Mode;
UINTN Index;
DEBUG ((DEBUG_VERBOSE, "InternalAppleEventDataFromCurrentKeyStroke\n"));
ZeroMem (&InputKey, sizeof (InputKey));
KeyCodes = NULL;
Status = EFI_UNSUPPORTED;
if ( (mKeyMapAggregator != NULL)
&& (EventData != NULL)
&& (Modifiers != NULL))
{
AppleModifiers = 0;
NumberOfKeyCodes = 0;
InternalGetAppleKeyStrokes (
&AppleModifiers,
&NumberOfKeyCodes,
&KeyCodes
);
if (!mGraphicsInputMirroring) {
//
// Apple OEM AppleEvent unconditionally includes this logic, but
// when an AppleEvent handler such as CrScreenshotDxe is active
// this code will run and (not entirely consistently across different
// firmware) may prevent keystrokes from reaching ConIn-based UEFI GUI
// apps such as Windows BitLocker.
// REF: https://github.com/acidanthera/bugtracker/issues/1716
//
Mode = EfiConsoleControlScreenGraphics;
Status = gBS->LocateProtocol (
&gEfiConsoleControlProtocolGuid,
NULL,
(VOID *)&ConsoleControl
);
if (!EFI_ERROR (Status)) {
ConsoleControl->GetMode (ConsoleControl, &Mode, NULL, NULL);
}
if (Mode == EfiConsoleControlScreenGraphics) {
for (Index = 0; Index < (NumberOfKeyCodes + 1); ++Index) {
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey);
if (EFI_ERROR (Status)) {
break;
}
}
}
}
*Modifiers = AppleModifiers;
Status = InternalGetCurrentKeyStroke (
AppleModifiers,
&NumberOfKeyCodes,
KeyCodes,
&InputKey
);
if (!EFI_ERROR (Status)) {
Status = EFI_SUCCESS;
if (NumberOfKeyCodes > 0) {
InternalAppleKeyEventDataFromInputKey (EventData, KeyCodes, &InputKey);
}
}
if (KeyCodes) {
FreePool (KeyCodes);
}
}
return Status;
}
// InternalKeyStrokePollNotifyFunction
STATIC
VOID
EFIAPI
InternalKeyStrokePollNotifyFunction (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
APPLE_EVENT_DATA EventData;
APPLE_MODIFIER_MAP Modifiers;
APPLE_MODIFIER_MAP PartialModifers;
DEBUG ((DEBUG_VERBOSE, "InternalKeyStrokePollNotifyFunction\n"));
EventData.KeyData = NULL;
Modifiers = 0;
Status = InternalAppleEventDataFromCurrentKeyStroke (
&EventData,
&Modifiers
);
if (!EFI_ERROR (Status)) {
if (EventData.KeyData != NULL) {
EventCreateEventQueue (EventData, APPLE_EVENT_TYPE_KEY_DOWN, Modifiers);
}
if (mPreviousModifiers != Modifiers) {
PartialModifers = ((mPreviousModifiers ^ Modifiers) & mPreviousModifiers);
if (PartialModifers != 0) {
EventData.KeyData = NULL;
EventCreateEventQueue (
EventData,
APPLE_EVENT_TYPE_MODIFIER_UP,
PartialModifers
);
}
PartialModifers = ((mPreviousModifiers ^ Modifiers) & Modifiers);
if (PartialModifers != 0) {
EventData.KeyData = NULL;
EventCreateEventQueue (
EventData,
APPLE_EVENT_TYPE_MODIFIER_DOWN,
PartialModifers
);
}
mPreviousModifiers = Modifiers;
}
}
}
// InternalInitializeKeyHandler
STATIC
VOID
InternalInitializeKeyHandler (
VOID
)
{
DEBUG ((DEBUG_VERBOSE, "InternalInitializeKeyHandler\n"));
if (!mInitialized) {
mInitialized = TRUE;
ZeroMem ((VOID *)&mKeyStrokeInfo[0], sizeof (mKeyStrokeInfo));
mModifiers = 0;
mCLockOn = FALSE;
mCLockChanged = FALSE;
}
}
// EventCreateKeyStrokePollEvent
EFI_STATUS
EventCreateKeyStrokePollEvent (
VOID
)
{
EFI_STATUS Status;
DEBUG ((DEBUG_VERBOSE, "EventCreateKeyStrokePollEvent\n"));
Status = gBS->LocateProtocol (
&gAppleKeyMapAggregatorProtocolGuid,
NULL,
(VOID **)&mKeyMapAggregator
);
if (!EFI_ERROR (Status)) {
InternalInitializeKeyHandler ();
mKeyStrokePollEvent = EventLibCreateNotifyTimerEvent (
InternalKeyStrokePollNotifyFunction,
NULL,
KEY_STROKE_POLL_FREQUENCY,
TRUE
);
Status = ((mKeyStrokePollEvent == NULL)
? EFI_OUT_OF_RESOURCES
: EFI_SUCCESS);
}
return Status;
}
// EventCancelKeyStrokePollEvent
VOID
EventCancelKeyStrokePollEvent (
VOID
)
{
DEBUG ((DEBUG_VERBOSE, "EventCancelKeyStrokePollEvent\n"));
EventLibCancelEvent (mKeyStrokePollEvent);
mKeyStrokePollEvent = NULL;
}
// EventIsCapsLockOnImpl
/** Retrieves the state of the CapsLock key.
@param[in,out] CLockOn This parameter indicates the state of the CapsLock
key.
@retval EFI_SUCCESS The CapsLock state was successfully returned
in CLockOn.
@retval EFI_INVALID_PARAMETER CLockOn is NULL.
**/
EFI_STATUS
EFIAPI
EventIsCapsLockOnImpl (
IN OUT BOOLEAN *CLockOn
)
{
EFI_STATUS Status;
DEBUG ((DEBUG_VERBOSE, "EventIsCapsLockOnImpl\n"));
Status = EFI_INVALID_PARAMETER;
if (CLockOn != NULL) {
*CLockOn = mCLockOn;
Status = EFI_SUCCESS;
}
return Status;
}