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