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