Home | History | Annotate | Download | only in WinNtGopDxe
      1 /** @file
      2 
      3 Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
      4 This program and the accompanying materials
      5 are licensed and made available under the terms and conditions of the BSD License
      6 which accompanies this distribution.  The full text of the license may be found at
      7 http://opensource.org/licenses/bsd-license.php
      8 
      9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     11 
     12 Module Name:
     13 
     14     WinNtGopScreen.c
     15 
     16 Abstract:
     17 
     18   This file produces the graphics abstration of GOP. It is called by
     19   WinNtGopDriver.c file which deals with the UEFI 2.0 driver model.
     20   This file just does graphics.
     21 
     22 
     23 **/
     24 
     25 #include "WinNtGop.h"
     26 
     27 EFI_WIN_NT_THUNK_PROTOCOL *mWinNt;
     28 DWORD                     mTlsIndex         = TLS_OUT_OF_INDEXES;
     29 DWORD                     mTlsIndexUseCount = 0;  // lets us know when we can free mTlsIndex.
     30 EFI_EVENT          mGopScreenExitBootServicesEvent;
     31 GOP_MODE_DATA mGopModeData[] = {
     32     {800, 600, 0, 0},
     33     {640, 480, 0, 0},
     34     {720, 400, 0, 0},
     35     {1024, 768, 0, 0},
     36     {1280, 1024, 0, 0}
     37     };
     38 
     39 EFI_STATUS
     40 WinNtGopStartWindow (
     41   IN  GOP_PRIVATE_DATA    *Private,
     42   IN  UINT32              HorizontalResolution,
     43   IN  UINT32              VerticalResolution,
     44   IN  UINT32              ColorDepth,
     45   IN  UINT32              RefreshRate
     46   );
     47 
     48 VOID
     49 EFIAPI
     50 KillNtGopThread (
     51   IN EFI_EVENT  Event,
     52   IN VOID       *Context
     53   );
     54 
     55 BOOLEAN
     56 WinNtGopConvertParamToEfiKeyShiftState (
     57   IN  GOP_PRIVATE_DATA  *Private,
     58   IN  WPARAM            *wParam,
     59   IN  LPARAM            *lParam,
     60   IN  BOOLEAN           Flag
     61   )
     62 {
     63   switch (*wParam) {
     64   //
     65   // BUGBUG: Only GetAsyncKeyState() and GetKeyState() can distinguish
     66   // left and right Ctrl, and Shift key.
     67   // Neither of the two is defined in EFI_WIN_NT_THUNK_PROTOCOL.
     68   // Therefor, we can not set the correct Shift state here.
     69   //
     70   case VK_SHIFT:
     71     if ((*lParam & GOP_EXTENDED_KEY) == GOP_EXTENDED_KEY) {
     72         Private->RightShift = Flag;
     73       } else {
     74         Private->LeftShift = Flag;
     75       }
     76     return TRUE;
     77 
     78   case VK_LSHIFT:
     79     Private->LeftShift = Flag;
     80     return TRUE;
     81 
     82   case VK_RSHIFT:
     83     Private->RightShift = Flag;
     84     return TRUE;
     85 
     86   case VK_CONTROL:
     87     if ((*lParam & GOP_EXTENDED_KEY) == GOP_EXTENDED_KEY) {
     88         Private->RightCtrl= Flag;
     89       } else {
     90         Private->LeftCtrl = Flag;
     91       }
     92     return TRUE;
     93 
     94   case VK_LCONTROL:
     95     Private->LeftCtrl = Flag;
     96     return TRUE;
     97 
     98   case VK_RCONTROL:
     99     Private->RightCtrl = Flag;
    100     return TRUE;
    101 
    102   case VK_LWIN:
    103     Private->LeftLogo   = Flag;
    104     return TRUE;
    105 
    106   case VK_RWIN:
    107     Private->RightLogo  = Flag;
    108     return TRUE;
    109 
    110   case VK_APPS:
    111     Private->Menu       = Flag;
    112     return TRUE;
    113   //
    114   // BUGBUG: PrintScreen/SysRq can not trigger WM_KEYDOWN message,
    115   // so SySReq shift state is not supported here.
    116   //
    117   case VK_PRINT:
    118     Private->SysReq     = Flag;
    119     return TRUE;
    120   //
    121   // For Alt Keystroke.
    122   //
    123   case VK_MENU:
    124     if ((*lParam & GOP_EXTENDED_KEY) == GOP_EXTENDED_KEY) {
    125         Private->RightAlt = Flag;
    126       } else {
    127         Private->LeftAlt = Flag;
    128       }
    129     return TRUE;
    130 
    131   default:
    132     return FALSE;
    133   }
    134 }
    135 
    136 BOOLEAN
    137 WinNtGopConvertParamToEfiKey (
    138   IN  GOP_PRIVATE_DATA  *Private,
    139   IN  WPARAM            *wParam,
    140   IN  LPARAM            *lParam,
    141   IN  EFI_INPUT_KEY     *Key
    142   )
    143 {
    144   BOOLEAN Flag;
    145   Flag = FALSE;
    146   switch (*wParam) {
    147   case VK_HOME:       Key->ScanCode = SCAN_HOME;      Flag = TRUE; break;
    148   case VK_END:        Key->ScanCode = SCAN_END;       Flag = TRUE; break;
    149   case VK_LEFT:       Key->ScanCode = SCAN_LEFT;      Flag = TRUE; break;
    150   case VK_RIGHT:      Key->ScanCode = SCAN_RIGHT;     Flag = TRUE; break;
    151   case VK_UP:         Key->ScanCode = SCAN_UP;        Flag = TRUE; break;
    152   case VK_DOWN:       Key->ScanCode = SCAN_DOWN;      Flag = TRUE; break;
    153   case VK_DELETE:     Key->ScanCode = SCAN_DELETE;    Flag = TRUE; break;
    154   case VK_INSERT:     Key->ScanCode = SCAN_INSERT;    Flag = TRUE; break;
    155   case VK_PRIOR:      Key->ScanCode = SCAN_PAGE_UP;   Flag = TRUE; break;
    156   case VK_NEXT:       Key->ScanCode = SCAN_PAGE_DOWN; Flag = TRUE; break;
    157   case VK_ESCAPE:     Key->ScanCode = SCAN_ESC;       Flag = TRUE; break;
    158 
    159   case VK_F1:         Key->ScanCode = SCAN_F1;    Flag = TRUE;     break;
    160   case VK_F2:         Key->ScanCode = SCAN_F2;    Flag = TRUE;     break;
    161   case VK_F3:         Key->ScanCode = SCAN_F3;    Flag = TRUE;     break;
    162   case VK_F4:         Key->ScanCode = SCAN_F4;    Flag = TRUE;     break;
    163   case VK_F5:         Key->ScanCode = SCAN_F5;    Flag = TRUE;     break;
    164   case VK_F6:         Key->ScanCode = SCAN_F6;    Flag = TRUE;     break;
    165   case VK_F7:         Key->ScanCode = SCAN_F7;    Flag = TRUE;     break;
    166   case VK_F8:         Key->ScanCode = SCAN_F8;    Flag = TRUE;     break;
    167   case VK_F9:         Key->ScanCode = SCAN_F9;    Flag = TRUE;     break;
    168   case VK_F11:        Key->ScanCode = SCAN_F11;   Flag = TRUE;     break;
    169   case VK_F12:        Key->ScanCode = SCAN_F12;   Flag = TRUE;     break;
    170 
    171   case VK_F13:        Key->ScanCode = SCAN_F13;   Flag = TRUE;     break;
    172   case VK_F14:        Key->ScanCode = SCAN_F14;   Flag = TRUE;     break;
    173   case VK_F15:        Key->ScanCode = SCAN_F15;   Flag = TRUE;     break;
    174   case VK_F16:        Key->ScanCode = SCAN_F16;   Flag = TRUE;     break;
    175   case VK_F17:        Key->ScanCode = SCAN_F17;   Flag = TRUE;     break;
    176   case VK_F18:        Key->ScanCode = SCAN_F18;   Flag = TRUE;     break;
    177   case VK_F19:        Key->ScanCode = SCAN_F19;   Flag = TRUE;     break;
    178   case VK_F20:        Key->ScanCode = SCAN_F20;   Flag = TRUE;     break;
    179   case VK_F21:        Key->ScanCode = SCAN_F21;   Flag = TRUE;     break;
    180   case VK_F22:        Key->ScanCode = SCAN_F22;   Flag = TRUE;     break;
    181   case VK_F23:        Key->ScanCode = SCAN_F23;   Flag = TRUE;     break;
    182   case VK_F24:        Key->ScanCode = SCAN_F24;   Flag = TRUE;     break;
    183   case VK_PAUSE:      Key->ScanCode = SCAN_PAUSE; Flag = TRUE;     break;
    184 
    185   //
    186   // Set toggle state
    187   //
    188   case VK_NUMLOCK:
    189     Private->NumLock    = (BOOLEAN)(!Private->NumLock);
    190     Flag = TRUE;
    191     break;
    192   case VK_SCROLL:
    193     Private->ScrollLock = (BOOLEAN)(!Private->ScrollLock);
    194     Flag = TRUE;
    195     break;
    196   case VK_CAPITAL:
    197     Private->CapsLock   = (BOOLEAN)(!Private->CapsLock);
    198     Flag = TRUE;
    199     break;
    200   }
    201 
    202   return (WinNtGopConvertParamToEfiKeyShiftState (Private, wParam, lParam, TRUE)) == TRUE ? TRUE : Flag;
    203 }
    204 
    205 
    206 //
    207 // GOP Protocol Member Functions
    208 //
    209 
    210 
    211 /**
    212   Graphics Output protocol interface to get video mode
    213 
    214   @param  This                   Protocol instance pointer.
    215   @param  ModeNumber             The mode number to return information on.
    216   @param  Info                   Caller allocated buffer that returns information
    217                                  about ModeNumber.
    218   @param  SizeOfInfo             A pointer to the size, in bytes, of the Info
    219                                  buffer.
    220 
    221   @retval EFI_SUCCESS            Mode information returned.
    222   @retval EFI_BUFFER_TOO_SMALL   The Info buffer was too small.
    223   @retval EFI_DEVICE_ERROR       A hardware error occurred trying to retrieve the
    224                                  video mode.
    225   @retval EFI_NOT_STARTED        Video display is not initialized. Call SetMode ()
    226   @retval EFI_INVALID_PARAMETER  One of the input args was NULL.
    227 
    228 **/
    229 EFI_STATUS
    230 EFIAPI
    231 WinNtGopQuerytMode (
    232   IN  EFI_GRAPHICS_OUTPUT_PROTOCOL          *This,
    233   IN  UINT32                                ModeNumber,
    234   OUT UINTN                                 *SizeOfInfo,
    235   OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  **Info
    236   )
    237 {
    238   GOP_PRIVATE_DATA  *Private;
    239 
    240   Private = GOP_PRIVATE_DATA_FROM_THIS (This);
    241 
    242   if (Info == NULL || SizeOfInfo == NULL || (UINTN) ModeNumber >= This->Mode->MaxMode) {
    243     return EFI_INVALID_PARAMETER;
    244   }
    245 
    246   *Info = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));
    247   if (*Info == NULL) {
    248     return EFI_OUT_OF_RESOURCES;
    249   }
    250 
    251   *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
    252 
    253   (*Info)->Version = 0;
    254   (*Info)->HorizontalResolution = Private->ModeData[ModeNumber].HorizontalResolution;
    255   (*Info)->VerticalResolution   = Private->ModeData[ModeNumber].VerticalResolution;
    256   (*Info)->PixelFormat = PixelBltOnly;
    257   (*Info)->PixelsPerScanLine = (*Info)->HorizontalResolution;
    258 
    259   return EFI_SUCCESS;
    260 }
    261 
    262 
    263 /**
    264   Graphics Output protocol interface to set video mode
    265 
    266   @param  This                   Protocol instance pointer.
    267   @param  ModeNumber             The mode number to be set.
    268 
    269   @retval EFI_SUCCESS            Graphics mode was changed.
    270   @retval EFI_DEVICE_ERROR       The device had an error and could not complete the
    271                                  request.
    272   @retval EFI_UNSUPPORTED        ModeNumber is not supported by this device.
    273 
    274 **/
    275 EFI_STATUS
    276 EFIAPI
    277 WinNtGopSetMode (
    278   IN  EFI_GRAPHICS_OUTPUT_PROTOCOL * This,
    279   IN  UINT32                       ModeNumber
    280   )
    281 {
    282   EFI_STATUS                    Status;
    283   GOP_PRIVATE_DATA              *Private;
    284   GOP_MODE_DATA *ModeData;
    285   EFI_GRAPHICS_OUTPUT_BLT_PIXEL Fill;
    286   EFI_GRAPHICS_OUTPUT_BLT_PIXEL *NewFillLine;
    287   RECT                          Rect;
    288   UINTN                         Size;
    289   UINTN                         Width;
    290   UINTN                         Height;
    291 
    292   Private = GOP_PRIVATE_DATA_FROM_THIS (This);
    293 
    294   if (ModeNumber >= This->Mode->MaxMode) {
    295     return EFI_UNSUPPORTED;
    296   }
    297 
    298   ModeData = &Private->ModeData[ModeNumber];
    299   This->Mode->Mode = ModeNumber;
    300   Private->GraphicsOutput.Mode->Info->HorizontalResolution = ModeData->HorizontalResolution;
    301   Private->GraphicsOutput.Mode->Info->VerticalResolution = ModeData->VerticalResolution;
    302   Private->GraphicsOutput.Mode->Info->PixelsPerScanLine = ModeData->HorizontalResolution;
    303 
    304   if (Private->HardwareNeedsStarting) {
    305     Status = WinNtGopStartWindow (
    306               Private,
    307               ModeData->HorizontalResolution,
    308               ModeData->VerticalResolution,
    309               ModeData->ColorDepth,
    310               ModeData->RefreshRate
    311               );
    312     if (EFI_ERROR (Status)) {
    313       return EFI_DEVICE_ERROR;
    314     }
    315 
    316     Private->HardwareNeedsStarting = FALSE;
    317   } else {
    318     //
    319     // Change the resolution and resize of the window
    320     //
    321 
    322     //
    323     // Free the old buffer. We do not save the content of the old buffer since the
    324     // screen is to be cleared anyway. Clearing the screen is required by the EFI spec.
    325     // See UEFI spec -EFI_GRAPHICS_OUTPUT_PROTOCOL.SetMode()
    326     //
    327     Private->WinNtThunk->HeapFree (Private->WinNtThunk->GetProcessHeap (), 0, Private->VirtualScreenInfo);
    328 
    329     //
    330     // Allocate DIB frame buffer directly from NT for performance enhancement
    331     // This buffer is the virtual screen/frame buffer. This buffer is not the
    332     // same a a frame buffer. The first row of this buffer will be the bottom
    333     // line of the image. This is an artifact of the way we draw to the screen.
    334     //
    335     Size = ModeData->HorizontalResolution * ModeData->VerticalResolution * sizeof (RGBQUAD) + sizeof (BITMAPV4HEADER);
    336     Private->VirtualScreenInfo = Private->WinNtThunk->HeapAlloc (
    337                                                         Private->WinNtThunk->GetProcessHeap (),
    338                                                         HEAP_ZERO_MEMORY,
    339                                                         Size
    340                                                         );
    341 
    342     //
    343     // Update the virtual screen info data structure
    344     //
    345     Private->VirtualScreenInfo->bV4Size           = sizeof (BITMAPV4HEADER);
    346     Private->VirtualScreenInfo->bV4Width          = ModeData->HorizontalResolution;
    347     Private->VirtualScreenInfo->bV4Height         = ModeData->VerticalResolution;
    348     Private->VirtualScreenInfo->bV4Planes         = 1;
    349     Private->VirtualScreenInfo->bV4BitCount       = 32;
    350     //
    351     // uncompressed
    352     //
    353     Private->VirtualScreenInfo->bV4V4Compression  = BI_RGB;
    354 
    355     //
    356     // The rest of the allocated memory block is the virtual screen buffer
    357     //
    358     Private->VirtualScreen = (RGBQUAD *) (Private->VirtualScreenInfo + 1);
    359 
    360     //
    361     // Use the AdjuctWindowRect fuction to calculate the real width and height
    362     // of the new window including the border and caption
    363     //
    364     Rect.left   = 0;
    365     Rect.top    = 0;
    366     Rect.right  = ModeData->HorizontalResolution;
    367     Rect.bottom = ModeData->VerticalResolution;
    368 
    369     Private->WinNtThunk->AdjustWindowRect (&Rect, WS_OVERLAPPEDWINDOW, 0);
    370 
    371     Width   = Rect.right - Rect.left;
    372     Height  = Rect.bottom - Rect.top;
    373 
    374     //
    375     // Retrieve the original window position information
    376     //
    377     Private->WinNtThunk->GetWindowRect (Private->WindowHandle, &Rect);
    378 
    379     //
    380     // Adjust the window size
    381     //
    382     Private->WinNtThunk->MoveWindow (Private->WindowHandle, Rect.left, Rect.top, (INT32)Width, (INT32)Height, TRUE);
    383 
    384   }
    385 
    386   NewFillLine = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * ModeData->HorizontalResolution);
    387   if (NewFillLine == NULL) {
    388     return EFI_DEVICE_ERROR;
    389   }
    390 
    391   if (Private->FillLine != NULL) {
    392     FreePool (Private->FillLine);
    393   }
    394 
    395   Private->FillLine             = NewFillLine;
    396 
    397   Fill.Red                      = 0x00;
    398   Fill.Green                    = 0x00;
    399   Fill.Blue                     = 0x00;
    400   This->Blt (
    401           This,
    402           &Fill,
    403           EfiBltVideoFill,
    404           0,
    405           0,
    406           0,
    407           0,
    408           ModeData->HorizontalResolution,
    409           ModeData->VerticalResolution,
    410           ModeData->HorizontalResolution * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
    411           );
    412   return EFI_SUCCESS;
    413 }
    414 
    415 
    416 /**
    417   Blt pixels from the rectangle (Width X Height) formed by the BltBuffer
    418   onto the graphics screen starting a location (X, Y). (0, 0) is defined as
    419   the upper left hand side of the screen. (X, Y) can be outside of the
    420   current screen geometry and the BltBuffer will be cliped when it is
    421   displayed. X and Y can be negative or positive. If Width or Height is
    422   bigger than the current video screen the image will be clipped.
    423 
    424   @param  This                   Protocol instance pointer.
    425   @param  X                      X location on graphics screen.
    426   @param  Y                      Y location on the graphics screen.
    427   @param  Width                  Width of BltBuffer.
    428   @param  Height                 Hight of BltBuffer
    429   @param  BltOperation           Operation to perform on BltBuffer and video memory
    430   @param  BltBuffer              Buffer containing data to blt into video buffer.
    431                                  This  buffer has a size of
    432                                  Width*Height*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
    433   @param  SourceX                If the BltOperation is a EfiCopyBlt this is the
    434                                  source of the copy. For other BLT operations this
    435                                  argument is not used.
    436   @param  SourceX                If the BltOperation is a EfiCopyBlt this is the
    437                                  source of the copy. For other BLT operations this
    438                                  argument is not used.
    439 
    440   @retval EFI_SUCCESS            The palette is updated with PaletteArray.
    441   @retval EFI_INVALID_PARAMETER  BltOperation is not valid.
    442   @retval EFI_DEVICE_ERROR       A hardware error occured writting to the video
    443                                  buffer.
    444 
    445 **/
    446 // TODO:    SourceY - add argument and description to function comment
    447 // TODO:    DestinationX - add argument and description to function comment
    448 // TODO:    DestinationY - add argument and description to function comment
    449 // TODO:    Delta - add argument and description to function comment
    450 EFI_STATUS
    451 EFIAPI
    452 WinNtGopBlt (
    453   IN  EFI_GRAPHICS_OUTPUT_PROTOCOL                   *This,
    454   IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL                           *BltBuffer, OPTIONAL
    455   IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION                   BltOperation,
    456   IN  UINTN                                   SourceX,
    457   IN  UINTN                                   SourceY,
    458   IN  UINTN                                   DestinationX,
    459   IN  UINTN                                   DestinationY,
    460   IN  UINTN                                   Width,
    461   IN  UINTN                                   Height,
    462   IN  UINTN                                   Delta         OPTIONAL
    463   )
    464 {
    465   GOP_PRIVATE_DATA              *Private;
    466   EFI_TPL                       OriginalTPL;
    467   UINTN                         DstY;
    468   UINTN                         SrcY;
    469   RGBQUAD                       *VScreen;
    470   RGBQUAD                       *VScreenSrc;
    471   EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt;
    472   UINTN                         Index;
    473   RECT                          Rect;
    474   EFI_GRAPHICS_OUTPUT_BLT_PIXEL *FillPixel;
    475   UINT32                        VerticalResolution;
    476   UINT32                        HorizontalResolution;
    477 
    478   Private = GOP_PRIVATE_DATA_FROM_THIS (This);
    479 
    480   if ((BltOperation < 0) || (BltOperation >= EfiGraphicsOutputBltOperationMax)) {
    481     return EFI_INVALID_PARAMETER;
    482   }
    483 
    484   if (Width == 0 || Height == 0) {
    485     return EFI_INVALID_PARAMETER;
    486   }
    487   //
    488   // If Delta is zero, then the entire BltBuffer is being used, so Delta
    489   // is the number of bytes in each row of BltBuffer.  Since BltBuffer is Width pixels size,
    490   // the number of bytes in each row can be computed.
    491   //
    492   if (Delta == 0) {
    493     Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
    494   }
    495 
    496   //
    497   // We need to fill the Virtual Screen buffer with the blt data.
    498   // The virtual screen is upside down, as the first row is the bootom row of
    499   // the image.
    500   //
    501   VerticalResolution = This->Mode->Info->VerticalResolution;
    502   HorizontalResolution = This->Mode->Info->HorizontalResolution;
    503   if (BltOperation == EfiBltVideoToBltBuffer) {
    504 
    505     //
    506     // Video to BltBuffer: Source is Video, destination is BltBuffer
    507     //
    508     if (SourceY + Height > VerticalResolution) {
    509       return EFI_INVALID_PARAMETER;
    510     }
    511 
    512     if (SourceX + Width > HorizontalResolution) {
    513       return EFI_INVALID_PARAMETER;
    514     }
    515     //
    516     // We have to raise to TPL Notify, so we make an atomic write the frame buffer.
    517     // We would not want a timer based event (Cursor, ...) to come in while we are
    518     // doing this operation.
    519     //
    520     OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
    521 
    522     for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) {
    523       Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltBuffer + (DstY * Delta) + DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
    524       VScreen = &Private->VirtualScreen[(VerticalResolution - SrcY - 1) * HorizontalResolution + SourceX];
    525       CopyMem (Blt, VScreen, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * Width);
    526     }
    527   } else {
    528     //
    529     // BltBuffer to Video: Source is BltBuffer, destination is Video
    530     //
    531     if (DestinationY + Height > VerticalResolution) {
    532       return EFI_INVALID_PARAMETER;
    533     }
    534 
    535     if (DestinationX + Width > HorizontalResolution) {
    536       return EFI_INVALID_PARAMETER;
    537     }
    538 
    539     //
    540     // We have to raise to TPL Notify, so we make an atomic write the frame buffer.
    541     // We would not want a timer based event (Cursor, ...) to come in while we are
    542     // doing this operation.
    543     //
    544     OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
    545 
    546     if (BltOperation == EfiBltVideoFill) {
    547       FillPixel = BltBuffer;
    548       for (Index = 0; Index < Width; Index++) {
    549         Private->FillLine[Index] = *FillPixel;
    550       }
    551     }
    552 
    553     for (Index = 0; Index < Height; Index++) {
    554       if (DestinationY <= SourceY) {
    555         SrcY  = SourceY + Index;
    556         DstY  = DestinationY + Index;
    557       } else {
    558         SrcY  = SourceY + Height - Index - 1;
    559         DstY  = DestinationY + Height - Index - 1;
    560       }
    561 
    562       VScreen = &Private->VirtualScreen[(VerticalResolution - DstY - 1) * HorizontalResolution + DestinationX];
    563       switch (BltOperation) {
    564       case EfiBltBufferToVideo:
    565         Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltBuffer + (SrcY * Delta) + SourceX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
    566         CopyMem (VScreen, Blt, Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
    567         break;
    568 
    569       case EfiBltVideoToVideo:
    570         VScreenSrc = &Private->VirtualScreen[(VerticalResolution - SrcY - 1) * HorizontalResolution + SourceX];
    571         CopyMem (VScreen, VScreenSrc, Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
    572         break;
    573 
    574       case EfiBltVideoFill:
    575         CopyMem (VScreen, Private->FillLine, Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
    576         break;
    577       }
    578     }
    579   }
    580 
    581   if (BltOperation != EfiBltVideoToBltBuffer) {
    582     //
    583     // Mark the area we just blted as Invalid so WM_PAINT will update.
    584     //
    585     Rect.left   = (LONG)DestinationX;
    586     Rect.top    = (LONG)DestinationY;
    587     Rect.right  = (LONG)(DestinationX + Width);
    588     Rect.bottom = (LONG)(DestinationY + Height);
    589     Private->WinNtThunk->InvalidateRect (Private->WindowHandle, &Rect, FALSE);
    590 
    591     //
    592     // Send the WM_PAINT message to the thread that is drawing the window. We
    593     // are in the main thread and the window drawing is in a child thread.
    594     // There is a child thread per window. We have no CriticalSection or Mutex
    595     // since we write the data and the other thread displays the data. While
    596     // we may miss some data for a short period of time this is no different than
    597     // a write combining on writes to a frame buffer.
    598     //
    599 
    600     Private->WinNtThunk->UpdateWindow (Private->WindowHandle);
    601   }
    602 
    603   gBS->RestoreTPL (OriginalTPL);
    604 
    605   return EFI_SUCCESS;
    606 }
    607 
    608 //
    609 // Construction and Destruction functions
    610 //
    611 
    612 
    613 /**
    614 
    615 
    616   @return None
    617 
    618 **/
    619 // TODO:    WinNtIo - add argument and description to function comment
    620 // TODO:    EFI_UNSUPPORTED - add return value to function comment
    621 // TODO:    EFI_SUCCESS - add return value to function comment
    622 EFI_STATUS
    623 WinNtGopSupported (
    624   IN  EFI_WIN_NT_IO_PROTOCOL  *WinNtIo
    625   )
    626 {
    627   //
    628   // Check to see if the IO abstraction represents a device type we support.
    629   //
    630   // This would be replaced a check of PCI subsystem ID, etc.
    631   //
    632   if (!CompareGuid (WinNtIo->TypeGuid, &gEfiWinNtGopGuid)) {
    633     return EFI_UNSUPPORTED;
    634   }
    635 
    636   return EFI_SUCCESS;
    637 }
    638 
    639 
    640 /**
    641   Win32 Windows event handler.
    642 
    643   See Win32 Book
    644 
    645   @return See Win32 Book
    646 
    647 **/
    648 // TODO:    hwnd - add argument and description to function comment
    649 // TODO:    iMsg - add argument and description to function comment
    650 // TODO:    wParam - add argument and description to function comment
    651 // TODO:    lParam - add argument and description to function comment
    652 LRESULT
    653 CALLBACK
    654 WinNtGopThreadWindowProc (
    655   IN  HWND    hwnd,
    656   IN  UINT    iMsg,
    657   IN  WPARAM  wParam,
    658   IN  LPARAM  lParam
    659   )
    660 {
    661   GOP_PRIVATE_DATA  *Private;
    662   UINTN             Size;
    663   HDC               Handle;
    664   PAINTSTRUCT       PaintStruct;
    665   LPARAM            Index;
    666   EFI_INPUT_KEY     Key;
    667   BOOLEAN           AltIsPress;
    668 
    669   //
    670   // BugBug - if there are two instances of this DLL in memory (such as is
    671   // the case for ERM), the correct instance of this function may not be called.
    672   // This also means that the address of the mTlsIndex value will be wrong, and
    673   // the value may be wrong too.
    674   //
    675 
    676 
    677   //
    678   // Use mTlsIndex global to get a Thread Local Storage version of Private.
    679   // This works since each Gop protocol has a unique Private data instance and
    680   // a unique thread.
    681   //
    682   AltIsPress = FALSE;
    683   Private = mWinNt->TlsGetValue (mTlsIndex);
    684   ASSERT (NULL != Private);
    685 
    686   switch (iMsg) {
    687   case WM_CREATE:
    688     Size = Private->GraphicsOutput.Mode->Info->HorizontalResolution * Private->GraphicsOutput.Mode->Info->VerticalResolution * sizeof (RGBQUAD);
    689 
    690     //
    691     // Allocate DIB frame buffer directly from NT for performance enhancement
    692     // This buffer is the virtual screen/frame buffer. This buffer is not the
    693     // same a a frame buffer. The first fow of this buffer will be the bottom
    694     // line of the image. This is an artifact of the way we draw to the screen.
    695     //
    696     Private->VirtualScreenInfo = Private->WinNtThunk->HeapAlloc (
    697                                                         Private->WinNtThunk->GetProcessHeap (),
    698                                                         HEAP_ZERO_MEMORY,
    699                                                         Size
    700                                                         );
    701 
    702     Private->VirtualScreenInfo->bV4Size           = sizeof (BITMAPV4HEADER);
    703     Private->VirtualScreenInfo->bV4Width          = Private->GraphicsOutput.Mode->Info->HorizontalResolution;
    704     Private->VirtualScreenInfo->bV4Height         = Private->GraphicsOutput.Mode->Info->VerticalResolution;
    705     Private->VirtualScreenInfo->bV4Planes         = 1;
    706     Private->VirtualScreenInfo->bV4BitCount       = 32;
    707     //
    708     // uncompressed
    709     //
    710     Private->VirtualScreenInfo->bV4V4Compression  = BI_RGB;
    711     Private->VirtualScreen = (RGBQUAD *) (Private->VirtualScreenInfo + 1);
    712     return 0;
    713 
    714   case WM_PAINT:
    715     //
    716     // I have not found a way to convert hwnd into a Private context. So for
    717     // now we use this API to convert hwnd to Private data.
    718     //
    719 
    720     Handle = mWinNt->BeginPaint (hwnd, &PaintStruct);
    721 
    722     mWinNt->SetDIBitsToDevice (
    723               Handle,                                     // Destination Device Context
    724               0,                                          // Destination X - 0
    725               0,                                          // Destination Y - 0
    726               Private->GraphicsOutput.Mode->Info->HorizontalResolution,              // Width
    727               Private->GraphicsOutput.Mode->Info->VerticalResolution,                // Height
    728               0,                                          // Source X
    729               0,                                          // Source Y
    730               0,                                          // DIB Start Scan Line
    731               Private->GraphicsOutput.Mode->Info->VerticalResolution,                // Number of scan lines
    732               Private->VirtualScreen,                     // Address of array of DIB bits
    733               (BITMAPINFO *) Private->VirtualScreenInfo,  // Address of structure with bitmap info
    734               DIB_RGB_COLORS                              // RGB or palette indexes
    735               );
    736 
    737     mWinNt->EndPaint (hwnd, &PaintStruct);
    738     return 0;
    739 
    740   //
    741   // F10 and the ALT key do not create a WM_KEYDOWN message, thus this special case
    742   // WM_SYSKEYDOWN is posted when F10 is pressed or
    743   // holds down ALT key and then presses another key.
    744   //
    745   case WM_SYSKEYDOWN:
    746 
    747     Key.ScanCode    = 0;
    748     Key.UnicodeChar = CHAR_NULL;
    749     switch (wParam) {
    750     case VK_F10:
    751       Key.ScanCode    = SCAN_F10;
    752       Key.UnicodeChar = CHAR_NULL;
    753       GopPrivateAddKey (Private, Key);
    754       return 0;
    755     }
    756 
    757     //
    758     // If ALT or ALT + modifier key is pressed.
    759     //
    760     if (WinNtGopConvertParamToEfiKey (Private, &wParam, &lParam, &Key)) {
    761       if (Key.ScanCode != 0){
    762         //
    763         // If ALT is pressed with other ScanCode.
    764         // Always revers the left Alt for simple.
    765         //
    766         Private->LeftAlt = TRUE;
    767       }
    768       GopPrivateAddKey (Private, Key);
    769       //
    770       // When Alt is released there is no windoes message, so
    771       // clean it after using it.
    772       //
    773       Private->RightAlt = FALSE;
    774       Private->LeftAlt  = FALSE;
    775       return 0;
    776     }
    777     AltIsPress = TRUE;
    778 
    779   case WM_CHAR:
    780     //
    781     // The ESC key also generate WM_CHAR.
    782     //
    783     if (wParam == 0x1B) {
    784 	    return 0;
    785     }
    786 
    787     if (AltIsPress == TRUE) {
    788       //
    789       // If AltIsPress is true that means the Alt key is pressed.
    790       //
    791       Private->LeftAlt = TRUE;
    792     }
    793     for (Index = 0; Index < (lParam & 0xffff); Index++) {
    794       if (wParam != 0) {
    795         Key.UnicodeChar = (CHAR16) wParam;
    796         Key.ScanCode    = SCAN_NULL;
    797         GopPrivateAddKey (Private, Key);
    798       }
    799     }
    800     if (AltIsPress == TRUE) {
    801       //
    802       // When Alt is released there is no windoes message, so
    803       // clean it after using it.
    804       //
    805       Private->LeftAlt  = FALSE;
    806       Private->RightAlt = FALSE;
    807     }
    808     return 0;
    809 
    810   case WM_SYSKEYUP:
    811     //
    812     // ALT is pressed with another key released
    813     //
    814     WinNtGopConvertParamToEfiKeyShiftState (Private, &wParam, &lParam, FALSE);
    815     return 0;
    816 
    817   case WM_KEYDOWN:
    818     Key.ScanCode    = SCAN_NULL;
    819     Key.UnicodeChar = CHAR_NULL;
    820     //
    821     // A value key press will cause a WM_KEYDOWN first, then cause a WM_CHAR
    822     // So if there is no modifier key updated, skip the WM_KEYDOWN even.
    823     //
    824     if (WinNtGopConvertParamToEfiKey (Private, &wParam, &lParam, &Key)) {
    825       //
    826       // Support the partial keystroke, add all keydown event into the queue.
    827       //
    828       GopPrivateAddKey (Private, Key);
    829     }
    830     return 0;
    831 
    832   case WM_KEYUP:
    833     WinNtGopConvertParamToEfiKeyShiftState (Private, &wParam, &lParam, FALSE);
    834     return 0;
    835 
    836   case WM_CLOSE:
    837     //
    838     // This close message is issued by user, core is not aware of this,
    839     // so don't release the window display resource, just hide the window.
    840     //
    841     Private->WinNtThunk->ShowWindow (Private->WindowHandle, SW_HIDE);
    842     return 0;
    843 
    844   case WM_DESTROY:
    845     mWinNt->DestroyWindow (hwnd);
    846     mWinNt->PostQuitMessage (0);
    847 
    848     mWinNt->HeapFree (Private->WinNtThunk->GetProcessHeap (), 0, Private->VirtualScreenInfo);
    849 
    850     mWinNt->ExitThread (0);
    851     return 0;
    852 
    853   default:
    854     break;
    855   };
    856 
    857   return mWinNt->DefWindowProc (hwnd, iMsg, wParam, lParam);
    858 }
    859 
    860 
    861 /**
    862   This thread simulates the end of WinMain () aplication. Each Winow nededs
    863   to process it's events. The messages are dispatched to
    864   WinNtGopThreadWindowProc ().
    865   Be very careful sine WinNtGopThreadWinMain () and WinNtGopThreadWindowProc ()
    866   are running in a seperate thread. We have to do this to process the events.
    867 
    868   @param  lpParameter            Handle of window to manage.
    869 
    870   @return if a WM_QUIT message is returned exit.
    871 
    872 **/
    873 DWORD
    874 WINAPI
    875 WinNtGopThreadWinMain (
    876   LPVOID    lpParameter
    877   )
    878 {
    879   MSG               Message;
    880   GOP_PRIVATE_DATA  *Private;
    881   RECT              Rect;
    882 
    883   Private = (GOP_PRIVATE_DATA *) lpParameter;
    884   ASSERT (NULL != Private);
    885 
    886   //
    887   // Since each thread has unique private data, save the private data in Thread
    888   // Local Storage slot. Then the shared global mTlsIndex can be used to get
    889   // thread specific context.
    890   //
    891   Private->WinNtThunk->TlsSetValue (mTlsIndex, Private);
    892 
    893   Private->ThreadId                   = Private->WinNtThunk->GetCurrentThreadId ();
    894 
    895   Private->WindowsClass.cbSize        = sizeof (WNDCLASSEX);
    896   Private->WindowsClass.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    897   Private->WindowsClass.lpfnWndProc   = WinNtGopThreadWindowProc;
    898   Private->WindowsClass.cbClsExtra    = 0;
    899   Private->WindowsClass.cbWndExtra    = 0;
    900   Private->WindowsClass.hInstance     = NULL;
    901   Private->WindowsClass.hIcon         = Private->WinNtThunk->LoadIcon (NULL, IDI_APPLICATION);
    902   Private->WindowsClass.hCursor       = Private->WinNtThunk->LoadCursor (NULL, IDC_ARROW);
    903   Private->WindowsClass.hbrBackground = (HBRUSH)(UINTN)COLOR_WINDOW;
    904   Private->WindowsClass.lpszMenuName  = NULL;
    905   Private->WindowsClass.lpszClassName = WIN_NT_GOP_CLASS_NAME;
    906   Private->WindowsClass.hIconSm       = Private->WinNtThunk->LoadIcon (NULL, IDI_APPLICATION);
    907 
    908   //
    909   // This call will fail after the first time, but thats O.K. since we only need
    910   // WIN_NT_GOP_CLASS_NAME to exist to create the window.
    911   //
    912   // Note: Multiple instances of this DLL will use the same instance of this
    913   // Class, including the callback function, unless the Class is unregistered and
    914   // successfully registered again.
    915   //
    916   Private->WinNtThunk->RegisterClassEx (&Private->WindowsClass);
    917 
    918   //
    919   // Setting Rect values to allow for the AdjustWindowRect to provide
    920   // us the correct sizes for the client area when doing the CreateWindowEx
    921   //
    922   Rect.top    = 0;
    923   Rect.bottom = Private->GraphicsOutput.Mode->Info->VerticalResolution;
    924   Rect.left   = 0;
    925   Rect.right  = Private->GraphicsOutput.Mode->Info->HorizontalResolution;
    926 
    927   Private->WinNtThunk->AdjustWindowRect (&Rect, WS_OVERLAPPEDWINDOW, 0);
    928 
    929   Private->WindowHandle = Private->WinNtThunk->CreateWindowEx (
    930                                                 0,
    931                                                 WIN_NT_GOP_CLASS_NAME,
    932                                                 Private->WindowName,
    933                                                 WS_OVERLAPPEDWINDOW,
    934                                                 CW_USEDEFAULT,
    935                                                 CW_USEDEFAULT,
    936                                                 Rect.right - Rect.left,
    937                                                 Rect.bottom - Rect.top,
    938                                                 NULL,
    939                                                 NULL,
    940                                                 NULL,
    941                                                 (VOID **)&Private
    942                                                 );
    943 
    944   //
    945   // The reset of this thread is the standard winows program. We need a sperate
    946   // thread since we must process the message loop to make windows act like
    947   // windows.
    948   //
    949 
    950   Private->WinNtThunk->ShowWindow (Private->WindowHandle, SW_SHOW);
    951   Private->WinNtThunk->UpdateWindow (Private->WindowHandle);
    952 
    953   //
    954   // Let the main thread get some work done
    955   //
    956   Private->WinNtThunk->ReleaseSemaphore (Private->ThreadInited, 1, NULL);
    957 
    958   //
    959   // This is the message loop that all Windows programs need.
    960   //
    961   while (Private->WinNtThunk->GetMessage (&Message, Private->WindowHandle, 0, 0)) {
    962     Private->WinNtThunk->TranslateMessage (&Message);
    963     Private->WinNtThunk->DispatchMessage (&Message);
    964   }
    965 
    966   return (DWORD)Message.wParam;
    967 }
    968 
    969 
    970 /**
    971   TODO: Add function description
    972 
    973   @param  Private                TODO: add argument description
    974   @param  HorizontalResolution   TODO: add argument description
    975   @param  VerticalResolution     TODO: add argument description
    976   @param  ColorDepth             TODO: add argument description
    977   @param  RefreshRate            TODO: add argument description
    978 
    979   @return TODO: add return values
    980 
    981 **/
    982 EFI_STATUS
    983 WinNtGopStartWindow (
    984   IN  GOP_PRIVATE_DATA    *Private,
    985   IN  UINT32              HorizontalResolution,
    986   IN  UINT32              VerticalResolution,
    987   IN  UINT32              ColorDepth,
    988   IN  UINT32              RefreshRate
    989   )
    990 {
    991   EFI_STATUS          Status;
    992   DWORD               NewThreadId;
    993 
    994   mWinNt  = Private->WinNtThunk;
    995 
    996   //
    997   // Initialize a Thread Local Storge variable slot. We use TLS to get the
    998   // correct Private data instance into the windows thread.
    999   //
   1000   if (mTlsIndex == TLS_OUT_OF_INDEXES) {
   1001     ASSERT (0 == mTlsIndexUseCount);
   1002     mTlsIndex = Private->WinNtThunk->TlsAlloc ();
   1003   }
   1004 
   1005   //
   1006   // always increase the use count!
   1007   //
   1008   mTlsIndexUseCount++;
   1009 
   1010   //
   1011   // Register to be notified on exit boot services so we can destroy the window.
   1012   //
   1013   Status = gBS->CreateEventEx (
   1014                   EVT_NOTIFY_SIGNAL,
   1015                   TPL_CALLBACK,
   1016                   KillNtGopThread,
   1017                   Private,
   1018                   &gEfiEventExitBootServicesGuid,
   1019                   &mGopScreenExitBootServicesEvent
   1020                   );
   1021 
   1022   Private->ThreadInited = Private->WinNtThunk->CreateSemaphore (NULL, 0, 1, NULL);
   1023   Private->ThreadHandle = Private->WinNtThunk->CreateThread (
   1024                                                 NULL,
   1025                                                 0,
   1026                                                 WinNtGopThreadWinMain,
   1027                                                 (VOID *) Private,
   1028                                                 0,
   1029                                                 &NewThreadId
   1030                                                 );
   1031 
   1032   //
   1033   // The other thread has entered the windows message loop so we can
   1034   // continue our initialization.
   1035   //
   1036   Private->WinNtThunk->WaitForSingleObject (Private->ThreadInited, INFINITE);
   1037   Private->WinNtThunk->CloseHandle (Private->ThreadInited);
   1038 
   1039   return Status;
   1040 }
   1041 
   1042 
   1043 /**
   1044 
   1045 
   1046   @return None
   1047 
   1048 **/
   1049 // TODO:    Private - add argument and description to function comment
   1050 // TODO:    EFI_SUCCESS - add return value to function comment
   1051 EFI_STATUS
   1052 WinNtGopConstructor (
   1053   GOP_PRIVATE_DATA    *Private
   1054   )
   1055 {
   1056   Private->ModeData = mGopModeData;
   1057 
   1058   Private->GraphicsOutput.QueryMode = WinNtGopQuerytMode;
   1059   Private->GraphicsOutput.SetMode = WinNtGopSetMode;
   1060   Private->GraphicsOutput.Blt            = WinNtGopBlt;
   1061 
   1062   //
   1063   // Allocate buffer for Graphics Output Protocol mode information
   1064   //
   1065   Private->GraphicsOutput.Mode = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE));
   1066   if (Private->GraphicsOutput.Mode == NULL) {
   1067     return EFI_OUT_OF_RESOURCES;
   1068   }
   1069   Private->GraphicsOutput.Mode->Info = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));
   1070   if (Private->GraphicsOutput.Mode->Info == NULL) {
   1071     return EFI_OUT_OF_RESOURCES;
   1072   }
   1073 
   1074   Private->GraphicsOutput.Mode->MaxMode = sizeof(mGopModeData) / sizeof(GOP_MODE_DATA);
   1075   //
   1076   // Till now, we have no idea about the window size.
   1077   //
   1078   Private->GraphicsOutput.Mode->Mode = GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER;
   1079   Private->GraphicsOutput.Mode->Info->Version = 0;
   1080   Private->GraphicsOutput.Mode->Info->HorizontalResolution = 0;
   1081   Private->GraphicsOutput.Mode->Info->VerticalResolution = 0;
   1082   Private->GraphicsOutput.Mode->Info->PixelFormat = PixelBltOnly;
   1083   Private->GraphicsOutput.Mode->SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
   1084   Private->GraphicsOutput.Mode->FrameBufferBase = (EFI_PHYSICAL_ADDRESS) (UINTN) NULL;
   1085   Private->GraphicsOutput.Mode->FrameBufferSize = 0;
   1086 
   1087   Private->HardwareNeedsStarting  = TRUE;
   1088   Private->FillLine               = NULL;
   1089 
   1090   WinNtGopInitializeSimpleTextInForWindow (Private);
   1091 
   1092   return EFI_SUCCESS;
   1093 }
   1094 
   1095 
   1096 /**
   1097 
   1098 
   1099   @return None
   1100 
   1101 **/
   1102 // TODO:    Private - add argument and description to function comment
   1103 // TODO:    EFI_SUCCESS - add return value to function comment
   1104 EFI_STATUS
   1105 WinNtGopDestructor (
   1106   GOP_PRIVATE_DATA     *Private
   1107   )
   1108 {
   1109   if (!Private->HardwareNeedsStarting) {
   1110     //
   1111     // BugBug: Shutdown GOP Hardware and any child devices.
   1112     //
   1113     Private->WinNtThunk->SendMessage (Private->WindowHandle, WM_DESTROY, 0, 0);
   1114     Private->WinNtThunk->CloseHandle (Private->ThreadHandle);
   1115 
   1116     mTlsIndexUseCount--;
   1117 
   1118     //
   1119     // The callback function for another window could still be called,
   1120     // so we need to make sure there are no more users of mTlsIndex.
   1121     //
   1122     if (0 == mTlsIndexUseCount) {
   1123       ASSERT (TLS_OUT_OF_INDEXES != mTlsIndex);
   1124 
   1125       Private->WinNtThunk->TlsFree (mTlsIndex);
   1126       mTlsIndex = TLS_OUT_OF_INDEXES;
   1127 
   1128       Private->WinNtThunk->UnregisterClass (
   1129                               Private->WindowsClass.lpszClassName,
   1130                               Private->WindowsClass.hInstance
   1131                               );
   1132     }
   1133 
   1134     WinNtGopDestroySimpleTextInForWindow (Private);
   1135   }
   1136 
   1137   //
   1138   // Free graphics output protocol occupied resource
   1139   //
   1140   if (Private->GraphicsOutput.Mode != NULL) {
   1141     if (Private->GraphicsOutput.Mode->Info != NULL) {
   1142       FreePool (Private->GraphicsOutput.Mode->Info);
   1143     }
   1144     FreePool (Private->GraphicsOutput.Mode);
   1145   }
   1146 
   1147   return EFI_SUCCESS;
   1148 }
   1149 
   1150 
   1151 /**
   1152   This is the GOP screen's callback notification function for exit-boot-services.
   1153   All we do here is call WinNtGopDestructor().
   1154 
   1155   @param  Event                  not used
   1156   @param  Context                pointer to the Private structure.
   1157 
   1158   @return None.
   1159 
   1160 **/
   1161 VOID
   1162 EFIAPI
   1163 KillNtGopThread (
   1164   IN EFI_EVENT  Event,
   1165   IN VOID       *Context
   1166   )
   1167 {
   1168   WinNtGopDestructor (Context);
   1169 }
   1170