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
WinNtGopConvertParamToEfiKeyShiftState(IN GOP_PRIVATE_DATA * Private,IN WPARAM * wParam,IN LPARAM * lParam,IN BOOLEAN Flag)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
WinNtGopConvertParamToEfiKey(IN GOP_PRIVATE_DATA * Private,IN WPARAM * wParam,IN LPARAM * lParam,IN EFI_INPUT_KEY * Key)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
WinNtGopQuerytMode(IN EFI_GRAPHICS_OUTPUT_PROTOCOL * This,IN UINT32 ModeNumber,OUT UINTN * SizeOfInfo,OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION ** Info)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
WinNtGopSetMode(IN EFI_GRAPHICS_OUTPUT_PROTOCOL * This,IN UINT32 ModeNumber)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
WinNtGopBlt(IN EFI_GRAPHICS_OUTPUT_PROTOCOL * This,IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL * BltBuffer,OPTIONAL IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,IN UINTN SourceX,IN UINTN SourceY,IN UINTN DestinationX,IN UINTN DestinationY,IN UINTN Width,IN UINTN Height,IN UINTN Delta OPTIONAL)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
WinNtGopSupported(IN EFI_WIN_NT_IO_PROTOCOL * WinNtIo)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
WinNtGopThreadWindowProc(IN HWND hwnd,IN UINT iMsg,IN WPARAM wParam,IN LPARAM lParam)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
WinNtGopThreadWinMain(LPVOID lpParameter)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
WinNtGopStartWindow(IN GOP_PRIVATE_DATA * Private,IN UINT32 HorizontalResolution,IN UINT32 VerticalResolution,IN UINT32 ColorDepth,IN UINT32 RefreshRate)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
WinNtGopConstructor(GOP_PRIVATE_DATA * Private)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
WinNtGopDestructor(GOP_PRIVATE_DATA * Private)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
KillNtGopThread(IN EFI_EVENT Event,IN VOID * Context)1163 KillNtGopThread (
1164   IN EFI_EVENT  Event,
1165   IN VOID       *Context
1166   )
1167 {
1168   WinNtGopDestructor (Context);
1169 }
1170