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