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