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