/*++ @file

Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
Portions copyright (c) 2010 0 2011,Apple Inc. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent


**/

#include "Gop.h"

BOOLEAN
GopPrivateIsKeyRegistered (
  IN EFI_KEY_DATA  *RegsiteredData,
  IN EFI_KEY_DATA  *InputData
  )

/*++

Routine Description:

Arguments:

  RegsiteredData    - A pointer to a buffer that is filled in with the keystroke
                      state data for the key that was registered.
  InputData         - A pointer to a buffer that is filled in with the keystroke
                      state data for the key that was pressed.

Returns:
  TRUE              - Key be pressed matches a registered key.
  FLASE             - Match failed.

**/
{
  ASSERT (RegsiteredData != NULL && InputData != NULL);

  if ((RegsiteredData->Key.ScanCode    != InputData->Key.ScanCode) ||
      (RegsiteredData->Key.UnicodeChar != InputData->Key.UnicodeChar))
  {
    return FALSE;
  }

  //
  // Assume KeyShiftState/KeyToggleState = 0 in Registered key data means these state could be ignored.
  //
  if ((RegsiteredData->KeyState.KeyShiftState != 0) &&
      (RegsiteredData->KeyState.KeyShiftState != InputData->KeyState.KeyShiftState))
  {
    return FALSE;
  }

  if ((RegsiteredData->KeyState.KeyToggleState != 0) &&
      (RegsiteredData->KeyState.KeyToggleState != InputData->KeyState.KeyToggleState))
  {
    return FALSE;
  }

  return TRUE;
}

VOID
EFIAPI
GopPrivateMakeCallbackFunction (
  IN VOID          *Context,
  IN EFI_KEY_DATA  *KeyData
  )
{
  LIST_ENTRY                       *Link;
  EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY  *CurrentNotify;
  GOP_PRIVATE_DATA                 *Private = (GOP_PRIVATE_DATA *)Context;

  KeyMapMake (KeyData);

  for (Link = Private->NotifyList.ForwardLink; Link != &Private->NotifyList; Link = Link->ForwardLink) {
    CurrentNotify = CR (
                      Link,
                      EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY,
                      NotifyEntry,
                      EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY_SIGNATURE
                      );
    if (GopPrivateIsKeyRegistered (&CurrentNotify->KeyData, KeyData)) {
      // We could be called at a high TPL so signal an event to call the registered function
      // at a lower TPL.
      gBS->SignalEvent (CurrentNotify->Event);
    }
  }
}

VOID
EFIAPI
GopPrivateBreakCallbackFunction (
  IN VOID          *Context,
  IN EFI_KEY_DATA  *KeyData
  )
{
  KeyMapBreak (KeyData);
}

//
// Simple Text In implementation.
//

/**
  Reset the input device and optionally run diagnostics

  @param  This                 Protocol instance pointer.
  @param  ExtendedVerification Driver may perform diagnostics on reset.

  @retval EFI_SUCCESS          The device was reset.
  @retval EFI_DEVICE_ERROR     The device is not functioning properly and could not be reset.

**/
EFI_STATUS
EFIAPI
EmuGopSimpleTextInReset (
  IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *This,
  IN BOOLEAN                         ExtendedVerification
  )
{
  GOP_PRIVATE_DATA  *Private;
  EFI_KEY_DATA      KeyData;
  EFI_TPL           OldTpl;

  Private = GOP_PRIVATE_DATA_FROM_TEXT_IN_THIS (This);
  if (Private->EmuGraphicsWindow == NULL) {
    return EFI_SUCCESS;
  }

  //
  // Enter critical section
  //
  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);

  //
  // A reset is draining the Queue
  //
  while (Private->EmuGraphicsWindow->GetKey (Private->EmuGraphicsWindow, &KeyData) == EFI_SUCCESS) {
  }

  //
  // Leave critical section and return
  //
  gBS->RestoreTPL (OldTpl);
  return EFI_SUCCESS;
}

