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