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