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