fa34596497f8f49b43cd102be84469ad68d1c38c
[mirror_edk2.git] / EmulatorPkg / Win / Host / WinGopScreen.c
1 /** @file\r
2 \r
3 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
4 SPDX-License-Identifier: BSD-2-Clause-Patent\r
5 \r
6 Module Name:\r
7 \r
8     WinGopScreen.c\r
9 \r
10 Abstract:\r
11 \r
12   This file produces the graphics abstration of GOP. It is called by\r
13   WinNtGopDriver.c file which deals with the UEFI 2.0 driver model.\r
14   This file just does graphics.\r
15 \r
16 \r
17 **/\r
18 \r
19 #include "WinGop.h"\r
20 \r
21 DWORD                     mTlsIndex         = TLS_OUT_OF_INDEXES;\r
22 DWORD                     mTlsIndexUseCount = 0;  // lets us know when we can free mTlsIndex.\r
23 \r
24 BOOLEAN\r
25 WinNtGopConvertParamToEfiKeyShiftState (\r
26   IN  GRAPHICS_PRIVATE_DATA  *Private,\r
27   IN  WPARAM                 *wParam,\r
28   IN  LPARAM                 *lParam,\r
29   IN  BOOLEAN                Flag\r
30   )\r
31 {\r
32   switch (*wParam) {\r
33   //\r
34   // BUGBUG: Only GetAsyncKeyState() and GetKeyState() can distinguish\r
35   // left and right Ctrl, and Shift key.\r
36   // Neither of the two is defined in EFI_WIN_NT_THUNK_PROTOCOL.\r
37   // Therefor, we can not set the correct Shift state here.\r
38   //\r
39   case VK_SHIFT:\r
40     if ((*lParam & GOP_EXTENDED_KEY) == GOP_EXTENDED_KEY) {\r
41         Private->RightShift = Flag;\r
42       } else {\r
43         Private->LeftShift = Flag;\r
44       }\r
45     return TRUE;\r
46 \r
47   case VK_LSHIFT:\r
48     Private->LeftShift = Flag;\r
49     return TRUE;\r
50 \r
51   case VK_RSHIFT:\r
52     Private->RightShift = Flag;\r
53     return TRUE;\r
54 \r
55   case VK_CONTROL:\r
56     if ((*lParam & GOP_EXTENDED_KEY) == GOP_EXTENDED_KEY) {\r
57         Private->RightCtrl= Flag;\r
58       } else {\r
59         Private->LeftCtrl = Flag;\r
60       }\r
61     return TRUE;\r
62 \r
63   case VK_LCONTROL:\r
64     Private->LeftCtrl = Flag;\r
65     return TRUE;\r
66 \r
67   case VK_RCONTROL:\r
68     Private->RightCtrl = Flag;\r
69     return TRUE;\r
70 \r
71   case VK_LWIN:\r
72     Private->LeftLogo   = Flag;\r
73     return TRUE;\r
74 \r
75   case VK_RWIN:\r
76     Private->RightLogo  = Flag;\r
77     return TRUE;\r
78 \r
79   case VK_APPS:\r
80     Private->Menu       = Flag;\r
81     return TRUE;\r
82   //\r
83   // BUGBUG: PrintScreen/SysRq can not trigger WM_KEYDOWN message,\r
84   // so SySReq shift state is not supported here.\r
85   //\r
86   case VK_PRINT:\r
87     Private->SysReq     = Flag;\r
88     return TRUE;\r
89   //\r
90   // For Alt Keystroke.\r
91   //\r
92   case VK_MENU:\r
93     if ((*lParam & GOP_EXTENDED_KEY) == GOP_EXTENDED_KEY) {\r
94         Private->RightAlt = Flag;\r
95       } else {\r
96         Private->LeftAlt = Flag;\r
97       }\r
98     return TRUE;\r
99 \r
100   default:\r
101     return FALSE;\r
102   }\r
103 }\r
104 \r
105 BOOLEAN\r
106 WinNtGopConvertParamToEfiKey (\r
107   IN  GRAPHICS_PRIVATE_DATA  *Private,\r
108   IN  WPARAM                 *wParam,\r
109   IN  LPARAM                 *lParam,\r
110   IN  EFI_INPUT_KEY          *Key\r
111   )\r
112 {\r
113   BOOLEAN Flag;\r
114   Flag = FALSE;\r
115   switch (*wParam) {\r
116   case VK_HOME:       Key->ScanCode = SCAN_HOME;      Flag = TRUE; break;\r
117   case VK_END:        Key->ScanCode = SCAN_END;       Flag = TRUE; break;\r
118   case VK_LEFT:       Key->ScanCode = SCAN_LEFT;      Flag = TRUE; break;\r
119   case VK_RIGHT:      Key->ScanCode = SCAN_RIGHT;     Flag = TRUE; break;\r
120   case VK_UP:         Key->ScanCode = SCAN_UP;        Flag = TRUE; break;\r
121   case VK_DOWN:       Key->ScanCode = SCAN_DOWN;      Flag = TRUE; break;\r
122   case VK_DELETE:     Key->ScanCode = SCAN_DELETE;    Flag = TRUE; break;\r
123   case VK_INSERT:     Key->ScanCode = SCAN_INSERT;    Flag = TRUE; break;\r
124   case VK_PRIOR:      Key->ScanCode = SCAN_PAGE_UP;   Flag = TRUE; break;\r
125   case VK_NEXT:       Key->ScanCode = SCAN_PAGE_DOWN; Flag = TRUE; break;\r
126   case VK_ESCAPE:     Key->ScanCode = SCAN_ESC;       Flag = TRUE; break;\r
127 \r
128   case VK_F1:         Key->ScanCode = SCAN_F1;    Flag = TRUE;     break;\r
129   case VK_F2:         Key->ScanCode = SCAN_F2;    Flag = TRUE;     break;\r
130   case VK_F3:         Key->ScanCode = SCAN_F3;    Flag = TRUE;     break;\r
131   case VK_F4:         Key->ScanCode = SCAN_F4;    Flag = TRUE;     break;\r
132   case VK_F5:         Key->ScanCode = SCAN_F5;    Flag = TRUE;     break;\r
133   case VK_F6:         Key->ScanCode = SCAN_F6;    Flag = TRUE;     break;\r
134   case VK_F7:         Key->ScanCode = SCAN_F7;    Flag = TRUE;     break;\r
135   case VK_F8:         Key->ScanCode = SCAN_F8;    Flag = TRUE;     break;\r
136   case VK_F9:         Key->ScanCode = SCAN_F9;    Flag = TRUE;     break;\r
137   case VK_F11:        Key->ScanCode = SCAN_F11;   Flag = TRUE;     break;\r
138   case VK_F12:        Key->ScanCode = SCAN_F12;   Flag = TRUE;     break;\r
139 \r
140   case VK_F13:        Key->ScanCode = SCAN_F13;   Flag = TRUE;     break;\r
141   case VK_F14:        Key->ScanCode = SCAN_F14;   Flag = TRUE;     break;\r
142   case VK_F15:        Key->ScanCode = SCAN_F15;   Flag = TRUE;     break;\r
143   case VK_F16:        Key->ScanCode = SCAN_F16;   Flag = TRUE;     break;\r
144   case VK_F17:        Key->ScanCode = SCAN_F17;   Flag = TRUE;     break;\r
145   case VK_F18:        Key->ScanCode = SCAN_F18;   Flag = TRUE;     break;\r
146   case VK_F19:        Key->ScanCode = SCAN_F19;   Flag = TRUE;     break;\r
147   case VK_F20:        Key->ScanCode = SCAN_F20;   Flag = TRUE;     break;\r
148   case VK_F21:        Key->ScanCode = SCAN_F21;   Flag = TRUE;     break;\r
149   case VK_F22:        Key->ScanCode = SCAN_F22;   Flag = TRUE;     break;\r
150   case VK_F23:        Key->ScanCode = SCAN_F23;   Flag = TRUE;     break;\r
151   case VK_F24:        Key->ScanCode = SCAN_F24;   Flag = TRUE;     break;\r
152   case VK_PAUSE:      Key->ScanCode = SCAN_PAUSE; Flag = TRUE;     break;\r
153 \r
154   //\r
155   // Set toggle state\r
156   //\r
157   case VK_NUMLOCK:\r
158     Private->NumLock    = (BOOLEAN)(!Private->NumLock);\r
159     Flag = TRUE;\r
160     break;\r
161   case VK_SCROLL:\r
162     Private->ScrollLock = (BOOLEAN)(!Private->ScrollLock);\r
163     Flag = TRUE;\r
164     break;\r
165   case VK_CAPITAL:\r
166     Private->CapsLock   = (BOOLEAN)(!Private->CapsLock);\r
167     Flag = TRUE;\r
168     break;\r
169   }\r
170 \r
171   return (WinNtGopConvertParamToEfiKeyShiftState (Private, wParam, lParam, TRUE)) == TRUE ? TRUE : Flag;\r
172 }\r
173 \r
174 \r
175 //\r
176 // GOP Protocol Member Functions\r
177 //\r
178 \r
179 /**\r
180   Change the resolution and resize of the window\r
181 **/\r
182 EFI_STATUS\r
183 EFIAPI\r
184 WinNtWndSize (\r
185   IN  EMU_GRAPHICS_WINDOW_PROTOCOL  *GraphicsIo,\r
186   IN  UINT32                        Width,\r
187   IN  UINT32                        Height\r
188 )\r
189 {\r
190   RETURN_STATUS                        RStatus;\r
191   EFI_GRAPHICS_OUTPUT_MODE_INFORMATION Info;\r
192   GRAPHICS_PRIVATE_DATA                *Private;\r
193   RECT                                 Rect;\r
194   BITMAPV4HEADER                       *VirtualScreenInfo;\r
195   FRAME_BUFFER_CONFIGURE               *FrameBufferConfigure;\r
196   UINTN                                FrameBufferConfigureSize;\r
197 \r
198   Private = GRAPHICS_PRIVATE_DATA_FROM_THIS (GraphicsIo);\r
199 \r
200   //\r
201   // Allocate DIB frame buffer directly from NT for performance enhancement\r
202   // This buffer is the virtual screen/frame buffer.\r
203   //\r
204   VirtualScreenInfo = HeapAlloc (\r
205     GetProcessHeap (),\r
206     HEAP_ZERO_MEMORY,\r
207     Width * Height * sizeof (RGBQUAD) + sizeof (BITMAPV4HEADER)\r
208   );\r
209   if (VirtualScreenInfo == NULL) {\r
210     return EFI_OUT_OF_RESOURCES;\r
211   }\r
212 \r
213   //\r
214   // Update the virtual screen info data structure\r
215   // Use negative Height to make sure screen/buffer are using the same coordinate.\r
216   //\r
217   VirtualScreenInfo->bV4Size = sizeof (BITMAPV4HEADER);\r
218   VirtualScreenInfo->bV4Width = Width;\r
219   VirtualScreenInfo->bV4Height = -(LONG)Height;\r
220   VirtualScreenInfo->bV4Planes = 1;\r
221   VirtualScreenInfo->bV4BitCount = 32;\r
222   //\r
223   // uncompressed\r
224   //\r
225   VirtualScreenInfo->bV4V4Compression = BI_RGB;\r
226 \r
227   Info.HorizontalResolution = Width;\r
228   Info.VerticalResolution   = Height;\r
229   Info.PixelFormat          = PixelBlueGreenRedReserved8BitPerColor;\r
230   Info.PixelsPerScanLine    = Width;\r
231   FrameBufferConfigureSize  = 0;\r
232   RStatus = FrameBufferBltConfigure (VirtualScreenInfo + 1, &Info, NULL, &FrameBufferConfigureSize);\r
233   ASSERT (RStatus == EFI_BUFFER_TOO_SMALL);\r
234   FrameBufferConfigure = AllocatePool (FrameBufferConfigureSize);\r
235   if (FrameBufferConfigure == NULL) {\r
236     HeapFree (GetProcessHeap (), 0, VirtualScreenInfo);\r
237     return EFI_OUT_OF_RESOURCES;\r
238   }\r
239   RStatus = FrameBufferBltConfigure (VirtualScreenInfo + 1, &Info, FrameBufferConfigure, &FrameBufferConfigureSize);\r
240   ASSERT_RETURN_ERROR (RStatus);\r
241 \r
242 \r
243   if (Private->FrameBufferConfigure != NULL) {\r
244     FreePool (Private->FrameBufferConfigure);\r
245   }\r
246   Private->FrameBufferConfigure = FrameBufferConfigure;\r
247 \r
248   //\r
249   // Free the old buffer. We do not save the content of the old buffer since the\r
250   // screen is to be cleared anyway. Clearing the screen is required by the EFI spec.\r
251   // See UEFI spec -EFI_GRAPHICS_OUTPUT_PROTOCOL.SetMode()\r
252   //\r
253   if (Private->VirtualScreenInfo != NULL) {\r
254     HeapFree (GetProcessHeap (), 0, Private->VirtualScreenInfo);\r
255   }\r
256   Private->VirtualScreenInfo = VirtualScreenInfo;\r
257 \r
258   Private->Width  = Width;\r
259   Private->Height = Height;\r
260 \r
261   //\r
262   // Use the AdjuctWindowRect fuction to calculate the real width and height\r
263   // of the new window including the border and caption\r
264   //\r
265   Rect.left   = 0;\r
266   Rect.top    = 0;\r
267   Rect.right  = Width;\r
268   Rect.bottom = Height;\r
269 \r
270   AdjustWindowRect (&Rect, WS_OVERLAPPEDWINDOW, 0);\r
271 \r
272   Width  = Rect.right - Rect.left;\r
273   Height = Rect.bottom - Rect.top;\r
274 \r
275   //\r
276   // Retrieve the original window position information\r
277   //\r
278   GetWindowRect (Private->WindowHandle, &Rect);\r
279 \r
280   //\r
281   // Adjust the window size\r
282   //\r
283   MoveWindow (Private->WindowHandle, Rect.left, Rect.top, (INT32)Width, (INT32)Height, TRUE);\r
284 \r
285   return EFI_SUCCESS;\r
286 }\r
287 \r
288 /**\r
289   Blt pixels from the rectangle (Width X Height) formed by the BltBuffer\r
290   onto the graphics screen starting a location (X, Y). (0, 0) is defined as\r
291   the upper left hand side of the screen. (X, Y) can be outside of the\r
292   current screen geometry and the BltBuffer will be cliped when it is\r
293   displayed. X and Y can be negative or positive. If Width or Height is\r
294   bigger than the current video screen the image will be clipped.\r
295 \r
296   @param  This                   Protocol instance pointer.\r
297   @param  X                      X location on graphics screen.\r
298   @param  Y                      Y location on the graphics screen.\r
299   @param  Width                  Width of BltBuffer.\r
300   @param  Height                 Hight of BltBuffer\r
301   @param  BltOperation           Operation to perform on BltBuffer and video memory\r
302   @param  BltBuffer              Buffer containing data to blt into video buffer.\r
303                                  This  buffer has a size of\r
304                                  Width*Height*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)\r
305   @param  SourceX                If the BltOperation is a EfiCopyBlt this is the\r
306                                  source of the copy. For other BLT operations this\r
307                                  argument is not used.\r
308   @param  SourceX                If the BltOperation is a EfiCopyBlt this is the\r
309                                  source of the copy. For other BLT operations this\r
310                                  argument is not used.\r
311 \r
312   @retval EFI_SUCCESS            The palette is updated with PaletteArray.\r
313   @retval EFI_INVALID_PARAMETER  BltOperation is not valid.\r
314   @retval EFI_DEVICE_ERROR       A hardware error occured writting to the video\r
315                                  buffer.\r
316 \r
317 **/\r
318 // TODO:    SourceY - add argument and description to function comment\r
319 // TODO:    DestinationX - add argument and description to function comment\r
320 // TODO:    DestinationY - add argument and description to function comment\r
321 // TODO:    Delta - add argument and description to function comment\r
322 EFI_STATUS\r
323 WinNtWndBlt (\r
324   IN  EMU_GRAPHICS_WINDOW_PROTOCOL            *GraphicsIo,\r
325   IN  EFI_UGA_PIXEL                           *BltBuffer OPTIONAL,\r
326   IN  EFI_UGA_BLT_OPERATION                   BltOperation,\r
327   IN  EMU_GRAPHICS_WINDOWS__BLT_ARGS          *Args\r
328 )\r
329 {\r
330   RETURN_STATUS                 RStatus;\r
331   GRAPHICS_PRIVATE_DATA         *Private;\r
332   RECT                          Rect;\r
333 \r
334   Private = GRAPHICS_PRIVATE_DATA_FROM_THIS (GraphicsIo);\r
335   RStatus = FrameBufferBlt (\r
336               Private->FrameBufferConfigure,\r
337               BltBuffer,\r
338               BltOperation,\r
339               Args->SourceX, Args->SourceY,\r
340               Args->DestinationX, Args->DestinationY,\r
341               Args->Width, Args->Height,\r
342               Args->Delta\r
343               );\r
344   if (RETURN_ERROR (RStatus)) {\r
345     return (EFI_STATUS)RStatus;\r
346   }\r
347 \r
348   if (BltOperation != EfiBltVideoToBltBuffer) {\r
349     //\r
350     // Mark the area we just blted as Invalid so WM_PAINT will update.\r
351     //\r
352     Rect.left   = (LONG)Args->DestinationX;\r
353     Rect.top    = (LONG)Args->DestinationY;\r
354     Rect.right  = (LONG)(Args->DestinationX + Args->Width);\r
355     Rect.bottom = (LONG)(Args->DestinationY + Args->Height);\r
356     InvalidateRect (Private->WindowHandle, &Rect, FALSE);\r
357 \r
358     //\r
359     // Send the WM_PAINT message to the thread that is drawing the window. We\r
360     // are in the main thread and the window drawing is in a child thread.\r
361     // There is a child thread per window. We have no CriticalSection or Mutex\r
362     // since we write the data and the other thread displays the data. While\r
363     // we may miss some data for a short period of time this is no different than\r
364     // a write combining on writes to a frame buffer.\r
365     //\r
366 \r
367     UpdateWindow (Private->WindowHandle);\r
368   }\r
369 \r
370   return EFI_SUCCESS;\r
371 }\r
372 \r
373 \r
374 \r
375 /**\r
376   Win32 Windows event handler.\r
377 \r
378   See Win32 Book\r
379 \r
380   @return See Win32 Book\r
381 \r
382 **/\r
383 // TODO:    hwnd - add argument and description to function comment\r
384 // TODO:    iMsg - add argument and description to function comment\r
385 // TODO:    wParam - add argument and description to function comment\r
386 // TODO:    lParam - add argument and description to function comment\r
387 LRESULT\r
388 CALLBACK\r
389 WinNtGopThreadWindowProc (\r
390   IN  HWND    hwnd,\r
391   IN  UINT    iMsg,\r
392   IN  WPARAM  wParam,\r
393   IN  LPARAM  lParam\r
394   )\r
395 {\r
396   GRAPHICS_PRIVATE_DATA *Private;\r
397   HDC                   Handle;\r
398   PAINTSTRUCT           PaintStruct;\r
399   LPARAM                Index;\r
400   EFI_INPUT_KEY         Key;\r
401   BOOLEAN               AltIsPress;\r
402   INT32                 PosX;\r
403   INT32                 PosY;\r
404 \r
405   //\r
406   // Use mTlsIndex global to get a Thread Local Storage version of Private.\r
407   // This works since each Gop protocol has a unique Private data instance and\r
408   // a unique thread.\r
409   //\r
410   AltIsPress = FALSE;\r
411   Private = TlsGetValue (mTlsIndex);\r
412   ASSERT (NULL != Private);\r
413 \r
414   switch (iMsg) {\r
415   case WM_PAINT:\r
416     Handle = BeginPaint (hwnd, &PaintStruct);\r
417 \r
418     SetDIBitsToDevice (\r
419       Handle,                                     // Destination Device Context\r
420       0,                                          // Destination X - 0\r
421       0,                                          // Destination Y - 0\r
422       Private->Width,                             // Width\r
423       Private->Height,                            // Height\r
424       0,                                          // Source X\r
425       0,                                          // Source Y\r
426       0,                                          // DIB Start Scan Line\r
427       Private->Height,                            // Number of scan lines\r
428       Private->VirtualScreenInfo + 1,             // Address of array of DIB bits\r
429       (BITMAPINFO *) Private->VirtualScreenInfo,  // Address of structure with bitmap info\r
430       DIB_RGB_COLORS                              // RGB or palette indexes\r
431       );\r
432 \r
433     EndPaint (hwnd, &PaintStruct);\r
434     return 0;\r
435 \r
436   //\r
437   // F10 and the ALT key do not create a WM_KEYDOWN message, thus this special case\r
438   // WM_SYSKEYDOWN is posted when F10 is pressed or\r
439   // holds down ALT key and then presses another key.\r
440   //\r
441   case WM_SYSKEYDOWN:\r
442 \r
443     Key.ScanCode    = 0;\r
444     Key.UnicodeChar = CHAR_NULL;\r
445     switch (wParam) {\r
446     case VK_F10:\r
447       Key.ScanCode    = SCAN_F10;\r
448       Key.UnicodeChar = CHAR_NULL;\r
449       GopPrivateAddKey (Private, Key);\r
450       return 0;\r
451     }\r
452 \r
453     //\r
454     // If ALT or ALT + modifier key is pressed.\r
455     //\r
456     if (WinNtGopConvertParamToEfiKey (Private, &wParam, &lParam, &Key)) {\r
457       if (Key.ScanCode != 0){\r
458         //\r
459         // If ALT is pressed with other ScanCode.\r
460         // Always revers the left Alt for simple.\r
461         //\r
462         Private->LeftAlt = TRUE;\r
463       }\r
464       GopPrivateAddKey (Private, Key);\r
465       //\r
466       // When Alt is released there is no windoes message, so\r
467       // clean it after using it.\r
468       //\r
469       Private->RightAlt = FALSE;\r
470       Private->LeftAlt  = FALSE;\r
471       return 0;\r
472     }\r
473     AltIsPress = TRUE;\r
474 \r
475   case WM_CHAR:\r
476     //\r
477     // The ESC key also generate WM_CHAR.\r
478     //\r
479     if (wParam == 0x1B) {\r
480       return 0;\r
481     }\r
482 \r
483     if (AltIsPress == TRUE) {\r
484       //\r
485       // If AltIsPress is true that means the Alt key is pressed.\r
486       //\r
487       Private->LeftAlt = TRUE;\r
488     }\r
489     for (Index = 0; Index < (lParam & 0xffff); Index++) {\r
490       if (wParam != 0) {\r
491         Key.UnicodeChar = (CHAR16) wParam;\r
492         Key.ScanCode    = SCAN_NULL;\r
493         GopPrivateAddKey (Private, Key);\r
494       }\r
495     }\r
496     if (AltIsPress == TRUE) {\r
497       //\r
498       // When Alt is released there is no windoes message, so\r
499       // clean it after using it.\r
500       //\r
501       Private->LeftAlt  = FALSE;\r
502       Private->RightAlt = FALSE;\r
503     }\r
504     return 0;\r
505 \r
506   case WM_SYSKEYUP:\r
507     //\r
508     // ALT is pressed with another key released\r
509     //\r
510     WinNtGopConvertParamToEfiKeyShiftState (Private, &wParam, &lParam, FALSE);\r
511     return 0;\r
512 \r
513   case WM_KEYDOWN:\r
514     Key.ScanCode    = SCAN_NULL;\r
515     Key.UnicodeChar = CHAR_NULL;\r
516     //\r
517     // A value key press will cause a WM_KEYDOWN first, then cause a WM_CHAR\r
518     // So if there is no modifier key updated, skip the WM_KEYDOWN even.\r
519     //\r
520     if (WinNtGopConvertParamToEfiKey (Private, &wParam, &lParam, &Key)) {\r
521       //\r
522       // Support the partial keystroke, add all keydown event into the queue.\r
523       //\r
524       GopPrivateAddKey (Private, Key);\r
525     }\r
526     return 0;\r
527 \r
528   case WM_KEYUP:\r
529     WinNtGopConvertParamToEfiKeyShiftState (Private, &wParam, &lParam, FALSE);\r
530     return 0;\r
531 \r
532   case WM_MOUSEMOVE:\r
533     PosX = GET_X_LPARAM (lParam);\r
534     PosY = GET_Y_LPARAM (lParam);\r
535 \r
536     if (Private->PointerPreviousX != PosX) {\r
537       Private->PointerState.RelativeMovementX += (PosX - Private->PointerPreviousX);\r
538       Private->PointerPreviousX                = PosX;\r
539       Private->PointerStateChanged             = TRUE;\r
540     }\r
541 \r
542     if (Private->PointerPreviousY != PosY) {\r
543       Private->PointerState.RelativeMovementY += (PosY - Private->PointerPreviousY);\r
544       Private->PointerPreviousY                = PosY;\r
545       Private->PointerStateChanged             = TRUE;\r
546     }\r
547 \r
548     Private->PointerState.RelativeMovementZ  = 0;\r
549     return 0;\r
550 \r
551   case WM_LBUTTONDOWN:\r
552     Private->PointerState.LeftButton = TRUE;\r
553     Private->PointerStateChanged     = TRUE;\r
554     return 0;\r
555 \r
556   case WM_LBUTTONUP:\r
557     Private->PointerState.LeftButton = FALSE;\r
558     Private->PointerStateChanged     = TRUE;\r
559     return 0;\r
560 \r
561   case WM_RBUTTONDOWN:\r
562     Private->PointerState.RightButton = TRUE;\r
563     Private->PointerStateChanged      = TRUE;\r
564     return 0;\r
565 \r
566   case WM_RBUTTONUP:\r
567     Private->PointerState.RightButton = FALSE;\r
568     Private->PointerStateChanged      = TRUE;\r
569     return 0;\r
570 \r
571   case WM_CLOSE:\r
572     //\r
573     // This close message is issued by user, core is not aware of this,\r
574     // so don't release the window display resource, just hide the window.\r
575     //\r
576     ShowWindow (Private->WindowHandle, SW_HIDE);\r
577     return 0;\r
578 \r
579   case WM_DESTROY:\r
580     DestroyWindow (hwnd);\r
581     PostQuitMessage (0);\r
582 \r
583     HeapFree (GetProcessHeap (), 0, Private->VirtualScreenInfo);\r
584 \r
585     ExitThread (0);\r
586 \r
587   default:\r
588     break;\r
589   };\r
590 \r
591   return DefWindowProc (hwnd, iMsg, wParam, lParam);\r
592 }\r
593 \r
594 \r
595 /**\r
596   This thread simulates the end of WinMain () aplication. Each Winow nededs\r
597   to process it's events. The messages are dispatched to\r
598   WinNtGopThreadWindowProc ().\r
599   Be very careful sine WinNtGopThreadWinMain () and WinNtGopThreadWindowProc ()\r
600   are running in a seperate thread. We have to do this to process the events.\r
601 \r
602   @param  lpParameter            Handle of window to manage.\r
603 \r
604   @return if a WM_QUIT message is returned exit.\r
605 \r
606 **/\r
607 DWORD\r
608 WINAPI\r
609 WinNtGopThreadWinMain (\r
610   LPVOID    lpParameter\r
611   )\r
612 {\r
613   MSG                    Message;\r
614   GRAPHICS_PRIVATE_DATA  *Private;\r
615   RECT                   Rect;\r
616 \r
617   Private = (GRAPHICS_PRIVATE_DATA *) lpParameter;\r
618   ASSERT (NULL != Private);\r
619 \r
620   //\r
621   // Since each thread has unique private data, save the private data in Thread\r
622   // Local Storage slot. Then the shared global mTlsIndex can be used to get\r
623   // thread specific context.\r
624   //\r
625   TlsSetValue (mTlsIndex, Private);\r
626 \r
627   Private->ThreadId                   = GetCurrentThreadId ();\r
628 \r
629   Private->WindowsClass.cbSize        = sizeof (WNDCLASSEX);\r
630   Private->WindowsClass.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;\r
631   Private->WindowsClass.lpfnWndProc   = WinNtGopThreadWindowProc;\r
632   Private->WindowsClass.cbClsExtra    = 0;\r
633   Private->WindowsClass.cbWndExtra    = 0;\r
634   Private->WindowsClass.hInstance     = NULL;\r
635   Private->WindowsClass.hIcon         = LoadIcon (NULL, IDI_APPLICATION);\r
636   Private->WindowsClass.hCursor       = LoadCursor (NULL, IDC_ARROW);\r
637   Private->WindowsClass.hbrBackground = (HBRUSH)(UINTN)COLOR_WINDOW;\r
638   Private->WindowsClass.lpszMenuName  = NULL;\r
639   Private->WindowsClass.lpszClassName = WIN_NT_GOP_CLASS_NAME;\r
640   Private->WindowsClass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION);\r
641 \r
642   //\r
643   // Use 100 x 100 as initial Window size.\r
644   //\r
645   Private->Width  = 100;\r
646   Private->Height = 100;\r
647 \r
648 \r
649   //\r
650   // This call will fail after the first time, but thats O.K. since we only need\r
651   // WIN_NT_GOP_CLASS_NAME to exist to create the window.\r
652   //\r
653   RegisterClassEx (&Private->WindowsClass);\r
654 \r
655   //\r
656   // Setting Rect values to allow for the AdjustWindowRect to provide\r
657   // us the correct sizes for the client area when doing the CreateWindowEx\r
658   //\r
659   Rect.top    = 0;\r
660   Rect.bottom = Private->Height;\r
661   Rect.left   = 0;\r
662   Rect.right  = Private->Width;\r
663 \r
664   AdjustWindowRect (&Rect, WS_OVERLAPPEDWINDOW, 0);\r
665 \r
666   Private->WindowHandle = CreateWindowEx (\r
667                             0,\r
668                             WIN_NT_GOP_CLASS_NAME,\r
669                             Private->WindowName,\r
670                             WS_OVERLAPPEDWINDOW,\r
671                             CW_USEDEFAULT,\r
672                             CW_USEDEFAULT,\r
673                             Rect.right - Rect.left,\r
674                             Rect.bottom - Rect.top,\r
675                             NULL,\r
676                             NULL,\r
677                             NULL,\r
678                             (VOID **)&Private\r
679                             );\r
680 \r
681   //\r
682   // The reset of this thread is the standard winows program. We need a sperate\r
683   // thread since we must process the message loop to make windows act like\r
684   // windows.\r
685   //\r
686 \r
687   ShowWindow (Private->WindowHandle, SW_SHOW);\r
688   UpdateWindow (Private->WindowHandle);\r
689 \r
690   //\r
691   // Let the main thread get some work done\r
692   //\r
693   ReleaseSemaphore (Private->ThreadInited, 1, NULL);\r
694 \r
695   //\r
696   // This is the message loop that all Windows programs need.\r
697   //\r
698   while (GetMessage (&Message, Private->WindowHandle, 0, 0)) {\r
699     TranslateMessage (&Message);\r
700     DispatchMessage (&Message);\r
701   }\r
702 \r
703   return (DWORD)Message.wParam;\r
704 }\r
705 \r
706 \r
707 /**\r
708   TODO: Add function description\r
709 \r
710   @param  Private                TODO: add argument description\r
711   @param  HorizontalResolution   TODO: add argument description\r
712   @param  VerticalResolution     TODO: add argument description\r
713   @param  ColorDepth             TODO: add argument description\r
714   @param  RefreshRate            TODO: add argument description\r
715 \r
716   @return TODO: add return values\r
717 \r
718 **/\r
719 EFI_STATUS\r
720 EFIAPI\r
721 WinNtGraphicsWindowOpen (\r
722   IN  EMU_IO_THUNK_PROTOCOL *This\r
723   )\r
724 {\r
725   DWORD                    NewThreadId;\r
726   GRAPHICS_PRIVATE_DATA    *Private;\r
727 \r
728   Private = AllocateZeroPool (sizeof (*Private));\r
729 \r
730   GopPrivateCreateQ (Private, &Private->QueueForRead);\r
731 \r
732   Private->GraphicsWindowIo.Size              = WinNtWndSize;\r
733   Private->GraphicsWindowIo.CheckKey          = WinNtWndCheckKey;\r
734   Private->GraphicsWindowIo.GetKey            = WinNtWndGetKey;\r
735   Private->GraphicsWindowIo.KeySetState       = WinNtWndKeySetState;\r
736   Private->GraphicsWindowIo.RegisterKeyNotify = WinNtWndRegisterKeyNotify;\r
737   Private->GraphicsWindowIo.Blt               = WinNtWndBlt;\r
738   Private->GraphicsWindowIo.CheckPointer      = WinNtWndCheckPointer;\r
739   Private->GraphicsWindowIo.GetPointerState   = WinNtWndGetPointerState;\r
740 \r
741   Private->WindowName = This->ConfigString;\r
742   //\r
743   // Initialize a Thread Local Storge variable slot. We use TLS to get the\r
744   // correct Private data instance into the windows thread.\r
745   //\r
746   if (mTlsIndex == TLS_OUT_OF_INDEXES) {\r
747     ASSERT (0 == mTlsIndexUseCount);\r
748     mTlsIndex = TlsAlloc ();\r
749   }\r
750 \r
751   //\r
752   // always increase the use count!\r
753   //\r
754   mTlsIndexUseCount++;\r
755 \r
756   Private->ThreadInited = CreateSemaphore (NULL, 0, 1, NULL);\r
757   Private->ThreadHandle = CreateThread (\r
758                             NULL,\r
759                             0,\r
760                             WinNtGopThreadWinMain,\r
761                             (VOID *) Private,\r
762                             0,\r
763                             &NewThreadId\r
764                             );\r
765 \r
766   //\r
767   // The other thread has entered the windows message loop so we can\r
768   // continue our initialization.\r
769   //\r
770   WaitForSingleObject (Private->ThreadInited, INFINITE);\r
771   CloseHandle (Private->ThreadInited);\r
772 \r
773   This->Private = Private;\r
774   This->Interface = &Private->GraphicsWindowIo;\r
775 \r
776   return EFI_SUCCESS;\r
777 }\r
778 \r
779 EFI_STATUS\r
780 EFIAPI\r
781 WinNtGraphicsWindowClose (\r
782   IN  EMU_IO_THUNK_PROTOCOL   *This\r
783 )\r
784 {\r
785   GRAPHICS_PRIVATE_DATA       *Private;\r
786 \r
787   Private = (GRAPHICS_PRIVATE_DATA *)This->Private;\r
788 \r
789   //\r
790   // BugBug: Shutdown GOP Hardware and any child devices.\r
791   //\r
792   SendMessage (Private->WindowHandle, WM_DESTROY, 0, 0);\r
793   CloseHandle (Private->ThreadHandle);\r
794 \r
795   mTlsIndexUseCount--;\r
796 \r
797   //\r
798   // The callback function for another window could still be called,\r
799   // so we need to make sure there are no more users of mTlsIndex.\r
800   //\r
801   if (0 == mTlsIndexUseCount) {\r
802     ASSERT (TLS_OUT_OF_INDEXES != mTlsIndex);\r
803 \r
804     TlsFree (mTlsIndex);\r
805     mTlsIndex = TLS_OUT_OF_INDEXES;\r
806 \r
807     UnregisterClass (\r
808       Private->WindowsClass.lpszClassName,\r
809       Private->WindowsClass.hInstance\r
810     );\r
811   }\r
812 \r
813 \r
814   GopPrivateDestroyQ (Private, &Private->QueueForRead);\r
815   return EFI_SUCCESS;\r
816 }\r
817 \r
818 \r
819 EMU_IO_THUNK_PROTOCOL mWinNtWndThunkIo = {\r
820   &gEmuGraphicsWindowProtocolGuid,\r
821   NULL,\r
822   NULL,\r
823   0,\r
824   WinNtGraphicsWindowOpen,\r
825   WinNtGraphicsWindowClose,\r
826   NULL\r
827 };\r