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