1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Console Server DLL
4  * FILE:            win32ss/user/winsrv/consrv/frontends/gui/conwnd.c
5  * PURPOSE:         GUI Console Window Class
6  * PROGRAMMERS:     G� van Geldorp
7  *                  Johannes Anderwald
8  *                  Jeffrey Morlan
9  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10  *                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
11  */
12 
13 /* INCLUDES *******************************************************************/
14 
15 #include <consrv.h>
16 #include <intrin.h>
17 #include <windowsx.h>
18 #include <shellapi.h>
19 
20 #define NDEBUG
21 #include <debug.h>
22 
23 #include "concfg/font.h"
24 #include "guiterm.h"
25 #include "resource.h"
26 
27 /* GLOBALS ********************************************************************/
28 
29 // #define PM_CREATE_CONSOLE       (WM_APP + 1)
30 // #define PM_DESTROY_CONSOLE      (WM_APP + 2)
31 
32 // See guiterm.c
33 #define CONGUI_MIN_WIDTH      10
34 #define CONGUI_MIN_HEIGHT     10
35 #define CONGUI_UPDATE_TIME    0
36 #define CONGUI_UPDATE_TIMER   1
37 
38 #define CURSOR_BLINK_TIME 500
39 
40 
41 /**************************************************************\
42 \** Define the Console Leader Process for the console window **/
43 #define GWLP_CONWND_ALLOC      (2 * sizeof(LONG_PTR))
44 #define GWLP_CONSOLE_LEADER_PID 0
45 #define GWLP_CONSOLE_LEADER_TID 4
46 
47 VOID
48 SetConWndConsoleLeaderCID(IN PGUI_CONSOLE_DATA GuiData)
49 {
50     PCONSOLE_PROCESS_DATA ProcessData;
51     CLIENT_ID ConsoleLeaderCID;
52 
53     ProcessData = ConSrvGetConsoleLeaderProcess(GuiData->Console);
54     ConsoleLeaderCID = ProcessData->Process->ClientId;
55     SetWindowLongPtrW(GuiData->hWindow, GWLP_CONSOLE_LEADER_PID,
56                       (LONG_PTR)(ConsoleLeaderCID.UniqueProcess));
57     SetWindowLongPtrW(GuiData->hWindow, GWLP_CONSOLE_LEADER_TID,
58                       (LONG_PTR)(ConsoleLeaderCID.UniqueThread));
59 }
60 /**************************************************************/
61 
62 HICON   ghDefaultIcon = NULL;
63 HICON   ghDefaultIconSm = NULL;
64 HCURSOR ghDefaultCursor = NULL;
65 
66 typedef struct _GUICONSOLE_MENUITEM
67 {
68     UINT uID;
69     const struct _GUICONSOLE_MENUITEM *SubMenu;
70     WORD wCmdID;
71 } GUICONSOLE_MENUITEM, *PGUICONSOLE_MENUITEM;
72 
73 static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems[] =
74 {
75     { IDS_MARK,         NULL, ID_SYSTEM_EDIT_MARK       },
76     { IDS_COPY,         NULL, ID_SYSTEM_EDIT_COPY       },
77     { IDS_PASTE,        NULL, ID_SYSTEM_EDIT_PASTE      },
78     { IDS_SELECTALL,    NULL, ID_SYSTEM_EDIT_SELECTALL  },
79     { IDS_SCROLL,       NULL, ID_SYSTEM_EDIT_SCROLL     },
80     { IDS_FIND,         NULL, ID_SYSTEM_EDIT_FIND       },
81 
82     { 0, NULL, 0 } /* End of list */
83 };
84 
85 static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems[] =
86 {
87     { IDS_EDIT,         GuiConsoleEditMenuItems, 0 },
88     { IDS_DEFAULTS,     NULL, ID_SYSTEM_DEFAULTS   },
89     { IDS_PROPERTIES,   NULL, ID_SYSTEM_PROPERTIES },
90 
91     { 0, NULL, 0 } /* End of list */
92 };
93 
94 /*
95  * Default 16-color palette for foreground and background
96  * (corresponding flags in comments).
97  */
98 const COLORREF s_Colors[16] =
99 {
100     RGB(0, 0, 0),       // (Black)
101     RGB(0, 0, 128),     // BLUE
102     RGB(0, 128, 0),     // GREEN
103     RGB(0, 128, 128),   // BLUE  | GREEN
104     RGB(128, 0, 0),     // RED
105     RGB(128, 0, 128),   // BLUE  | RED
106     RGB(128, 128, 0),   // GREEN | RED
107     RGB(192, 192, 192), // BLUE  | GREEN | RED
108 
109     RGB(128, 128, 128), // (Grey)  INTENSITY
110     RGB(0, 0, 255),     // BLUE  | INTENSITY
111     RGB(0, 255, 0),     // GREEN | INTENSITY
112     RGB(0, 255, 255),   // BLUE  | GREEN | INTENSITY
113     RGB(255, 0, 0),     // RED   | INTENSITY
114     RGB(255, 0, 255),   // BLUE  | RED   | INTENSITY
115     RGB(255, 255, 0),   // GREEN | RED   | INTENSITY
116     RGB(255, 255, 255)  // BLUE  | GREEN | RED | INTENSITY
117 };
118 
119 /* FUNCTIONS ******************************************************************/
120 
121 static LRESULT CALLBACK
122 ConWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
123 
124 BOOLEAN
125 RegisterConWndClass(IN HINSTANCE hInstance)
126 {
127     WNDCLASSEXW WndClass;
128     ATOM WndClassAtom;
129 
130     ghDefaultIcon   = LoadImageW(hInstance,
131                                  MAKEINTRESOURCEW(IDI_TERMINAL),
132                                  IMAGE_ICON,
133                                  GetSystemMetrics(SM_CXICON),
134                                  GetSystemMetrics(SM_CYICON),
135                                  LR_SHARED);
136     ghDefaultIconSm = LoadImageW(hInstance,
137                                  MAKEINTRESOURCEW(IDI_TERMINAL),
138                                  IMAGE_ICON,
139                                  GetSystemMetrics(SM_CXSMICON),
140                                  GetSystemMetrics(SM_CYSMICON),
141                                  LR_SHARED);
142     ghDefaultCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_ARROW));
143 
144     WndClass.cbSize = sizeof(WNDCLASSEXW);
145     WndClass.lpszClassName = GUI_CONWND_CLASS;
146     WndClass.lpfnWndProc = ConWndProc;
147     WndClass.style = CS_DBLCLKS /* | CS_HREDRAW | CS_VREDRAW */;
148     WndClass.hInstance = hInstance;
149     WndClass.hIcon = ghDefaultIcon;
150     WndClass.hIconSm = ghDefaultIconSm;
151     WndClass.hCursor = ghDefaultCursor;
152     WndClass.hbrBackground = NULL;
153     WndClass.lpszMenuName = NULL;
154     WndClass.cbClsExtra = 0;
155     WndClass.cbWndExtra = GWLP_CONWND_ALLOC;
156 
157     WndClassAtom = RegisterClassExW(&WndClass);
158     if (WndClassAtom == 0)
159     {
160         DPRINT1("Failed to register GUI console class\n");
161     }
162     else
163     {
164         NtUserConsoleControl(GuiConsoleWndClassAtom, &WndClassAtom, sizeof(ATOM));
165     }
166 
167     return (WndClassAtom != 0);
168 }
169 
170 BOOLEAN
171 UnRegisterConWndClass(HINSTANCE hInstance)
172 {
173     return !!UnregisterClassW(GUI_CONWND_CLASS, hInstance);
174 }
175 
176 static VOID
177 AppendMenuItems(HMENU hMenu,
178                 const GUICONSOLE_MENUITEM *Items)
179 {
180     UINT i = 0;
181     WCHAR szMenuString[255];
182     HMENU hSubMenu;
183 
184     do
185     {
186         if (Items[i].uID != (UINT)-1)
187         {
188             if (LoadStringW(ConSrvDllInstance,
189                             Items[i].uID,
190                             szMenuString,
191                             ARRAYSIZE(szMenuString)) > 0)
192             {
193                 if (Items[i].SubMenu != NULL)
194                 {
195                     hSubMenu = CreatePopupMenu();
196                     if (hSubMenu != NULL)
197                     {
198                         AppendMenuItems(hSubMenu, Items[i].SubMenu);
199 
200                         if (!AppendMenuW(hMenu,
201                                          MF_STRING | MF_POPUP,
202                                          (UINT_PTR)hSubMenu,
203                                          szMenuString))
204                         {
205                             DestroyMenu(hSubMenu);
206                         }
207                     }
208                 }
209                 else
210                 {
211                     AppendMenuW(hMenu,
212                                 MF_STRING,
213                                 Items[i].wCmdID,
214                                 szMenuString);
215                 }
216             }
217         }
218         else
219         {
220             AppendMenuW(hMenu,
221                         MF_SEPARATOR,
222                         0,
223                         NULL);
224         }
225         i++;
226     } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
227 }
228 
229 //static
230 VOID
231 CreateSysMenu(HWND hWnd)
232 {
233     MENUITEMINFOW mii;
234     HMENU hMenu;
235     PWCHAR ptrTab;
236     WCHAR szMenuStringBack[255];
237 
238     hMenu = GetSystemMenu(hWnd, FALSE);
239     if (hMenu == NULL)
240         return;
241 
242     mii.cbSize = sizeof(mii);
243     mii.fMask = MIIM_STRING;
244     mii.dwTypeData = szMenuStringBack;
245     mii.cch = ARRAYSIZE(szMenuStringBack);
246 
247     GetMenuItemInfoW(hMenu, SC_CLOSE, FALSE, &mii);
248 
249     ptrTab = wcschr(szMenuStringBack, L'\t');
250     if (ptrTab)
251     {
252         *ptrTab = L'\0';
253         mii.cch = (UINT)wcslen(szMenuStringBack);
254 
255         SetMenuItemInfoW(hMenu, SC_CLOSE, FALSE, &mii);
256     }
257 
258     AppendMenuItems(hMenu, GuiConsoleMainMenuItems);
259     DrawMenuBar(hWnd);
260 }
261 
262 static VOID
263 SendMenuEvent(PCONSRV_CONSOLE Console, UINT CmdId)
264 {
265     INPUT_RECORD er;
266 
267     DPRINT("Menu item ID: %d\n", CmdId);
268 
269     if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
270 
271     /* Send a menu event */
272     er.EventType = MENU_EVENT;
273     er.Event.MenuEvent.dwCommandId = CmdId;
274     ConioProcessInputEvent(Console, &er);
275 
276     LeaveCriticalSection(&Console->Lock);
277 }
278 
279 static VOID
280 Copy(PGUI_CONSOLE_DATA GuiData);
281 static VOID
282 Paste(PGUI_CONSOLE_DATA GuiData);
283 static VOID
284 UpdateSelection(PGUI_CONSOLE_DATA GuiData,
285                 PCOORD SelectionAnchor OPTIONAL,
286                 PCOORD coord);
287 
288 static VOID
289 Mark(PGUI_CONSOLE_DATA GuiData)
290 {
291     PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
292 
293     /* Clear the old selection */
294     GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
295 
296     /* Restart a new selection */
297     GuiData->dwSelectionCursor = ActiveBuffer->ViewOrigin;
298     UpdateSelection(GuiData,
299                     &GuiData->dwSelectionCursor,
300                     &GuiData->dwSelectionCursor);
301 }
302 
303 static VOID
304 SelectAll(PGUI_CONSOLE_DATA GuiData)
305 {
306     PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
307     COORD SelectionAnchor;
308 
309     /* Clear the old selection */
310     GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
311 
312     /*
313      * The selection area extends to the whole screen buffer's width.
314      */
315     SelectionAnchor.X = SelectionAnchor.Y = 0;
316     GuiData->dwSelectionCursor.X = ActiveBuffer->ScreenBufferSize.X - 1;
317 
318     /*
319      * Determine whether the selection must extend to just some part
320      * (for text-mode screen buffers) or to all of the screen buffer's
321      * height (for graphics ones).
322      */
323     if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
324     {
325         /*
326          * We select all the characters from the first line
327          * to the line where the cursor is positioned.
328          */
329         GuiData->dwSelectionCursor.Y = ActiveBuffer->CursorPosition.Y;
330     }
331     else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
332     {
333         /*
334          * We select all the screen buffer area.
335          */
336         GuiData->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
337     }
338 
339     /* Restart a new selection */
340     GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION;
341     UpdateSelection(GuiData, &SelectionAnchor, &GuiData->dwSelectionCursor);
342 }
343 
344 static LRESULT
345 OnCommand(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
346 {
347     LRESULT Ret = TRUE;
348     PCONSRV_CONSOLE Console = GuiData->Console;
349 
350     /*
351      * In case the selected menu item belongs to the user-reserved menu id range,
352      * send to him a menu event and return directly. The user must handle those
353      * reserved menu commands...
354      */
355     if (GuiData->CmdIdLow <= (UINT)wParam && (UINT)wParam <= GuiData->CmdIdHigh)
356     {
357         SendMenuEvent(Console, (UINT)wParam);
358         goto Quit;
359     }
360 
361     /* ... otherwise, perform actions. */
362     switch (wParam)
363     {
364         case ID_SYSTEM_EDIT_MARK:
365             Mark(GuiData);
366             break;
367 
368         case ID_SYSTEM_EDIT_COPY:
369             Copy(GuiData);
370             break;
371 
372         case ID_SYSTEM_EDIT_PASTE:
373             Paste(GuiData);
374             break;
375 
376         case ID_SYSTEM_EDIT_SELECTALL:
377             SelectAll(GuiData);
378             break;
379 
380         case ID_SYSTEM_EDIT_SCROLL:
381             DPRINT1("Scrolling is not handled yet\n");
382             break;
383 
384         case ID_SYSTEM_EDIT_FIND:
385             DPRINT1("Finding is not handled yet\n");
386             break;
387 
388         case ID_SYSTEM_DEFAULTS:
389             GuiConsoleShowConsoleProperties(GuiData, TRUE);
390             break;
391 
392         case ID_SYSTEM_PROPERTIES:
393             GuiConsoleShowConsoleProperties(GuiData, FALSE);
394             break;
395 
396         default:
397             Ret = FALSE;
398             break;
399     }
400 
401 Quit:
402     if (!Ret)
403         Ret = DefWindowProcW(GuiData->hWindow, WM_SYSCOMMAND, wParam, lParam);
404 
405     return Ret;
406 }
407 
408 static PGUI_CONSOLE_DATA
409 GuiGetGuiData(HWND hWnd)
410 {
411     /* This function ensures that the console pointer is not NULL */
412     PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
413     return ( ((GuiData == NULL) || (GuiData->hWindow == hWnd && GuiData->Console != NULL)) ? GuiData : NULL );
414 }
415 
416 static VOID
417 ResizeConWnd(PGUI_CONSOLE_DATA GuiData, DWORD WidthUnit, DWORD HeightUnit)
418 {
419     PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
420     SCROLLINFO sInfo;
421 
422     DWORD Width, Height;
423 
424     Width  = Buff->ViewSize.X * WidthUnit  +
425              2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
426     Height = Buff->ViewSize.Y * HeightUnit +
427              2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
428 
429     /* Set scrollbar sizes */
430     sInfo.cbSize = sizeof(sInfo);
431     sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
432     sInfo.nMin = 0;
433     if (Buff->ScreenBufferSize.Y > Buff->ViewSize.Y)
434     {
435         sInfo.nMax  = Buff->ScreenBufferSize.Y - 1;
436         sInfo.nPage = Buff->ViewSize.Y;
437         sInfo.nPos  = Buff->ViewOrigin.Y;
438         SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE);
439         Width += GetSystemMetrics(SM_CXVSCROLL);
440         ShowScrollBar(GuiData->hWindow, SB_VERT, TRUE);
441     }
442     else
443     {
444         ShowScrollBar(GuiData->hWindow, SB_VERT, FALSE);
445     }
446 
447     if (Buff->ScreenBufferSize.X > Buff->ViewSize.X)
448     {
449         sInfo.nMax  = Buff->ScreenBufferSize.X - 1;
450         sInfo.nPage = Buff->ViewSize.X;
451         sInfo.nPos  = Buff->ViewOrigin.X;
452         SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE);
453         Height += GetSystemMetrics(SM_CYHSCROLL);
454         ShowScrollBar(GuiData->hWindow, SB_HORZ, TRUE);
455     }
456     else
457     {
458         ShowScrollBar(GuiData->hWindow, SB_HORZ, FALSE);
459     }
460 
461     /* Resize the window */
462     SetWindowPos(GuiData->hWindow, NULL, 0, 0, Width, Height,
463                  SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS);
464     // NOTE: The SWP_NOCOPYBITS flag can be replaced by a subsequent call
465     // to: InvalidateRect(GuiData->hWindow, NULL, TRUE);
466 }
467 
468 
469 VOID
470 DeleteFonts(PGUI_CONSOLE_DATA GuiData)
471 {
472     ULONG i;
473     for (i = 0; i < ARRAYSIZE(GuiData->Font); ++i)
474     {
475         if (GuiData->Font[i] != NULL) DeleteObject(GuiData->Font[i]);
476         GuiData->Font[i] = NULL;
477     }
478 }
479 
480 static HFONT
481 CreateDerivedFont(HFONT OrgFont,
482                   // COORD   FontSize,
483                   ULONG   FontWeight,
484                   // BOOLEAN bItalic,
485                   BOOLEAN bUnderline,
486                   BOOLEAN bStrikeOut)
487 {
488     LOGFONTW lf;
489 
490     /* Initialize the LOGFONT structure */
491     RtlZeroMemory(&lf, sizeof(lf));
492 
493     /* Retrieve the details of the current font */
494     if (GetObjectW(OrgFont, sizeof(lf), &lf) == 0)
495         return NULL;
496 
497     /* Change the font attributes */
498     // lf.lfHeight = FontSize.Y;
499     // lf.lfWidth  = FontSize.X;
500     lf.lfWeight = FontWeight;
501     // lf.lfItalic = bItalic;
502     lf.lfUnderline = bUnderline;
503     lf.lfStrikeOut = bStrikeOut;
504 
505     /* Build a new font */
506     return CreateFontIndirectW(&lf);
507 }
508 
509 BOOL
510 InitFonts(PGUI_CONSOLE_DATA GuiData,
511           LPWSTR FaceName, // Points to a WCHAR array of LF_FACESIZE elements.
512           ULONG  FontFamily,
513           COORD  FontSize,
514           ULONG  FontWeight)
515 {
516     HDC hDC;
517     HFONT hFont;
518 
519     /*
520      * Initialize a new NORMAL font and get its character cell size.
521      */
522     /* NOTE: FontSize is always in cell height/width units (pixels) */
523     hFont = CreateConsoleFontEx((LONG)(ULONG)FontSize.Y,
524                                 (LONG)(ULONG)FontSize.X,
525                                 FaceName,
526                                 FontFamily,
527                                 FontWeight,
528                                 GuiData->Console->OutputCodePage);
529     if (hFont == NULL)
530     {
531         DPRINT1("InitFonts: CreateConsoleFontEx failed\n");
532         return FALSE;
533     }
534 
535     hDC = GetDC(GuiData->hWindow);
536     if (!GetFontCellSize(hDC, hFont, &GuiData->CharHeight, &GuiData->CharWidth))
537     {
538         DPRINT1("InitFonts: GetFontCellSize failed\n");
539         ReleaseDC(GuiData->hWindow, hDC);
540         DeleteObject(hFont);
541         return FALSE;
542     }
543     ReleaseDC(GuiData->hWindow, hDC);
544 
545     /*
546      * Initialization succeeded.
547      */
548     // Delete all the old fonts first.
549     DeleteFonts(GuiData);
550     GuiData->Font[FONT_NORMAL] = hFont;
551 
552     /*
553      * Now build the other fonts (bold, underlined, mixed).
554      */
555     GuiData->Font[FONT_BOLD] =
556         CreateDerivedFont(GuiData->Font[FONT_NORMAL],
557                           FontWeight < FW_BOLD ? FW_BOLD : FontWeight,
558                           FALSE,
559                           FALSE);
560     GuiData->Font[FONT_UNDERLINE] =
561         CreateDerivedFont(GuiData->Font[FONT_NORMAL],
562                           FontWeight,
563                           TRUE,
564                           FALSE);
565     GuiData->Font[FONT_BOLD | FONT_UNDERLINE] =
566         CreateDerivedFont(GuiData->Font[FONT_NORMAL],
567                           FontWeight < FW_BOLD ? FW_BOLD : FontWeight,
568                           TRUE,
569                           FALSE);
570 
571     /*
572      * Save the settings.
573      */
574     if (FaceName != GuiData->GuiInfo.FaceName)
575     {
576         StringCchCopyNW(GuiData->GuiInfo.FaceName, ARRAYSIZE(GuiData->GuiInfo.FaceName),
577                         FaceName, LF_FACESIZE);
578     }
579     GuiData->GuiInfo.FontFamily = FontFamily;
580     GuiData->GuiInfo.FontSize   = FontSize;
581     GuiData->GuiInfo.FontWeight = FontWeight;
582 
583     return TRUE;
584 }
585 
586 
587 static BOOL
588 OnNcCreate(HWND hWnd, LPCREATESTRUCTW Create)
589 {
590     PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)Create->lpCreateParams;
591     PCONSRV_CONSOLE Console;
592 
593     if (GuiData == NULL)
594     {
595         DPRINT1("GuiConsoleNcCreate: No GUI data\n");
596         return FALSE;
597     }
598 
599     Console = GuiData->Console;
600 
601     GuiData->hWindow = hWnd;
602     GuiData->hSysMenu = GetSystemMenu(hWnd, FALSE);
603 
604     /* Initialize the fonts */
605     if (!InitFonts(GuiData,
606                    GuiData->GuiInfo.FaceName,
607                    GuiData->GuiInfo.FontFamily,
608                    GuiData->GuiInfo.FontSize,
609                    GuiData->GuiInfo.FontWeight))
610     {
611         DPRINT1("GuiConsoleNcCreate: InitFonts failed\n");
612         GuiData->hWindow = NULL;
613         NtSetEvent(GuiData->hGuiInitEvent, NULL);
614         return FALSE;
615     }
616 
617     /* Initialize the terminal framebuffer */
618     GuiData->hMemDC  = CreateCompatibleDC(NULL);
619     GuiData->hBitmap = NULL;
620     GuiData->hSysPalette = NULL; /* Original system palette */
621 
622     /* Update the icons of the window */
623     if (GuiData->hIcon != ghDefaultIcon)
624     {
625         DefWindowProcW(GuiData->hWindow, WM_SETICON, ICON_BIG  , (LPARAM)GuiData->hIcon  );
626         DefWindowProcW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
627     }
628 
629     // FIXME: Keep these instructions here ? ///////////////////////////////////
630     Console->ActiveBuffer->CursorBlinkOn = TRUE;
631     Console->ActiveBuffer->ForceCursorOff = FALSE;
632     ////////////////////////////////////////////////////////////////////////////
633 
634     SetWindowLongPtrW(GuiData->hWindow, GWLP_USERDATA, (DWORD_PTR)GuiData);
635 
636     if (GuiData->IsWindowVisible)
637     {
638         SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
639     }
640 
641     // FIXME: HACK: Potential HACK for CORE-8129; see revision 63595.
642     //CreateSysMenu(GuiData->hWindow);
643 
644     DPRINT("OnNcCreate - setting start event\n");
645     NtSetEvent(GuiData->hGuiInitEvent, NULL);
646 
647     /* We accept dropped files */
648     DragAcceptFiles(GuiData->hWindow, TRUE);
649 
650     return (BOOL)DefWindowProcW(GuiData->hWindow, WM_NCCREATE, 0, (LPARAM)Create);
651 }
652 
653 static VOID
654 OnActivate(PGUI_CONSOLE_DATA GuiData, WPARAM wParam)
655 {
656     WORD ActivationState = LOWORD(wParam);
657 
658     DPRINT("WM_ACTIVATE - ActivationState = %d\n", ActivationState);
659 
660     if ( ActivationState == WA_ACTIVE ||
661          ActivationState == WA_CLICKACTIVE )
662     {
663         if (GuiData->GuiInfo.FullScreen)
664         {
665             EnterFullScreen(GuiData);
666             // // PostMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
667             // SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
668         }
669     }
670     else // if (ActivationState == WA_INACTIVE)
671     {
672         if (GuiData->GuiInfo.FullScreen)
673         {
674             SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
675             LeaveFullScreen(GuiData);
676             // // PostMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
677             // SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
678         }
679     }
680 
681     /*
682      * Ignore the next mouse signal when we are going to be enabled again via
683      * the mouse, in order to prevent, e.g. when we are in Edit mode, erroneous
684      * mouse actions from the user that could spoil text selection or copy/pastes.
685      */
686     if (ActivationState == WA_CLICKACTIVE)
687         GuiData->IgnoreNextMouseSignal = TRUE;
688 }
689 
690 static VOID
691 OnFocus(PGUI_CONSOLE_DATA GuiData, BOOL SetFocus)
692 {
693     PCONSRV_CONSOLE Console = GuiData->Console;
694     INPUT_RECORD er;
695 
696     if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
697 
698     /* Set console focus state */
699     Console->HasFocus = SetFocus;
700 
701     /*
702      * Set the priority of the processes of this console
703      * in accordance with the console focus state.
704      */
705     ConSrvSetConsoleProcessFocus(Console, SetFocus);
706 
707     /* Send a focus event */
708     er.EventType = FOCUS_EVENT;
709     er.Event.FocusEvent.bSetFocus = SetFocus;
710     ConioProcessInputEvent(Console, &er);
711 
712     LeaveCriticalSection(&Console->Lock);
713 
714     if (SetFocus)
715         DPRINT("TODO: Create console caret\n");
716     else
717         DPRINT("TODO: Destroy console caret\n");
718 }
719 
720 VOID
721 GetSelectionBeginEnd(PCOORD Begin, PCOORD End,
722                      PCOORD SelectionAnchor,
723                      PSMALL_RECT SmallRect)
724 {
725     if (Begin == NULL || End == NULL) return;
726 
727     *Begin = *SelectionAnchor;
728     End->X = (SelectionAnchor->X == SmallRect->Left) ? SmallRect->Right
729               /* Case X != Left, must be == Right */ : SmallRect->Left;
730     End->Y = (SelectionAnchor->Y == SmallRect->Top ) ? SmallRect->Bottom
731               /* Case Y != Top, must be == Bottom */ : SmallRect->Top;
732 
733     /* Exchange Begin / End if Begin > End lexicographically */
734     if (Begin->Y > End->Y || (Begin->Y == End->Y && Begin->X > End->X))
735     {
736         End->X = _InterlockedExchange16(&Begin->X, End->X);
737         End->Y = _InterlockedExchange16(&Begin->Y, End->Y);
738     }
739 }
740 
741 static HRGN
742 CreateSelectionRgn(PGUI_CONSOLE_DATA GuiData,
743                    BOOL LineSelection,
744                    PCOORD SelectionAnchor,
745                    PSMALL_RECT SmallRect)
746 {
747     if (!LineSelection)
748     {
749         RECT rect;
750         SmallRectToRect(GuiData, &rect, SmallRect);
751         return CreateRectRgnIndirect(&rect);
752     }
753     else
754     {
755         HRGN SelRgn;
756         COORD Begin, End;
757 
758         GetSelectionBeginEnd(&Begin, &End, SelectionAnchor, SmallRect);
759 
760         if (Begin.Y == End.Y)
761         {
762             SMALL_RECT sr;
763             RECT       r ;
764 
765             sr.Left   = Begin.X;
766             sr.Top    = Begin.Y;
767             sr.Right  = End.X;
768             sr.Bottom = End.Y;
769 
770             // Debug thingie to see whether I can put this corner case
771             // together with the previous one.
772             if (SmallRect->Left   != sr.Left  ||
773                 SmallRect->Top    != sr.Top   ||
774                 SmallRect->Right  != sr.Right ||
775                 SmallRect->Bottom != sr.Bottom)
776             {
777                 DPRINT1("\n"
778                         "SmallRect = (%d, %d, %d, %d)\n"
779                         "sr = (%d, %d, %d, %d)\n"
780                         "\n",
781                         SmallRect->Left, SmallRect->Top, SmallRect->Right, SmallRect->Bottom,
782                         sr.Left, sr.Top, sr.Right, sr.Bottom);
783             }
784 
785             SmallRectToRect(GuiData, &r, &sr);
786             SelRgn = CreateRectRgnIndirect(&r);
787         }
788         else
789         {
790             PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
791 
792             HRGN       rg1, rg2, rg3;
793             SMALL_RECT sr1, sr2, sr3;
794             RECT       r1 , r2 , r3 ;
795 
796             sr1.Left   = Begin.X;
797             sr1.Top    = Begin.Y;
798             sr1.Right  = ActiveBuffer->ScreenBufferSize.X - 1;
799             sr1.Bottom = Begin.Y;
800 
801             sr2.Left   = 0;
802             sr2.Top    = Begin.Y + 1;
803             sr2.Right  = ActiveBuffer->ScreenBufferSize.X - 1;
804             sr2.Bottom = End.Y - 1;
805 
806             sr3.Left   = 0;
807             sr3.Top    = End.Y;
808             sr3.Right  = End.X;
809             sr3.Bottom = End.Y;
810 
811             SmallRectToRect(GuiData, &r1, &sr1);
812             SmallRectToRect(GuiData, &r2, &sr2);
813             SmallRectToRect(GuiData, &r3, &sr3);
814 
815             rg1 = CreateRectRgnIndirect(&r1);
816             rg2 = CreateRectRgnIndirect(&r2);
817             rg3 = CreateRectRgnIndirect(&r3);
818 
819             CombineRgn(rg1, rg1, rg2, RGN_XOR);
820             CombineRgn(rg1, rg1, rg3, RGN_XOR);
821             DeleteObject(rg3);
822             DeleteObject(rg2);
823 
824             SelRgn = rg1;
825         }
826 
827         return SelRgn;
828     }
829 }
830 
831 static VOID
832 PaintSelectionRect(PGUI_CONSOLE_DATA GuiData, PPAINTSTRUCT pps)
833 {
834     HRGN rgnPaint = CreateRectRgnIndirect(&pps->rcPaint);
835     HRGN rgnSel   = CreateSelectionRgn(GuiData, GuiData->LineSelection,
836                                        &GuiData->Selection.dwSelectionAnchor,
837                                        &GuiData->Selection.srSelection);
838 
839     /* Invert the selection */
840 
841     int ErrorCode = CombineRgn(rgnPaint, rgnPaint, rgnSel, RGN_AND);
842     if (ErrorCode != ERROR && ErrorCode != NULLREGION)
843     {
844         InvertRgn(pps->hdc, rgnPaint);
845     }
846 
847     DeleteObject(rgnSel);
848     DeleteObject(rgnPaint);
849 }
850 
851 static VOID
852 UpdateSelection(PGUI_CONSOLE_DATA GuiData,
853                 PCOORD SelectionAnchor OPTIONAL,
854                 PCOORD coord)
855 {
856     PCONSRV_CONSOLE Console = GuiData->Console;
857     HRGN oldRgn = CreateSelectionRgn(GuiData, GuiData->LineSelection,
858                                      &GuiData->Selection.dwSelectionAnchor,
859                                      &GuiData->Selection.srSelection);
860 
861     /* Update the anchor if needed (use the old one if NULL) */
862     if (SelectionAnchor)
863         GuiData->Selection.dwSelectionAnchor = *SelectionAnchor;
864 
865     // TODO: Scroll buffer to bring 'coord' into view
866 
867     if (coord != NULL)
868     {
869         SMALL_RECT rc;
870         HRGN newRgn;
871 
872         /*
873          * Pressing the Control key while selecting text, allows us to enter
874          * into line-selection mode, the selection mode of *nix terminals.
875          */
876         BOOL OldLineSel = GuiData->LineSelection;
877         GuiData->LineSelection = !!(GetKeyState(VK_CONTROL) & KEY_PRESSED);
878 
879         /* Exchange left/top with right/bottom if required */
880         rc.Left   = min(GuiData->Selection.dwSelectionAnchor.X, coord->X);
881         rc.Top    = min(GuiData->Selection.dwSelectionAnchor.Y, coord->Y);
882         rc.Right  = max(GuiData->Selection.dwSelectionAnchor.X, coord->X);
883         rc.Bottom = max(GuiData->Selection.dwSelectionAnchor.Y, coord->Y);
884 
885         newRgn = CreateSelectionRgn(GuiData, GuiData->LineSelection,
886                                     &GuiData->Selection.dwSelectionAnchor,
887                                     &rc);
888 
889         if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
890         {
891             if (OldLineSel != GuiData->LineSelection ||
892                 memcmp(&rc, &GuiData->Selection.srSelection, sizeof(SMALL_RECT)) != 0)
893             {
894                 /* Calculate the region that needs to be updated */
895                 if (oldRgn && newRgn && CombineRgn(newRgn, newRgn, oldRgn, RGN_XOR) != ERROR)
896                 {
897                     InvalidateRgn(GuiData->hWindow, newRgn, FALSE);
898                 }
899             }
900         }
901         else
902         {
903             InvalidateRgn(GuiData->hWindow, newRgn, FALSE);
904         }
905 
906         DeleteObject(newRgn);
907 
908         GuiData->Selection.dwFlags |= CONSOLE_SELECTION_NOT_EMPTY;
909         GuiData->Selection.srSelection = rc;
910         GuiData->dwSelectionCursor = *coord;
911 
912         if ((GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0)
913         {
914             LPWSTR SelTypeStr = NULL   , WindowTitle = NULL;
915             SIZE_T SelTypeStrLength = 0, Length = 0;
916 
917             /* Clear the old selection */
918             if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
919             {
920                 InvalidateRgn(GuiData->hWindow, oldRgn, FALSE);
921             }
922 
923             /*
924              * When passing a zero-length buffer size, LoadString(...) returns
925              * a read-only pointer buffer to the program's resource string.
926              */
927             SelTypeStrLength =
928                 LoadStringW(ConSrvDllInstance,
929                             (GuiData->Selection.dwFlags & CONSOLE_MOUSE_SELECTION)
930                                 ? IDS_SELECT_TITLE : IDS_MARK_TITLE,
931                             (LPWSTR)&SelTypeStr, 0);
932 
933             /*
934              * Prepend the selection type string to the current console title
935              * if we succeeded in retrieving a valid localized string.
936              */
937             if (SelTypeStr)
938             {
939                 // 3 for " - " and 1 for NULL
940                 Length = Console->Title.Length + (SelTypeStrLength + 3 + 1) * sizeof(WCHAR);
941                 WindowTitle = ConsoleAllocHeap(0, Length);
942 
943                 wcsncpy(WindowTitle, SelTypeStr, SelTypeStrLength);
944                 WindowTitle[SelTypeStrLength] = UNICODE_NULL;
945                 wcscat(WindowTitle, L" - ");
946                 wcscat(WindowTitle, Console->Title.Buffer);
947 
948                 SetWindowTextW(GuiData->hWindow, WindowTitle);
949                 ConsoleFreeHeap(WindowTitle);
950             }
951 
952             GuiData->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS;
953             ConioPause(Console, PAUSED_FROM_SELECTION);
954         }
955     }
956     else
957     {
958         /* Clear the selection */
959         if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
960         {
961             InvalidateRgn(GuiData->hWindow, oldRgn, FALSE);
962         }
963 
964         GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
965         ConioUnpause(Console, PAUSED_FROM_SELECTION);
966 
967         /* Restore the console title */
968         SetWindowTextW(GuiData->hWindow, Console->Title.Buffer);
969     }
970 
971     DeleteObject(oldRgn);
972 }
973 
974 static VOID
975 OnPaint(PGUI_CONSOLE_DATA GuiData)
976 {
977     PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
978     PAINTSTRUCT ps;
979     RECT rcPaint;
980 
981     /* Do nothing if the window is hidden */
982     if (!GuiData->IsWindowVisible) return;
983 
984     BeginPaint(GuiData->hWindow, &ps);
985     if (ps.hdc != NULL &&
986         ps.rcPaint.left < ps.rcPaint.right &&
987         ps.rcPaint.top < ps.rcPaint.bottom)
988     {
989         EnterCriticalSection(&GuiData->Lock);
990 
991         /* Compose the current screen-buffer on-memory */
992         if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
993         {
994             GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)ActiveBuffer,
995                                    GuiData, &ps.rcPaint, &rcPaint);
996         }
997         else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
998         {
999             GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)ActiveBuffer,
1000                                    GuiData, &ps.rcPaint, &rcPaint);
1001         }
1002 
1003         /* Send it to screen */
1004         BitBlt(ps.hdc,
1005                ps.rcPaint.left,
1006                ps.rcPaint.top,
1007                rcPaint.right  - rcPaint.left,
1008                rcPaint.bottom - rcPaint.top,
1009                GuiData->hMemDC,
1010                rcPaint.left,
1011                rcPaint.top,
1012                SRCCOPY);
1013 
1014         /* Draw the selection region if needed */
1015         if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
1016         {
1017             PaintSelectionRect(GuiData, &ps);
1018         }
1019 
1020         // TODO: Move cursor display here!
1021 
1022         LeaveCriticalSection(&GuiData->Lock);
1023     }
1024     EndPaint(GuiData->hWindow, &ps);
1025 
1026     return;
1027 }
1028 
1029 static VOID
1030 OnPaletteChanged(PGUI_CONSOLE_DATA GuiData)
1031 {
1032     PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
1033 
1034     /* Do nothing if the window is hidden */
1035     if (!GuiData->IsWindowVisible) return;
1036 
1037     // See WM_PALETTECHANGED message
1038     // if ((HWND)wParam == hWnd) break;
1039 
1040     // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
1041     if (ActiveBuffer->PaletteHandle)
1042     {
1043         DPRINT("WM_PALETTECHANGED changing palette\n");
1044 
1045         /* Specify the use of the system palette for the framebuffer */
1046         SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage);
1047 
1048         /* Realize the (logical) palette */
1049         RealizePalette(GuiData->hMemDC);
1050     }
1051 }
1052 
1053 static BOOL
1054 IsSystemKey(WORD VirtualKeyCode)
1055 {
1056     switch (VirtualKeyCode)
1057     {
1058         /* From MSDN, "Virtual-Key Codes" */
1059         case VK_RETURN:
1060         case VK_SHIFT:
1061         case VK_CONTROL:
1062         case VK_MENU:
1063         case VK_PAUSE:
1064         case VK_CAPITAL:
1065         case VK_ESCAPE:
1066         case VK_LWIN:
1067         case VK_RWIN:
1068         case VK_NUMLOCK:
1069         case VK_SCROLL:
1070             return TRUE;
1071         default:
1072             return FALSE;
1073     }
1074 }
1075 
1076 static VOID
1077 OnKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
1078 {
1079     PCONSRV_CONSOLE Console = GuiData->Console;
1080     PCONSOLE_SCREEN_BUFFER ActiveBuffer;
1081 
1082     if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
1083 
1084     ActiveBuffer = GuiData->ActiveBuffer;
1085 
1086     if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS)
1087     {
1088         WORD VirtualKeyCode = LOWORD(wParam);
1089 
1090         if (msg != WM_KEYDOWN) goto Quit;
1091 
1092         if (VirtualKeyCode == VK_RETURN)
1093         {
1094             /* Copy (and clear) selection if ENTER is pressed */
1095             Copy(GuiData);
1096             goto Quit;
1097         }
1098         else if ( VirtualKeyCode == VK_ESCAPE ||
1099                  (VirtualKeyCode == 'C' && (GetKeyState(VK_CONTROL) & KEY_PRESSED)) )
1100         {
1101             /* Cancel selection if ESC or Ctrl-C are pressed */
1102             UpdateSelection(GuiData, NULL, NULL);
1103             goto Quit;
1104         }
1105 
1106         if ((GuiData->Selection.dwFlags & CONSOLE_MOUSE_SELECTION) == 0)
1107         {
1108             /* Keyboard selection mode */
1109             BOOL Interpreted = FALSE;
1110             BOOL MajPressed  = !!(GetKeyState(VK_SHIFT) & KEY_PRESSED);
1111 
1112             switch (VirtualKeyCode)
1113             {
1114                 case VK_LEFT:
1115                 {
1116                     Interpreted = TRUE;
1117                     if (GuiData->dwSelectionCursor.X > 0)
1118                         GuiData->dwSelectionCursor.X--;
1119 
1120                     break;
1121                 }
1122 
1123                 case VK_RIGHT:
1124                 {
1125                     Interpreted = TRUE;
1126                     if (GuiData->dwSelectionCursor.X < ActiveBuffer->ScreenBufferSize.X - 1)
1127                         GuiData->dwSelectionCursor.X++;
1128 
1129                     break;
1130                 }
1131 
1132                 case VK_UP:
1133                 {
1134                     Interpreted = TRUE;
1135                     if (GuiData->dwSelectionCursor.Y > 0)
1136                         GuiData->dwSelectionCursor.Y--;
1137 
1138                     break;
1139                 }
1140 
1141                 case VK_DOWN:
1142                 {
1143                     Interpreted = TRUE;
1144                     if (GuiData->dwSelectionCursor.Y < ActiveBuffer->ScreenBufferSize.Y - 1)
1145                         GuiData->dwSelectionCursor.Y++;
1146 
1147                     break;
1148                 }
1149 
1150                 case VK_HOME:
1151                 {
1152                     Interpreted = TRUE;
1153                     GuiData->dwSelectionCursor.X = 0;
1154                     GuiData->dwSelectionCursor.Y = 0;
1155                     break;
1156                 }
1157 
1158                 case VK_END:
1159                 {
1160                     Interpreted = TRUE;
1161                     GuiData->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
1162                     break;
1163                 }
1164 
1165                 case VK_PRIOR:
1166                 {
1167                     Interpreted = TRUE;
1168                     GuiData->dwSelectionCursor.Y -= ActiveBuffer->ViewSize.Y;
1169                     if (GuiData->dwSelectionCursor.Y < 0)
1170                         GuiData->dwSelectionCursor.Y = 0;
1171 
1172                     break;
1173                 }
1174 
1175                 case VK_NEXT:
1176                 {
1177                     Interpreted = TRUE;
1178                     GuiData->dwSelectionCursor.Y += ActiveBuffer->ViewSize.Y;
1179                     if (GuiData->dwSelectionCursor.Y >= ActiveBuffer->ScreenBufferSize.Y)
1180                         GuiData->dwSelectionCursor.Y  = ActiveBuffer->ScreenBufferSize.Y - 1;
1181 
1182                     break;
1183                 }
1184 
1185                 default:
1186                     break;
1187             }
1188 
1189             if (Interpreted)
1190             {
1191                 UpdateSelection(GuiData,
1192                                 !MajPressed ? &GuiData->dwSelectionCursor : NULL,
1193                                 &GuiData->dwSelectionCursor);
1194             }
1195             else if (!IsSystemKey(VirtualKeyCode))
1196             {
1197                 /* Emit an error beep sound */
1198                 SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0);
1199             }
1200 
1201             goto Quit;
1202         }
1203         else
1204         {
1205             /* Mouse selection mode */
1206 
1207             if (!IsSystemKey(VirtualKeyCode))
1208             {
1209                 /* Clear the selection and send the key into the input buffer */
1210                 UpdateSelection(GuiData, NULL, NULL);
1211             }
1212             else
1213             {
1214                 goto Quit;
1215             }
1216         }
1217     }
1218 
1219     if ((GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0)
1220     {
1221         MSG Message;
1222 
1223         Message.hwnd = GuiData->hWindow;
1224         Message.message = msg;
1225         Message.wParam = wParam;
1226         Message.lParam = lParam;
1227 
1228         ConioProcessKey(Console, &Message);
1229     }
1230 
1231 Quit:
1232     LeaveCriticalSection(&Console->Lock);
1233 }
1234 
1235 
1236 // FIXME: Remove after fixing OnTimer
1237 VOID
1238 InvalidateCell(PGUI_CONSOLE_DATA GuiData,
1239                SHORT x, SHORT y);
1240 
1241 static VOID
1242 OnTimer(PGUI_CONSOLE_DATA GuiData)
1243 {
1244     PCONSRV_CONSOLE Console = GuiData->Console;
1245     PCONSOLE_SCREEN_BUFFER Buff;
1246 
1247     /* Do nothing if the window is hidden */
1248     if (!GuiData->IsWindowVisible) return;
1249 
1250     SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CURSOR_BLINK_TIME, NULL);
1251 
1252     if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
1253 
1254     Buff = GuiData->ActiveBuffer;
1255 
1256     if (GetType(Buff) == TEXTMODE_BUFFER)
1257     {
1258         /* Repaint the caret */
1259         InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
1260         Buff->CursorBlinkOn = !Buff->CursorBlinkOn;
1261 
1262         if ((GuiData->OldCursor.x != Buff->CursorPosition.X) ||
1263             (GuiData->OldCursor.y != Buff->CursorPosition.Y))
1264         {
1265             SCROLLINFO sInfo;
1266             int OldScrollX = -1, OldScrollY = -1;
1267             int NewScrollX = -1, NewScrollY = -1;
1268 
1269             sInfo.cbSize = sizeof(sInfo);
1270             sInfo.fMask = SIF_POS;
1271             // Capture the original position of the scroll bars and save them.
1272             if (GetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo)) OldScrollX = sInfo.nPos;
1273             if (GetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo)) OldScrollY = sInfo.nPos;
1274 
1275             // If we successfully got the info for the horizontal scrollbar
1276             if (OldScrollX >= 0)
1277             {
1278                 if ((Buff->CursorPosition.X < Buff->ViewOrigin.X) ||
1279                     (Buff->CursorPosition.X >= (Buff->ViewOrigin.X + Buff->ViewSize.X)))
1280                 {
1281                     // Handle the horizontal scroll bar
1282                     if (Buff->CursorPosition.X >= Buff->ViewSize.X)
1283                         NewScrollX = Buff->CursorPosition.X - Buff->ViewSize.X + 1;
1284                     else
1285                         NewScrollX = 0;
1286                 }
1287                 else
1288                 {
1289                     NewScrollX = OldScrollX;
1290                 }
1291             }
1292             // If we successfully got the info for the vertical scrollbar
1293             if (OldScrollY >= 0)
1294             {
1295                 if ((Buff->CursorPosition.Y < Buff->ViewOrigin.Y) ||
1296                     (Buff->CursorPosition.Y >= (Buff->ViewOrigin.Y + Buff->ViewSize.Y)))
1297                 {
1298                     // Handle the vertical scroll bar
1299                     if (Buff->CursorPosition.Y >= Buff->ViewSize.Y)
1300                         NewScrollY = Buff->CursorPosition.Y - Buff->ViewSize.Y + 1;
1301                     else
1302                         NewScrollY = 0;
1303                 }
1304                 else
1305                 {
1306                     NewScrollY = OldScrollY;
1307                 }
1308             }
1309 
1310             // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area
1311             // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar
1312             //       was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling)
1313             //       and their associated scrollbar is left alone.
1314             if ((OldScrollX != NewScrollX) || (OldScrollY != NewScrollY))
1315             {
1316                 Buff->ViewOrigin.X = NewScrollX;
1317                 Buff->ViewOrigin.Y = NewScrollY;
1318                 ScrollWindowEx(GuiData->hWindow,
1319                                (OldScrollX - NewScrollX) * GuiData->CharWidth,
1320                                (OldScrollY - NewScrollY) * GuiData->CharHeight,
1321                                NULL,
1322                                NULL,
1323                                NULL,
1324                                NULL,
1325                                SW_INVALIDATE);
1326                 if (NewScrollX >= 0)
1327                 {
1328                     sInfo.nPos = NewScrollX;
1329                     SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE);
1330                 }
1331                 if (NewScrollY >= 0)
1332                 {
1333                     sInfo.nPos = NewScrollY;
1334                     SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE);
1335                 }
1336                 UpdateWindow(GuiData->hWindow);
1337                 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1338                 GuiData->OldCursor.x = Buff->CursorPosition.X;
1339                 GuiData->OldCursor.y = Buff->CursorPosition.Y;
1340             }
1341         }
1342     }
1343     else /* if (GetType(Buff) == GRAPHICS_BUFFER) */
1344     {
1345     }
1346 
1347     LeaveCriticalSection(&Console->Lock);
1348 }
1349 
1350 static BOOL
1351 OnClose(PGUI_CONSOLE_DATA GuiData)
1352 {
1353     PCONSRV_CONSOLE Console = GuiData->Console;
1354 
1355     if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE))
1356         return TRUE;
1357 
1358     // TODO: Prompt for termination ? (Warn the user about possible apps running in this console)
1359 
1360     /*
1361      * FIXME: Windows will wait up to 5 seconds for the thread to exit.
1362      * We shouldn't wait here, though, since the console lock is entered.
1363      * A copy of the thread list probably needs to be made.
1364      */
1365     ConSrvConsoleProcessCtrlEvent(Console, 0, CTRL_CLOSE_EVENT);
1366 
1367     LeaveCriticalSection(&Console->Lock);
1368     return FALSE;
1369 }
1370 
1371 static LRESULT
1372 OnNcDestroy(HWND hWnd)
1373 {
1374     PGUI_CONSOLE_DATA GuiData = GuiGetGuiData(hWnd);
1375 
1376     /* Free the GuiData registration */
1377     SetWindowLongPtrW(hWnd, GWLP_USERDATA, (DWORD_PTR)NULL);
1378 
1379     /* Reset the system menu back to default and destroy the previous menu */
1380     GetSystemMenu(hWnd, TRUE);
1381 
1382     if (GuiData)
1383     {
1384         if (GuiData->IsWindowVisible)
1385             KillTimer(hWnd, CONGUI_UPDATE_TIMER);
1386 
1387         /* Free the terminal framebuffer */
1388         if (GuiData->hMemDC ) DeleteDC(GuiData->hMemDC);
1389         if (GuiData->hBitmap) DeleteObject(GuiData->hBitmap);
1390         // if (GuiData->hSysPalette) DeleteObject(GuiData->hSysPalette);
1391         DeleteFonts(GuiData);
1392     }
1393 
1394     return DefWindowProcW(hWnd, WM_NCDESTROY, 0, 0);
1395 }
1396 
1397 static VOID
1398 OnScroll(PGUI_CONSOLE_DATA GuiData, INT nBar, WORD sbCode)
1399 {
1400     PCONSRV_CONSOLE Console = GuiData->Console;
1401     PCONSOLE_SCREEN_BUFFER Buff;
1402     SCROLLINFO sInfo;
1403     INT oldPos, Maximum;
1404     PSHORT pOriginXY;
1405 
1406     ASSERT(nBar == SB_HORZ || nBar == SB_VERT);
1407 
1408     if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
1409 
1410     Buff = GuiData->ActiveBuffer;
1411 
1412     if (nBar == SB_HORZ)
1413     {
1414         Maximum = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
1415         pOriginXY = &Buff->ViewOrigin.X;
1416     }
1417     else // if (nBar == SB_VERT)
1418     {
1419         Maximum = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
1420         pOriginXY = &Buff->ViewOrigin.Y;
1421     }
1422 
1423     /* Set scrollbar sizes */
1424     sInfo.cbSize = sizeof(sInfo);
1425     sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
1426 
1427     if (!GetScrollInfo(GuiData->hWindow, nBar, &sInfo)) goto Quit;
1428 
1429     oldPos = sInfo.nPos;
1430 
1431     switch (sbCode)
1432     {
1433         case SB_LINEUP:   // SB_LINELEFT:
1434             sInfo.nPos--;
1435             break;
1436 
1437         case SB_LINEDOWN: // SB_LINERIGHT:
1438             sInfo.nPos++;
1439             break;
1440 
1441         case SB_PAGEUP:   // SB_PAGELEFT:
1442             sInfo.nPos -= sInfo.nPage;
1443             break;
1444 
1445         case SB_PAGEDOWN: // SB_PAGERIGHT:
1446             sInfo.nPos += sInfo.nPage;
1447             break;
1448 
1449         case SB_THUMBTRACK:
1450             sInfo.nPos = sInfo.nTrackPos;
1451             ConioPause(Console, PAUSED_FROM_SCROLLBAR);
1452             break;
1453 
1454         case SB_THUMBPOSITION:
1455             sInfo.nPos = sInfo.nTrackPos;
1456             ConioUnpause(Console, PAUSED_FROM_SCROLLBAR);
1457             break;
1458 
1459         case SB_TOP:    // SB_LEFT:
1460             sInfo.nPos = sInfo.nMin;
1461             break;
1462 
1463         case SB_BOTTOM: // SB_RIGHT:
1464             sInfo.nPos = sInfo.nMax;
1465             break;
1466 
1467         default:
1468             break;
1469     }
1470 
1471     sInfo.nPos = min(max(sInfo.nPos, 0), Maximum);
1472 
1473     if (oldPos != sInfo.nPos)
1474     {
1475         USHORT OldX = Buff->ViewOrigin.X;
1476         USHORT OldY = Buff->ViewOrigin.Y;
1477         UINT   WidthUnit, HeightUnit;
1478 
1479         /* We now modify Buff->ViewOrigin */
1480         *pOriginXY = sInfo.nPos;
1481 
1482         GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
1483 
1484         ScrollWindowEx(GuiData->hWindow,
1485                        (OldX - Buff->ViewOrigin.X) * WidthUnit ,
1486                        (OldY - Buff->ViewOrigin.Y) * HeightUnit,
1487                        NULL,
1488                        NULL,
1489                        NULL,
1490                        NULL,
1491                        SW_INVALIDATE);
1492 
1493         sInfo.fMask = SIF_POS;
1494         SetScrollInfo(GuiData->hWindow, nBar, &sInfo, TRUE);
1495 
1496         UpdateWindow(GuiData->hWindow);
1497         // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1498     }
1499 
1500 Quit:
1501     LeaveCriticalSection(&Console->Lock);
1502     return;
1503 }
1504 
1505 static COORD
1506 PointToCoord(PGUI_CONSOLE_DATA GuiData, LPARAM lParam)
1507 {
1508     PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1509     COORD Coord;
1510     UINT  WidthUnit, HeightUnit;
1511 
1512     GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit);
1513 
1514     Coord.X = Buffer->ViewOrigin.X + ((SHORT)LOWORD(lParam) / (int)WidthUnit );
1515     Coord.Y = Buffer->ViewOrigin.Y + ((SHORT)HIWORD(lParam) / (int)HeightUnit);
1516 
1517     /* Clip coordinate to ensure it's inside buffer */
1518     if (Coord.X < 0)
1519         Coord.X = 0;
1520     else if (Coord.X >= Buffer->ScreenBufferSize.X)
1521         Coord.X = Buffer->ScreenBufferSize.X - 1;
1522 
1523     if (Coord.Y < 0)
1524         Coord.Y = 0;
1525     else if (Coord.Y >= Buffer->ScreenBufferSize.Y)
1526         Coord.Y = Buffer->ScreenBufferSize.Y - 1;
1527 
1528     return Coord;
1529 }
1530 
1531 static LRESULT
1532 OnMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
1533 {
1534     BOOL DoDefault = FALSE;
1535     PCONSRV_CONSOLE Console = GuiData->Console;
1536 
1537     /*
1538      * HACK FOR CORE-8394 (Part 2):
1539      *
1540      * Check whether we should ignore the next mouse move event.
1541      * In either case we reset the HACK flag.
1542      *
1543      * See Part 1 of this hack below.
1544      */
1545     if (GuiData->HackCORE8394IgnoreNextMove && msg == WM_MOUSEMOVE)
1546     {
1547         GuiData->HackCORE8394IgnoreNextMove = FALSE;
1548         goto Quit;
1549     }
1550     GuiData->HackCORE8394IgnoreNextMove = FALSE;
1551 
1552     // FIXME: It's here that we need to check whether we have focus or not
1553     // and whether we are or not in edit mode, in order to know if we need
1554     // to deal with the mouse.
1555 
1556     if (GuiData->IgnoreNextMouseSignal)
1557     {
1558         if (msg != WM_LBUTTONDOWN &&
1559             msg != WM_MBUTTONDOWN &&
1560             msg != WM_RBUTTONDOWN &&
1561             msg != WM_XBUTTONDOWN)
1562         {
1563             /*
1564              * If this mouse signal is not a button-down action
1565              * then this is the last one being ignored.
1566              */
1567             GuiData->IgnoreNextMouseSignal = FALSE;
1568         }
1569         else
1570         {
1571             /*
1572              * This mouse signal is a button-down action.
1573              * Ignore it and perform default action.
1574              */
1575             DoDefault = TRUE;
1576         }
1577         goto Quit;
1578     }
1579 
1580     if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE))
1581     {
1582         DoDefault = TRUE;
1583         goto Quit;
1584     }
1585 
1586     if ( (GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) ||
1587          (Console->QuickEdit) )
1588     {
1589         switch (msg)
1590         {
1591             case WM_LBUTTONDOWN:
1592             {
1593                 /* Check for selection state */
1594                 if ( (GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
1595                      (GuiData->Selection.dwFlags & CONSOLE_MOUSE_SELECTION)       &&
1596                      (GetKeyState(VK_SHIFT) & KEY_PRESSED) )
1597                 {
1598                     /*
1599                      * A mouse selection is currently in progress and the user
1600                      * has pressed the SHIFT key and clicked somewhere, update
1601                      * the selection.
1602                      */
1603                     GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1604                     UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1605                 }
1606                 else
1607                 {
1608                     /* Clear the old selection */
1609                     GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
1610 
1611                     /* Restart a new selection */
1612                     GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1613                     SetCapture(GuiData->hWindow);
1614                     GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1615                     UpdateSelection(GuiData,
1616                                     &GuiData->dwSelectionCursor,
1617                                     &GuiData->dwSelectionCursor);
1618                 }
1619 
1620                 break;
1621             }
1622 
1623             case WM_LBUTTONUP:
1624             {
1625                 if (!(GuiData->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1626 
1627                 // GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1628                 GuiData->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN;
1629                 // UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1630                 ReleaseCapture();
1631 
1632                 break;
1633             }
1634 
1635             case WM_LBUTTONDBLCLK:
1636             {
1637                 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1638 
1639                 if (GetType(Buffer) == TEXTMODE_BUFFER)
1640                 {
1641 #define IS_WORD_SEP(c)  \
1642     ((c) == L'\0' || (c) == L' ' || (c) == L'\t' || (c) == L'\r' || (c) == L'\n')
1643 
1644                     PTEXTMODE_SCREEN_BUFFER TextBuffer = (PTEXTMODE_SCREEN_BUFFER)Buffer;
1645                     COORD cL, cR;
1646                     PCHAR_INFO ptrL, ptrR;
1647 
1648                     /* Starting point */
1649                     cL = cR = PointToCoord(GuiData, lParam);
1650                     ptrL = ptrR = ConioCoordToPointer(TextBuffer, cL.X, cL.Y);
1651 
1652                     /* Enlarge the selection by checking for whitespace */
1653                     while ((0 < cL.X) && !IS_WORD_SEP(ptrL->Char.UnicodeChar)
1654                                       && !IS_WORD_SEP((ptrL-1)->Char.UnicodeChar))
1655                     {
1656                         --cL.X;
1657                         --ptrL;
1658                     }
1659                     while ((cR.X < TextBuffer->ScreenBufferSize.X - 1) &&
1660                            !IS_WORD_SEP(ptrR->Char.UnicodeChar)        &&
1661                            !IS_WORD_SEP((ptrR+1)->Char.UnicodeChar))
1662                     {
1663                         ++cR.X;
1664                         ++ptrR;
1665                     }
1666 
1667                     /*
1668                      * Update the selection started with the single
1669                      * left-click that preceded this double-click.
1670                      */
1671                     GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1672                     UpdateSelection(GuiData, &cL, &cR);
1673 
1674                     /* Ignore the next mouse move signal */
1675                     GuiData->IgnoreNextMouseSignal = TRUE;
1676 #undef IS_WORD_SEP
1677                 }
1678 
1679                 break;
1680             }
1681 
1682             case WM_RBUTTONDOWN:
1683             case WM_RBUTTONDBLCLK:
1684             {
1685                 if (!(GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY))
1686                 {
1687                     Paste(GuiData);
1688                 }
1689                 else
1690                 {
1691                     Copy(GuiData);
1692                 }
1693 
1694                 /* Ignore the next mouse move signal */
1695                 GuiData->IgnoreNextMouseSignal = TRUE;
1696                 break;
1697             }
1698 
1699             case WM_MOUSEMOVE:
1700             {
1701                 if (!(GET_KEYSTATE_WPARAM(wParam) & MK_LBUTTON)) break;
1702                 if (!(GuiData->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1703 
1704                 GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1705                 UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1706                 break;
1707             }
1708 
1709             default:
1710                 DoDefault = TRUE; // FALSE;
1711                 break;
1712         }
1713     }
1714     else if (GetConsoleInputBufferMode(Console) & ENABLE_MOUSE_INPUT)
1715     {
1716         INPUT_RECORD er;
1717         WORD  wKeyState         = GET_KEYSTATE_WPARAM(wParam);
1718         DWORD dwButtonState     = 0;
1719         DWORD dwControlKeyState = 0;
1720         DWORD dwEventFlags      = 0;
1721 
1722         switch (msg)
1723         {
1724             case WM_LBUTTONDOWN:
1725                 SetCapture(GuiData->hWindow);
1726                 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1727                 dwEventFlags  = 0;
1728                 break;
1729 
1730             case WM_MBUTTONDOWN:
1731                 SetCapture(GuiData->hWindow);
1732                 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1733                 dwEventFlags  = 0;
1734                 break;
1735 
1736             case WM_RBUTTONDOWN:
1737                 SetCapture(GuiData->hWindow);
1738                 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1739                 dwEventFlags  = 0;
1740                 break;
1741 
1742             case WM_XBUTTONDOWN:
1743             {
1744                 /* Get which X-button was pressed */
1745                 WORD wButton = GET_XBUTTON_WPARAM(wParam);
1746 
1747                 /* Check for X-button validity */
1748                 if (wButton & ~(XBUTTON1 | XBUTTON2))
1749                 {
1750                     DPRINT1("X-button 0x%04x invalid\n", wButton);
1751                     DoDefault = TRUE;
1752                     break;
1753                 }
1754 
1755                 SetCapture(GuiData->hWindow);
1756                 dwButtonState = (wButton == XBUTTON1 ? FROM_LEFT_3RD_BUTTON_PRESSED
1757                                                      : FROM_LEFT_4TH_BUTTON_PRESSED);
1758                 dwEventFlags  = 0;
1759                 break;
1760             }
1761 
1762             case WM_LBUTTONUP:
1763                 ReleaseCapture();
1764                 dwButtonState = 0;
1765                 dwEventFlags  = 0;
1766                 break;
1767 
1768             case WM_MBUTTONUP:
1769                 ReleaseCapture();
1770                 dwButtonState = 0;
1771                 dwEventFlags  = 0;
1772                 break;
1773 
1774             case WM_RBUTTONUP:
1775                 ReleaseCapture();
1776                 dwButtonState = 0;
1777                 dwEventFlags  = 0;
1778                 break;
1779 
1780             case WM_XBUTTONUP:
1781             {
1782                 /* Get which X-button was released */
1783                 WORD wButton = GET_XBUTTON_WPARAM(wParam);
1784 
1785                 /* Check for X-button validity */
1786                 if (wButton & ~(XBUTTON1 | XBUTTON2))
1787                 {
1788                     DPRINT1("X-button 0x%04x invalid\n", wButton);
1789                     /* Ok, just release the button anyway... */
1790                 }
1791 
1792                 ReleaseCapture();
1793                 dwButtonState = 0;
1794                 dwEventFlags  = 0;
1795                 break;
1796             }
1797 
1798             case WM_LBUTTONDBLCLK:
1799                 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1800                 dwEventFlags  = DOUBLE_CLICK;
1801                 break;
1802 
1803             case WM_MBUTTONDBLCLK:
1804                 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1805                 dwEventFlags  = DOUBLE_CLICK;
1806                 break;
1807 
1808             case WM_RBUTTONDBLCLK:
1809                 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1810                 dwEventFlags  = DOUBLE_CLICK;
1811                 break;
1812 
1813             case WM_XBUTTONDBLCLK:
1814             {
1815                 /* Get which X-button was double-clicked */
1816                 WORD wButton = GET_XBUTTON_WPARAM(wParam);
1817 
1818                 /* Check for X-button validity */
1819                 if (wButton & ~(XBUTTON1 | XBUTTON2))
1820                 {
1821                     DPRINT1("X-button 0x%04x invalid\n", wButton);
1822                     DoDefault = TRUE;
1823                     break;
1824                 }
1825 
1826                 dwButtonState = (wButton == XBUTTON1 ? FROM_LEFT_3RD_BUTTON_PRESSED
1827                                                      : FROM_LEFT_4TH_BUTTON_PRESSED);
1828                 dwEventFlags  = DOUBLE_CLICK;
1829                 break;
1830             }
1831 
1832             case WM_MOUSEMOVE:
1833                 dwButtonState = 0;
1834                 dwEventFlags  = MOUSE_MOVED;
1835                 break;
1836 
1837             case WM_MOUSEWHEEL:
1838                 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1839                 dwEventFlags  = MOUSE_WHEELED;
1840                 break;
1841 
1842             case WM_MOUSEHWHEEL:
1843                 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1844                 dwEventFlags  = MOUSE_HWHEELED;
1845                 break;
1846 
1847             default:
1848                 DoDefault = TRUE;
1849                 break;
1850         }
1851 
1852         /*
1853          * HACK FOR CORE-8394 (Part 1):
1854          *
1855          * It appears that depending on which VM ReactOS runs, the next mouse
1856          * signal coming after a button-down action can be a mouse-move (e.g.
1857          * on VBox, whereas on QEMU it is not the case). However it is NOT a
1858          * rule, so that we cannot use the IgnoreNextMouseSignal flag to just
1859          * "ignore" the next mouse event, thinking it would always be a mouse-
1860          * move signal.
1861          *
1862          * To work around this problem (that should really be fixed in Win32k),
1863          * we use a second flag to ignore this possible next mouse move signal.
1864          */
1865         switch (msg)
1866         {
1867             case WM_LBUTTONDOWN:
1868             case WM_MBUTTONDOWN:
1869             case WM_RBUTTONDOWN:
1870             case WM_XBUTTONDOWN:
1871                 GuiData->HackCORE8394IgnoreNextMove = TRUE;
1872             default:
1873                 break;
1874         }
1875 
1876         if (!DoDefault)
1877         {
1878             if (wKeyState & MK_LBUTTON)
1879                 dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED;
1880             if (wKeyState & MK_MBUTTON)
1881                 dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED;
1882             if (wKeyState & MK_RBUTTON)
1883                 dwButtonState |= RIGHTMOST_BUTTON_PRESSED;
1884             if (wKeyState & MK_XBUTTON1)
1885                 dwButtonState |= FROM_LEFT_3RD_BUTTON_PRESSED;
1886             if (wKeyState & MK_XBUTTON2)
1887                 dwButtonState |= FROM_LEFT_4TH_BUTTON_PRESSED;
1888 
1889             if (GetKeyState(VK_RMENU) & KEY_PRESSED)
1890                 dwControlKeyState |= RIGHT_ALT_PRESSED;
1891             if (GetKeyState(VK_LMENU) & KEY_PRESSED)
1892                 dwControlKeyState |= LEFT_ALT_PRESSED;
1893             if (GetKeyState(VK_RCONTROL) & KEY_PRESSED)
1894                 dwControlKeyState |= RIGHT_CTRL_PRESSED;
1895             if (GetKeyState(VK_LCONTROL) & KEY_PRESSED)
1896                 dwControlKeyState |= LEFT_CTRL_PRESSED;
1897             if (GetKeyState(VK_SHIFT) & KEY_PRESSED)
1898                 dwControlKeyState |= SHIFT_PRESSED;
1899             if (GetKeyState(VK_NUMLOCK) & KEY_TOGGLED)
1900                 dwControlKeyState |= NUMLOCK_ON;
1901             if (GetKeyState(VK_SCROLL) & KEY_TOGGLED)
1902                 dwControlKeyState |= SCROLLLOCK_ON;
1903             if (GetKeyState(VK_CAPITAL) & KEY_TOGGLED)
1904                 dwControlKeyState |= CAPSLOCK_ON;
1905             /* See WM_CHAR MSDN documentation for instance */
1906             if (lParam & 0x01000000)
1907                 dwControlKeyState |= ENHANCED_KEY;
1908 
1909             /* Send a mouse event */
1910             er.EventType = MOUSE_EVENT;
1911             er.Event.MouseEvent.dwMousePosition   = PointToCoord(GuiData, lParam);
1912             er.Event.MouseEvent.dwButtonState     = dwButtonState;
1913             er.Event.MouseEvent.dwControlKeyState = dwControlKeyState;
1914             er.Event.MouseEvent.dwEventFlags      = dwEventFlags;
1915 
1916             ConioProcessInputEvent(Console, &er);
1917         }
1918     }
1919     else
1920     {
1921         DoDefault = TRUE;
1922     }
1923 
1924     LeaveCriticalSection(&Console->Lock);
1925 
1926 Quit:
1927     if (!DoDefault)
1928         return 0;
1929 
1930     if (msg == WM_MOUSEWHEEL || msg == WM_MOUSEHWHEEL)
1931     {
1932         INT   nBar;
1933         WORD  sbCode;
1934         // WORD  wKeyState = GET_KEYSTATE_WPARAM(wParam);
1935         SHORT wDelta    = GET_WHEEL_DELTA_WPARAM(wParam);
1936 
1937         if (msg == WM_MOUSEWHEEL)
1938             nBar = SB_VERT;
1939         else // if (msg == WM_MOUSEHWHEEL)
1940             nBar = SB_HORZ;
1941 
1942         // NOTE: We currently do not support zooming...
1943         // if (wKeyState & MK_CONTROL)
1944 
1945         // FIXME: For some reason our win32k does not set the key states
1946         // when sending WM_MOUSEWHEEL or WM_MOUSEHWHEEL ...
1947         // if (wKeyState & MK_SHIFT)
1948         if (GetKeyState(VK_SHIFT) & KEY_PRESSED)
1949             sbCode = (wDelta >= 0 ? SB_PAGEUP : SB_PAGEDOWN);
1950         else
1951             sbCode = (wDelta >= 0 ? SB_LINEUP : SB_LINEDOWN);
1952 
1953         OnScroll(GuiData, nBar, sbCode);
1954     }
1955 
1956     return DefWindowProcW(GuiData->hWindow, msg, wParam, lParam);
1957 }
1958 
1959 
1960 static VOID
1961 Copy(PGUI_CONSOLE_DATA GuiData)
1962 {
1963     if (OpenClipboard(GuiData->hWindow))
1964     {
1965         PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1966 
1967         if (GetType(Buffer) == TEXTMODE_BUFFER)
1968         {
1969             GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer, GuiData);
1970         }
1971         else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1972         {
1973             GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer, GuiData);
1974         }
1975 
1976         CloseClipboard();
1977     }
1978 
1979     /* Clear the selection */
1980     UpdateSelection(GuiData, NULL, NULL);
1981 }
1982 
1983 static VOID
1984 Paste(PGUI_CONSOLE_DATA GuiData)
1985 {
1986     if (OpenClipboard(GuiData->hWindow))
1987     {
1988         PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1989 
1990         if (GetType(Buffer) == TEXTMODE_BUFFER)
1991         {
1992             GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer, GuiData);
1993         }
1994         else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1995         {
1996             GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer, GuiData);
1997         }
1998 
1999         CloseClipboard();
2000     }
2001 }
2002 
2003 static VOID
2004 OnGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData, PMINMAXINFO minMaxInfo)
2005 {
2006     PCONSRV_CONSOLE Console = GuiData->Console;
2007     PCONSOLE_SCREEN_BUFFER ActiveBuffer;
2008     DWORD windx, windy;
2009     UINT  WidthUnit, HeightUnit;
2010 
2011     if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
2012 
2013     ActiveBuffer = GuiData->ActiveBuffer;
2014 
2015     GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
2016 
2017     windx = CONGUI_MIN_WIDTH  * WidthUnit  + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
2018     windy = CONGUI_MIN_HEIGHT * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
2019 
2020     minMaxInfo->ptMinTrackSize.x = windx;
2021     minMaxInfo->ptMinTrackSize.y = windy;
2022 
2023     windx = (ActiveBuffer->ScreenBufferSize.X) * WidthUnit  + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
2024     windy = (ActiveBuffer->ScreenBufferSize.Y) * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
2025 
2026     if (ActiveBuffer->ViewSize.X < ActiveBuffer->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // Window currently has a horizontal scrollbar
2027     if (ActiveBuffer->ViewSize.Y < ActiveBuffer->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // Window currently has a vertical scrollbar
2028 
2029     minMaxInfo->ptMaxTrackSize.x = windx;
2030     minMaxInfo->ptMaxTrackSize.y = windy;
2031 
2032     LeaveCriticalSection(&Console->Lock);
2033 }
2034 
2035 static VOID
2036 OnSize(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
2037 {
2038     PCONSRV_CONSOLE Console = GuiData->Console;
2039 
2040     /* Do nothing if the window is hidden */
2041     if (!GuiData->IsWindowVisible || IsIconic(GuiData->hWindow)) return;
2042 
2043     if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
2044 
2045     if (!GuiData->WindowSizeLock &&
2046         (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED))
2047     {
2048         PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
2049         DWORD windx, windy, charx, chary;
2050         UINT  WidthUnit, HeightUnit;
2051 
2052         GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
2053 
2054         GuiData->WindowSizeLock = TRUE;
2055 
2056         windx = LOWORD(lParam);
2057         windy = HIWORD(lParam);
2058 
2059         /* Compensate for existing scroll bars (because lParam values do not accommodate scroll bar) */
2060         if (Buff->ViewSize.X < Buff->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // Window currently has a horizontal scrollbar
2061         if (Buff->ViewSize.Y < Buff->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // Window currently has a vertical scrollbar
2062 
2063         charx = windx / (int)WidthUnit ;
2064         chary = windy / (int)HeightUnit;
2065 
2066         /* Character alignment (round size up or down) */
2067         if ((windx % WidthUnit ) >= (WidthUnit  / 2)) ++charx;
2068         if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
2069 
2070         /* Compensate for added scroll bars in window */
2071         if (Buff->ViewSize.X < Buff->ScreenBufferSize.X) windy -= GetSystemMetrics(SM_CYHSCROLL); // Window will have a horizontal scroll bar
2072         if (Buff->ViewSize.Y < Buff->ScreenBufferSize.Y) windx -= GetSystemMetrics(SM_CXVSCROLL); // Window will have a vertical scroll bar
2073 
2074         charx = windx / (int)WidthUnit ;
2075         chary = windy / (int)HeightUnit;
2076 
2077         /* Character alignment (round size up or down) */
2078         if ((windx % WidthUnit ) >= (WidthUnit  / 2)) ++charx;
2079         if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
2080 
2081         /* Resize window */
2082         if ((charx != Buff->ViewSize.X) || (chary != Buff->ViewSize.Y))
2083         {
2084             Buff->ViewSize.X = (charx <= (DWORD)Buff->ScreenBufferSize.X) ? charx : Buff->ScreenBufferSize.X;
2085             Buff->ViewSize.Y = (chary <= (DWORD)Buff->ScreenBufferSize.Y) ? chary : Buff->ScreenBufferSize.Y;
2086         }
2087 
2088         ResizeConWnd(GuiData, WidthUnit, HeightUnit);
2089 
2090         /* Adjust the start of the visible area if we are attempting to show nonexistent areas */
2091         if ((Buff->ScreenBufferSize.X - Buff->ViewOrigin.X) < Buff->ViewSize.X) Buff->ViewOrigin.X = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
2092         if ((Buff->ScreenBufferSize.Y - Buff->ViewOrigin.Y) < Buff->ViewSize.Y) Buff->ViewOrigin.Y = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
2093         InvalidateRect(GuiData->hWindow, NULL, TRUE);
2094 
2095         GuiData->WindowSizeLock = FALSE;
2096     }
2097 
2098     LeaveCriticalSection(&Console->Lock);
2099 }
2100 
2101 static VOID
2102 OnMove(PGUI_CONSOLE_DATA GuiData)
2103 {
2104     RECT rcWnd;
2105 
2106     // TODO: Simplify the code.
2107     // See: GuiConsoleNotifyWndProc() PM_CREATE_CONSOLE.
2108 
2109     /* Retrieve our real position */
2110     GetWindowRect(GuiData->hWindow, &rcWnd);
2111     GuiData->GuiInfo.WindowOrigin.x = rcWnd.left;
2112     GuiData->GuiInfo.WindowOrigin.y = rcWnd.top;
2113 }
2114 
2115 static VOID
2116 OnDropFiles(PCONSRV_CONSOLE Console, HDROP hDrop)
2117 {
2118     LPWSTR pszPath;
2119     WCHAR szPath[MAX_PATH + 2];
2120 
2121     szPath[0] = L'"';
2122 
2123     DragQueryFileW(hDrop, 0, &szPath[1], ARRAYSIZE(szPath) - 1);
2124     DragFinish(hDrop);
2125 
2126     if (wcschr(&szPath[1], L' ') != NULL)
2127     {
2128         StringCchCatW(szPath, ARRAYSIZE(szPath), L"\"");
2129         pszPath = szPath;
2130     }
2131     else
2132     {
2133         pszPath = &szPath[1];
2134     }
2135 
2136     PasteText(Console, pszPath, wcslen(pszPath));
2137 }
2138 
2139 /*
2140 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
2141 
2142 VOID
2143 GuiConsoleHandleScrollbarMenu(VOID)
2144 {
2145     HMENU hMenu;
2146 
2147     hMenu = CreatePopupMenu();
2148     if (hMenu == NULL)
2149     {
2150         DPRINT("CreatePopupMenu failed\n");
2151         return;
2152     }
2153 
2154     //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
2155     //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2156     //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
2157     //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
2158     //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2159     //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
2160     //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
2161     //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2162     //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
2163     //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
2164 }
2165 */
2166 
2167 HBITMAP
2168 CreateFrameBufferBitmap(HDC hDC, int width, int height)
2169 {
2170     BITMAPINFO bmi;
2171     ZeroMemory(&bmi, sizeof(BITMAPINFO));
2172     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
2173     bmi.bmiHeader.biWidth = width;
2174     bmi.bmiHeader.biHeight = height;
2175     bmi.bmiHeader.biPlanes = 1;
2176     bmi.bmiHeader.biBitCount = GetDeviceCaps(hDC, BITSPIXEL);
2177     bmi.bmiHeader.biCompression = BI_RGB;
2178     return CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, NULL, NULL, 0);
2179 }
2180 
2181 static LRESULT CALLBACK
2182 ConWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
2183 {
2184     LRESULT Result = 0;
2185     PGUI_CONSOLE_DATA GuiData = NULL;
2186     PCONSRV_CONSOLE Console = NULL;
2187 
2188     /*
2189      * - If it's the first time we create a window for the terminal,
2190      *   just initialize it and return.
2191      *
2192      * - If we are destroying the window, just do it and return.
2193      */
2194     if (msg == WM_NCCREATE)
2195     {
2196         return (LRESULT)OnNcCreate(hWnd, (LPCREATESTRUCTW)lParam);
2197     }
2198     else if (msg == WM_NCDESTROY)
2199     {
2200         return OnNcDestroy(hWnd);
2201     }
2202 
2203     /*
2204      * Now the terminal window is initialized.
2205      * Get the terminal data via the window's data.
2206      * If there is no data, just go away.
2207      */
2208     GuiData = GuiGetGuiData(hWnd);
2209     if (GuiData == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
2210 
2211     // TEMPORARY HACK until all of the functions can deal with a NULL GuiData->ActiveBuffer ...
2212     if (GuiData->ActiveBuffer == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
2213 
2214     /*
2215      * Just retrieve a pointer to the console in case somebody needs it.
2216      * It is not NULL because it was checked in GuiGetGuiData.
2217      * Each helper function which needs the console has to validate and lock it.
2218      */
2219     Console = GuiData->Console;
2220 
2221     /* We have a console, start message dispatching */
2222     switch (msg)
2223     {
2224         case WM_ACTIVATE:
2225             OnActivate(GuiData, wParam);
2226             break;
2227 
2228         case WM_CLOSE:
2229             if (OnClose(GuiData)) goto Default;
2230             break;
2231 
2232         case WM_ERASEBKGND:
2233             return TRUE;
2234 
2235         case WM_PAINT:
2236             OnPaint(GuiData);
2237             break;
2238 
2239         case WM_TIMER:
2240             OnTimer(GuiData);
2241             break;
2242 
2243         case WM_PALETTECHANGED:
2244         {
2245             DPRINT("WM_PALETTECHANGED called\n");
2246 
2247             /*
2248              * Protects against infinite loops:
2249              * "... A window that receives this message must not realize
2250              * its palette, unless it determines that wParam does not contain
2251              * its own window handle." (WM_PALETTECHANGED description - MSDN)
2252              *
2253              * This message is sent to all windows, including the one that
2254              * changed the system palette and caused this message to be sent.
2255              * The wParam of this message contains the handle of the window
2256              * that caused the system palette to change. To avoid an infinite
2257              * loop, care must be taken to check that the wParam of this message
2258              * does not match the window's handle.
2259              */
2260             if ((HWND)wParam == hWnd) break;
2261 
2262             DPRINT("WM_PALETTECHANGED ok\n");
2263             OnPaletteChanged(GuiData);
2264             DPRINT("WM_PALETTECHANGED quit\n");
2265             break;
2266         }
2267 
2268         case WM_KEYDOWN:
2269         case WM_KEYUP:
2270         case WM_CHAR:
2271         case WM_DEADCHAR:
2272         case WM_SYSKEYDOWN:
2273         case WM_SYSKEYUP:
2274         case WM_SYSCHAR:
2275         case WM_SYSDEADCHAR:
2276         {
2277             /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */
2278             if (msg == WM_SYSKEYDOWN && (HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_RETURN)
2279             {
2280                 /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */
2281                 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
2282                     GuiConsoleSwitchFullScreen(GuiData);
2283 
2284                 break;
2285             }
2286             /* Detect Alt-Esc/Space/Tab presses defer to DefWindowProc */
2287             if ( (HIWORD(lParam) & KF_ALTDOWN) && (wParam == VK_ESCAPE || wParam == VK_SPACE || wParam == VK_TAB))
2288             {
2289                 return DefWindowProcW(hWnd, msg, wParam, lParam);
2290             }
2291 
2292             OnKey(GuiData, msg, wParam, lParam);
2293             break;
2294         }
2295 
2296         case WM_SETCURSOR:
2297         {
2298             /* Do nothing if the window is hidden */
2299             if (!GuiData->IsWindowVisible) goto Default;
2300 
2301             /*
2302              * The message was sent because we are manually triggering a change.
2303              * Check whether the mouse is indeed present on this console window
2304              * and take appropriate decisions.
2305              */
2306             if (wParam == -1 && lParam == -1)
2307             {
2308                 POINT mouseCoords;
2309                 HWND  hWndHit;
2310 
2311                 /* Get the placement of the mouse */
2312                 GetCursorPos(&mouseCoords);
2313 
2314                 /* On which window is placed the mouse ? */
2315                 hWndHit = WindowFromPoint(mouseCoords);
2316 
2317                 /* It's our window. Perform the hit-test to be used later on. */
2318                 if (hWndHit == hWnd)
2319                 {
2320                     wParam = (WPARAM)hWnd;
2321                     lParam = DefWindowProcW(hWndHit, WM_NCHITTEST, 0,
2322                                             MAKELPARAM(mouseCoords.x, mouseCoords.y));
2323                 }
2324             }
2325 
2326             /* Set the mouse cursor only when we are in the client area */
2327             if ((HWND)wParam == hWnd && LOWORD(lParam) == HTCLIENT)
2328             {
2329                 if (GuiData->MouseCursorRefCount >= 0)
2330                 {
2331                     /* Show the cursor */
2332                     SetCursor(GuiData->hCursor);
2333                 }
2334                 else
2335                 {
2336                     /* Hide the cursor if the reference count is negative */
2337                     SetCursor(NULL);
2338                 }
2339                 return TRUE;
2340             }
2341             else
2342             {
2343                 goto Default;
2344             }
2345         }
2346 
2347         case WM_LBUTTONDOWN:
2348         case WM_MBUTTONDOWN:
2349         case WM_RBUTTONDOWN:
2350         case WM_XBUTTONDOWN:
2351         case WM_LBUTTONUP:
2352         case WM_MBUTTONUP:
2353         case WM_RBUTTONUP:
2354         case WM_XBUTTONUP:
2355         case WM_LBUTTONDBLCLK:
2356         case WM_MBUTTONDBLCLK:
2357         case WM_RBUTTONDBLCLK:
2358         case WM_XBUTTONDBLCLK:
2359         case WM_MOUSEMOVE:
2360         case WM_MOUSEWHEEL:
2361         case WM_MOUSEHWHEEL:
2362         {
2363             Result = OnMouse(GuiData, msg, wParam, lParam);
2364             break;
2365         }
2366 
2367         case WM_HSCROLL:
2368             OnScroll(GuiData, SB_HORZ, LOWORD(wParam));
2369             break;
2370 
2371         case WM_VSCROLL:
2372             OnScroll(GuiData, SB_VERT, LOWORD(wParam));
2373             break;
2374 
2375         case WM_CONTEXTMENU:
2376         {
2377             /* Do nothing if the window is hidden */
2378             if (!GuiData->IsWindowVisible) break;
2379 
2380             if (DefWindowProcW(hWnd /*GuiData->hWindow*/, WM_NCHITTEST, 0, lParam) == HTCLIENT)
2381             {
2382                 HMENU hMenu = CreatePopupMenu();
2383                 if (hMenu != NULL)
2384                 {
2385                     AppendMenuItems(hMenu, GuiConsoleEditMenuItems);
2386                     TrackPopupMenuEx(hMenu,
2387                                      TPM_RIGHTBUTTON,
2388                                      GET_X_LPARAM(lParam),
2389                                      GET_Y_LPARAM(lParam),
2390                                      hWnd,
2391                                      NULL);
2392                     DestroyMenu(hMenu);
2393                 }
2394                 break;
2395             }
2396             else
2397             {
2398                 goto Default;
2399             }
2400         }
2401 
2402         case WM_INITMENU:
2403         {
2404             HMENU hMenu = (HMENU)wParam;
2405             if (hMenu != NULL)
2406             {
2407                 /* Enable or disable the Close menu item */
2408                 EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND |
2409                                (GuiData->IsCloseButtonEnabled ? MF_ENABLED : MF_GRAYED));
2410 
2411                 /* Enable or disable the Copy and Paste items */
2412                 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_COPY , MF_BYCOMMAND |
2413                                ((GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
2414                                 (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) ? MF_ENABLED : MF_GRAYED));
2415                 // FIXME: Following whether the active screen buffer is text-mode
2416                 // or graphics-mode, search for CF_UNICODETEXT or CF_BITMAP formats.
2417                 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_PASTE, MF_BYCOMMAND |
2418                                (!(GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
2419                                 IsClipboardFormatAvailable(CF_UNICODETEXT) ? MF_ENABLED : MF_GRAYED));
2420             }
2421 
2422             SendMenuEvent(Console, WM_INITMENU);
2423             break;
2424         }
2425 
2426         case WM_MENUSELECT:
2427         {
2428             if (HIWORD(wParam) == 0xFFFF) // Allow all the menu flags
2429             {
2430                 SendMenuEvent(Console, WM_MENUSELECT);
2431             }
2432             break;
2433         }
2434 
2435         case WM_COMMAND:
2436         case WM_SYSCOMMAND:
2437         {
2438             Result = OnCommand(GuiData, wParam, lParam);
2439             break;
2440         }
2441 
2442         case WM_DROPFILES:
2443             OnDropFiles(Console, (HDROP)wParam);
2444             break;
2445 
2446         case WM_SETFOCUS:
2447         case WM_KILLFOCUS:
2448             OnFocus(GuiData, (msg == WM_SETFOCUS));
2449             break;
2450 
2451         case WM_GETMINMAXINFO:
2452             OnGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam);
2453             break;
2454 
2455         case WM_MOVE:
2456             OnMove(GuiData);
2457             break;
2458 
2459 #if 0 // This code is here to prepare & control dynamic console SB resizing.
2460         case WM_SIZING:
2461         {
2462             PRECT dragRect = (PRECT)lParam;
2463             switch (wParam)
2464             {
2465                 case WMSZ_LEFT:
2466                     DPRINT1("WMSZ_LEFT\n");
2467                     break;
2468                 case WMSZ_RIGHT:
2469                     DPRINT1("WMSZ_RIGHT\n");
2470                     break;
2471                 case WMSZ_TOP:
2472                     DPRINT1("WMSZ_TOP\n");
2473                     break;
2474                 case WMSZ_TOPLEFT:
2475                     DPRINT1("WMSZ_TOPLEFT\n");
2476                     break;
2477                 case WMSZ_TOPRIGHT:
2478                     DPRINT1("WMSZ_TOPRIGHT\n");
2479                     break;
2480                 case WMSZ_BOTTOM:
2481                     DPRINT1("WMSZ_BOTTOM\n");
2482                     break;
2483                 case WMSZ_BOTTOMLEFT:
2484                     DPRINT1("WMSZ_BOTTOMLEFT\n");
2485                     break;
2486                 case WMSZ_BOTTOMRIGHT:
2487                     DPRINT1("WMSZ_BOTTOMRIGHT\n");
2488                     break;
2489                 default:
2490                     DPRINT1("wParam = %d\n", wParam);
2491                     break;
2492             }
2493             DPRINT1("dragRect = {.left = %d ; .top = %d ; .right = %d ; .bottom = %d}\n",
2494                     dragRect->left, dragRect->top, dragRect->right, dragRect->bottom);
2495             break;
2496         }
2497 #endif
2498 
2499         case WM_SIZE:
2500             OnSize(GuiData, wParam, lParam);
2501             break;
2502 
2503         case PM_RESIZE_TERMINAL:
2504         {
2505             PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
2506             HDC hDC;
2507             HBITMAP hnew, hold;
2508 
2509             DWORD Width, Height;
2510             UINT  WidthUnit, HeightUnit;
2511 
2512             /* Do nothing if the window is hidden */
2513             if (!GuiData->IsWindowVisible) break;
2514 
2515             GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
2516 
2517             Width  = Buff->ScreenBufferSize.X * WidthUnit ;
2518             Height = Buff->ScreenBufferSize.Y * HeightUnit;
2519 
2520             /* Recreate the framebuffer */
2521             hDC  = GetDC(GuiData->hWindow);
2522             hnew = CreateFrameBufferBitmap(hDC, Width, Height);
2523             ReleaseDC(GuiData->hWindow, hDC);
2524             hold = SelectObject(GuiData->hMemDC, hnew);
2525             if (GuiData->hBitmap)
2526             {
2527                 if (hold == GuiData->hBitmap) DeleteObject(GuiData->hBitmap);
2528             }
2529             GuiData->hBitmap = hnew;
2530 
2531             /* Resize the window to the user's values */
2532             GuiData->WindowSizeLock = TRUE;
2533             ResizeConWnd(GuiData, WidthUnit, HeightUnit);
2534             GuiData->WindowSizeLock = FALSE;
2535             break;
2536         }
2537 
2538         /*
2539          * Undocumented message sent by Windows' console.dll for applying console info.
2540          * See http://www.catch22.net/sites/default/source/files/setconsoleinfo.c
2541          * and http://www.scn.rain.com/~neighorn/PDF/MSBugPaper.pdf
2542          * for more information.
2543          */
2544         case WM_SETCONSOLEINFO:
2545         {
2546             GuiApplyUserSettings(GuiData, (HANDLE)wParam);
2547             break;
2548         }
2549 
2550         case PM_CONSOLE_BEEP:
2551             DPRINT1("Beep\n");
2552             Beep(800, 200);
2553             break;
2554 
2555          case PM_CONSOLE_SET_TITLE:
2556             SetWindowTextW(GuiData->hWindow, GuiData->Console->Title.Buffer);
2557             break;
2558 
2559         default: Default:
2560             Result = DefWindowProcW(hWnd, msg, wParam, lParam);
2561             break;
2562     }
2563 
2564     return Result;
2565 }
2566 
2567 /* EOF */
2568