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