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