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