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