/**
  Reads the next keystroke from the input device. The WaitForKey Event can
  be used to test for existence of a keystroke via WaitForEvent () call.

  @param  This  Protocol instance pointer.
  @param  Key   A pointer to a buffer that is filled in with the keystroke
                information for the key that was pressed.

  @retval EFI_SUCCESS      The keystroke information was returned.
  @retval EFI_NOT_READY    There was no keystroke data available.
  @retval EFI_DEVICE_ERROR The keystroke information was not returned due to
                           hardware errors.

**/
EFI_STATUS
EFIAPI
EmuGopSimpleTextInReadKeyStroke (
  IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *This,
  OUT EFI_INPUT_KEY                  *Key
  )
{
  GOP_PRIVATE_DATA  *Private;
  EFI_STATUS        Status;
  EFI_TPL           OldTpl;
  EFI_KEY_DATA      KeyData;

  Private = GOP_PRIVATE_DATA_FROM_TEXT_IN_THIS (This);
  if (Private->EmuGraphicsWindow == NULL) {
    return EFI_NOT_READY;
  }

  //
  // Enter critical section
  //
  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);

  Status = Private->EmuGraphicsWindow->GetKey (Private->EmuGraphicsWindow, &KeyData);
  if (!EFI_ERROR (Status)) {
    if ((KeyData.Key.ScanCode == 0) && (KeyData.Key.UnicodeChar == 0)) {
      // Modifier key was pressed
      Status = EFI_NOT_READY;
    } else {
      CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY));
    }
  }

  //
  // Leave critical section and return
  //
  gBS->RestoreTPL (OldTpl);

  return Status;
}

/**
  SimpleTextIn and SimpleTextInEx Notify Wait Event

  @param  Event                 Event whose notification function is being invoked.
  @param  Context               Pointer to GOP_PRIVATE_DATA.

**/
VOID
EFIAPI
EmuGopSimpleTextInWaitForKey (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  GOP_PRIVATE_DATA  *Private;
  EFI_STATUS        Status;
  EFI_TPL           OldTpl;

  Private = (GOP_PRIVATE_DATA *)Context;
  if (Private->EmuGraphicsWindow == NULL) {
    return;
  }

  //
  // Enter critical section
  //
  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);

  Status = Private->EmuGraphicsWindow->CheckKey (Private->EmuGraphicsWindow);
  if (!EFI_ERROR (Status)) {
    //
    // If a there is a key in the queue signal our event.
    //
    gBS->SignalEvent (Event);
  }

  //
  // Leave critical section and return
  //
  gBS->RestoreTPL (OldTpl);
}

//
// Simple Text Input Ex protocol functions
//

/**
  The Reset() function resets the input device hardware. As part
  of initialization process, the firmware/device will make a quick
  but reasonable attempt to verify that the device is functioning.
  If the ExtendedVerification flag is TRUE the firmware may take
  an extended amount of time to verify the device is operating on
  reset. Otherwise the reset operation is to occur as quickly as
  possible. The hardware verification process is not defined by
  this specification and is left up to the platform firmware or
  driver to implement.

  @param This                 A pointer to the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL instance.

  @param ExtendedVerification Indicates that the driver may
                              perform a more exhaustive
                              verification operation of the
                              device during reset.


  @retval EFI_SUCCESS       The device was reset.

  @retval EFI_DEVICE_ERROR  The device is not functioning
                            correctly and could not be reset.

**/
EFI_STATUS
EFIAPI
EmuGopSimpleTextInExResetEx (
  IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
  IN BOOLEAN                            ExtendedVerification
  )

/*++

  Routine Description:
    Reset the input device and optionaly run diagnostics

  Arguments:
    This                 - Protocol instance pointer.
    ExtendedVerification - Driver may perform diagnostics on reset.

  Returns:
    EFI_SUCCESS           - The device was reset.

**/
{
  return EFI_SUCCESS;
}

