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