[WIN32SS][WINSRV] Fullwidth character handling Part 2 (#2240)
[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 = NULL;
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 // TODO: Move cursor display here!
1021
1022 LeaveCriticalSection(&GuiData->Lock);
1023 }
1024 EndPaint(GuiData->hWindow, &ps);
1025
1026 return;
1027 }
1028
1029 static VOID
1030 OnPaletteChanged(PGUI_CONSOLE_DATA GuiData)
1031 {
1032 PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
1033
1034 /* Do nothing if the window is hidden */
1035 if (!GuiData->IsWindowVisible) return;
1036
1037 // See WM_PALETTECHANGED message
1038 // if ((HWND)wParam == hWnd) break;
1039
1040 // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
1041 if (ActiveBuffer->PaletteHandle)
1042 {
1043 DPRINT("WM_PALETTECHANGED changing palette\n");
1044
1045 /* Specify the use of the system palette for the framebuffer */
1046 SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage);
1047
1048 /* Realize the (logical) palette */
1049 RealizePalette(GuiData->hMemDC);
1050 }
1051 }
1052
1053 static BOOL
1054 IsSystemKey(WORD VirtualKeyCode)
1055 {
1056 switch (VirtualKeyCode)
1057 {
1058 /* From MSDN, "Virtual-Key Codes" */
1059 case VK_RETURN:
1060 case VK_SHIFT:
1061 case VK_CONTROL:
1062 case VK_MENU:
1063 case VK_PAUSE:
1064 case VK_CAPITAL:
1065 case VK_ESCAPE:
1066 case VK_LWIN:
1067 case VK_RWIN:
1068 case VK_NUMLOCK:
1069 case VK_SCROLL:
1070 return TRUE;
1071 default:
1072 return FALSE;
1073 }
1074 }
1075
1076 static VOID
1077 OnKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
1078 {
1079 PCONSRV_CONSOLE Console = GuiData->Console;
1080 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
1081
1082 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
1083
1084 ActiveBuffer = GuiData->ActiveBuffer;
1085
1086 if (GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS)
1087 {
1088 WORD VirtualKeyCode = LOWORD(wParam);
1089
1090 if (msg != WM_KEYDOWN) goto Quit;
1091
1092 if (VirtualKeyCode == VK_RETURN)
1093 {
1094 /* Copy (and clear) selection if ENTER is pressed */
1095 Copy(GuiData);
1096 goto Quit;
1097 }
1098 else if ( VirtualKeyCode == VK_ESCAPE ||
1099 (VirtualKeyCode == 'C' && (GetKeyState(VK_CONTROL) & KEY_PRESSED)) )
1100 {
1101 /* Cancel selection if ESC or Ctrl-C are pressed */
1102 UpdateSelection(GuiData, NULL, NULL);
1103 goto Quit;
1104 }
1105
1106 if ((GuiData->Selection.dwFlags & CONSOLE_MOUSE_SELECTION) == 0)
1107 {
1108 /* Keyboard selection mode */
1109 BOOL Interpreted = FALSE;
1110 BOOL MajPressed = !!(GetKeyState(VK_SHIFT) & KEY_PRESSED);
1111
1112 switch (VirtualKeyCode)
1113 {
1114 case VK_LEFT:
1115 {
1116 Interpreted = TRUE;
1117 if (GuiData->dwSelectionCursor.X > 0)
1118 GuiData->dwSelectionCursor.X--;
1119
1120 break;
1121 }
1122
1123 case VK_RIGHT:
1124 {
1125 Interpreted = TRUE;
1126 if (GuiData->dwSelectionCursor.X < ActiveBuffer->ScreenBufferSize.X - 1)
1127 GuiData->dwSelectionCursor.X++;
1128
1129 break;
1130 }
1131
1132 case VK_UP:
1133 {
1134 Interpreted = TRUE;
1135 if (GuiData->dwSelectionCursor.Y > 0)
1136 GuiData->dwSelectionCursor.Y--;
1137
1138 break;
1139 }
1140
1141 case VK_DOWN:
1142 {
1143 Interpreted = TRUE;
1144 if (GuiData->dwSelectionCursor.Y < ActiveBuffer->ScreenBufferSize.Y - 1)
1145 GuiData->dwSelectionCursor.Y++;
1146
1147 break;
1148 }
1149
1150 case VK_HOME:
1151 {
1152 Interpreted = TRUE;
1153 GuiData->dwSelectionCursor.X = 0;
1154 GuiData->dwSelectionCursor.Y = 0;
1155 break;
1156 }
1157
1158 case VK_END:
1159 {
1160 Interpreted = TRUE;
1161 GuiData->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
1162 break;
1163 }
1164
1165 case VK_PRIOR:
1166 {
1167 Interpreted = TRUE;
1168 GuiData->dwSelectionCursor.Y -= ActiveBuffer->ViewSize.Y;
1169 if (GuiData->dwSelectionCursor.Y < 0)
1170 GuiData->dwSelectionCursor.Y = 0;
1171
1172 break;
1173 }
1174
1175 case VK_NEXT:
1176 {
1177 Interpreted = TRUE;
1178 GuiData->dwSelectionCursor.Y += ActiveBuffer->ViewSize.Y;
1179 if (GuiData->dwSelectionCursor.Y >= ActiveBuffer->ScreenBufferSize.Y)
1180 GuiData->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
1181
1182 break;
1183 }
1184
1185 default:
1186 break;
1187 }
1188
1189 if (Interpreted)
1190 {
1191 UpdateSelection(GuiData,
1192 !MajPressed ? &GuiData->dwSelectionCursor : NULL,
1193 &GuiData->dwSelectionCursor);
1194 }
1195 else if (!IsSystemKey(VirtualKeyCode))
1196 {
1197 /* Emit an error beep sound */
1198 SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0);
1199 }
1200
1201 goto Quit;
1202 }
1203 else
1204 {
1205 /* Mouse selection mode */
1206
1207 if (!IsSystemKey(VirtualKeyCode))
1208 {
1209 /* Clear the selection and send the key into the input buffer */
1210 UpdateSelection(GuiData, NULL, NULL);
1211 }
1212 else
1213 {
1214 goto Quit;
1215 }
1216 }
1217 }
1218
1219 if ((GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0)
1220 {
1221 MSG Message;
1222
1223 Message.hwnd = GuiData->hWindow;
1224 Message.message = msg;
1225 Message.wParam = wParam;
1226 Message.lParam = lParam;
1227
1228 ConioProcessKey(Console, &Message);
1229 }
1230
1231 Quit:
1232 LeaveCriticalSection(&Console->Lock);
1233 }
1234
1235
1236 // FIXME: Remove after fixing OnTimer
1237 VOID
1238 InvalidateCell(PGUI_CONSOLE_DATA GuiData,
1239 SHORT x, SHORT y);
1240
1241 static VOID
1242 OnTimer(PGUI_CONSOLE_DATA GuiData)
1243 {
1244 PCONSRV_CONSOLE Console = GuiData->Console;
1245 PCONSOLE_SCREEN_BUFFER Buff;
1246
1247 /* Do nothing if the window is hidden */
1248 if (!GuiData->IsWindowVisible) return;
1249
1250 SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CURSOR_BLINK_TIME, NULL);
1251
1252 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
1253
1254 Buff = GuiData->ActiveBuffer;
1255
1256 if (GetType(Buff) == TEXTMODE_BUFFER)
1257 {
1258 InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
1259 Buff->CursorBlinkOn = !Buff->CursorBlinkOn;
1260
1261 if ((GuiData->OldCursor.x != Buff->CursorPosition.X) ||
1262 (GuiData->OldCursor.y != Buff->CursorPosition.Y))
1263 {
1264 SCROLLINFO sInfo;
1265 int OldScrollX = -1, OldScrollY = -1;
1266 int NewScrollX = -1, NewScrollY = -1;
1267
1268 sInfo.cbSize = sizeof(sInfo);
1269 sInfo.fMask = SIF_POS;
1270 // Capture the original position of the scroll bars and save them.
1271 if (GetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo)) OldScrollX = sInfo.nPos;
1272 if (GetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo)) OldScrollY = sInfo.nPos;
1273
1274 // If we successfully got the info for the horizontal scrollbar
1275 if (OldScrollX >= 0)
1276 {
1277 if ((Buff->CursorPosition.X < Buff->ViewOrigin.X) ||
1278 (Buff->CursorPosition.X >= (Buff->ViewOrigin.X + Buff->ViewSize.X)))
1279 {
1280 // Handle the horizontal scroll bar
1281 if (Buff->CursorPosition.X >= Buff->ViewSize.X)
1282 NewScrollX = Buff->CursorPosition.X - Buff->ViewSize.X + 1;
1283 else
1284 NewScrollX = 0;
1285 }
1286 else
1287 {
1288 NewScrollX = OldScrollX;
1289 }
1290 }
1291 // If we successfully got the info for the vertical scrollbar
1292 if (OldScrollY >= 0)
1293 {
1294 if ((Buff->CursorPosition.Y < Buff->ViewOrigin.Y) ||
1295 (Buff->CursorPosition.Y >= (Buff->ViewOrigin.Y + Buff->ViewSize.Y)))
1296 {
1297 // Handle the vertical scroll bar
1298 if (Buff->CursorPosition.Y >= Buff->ViewSize.Y)
1299 NewScrollY = Buff->CursorPosition.Y - Buff->ViewSize.Y + 1;
1300 else
1301 NewScrollY = 0;
1302 }
1303 else
1304 {
1305 NewScrollY = OldScrollY;
1306 }
1307 }
1308
1309 // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area
1310 // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar
1311 // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling)
1312 // and their associated scrollbar is left alone.
1313 if ((OldScrollX != NewScrollX) || (OldScrollY != NewScrollY))
1314 {
1315 Buff->ViewOrigin.X = NewScrollX;
1316 Buff->ViewOrigin.Y = NewScrollY;
1317 ScrollWindowEx(GuiData->hWindow,
1318 (OldScrollX - NewScrollX) * GuiData->CharWidth,
1319 (OldScrollY - NewScrollY) * GuiData->CharHeight,
1320 NULL,
1321 NULL,
1322 NULL,
1323 NULL,
1324 SW_INVALIDATE);
1325 if (NewScrollX >= 0)
1326 {
1327 sInfo.nPos = NewScrollX;
1328 SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE);
1329 }
1330 if (NewScrollY >= 0)
1331 {
1332 sInfo.nPos = NewScrollY;
1333 SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE);
1334 }
1335 UpdateWindow(GuiData->hWindow);
1336 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1337 GuiData->OldCursor.x = Buff->CursorPosition.X;
1338 GuiData->OldCursor.y = Buff->CursorPosition.Y;
1339 }
1340 }
1341 }
1342 else /* if (GetType(Buff) == GRAPHICS_BUFFER) */
1343 {
1344 }
1345
1346 LeaveCriticalSection(&Console->Lock);
1347 }
1348
1349 static BOOL
1350 OnClose(PGUI_CONSOLE_DATA GuiData)
1351 {
1352 PCONSRV_CONSOLE Console = GuiData->Console;
1353
1354 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE))
1355 return TRUE;
1356
1357 // TODO: Prompt for termination ? (Warn the user about possible apps running in this console)
1358
1359 /*
1360 * FIXME: Windows will wait up to 5 seconds for the thread to exit.
1361 * We shouldn't wait here, though, since the console lock is entered.
1362 * A copy of the thread list probably needs to be made.
1363 */
1364 ConSrvConsoleProcessCtrlEvent(Console, 0, CTRL_CLOSE_EVENT);
1365
1366 LeaveCriticalSection(&Console->Lock);
1367 return FALSE;
1368 }
1369
1370 static LRESULT
1371 OnNcDestroy(HWND hWnd)
1372 {
1373 PGUI_CONSOLE_DATA GuiData = GuiGetGuiData(hWnd);
1374
1375 /* Free the GuiData registration */
1376 SetWindowLongPtrW(hWnd, GWLP_USERDATA, (DWORD_PTR)NULL);
1377
1378 /* Reset the system menu back to default and destroy the previous menu */
1379 GetSystemMenu(hWnd, TRUE);
1380
1381 if (GuiData)
1382 {
1383 if (GuiData->IsWindowVisible)
1384 KillTimer(hWnd, CONGUI_UPDATE_TIMER);
1385
1386 /* Free the terminal framebuffer */
1387 if (GuiData->hMemDC ) DeleteDC(GuiData->hMemDC);
1388 if (GuiData->hBitmap) DeleteObject(GuiData->hBitmap);
1389 // if (GuiData->hSysPalette) DeleteObject(GuiData->hSysPalette);
1390 DeleteFonts(GuiData);
1391 }
1392
1393 return DefWindowProcW(hWnd, WM_NCDESTROY, 0, 0);
1394 }
1395
1396 static VOID
1397 OnScroll(PGUI_CONSOLE_DATA GuiData, INT nBar, WORD sbCode)
1398 {
1399 PCONSRV_CONSOLE Console = GuiData->Console;
1400 PCONSOLE_SCREEN_BUFFER Buff;
1401 SCROLLINFO sInfo;
1402 INT oldPos, Maximum;
1403 PSHORT pOriginXY;
1404
1405 ASSERT(nBar == SB_HORZ || nBar == SB_VERT);
1406
1407 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
1408
1409 Buff = GuiData->ActiveBuffer;
1410
1411 if (nBar == SB_HORZ)
1412 {
1413 Maximum = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
1414 pOriginXY = &Buff->ViewOrigin.X;
1415 }
1416 else // if (nBar == SB_VERT)
1417 {
1418 Maximum = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
1419 pOriginXY = &Buff->ViewOrigin.Y;
1420 }
1421
1422 /* Set scrollbar sizes */
1423 sInfo.cbSize = sizeof(sInfo);
1424 sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
1425
1426 if (!GetScrollInfo(GuiData->hWindow, nBar, &sInfo)) goto Quit;
1427
1428 oldPos = sInfo.nPos;
1429
1430 switch (sbCode)
1431 {
1432 case SB_LINEUP: // SB_LINELEFT:
1433 sInfo.nPos--;
1434 break;
1435
1436 case SB_LINEDOWN: // SB_LINERIGHT:
1437 sInfo.nPos++;
1438 break;
1439
1440 case SB_PAGEUP: // SB_PAGELEFT:
1441 sInfo.nPos -= sInfo.nPage;
1442 break;
1443
1444 case SB_PAGEDOWN: // SB_PAGERIGHT:
1445 sInfo.nPos += sInfo.nPage;
1446 break;
1447
1448 case SB_THUMBTRACK:
1449 sInfo.nPos = sInfo.nTrackPos;
1450 ConioPause(Console, PAUSED_FROM_SCROLLBAR);
1451 break;
1452
1453 case SB_THUMBPOSITION:
1454 sInfo.nPos = sInfo.nTrackPos;
1455 ConioUnpause(Console, PAUSED_FROM_SCROLLBAR);
1456 break;
1457
1458 case SB_TOP: // SB_LEFT:
1459 sInfo.nPos = sInfo.nMin;
1460 break;
1461
1462 case SB_BOTTOM: // SB_RIGHT:
1463 sInfo.nPos = sInfo.nMax;
1464 break;
1465
1466 default:
1467 break;
1468 }
1469
1470 sInfo.nPos = min(max(sInfo.nPos, 0), Maximum);
1471
1472 if (oldPos != sInfo.nPos)
1473 {
1474 USHORT OldX = Buff->ViewOrigin.X;
1475 USHORT OldY = Buff->ViewOrigin.Y;
1476 UINT WidthUnit, HeightUnit;
1477
1478 /* We now modify Buff->ViewOrigin */
1479 *pOriginXY = sInfo.nPos;
1480
1481 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
1482
1483 ScrollWindowEx(GuiData->hWindow,
1484 (OldX - Buff->ViewOrigin.X) * WidthUnit ,
1485 (OldY - Buff->ViewOrigin.Y) * HeightUnit,
1486 NULL,
1487 NULL,
1488 NULL,
1489 NULL,
1490 SW_INVALIDATE);
1491
1492 sInfo.fMask = SIF_POS;
1493 SetScrollInfo(GuiData->hWindow, nBar, &sInfo, TRUE);
1494
1495 UpdateWindow(GuiData->hWindow);
1496 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1497 }
1498
1499 Quit:
1500 LeaveCriticalSection(&Console->Lock);
1501 return;
1502 }
1503
1504 static COORD
1505 PointToCoord(PGUI_CONSOLE_DATA GuiData, LPARAM lParam)
1506 {
1507 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1508 COORD Coord;
1509 UINT WidthUnit, HeightUnit;
1510
1511 GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit);
1512
1513 Coord.X = Buffer->ViewOrigin.X + ((SHORT)LOWORD(lParam) / (int)WidthUnit );
1514 Coord.Y = Buffer->ViewOrigin.Y + ((SHORT)HIWORD(lParam) / (int)HeightUnit);
1515
1516 /* Clip coordinate to ensure it's inside buffer */
1517 if (Coord.X < 0)
1518 Coord.X = 0;
1519 else if (Coord.X >= Buffer->ScreenBufferSize.X)
1520 Coord.X = Buffer->ScreenBufferSize.X - 1;
1521
1522 if (Coord.Y < 0)
1523 Coord.Y = 0;
1524 else if (Coord.Y >= Buffer->ScreenBufferSize.Y)
1525 Coord.Y = Buffer->ScreenBufferSize.Y - 1;
1526
1527 return Coord;
1528 }
1529
1530 static LRESULT
1531 OnMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
1532 {
1533 BOOL DoDefault = FALSE;
1534 PCONSRV_CONSOLE Console = GuiData->Console;
1535
1536 /*
1537 * HACK FOR CORE-8394 (Part 2):
1538 *
1539 * Check whether we should ignore the next mouse move event.
1540 * In either case we reset the HACK flag.
1541 *
1542 * See Part 1 of this hack below.
1543 */
1544 if (GuiData->HackCORE8394IgnoreNextMove && msg == WM_MOUSEMOVE)
1545 {
1546 GuiData->HackCORE8394IgnoreNextMove = FALSE;
1547 goto Quit;
1548 }
1549 GuiData->HackCORE8394IgnoreNextMove = FALSE;
1550
1551 // FIXME: It's here that we need to check whether we have focus or not
1552 // and whether we are or not in edit mode, in order to know if we need
1553 // to deal with the mouse.
1554
1555 if (GuiData->IgnoreNextMouseSignal)
1556 {
1557 if (msg != WM_LBUTTONDOWN &&
1558 msg != WM_MBUTTONDOWN &&
1559 msg != WM_RBUTTONDOWN &&
1560 msg != WM_XBUTTONDOWN)
1561 {
1562 /*
1563 * If this mouse signal is not a button-down action
1564 * then this is the last one being ignored.
1565 */
1566 GuiData->IgnoreNextMouseSignal = FALSE;
1567 }
1568 else
1569 {
1570 /*
1571 * This mouse signal is a button-down action.
1572 * Ignore it and perform default action.
1573 */
1574 DoDefault = TRUE;
1575 }
1576 goto Quit;
1577 }
1578
1579 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE))
1580 {
1581 DoDefault = TRUE;
1582 goto Quit;
1583 }
1584
1585 if ( (GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) ||
1586 (Console->QuickEdit) )
1587 {
1588 switch (msg)
1589 {
1590 case WM_LBUTTONDOWN:
1591 {
1592 /* Check for selection state */
1593 if ( (GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
1594 (GuiData->Selection.dwFlags & CONSOLE_MOUSE_SELECTION) &&
1595 (GetKeyState(VK_SHIFT) & KEY_PRESSED) )
1596 {
1597 /*
1598 * A mouse selection is currently in progress and the user
1599 * has pressed the SHIFT key and clicked somewhere, update
1600 * the selection.
1601 */
1602 GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1603 UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1604 }
1605 else
1606 {
1607 /* Clear the old selection */
1608 GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
1609
1610 /* Restart a new selection */
1611 GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1612 SetCapture(GuiData->hWindow);
1613 GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1614 UpdateSelection(GuiData,
1615 &GuiData->dwSelectionCursor,
1616 &GuiData->dwSelectionCursor);
1617 }
1618
1619 break;
1620 }
1621
1622 case WM_LBUTTONUP:
1623 {
1624 if (!(GuiData->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1625
1626 // GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1627 GuiData->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN;
1628 // UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1629 ReleaseCapture();
1630
1631 break;
1632 }
1633
1634 case WM_LBUTTONDBLCLK:
1635 {
1636 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1637
1638 if (GetType(Buffer) == TEXTMODE_BUFFER)
1639 {
1640 #define IS_WORD_SEP(c) \
1641 ((c) == L'\0' || (c) == L' ' || (c) == L'\t' || (c) == L'\r' || (c) == L'\n')
1642
1643 PTEXTMODE_SCREEN_BUFFER TextBuffer = (PTEXTMODE_SCREEN_BUFFER)Buffer;
1644 COORD cL, cR;
1645 PCHAR_INFO ptrL, ptrR;
1646
1647 /* Starting point */
1648 cL = cR = PointToCoord(GuiData, lParam);
1649 ptrL = ptrR = ConioCoordToPointer(TextBuffer, cL.X, cL.Y);
1650
1651 /* Enlarge the selection by checking for whitespace */
1652 while ((0 < cL.X) && !IS_WORD_SEP(ptrL->Char.UnicodeChar)
1653 && !IS_WORD_SEP((ptrL-1)->Char.UnicodeChar))
1654 {
1655 --cL.X;
1656 --ptrL;
1657 }
1658 while ((cR.X < TextBuffer->ScreenBufferSize.X - 1) &&
1659 !IS_WORD_SEP(ptrR->Char.UnicodeChar) &&
1660 !IS_WORD_SEP((ptrR+1)->Char.UnicodeChar))
1661 {
1662 ++cR.X;
1663 ++ptrR;
1664 }
1665
1666 /*
1667 * Update the selection started with the single
1668 * left-click that preceded this double-click.
1669 */
1670 GuiData->Selection.dwFlags |= CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
1671 UpdateSelection(GuiData, &cL, &cR);
1672
1673 /* Ignore the next mouse move signal */
1674 GuiData->IgnoreNextMouseSignal = TRUE;
1675 #undef IS_WORD_SEP
1676 }
1677
1678 break;
1679 }
1680
1681 case WM_RBUTTONDOWN:
1682 case WM_RBUTTONDBLCLK:
1683 {
1684 if (!(GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY))
1685 {
1686 Paste(GuiData);
1687 }
1688 else
1689 {
1690 Copy(GuiData);
1691 }
1692
1693 /* Ignore the next mouse move signal */
1694 GuiData->IgnoreNextMouseSignal = TRUE;
1695 break;
1696 }
1697
1698 case WM_MOUSEMOVE:
1699 {
1700 if (!(GET_KEYSTATE_WPARAM(wParam) & MK_LBUTTON)) break;
1701 if (!(GuiData->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
1702
1703 GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1704 UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1705 break;
1706 }
1707
1708 default:
1709 DoDefault = TRUE; // FALSE;
1710 break;
1711 }
1712 }
1713 else if (Console->InputBuffer.Mode & ENABLE_MOUSE_INPUT)
1714 {
1715 INPUT_RECORD er;
1716 WORD wKeyState = GET_KEYSTATE_WPARAM(wParam);
1717 DWORD dwButtonState = 0;
1718 DWORD dwControlKeyState = 0;
1719 DWORD dwEventFlags = 0;
1720
1721 switch (msg)
1722 {
1723 case WM_LBUTTONDOWN:
1724 SetCapture(GuiData->hWindow);
1725 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1726 dwEventFlags = 0;
1727 break;
1728
1729 case WM_MBUTTONDOWN:
1730 SetCapture(GuiData->hWindow);
1731 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1732 dwEventFlags = 0;
1733 break;
1734
1735 case WM_RBUTTONDOWN:
1736 SetCapture(GuiData->hWindow);
1737 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1738 dwEventFlags = 0;
1739 break;
1740
1741 case WM_XBUTTONDOWN:
1742 {
1743 /* Get which X-button was pressed */
1744 WORD wButton = GET_XBUTTON_WPARAM(wParam);
1745
1746 /* Check for X-button validity */
1747 if (wButton & ~(XBUTTON1 | XBUTTON2))
1748 {
1749 DPRINT1("X-button 0x%04x invalid\n", wButton);
1750 DoDefault = TRUE;
1751 break;
1752 }
1753
1754 SetCapture(GuiData->hWindow);
1755 dwButtonState = (wButton == XBUTTON1 ? FROM_LEFT_3RD_BUTTON_PRESSED
1756 : FROM_LEFT_4TH_BUTTON_PRESSED);
1757 dwEventFlags = 0;
1758 break;
1759 }
1760
1761 case WM_LBUTTONUP:
1762 ReleaseCapture();
1763 dwButtonState = 0;
1764 dwEventFlags = 0;
1765 break;
1766
1767 case WM_MBUTTONUP:
1768 ReleaseCapture();
1769 dwButtonState = 0;
1770 dwEventFlags = 0;
1771 break;
1772
1773 case WM_RBUTTONUP:
1774 ReleaseCapture();
1775 dwButtonState = 0;
1776 dwEventFlags = 0;
1777 break;
1778
1779 case WM_XBUTTONUP:
1780 {
1781 /* Get which X-button was released */
1782 WORD wButton = GET_XBUTTON_WPARAM(wParam);
1783
1784 /* Check for X-button validity */
1785 if (wButton & ~(XBUTTON1 | XBUTTON2))
1786 {
1787 DPRINT1("X-button 0x%04x invalid\n", wButton);
1788 /* Ok, just release the button anyway... */
1789 }
1790
1791 ReleaseCapture();
1792 dwButtonState = 0;
1793 dwEventFlags = 0;
1794 break;
1795 }
1796
1797 case WM_LBUTTONDBLCLK:
1798 dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1799 dwEventFlags = DOUBLE_CLICK;
1800 break;
1801
1802 case WM_MBUTTONDBLCLK:
1803 dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1804 dwEventFlags = DOUBLE_CLICK;
1805 break;
1806
1807 case WM_RBUTTONDBLCLK:
1808 dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1809 dwEventFlags = DOUBLE_CLICK;
1810 break;
1811
1812 case WM_XBUTTONDBLCLK:
1813 {
1814 /* Get which X-button was double-clicked */
1815 WORD wButton = GET_XBUTTON_WPARAM(wParam);
1816
1817 /* Check for X-button validity */
1818 if (wButton & ~(XBUTTON1 | XBUTTON2))
1819 {
1820 DPRINT1("X-button 0x%04x invalid\n", wButton);
1821 DoDefault = TRUE;
1822 break;
1823 }
1824
1825 dwButtonState = (wButton == XBUTTON1 ? FROM_LEFT_3RD_BUTTON_PRESSED
1826 : FROM_LEFT_4TH_BUTTON_PRESSED);
1827 dwEventFlags = DOUBLE_CLICK;
1828 break;
1829 }
1830
1831 case WM_MOUSEMOVE:
1832 dwButtonState = 0;
1833 dwEventFlags = MOUSE_MOVED;
1834 break;
1835
1836 case WM_MOUSEWHEEL:
1837 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1838 dwEventFlags = MOUSE_WHEELED;
1839 break;
1840
1841 case WM_MOUSEHWHEEL:
1842 dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
1843 dwEventFlags = MOUSE_HWHEELED;
1844 break;
1845
1846 default:
1847 DoDefault = TRUE;
1848 break;
1849 }
1850
1851 /*
1852 * HACK FOR CORE-8394 (Part 1):
1853 *
1854 * It appears that depending on which VM ReactOS runs, the next mouse
1855 * signal coming after a button-down action can be a mouse-move (e.g.
1856 * on VBox, whereas on QEMU it is not the case). However it is NOT a
1857 * rule, so that we cannot use the IgnoreNextMouseSignal flag to just
1858 * "ignore" the next mouse event, thinking it would always be a mouse-
1859 * move signal.
1860 *
1861 * To work around this problem (that should really be fixed in Win32k),
1862 * we use a second flag to ignore this possible next mouse move signal.
1863 */
1864 switch (msg)
1865 {
1866 case WM_LBUTTONDOWN:
1867 case WM_MBUTTONDOWN:
1868 case WM_RBUTTONDOWN:
1869 case WM_XBUTTONDOWN:
1870 GuiData->HackCORE8394IgnoreNextMove = TRUE;
1871 default:
1872 break;
1873 }
1874
1875 if (!DoDefault)
1876 {
1877 if (wKeyState & MK_LBUTTON)
1878 dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED;
1879 if (wKeyState & MK_MBUTTON)
1880 dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED;
1881 if (wKeyState & MK_RBUTTON)
1882 dwButtonState |= RIGHTMOST_BUTTON_PRESSED;
1883 if (wKeyState & MK_XBUTTON1)
1884 dwButtonState |= FROM_LEFT_3RD_BUTTON_PRESSED;
1885 if (wKeyState & MK_XBUTTON2)
1886 dwButtonState |= FROM_LEFT_4TH_BUTTON_PRESSED;
1887
1888 if (GetKeyState(VK_RMENU) & KEY_PRESSED)
1889 dwControlKeyState |= RIGHT_ALT_PRESSED;
1890 if (GetKeyState(VK_LMENU) & KEY_PRESSED)
1891 dwControlKeyState |= LEFT_ALT_PRESSED;
1892 if (GetKeyState(VK_RCONTROL) & KEY_PRESSED)
1893 dwControlKeyState |= RIGHT_CTRL_PRESSED;
1894 if (GetKeyState(VK_LCONTROL) & KEY_PRESSED)
1895 dwControlKeyState |= LEFT_CTRL_PRESSED;
1896 if (GetKeyState(VK_SHIFT) & KEY_PRESSED)
1897 dwControlKeyState |= SHIFT_PRESSED;
1898 if (GetKeyState(VK_NUMLOCK) & KEY_TOGGLED)
1899 dwControlKeyState |= NUMLOCK_ON;
1900 if (GetKeyState(VK_SCROLL) & KEY_TOGGLED)
1901 dwControlKeyState |= SCROLLLOCK_ON;
1902 if (GetKeyState(VK_CAPITAL) & KEY_TOGGLED)
1903 dwControlKeyState |= CAPSLOCK_ON;
1904 /* See WM_CHAR MSDN documentation for instance */
1905 if (lParam & 0x01000000)
1906 dwControlKeyState |= ENHANCED_KEY;
1907
1908 /* Send a mouse event */
1909 er.EventType = MOUSE_EVENT;
1910 er.Event.MouseEvent.dwMousePosition = PointToCoord(GuiData, lParam);
1911 er.Event.MouseEvent.dwButtonState = dwButtonState;
1912 er.Event.MouseEvent.dwControlKeyState = dwControlKeyState;
1913 er.Event.MouseEvent.dwEventFlags = dwEventFlags;
1914
1915 ConioProcessInputEvent(Console, &er);
1916 }
1917 }
1918 else
1919 {
1920 DoDefault = TRUE;
1921 }
1922
1923 LeaveCriticalSection(&Console->Lock);
1924
1925 Quit:
1926 if (!DoDefault)
1927 return 0;
1928
1929 if (msg == WM_MOUSEWHEEL || msg == WM_MOUSEHWHEEL)
1930 {
1931 INT nBar;
1932 WORD sbCode;
1933 // WORD wKeyState = GET_KEYSTATE_WPARAM(wParam);
1934 SHORT wDelta = GET_WHEEL_DELTA_WPARAM(wParam);
1935
1936 if (msg == WM_MOUSEWHEEL)
1937 nBar = SB_VERT;
1938 else // if (msg == WM_MOUSEHWHEEL)
1939 nBar = SB_HORZ;
1940
1941 // NOTE: We currently do not support zooming...
1942 // if (wKeyState & MK_CONTROL)
1943
1944 // FIXME: For some reason our win32k does not set the key states
1945 // when sending WM_MOUSEWHEEL or WM_MOUSEHWHEEL ...
1946 // if (wKeyState & MK_SHIFT)
1947 if (GetKeyState(VK_SHIFT) & KEY_PRESSED)
1948 sbCode = (wDelta >= 0 ? SB_PAGEUP : SB_PAGEDOWN);
1949 else
1950 sbCode = (wDelta >= 0 ? SB_LINEUP : SB_LINEDOWN);
1951
1952 OnScroll(GuiData, nBar, sbCode);
1953 }
1954
1955 return DefWindowProcW(GuiData->hWindow, msg, wParam, lParam);
1956 }
1957
1958
1959 static VOID
1960 Copy(PGUI_CONSOLE_DATA GuiData)
1961 {
1962 if (OpenClipboard(GuiData->hWindow))
1963 {
1964 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1965
1966 if (GetType(Buffer) == TEXTMODE_BUFFER)
1967 {
1968 GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer, GuiData);
1969 }
1970 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1971 {
1972 GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer, GuiData);
1973 }
1974
1975 CloseClipboard();
1976 }
1977
1978 /* Clear the selection */
1979 UpdateSelection(GuiData, NULL, NULL);
1980 }
1981
1982 static VOID
1983 Paste(PGUI_CONSOLE_DATA GuiData)
1984 {
1985 if (OpenClipboard(GuiData->hWindow))
1986 {
1987 PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
1988
1989 if (GetType(Buffer) == TEXTMODE_BUFFER)
1990 {
1991 GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer, GuiData);
1992 }
1993 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1994 {
1995 GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer, GuiData);
1996 }
1997
1998 CloseClipboard();
1999 }
2000 }
2001
2002 static VOID
2003 OnGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData, PMINMAXINFO minMaxInfo)
2004 {
2005 PCONSRV_CONSOLE Console = GuiData->Console;
2006 PCONSOLE_SCREEN_BUFFER ActiveBuffer;
2007 DWORD windx, windy;
2008 UINT WidthUnit, HeightUnit;
2009
2010 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
2011
2012 ActiveBuffer = GuiData->ActiveBuffer;
2013
2014 GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
2015
2016 windx = CONGUI_MIN_WIDTH * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
2017 windy = CONGUI_MIN_HEIGHT * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
2018
2019 minMaxInfo->ptMinTrackSize.x = windx;
2020 minMaxInfo->ptMinTrackSize.y = windy;
2021
2022 windx = (ActiveBuffer->ScreenBufferSize.X) * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
2023 windy = (ActiveBuffer->ScreenBufferSize.Y) * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
2024
2025 if (ActiveBuffer->ViewSize.X < ActiveBuffer->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // Window currently has a horizontal scrollbar
2026 if (ActiveBuffer->ViewSize.Y < ActiveBuffer->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // Window currently has a vertical scrollbar
2027
2028 minMaxInfo->ptMaxTrackSize.x = windx;
2029 minMaxInfo->ptMaxTrackSize.y = windy;
2030
2031 LeaveCriticalSection(&Console->Lock);
2032 }
2033
2034 static VOID
2035 OnSize(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
2036 {
2037 PCONSRV_CONSOLE Console = GuiData->Console;
2038
2039 /* Do nothing if the window is hidden */
2040 if (!GuiData->IsWindowVisible || IsIconic(GuiData->hWindow)) return;
2041
2042 if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
2043
2044 if (!GuiData->WindowSizeLock &&
2045 (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED))
2046 {
2047 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
2048 DWORD windx, windy, charx, chary;
2049 UINT WidthUnit, HeightUnit;
2050
2051 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
2052
2053 GuiData->WindowSizeLock = TRUE;
2054
2055 windx = LOWORD(lParam);
2056 windy = HIWORD(lParam);
2057
2058 /* Compensate for existing scroll bars (because lParam values do not accommodate scroll bar) */
2059 if (Buff->ViewSize.X < Buff->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // Window currently has a horizontal scrollbar
2060 if (Buff->ViewSize.Y < Buff->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // Window currently has a vertical scrollbar
2061
2062 charx = windx / (int)WidthUnit ;
2063 chary = windy / (int)HeightUnit;
2064
2065 /* Character alignment (round size up or down) */
2066 if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx;
2067 if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
2068
2069 /* Compensate for added scroll bars in window */
2070 if (Buff->ViewSize.X < Buff->ScreenBufferSize.X) windy -= GetSystemMetrics(SM_CYHSCROLL); // Window will have a horizontal scroll bar
2071 if (Buff->ViewSize.Y < Buff->ScreenBufferSize.Y) windx -= GetSystemMetrics(SM_CXVSCROLL); // Window will have a vertical scroll bar
2072
2073 charx = windx / (int)WidthUnit ;
2074 chary = windy / (int)HeightUnit;
2075
2076 /* Character alignment (round size up or down) */
2077 if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx;
2078 if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
2079
2080 /* Resize window */
2081 if ((charx != Buff->ViewSize.X) || (chary != Buff->ViewSize.Y))
2082 {
2083 Buff->ViewSize.X = (charx <= (DWORD)Buff->ScreenBufferSize.X) ? charx : Buff->ScreenBufferSize.X;
2084 Buff->ViewSize.Y = (chary <= (DWORD)Buff->ScreenBufferSize.Y) ? chary : Buff->ScreenBufferSize.Y;
2085 }
2086
2087 ResizeConWnd(GuiData, WidthUnit, HeightUnit);
2088
2089 /* Adjust the start of the visible area if we are attempting to show nonexistent areas */
2090 if ((Buff->ScreenBufferSize.X - Buff->ViewOrigin.X) < Buff->ViewSize.X) Buff->ViewOrigin.X = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
2091 if ((Buff->ScreenBufferSize.Y - Buff->ViewOrigin.Y) < Buff->ViewSize.Y) Buff->ViewOrigin.Y = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
2092 InvalidateRect(GuiData->hWindow, NULL, TRUE);
2093
2094 GuiData->WindowSizeLock = FALSE;
2095 }
2096
2097 LeaveCriticalSection(&Console->Lock);
2098 }
2099
2100 static VOID
2101 OnMove(PGUI_CONSOLE_DATA GuiData)
2102 {
2103 RECT rcWnd;
2104
2105 // TODO: Simplify the code.
2106 // See: GuiConsoleNotifyWndProc() PM_CREATE_CONSOLE.
2107
2108 /* Retrieve our real position */
2109 GetWindowRect(GuiData->hWindow, &rcWnd);
2110 GuiData->GuiInfo.WindowOrigin.x = rcWnd.left;
2111 GuiData->GuiInfo.WindowOrigin.y = rcWnd.top;
2112 }
2113
2114 static VOID
2115 OnDropFiles(PCONSRV_CONSOLE Console, HDROP hDrop)
2116 {
2117 LPWSTR pszPath;
2118 WCHAR szPath[MAX_PATH + 2];
2119
2120 szPath[0] = L'"';
2121
2122 DragQueryFileW(hDrop, 0, &szPath[1], ARRAYSIZE(szPath) - 1);
2123 DragFinish(hDrop);
2124
2125 if (wcschr(&szPath[1], L' ') != NULL)
2126 {
2127 StringCchCatW(szPath, ARRAYSIZE(szPath), L"\"");
2128 pszPath = szPath;
2129 }
2130 else
2131 {
2132 pszPath = &szPath[1];
2133 }
2134
2135 PasteText(Console, pszPath, wcslen(pszPath));
2136 }
2137
2138 /*
2139 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
2140
2141 VOID
2142 GuiConsoleHandleScrollbarMenu(VOID)
2143 {
2144 HMENU hMenu;
2145
2146 hMenu = CreatePopupMenu();
2147 if (hMenu == NULL)
2148 {
2149 DPRINT("CreatePopupMenu failed\n");
2150 return;
2151 }
2152
2153 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
2154 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2155 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
2156 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
2157 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2158 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
2159 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
2160 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2161 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
2162 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
2163 }
2164 */
2165
2166 static LRESULT CALLBACK
2167 ConWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
2168 {
2169 LRESULT Result = 0;
2170 PGUI_CONSOLE_DATA GuiData = NULL;
2171 PCONSRV_CONSOLE Console = NULL;
2172
2173 /*
2174 * - If it's the first time we create a window for the terminal,
2175 * just initialize it and return.
2176 *
2177 * - If we are destroying the window, just do it and return.
2178 */
2179 if (msg == WM_NCCREATE)
2180 {
2181 return (LRESULT)OnNcCreate(hWnd, (LPCREATESTRUCTW)lParam);
2182 }
2183 else if (msg == WM_NCDESTROY)
2184 {
2185 return OnNcDestroy(hWnd);
2186 }
2187
2188 /*
2189 * Now the terminal window is initialized.
2190 * Get the terminal data via the window's data.
2191 * If there is no data, just go away.
2192 */
2193 GuiData = GuiGetGuiData(hWnd);
2194 if (GuiData == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
2195
2196 // TEMPORARY HACK until all of the functions can deal with a NULL GuiData->ActiveBuffer ...
2197 if (GuiData->ActiveBuffer == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
2198
2199 /*
2200 * Just retrieve a pointer to the console in case somebody needs it.
2201 * It is not NULL because it was checked in GuiGetGuiData.
2202 * Each helper function which needs the console has to validate and lock it.
2203 */
2204 Console = GuiData->Console;
2205
2206 /* We have a console, start message dispatching */
2207 switch (msg)
2208 {
2209 case WM_ACTIVATE:
2210 OnActivate(GuiData, wParam);
2211 break;
2212
2213 case WM_CLOSE:
2214 if (OnClose(GuiData)) goto Default;
2215 break;
2216
2217 case WM_ERASEBKGND:
2218 return TRUE;
2219
2220 case WM_PAINT:
2221 OnPaint(GuiData);
2222 break;
2223
2224 case WM_TIMER:
2225 OnTimer(GuiData);
2226 break;
2227
2228 case WM_PALETTECHANGED:
2229 {
2230 DPRINT("WM_PALETTECHANGED called\n");
2231
2232 /*
2233 * Protects against infinite loops:
2234 * "... A window that receives this message must not realize
2235 * its palette, unless it determines that wParam does not contain
2236 * its own window handle." (WM_PALETTECHANGED description - MSDN)
2237 *
2238 * This message is sent to all windows, including the one that
2239 * changed the system palette and caused this message to be sent.
2240 * The wParam of this message contains the handle of the window
2241 * that caused the system palette to change. To avoid an infinite
2242 * loop, care must be taken to check that the wParam of this message
2243 * does not match the window's handle.
2244 */
2245 if ((HWND)wParam == hWnd) break;
2246
2247 DPRINT("WM_PALETTECHANGED ok\n");
2248 OnPaletteChanged(GuiData);
2249 DPRINT("WM_PALETTECHANGED quit\n");
2250 break;
2251 }
2252
2253 case WM_KEYDOWN:
2254 case WM_KEYUP:
2255 case WM_CHAR:
2256 case WM_DEADCHAR:
2257 case WM_SYSKEYDOWN:
2258 case WM_SYSKEYUP:
2259 case WM_SYSCHAR:
2260 case WM_SYSDEADCHAR:
2261 {
2262 /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */
2263 if (msg == WM_SYSKEYDOWN && (HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_RETURN)
2264 {
2265 /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */
2266 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
2267 GuiConsoleSwitchFullScreen(GuiData);
2268
2269 break;
2270 }
2271 /* Detect Alt-Esc/Space/Tab presses defer to DefWindowProc */
2272 if ( (HIWORD(lParam) & KF_ALTDOWN) && (wParam == VK_ESCAPE || wParam == VK_SPACE || wParam == VK_TAB))
2273 {
2274 return DefWindowProcW(hWnd, msg, wParam, lParam);
2275 }
2276
2277 OnKey(GuiData, msg, wParam, lParam);
2278 break;
2279 }
2280
2281 case WM_SETCURSOR:
2282 {
2283 /* Do nothing if the window is hidden */
2284 if (!GuiData->IsWindowVisible) goto Default;
2285
2286 /*
2287 * The message was sent because we are manually triggering a change.
2288 * Check whether the mouse is indeed present on this console window
2289 * and take appropriate decisions.
2290 */
2291 if (wParam == -1 && lParam == -1)
2292 {
2293 POINT mouseCoords;
2294 HWND hWndHit;
2295
2296 /* Get the placement of the mouse */
2297 GetCursorPos(&mouseCoords);
2298
2299 /* On which window is placed the mouse ? */
2300 hWndHit = WindowFromPoint(mouseCoords);
2301
2302 /* It's our window. Perform the hit-test to be used later on. */
2303 if (hWndHit == hWnd)
2304 {
2305 wParam = (WPARAM)hWnd;
2306 lParam = DefWindowProcW(hWndHit, WM_NCHITTEST, 0,
2307 MAKELPARAM(mouseCoords.x, mouseCoords.y));
2308 }
2309 }
2310
2311 /* Set the mouse cursor only when we are in the client area */
2312 if ((HWND)wParam == hWnd && LOWORD(lParam) == HTCLIENT)
2313 {
2314 if (GuiData->MouseCursorRefCount >= 0)
2315 {
2316 /* Show the cursor */
2317 SetCursor(GuiData->hCursor);
2318 }
2319 else
2320 {
2321 /* Hide the cursor if the reference count is negative */
2322 SetCursor(NULL);
2323 }
2324 return TRUE;
2325 }
2326 else
2327 {
2328 goto Default;
2329 }
2330 }
2331
2332 case WM_LBUTTONDOWN:
2333 case WM_MBUTTONDOWN:
2334 case WM_RBUTTONDOWN:
2335 case WM_XBUTTONDOWN:
2336 case WM_LBUTTONUP:
2337 case WM_MBUTTONUP:
2338 case WM_RBUTTONUP:
2339 case WM_XBUTTONUP:
2340 case WM_LBUTTONDBLCLK:
2341 case WM_MBUTTONDBLCLK:
2342 case WM_RBUTTONDBLCLK:
2343 case WM_XBUTTONDBLCLK:
2344 case WM_MOUSEMOVE:
2345 case WM_MOUSEWHEEL:
2346 case WM_MOUSEHWHEEL:
2347 {
2348 Result = OnMouse(GuiData, msg, wParam, lParam);
2349 break;
2350 }
2351
2352 case WM_HSCROLL:
2353 OnScroll(GuiData, SB_HORZ, LOWORD(wParam));
2354 break;
2355
2356 case WM_VSCROLL:
2357 OnScroll(GuiData, SB_VERT, LOWORD(wParam));
2358 break;
2359
2360 case WM_CONTEXTMENU:
2361 {
2362 /* Do nothing if the window is hidden */
2363 if (!GuiData->IsWindowVisible) break;
2364
2365 if (DefWindowProcW(hWnd /*GuiData->hWindow*/, WM_NCHITTEST, 0, lParam) == HTCLIENT)
2366 {
2367 HMENU hMenu = CreatePopupMenu();
2368 if (hMenu != NULL)
2369 {
2370 AppendMenuItems(hMenu, GuiConsoleEditMenuItems);
2371 TrackPopupMenuEx(hMenu,
2372 TPM_RIGHTBUTTON,
2373 GET_X_LPARAM(lParam),
2374 GET_Y_LPARAM(lParam),
2375 hWnd,
2376 NULL);
2377 DestroyMenu(hMenu);
2378 }
2379 break;
2380 }
2381 else
2382 {
2383 goto Default;
2384 }
2385 }
2386
2387 case WM_INITMENU:
2388 {
2389 HMENU hMenu = (HMENU)wParam;
2390 if (hMenu != NULL)
2391 {
2392 /* Enable or disable the Close menu item */
2393 EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND |
2394 (GuiData->IsCloseButtonEnabled ? MF_ENABLED : MF_GRAYED));
2395
2396 /* Enable or disable the Copy and Paste items */
2397 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_COPY , MF_BYCOMMAND |
2398 ((GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
2399 (GuiData->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) ? MF_ENABLED : MF_GRAYED));
2400 // FIXME: Following whether the active screen buffer is text-mode
2401 // or graphics-mode, search for CF_UNICODETEXT or CF_BITMAP formats.
2402 EnableMenuItem(hMenu, ID_SYSTEM_EDIT_PASTE, MF_BYCOMMAND |
2403 (!(GuiData->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
2404 IsClipboardFormatAvailable(CF_UNICODETEXT) ? MF_ENABLED : MF_GRAYED));
2405 }
2406
2407 SendMenuEvent(Console, WM_INITMENU);
2408 break;
2409 }
2410
2411 case WM_MENUSELECT:
2412 {
2413 if (HIWORD(wParam) == 0xFFFF) // Allow all the menu flags
2414 {
2415 SendMenuEvent(Console, WM_MENUSELECT);
2416 }
2417 break;
2418 }
2419
2420 case WM_COMMAND:
2421 case WM_SYSCOMMAND:
2422 {
2423 Result = OnCommand(GuiData, wParam, lParam);
2424 break;
2425 }
2426
2427 case WM_DROPFILES:
2428 OnDropFiles(Console, (HDROP)wParam);
2429 break;
2430
2431 case WM_SETFOCUS:
2432 case WM_KILLFOCUS:
2433 OnFocus(GuiData, (msg == WM_SETFOCUS));
2434 break;
2435
2436 case WM_GETMINMAXINFO:
2437 OnGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam);
2438 break;
2439
2440 case WM_MOVE:
2441 OnMove(GuiData);
2442 break;
2443
2444 #if 0 // This code is here to prepare & control dynamic console SB resizing.
2445 case WM_SIZING:
2446 {
2447 PRECT dragRect = (PRECT)lParam;
2448 switch (wParam)
2449 {
2450 case WMSZ_LEFT:
2451 DPRINT1("WMSZ_LEFT\n");
2452 break;
2453 case WMSZ_RIGHT:
2454 DPRINT1("WMSZ_RIGHT\n");
2455 break;
2456 case WMSZ_TOP:
2457 DPRINT1("WMSZ_TOP\n");
2458 break;
2459 case WMSZ_TOPLEFT:
2460 DPRINT1("WMSZ_TOPLEFT\n");
2461 break;
2462 case WMSZ_TOPRIGHT:
2463 DPRINT1("WMSZ_TOPRIGHT\n");
2464 break;
2465 case WMSZ_BOTTOM:
2466 DPRINT1("WMSZ_BOTTOM\n");
2467 break;
2468 case WMSZ_BOTTOMLEFT:
2469 DPRINT1("WMSZ_BOTTOMLEFT\n");
2470 break;
2471 case WMSZ_BOTTOMRIGHT:
2472 DPRINT1("WMSZ_BOTTOMRIGHT\n");
2473 break;
2474 default:
2475 DPRINT1("wParam = %d\n", wParam);
2476 break;
2477 }
2478 DPRINT1("dragRect = {.left = %d ; .top = %d ; .right = %d ; .bottom = %d}\n",
2479 dragRect->left, dragRect->top, dragRect->right, dragRect->bottom);
2480 break;
2481 }
2482 #endif
2483
2484 case WM_SIZE:
2485 OnSize(GuiData, wParam, lParam);
2486 break;
2487
2488 case PM_RESIZE_TERMINAL:
2489 {
2490 PCONSOLE_SCREEN_BUFFER Buff = GuiData->ActiveBuffer;
2491 HDC hDC;
2492 HBITMAP hnew, hold;
2493
2494 DWORD Width, Height;
2495 UINT WidthUnit, HeightUnit;
2496
2497 /* Do nothing if the window is hidden */
2498 if (!GuiData->IsWindowVisible) break;
2499
2500 GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
2501
2502 Width = Buff->ScreenBufferSize.X * WidthUnit ;
2503 Height = Buff->ScreenBufferSize.Y * HeightUnit;
2504
2505 /* Recreate the framebuffer */
2506 hDC = GetDC(GuiData->hWindow);
2507 hnew = CreateCompatibleBitmap(hDC, Width, Height);
2508 ReleaseDC(GuiData->hWindow, hDC);
2509 hold = SelectObject(GuiData->hMemDC, hnew);
2510 if (GuiData->hBitmap)
2511 {
2512 if (hold == GuiData->hBitmap) DeleteObject(GuiData->hBitmap);
2513 }
2514 GuiData->hBitmap = hnew;
2515
2516 /* Resize the window to the user's values */
2517 GuiData->WindowSizeLock = TRUE;
2518 ResizeConWnd(GuiData, WidthUnit, HeightUnit);
2519 GuiData->WindowSizeLock = FALSE;
2520 break;
2521 }
2522
2523 /*
2524 * Undocumented message sent by Windows' console.dll for applying console info.
2525 * See http://www.catch22.net/sites/default/source/files/setconsoleinfo.c
2526 * and http://www.scn.rain.com/~neighorn/PDF/MSBugPaper.pdf
2527 * for more information.
2528 */
2529 case WM_SETCONSOLEINFO:
2530 {
2531 GuiApplyUserSettings(GuiData, (HANDLE)wParam);
2532 break;
2533 }
2534
2535 case PM_CONSOLE_BEEP:
2536 DPRINT1("Beep\n");
2537 Beep(800, 200);
2538 break;
2539
2540 case PM_CONSOLE_SET_TITLE:
2541 SetWindowTextW(GuiData->hWindow, GuiData->Console->Title.Buffer);
2542 break;
2543
2544 default: Default:
2545 Result = DefWindowProcW(hWnd, msg, wParam, lParam);
2546 break;
2547 }
2548
2549 return Result;
2550 }
2551
2552 /* EOF */