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