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