/**
  The function reads the next keystroke from the input device. If
  there is no pending keystroke the function returns
  EFI_NOT_READY. If there is a pending keystroke, then
  KeyData.Key.ScanCode is the EFI scan code defined in Error!
  Reference source not found. The KeyData.Key.UnicodeChar is the
  actual printable character or is zero if the key does not
  represent a printable character (control key, function key,
  etc.). The KeyData.KeyState is shift state for the character
  reflected in KeyData.Key.UnicodeChar or KeyData.Key.ScanCode .
  When interpreting the data from this function, it should be
  noted that if a class of printable characters that are
  normally adjusted by shift modifiers (e.g. Shift Key + "f"
  key) would be presented solely as a KeyData.Key.UnicodeChar
  without the associated shift state. So in the previous example
  of a Shift Key + "f" key being pressed, the only pertinent
  data returned would be KeyData.Key.UnicodeChar with the value
  of "F". This of course would not typically be the case for
  non-printable characters such as the pressing of the Right
  Shift Key + F10 key since the corresponding returned data
  would be reflected both in the KeyData.KeyState.KeyShiftState
  and KeyData.Key.ScanCode values. UEFI drivers which implement
  the EFI_SIMPLE_TEXT_INPUT_EX protocol are required to return
  KeyData.Key and KeyData.KeyState values. These drivers must
  always return the most current state of
  KeyData.KeyState.KeyShiftState and
  KeyData.KeyState.KeyToggleState. It should also be noted that
  certain input devices may not be able to produce shift or toggle
  state information, and in those cases the high order bit in the
  respective Toggle and Shift state fields should not be active.


  @param This     A pointer to the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL instance.

  @param KeyData  A pointer to a buffer that is filled in with
                  the keystroke state data for the key that was
                  pressed.


  @retval EFI_SUCCESS     The keystroke information was
                          returned.

  @retval EFI_NOT_READY   There was no keystroke data available.
                          EFI_DEVICE_ERROR The keystroke
                          information was not returned due to
                          hardware errors.


**/
EFI_STATUS
EFIAPI
EmuGopSimpleTextInExReadKeyStrokeEx (
  IN  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
  OUT EFI_KEY_DATA                       *KeyData
  )

/*++

  Routine Description:
    Reads the next keystroke from the input device. The WaitForKey Event can
    be used to test for existance of a keystroke via WaitForEvent () call.

  Arguments:
    This       - Protocol instance pointer.
    KeyData    - A pointer to a buffer that is filled in with the keystroke
                 state data for the key that was pressed.

  Returns:
    EFI_SUCCESS           - The keystroke information was returned.
    EFI_NOT_READY         - There was no keystroke data availiable.
    EFI_DEVICE_ERROR      - The keystroke information was not returned due to
                            hardware errors.
    EFI_INVALID_PARAMETER - KeyData is NULL.

**/
{
  EFI_STATUS        Status;
  GOP_PRIVATE_DATA  *Private;
  EFI_TPL           OldTpl;

  if (KeyData == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = GOP_PRIVATE_DATA_FROM_TEXT_IN_EX_THIS (This);
  if (Private->EmuGraphicsWindow == NULL) {
    return EFI_NOT_READY;
  }

  //
  // Enter critical section
  //
  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);

  Status = Private->EmuGraphicsWindow->GetKey (Private->EmuGraphicsWindow, KeyData);

  //
  // Leave critical section and return
  //
  gBS->RestoreTPL (OldTpl);

  return Status;
}

