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