3 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
4 SPDX-License-Identifier: BSD-2-Clause-Patent
12 This file produces the graphics abstration of GOP. It is called by
13 WinNtGopDriver.c file which deals with the UEFI 2.0 driver model.
14 This file just does graphics.
21 DWORD mTlsIndex
= TLS_OUT_OF_INDEXES
;
22 DWORD mTlsIndexUseCount
= 0; // lets us know when we can free mTlsIndex.
25 WinNtGopConvertParamToEfiKeyShiftState (
26 IN GRAPHICS_PRIVATE_DATA
*Private
,
34 // BUGBUG: Only GetAsyncKeyState() and GetKeyState() can distinguish
35 // left and right Ctrl, and Shift key.
36 // Neither of the two is defined in EFI_WIN_NT_THUNK_PROTOCOL.
37 // Therefor, we can not set the correct Shift state here.
40 if ((*lParam
& GOP_EXTENDED_KEY
) == GOP_EXTENDED_KEY
) {
41 Private
->RightShift
= Flag
;
43 Private
->LeftShift
= Flag
;
49 Private
->LeftShift
= Flag
;
53 Private
->RightShift
= Flag
;
57 if ((*lParam
& GOP_EXTENDED_KEY
) == GOP_EXTENDED_KEY
) {
58 Private
->RightCtrl
= Flag
;
60 Private
->LeftCtrl
= Flag
;
66 Private
->LeftCtrl
= Flag
;
70 Private
->RightCtrl
= Flag
;
74 Private
->LeftLogo
= Flag
;
78 Private
->RightLogo
= Flag
;
85 // BUGBUG: PrintScreen/SysRq can not trigger WM_KEYDOWN message,
86 // so SySReq shift state is not supported here.
89 Private
->SysReq
= Flag
;
95 if ((*lParam
& GOP_EXTENDED_KEY
) == GOP_EXTENDED_KEY
) {
96 Private
->RightAlt
= Flag
;
98 Private
->LeftAlt
= Flag
;
109 WinNtGopConvertParamToEfiKey (
110 IN GRAPHICS_PRIVATE_DATA
*Private
,
113 IN EFI_INPUT_KEY
*Key
120 case VK_HOME
: Key
->ScanCode
= SCAN_HOME
;
123 case VK_END
: Key
->ScanCode
= SCAN_END
;
126 case VK_LEFT
: Key
->ScanCode
= SCAN_LEFT
;
129 case VK_RIGHT
: Key
->ScanCode
= SCAN_RIGHT
;
132 case VK_UP
: Key
->ScanCode
= SCAN_UP
;
135 case VK_DOWN
: Key
->ScanCode
= SCAN_DOWN
;
138 case VK_DELETE
: Key
->ScanCode
= SCAN_DELETE
;
141 case VK_INSERT
: Key
->ScanCode
= SCAN_INSERT
;
144 case VK_PRIOR
: Key
->ScanCode
= SCAN_PAGE_UP
;
147 case VK_NEXT
: Key
->ScanCode
= SCAN_PAGE_DOWN
;
150 case VK_ESCAPE
: Key
->ScanCode
= SCAN_ESC
;
154 case VK_F1
: Key
->ScanCode
= SCAN_F1
;
157 case VK_F2
: Key
->ScanCode
= SCAN_F2
;
160 case VK_F3
: Key
->ScanCode
= SCAN_F3
;
163 case VK_F4
: Key
->ScanCode
= SCAN_F4
;
166 case VK_F5
: Key
->ScanCode
= SCAN_F5
;
169 case VK_F6
: Key
->ScanCode
= SCAN_F6
;
172 case VK_F7
: Key
->ScanCode
= SCAN_F7
;
175 case VK_F8
: Key
->ScanCode
= SCAN_F8
;
178 case VK_F9
: Key
->ScanCode
= SCAN_F9
;
181 case VK_F11
: Key
->ScanCode
= SCAN_F11
;
184 case VK_F12
: Key
->ScanCode
= SCAN_F12
;
188 case VK_F13
: Key
->ScanCode
= SCAN_F13
;
191 case VK_F14
: Key
->ScanCode
= SCAN_F14
;
194 case VK_F15
: Key
->ScanCode
= SCAN_F15
;
197 case VK_F16
: Key
->ScanCode
= SCAN_F16
;
200 case VK_F17
: Key
->ScanCode
= SCAN_F17
;
203 case VK_F18
: Key
->ScanCode
= SCAN_F18
;
206 case VK_F19
: Key
->ScanCode
= SCAN_F19
;
209 case VK_F20
: Key
->ScanCode
= SCAN_F20
;
212 case VK_F21
: Key
->ScanCode
= SCAN_F21
;
215 case VK_F22
: Key
->ScanCode
= SCAN_F22
;
218 case VK_F23
: Key
->ScanCode
= SCAN_F23
;
221 case VK_F24
: Key
->ScanCode
= SCAN_F24
;
224 case VK_PAUSE
: Key
->ScanCode
= SCAN_PAUSE
;
232 Private
->NumLock
= (BOOLEAN
)(!Private
->NumLock
);
236 Private
->ScrollLock
= (BOOLEAN
)(!Private
->ScrollLock
);
240 Private
->CapsLock
= (BOOLEAN
)(!Private
->CapsLock
);
245 return (WinNtGopConvertParamToEfiKeyShiftState (Private
, wParam
, lParam
, TRUE
)) == TRUE
? TRUE
: Flag
;
249 // GOP Protocol Member Functions
253 Change the resolution and resize of the window
258 IN EMU_GRAPHICS_WINDOW_PROTOCOL
*GraphicsIo
,
263 RETURN_STATUS RStatus
;
264 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION Info
;
265 GRAPHICS_PRIVATE_DATA
*Private
;
267 BITMAPV4HEADER
*VirtualScreenInfo
;
268 FRAME_BUFFER_CONFIGURE
*FrameBufferConfigure
;
269 UINTN FrameBufferConfigureSize
;
271 Private
= GRAPHICS_PRIVATE_DATA_FROM_THIS (GraphicsIo
);
274 // Allocate DIB frame buffer directly from NT for performance enhancement
275 // This buffer is the virtual screen/frame buffer.
277 VirtualScreenInfo
= HeapAlloc (
280 Width
* Height
* sizeof (RGBQUAD
) + sizeof (BITMAPV4HEADER
)
282 if (VirtualScreenInfo
== NULL
) {
283 return EFI_OUT_OF_RESOURCES
;
287 // Update the virtual screen info data structure
288 // Use negative Height to make sure screen/buffer are using the same coordinate.
290 VirtualScreenInfo
->bV4Size
= sizeof (BITMAPV4HEADER
);
291 VirtualScreenInfo
->bV4Width
= Width
;
292 VirtualScreenInfo
->bV4Height
= -(LONG
)Height
;
293 VirtualScreenInfo
->bV4Planes
= 1;
294 VirtualScreenInfo
->bV4BitCount
= 32;
298 VirtualScreenInfo
->bV4V4Compression
= BI_RGB
;
300 Info
.HorizontalResolution
= Width
;
301 Info
.VerticalResolution
= Height
;
302 Info
.PixelFormat
= PixelBlueGreenRedReserved8BitPerColor
;
303 Info
.PixelsPerScanLine
= Width
;
304 FrameBufferConfigureSize
= 0;
305 RStatus
= FrameBufferBltConfigure (VirtualScreenInfo
+ 1, &Info
, NULL
, &FrameBufferConfigureSize
);
306 ASSERT (RStatus
== EFI_BUFFER_TOO_SMALL
);
307 FrameBufferConfigure
= AllocatePool (FrameBufferConfigureSize
);
308 if (FrameBufferConfigure
== NULL
) {
309 HeapFree (GetProcessHeap (), 0, VirtualScreenInfo
);
310 return EFI_OUT_OF_RESOURCES
;
313 RStatus
= FrameBufferBltConfigure (VirtualScreenInfo
+ 1, &Info
, FrameBufferConfigure
, &FrameBufferConfigureSize
);
314 ASSERT_RETURN_ERROR (RStatus
);
316 if (Private
->FrameBufferConfigure
!= NULL
) {
317 FreePool (Private
->FrameBufferConfigure
);
320 Private
->FrameBufferConfigure
= FrameBufferConfigure
;
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()
327 if (Private
->VirtualScreenInfo
!= NULL
) {
328 HeapFree (GetProcessHeap (), 0, Private
->VirtualScreenInfo
);
331 Private
->VirtualScreenInfo
= VirtualScreenInfo
;
333 Private
->Width
= Width
;
334 Private
->Height
= Height
;
337 // Use the AdjuctWindowRect fuction to calculate the real width and height
338 // of the new window including the border and caption
343 Rect
.bottom
= Height
;
345 AdjustWindowRect (&Rect
, WS_OVERLAPPEDWINDOW
, 0);
347 Width
= Rect
.right
- Rect
.left
;
348 Height
= Rect
.bottom
- Rect
.top
;
351 // Retrieve the original window position information
353 GetWindowRect (Private
->WindowHandle
, &Rect
);
356 // Adjust the window size
358 MoveWindow (Private
->WindowHandle
, Rect
.left
, Rect
.top
, (INT32
)Width
, (INT32
)Height
, TRUE
);
364 Blt pixels from the rectangle (Width X Height) formed by the BltBuffer
365 onto the graphics screen starting a location (X, Y). (0, 0) is defined as
366 the upper left hand side of the screen. (X, Y) can be outside of the
367 current screen geometry and the BltBuffer will be cliped when it is
368 displayed. X and Y can be negative or positive. If Width or Height is
369 bigger than the current video screen the image will be clipped.
371 @param This Protocol instance pointer.
372 @param X X location on graphics screen.
373 @param Y Y location on the graphics screen.
374 @param Width Width of BltBuffer.
375 @param Height Height of BltBuffer
376 @param BltOperation Operation to perform on BltBuffer and video memory
377 @param BltBuffer Buffer containing data to blt into video buffer.
378 This buffer has a size of
379 Width*Height*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
380 @param SourceX If the BltOperation is a EfiCopyBlt this is the
381 source of the copy. For other BLT operations this
382 argument is not used.
383 @param SourceX If the BltOperation is a EfiCopyBlt this is the
384 source of the copy. For other BLT operations this
385 argument is not used.
387 @retval EFI_SUCCESS The palette is updated with PaletteArray.
388 @retval EFI_INVALID_PARAMETER BltOperation is not valid.
389 @retval EFI_DEVICE_ERROR A hardware error occurred writing to the video
393 // TODO: SourceY - add argument and description to function comment
394 // TODO: DestinationX - add argument and description to function comment
395 // TODO: DestinationY - add argument and description to function comment
396 // TODO: Delta - add argument and description to function comment
399 IN EMU_GRAPHICS_WINDOW_PROTOCOL
*GraphicsIo
,
400 IN EFI_UGA_PIXEL
*BltBuffer OPTIONAL
,
401 IN EFI_UGA_BLT_OPERATION BltOperation
,
402 IN EMU_GRAPHICS_WINDOWS__BLT_ARGS
*Args
405 RETURN_STATUS RStatus
;
406 GRAPHICS_PRIVATE_DATA
*Private
;
409 Private
= GRAPHICS_PRIVATE_DATA_FROM_THIS (GraphicsIo
);
410 RStatus
= FrameBufferBlt (
411 Private
->FrameBufferConfigure
,
422 if (RETURN_ERROR (RStatus
)) {
423 return (EFI_STATUS
)RStatus
;
426 if (BltOperation
!= EfiBltVideoToBltBuffer
) {
428 // Mark the area we just blted as Invalid so WM_PAINT will update.
430 Rect
.left
= (LONG
)Args
->DestinationX
;
431 Rect
.top
= (LONG
)Args
->DestinationY
;
432 Rect
.right
= (LONG
)(Args
->DestinationX
+ Args
->Width
);
433 Rect
.bottom
= (LONG
)(Args
->DestinationY
+ Args
->Height
);
434 InvalidateRect (Private
->WindowHandle
, &Rect
, FALSE
);
437 // Send the WM_PAINT message to the thread that is drawing the window. We
438 // are in the main thread and the window drawing is in a child thread.
439 // There is a child thread per window. We have no CriticalSection or Mutex
440 // since we write the data and the other thread displays the data. While
441 // we may miss some data for a short period of time this is no different than
442 // a write combining on writes to a frame buffer.
445 UpdateWindow (Private
->WindowHandle
);
452 Win32 Windows event handler.
456 @return See Win32 Book
459 // TODO: hwnd - add argument and description to function comment
460 // TODO: iMsg - add argument and description to function comment
461 // TODO: wParam - add argument and description to function comment
462 // TODO: lParam - add argument and description to function comment
465 WinNtGopThreadWindowProc (
472 GRAPHICS_PRIVATE_DATA
*Private
;
474 PAINTSTRUCT PaintStruct
;
482 // Use mTlsIndex global to get a Thread Local Storage version of Private.
483 // This works since each Gop protocol has a unique Private data instance and
487 Private
= TlsGetValue (mTlsIndex
);
488 ASSERT (NULL
!= Private
);
492 Handle
= BeginPaint (hwnd
, &PaintStruct
);
495 Handle
, // Destination Device Context
496 0, // Destination X - 0
497 0, // Destination Y - 0
498 Private
->Width
, // Width
499 Private
->Height
, // Height
502 0, // DIB Start Scan Line
503 Private
->Height
, // Number of scan lines
504 Private
->VirtualScreenInfo
+ 1, // Address of array of DIB bits
505 (BITMAPINFO
*)Private
->VirtualScreenInfo
, // Address of structure with bitmap info
506 DIB_RGB_COLORS
// RGB or palette indexes
509 EndPaint (hwnd
, &PaintStruct
);
513 // F10 and the ALT key do not create a WM_KEYDOWN message, thus this special case
514 // WM_SYSKEYDOWN is posted when F10 is pressed or
515 // holds down ALT key and then presses another key.
520 Key
.UnicodeChar
= CHAR_NULL
;
523 Key
.ScanCode
= SCAN_F10
;
524 Key
.UnicodeChar
= CHAR_NULL
;
525 GopPrivateAddKey (Private
, Key
);
530 // If ALT or ALT + modifier key is pressed.
532 if (WinNtGopConvertParamToEfiKey (Private
, &wParam
, &lParam
, &Key
)) {
533 if (Key
.ScanCode
!= 0) {
535 // If ALT is pressed with other ScanCode.
536 // Always revers the left Alt for simple.
538 Private
->LeftAlt
= TRUE
;
541 GopPrivateAddKey (Private
, Key
);
543 // When Alt is released there is no windoes message, so
544 // clean it after using it.
546 Private
->RightAlt
= FALSE
;
547 Private
->LeftAlt
= FALSE
;
555 // The ESC key also generate WM_CHAR.
557 if (wParam
== 0x1B) {
561 if (AltIsPress
== TRUE
) {
563 // If AltIsPress is true that means the Alt key is pressed.
565 Private
->LeftAlt
= TRUE
;
568 for (Index
= 0; Index
< (lParam
& 0xffff); Index
++) {
570 Key
.UnicodeChar
= (CHAR16
)wParam
;
571 Key
.ScanCode
= SCAN_NULL
;
572 GopPrivateAddKey (Private
, Key
);
576 if (AltIsPress
== TRUE
) {
578 // When Alt is released there is no windoes message, so
579 // clean it after using it.
581 Private
->LeftAlt
= FALSE
;
582 Private
->RightAlt
= FALSE
;
589 // ALT is pressed with another key released
591 WinNtGopConvertParamToEfiKeyShiftState (Private
, &wParam
, &lParam
, FALSE
);
595 Key
.ScanCode
= SCAN_NULL
;
596 Key
.UnicodeChar
= CHAR_NULL
;
598 // A value key press will cause a WM_KEYDOWN first, then cause a WM_CHAR
599 // So if there is no modifier key updated, skip the WM_KEYDOWN even.
601 if (WinNtGopConvertParamToEfiKey (Private
, &wParam
, &lParam
, &Key
)) {
603 // Support the partial keystroke, add all keydown event into the queue.
605 GopPrivateAddKey (Private
, Key
);
611 WinNtGopConvertParamToEfiKeyShiftState (Private
, &wParam
, &lParam
, FALSE
);
615 PosX
= GET_X_LPARAM (lParam
);
616 PosY
= GET_Y_LPARAM (lParam
);
618 if (Private
->PointerPreviousX
!= PosX
) {
619 Private
->PointerState
.RelativeMovementX
+= (PosX
- Private
->PointerPreviousX
);
620 Private
->PointerPreviousX
= PosX
;
621 Private
->PointerStateChanged
= TRUE
;
624 if (Private
->PointerPreviousY
!= PosY
) {
625 Private
->PointerState
.RelativeMovementY
+= (PosY
- Private
->PointerPreviousY
);
626 Private
->PointerPreviousY
= PosY
;
627 Private
->PointerStateChanged
= TRUE
;
630 Private
->PointerState
.RelativeMovementZ
= 0;
634 Private
->PointerState
.LeftButton
= TRUE
;
635 Private
->PointerStateChanged
= TRUE
;
639 Private
->PointerState
.LeftButton
= FALSE
;
640 Private
->PointerStateChanged
= TRUE
;
644 Private
->PointerState
.RightButton
= TRUE
;
645 Private
->PointerStateChanged
= TRUE
;
649 Private
->PointerState
.RightButton
= FALSE
;
650 Private
->PointerStateChanged
= TRUE
;
655 // This close message is issued by user, core is not aware of this,
656 // so don't release the window display resource, just hide the window.
658 ShowWindow (Private
->WindowHandle
, SW_HIDE
);
662 DestroyWindow (hwnd
);
665 HeapFree (GetProcessHeap (), 0, Private
->VirtualScreenInfo
);
673 return DefWindowProc (hwnd
, iMsg
, wParam
, lParam
);
677 This thread simulates the end of WinMain () application. Each Window needs
678 to process its events. The messages are dispatched to
679 WinNtGopThreadWindowProc ().
680 Be very careful since WinNtGopThreadWinMain () and WinNtGopThreadWindowProc ()
681 are running in a separate thread. We have to do this to process the events.
683 @param lpParameter Handle of window to manage.
685 @return if a WM_QUIT message is returned exit.
690 WinNtGopThreadWinMain (
695 GRAPHICS_PRIVATE_DATA
*Private
;
698 Private
= (GRAPHICS_PRIVATE_DATA
*)lpParameter
;
699 ASSERT (NULL
!= Private
);
702 // Since each thread has unique private data, save the private data in Thread
703 // Local Storage slot. Then the shared global mTlsIndex can be used to get
704 // thread specific context.
706 TlsSetValue (mTlsIndex
, Private
);
708 Private
->ThreadId
= GetCurrentThreadId ();
710 Private
->WindowsClass
.cbSize
= sizeof (WNDCLASSEX
);
711 Private
->WindowsClass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_OWNDC
;
712 Private
->WindowsClass
.lpfnWndProc
= WinNtGopThreadWindowProc
;
713 Private
->WindowsClass
.cbClsExtra
= 0;
714 Private
->WindowsClass
.cbWndExtra
= 0;
715 Private
->WindowsClass
.hInstance
= NULL
;
716 Private
->WindowsClass
.hIcon
= LoadIcon (NULL
, IDI_APPLICATION
);
717 Private
->WindowsClass
.hCursor
= LoadCursor (NULL
, IDC_ARROW
);
718 Private
->WindowsClass
.hbrBackground
= (HBRUSH
)(UINTN
)COLOR_WINDOW
;
719 Private
->WindowsClass
.lpszMenuName
= NULL
;
720 Private
->WindowsClass
.lpszClassName
= WIN_NT_GOP_CLASS_NAME
;
721 Private
->WindowsClass
.hIconSm
= LoadIcon (NULL
, IDI_APPLICATION
);
724 // Use 100 x 100 as initial Window size.
726 Private
->Width
= 100;
727 Private
->Height
= 100;
730 // This call will fail after the first time, but thats O.K. since we only need
731 // WIN_NT_GOP_CLASS_NAME to exist to create the window.
733 RegisterClassEx (&Private
->WindowsClass
);
736 // Setting Rect values to allow for the AdjustWindowRect to provide
737 // us the correct sizes for the client area when doing the CreateWindowEx
740 Rect
.bottom
= Private
->Height
;
742 Rect
.right
= Private
->Width
;
744 AdjustWindowRect (&Rect
, WS_OVERLAPPEDWINDOW
, 0);
746 Private
->WindowHandle
= CreateWindowEx (
748 WIN_NT_GOP_CLASS_NAME
,
753 Rect
.right
- Rect
.left
,
754 Rect
.bottom
- Rect
.top
,
762 // The reset of this thread is the standard windows program. We need a separate
763 // thread since we must process the message loop to make windows act like
767 ShowWindow (Private
->WindowHandle
, SW_SHOW
);
768 UpdateWindow (Private
->WindowHandle
);
771 // Let the main thread get some work done
773 ReleaseSemaphore (Private
->ThreadInited
, 1, NULL
);
776 // This is the message loop that all Windows programs need.
778 while (GetMessage (&Message
, Private
->WindowHandle
, 0, 0)) {
779 TranslateMessage (&Message
);
780 DispatchMessage (&Message
);
783 return (DWORD
)Message
.wParam
;
787 TODO: Add function description
789 @param Private TODO: add argument description
790 @param HorizontalResolution TODO: add argument description
791 @param VerticalResolution TODO: add argument description
792 @param ColorDepth TODO: add argument description
793 @param RefreshRate TODO: add argument description
795 @return TODO: add return values
800 WinNtGraphicsWindowOpen (
801 IN EMU_IO_THUNK_PROTOCOL
*This
805 GRAPHICS_PRIVATE_DATA
*Private
;
807 Private
= AllocateZeroPool (sizeof (*Private
));
809 GopPrivateCreateQ (Private
, &Private
->QueueForRead
);
811 Private
->GraphicsWindowIo
.Size
= WinNtWndSize
;
812 Private
->GraphicsWindowIo
.CheckKey
= WinNtWndCheckKey
;
813 Private
->GraphicsWindowIo
.GetKey
= WinNtWndGetKey
;
814 Private
->GraphicsWindowIo
.KeySetState
= WinNtWndKeySetState
;
815 Private
->GraphicsWindowIo
.RegisterKeyNotify
= WinNtWndRegisterKeyNotify
;
816 Private
->GraphicsWindowIo
.Blt
= WinNtWndBlt
;
817 Private
->GraphicsWindowIo
.CheckPointer
= WinNtWndCheckPointer
;
818 Private
->GraphicsWindowIo
.GetPointerState
= WinNtWndGetPointerState
;
820 Private
->WindowName
= This
->ConfigString
;
822 // Initialize a Thread Local Storge variable slot. We use TLS to get the
823 // correct Private data instance into the windows thread.
825 if (mTlsIndex
== TLS_OUT_OF_INDEXES
) {
826 ASSERT (0 == mTlsIndexUseCount
);
827 mTlsIndex
= TlsAlloc ();
831 // always increase the use count!
835 Private
->ThreadInited
= CreateSemaphore (NULL
, 0, 1, NULL
);
836 Private
->ThreadHandle
= CreateThread (
839 WinNtGopThreadWinMain
,
846 // The other thread has entered the windows message loop so we can
847 // continue our initialization.
849 WaitForSingleObject (Private
->ThreadInited
, INFINITE
);
850 CloseHandle (Private
->ThreadInited
);
852 This
->Private
= Private
;
853 This
->Interface
= &Private
->GraphicsWindowIo
;
860 WinNtGraphicsWindowClose (
861 IN EMU_IO_THUNK_PROTOCOL
*This
864 GRAPHICS_PRIVATE_DATA
*Private
;
866 Private
= (GRAPHICS_PRIVATE_DATA
*)This
->Private
;
869 // BugBug: Shutdown GOP Hardware and any child devices.
871 SendMessage (Private
->WindowHandle
, WM_DESTROY
, 0, 0);
872 CloseHandle (Private
->ThreadHandle
);
877 // The callback function for another window could still be called,
878 // so we need to make sure there are no more users of mTlsIndex.
880 if (0 == mTlsIndexUseCount
) {
881 ASSERT (TLS_OUT_OF_INDEXES
!= mTlsIndex
);
884 mTlsIndex
= TLS_OUT_OF_INDEXES
;
887 Private
->WindowsClass
.lpszClassName
,
888 Private
->WindowsClass
.hInstance
892 GopPrivateDestroyQ (Private
, &Private
->QueueForRead
);
896 EMU_IO_THUNK_PROTOCOL mWinNtWndThunkIo
= {
897 &gEmuGraphicsWindowProtocolGuid
,
901 WinNtGraphicsWindowOpen
,
902 WinNtGraphicsWindowClose
,