/**
  The SetState() function allows the input device hardware to
  have state settings adjusted.

  @param This           A pointer to the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL instance.

  @param KeyToggleState Pointer to the EFI_KEY_TOGGLE_STATE to
                        set the state for the input device.


  @retval EFI_SUCCESS       The device state was set appropriately.

  @retval EFI_DEVICE_ERROR  The device is not functioning
                            correctly and could not have the
                            setting adjusted.

  @retval EFI_UNSUPPORTED   The device does not support the
                            ability to have its state set.

**/
EFI_STATUS
EFIAPI
EmuGopSimpleTextInExSetState (
  IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
  IN EFI_KEY_TOGGLE_STATE               *KeyToggleState
  )
{
  GOP_PRIVATE_DATA  *Private;
  EFI_STATUS        Status;
  EFI_TPL           OldTpl;

  if (KeyToggleState == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = GOP_PRIVATE_DATA_FROM_TEXT_IN_EX_THIS (This);
  if (Private->EmuGraphicsWindow == NULL) {
    return EFI_NOT_READY;
  }

  if (((Private->KeyState.KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID) ||
      ((*KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID))
  {
    return EFI_UNSUPPORTED;
  }

  //
  // Enter critical section
  //
  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);

  Status = Private->EmuGraphicsWindow->KeySetState (Private->EmuGraphicsWindow, KeyToggleState);
  //
  // Leave critical section and return
  //
  gBS->RestoreTPL (OldTpl);

  return Status;
}

/**
  SimpleTextIn and SimpleTextInEx Notify Wait Event

  @param  Event                 Event whose notification function is being invoked.
  @param  Context               Pointer to GOP_PRIVATE_DATA.

**/
VOID
EFIAPI
EmuGopRegisterKeyCallback (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY  *ExNotify = (EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY *)Context;

  ExNotify->KeyNotificationFn (&ExNotify->KeyData);
}

/**
  The RegisterKeystrokeNotify() function registers a function
  which will be called when a specified keystroke will occur.

  @param This                     A pointer to the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL instance.

  @param KeyData                  A pointer to a buffer that is filled in with
                                  the keystroke information for the key that was
                                  pressed.

  @param KeyNotificationFunction  Points to the function to be
                                  called when the key sequence
                                  is typed specified by KeyData.


  @param NotifyHandle             Points to the unique handle assigned to
                                  the registered notification.

  @retval EFI_SUCCESS           The device state was set
                                appropriately.

  @retval EFI_OUT_OF_RESOURCES  Unable to allocate necessary
                                data structures.

**/
EFI_STATUS
EFIAPI
EmuGopSimpleTextInExRegisterKeyNotify (
  IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
  IN EFI_KEY_DATA                       *KeyData,
  IN EFI_KEY_NOTIFY_FUNCTION            KeyNotificationFunction,
  OUT VOID                              **NotifyHandle
  )
{
  EFI_STATUS                       Status;
  GOP_PRIVATE_DATA                 *Private;
  EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY  *CurrentNotify;
  LIST_ENTRY                       *Link;
  EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY  *NewNotify;

  if ((KeyData == NULL) || (KeyNotificationFunction == NULL) || (NotifyHandle == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  Private = GOP_PRIVATE_DATA_FROM_TEXT_IN_EX_THIS (This);

  //
  // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered.
  //
  for (Link = Private->NotifyList.ForwardLink; Link != &Private->NotifyList; Link = Link->ForwardLink) {
    CurrentNotify = CR (
                      Link,
                      EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY,
                      NotifyEntry,
                      EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY_SIGNATURE
                      );
    if (GopPrivateIsKeyRegistered (&CurrentNotify->KeyData, KeyData)) {
      if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) {
        *NotifyHandle = CurrentNotify->NotifyHandle;
        return EFI_SUCCESS;
      }
    }
  }

  //
  // Allocate resource to save the notification function
  //
  NewNotify = (EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY *)AllocateZeroPool (sizeof (EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY));
  if (NewNotify == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  NewNotify->Signature         = EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY_SIGNATURE;
  NewNotify->KeyNotificationFn = KeyNotificationFunction;
  NewNotify->NotifyHandle      = (EFI_HANDLE)NewNotify;
  CopyMem (&NewNotify->KeyData, KeyData, sizeof (KeyData));
  InsertTailList (&Private->NotifyList, &NewNotify->NotifyEntry);

  Status = gBS->CreateEvent (
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  EmuGopRegisterKeyCallback,
                  NewNotify,
                  &NewNotify->Event
                  );
  ASSERT_EFI_ERROR (Status);

  *NotifyHandle = NewNotify->NotifyHandle;

  return EFI_SUCCESS;
}

/**
  The UnregisterKeystrokeNotify() function removes the
  notification which was previously registered.

  @param This               A pointer to the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL instance.

  @param NotificationHandle The handle of the notification
                            function being unregistered.

  @retval EFI_SUCCESS The device state was set appropriately.

  @retval EFI_INVALID_PARAMETER The NotificationHandle is
                                invalid.

**/
EFI_STATUS
EFIAPI
EmuGopSimpleTextInExUnregisterKeyNotify (
  IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *This,
  IN VOID                               *NotificationHandle
  )

/*++

  Routine Description:
    Remove a registered notification function from a particular keystroke.

  Arguments:
    This                    - Protocol instance pointer.
    NotificationHandle      - The handle of the notification function being unregistered.

  Returns:
    EFI_SUCCESS             - The notification function was unregistered successfully.
    EFI_INVALID_PARAMETER   - The NotificationHandle is invalid.

**/
{
  GOP_PRIVATE_DATA                 *Private;
  LIST_ENTRY                       *Link;
  EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY  *CurrentNotify;

  if (NotificationHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (((EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY *)NotificationHandle)->Signature != EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  Private = GOP_PRIVATE_DATA_FROM_TEXT_IN_EX_THIS (This);

  for (Link = Private->NotifyList.ForwardLink; Link != &Private->NotifyList; Link = Link->ForwardLink) {
    CurrentNotify = CR (
                      Link,
                      EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY,
                      NotifyEntry,
                      EMU_GOP_SIMPLE_TEXTIN_EX_NOTIFY_SIGNATURE
                      );
    if (CurrentNotify->NotifyHandle == NotificationHandle) {
      //
      // Remove the notification function from NotifyList and free resources
      //
      RemoveEntryList (&CurrentNotify->NotifyEntry);

      gBS->CloseEvent (CurrentNotify->Event);

      gBS->FreePool (CurrentNotify);
      return EFI_SUCCESS;
    }
  }

  //
  // Can not find the specified Notification Handle
  //
  return EFI_INVALID_PARAMETER;
}

/**
  Initialize SimplelTextIn and SimpleTextInEx protocols in the Private
  context structure.

  @param  Private               Context structure to fill in.

  @return EFI_SUCCESS           Initialization was a success

**/
EFI_STATUS
EmuGopInitializeSimpleTextInForWindow (
  IN  GOP_PRIVATE_DATA  *Private
  )
{
  EFI_STATUS  Status;

  //
  // Initialize Simple Text In protoocol
  //
  Private->SimpleTextIn.Reset         = EmuGopSimpleTextInReset;
  Private->SimpleTextIn.ReadKeyStroke = EmuGopSimpleTextInReadKeyStroke;

  Status = gBS->CreateEvent (
                  EVT_NOTIFY_WAIT,
                  TPL_NOTIFY,
                  EmuGopSimpleTextInWaitForKey,
                  Private,
                  &Private->SimpleTextIn.WaitForKey
                  );
  ASSERT_EFI_ERROR (Status);

  //
  // Initialize Simple Text In Ex
  //

  Private->SimpleTextInEx.Reset               = EmuGopSimpleTextInExResetEx;
  Private->SimpleTextInEx.ReadKeyStrokeEx     = EmuGopSimpleTextInExReadKeyStrokeEx;
  Private->SimpleTextInEx.SetState            = EmuGopSimpleTextInExSetState;
  Private->SimpleTextInEx.RegisterKeyNotify   = EmuGopSimpleTextInExRegisterKeyNotify;
  Private->SimpleTextInEx.UnregisterKeyNotify = EmuGopSimpleTextInExUnregisterKeyNotify;

  Private->SimpleTextInEx.Reset (&Private->SimpleTextInEx, FALSE);

  InitializeListHead (&Private->NotifyList);

  Status = gBS->CreateEvent (
                  EVT_NOTIFY_WAIT,
                  TPL_NOTIFY,
                  EmuGopSimpleTextInWaitForKey,
                  Private,
                  &Private->SimpleTextInEx.WaitForKeyEx
                  );
  ASSERT_EFI_ERROR (Status);

  return Status;
}

//
// Simple Pointer implementation.
//

/**
  Resets the pointer device hardware.

  @param  This                  A pointer to the EFI_SIMPLE_POINTER_PROTOCOL
                                instance.
  @param  ExtendedVerification  Indicates that the driver may perform a more exhaustive
                                verification operation of the device during reset.

  @retval EFI_SUCCESS           The device was reset.
  @retval EFI_DEVICE_ERROR      The device is not functioning correctly and could not be reset.

**/
EFI_STATUS
EFIAPI
EmuGopSimplePointerReset (
  IN EFI_SIMPLE_POINTER_PROTOCOL  *This,
  IN BOOLEAN                      ExtendedVerification
  )
{
  GOP_PRIVATE_DATA          *Private;
  EFI_SIMPLE_POINTER_STATE  State;
  EFI_TPL                   OldTpl;

  Private = GOP_PRIVATE_DATA_FROM_POINTER_MODE_THIS (This);
  if (Private->EmuGraphicsWindow == NULL) {
    return EFI_SUCCESS;
  }

  //
  // Enter critical section
  //
  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);

  //
  // A reset is draining the Queue
  //
  while (Private->EmuGraphicsWindow->GetPointerState (Private->EmuGraphicsWindow, &State) == EFI_SUCCESS) {
  }

  //
  // Leave critical section and return
  //
  gBS->RestoreTPL (OldTpl);
  return EFI_SUCCESS;
}

/**
  Retrieves the current state of a pointer device.

  @param  This                  A pointer to the EFI_SIMPLE_POINTER_PROTOCOL
                                instance.
  @param  State                 A pointer to the state information on the pointer device.

  @retval EFI_SUCCESS           The state of the pointer device was returned in State.
  @retval EFI_NOT_READY         The state of the pointer device has not changed since the last call to
                                GetState().
  @retval EFI_DEVICE_ERROR      A device error occurred while attempting to retrieve the pointer device's
                                current state.

**/
EFI_STATUS
EFIAPI
EmuGopSimplePointerGetState (
  IN EFI_SIMPLE_POINTER_PROTOCOL   *This,
  IN OUT EFI_SIMPLE_POINTER_STATE  *State
  )
{
  GOP_PRIVATE_DATA  *Private;
  EFI_STATUS        Status;
  EFI_TPL           OldTpl;

  Private = GOP_PRIVATE_DATA_FROM_POINTER_MODE_THIS (This);
  if (Private->EmuGraphicsWindow == NULL) {
    return EFI_NOT_READY;
  }

  //
  // Enter critical section
  //
  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);

  Status = Private->EmuGraphicsWindow->GetPointerState (Private->EmuGraphicsWindow, State);
  //
  // Leave critical section and return
  //
  gBS->RestoreTPL (OldTpl);

  return Status;
}

/**
  SimplePointer Notify Wait Event

  @param  Event                 Event whose notification function is being invoked.
  @param  Context               Pointer to GOP_PRIVATE_DATA.

**/
VOID
EFIAPI
EmuGopSimplePointerWaitForInput (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  GOP_PRIVATE_DATA  *Private;
  EFI_STATUS        Status;
  EFI_TPL           OldTpl;

  Private = (GOP_PRIVATE_DATA *)Context;
  if (Private->EmuGraphicsWindow == NULL) {
    return;
  }

  //
  // Enter critical section
  //
  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);

  Status = Private->EmuGraphicsWindow->CheckPointer (Private->EmuGraphicsWindow);
  if (!EFI_ERROR (Status)) {
    //
    // If the pointer state has changed, signal our event.
    //
    gBS->SignalEvent (Event);
  }

  //
  // Leave critical section and return
  //
  gBS->RestoreTPL (OldTpl);
}

/**
  SimplePointer constructor

  @param  Private Context structure to fill in.

  @retval EFI_SUCCESS Constructor had success

**/
EFI_STATUS
EmuGopInitializeSimplePointerForWindow (
  IN  GOP_PRIVATE_DATA  *Private
  )
{
  EFI_STATUS  Status;

  //
  // Initialize Simple Pointer protoocol
  //
  Private->PointerMode.ResolutionX = 1;
  Private->PointerMode.ResolutionY = 1;
  Private->PointerMode.ResolutionZ = 1;
  Private->PointerMode.LeftButton  = TRUE;
  Private->PointerMode.RightButton = TRUE;

  Private->SimplePointer.Reset    = EmuGopSimplePointerReset;
  Private->SimplePointer.GetState = EmuGopSimplePointerGetState;
  Private->SimplePointer.Mode     = &Private->PointerMode;

  Status = gBS->CreateEvent (
                  EVT_NOTIFY_WAIT,
                  TPL_NOTIFY,
                  EmuGopSimplePointerWaitForInput,
                  Private,
                  &Private->SimplePointer.WaitForInput
                  );

  return Status;
}
