[TASKMGR] Process page: Allow using "Open File Location" functionality without runnin...
[reactos.git] / win32ss / user / winsrv / consrv / frontends / gui / conwnd.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/winsrv/consrv/frontends/gui/conwnd.c
5 * PURPOSE: GUI Console Window Class
6 * PROGRAMMERS: Gé van Geldorp
7 * Johannes Anderwald
8 * Jeffrey Morlan
9 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
11 */
12
13 /* INCLUDES *******************************************************************/
14
15 #include <consrv.h>
16 #include <intrin.h>
17 #include <windowsx.h>
18 #include <shellapi.h>
19
20 #define NDEBUG
21 #include <debug.h>
22
23 #include "concfg/font.h"
24 #include "guiterm.h"
25 #include "resource.h"
26
27 /* GLOBALS ********************************************************************/
28
29 // #define PM_CREATE_CONSOLE (WM_APP + 1)
30 // #define PM_DESTROY_CONSOLE (WM_APP + 2)
31
32 // See guiterm.c
33 #define CONGUI_MIN_WIDTH 10
34 #define CONGUI_MIN_HEIGHT 10
35 #define CONGUI_UPDATE_TIME 0
36 #define CONGUI_UPDATE_TIMER 1
37
38 #define CURSOR_BLINK_TIME 500
39
40
41 /**************************************************************\
42 \** Define the Console Leader Process for the console window **/
43 #define GWLP_CONWND_ALLOC (2 * sizeof(LONG_PTR))
44 #define GWLP_CONSOLE_LEADER_PID 0
45 #define GWLP_CONSOLE_LEADER_TID 4
46
47 VOID
48 SetConWndConsoleLeaderCID(IN PGUI_CONSOLE_DATA GuiData)
49 {
50 PCONSOLE_PROCESS_DATA ProcessData;
51
52 ProcessData = ConSrvGetConsoleLeaderProcess(GuiData->Console);
53
54 ASSERT(ProcessData != NULL);
55 DPRINT("ProcessData: %p, ProcessData->Process %p.\n", ProcessData, ProcessData->Process);
56
57 if (ProcessData->Process)
58 {
59 CLIENT_ID ConsoleLeaderCID = ProcessData->Process->ClientId;
60 SetWindowLongPtrW(GuiData->hWindow, GWLP_CONSOLE_LEADER_PID,
61 (LONG_PTR)(ConsoleLeaderCID.UniqueProcess));
62 SetWindowLongPtrW(GuiData->hWindow, GWLP_CONSOLE_LEADER_TID,
63 (LONG_PTR)(ConsoleLeaderCID.UniqueThread));
64 }
65 else
66 {
67 SetWindowLongPtrW(GuiData->hWindow, GWLP_CONSOLE_LEADER_PID, 0);
68 SetWindowLongPtrW(GuiData->hWindow, GWLP_CONSOLE_LEADER_TID, 0);
69 }
70 }
71 /**************************************************************/
72
73 HICON ghDefaultIcon = NULL;
74 HICON ghDefaultIconSm = NULL;
75 HCURSOR ghDefaultCursor = NULL;
76
77 typedef struct _GUICONSOLE_MENUITEM
78 {
79 UINT uID;
80 const struct _GUICONSOLE_MENUITEM *SubMenu;
81 WORD wCmdID;
82 } GUICONSOLE_MENUITEM, *PGUICONSOLE_MENUITEM;
83
84 static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems[] =
85 {
86 { IDS_MARK, NULL, ID_SYSTEM_EDIT_MARK },
87 { IDS_COPY, NULL, ID_SYSTEM_EDIT_COPY },
88 { IDS_PASTE, NULL, ID_SYSTEM_EDIT_PASTE },
89 { IDS_SELECTALL, NULL, ID_SYSTEM_EDIT_SELECTALL },
90 { IDS_SCROLL, NULL, ID_SYSTEM_EDIT_SCROLL },
91 { IDS_FIND, NULL, ID_SYSTEM_EDIT_FIND },
92
93 { 0, NULL, 0 } /* End of list */
94 };
95
96 static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems[] =
97 {
98 { IDS_EDIT, GuiConsoleEditMenuItems, 0 },
99 { IDS_DEFAULTS, NULL, ID_SYSTEM_DEFAULTS },
100 { IDS_PROPERTIES, NULL, ID_SYSTEM_PROPERTIES },
101
102 { 0, NULL, 0 } /* End of list */
103 };
104
105 /*
106 * Default 16-color palette for foreground and background
107 * (corresponding flags in comments).
108 */
109 const COLORREF s_Colors[16] =
110 {
111 RGB(0, 0, 0), // (Black)
112 RGB(0, 0, 128), // BLUE
113 RGB(0, 128, 0), // GREEN
114 RGB(0, 128, 128), // BLUE | GREEN
115 RGB(128, 0, 0), // RED
116 RGB(128, 0, 128), // BLUE | RED
117 RGB(128, 128, 0), // GREEN | RED
118 RGB(192, 192, 192), // BLUE | GREEN | RED
119
120 RGB(128, 128, 128), // (Grey) INTENSITY
121 RGB(0, 0, 255), // BLUE | INTENSITY
122 RGB(0, 255, 0), // GREEN | INTENSITY
123 RGB(0, 255, 255), // BLUE | GREEN | INTENSITY
124 RGB(255, 0, 0), // RED | INTENSITY
125 RGB(255, 0, 255), // BLUE | RED | INTENSITY
126 RGB(255, 255, 0), // GREEN | RED | INTENSITY
127 RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY
128 };
129
130 /* FUNCTIONS ******************************************************************/
131
132 static LRESULT CALLBACK
133 ConWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
134
135 BOOLEAN
136 RegisterConWndClass(IN HINSTANCE hInstance)
137 {
138 WNDCLASSEXW WndClass;
139 ATOM WndClassAtom;
140
141 ghDefaultIcon = LoadImageW(hInstance,
142 MAKEINTRESOURCEW(IDI_TERMINAL),
143 IMAGE_ICON,
144 GetSystemMetrics(SM_CXICON),
145 GetSystemMetrics(SM_CYICON),
146 LR_SHARED);
147 ghDefaultIconSm = LoadImageW(hInstance,
148 MAKEINTRESOURCEW(IDI_TERMINAL),
149 IMAGE_ICON,
150 GetSystemMetrics(SM_CXSMICON),
151 GetSystemMetrics(SM_CYSMICON),
152 LR_SHARED);
153 ghDefaultCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_ARROW));
154
155 WndClass.cbSize = sizeof(WNDCLASSEXW);
156 WndClass.lpszClassName = GUI_CONWND_CLASS;
157 WndClass.lpfnWndProc = ConWndProc;
158 WndClass.style = CS_DBLCLKS /* | CS_HREDRAW | CS_VREDRAW */;
159 WndClass.hInstance = hInstance;
160 WndClass.hIcon = ghDefaultIcon;
161 WndClass.hIconSm = ghDefaultIconSm;
162 WndClass.hCursor = ghDefaultCursor;
163 WndClass.hbrBackground = NULL;
164 WndClass.lpszMenuName = NULL;
165 WndClass.cbClsExtra = 0;
166 WndClass.cbWndExtra = GWLP_CONWND_ALLOC;
167
168 WndClassAtom = RegisterClassExW(&WndClass);
169 if (WndClassAtom == 0)
170 {
171 DPRINT1("Failed to register GUI console class\n");
172 }
173 else
174 {
175 NtUserConsoleControl(GuiConsoleWndClassAtom, &WndClassAtom, sizeof(ATOM));
176 }
177
178 return (WndClassAtom != 0);
179 }
180
181 BOOLEAN
182 UnRegisterConWndClass(HINSTANCE hInstance)
183 {
184 return !!UnregisterClassW(GUI_CONWND_CLASS, hInstance);
185 }
186
187 static VOID
188 AppendMenuItems(HMENU hMenu,
189 const GUICONSOLE_MENUITEM *Items)
190 {
191 UINT i = 0;
192 WCHAR szMenuString[255];
193 HMENU hSubMenu;
194
195 do
196 {
197 if (Items[i].uID != (UINT)-1)
198 {
199 if (LoadStringW(ConSrvDllInstance,
200 Items[i].uID,
201 szMenuString,
202 ARRAYSIZE(szMenuString)) > 0)
203 {
204 if (Items[i].SubMenu != NULL)
205 {
206 hSubMenu = CreatePopupMenu();
207 if (hSubMenu != NULL)
208 {
209 AppendMenuItems(hSubMenu, Items[i].SubMenu);
210
211 if (!AppendMenuW(hMenu,
212 MF_STRING | MF_POPUP,
213 (UINT_PTR)hSubMenu,
214 szMenuString))
215 {
216 DestroyMenu(hSubMenu);
217 }
218 }
219 }
220 else
221 {
222 AppendMenuW(hMenu,
223 MF_STRING,
224 Items[i].wCmdID,
225 szMenuString);
226 }
227 }
228 }
229 else
230 {
231 AppendMenuW(hMenu,
232 MF_SEPARATOR,
233 0,
234 NULL);
235 }
236 i++;
237 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
238 }
239
240 //static
241 VOID
242 CreateSysMenu(HWND hWnd)
243 {
244 MENUITEMINFOW mii;
245 HMENU hMenu;
246 PWCHAR ptrTab;
247 WCHAR szMenuStringBack[255];
248
249 hMenu = GetSystemMenu(hWnd, FALSE);
250 if (hMenu == NULL)
251 return;
252
253 mii.cbSize = sizeof(mii);
254 mii.fMask = MIIM_STRING;
255 mii.dwTypeData = szMenuStringBack;
256 mii.cch = ARRAYSIZE(szMenuStringBack);
257
258 GetMenuItemInfoW(hMenu, SC_CLOSE, FALSE, &mii);
259
260 ptrTab = wcschr(szMenuStringBack, L'\t');
261 if (ptrTab)
262 {
263 *ptrTab = L'\0';
264 mii.cch = (UINT)wcslen(szMenuStringBack);
265
266 SetMenuItemInfoW(hMenu, SC_CLOSE, FALSE, &mii);
267 }
268
269 AppendMenuItems(hMenu, GuiConsoleMainMenuItems);
270 DrawMenuBar(hWnd);
271 }
272
273 static VOID
274 SendMenuEvent(PCONSRV_CONSOLE Console, UINT CmdId)
275 {
276 INPUT_RECORD er;
277
278 DPRINT("Menu item ID: %d\n", CmdId);
279
280 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
281
282 /* Send a menu event */
283 er.EventType = MENU_EVENT;
284 er.Event.MenuEvent.dwCommandId = CmdId;
285 ConioProcessInputEvent(Console, &er);
286
287 LeaveCriticalSection(&Console->Lock);
288 }
289
290 static VOID
291 Copy(PGUI_CONSOLE_DATA GuiData);
292 static VOID
293 Paste(PGUI_CONSOLE_DATA GuiData);
294 static VOID
295 UpdateSelection(PGUI_CONSOLE_DATA GuiData,
296 PCOORD SelectionAnchor OPTIONAL,
297 PCOORD coord);
298
299 static VOID
300 Mark(PGUI_CONSOLE_DATA GuiData)
301 {
302 PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
303
304 /* Clear the old selection */
305 GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
306
307 /* Restart a new selection */
308 GuiData->dwSelectionCursor = ActiveBuffer->ViewOrigin;
309 UpdateSelection(GuiData,
310 &GuiData->dwSelectionCursor,
311 &GuiData->dwSelectionCursor);
312 }
313
314 static VOID
315 SelectAll(PGUI_CONSOLE_DATA GuiData)
316 {
317 PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
318 COORD SelectionAnchor;
319
320 /* Clear the old selection */
321 GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
322
323 /*
324 * The selection area extends to the whole screen buffer's width.
325 */
326 SelectionAnchor.X = SelectionAnchor.Y = 0;
327 GuiData->dwSelectionCursor.X = ActiveBuffer->ScreenBufferSize.X - 1;
328
329 /*
330 * Determine whether the selection must extend to just some part
331 * (for text-mode screen buffers) or to all of the screen buffer's
332 * height (for graphics ones).
333 */
334 if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
335 {
336 /*
337 * We select all the characters from the first line
338 * to the line where the cursor is positioned.
339 */
340 GuiData->dwSelectionCursor.Y = ActiveBuffer->CursorPosition.Y;
341 }
342 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
343 {
344 /*
345 * We select all the screen buffer area.
346 */
347 GuiData->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
348 }
349
350 /* Restart a new selection */
351 GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION;
352 UpdateSelection(GuiData, &SelectionAnchor, &GuiData->dwSelectionCursor);
353 }
354
355 static LRESULT
356 OnCommand(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
357 {
358 LRESULT Ret = TRUE;
359 PCONSRV_CONSOLE Console = GuiData->Console;
360
361 /*
362 * In case the selected menu item belongs to the user-reserved menu id range,
363 * send to him a menu event and return directly. The user must handle those
364 * reserved menu commands...
365 */
366 if (GuiData->CmdIdLow <= (UINT)wParam && (UINT)wParam <= GuiData->CmdIdHigh)
367 {
368 SendMenuEvent(Console, (UINT)wParam);
369 goto Quit;
370 }
371
372 /* ... otherwise, perform actions. */
373 switch (wParam)
374 {
375 case ID_SYSTEM_EDIT_MARK:
376 Mark(GuiData);
377 break;
378
379 case ID_SYSTEM_EDIT_COPY:
380 Copy(GuiData);
381 break;
382
383 case ID_SYSTEM_EDIT_PASTE:
384 Paste(GuiData);
385 break;
386
387 case ID_SYSTEM_EDIT_SELECTALL:
388 SelectAll(GuiData);
389 break;
390
391 case ID_SYSTEM_EDIT_SCROLL:
392 DPRINT1("Scrolling is not handled yet\n");
393 break;
394
395 case ID_SYSTEM_EDIT_FIND:
396 DPRINT1("Finding is not handled yet\n");
397 break;
398
399 case ID_SYSTEM_DEFAULTS:
400 GuiConsoleShowConsoleProperties(GuiData, TRUE);
401 break;
402
403 case ID_SYSTEM_PROPERTIES:
404 GuiConsoleShowConsoleProperties(GuiData, FALSE);
405 break;
406
407 default:
408 Ret = FALSE;
409 break;
410 }
411
412 Quit:
413 if (!Ret)
414 Ret = DefWindowProcW(GuiData->hWindow, WM_SYSCOMMAND, wParam, lParam);
415
416 return Ret;
417 }
418
419 static PGUI_CONSOLE_DATA
420 GuiGetGuiData(HWND hWnd)
421 {
422 /* This function ensures that the console pointer is not NULL */
423 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
424 return ( ((GuiData == NULL) || (GuiData->hWindow == hWnd && GuiData->Console != NULL)) ? GuiData : NULL );
425 }
426
427 static VOID
428 ResizeConWnd(PGUI_CONSOLE_DATA GuiData, DWORD WidthUnit, DWORD HeightUnit)
429 {
430 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
431 SCROLLINFO sInfo;
432
433 DWORD Width, Height;
434
435 Width = Buff->ViewSize.X * WidthUnit +
436 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
437 Height = Buff->ViewSize.Y * HeightUnit +
438 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
439
440 /* Set scrollbar sizes */
441 sInfo.cbSize = sizeof(sInfo);
442 sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
443 sInfo.nMin = 0;
444 if (Buff->ScreenBufferSize.Y > Buff->ViewSize.Y)
445 {
446 sInfo.nMax = Buff->ScreenBufferSize.Y - 1;
447 sInfo.nPage = Buff->ViewSize.Y;
448 sInfo.nPos = Buff->ViewOrigin.Y;
449 SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE);
450 Width += GetSystemMetrics(SM_CXVSCROLL);
451 ShowScrollBar(GuiData->hWindow, SB_VERT, TRUE);
452 }
453 else
454 {
455 ShowScrollBar(GuiData->hWindow, SB_VERT, FALSE);
456 }
457
458 if (Buff->ScreenBufferSize.X > Buff->ViewSize.X)
459 {
460 sInfo.nMax = Buff->ScreenBufferSize.X - 1;
461 sInfo.nPage = Buff->ViewSize.X;
462 sInfo.nPos = Buff->ViewOrigin.X;
463 SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE);
464 Height += GetSystemMetrics(SM_CYHSCROLL);
465 ShowScrollBar(GuiData->hWindow, SB_HORZ, TRUE);
466 }
467 else
468 {
469 ShowScrollBar(GuiData->hWindow, SB_HORZ, FALSE);
470 }
471
472 /* Resize the window */
473 SetWindowPos(GuiData->hWindow, NULL, 0, 0, Width, Height,
474 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS);
475 // NOTE: The SWP_NOCOPYBITS flag can be replaced by a subsequent call
476 // to: InvalidateRect(GuiData->hWindow, NULL, TRUE);
477 }
478
479
480 VOID
481 DeleteFonts(PGUI_CONSOLE_DATA GuiData)
482 {
483 ULONG i;
484 for (i = 0; i < ARRAYSIZE(GuiData->Font); ++i)
485 {
486 if (GuiData->Font[i] != NULL) DeleteObject(GuiData->Font[i]);
487 GuiData->Font[i] = NULL;
488 }
489 }
490
491 static HFONT
492 CreateDerivedFont(HFONT OrgFont,
493 // COORD FontSize,
494 ULONG FontWeight,
495 // BOOLEAN bItalic,
496 BOOLEAN bUnderline,
497 BOOLEAN bStrikeOut)
498 {
499 LOGFONTW lf;
500
501 /* Initialize the LOGFONT structure */
502 RtlZeroMemory(&lf, sizeof(lf));
503
504 /* Retrieve the details of the current font */
505 if (GetObjectW(OrgFont, sizeof(lf), &lf) == 0)
506 return NULL;
507
508 /* Change the font attributes */
509 // lf.lfHeight = FontSize.Y;
510 // lf.lfWidth = FontSize.X;
511 lf.lfWeight = FontWeight;
512 // lf.lfItalic = bItalic;
513 lf.lfUnderline = bUnderline;
514 lf.lfStrikeOut = bStrikeOut;
515
516 /* Build a new font */
517 return CreateFontIndirectW(&lf);
518 }
519
520 BOOL
521 InitFonts(
522 _Inout_ PGUI_CONSOLE_DATA GuiData,
523 _In_reads_or_z_(LF_FACESIZE)
524 PCWSTR FaceName,
525 _In_ ULONG FontWeight,
526 _In_ ULONG FontFamily,
527 _In_ COORD FontSize,
528 _In_opt_ UINT CodePage,
529 _In_ BOOL UseDefaultFallback)
530 {
531 HDC hDC;
532 HFONT hFont;
533 FONT_DATA FontData;
534 UINT OldCharWidth = GuiData->CharWidth;
535 UINT OldCharHeight = GuiData->CharHeight;
536 COORD OldFontSize = GuiData->GuiInfo.FontSize;
537 WCHAR NewFaceName[LF_FACESIZE];
538
539 /* Default to current code page if none has been provided */
540 if (!CodePage)
541 CodePage = GuiData->Console->OutputCodePage;
542
543 /*
544 * Initialize a new NORMAL font.
545 */
546
547 /* Copy the requested face name into the local buffer.
548 * It will be modified in output by CreateConsoleFontEx()
549 * to hold a possible fallback font face name. */
550 StringCchCopyNW(NewFaceName, ARRAYSIZE(NewFaceName),
551 FaceName, LF_FACESIZE);
552
553 /* NOTE: FontSize is always in cell height/width units (pixels) */
554 hFont = CreateConsoleFontEx((LONG)(ULONG)FontSize.Y,
555 (LONG)(ULONG)FontSize.X,
556 NewFaceName,
557 FontWeight,
558 FontFamily,
559 CodePage,
560 UseDefaultFallback,
561 &FontData);
562 if (!hFont)
563 {
564 DPRINT1("InitFonts: CreateConsoleFontEx('%S') failed\n", NewFaceName);
565 return FALSE;
566 }
567
568 /* Retrieve its character cell size */
569 hDC = GetDC(GuiData->hWindow);
570 if (!GetFontCellSize(hDC, hFont, &GuiData->CharHeight, &GuiData->CharWidth))
571 {
572 DPRINT1("InitFonts: GetFontCellSize failed\n");
573 ReleaseDC(GuiData->hWindow, hDC);
574 DeleteObject(hFont);
575 return FALSE;
576 }
577 ReleaseDC(GuiData->hWindow, hDC);
578
579 /*
580 * Initialization succeeded.
581 */
582 // Delete all the old fonts first.
583 DeleteFonts(GuiData);
584 GuiData->Font[FONT_NORMAL] = hFont;
585
586 /*
587 * Now build the optional fonts (bold, underlined, mixed).
588 * Do not error in case they fail to be created.
589 */
590 GuiData->Font[FONT_BOLD] =
591 CreateDerivedFont(GuiData->Font[FONT_NORMAL],
592 max(FW_BOLD, FontData.Weight),
593 FALSE,
594 FALSE);
595 GuiData->Font[FONT_UNDERLINE] =
596 CreateDerivedFont(GuiData->Font[FONT_NORMAL],
597 FontData.Weight,
598 TRUE,
599 FALSE);
600 GuiData->Font[FONT_BOLD | FONT_UNDERLINE] =
601 CreateDerivedFont(GuiData->Font[FONT_NORMAL],
602 max(FW_BOLD, FontData.Weight),
603 TRUE,
604 FALSE);
605
606 /*
607 * Save the new font characteristics.
608 */
609 StringCchCopyNW(GuiData->GuiInfo.FaceName,
610 ARRAYSIZE(GuiData->GuiInfo.FaceName),
611 NewFaceName, ARRAYSIZE(NewFaceName));
612 GuiData->GuiInfo.FontWeight = FontData.Weight;
613 GuiData->GuiInfo.FontFamily = FontData.Family;
614 GuiData->GuiInfo.FontSize = FontData.Size;
615
616 /* Resize the terminal, in case the new font has a different size */
617 if ((OldCharWidth != GuiData->CharWidth) ||
618 (OldCharHeight != GuiData->CharHeight) ||
619 (OldFontSize.X != FontData.Size.X ||
620 OldFontSize.Y != FontData.Size.Y))
621 {
622 TermResizeTerminal(GuiData->Console);
623 }
624
625 return TRUE;
626 }
627
628
629 static BOOL
630 OnNcCreate(HWND hWnd, LPCREATESTRUCTW Create)
631 {
632 PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)Create->lpCreateParams;
633 PCONSRV_CONSOLE Console;
634
635 if (GuiData == NULL)
636 {
637 DPRINT1("GuiConsoleNcCreate: No GUI data\n");
638 return FALSE;
639 }
640
641 Console = GuiData->Console;
642
643 GuiData->hWindow = hWnd;
644 GuiData->hSysMenu = GetSystemMenu(hWnd, FALSE);
645 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 */