[CONSRV]
[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 /* Free the GuiData registration */
1459 SetWindowLongPtrW(hWnd, GWLP_USERDATA, (DWORD_PTR)NULL);
1460
1461 GetSystemMenu(hWnd, TRUE);
1462
1463 if (GuiData)
1464 {
1465 if (GuiData->IsWindowVisible)
1466 KillTimer(hWnd, CONGUI_UPDATE_TIMER);
1467
1468 /* Free the terminal framebuffer */
1469 if (GuiData->hMemDC ) DeleteDC(GuiData->hMemDC);
1470 if (GuiData->hBitmap) DeleteObject(GuiData->hBitmap);
1471 // if (GuiData->hSysPalette) DeleteObject(GuiData->hSysPalette);
1472 DeleteFonts(GuiData);
1473 }
1474
1475 return DefWindowProcW(hWnd, WM_NCDESTROY, 0, 0);
1476 }
1477
1478 static COORD
1479 PointToCoord(PGUI_CONSOLE_DATA GuiData, LPARAM lParam)
1480 {
1481 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1482 COORD Coord;
1483 UINT WidthUnit, HeightUnit;
1484
1485 GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit);
1486
1487 Coord.X = Buffer->ViewOrigin.X + ((SHORT)LOWORD(lParam) / (int)WidthUnit );
1488 Coord.Y = Buffer->ViewOrigin.Y + ((SHORT)HIWORD(lParam) / (int)HeightUnit);
1489
1490 /* Clip coordinate to ensure it's inside buffer */
1491 if (Coord.X < 0)
1492 Coord.X = 0;
1493 else if (Coord.X >= Buffer->ScreenBufferSize.X)
1494 Coord.X = Buffer->ScreenBufferSize.X - 1;
1495
1496 if (Coord.Y < 0)
1497 Coord.Y = 0;
1498 else if (Coord.Y >= Buffer->ScreenBufferSize.Y)
1499 Coord.Y = Buffer->ScreenBufferSize.Y - 1;
1500
1501 return Coord;
1502 }
1503
1504 static LRESULT
1505 OnMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
1506 {
1507 BOOL Err = FALSE;
1508 PCONSRV_CONSOLE Console = GuiData->Console;
1509
1510 /*
1511 * HACK FOR CORE-8394 (Part 2):
1512 *
1513 * Check whether we should ignore the next mouse move event.
1514 * In either case we reset the HACK flag.
1515 *
1516 * See Part 1 of this hack below.
1517 */
1518 if (GuiData->HackCORE8394IgnoreNextMove && msg == WM_MOUSEMOVE)
1519 {
1520 GuiData->HackCORE8394IgnoreNextMove = FALSE;
1521 goto Quit;
1522 }
1523 GuiData->HackCORE8394IgnoreNextMove = FALSE;
1524
1525 // FIXME: It's here that we need to check whether we have focus or not
1526 // and whether we are or not in edit mode, in order to know if we need
1527 // to deal with the mouse.
1528
1529 if (GuiData->IgnoreNextMouseSignal)
1530 {
1531 if (msg != WM_LBUTTONDOWN &&
1532 msg != WM_MBUTTONDOWN &&
1533 msg != WM_RBUTTONDOWN &&
1534 msg != WM_XBUTTONDOWN)
1535 {
1536 /*
1537 * If this mouse signal is not a button-down action
1538 * then this is the last one being ignored.
1539 */
1540 GuiData->IgnoreNextMouseSignal = FALSE;
1541 }
1542 else
1543 {
1544 /*
1545 * This mouse signal is a button-down action.
1546 * Ignore it and perform default action.
1547 */
1548 Err = TRUE;
1549 }
1550 goto Quit;
1551 }
1552
1553 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE))
1554 {
1555 Err = TRUE;
1556 goto Quit;
1557 }
1558
1559 if ( (GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) ||
1560 (Console->QuickEdit) )
1561 {
1562 switch (msg)
1563 {
1564 case WM_LBUTTONDOWN:
1565 {
1566 /* Clear the old selection */
1567 GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
1568
1569 /* Restart a new selection */
1570 GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1571 SetCapture(GuiData->hWindow);
1572 GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1573 UpdateSelection(GuiData,
1574 &GuiData->dwSelectionCursor,
1575 &GuiData->dwSelectionCursor);
1576
1577 break;
1578 }
1579
1580 case WM_LBUTTONUP:
1581 {
1582 if (!(GuiData->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1583
1584 // GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1585 GuiData->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN;
1586 // UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1587 ReleaseCapture();
1588
1589 break;
1590 }
1591
1592 case WM_LBUTTONDBLCLK:
1593 {
1594 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1595
1596 if (GetType(Buffer) == TEXTMODE_BUFFER)
1597 {
1598 #define IS_WORD_SEP(c) \
1599 ((c) == L'\0' || (c) == L' ' || (c) == L'\t' || (c) == L'\r' || (c) == L'\n')
1600
1601 PTEXTMODE_SCREEN_BUFFER TextBuffer = (PTEXTMODE_SCREEN_BUFFER)Buffer;
1602 COORD cL, cR;
1603 PCHAR_INFO ptrL, ptrR;
1604
1605 /* Starting point */
1606 cL = cR = PointToCoord(GuiData, lParam);
1607 ptrL = ptrR = ConioCoordToPointer(TextBuffer, cL.X, cL.Y);
1608
1609 /* Enlarge the selection by checking for whitespace */
1610 while ((0 < cL.X) && !IS_WORD_SEP(ptrL->Char.UnicodeChar)
1611 && !IS_WORD_SEP((ptrL-1)->Char.UnicodeChar))
1612 {
1613 --cL.X;
1614 --ptrL;
1615 }
1616 while ((cR.X < TextBuffer->ScreenBufferSize.X - 1) &&
1617 !IS_WORD_SEP(ptrR->Char.UnicodeChar) &&
1618 !IS_WORD_SEP((ptrR+1)->Char.UnicodeChar))
1619 {
1620 ++cR.X;
1621 ++ptrR;
1622 }
1623
1624 /*
1625 * Update the selection started with the single
1626 * left-click that preceded this double-click.
1627 */
1628 GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1629 UpdateSelection(GuiData, &cL, &cR);
1630
1631 /* Ignore the next mouse move signal */
1632 GuiData->IgnoreNextMouseSignal = TRUE;
1633 }
1634
1635 break;
1636 }
1637
1638 case WM_RBUTTONDOWN:
1639 case WM_RBUTTONDBLCLK:
1640 {
1641 if (!(GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY))
1642 {
1643 Paste(GuiData);
1644 }
1645 else
1646 {
1647 Copy(GuiData);
1648 }
1649
1650 /* Ignore the next mouse move signal */
1651 GuiData->IgnoreNextMouseSignal = TRUE;
1652 break;
1653 }
1654
1655 case WM_MOUSEMOVE:
1656 {
1657 if (!(wParam & MK_LBUTTON)) break;
1658 if (!(GuiData->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1659
1660 // TODO: Scroll buffer to bring SelectionCursor into view
1661 GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1662 UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1663
1664 break;
1665 }
1666
1667 default:
1668 Err = FALSE; // TRUE;
1669 break;
1670 }
1671 }
1672 else if (Console->InputBuffer.Mode & ENABLE_MOUSE_INPUT)
1673 {
1674 INPUT_RECORD er;
1675 WORD wKeyState = GET_KEYSTATE_WPARAM(wParam);
1676 DWORD dwButtonState = 0;
1677 DWORD dwControlKeyState = 0;
1678 DWORD dwEventFlags = 0;
1679
1680 switch (msg)
1681 {
1682 case WM_LBUTTONDOWN:
1683 SetCapture(GuiData->hWindow);
1684 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1685 dwEventFlags = 0;
1686 break;
1687
1688 case WM_MBUTTONDOWN:
1689 SetCapture(GuiData->hWindow);
1690 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1691 dwEventFlags = 0;
1692 break;
1693
1694 case WM_RBUTTONDOWN:
1695 SetCapture(GuiData->hWindow);
1696 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1697 dwEventFlags = 0;
1698 break;
1699
1700 case WM_XBUTTONDOWN:
1701 {
1702 /* Get which X-button was pressed */
1703 WORD wButton = GET_XBUTTON_WPARAM(wParam);
1704
1705 /* Check for X-button validity */
1706 if (wButton & ~(XBUTTON1 | XBUTTON2))
1707 {
1708 DPRINT1("X-button 0x%04x invalid\n", wButton);
1709 Err = TRUE;
1710 break;
1711 }
1712
1713 SetCapture(GuiData->hWindow);
1714 dwButtonState = (wButton == XBUTTON1 ? FROM_LEFT_3RD_BUTTON_PRESSED
1715 : FROM_LEFT_4TH_BUTTON_PRESSED);
1716 dwEventFlags = 0;
1717 break;
1718 }
1719
1720 case WM_LBUTTONUP:
1721 ReleaseCapture();
1722 dwButtonState = 0;
1723 dwEventFlags = 0;
1724 break;
1725
1726 case WM_MBUTTONUP:
1727 ReleaseCapture();
1728 dwButtonState = 0;
1729 dwEventFlags = 0;
1730 break;
1731
1732 case WM_RBUTTONUP:
1733 ReleaseCapture();
1734 dwButtonState = 0;
1735 dwEventFlags = 0;
1736 break;
1737
1738 case WM_XBUTTONUP:
1739 {
1740 /* Get which X-button was released */
1741 WORD wButton = GET_XBUTTON_WPARAM(wParam);
1742
1743 /* Check for X-button validity */
1744 if (wButton & ~(XBUTTON1 | XBUTTON2))
1745 {
1746 DPRINT1("X-button 0x%04x invalid\n", wButton);
1747 /* Ok, just release the button anyway... */
1748 }
1749
1750 ReleaseCapture();
1751 dwButtonState = 0;
1752 dwEventFlags = 0;
1753 break;
1754 }
1755
1756 case WM_LBUTTONDBLCLK:
1757 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1758 dwEventFlags = DOUBLE_CLICK;
1759 break;
1760
1761 case WM_MBUTTONDBLCLK:
1762 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1763 dwEventFlags = DOUBLE_CLICK;
1764 break;
1765
1766 case WM_RBUTTONDBLCLK:
1767 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1768 dwEventFlags = DOUBLE_CLICK;
1769 break;
1770
1771 case WM_XBUTTONDBLCLK:
1772 {
1773 /* Get which X-button was double-clicked */
1774 WORD wButton = GET_XBUTTON_WPARAM(wParam);
1775
1776 /* Check for X-button validity */
1777 if (wButton & ~(XBUTTON1 | XBUTTON2))
1778 {
1779 DPRINT1("X-button 0x%04x invalid\n", wButton);
1780 Err = TRUE;
1781 break;
1782 }
1783
1784 dwButtonState = (wButton == XBUTTON1 ? FROM_LEFT_3RD_BUTTON_PRESSED
1785 : FROM_LEFT_4TH_BUTTON_PRESSED);
1786 dwEventFlags = DOUBLE_CLICK;
1787 break;
1788 }
1789
1790 case WM_MOUSEMOVE:
1791 dwButtonState = 0;
1792 dwEventFlags = MOUSE_MOVED;
1793 break;
1794
1795 case WM_MOUSEWHEEL:
1796 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1797 dwEventFlags = MOUSE_WHEELED;
1798 break;
1799
1800 case WM_MOUSEHWHEEL:
1801 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1802 dwEventFlags = MOUSE_HWHEELED;
1803 break;
1804
1805 default:
1806 Err = TRUE;
1807 break;
1808 }
1809
1810 /*
1811 * HACK FOR CORE-8394 (Part 1):
1812 *
1813 * It appears that depending on which VM ReactOS runs, the next mouse
1814 * signal coming after a button-down action can be a mouse-move (e.g.
1815 * on VBox, whereas on QEMU it is not the case). However it is NOT a
1816 * rule, so that we cannot use the IgnoreNextMouseSignal flag to just
1817 * "ignore" the next mouse event, thinking it would always be a mouse-
1818 * move signal.
1819 *
1820 * To work around this problem (that should really be fixed in Win32k),
1821 * we use a second flag to ignore this possible next mouse move signal.
1822 */
1823 switch (msg)
1824 {
1825 case WM_LBUTTONDOWN:
1826 case WM_MBUTTONDOWN:
1827 case WM_RBUTTONDOWN:
1828 case WM_XBUTTONDOWN:
1829 GuiData->HackCORE8394IgnoreNextMove = TRUE;
1830 default:
1831 break;
1832 }
1833
1834 if (!Err)
1835 {
1836 if (wKeyState & MK_LBUTTON)
1837 dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED;
1838 if (wKeyState & MK_MBUTTON)
1839 dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED;
1840 if (wKeyState & MK_RBUTTON)
1841 dwButtonState |= RIGHTMOST_BUTTON_PRESSED;
1842 if (wKeyState & MK_XBUTTON1)
1843 dwButtonState |= FROM_LEFT_3RD_BUTTON_PRESSED;
1844 if (wKeyState & MK_XBUTTON2)
1845 dwButtonState |= FROM_LEFT_4TH_BUTTON_PRESSED;
1846
1847 if (GetKeyState(VK_RMENU) & 0x8000)
1848 dwControlKeyState |= RIGHT_ALT_PRESSED;
1849 if (GetKeyState(VK_LMENU) & 0x8000)
1850 dwControlKeyState |= LEFT_ALT_PRESSED;
1851 if (GetKeyState(VK_RCONTROL) & 0x8000)
1852 dwControlKeyState |= RIGHT_CTRL_PRESSED;
1853 if (GetKeyState(VK_LCONTROL) & 0x8000)
1854 dwControlKeyState |= LEFT_CTRL_PRESSED;
1855 if (GetKeyState(VK_SHIFT) & 0x8000)
1856 dwControlKeyState |= SHIFT_PRESSED;
1857 if (GetKeyState(VK_NUMLOCK) & 0x0001)
1858 dwControlKeyState |= NUMLOCK_ON;
1859 if (GetKeyState(VK_SCROLL) & 0x0001)
1860 dwControlKeyState |= SCROLLLOCK_ON;
1861 if (GetKeyState(VK_CAPITAL) & 0x0001)
1862 dwControlKeyState |= CAPSLOCK_ON;
1863 /* See WM_CHAR MSDN documentation for instance */
1864 if (lParam & 0x01000000)
1865 dwControlKeyState |= ENHANCED_KEY;
1866
1867 /* Send a mouse event */
1868 er.EventType = MOUSE_EVENT;
1869 er.Event.MouseEvent.dwMousePosition = PointToCoord(GuiData, lParam);
1870 er.Event.MouseEvent.dwButtonState = dwButtonState;
1871 er.Event.MouseEvent.dwControlKeyState = dwControlKeyState;
1872 er.Event.MouseEvent.dwEventFlags = dwEventFlags;
1873
1874 ConioProcessInputEvent(Console, &er);
1875 }
1876 }
1877 else
1878 {
1879 Err = TRUE;
1880 }
1881
1882 LeaveCriticalSection(&Console->Lock);
1883
1884 Quit:
1885 if (Err)
1886 return DefWindowProcW(GuiData->hWindow, msg, wParam, lParam);
1887 else
1888 return 0;
1889 }
1890
1891 VOID
1892 GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
1893 PGUI_CONSOLE_DATA GuiData);
1894 VOID
1895 GuiCopyFromGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer,
1896 PGUI_CONSOLE_DATA GuiData);
1897
1898 static VOID
1899 Copy(PGUI_CONSOLE_DATA GuiData)
1900 {
1901 if (OpenClipboard(GuiData->hWindow))
1902 {
1903 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1904
1905 if (GetType(Buffer) == TEXTMODE_BUFFER)
1906 {
1907 GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer, GuiData);
1908 }
1909 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1910 {
1911 GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer, GuiData);
1912 }
1913
1914 CloseClipboard();
1915 }
1916
1917 /* Clear the selection */
1918 UpdateSelection(GuiData, NULL, NULL);
1919 }
1920
1921 VOID
1922 GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
1923 PGUI_CONSOLE_DATA GuiData);
1924 VOID
1925 GuiPasteToGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer,
1926 PGUI_CONSOLE_DATA GuiData);
1927
1928 static VOID
1929 Paste(PGUI_CONSOLE_DATA GuiData)
1930 {
1931 if (OpenClipboard(GuiData->hWindow))
1932 {
1933 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1934
1935 if (GetType(Buffer) == TEXTMODE_BUFFER)
1936 {
1937 GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer, GuiData);
1938 }
1939 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1940 {
1941 GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer, GuiData);
1942 }
1943
1944 CloseClipboard();
1945 }
1946 }
1947
1948 static VOID
1949 OnGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData, PMINMAXINFO minMaxInfo)
1950 {
1951 PCONSRV_CONSOLE Console = GuiData->Console;
1952 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
1953 DWORD windx, windy;
1954 UINT WidthUnit, HeightUnit;
1955
1956 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
1957
1958 ActiveBuffer = GuiData->ActiveBuffer;
1959
1960 GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
1961
1962 windx = CONGUI_MIN_WIDTH * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1963 windy = CONGUI_MIN_HEIGHT * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1964
1965 minMaxInfo->ptMinTrackSize.x = windx;
1966 minMaxInfo->ptMinTrackSize.y = windy;
1967
1968 windx = (ActiveBuffer->ScreenBufferSize.X) * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
1969 windy = (ActiveBuffer->ScreenBufferSize.Y) * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
1970
1971 if (ActiveBuffer->ViewSize.X < ActiveBuffer->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
1972 if (ActiveBuffer->ViewSize.Y < ActiveBuffer->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
1973
1974 minMaxInfo->ptMaxTrackSize.x = windx;
1975 minMaxInfo->ptMaxTrackSize.y = windy;
1976
1977 LeaveCriticalSection(&Console->Lock);
1978 }
1979
1980 static VOID
1981 OnSize(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
1982 {
1983 PCONSRV_CONSOLE Console = GuiData->Console;
1984
1985 /* Do nothing if the window is hidden */
1986 if (!GuiData->IsWindowVisible) return;
1987
1988 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
1989
1990 if ((GuiData->WindowSizeLock == FALSE) &&
1991 (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED))
1992 {
1993 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
1994 DWORD windx, windy, charx, chary;
1995 UINT WidthUnit, HeightUnit;
1996
1997 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
1998
1999 GuiData->WindowSizeLock = TRUE;
2000
2001 windx = LOWORD(lParam);
2002 windy = HIWORD(lParam);
2003
2004 // Compensate for existing scroll bars (because lParam values do not accommodate scroll bar)
2005 if (Buff->ViewSize.X < Buff->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
2006 if (Buff->ViewSize.Y < Buff->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
2007
2008 charx = windx / (int)WidthUnit ;
2009 chary = windy / (int)HeightUnit;
2010
2011 // Character alignment (round size up or down)
2012 if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx;
2013 if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
2014
2015 // Compensate for added scroll bars in new window
2016 if (charx < (DWORD)Buff->ScreenBufferSize.X) windy -= GetSystemMetrics(SM_CYHSCROLL); // new window will have a horizontal scroll bar
2017 if (chary < (DWORD)Buff->ScreenBufferSize.Y) windx -= GetSystemMetrics(SM_CXVSCROLL); // new window will have a vertical scroll bar
2018
2019 charx = windx / (int)WidthUnit ;
2020 chary = windy / (int)HeightUnit;
2021
2022 // Character alignment (round size up or down)
2023 if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx;
2024 if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
2025
2026 // Resize window
2027 if ((charx != Buff->ViewSize.X) || (chary != Buff->ViewSize.Y))
2028 {
2029 Buff->ViewSize.X = (charx <= (DWORD)Buff->ScreenBufferSize.X) ? charx : Buff->ScreenBufferSize.X;
2030 Buff->ViewSize.Y = (chary <= (DWORD)Buff->ScreenBufferSize.Y) ? chary : Buff->ScreenBufferSize.Y;
2031 }
2032
2033 ResizeConWnd(GuiData, WidthUnit, HeightUnit);
2034
2035 // Adjust the start of the visible area if we are attempting to show nonexistent areas
2036 if ((Buff->ScreenBufferSize.X - Buff->ViewOrigin.X) < Buff->ViewSize.X) Buff->ViewOrigin.X = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
2037 if ((Buff->ScreenBufferSize.Y - Buff->ViewOrigin.Y) < Buff->ViewSize.Y) Buff->ViewOrigin.Y = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
2038 InvalidateRect(GuiData->hWindow, NULL, TRUE);
2039
2040 GuiData->WindowSizeLock = FALSE;
2041 }
2042
2043 LeaveCriticalSection(&Console->Lock);
2044 }
2045
2046 static VOID
2047 OnMove(PGUI_CONSOLE_DATA GuiData)
2048 {
2049 RECT rcWnd;
2050
2051 // TODO: Simplify the code.
2052 // See: GuiConsoleNotifyWndProc() PM_CREATE_CONSOLE.
2053
2054 /* Retrieve our real position */
2055 GetWindowRect(GuiData->hWindow, &rcWnd);
2056 GuiData->GuiInfo.WindowOrigin.x = rcWnd.left;
2057 GuiData->GuiInfo.WindowOrigin.y = rcWnd.top;
2058 }
2059
2060 /*
2061 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
2062
2063 VOID
2064 GuiConsoleHandleScrollbarMenu(VOID)
2065 {
2066 HMENU hMenu;
2067
2068 hMenu = CreatePopupMenu();
2069 if (hMenu == NULL)
2070 {
2071 DPRINT("CreatePopupMenu failed\n");
2072 return;
2073 }
2074
2075 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
2076 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2077 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
2078 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
2079 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2080 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
2081 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
2082 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2083 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
2084 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
2085 }
2086 */
2087
2088 static LRESULT
2089 OnScroll(PGUI_CONSOLE_DATA GuiData, UINT uMsg, WPARAM wParam)
2090 {
2091 PCONSRV_CONSOLE Console = GuiData->Console;
2092 PCONSOLE_SCREEN_BUFFER Buff;
2093 SCROLLINFO sInfo;
2094 int fnBar;
2095 int old_pos, Maximum;
2096 PSHORT pShowXY;
2097
2098 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return 0;
2099
2100 Buff = GuiData->ActiveBuffer;
2101
2102 if (uMsg == WM_HSCROLL)
2103 {
2104 fnBar = SB_HORZ;
2105 Maximum = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
2106 pShowXY = &Buff->ViewOrigin.X;
2107 }
2108 else
2109 {
2110 fnBar = SB_VERT;
2111 Maximum = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
2112 pShowXY = &Buff->ViewOrigin.Y;
2113 }
2114
2115 /* set scrollbar sizes */
2116 sInfo.cbSize = sizeof(SCROLLINFO);
2117 sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
2118
2119 if (!GetScrollInfo(GuiData->hWindow, fnBar, &sInfo)) goto Quit;
2120
2121 old_pos = sInfo.nPos;
2122
2123 switch (LOWORD(wParam))
2124 {
2125 case SB_LINELEFT:
2126 sInfo.nPos -= 1;
2127 break;
2128
2129 case SB_LINERIGHT:
2130 sInfo.nPos += 1;
2131 break;
2132
2133 case SB_PAGELEFT:
2134 sInfo.nPos -= sInfo.nPage;
2135 break;
2136
2137 case SB_PAGERIGHT:
2138 sInfo.nPos += sInfo.nPage;
2139 break;
2140
2141 case SB_THUMBTRACK:
2142 sInfo.nPos = sInfo.nTrackPos;
2143 ConioPause(Console, PAUSED_FROM_SCROLLBAR);
2144 break;
2145
2146 case SB_THUMBPOSITION:
2147 ConioUnpause(Console, PAUSED_FROM_SCROLLBAR);
2148 break;
2149
2150 case SB_TOP:
2151 sInfo.nPos = sInfo.nMin;
2152 break;
2153
2154 case SB_BOTTOM:
2155 sInfo.nPos = sInfo.nMax;
2156 break;
2157
2158 default:
2159 break;
2160 }
2161
2162 sInfo.nPos = max(sInfo.nPos, 0);
2163 sInfo.nPos = min(sInfo.nPos, Maximum);
2164
2165 if (old_pos != sInfo.nPos)
2166 {
2167 USHORT OldX = Buff->ViewOrigin.X;
2168 USHORT OldY = Buff->ViewOrigin.Y;
2169 UINT WidthUnit, HeightUnit;
2170
2171 *pShowXY = sInfo.nPos;
2172
2173 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
2174
2175 ScrollWindowEx(GuiData->hWindow,
2176 (OldX - Buff->ViewOrigin.X) * WidthUnit ,
2177 (OldY - Buff->ViewOrigin.Y) * HeightUnit,
2178 NULL,
2179 NULL,
2180 NULL,
2181 NULL,
2182 SW_INVALIDATE);
2183
2184 sInfo.fMask = SIF_POS;
2185 SetScrollInfo(GuiData->hWindow, fnBar, &sInfo, TRUE);
2186
2187 UpdateWindow(GuiData->hWindow);
2188 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
2189 }
2190
2191 Quit:
2192 LeaveCriticalSection(&Console->Lock);
2193 return 0;
2194 }
2195
2196
2197 static LRESULT CALLBACK
2198 ConWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
2199 {
2200 LRESULT Result = 0;
2201 PGUI_CONSOLE_DATA GuiData = NULL;
2202 PCONSRV_CONSOLE Console = NULL;
2203
2204 /*
2205 * - If it's the first time we create a window for the terminal,
2206 * just initialize it and return.
2207 *
2208 * - If we are destroying the window, just do it and return.
2209 */
2210 if (msg == WM_NCCREATE)
2211 {
2212 return (LRESULT)OnNcCreate(hWnd, (LPCREATESTRUCTW)lParam);
2213 }
2214 else if (msg == WM_NCDESTROY)
2215 {
2216 return OnNcDestroy(hWnd);
2217 }
2218
2219 /*
2220 * Now the terminal window is initialized.
2221 * Get the terminal data via the window's data.
2222 * If there is no data, just go away.
2223 */
2224 GuiData = GuiGetGuiData(hWnd);
2225 if (GuiData == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
2226
2227 // TEMPORARY HACK until all of the functions can deal with a NULL GuiData->ActiveBuffer ...
2228 if (GuiData->ActiveBuffer == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
2229
2230 /*
2231 * Just retrieve a pointer to the console in case somebody needs it.
2232 * It is not NULL because it was checked in GuiGetGuiData.
2233 * Each helper function which needs the console has to validate and lock it.
2234 */
2235 Console = GuiData->Console;
2236
2237 /* We have a console, start message dispatching */
2238 switch (msg)
2239 {
2240 case WM_ACTIVATE:
2241 OnActivate(GuiData, wParam);
2242 break;
2243
2244 case WM_CLOSE:
2245 if (OnClose(GuiData)) goto Default;
2246 break;
2247
2248 case WM_PAINT:
2249 OnPaint(GuiData);
2250 break;
2251
2252 case WM_TIMER:
2253 OnTimer(GuiData);
2254 break;
2255
2256 case WM_PALETTECHANGED:
2257 {
2258 DPRINT("WM_PALETTECHANGED called\n");
2259
2260 /*
2261 * Protects against infinite loops:
2262 * "... A window that receives this message must not realize
2263 * its palette, unless it determines that wParam does not contain
2264 * its own window handle." (WM_PALETTECHANGED description - MSDN)
2265 *
2266 * This message is sent to all windows, including the one that
2267 * changed the system palette and caused this message to be sent.
2268 * The wParam of this message contains the handle of the window
2269 * that caused the system palette to change. To avoid an infinite
2270 * loop, care must be taken to check that the wParam of this message
2271 * does not match the window's handle.
2272 */
2273 if ((HWND)wParam == hWnd) break;
2274
2275 DPRINT("WM_PALETTECHANGED ok\n");
2276 OnPaletteChanged(GuiData);
2277 DPRINT("WM_PALETTECHANGED quit\n");
2278 break;
2279 }
2280
2281 case WM_KEYDOWN:
2282 case WM_KEYUP:
2283 case WM_CHAR:
2284 case WM_DEADCHAR:
2285 case WM_SYSKEYDOWN:
2286 case WM_SYSKEYUP:
2287 case WM_SYSCHAR:
2288 case WM_SYSDEADCHAR:
2289 {
2290 /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */
2291 if (msg == WM_SYSKEYDOWN && (HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_RETURN)
2292 {
2293 /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */
2294 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
2295 GuiConsoleSwitchFullScreen(GuiData);
2296
2297 break;
2298 }
2299 /* Detect Alt-Esc/Space/Tab presses defer to DefWindowProc */
2300 if ( (HIWORD(lParam) & KF_ALTDOWN) && (wParam == VK_ESCAPE || wParam == VK_SPACE || wParam == VK_TAB))
2301 {
2302 return DefWindowProcW(hWnd, msg, wParam, lParam);
2303 }
2304
2305 OnKey(GuiData, msg, wParam, lParam);
2306 break;
2307 }
2308
2309 case WM_SETCURSOR:
2310 {
2311 /* Do nothing if the window is hidden */
2312 if (!GuiData->IsWindowVisible) goto Default;
2313
2314 /*
2315 * The message was sent because we are manually triggering a change.
2316 * Check whether the mouse is indeed present on this console window
2317 * and take appropriate decisions.
2318 */
2319 if (wParam == -1 && lParam == -1)
2320 {
2321 POINT mouseCoords;
2322 HWND hWndHit;
2323
2324 /* Get the placement of the mouse */
2325 GetCursorPos(&mouseCoords);
2326
2327 /* On which window is placed the mouse ? */
2328 hWndHit = WindowFromPoint(mouseCoords);
2329
2330 /* It's our window. Perform the hit-test to be used later on. */
2331 if (hWndHit == hWnd)
2332 {
2333 wParam = (WPARAM)hWnd;
2334 lParam = DefWindowProcW(hWndHit, WM_NCHITTEST, 0,
2335 MAKELPARAM(mouseCoords.x, mouseCoords.y));
2336 }
2337 }
2338
2339 /* Set the mouse cursor only when we are in the client area */
2340 if ((HWND)wParam == hWnd && LOWORD(lParam) == HTCLIENT)
2341 {
2342 if (GuiData->MouseCursorRefCount >= 0)
2343 {
2344 /* Show the cursor */
2345 SetCursor(GuiData->hCursor);
2346 }
2347 else
2348 {
2349 /* Hide the cursor if the reference count is negative */
2350 SetCursor(NULL);
2351 }
2352 return TRUE;
2353 }
2354 else
2355 {
2356 goto Default;
2357 }
2358 }
2359
2360 case WM_LBUTTONDOWN:
2361 case WM_MBUTTONDOWN:
2362 case WM_RBUTTONDOWN:
2363 case WM_XBUTTONDOWN:
2364 case WM_LBUTTONUP:
2365 case WM_MBUTTONUP:
2366 case WM_RBUTTONUP:
2367 case WM_XBUTTONUP:
2368 case WM_LBUTTONDBLCLK:
2369 case WM_MBUTTONDBLCLK:
2370 case WM_RBUTTONDBLCLK:
2371 case WM_XBUTTONDBLCLK:
2372 case WM_MOUSEMOVE:
2373 case WM_MOUSEWHEEL:
2374 case WM_MOUSEHWHEEL:
2375 {
2376 Result = OnMouse(GuiData, msg, wParam, lParam);
2377 break;
2378 }
2379
2380 case WM_HSCROLL:
2381 case WM_VSCROLL:
2382 {
2383 Result = OnScroll(GuiData, msg, wParam);
2384 break;
2385 }
2386
2387 case WM_CONTEXTMENU:
2388 {
2389 /* Do nothing if the window is hidden */
2390 if (!GuiData->IsWindowVisible) break;
2391
2392 if (DefWindowProcW(hWnd /*GuiData->hWindow*/, WM_NCHITTEST, 0, lParam) == HTCLIENT)
2393 {
2394 HMENU hMenu = CreatePopupMenu();
2395 if (hMenu != NULL)
2396 {
2397 AppendMenuItems(hMenu, GuiConsoleEditMenuItems);
2398 TrackPopupMenuEx(hMenu,
2399 TPM_RIGHTBUTTON,
2400 GET_X_LPARAM(lParam),
2401 GET_Y_LPARAM(lParam),
2402 hWnd,
2403 NULL);
2404 DestroyMenu(hMenu);
2405 }
2406 break;
2407 }
2408 else
2409 {
2410 goto Default;
2411 }
2412 }
2413
2414 case WM_INITMENU:
2415 {
2416 HMENU hMenu = (HMENU)wParam;
2417 if (hMenu != NULL)
2418 {
2419 /* Enable or disable the Close menu item */
2420 EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND |
2421 (GuiData->IsCloseButtonEnabled ? MF_ENABLED : MF_GRAYED));
2422
2423 /* Enable or disable the Copy and Paste items */
2424 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_COPY , MF_BYCOMMAND |
2425 ((GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
2426 (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) ? MF_ENABLED : MF_GRAYED));
2427 // FIXME: Following whether the active screen buffer is text-mode
2428 // or graphics-mode, search for CF_UNICODETEXT or CF_BITMAP formats.
2429 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_PASTE, MF_BYCOMMAND |
2430 (!(GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
2431 IsClipboardFormatAvailable(CF_UNICODETEXT) ? MF_ENABLED : MF_GRAYED));
2432 }
2433
2434 SendMenuEvent(Console, WM_INITMENU);
2435 break;
2436 }
2437
2438 case WM_MENUSELECT:
2439 {
2440 if (HIWORD(wParam) == 0xFFFF) // Allow all the menu flags
2441 {
2442 SendMenuEvent(Console, WM_MENUSELECT);
2443 }
2444 break;
2445 }
2446
2447 case WM_COMMAND:
2448 case WM_SYSCOMMAND:
2449 {
2450 Result = OnCommand(GuiData, wParam, lParam);
2451 break;
2452 }
2453
2454 case WM_SETFOCUS:
2455 case WM_KILLFOCUS:
2456 OnFocus(GuiData, (msg == WM_SETFOCUS));
2457 break;
2458
2459 case WM_GETMINMAXINFO:
2460 OnGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam);
2461 break;
2462
2463 case WM_MOVE:
2464 OnMove(GuiData);
2465 break;
2466
2467 #if 0 // This code is here to prepare & control dynamic console SB resizing.
2468 case WM_SIZING:
2469 {
2470 PRECT dragRect = (PRECT)lParam;
2471 switch (wParam)
2472 {
2473 case WMSZ_LEFT:
2474 DPRINT1("WMSZ_LEFT\n");
2475 break;
2476 case WMSZ_RIGHT:
2477 DPRINT1("WMSZ_RIGHT\n");
2478 break;
2479 case WMSZ_TOP:
2480 DPRINT1("WMSZ_TOP\n");
2481 break;
2482 case WMSZ_TOPLEFT:
2483 DPRINT1("WMSZ_TOPLEFT\n");
2484 break;
2485 case WMSZ_TOPRIGHT:
2486 DPRINT1("WMSZ_TOPRIGHT\n");
2487 break;
2488 case WMSZ_BOTTOM:
2489 DPRINT1("WMSZ_BOTTOM\n");
2490 break;
2491 case WMSZ_BOTTOMLEFT:
2492 DPRINT1("WMSZ_BOTTOMLEFT\n");
2493 break;
2494 case WMSZ_BOTTOMRIGHT:
2495 DPRINT1("WMSZ_BOTTOMRIGHT\n");
2496 break;
2497 default:
2498 DPRINT1("wParam = %d\n", wParam);
2499 break;
2500 }
2501 DPRINT1("dragRect = {.left = %d ; .top = %d ; .right = %d ; .bottom = %d}\n",
2502 dragRect->left, dragRect->top, dragRect->right, dragRect->bottom);
2503 break;
2504 }
2505 #endif
2506
2507 case WM_SIZE:
2508 OnSize(GuiData, wParam, lParam);
2509 break;
2510
2511 case PM_RESIZE_TERMINAL:
2512 {
2513 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
2514 HDC hDC;
2515 HBITMAP hnew, hold;
2516
2517 DWORD Width, Height;
2518 UINT WidthUnit, HeightUnit;
2519
2520 /* Do nothing if the window is hidden */
2521 if (!GuiData->IsWindowVisible) break;
2522
2523 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
2524
2525 Width = Buff->ScreenBufferSize.X * WidthUnit ;
2526 Height = Buff->ScreenBufferSize.Y * HeightUnit;
2527
2528 /* Recreate the framebuffer */
2529 hDC = GetDC(GuiData->hWindow);
2530 hnew = CreateCompatibleBitmap(hDC, Width, Height);
2531 ReleaseDC(GuiData->hWindow, hDC);
2532 hold = SelectObject(GuiData->hMemDC, hnew);
2533 if (GuiData->hBitmap)
2534 {
2535 if (hold == GuiData->hBitmap) DeleteObject(GuiData->hBitmap);
2536 }
2537 GuiData->hBitmap = hnew;
2538
2539 /* Resize the window to the user's values */
2540 GuiData->WindowSizeLock = TRUE;
2541 ResizeConWnd(GuiData, WidthUnit, HeightUnit);
2542 GuiData->WindowSizeLock = FALSE;
2543 break;
2544 }
2545
2546 /*
2547 * Undocumented message sent by Windows' console.dll for applying console info.
2548 * See http://www.catch22.net/sites/default/source/files/setconsoleinfo.c
2549 * and http://www.scn.rain.com/~neighorn/PDF/MSBugPaper.pdf
2550 * for more information.
2551 */
2552 case WM_SETCONSOLEINFO:
2553 {
2554 GuiApplyUserSettings(GuiData, (HANDLE)wParam);
2555 break;
2556 }
2557
2558 case PM_CONSOLE_BEEP:
2559 DPRINT1("Beep\n");
2560 Beep(800, 200);
2561 break;
2562
2563 // case PM_CONSOLE_SET_TITLE:
2564 // SetWindowTextW(GuiData->hWindow, GuiData->Console->Title.Buffer);
2565 // break;
2566
2567 default: Default:
2568 Result = DefWindowProcW(hWnd, msg, wParam, lParam);
2569 break;
2570 }
2571
2572 return Result;
2573 }
2574
2575 /* EOF */