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