/** @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 #include #include #include #include #include #include "AppleEventInternal.h" // APPLE_EVENT_HANDLE_PRIVATE_SIGNATURE #define APPLE_EVENT_HANDLE_PRIVATE_SIGNATURE \ SIGNATURE_32 ('A', 'L', 's', 't') // APPLE_EVENT_HANDLE_PRIVATE_FROM_LIST_ENTRY #define APPLE_EVENT_HANDLE_PRIVATE_FROM_LIST_ENTRY(Handle) \ CR ( \ (Handle), \ APPLE_EVENT_HANDLE_PRIVATE, \ Link, \ APPLE_EVENT_HANDLE_PRIVATE_SIGNATURE \ ) // APPLE_EVENT_HANDLE_PRIVATE typedef struct { UINT32 Signature; ///< LIST_ENTRY Link; ///< BOOLEAN Ready; ///< BOOLEAN Registered; ///< APPLE_EVENT_TYPE EventType; ///< APPLE_EVENT_NOTIFY_FUNCTION NotifyFunction; ///< VOID *NotifyContext; ///< CHAR8 *Name; ///< } APPLE_EVENT_HANDLE_PRIVATE; // mEventHandles STATIC LIST_ENTRY mEventHandles = INITIALIZE_LIST_HEAD_VARIABLE (mEventHandles); // mNumberOfEventHandles STATIC UINTN mNumberOfEventHandles = 0; // EventLibCreateTimerEvent EFI_EVENT EventLibCreateTimerEvent ( IN EFI_EVENT_NOTIFY NotifyFunction, IN VOID *NotifyContext, IN UINT64 TriggerTime, IN BOOLEAN SignalPeriodic, IN EFI_TPL NotifyTpl ) { EFI_EVENT Event; EFI_STATUS Status; DEBUG ((DEBUG_VERBOSE, "EventLibCreateTimerEvent\n")); Event = NULL; if (NotifyTpl >= TPL_CALLBACK) { Status = gBS->CreateEvent ( ((NotifyFunction != NULL) ? (EVT_TIMER | EVT_NOTIFY_SIGNAL) : EVT_TIMER), NotifyTpl, NotifyFunction, NotifyContext, &Event ); if (!EFI_ERROR (Status)) { Status = gBS->SetTimer ( Event, (SignalPeriodic ? TimerPeriodic : TimerRelative), TriggerTime ); if (EFI_ERROR (Status)) { gBS->CloseEvent (Event); Event = NULL; } } } return Event; } // EventLibCreateNotifyTimerEvent EFI_EVENT EventLibCreateNotifyTimerEvent ( IN EFI_EVENT_NOTIFY NotifyFunction, IN VOID *NotifyContext, IN UINT64 TriggerTime, IN BOOLEAN SignalPeriodic ) { DEBUG ((DEBUG_VERBOSE, "EventLibCreateNotifyTimerEvent\n")); return EventLibCreateTimerEvent ( NotifyFunction, NotifyContext, TriggerTime, SignalPeriodic, TPL_CALLBACK ); } // EventLibCancelEvent VOID EventLibCancelEvent ( IN EFI_EVENT Event ) { EFI_STATUS Status; DEBUG ((DEBUG_VERBOSE, "EventLibCancelEvent\n")); Status = gBS->SetTimer (Event, TimerCancel, 0); if (!EFI_ERROR (Status)) { gBS->CloseEvent (Event); } } // EventSignalEvents VOID EventSignalEvents ( IN APPLE_EVENT_INFORMATION *EventInformation ) { LIST_ENTRY *EventHandleEntry; APPLE_EVENT_HANDLE_PRIVATE *EventHandle; DEBUG ((DEBUG_VERBOSE, "EventSignalEvents\n")); EventHandleEntry = GetFirstNode (&mEventHandles); while (!IsNull (&mEventHandles, EventHandleEntry)) { EventHandle = APPLE_EVENT_HANDLE_PRIVATE_FROM_LIST_ENTRY ( EventHandleEntry ); if ( EventHandle->Registered && EventHandle->Ready && ((EventInformation->EventType & EventHandle->EventType) != 0) && (EventHandle->NotifyFunction != NULL)) { EventHandle->NotifyFunction ( EventInformation, EventHandle->NotifyContext ); } EventHandleEntry = GetNextNode (&mEventHandles, EventHandleEntry); } } // InternalFlagAllEventsReady VOID InternalFlagAllEventsReady ( VOID ) { LIST_ENTRY *EventHandleEntry; APPLE_EVENT_HANDLE_PRIVATE *EventHandle; DEBUG ((DEBUG_VERBOSE, "InternalFlagAllEventsReady\n")); EventHandleEntry = GetFirstNode (&mEventHandles); if (!IsListEmpty (&mEventHandles)) { do { EventHandle = APPLE_EVENT_HANDLE_PRIVATE_FROM_LIST_ENTRY ( EventHandleEntry ); EventHandle->Ready = TRUE; EventHandleEntry = GetNextNode (&mEventHandles, EventHandleEntry); } while (!IsNull (&mEventHandles, EventHandleEntry)); } } // InternalSignalEvents VOID InternalSignalEvents ( IN APPLE_EVENT_INFORMATION *Information ) { LIST_ENTRY *EventHandleEntry; APPLE_EVENT_HANDLE_PRIVATE *EventHandle; DEBUG ((DEBUG_VERBOSE, "InternalSignalEvents\n")); EventHandleEntry = GetFirstNode (&mEventHandles); if (!IsListEmpty (&mEventHandles)) { do { EventHandle = APPLE_EVENT_HANDLE_PRIVATE_FROM_LIST_ENTRY ( EventHandleEntry ); if ( EventHandle->Registered && EventHandle->Ready && (EventHandle->EventType & Information->EventType) && (EventHandle->NotifyFunction != NULL)) { EventHandle->NotifyFunction ( Information, EventHandle->NotifyContext ); } EventHandleEntry = GetNextNode (&mEventHandles, EventHandleEntry); } while (!IsNull (&mEventHandles, EventHandleEntry)); } } // InternalRemoveUnregisteredEvents VOID InternalRemoveUnregisteredEvents ( VOID ) { LIST_ENTRY *EventHandleEntry; LIST_ENTRY *NextEventHandleEntry; APPLE_EVENT_HANDLE_PRIVATE *EventHandle; DEBUG ((DEBUG_VERBOSE, "InternalRemoveUnregisteredEvents\n")); EventHandleEntry = GetFirstNode (&mEventHandles); if (!IsListEmpty (&mEventHandles)) { do { NextEventHandleEntry = GetNextNode (&mEventHandles, EventHandleEntry); EventHandle = APPLE_EVENT_HANDLE_PRIVATE_FROM_LIST_ENTRY ( EventHandleEntry ); if (!EventHandle->Registered) { if (EventHandle->Name != NULL) { FreePool ((VOID *)EventHandle->Name); } RemoveEntryList (&EventHandle->Link); FreePool ((VOID *)EventHandle); } EventHandleEntry = NextEventHandleEntry; } while (!IsNull (&mEventHandles, NextEventHandleEntry)); } } // InternalCreatePollEvents STATIC EFI_STATUS InternalCreatePollEvents ( VOID ) { EFI_STATUS Status; DEBUG ((DEBUG_VERBOSE, "InternalCreatePollEvents\n")); Status = EventCreateSimplePointerPollEvent (); if (!EFI_ERROR (Status)) { Status = EventCreateKeyStrokePollEvent (); if (EFI_ERROR (Status)) { EventCancelSimplePointerPollEvent (); } } return Status; } // InternalCancelPollEvents VOID InternalCancelPollEvents ( VOID ) { DEBUG ((DEBUG_VERBOSE, "InternalCancelPollEvents\n")); EventCancelSimplePointerPollEvent (); EventCancelKeyStrokePollEvent (); } // EventRegisterHandler EFI_STATUS EFIAPI EventRegisterHandler ( IN APPLE_EVENT_TYPE Type, IN APPLE_EVENT_NOTIFY_FUNCTION NotifyFunction, OUT APPLE_EVENT_HANDLE *Handle, IN VOID *NotifyContext ) { EFI_STATUS Status; APPLE_EVENT_HANDLE_PRIVATE *EventHandle; DEBUG ((DEBUG_VERBOSE, "EventRegisterHandler\n")); Status = EFI_INVALID_PARAMETER; if ( (Handle != NULL) && (NotifyFunction != NULL) && (Type != APPLE_EVENT_TYPE_NONE)) { *Handle = NULL; InternalRemoveUnregisteredEvents (); Status = EFI_SUCCESS; if (mNumberOfEventHandles == 0) { Status = InternalCreatePollEvents (); if (EFI_ERROR (Status)) { goto Done; } } EventHandle = AllocatePool (sizeof (*EventHandle)); Status = EFI_OUT_OF_RESOURCES; if (EventHandle != NULL) { EventHandle->Signature = APPLE_EVENT_HANDLE_PRIVATE_SIGNATURE; EventHandle->Ready = FALSE; EventHandle->Registered = TRUE; EventHandle->EventType = Type; EventHandle->NotifyFunction = NotifyFunction; EventHandle->NotifyContext = NotifyContext; EventHandle->Name = NULL; ++mNumberOfEventHandles; InsertTailList (&mEventHandles, &EventHandle->Link); *Handle = EventHandle; Status = EFI_SUCCESS; } } Done: return Status; } // EventUnregisterHandler EFI_STATUS EFIAPI EventUnregisterHandler ( IN APPLE_EVENT_HANDLE Handle ) { EFI_STATUS Status; LIST_ENTRY *EventHandleEntry; APPLE_EVENT_HANDLE_PRIVATE *EventHandle; DEBUG ((DEBUG_VERBOSE, "EventUnregisterHandler\n")); Status = EFI_INVALID_PARAMETER; EventHandleEntry = GetFirstNode (&mEventHandles); while (!IsNull (&mEventHandles, EventHandleEntry)) { EventHandle = APPLE_EVENT_HANDLE_PRIVATE_FROM_LIST_ENTRY ( EventHandleEntry ); if ( ((UINTN)EventHandle == (UINTN)Handle) || ((UINTN)Handle == (UINTN)-1)) { EventHandle->Registered = FALSE; --mNumberOfEventHandles; Status = EFI_SUCCESS; if ((UINTN)Handle != (UINTN)-1) { break; } } EventHandleEntry = GetNextNode (&mEventHandles, EventHandleEntry); } if (mNumberOfEventHandles == 0) { InternalCancelPollEvents (); } return Status; } // EventSetCursorPosition /** This function is used to change the position of the cursor on the screen. @param[in] Position The position where to position the cursor. @retval EFI_INVALID_PARAMETER Position is invalid. **/ EFI_STATUS EFIAPI EventSetCursorPosition ( IN DIMENSION *Position ) { DEBUG ((DEBUG_VERBOSE, "EventSetCursorPosition\n")); return EventSetCursorPositionImpl (Position); } // EventSetEventName /** This function is used to assign a name to an event. @param[in,out] Handle @param[in] Name @retval EFI_SUCCESS The event name was assigned successfully. @retval EFI_INVALID_PARAMETER EventHandle or EventName is NULL. @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate the event name. **/ EFI_STATUS EFIAPI EventSetEventName ( IN OUT APPLE_EVENT_HANDLE Handle, IN CHAR8 *Name ) { EFI_STATUS Status; UINTN AllocationSize; CHAR8 *EventName; DEBUG ((DEBUG_VERBOSE, "EventSetEventName\n")); Status = EFI_INVALID_PARAMETER; if ((Handle != NULL) && (Name != NULL)) { AllocationSize = AsciiStrSize (Name); EventName = AllocateZeroPool (AllocationSize); ((APPLE_EVENT_HANDLE_PRIVATE *)Handle)->Name = EventName; Status = EFI_OUT_OF_RESOURCES; if (EventName != NULL) { AsciiStrCpyS (EventName, AllocationSize, Name); Status = EFI_SUCCESS; } } return Status; } // 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 EventIsCapsLockOn ( IN OUT BOOLEAN *CLockOn ) { DEBUG ((DEBUG_VERBOSE, "EventIsCapsLockOn\n")); return EventIsCapsLockOnImpl (CLockOn); } // InternalUnregisterHandlers STATIC VOID InternalUnregisterHandlers ( VOID ) { DEBUG ((DEBUG_VERBOSE, "InternalUnregisterHandlers\n")); EventSimplePointerDesctructor (); InternalRemoveUnregisteredEvents (); if (!IsListEmpty (&mEventHandles)) { EventUnregisterHandler ((APPLE_EVENT_HANDLE_PRIVATE *)((UINTN)-1)); } } // mAppleEventProtocol STATIC APPLE_EVENT_PROTOCOL mAppleEventProtocol = { APPLE_EVENT_PROTOCOL_REVISION, EventRegisterHandler, EventUnregisterHandler, EventSetCursorPosition, EventSetEventName, EventIsCapsLockOn }; // AppleEventUnload EFI_STATUS EFIAPI AppleEventUnload ( VOID ) { EFI_STATUS Status; DEBUG ((DEBUG_VERBOSE, "AppleEventUnload\n")); EventCloseSimplePointerInstallNotifyEvent (); InternalSignalAndCloseQueueEvent (); InternalUnregisterHandlers (); InternalCancelPollEvents (); Status = gBS->UninstallMultipleProtocolInterfaces ( gImageHandle, &gAppleEventProtocolGuid, (VOID *)&mAppleEventProtocol ); return Status; } APPLE_EVENT_PROTOCOL * OcAppleEventInstallProtocol ( IN BOOLEAN Install, IN BOOLEAN Reinstall, IN BOOLEAN CustomDelays, IN UINT16 KeyInitialDelay, IN UINT16 KeySubsequentDelay, IN BOOLEAN GraphicsInputMirroring, IN UINT32 PointerPollMin, IN UINT32 PointerPollMax, IN UINT32 PointerPollMask, IN UINT16 PointerSpeedDiv, IN UINT16 PointerSpeedMul ) { EFI_STATUS Status; APPLE_EVENT_PROTOCOL *AppleEvent; DEBUG ((DEBUG_VERBOSE, "OcAppleEventInstallProtocol\n")); if (!Reinstall) { Status = gBS->LocateProtocol ( &gAppleEventProtocolGuid, NULL, (VOID *)&AppleEvent ); if (!EFI_ERROR (Status)) { if (AppleEvent->Revision < APPLE_EVENT_PROTOCOL_REVISION_MINIMUM) { DEBUG (( DEBUG_INFO, "OCAE: Not using OEM revision %u, does not meet required minimum %u\n", (UINT32)AppleEvent->Revision, (UINT32)APPLE_EVENT_PROTOCOL_REVISION_MINIMUM )); Reinstall = TRUE; } else { DEBUG (( DEBUG_INFO, "OCAE: Using OEM revision %u, meets required minimum %u\n", (UINT32)AppleEvent->Revision, (UINT32)APPLE_EVENT_PROTOCOL_REVISION_MINIMUM )); return AppleEvent; } } } if (!Install) { DEBUG ((DEBUG_INFO, "OCAE: Letting OEM protocol connect later\n")); return NULL; } Status = OcUninstallAllProtocolInstances (&gAppleEventProtocolGuid); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "OCAE: OEM uninstall failed: %r\n", Status)); return NULL; } InternalSetKeyBehaviour ( CustomDelays, KeyInitialDelay, KeySubsequentDelay, GraphicsInputMirroring ); InternalSetPointerPolling (PointerPollMin, PointerPollMax, PointerPollMask); InternalSetPointerSpeed (PointerSpeedDiv, PointerSpeedMul); Status = gBS->InstallMultipleProtocolInterfaces ( &gImageHandle, &gAppleEventProtocolGuid, &mAppleEventProtocol, NULL ); if (!EFI_ERROR (Status)) { InternalCreateQueueEvent (); Status = EventCreateSimplePointerInstallNotifyEvent (); } if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "OCAE: Builtin install failed\n")); AppleEventUnload (); return NULL; } DEBUG ((DEBUG_INFO, "OCAE: Builtin installed\n")); return &mAppleEventProtocol; }