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
9 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
12 /* INCLUDES *******************************************************************/
25 /* GLOBALS ********************************************************************/
27 // #define PM_CREATE_CONSOLE (WM_APP + 1)
28 // #define PM_DESTROY_CONSOLE (WM_APP + 2)
31 #define CONGUI_MIN_WIDTH 10
32 #define CONGUI_MIN_HEIGHT 10
33 #define CONGUI_UPDATE_TIME 0
34 #define CONGUI_UPDATE_TIMER 1
36 #define CURSOR_BLINK_TIME 500
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
46 SetConWndConsoleLeaderCID(IN PGUI_CONSOLE_DATA GuiData
)
48 PCONSOLE_PROCESS_DATA ProcessData
;
49 CLIENT_ID ConsoleLeaderCID
;
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
));
58 /**************************************************************/
60 HICON ghDefaultIcon
= NULL
;
61 HICON ghDefaultIconSm
= NULL
;
62 HCURSOR ghDefaultCursor
= NULL
;
64 typedef struct _GUICONSOLE_MENUITEM
67 const struct _GUICONSOLE_MENUITEM
*SubMenu
;
69 } GUICONSOLE_MENUITEM
, *PGUICONSOLE_MENUITEM
;
71 static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems
[] =
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
},
80 { 0, NULL
, 0 } /* End of list */
83 static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems
[] =
85 { IDS_EDIT
, GuiConsoleEditMenuItems
, 0 },
86 { IDS_DEFAULTS
, NULL
, ID_SYSTEM_DEFAULTS
},
87 { IDS_PROPERTIES
, NULL
, ID_SYSTEM_PROPERTIES
},
89 { 0, NULL
, 0 } /* End of list */
93 * Default 16-color palette for foreground and background
94 * (corresponding flags in comments).
96 const COLORREF s_Colors
[16] =
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
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
117 /* FUNCTIONS ******************************************************************/
119 static LRESULT CALLBACK
120 ConWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
123 RegisterConWndClass(IN HINSTANCE hInstance
)
125 WNDCLASSEXW WndClass
;
128 ghDefaultIcon
= LoadImageW(hInstance
,
129 MAKEINTRESOURCEW(IDI_TERMINAL
),
131 GetSystemMetrics(SM_CXICON
),
132 GetSystemMetrics(SM_CYICON
),
134 ghDefaultIconSm
= LoadImageW(hInstance
,
135 MAKEINTRESOURCEW(IDI_TERMINAL
),
137 GetSystemMetrics(SM_CXSMICON
),
138 GetSystemMetrics(SM_CYSMICON
),
140 ghDefaultCursor
= LoadCursorW(NULL
, MAKEINTRESOURCEW(IDC_ARROW
));
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
;
155 WndClassAtom
= RegisterClassExW(&WndClass
);
156 if (WndClassAtom
== 0)
158 DPRINT1("Failed to register GUI console class\n");
162 NtUserConsoleControl(GuiConsoleWndClassAtom
, &WndClassAtom
, sizeof(ATOM
));
165 return (WndClassAtom
!= 0);
169 UnRegisterConWndClass(HINSTANCE hInstance
)
171 return !!UnregisterClassW(GUI_CONWND_CLASS
, hInstance
);
175 AppendMenuItems(HMENU hMenu
,
176 const GUICONSOLE_MENUITEM
*Items
)
179 WCHAR szMenuString
[255];
184 if (Items
[i
].uID
!= (UINT
)-1)
186 if (LoadStringW(ConSrvDllInstance
,
189 ARRAYSIZE(szMenuString
)) > 0)
191 if (Items
[i
].SubMenu
!= NULL
)
193 hSubMenu
= CreatePopupMenu();
194 if (hSubMenu
!= NULL
)
196 AppendMenuItems(hSubMenu
, Items
[i
].SubMenu
);
198 if (!AppendMenuW(hMenu
,
199 MF_STRING
| MF_POPUP
,
203 DestroyMenu(hSubMenu
);
224 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
229 CreateSysMenu(HWND hWnd
)
232 WCHAR szMenuStringBack
[255];
234 HMENU hMenu
= GetSystemMenu(hWnd
, FALSE
);
237 mii
.cbSize
= sizeof(mii
);
238 mii
.fMask
= MIIM_STRING
;
239 mii
.dwTypeData
= szMenuStringBack
;
240 mii
.cch
= sizeof(szMenuStringBack
)/sizeof(WCHAR
);
242 GetMenuItemInfoW(hMenu
, SC_CLOSE
, FALSE
, &mii
);
244 ptrTab
= wcschr(szMenuStringBack
, '\t');
248 mii
.cch
= wcslen(szMenuStringBack
);
250 SetMenuItemInfoW(hMenu
, SC_CLOSE
, FALSE
, &mii
);
253 AppendMenuItems(hMenu
, GuiConsoleMainMenuItems
);
259 SendMenuEvent(PCONSRV_CONSOLE Console
, UINT CmdId
)
263 DPRINT("Menu item ID: %d\n", CmdId
);
265 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
267 /* Send a menu event */
268 er
.EventType
= MENU_EVENT
;
269 er
.Event
.MenuEvent
.dwCommandId
= CmdId
;
270 ConioProcessInputEvent(Console
, &er
);
272 LeaveCriticalSection(&Console
->Lock
);
276 Copy(PGUI_CONSOLE_DATA GuiData
);
278 Paste(PGUI_CONSOLE_DATA GuiData
);
280 UpdateSelection(PGUI_CONSOLE_DATA GuiData
,
281 PCOORD SelectionAnchor OPTIONAL
,
285 Mark(PGUI_CONSOLE_DATA GuiData
)
287 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
289 /* Clear the old selection */
290 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
292 /* Restart a new selection */
293 GuiData
->dwSelectionCursor
= ActiveBuffer
->ViewOrigin
;
294 UpdateSelection(GuiData
,
295 &GuiData
->dwSelectionCursor
,
296 &GuiData
->dwSelectionCursor
);
300 SelectAll(PGUI_CONSOLE_DATA GuiData
)
302 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
303 COORD SelectionAnchor
;
305 /* Clear the old selection */
306 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
309 * The selection area extends to the whole screen buffer's width.
311 SelectionAnchor
.X
= SelectionAnchor
.Y
= 0;
312 GuiData
->dwSelectionCursor
.X
= ActiveBuffer
->ScreenBufferSize
.X
- 1;
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).
319 if (GetType(ActiveBuffer
) == TEXTMODE_BUFFER
)
322 * We select all the characters from the first line
323 * to the line where the cursor is positioned.
325 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->CursorPosition
.Y
;
327 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
330 * We select all the screen buffer area.
332 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->ScreenBufferSize
.Y
- 1;
335 /* Restart a new selection */
336 GuiData
->Selection
.dwFlags
|= CONSOLE_MOUSE_SELECTION
;
337 UpdateSelection(GuiData
, &SelectionAnchor
, &GuiData
->dwSelectionCursor
);
341 OnCommand(PGUI_CONSOLE_DATA GuiData
, WPARAM wParam
, LPARAM lParam
)
344 PCONSRV_CONSOLE Console
= GuiData
->Console
;
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...
351 if (GuiData
->CmdIdLow
<= (UINT
)wParam
&& (UINT
)wParam
<= GuiData
->CmdIdHigh
)
353 SendMenuEvent(Console
, (UINT
)wParam
);
357 /* ... otherwise, perform actions. */
360 case ID_SYSTEM_EDIT_MARK
:
364 case ID_SYSTEM_EDIT_COPY
:
368 case ID_SYSTEM_EDIT_PASTE
:
372 case ID_SYSTEM_EDIT_SELECTALL
:
376 case ID_SYSTEM_EDIT_SCROLL
:
377 DPRINT1("Scrolling is not handled yet\n");
380 case ID_SYSTEM_EDIT_FIND
:
381 DPRINT1("Finding is not handled yet\n");
384 case ID_SYSTEM_DEFAULTS
:
385 GuiConsoleShowConsoleProperties(GuiData
, TRUE
);
388 case ID_SYSTEM_PROPERTIES
:
389 GuiConsoleShowConsoleProperties(GuiData
, FALSE
);
399 Ret
= DefWindowProcW(GuiData
->hWindow
, WM_SYSCOMMAND
, wParam
, lParam
);
404 static PGUI_CONSOLE_DATA
405 GuiGetGuiData(HWND hWnd
)
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
);
413 ResizeConWnd(PGUI_CONSOLE_DATA GuiData
, DWORD WidthUnit
, DWORD HeightUnit
)
415 PCONSOLE_SCREEN_BUFFER Buff
= GuiData
->ActiveBuffer
;
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
);
425 /* Set scrollbar sizes */
426 sInfo
.cbSize
= sizeof(sInfo
);
427 sInfo
.fMask
= SIF_RANGE
| SIF_PAGE
| SIF_POS
;
429 if (Buff
->ScreenBufferSize
.Y
> Buff
->ViewSize
.Y
)
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
);
440 ShowScrollBar(GuiData
->hWindow
, SB_VERT
, FALSE
);
443 if (Buff
->ScreenBufferSize
.X
> Buff
->ViewSize
.X
)
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
);
454 ShowScrollBar(GuiData
->hWindow
, SB_HORZ
, FALSE
);
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);
466 DeleteFonts(PGUI_CONSOLE_DATA GuiData
)
469 for (i
= 0; i
< ARRAYSIZE(GuiData
->Font
); ++i
)
471 if (GuiData
->Font
[i
] != NULL
) DeleteObject(GuiData
->Font
[i
]);
472 GuiData
->Font
[i
] = NULL
;
477 CreateDerivedFont(HFONT OrgFont
,
486 /* Initialize the LOGFONT structure */
487 RtlZeroMemory(&lf
, sizeof(lf
));
489 /* Retrieve the details of the current font */
490 if (GetObjectW(OrgFont
, sizeof(lf
), &lf
) == 0)
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
;
501 /* Build a new font */
502 return CreateFontIndirectW(&lf
);
506 InitFonts(PGUI_CONSOLE_DATA GuiData
,
507 LPWSTR FaceName
, // Points to a WCHAR array of LF_FACESIZE elements.
516 * Initialize a new NORMAL font and get its character cell size.
518 /* NOTE: FontSize is always in cell height/width units (pixels) */
519 hFont
= CreateConsoleFontEx((LONG
)(ULONG
)FontSize
.Y
,
520 (LONG
)(ULONG
)FontSize
.X
,
524 GuiData
->Console
->OutputCodePage
);
527 DPRINT1("InitFonts: CreateConsoleFontEx failed\n");
531 hDC
= GetDC(GuiData
->hWindow
);
532 if (!GetFontCellSize(hDC
, hFont
, &GuiData
->CharHeight
, &GuiData
->CharWidth
))
534 DPRINT1("InitFonts: GetFontCellSize failed\n");
535 ReleaseDC(GuiData
->hWindow
, hDC
);
539 ReleaseDC(GuiData
->hWindow
, hDC
);
542 * Initialization succeeded.
544 // Delete all the old fonts first.
545 DeleteFonts(GuiData
);
546 GuiData
->Font
[FONT_NORMAL
] = hFont
;
549 * Now build the other fonts (bold, underlined, mixed).
551 GuiData
->Font
[FONT_BOLD
] =
552 CreateDerivedFont(GuiData
->Font
[FONT_NORMAL
],
553 FontWeight
< FW_BOLD
? FW_BOLD
: FontWeight
,
556 GuiData
->Font
[FONT_UNDERLINE
] =
557 CreateDerivedFont(GuiData
->Font
[FONT_NORMAL
],
561 GuiData
->Font
[FONT_BOLD
| FONT_UNDERLINE
] =
562 CreateDerivedFont(GuiData
->Font
[FONT_NORMAL
],
563 FontWeight
< FW_BOLD
? FW_BOLD
: FontWeight
,
570 if (FaceName
!= GuiData
->GuiInfo
.FaceName
)
572 StringCchCopyNW(GuiData
->GuiInfo
.FaceName
, ARRAYSIZE(GuiData
->GuiInfo
.FaceName
),
573 FaceName
, LF_FACESIZE
);
575 GuiData
->GuiInfo
.FontFamily
= FontFamily
;
576 GuiData
->GuiInfo
.FontSize
= FontSize
;
577 GuiData
->GuiInfo
.FontWeight
= FontWeight
;
584 OnNcCreate(HWND hWnd
, LPCREATESTRUCTW Create
)
586 PGUI_CONSOLE_DATA GuiData
= (PGUI_CONSOLE_DATA
)Create
->lpCreateParams
;
587 PCONSRV_CONSOLE Console
;
591 DPRINT1("GuiConsoleNcCreate: No GUI data\n");
595 Console
= GuiData
->Console
;
597 GuiData
->hWindow
= hWnd
;
599 /* Initialize the fonts */
600 if (!InitFonts(GuiData
,
601 GuiData
->GuiInfo
.FaceName
,
602 GuiData
->GuiInfo
.FontFamily
,
603 GuiData
->GuiInfo
.FontSize
,
604 GuiData
->GuiInfo
.FontWeight
))
606 DPRINT1("GuiConsoleNcCreate: InitFonts failed\n");
607 GuiData
->hWindow
= NULL
;
608 NtSetEvent(GuiData
->hGuiInitEvent
, NULL
);
612 /* Initialize the terminal framebuffer */
613 GuiData
->hMemDC
= CreateCompatibleDC(NULL
);
614 GuiData
->hBitmap
= NULL
;
615 GuiData
->hSysPalette
= NULL
; /* Original system palette */
617 /* Update the icons of the window */
618 if (GuiData
->hIcon
!= ghDefaultIcon
)
620 DefWindowProcW(GuiData
->hWindow
, WM_SETICON
, ICON_BIG
, (LPARAM
)GuiData
->hIcon
);
621 DefWindowProcW(GuiData
->hWindow
, WM_SETICON
, ICON_SMALL
, (LPARAM
)GuiData
->hIconSm
);
624 // FIXME: Keep these instructions here ? ///////////////////////////////////
625 Console
->ActiveBuffer
->CursorBlinkOn
= TRUE
;
626 Console
->ActiveBuffer
->ForceCursorOff
= FALSE
;
627 ////////////////////////////////////////////////////////////////////////////
629 SetWindowLongPtrW(GuiData
->hWindow
, GWLP_USERDATA
, (DWORD_PTR
)GuiData
);
631 if (GuiData
->IsWindowVisible
)
633 SetTimer(GuiData
->hWindow
, CONGUI_UPDATE_TIMER
, CONGUI_UPDATE_TIME
, NULL
);
636 // FIXME: HACK: Potential HACK for CORE-8129; see revision 63595.
637 //CreateSysMenu(GuiData->hWindow);
639 DPRINT("OnNcCreate - setting start event\n");
640 NtSetEvent(GuiData
->hGuiInitEvent
, NULL
);
642 return (BOOL
)DefWindowProcW(GuiData
->hWindow
, WM_NCCREATE
, 0, (LPARAM
)Create
);
647 EnterFullScreen(PGUI_CONSOLE_DATA GuiData
);
649 LeaveFullScreen(PGUI_CONSOLE_DATA GuiData
);
651 SwitchFullScreen(PGUI_CONSOLE_DATA GuiData
, BOOL FullScreen
);
653 GuiConsoleSwitchFullScreen(PGUI_CONSOLE_DATA GuiData
);
656 OnActivate(PGUI_CONSOLE_DATA GuiData
, WPARAM wParam
)
658 WORD ActivationState
= LOWORD(wParam
);
660 DPRINT("WM_ACTIVATE - ActivationState = %d\n", ActivationState
);
662 if ( ActivationState
== WA_ACTIVE
||
663 ActivationState
== WA_CLICKACTIVE
)
665 if (GuiData
->GuiInfo
.FullScreen
)
667 EnterFullScreen(GuiData
);
668 // // PostMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
669 // SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
672 else // if (ActivationState == WA_INACTIVE)
674 if (GuiData
->GuiInfo
.FullScreen
)
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);
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.
688 if (ActivationState
== WA_CLICKACTIVE
)
689 GuiData
->IgnoreNextMouseSignal
= TRUE
;
693 OnFocus(PGUI_CONSOLE_DATA GuiData
, BOOL SetFocus
)
695 PCONSRV_CONSOLE Console
= GuiData
->Console
;
698 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
700 /* Set console focus state */
701 Console
->HasFocus
= SetFocus
;
704 * Set the priority of the processes of this console
705 * in accordance with the console focus state.
707 ConSrvSetConsoleProcessFocus(Console
, SetFocus
);
709 /* Send a focus event */
710 er
.EventType
= FOCUS_EVENT
;
711 er
.Event
.FocusEvent
.bSetFocus
= SetFocus
;
712 ConioProcessInputEvent(Console
, &er
);
714 LeaveCriticalSection(&Console
->Lock
);
717 DPRINT("TODO: Create console caret\n");
719 DPRINT("TODO: Destroy console caret\n");
723 GetSelectionBeginEnd(PCOORD Begin
, PCOORD End
,
724 PCOORD SelectionAnchor
,
725 PSMALL_RECT SmallRect
)
727 if (Begin
== NULL
|| End
== NULL
) return;
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
;
735 /* Exchange Begin / End if Begin > End lexicographically */
736 if (Begin
->Y
> End
->Y
|| (Begin
->Y
== End
->Y
&& Begin
->X
> End
->X
))
738 End
->X
= _InterlockedExchange16(&Begin
->X
, End
->X
);
739 End
->Y
= _InterlockedExchange16(&Begin
->Y
, End
->Y
);
744 CreateSelectionRgn(PGUI_CONSOLE_DATA GuiData
,
746 PCOORD SelectionAnchor
,
747 PSMALL_RECT SmallRect
)
752 SmallRectToRect(GuiData
, &rect
, SmallRect
);
753 return CreateRectRgnIndirect(&rect
);
760 GetSelectionBeginEnd(&Begin
, &End
, SelectionAnchor
, SmallRect
);
762 if (Begin
.Y
== End
.Y
)
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
)
780 "SmallRect = (%d, %d, %d, %d)\n"
781 "sr = (%d, %d, %d, %d)\n"
783 SmallRect
->Left
, SmallRect
->Top
, SmallRect
->Right
, SmallRect
->Bottom
,
784 sr
.Left
, sr
.Top
, sr
.Right
, sr
.Bottom
);
787 SmallRectToRect(GuiData
, &r
, &sr
);
788 SelRgn
= CreateRectRgnIndirect(&r
);
792 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
795 SMALL_RECT sr1
, sr2
, sr3
;
800 sr1
.Right
= ActiveBuffer
->ScreenBufferSize
.X
- 1;
801 sr1
.Bottom
= Begin
.Y
;
804 sr2
.Top
= Begin
.Y
+ 1;
805 sr2
.Right
= ActiveBuffer
->ScreenBufferSize
.X
- 1;
806 sr2
.Bottom
= End
.Y
- 1;
813 SmallRectToRect(GuiData
, &r1
, &sr1
);
814 SmallRectToRect(GuiData
, &r2
, &sr2
);
815 SmallRectToRect(GuiData
, &r3
, &sr3
);
817 rg1
= CreateRectRgnIndirect(&r1
);
818 rg2
= CreateRectRgnIndirect(&r2
);
819 rg3
= CreateRectRgnIndirect(&r3
);
821 CombineRgn(rg1
, rg1
, rg2
, RGN_XOR
);
822 CombineRgn(rg1
, rg1
, rg3
, RGN_XOR
);
834 PaintSelectionRect(PGUI_CONSOLE_DATA GuiData
, PPAINTSTRUCT pps
)
836 HRGN rgnPaint
= CreateRectRgnIndirect(&pps
->rcPaint
);
837 HRGN rgnSel
= CreateSelectionRgn(GuiData
, GuiData
->LineSelection
,
838 &GuiData
->Selection
.dwSelectionAnchor
,
839 &GuiData
->Selection
.srSelection
);
841 /* Invert the selection */
843 int ErrorCode
= CombineRgn(rgnPaint
, rgnPaint
, rgnSel
, RGN_AND
);
844 if (ErrorCode
!= ERROR
&& ErrorCode
!= NULLREGION
)
846 InvertRgn(pps
->hdc
, rgnPaint
);
849 DeleteObject(rgnSel
);
850 DeleteObject(rgnPaint
);
854 UpdateSelection(PGUI_CONSOLE_DATA GuiData
,
855 PCOORD SelectionAnchor OPTIONAL
,
858 PCONSRV_CONSOLE Console
= GuiData
->Console
;
859 HRGN oldRgn
= CreateSelectionRgn(GuiData
, GuiData
->LineSelection
,
860 &GuiData
->Selection
.dwSelectionAnchor
,
861 &GuiData
->Selection
.srSelection
);
863 /* Update the anchor if needed (use the old one if NULL) */
865 GuiData
->Selection
.dwSelectionAnchor
= *SelectionAnchor
;
867 // TODO: Scroll buffer to bring 'coord' into view
875 * Pressing the Control key while selecting text, allows us to enter
876 * into line-selection mode, the selection mode of *nix terminals.
878 BOOL OldLineSel
= GuiData
->LineSelection
;
879 GuiData
->LineSelection
= !!(GetKeyState(VK_CONTROL
) & KEY_PRESSED
);
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
);
887 newRgn
= CreateSelectionRgn(GuiData
, GuiData
->LineSelection
,
888 &GuiData
->Selection
.dwSelectionAnchor
,
891 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
893 if (OldLineSel
!= GuiData
->LineSelection
||
894 memcmp(&rc
, &GuiData
->Selection
.srSelection
, sizeof(SMALL_RECT
)) != 0)
896 /* Calculate the region that needs to be updated */
897 if (oldRgn
&& newRgn
&& CombineRgn(newRgn
, newRgn
, oldRgn
, RGN_XOR
) != ERROR
)
899 InvalidateRgn(GuiData
->hWindow
, newRgn
, FALSE
);
905 InvalidateRgn(GuiData
->hWindow
, newRgn
, FALSE
);
908 DeleteObject(newRgn
);
910 GuiData
->Selection
.dwFlags
|= CONSOLE_SELECTION_NOT_EMPTY
;
911 GuiData
->Selection
.srSelection
= rc
;
912 GuiData
->dwSelectionCursor
= *coord
;
914 if ((GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) == 0)
916 LPWSTR SelTypeStr
= NULL
, WindowTitle
= NULL
;
917 SIZE_T SelTypeStrLength
= 0, Length
= 0;
919 /* Clear the old selection */
920 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
922 InvalidateRgn(GuiData
->hWindow
, oldRgn
, FALSE
);
926 * When passing a zero-length buffer size, LoadString(...) returns
927 * a read-only pointer buffer to the program's resource string.
930 LoadStringW(ConSrvDllInstance
,
931 (GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_SELECTION
)
932 ? IDS_SELECT_TITLE
: IDS_MARK_TITLE
,
933 (LPWSTR
)&SelTypeStr
, 0);
936 * Prepend the selection type string to the current console title
937 * if we succeeded in retrieving a valid localized string.
941 // 3 for " - " and 1 for NULL
942 Length
= Console
->Title
.Length
+ (SelTypeStrLength
+ 3 + 1) * sizeof(WCHAR
);
943 WindowTitle
= ConsoleAllocHeap(0, Length
);
945 wcsncpy(WindowTitle
, SelTypeStr
, SelTypeStrLength
);
946 WindowTitle
[SelTypeStrLength
] = UNICODE_NULL
;
947 wcscat(WindowTitle
, L
" - ");
948 wcscat(WindowTitle
, Console
->Title
.Buffer
);
950 SetWindowTextW(GuiData
->hWindow
, WindowTitle
);
951 ConsoleFreeHeap(WindowTitle
);
954 GuiData
->Selection
.dwFlags
|= CONSOLE_SELECTION_IN_PROGRESS
;
955 ConioPause(Console
, PAUSED_FROM_SELECTION
);
960 /* Clear the selection */
961 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
963 InvalidateRgn(GuiData
->hWindow
, oldRgn
, FALSE
);
966 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
967 ConioUnpause(Console
, PAUSED_FROM_SELECTION
);
969 /* Restore the console title */
970 SetWindowTextW(GuiData
->hWindow
, Console
->Title
.Buffer
);
973 DeleteObject(oldRgn
);
978 GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer
,
979 PGUI_CONSOLE_DATA GuiData
,
981 PRECT rcFramebuffer
);
983 GuiPaintGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer
,
984 PGUI_CONSOLE_DATA GuiData
,
986 PRECT rcFramebuffer
);
989 OnPaint(PGUI_CONSOLE_DATA GuiData
)
991 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
995 /* Do nothing if the window is hidden */
996 if (!GuiData
->IsWindowVisible
) return;
998 BeginPaint(GuiData
->hWindow
, &ps
);
999 if (ps
.hdc
!= NULL
&&
1000 ps
.rcPaint
.left
< ps
.rcPaint
.right
&&
1001 ps
.rcPaint
.top
< ps
.rcPaint
.bottom
)
1003 EnterCriticalSection(&GuiData
->Lock
);
1005 /* Compose the current screen-buffer on-memory */
1006 if (GetType(ActiveBuffer
) == TEXTMODE_BUFFER
)
1008 GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER
)ActiveBuffer
,
1009 GuiData
, &ps
.rcPaint
, &rcPaint
);
1011 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
1013 GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER
)ActiveBuffer
,
1014 GuiData
, &ps
.rcPaint
, &rcPaint
);
1017 /* Send it to screen */
1021 rcPaint
.right
- rcPaint
.left
,
1022 rcPaint
.bottom
- rcPaint
.top
,
1028 /* Draw the selection region if needed */
1029 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
1031 PaintSelectionRect(GuiData
, &ps
);
1034 LeaveCriticalSection(&GuiData
->Lock
);
1036 EndPaint(GuiData
->hWindow
, &ps
);
1042 OnPaletteChanged(PGUI_CONSOLE_DATA GuiData
)
1044 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
1046 /* Do nothing if the window is hidden */
1047 if (!GuiData
->IsWindowVisible
) return;
1049 // See WM_PALETTECHANGED message
1050 // if ((HWND)wParam == hWnd) break;
1052 // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
1053 if (ActiveBuffer
->PaletteHandle
)
1055 DPRINT("WM_PALETTECHANGED changing palette\n");
1057 /* Specify the use of the system palette for the framebuffer */
1058 SetSystemPaletteUse(GuiData
->hMemDC
, ActiveBuffer
->PaletteUsage
);
1060 /* Realize the (logical) palette */
1061 RealizePalette(GuiData
->hMemDC
);
1066 IsSystemKey(WORD VirtualKeyCode
)
1068 switch (VirtualKeyCode
)
1070 /* From MSDN, "Virtual-Key Codes" */
1089 OnKey(PGUI_CONSOLE_DATA GuiData
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1091 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1092 PCONSOLE_SCREEN_BUFFER ActiveBuffer
;
1094 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
1096 ActiveBuffer
= GuiData
->ActiveBuffer
;
1098 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
)
1100 WORD VirtualKeyCode
= LOWORD(wParam
);
1102 if (msg
!= WM_KEYDOWN
) goto Quit
;
1104 if (VirtualKeyCode
== VK_RETURN
)
1106 /* Copy (and clear) selection if ENTER is pressed */
1110 else if ( VirtualKeyCode
== VK_ESCAPE
||
1111 (VirtualKeyCode
== 'C' && (GetKeyState(VK_CONTROL
) & KEY_PRESSED
)) )
1113 /* Cancel selection if ESC or Ctrl-C are pressed */
1114 UpdateSelection(GuiData
, NULL
, NULL
);
1118 if ((GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_SELECTION
) == 0)
1120 /* Keyboard selection mode */
1121 BOOL Interpreted
= FALSE
;
1122 BOOL MajPressed
= !!(GetKeyState(VK_SHIFT
) & KEY_PRESSED
);
1124 switch (VirtualKeyCode
)
1129 if (GuiData
->dwSelectionCursor
.X
> 0)
1130 GuiData
->dwSelectionCursor
.X
--;
1138 if (GuiData
->dwSelectionCursor
.X
< ActiveBuffer
->ScreenBufferSize
.X
- 1)
1139 GuiData
->dwSelectionCursor
.X
++;
1147 if (GuiData
->dwSelectionCursor
.Y
> 0)
1148 GuiData
->dwSelectionCursor
.Y
--;
1156 if (GuiData
->dwSelectionCursor
.Y
< ActiveBuffer
->ScreenBufferSize
.Y
- 1)
1157 GuiData
->dwSelectionCursor
.Y
++;
1165 GuiData
->dwSelectionCursor
.X
= 0;
1166 GuiData
->dwSelectionCursor
.Y
= 0;
1173 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->ScreenBufferSize
.Y
- 1;
1180 GuiData
->dwSelectionCursor
.Y
-= ActiveBuffer
->ViewSize
.Y
;
1181 if (GuiData
->dwSelectionCursor
.Y
< 0)
1182 GuiData
->dwSelectionCursor
.Y
= 0;
1190 GuiData
->dwSelectionCursor
.Y
+= ActiveBuffer
->ViewSize
.Y
;
1191 if (GuiData
->dwSelectionCursor
.Y
>= ActiveBuffer
->ScreenBufferSize
.Y
)
1192 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->ScreenBufferSize
.Y
- 1;
1203 UpdateSelection(GuiData
,
1204 !MajPressed
? &GuiData
->dwSelectionCursor
: NULL
,
1205 &GuiData
->dwSelectionCursor
);
1207 else if (!IsSystemKey(VirtualKeyCode
))
1209 /* Emit an error beep sound */
1210 SendNotifyMessage(GuiData
->hWindow
, PM_CONSOLE_BEEP
, 0, 0);
1217 /* Mouse selection mode */
1219 if (!IsSystemKey(VirtualKeyCode
))
1221 /* Clear the selection and send the key into the input buffer */
1222 UpdateSelection(GuiData
, NULL
, NULL
);
1231 if ((GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) == 0)
1235 Message
.hwnd
= GuiData
->hWindow
;
1236 Message
.message
= msg
;
1237 Message
.wParam
= wParam
;
1238 Message
.lParam
= lParam
;
1240 ConioProcessKey(Console
, &Message
);
1244 LeaveCriticalSection(&Console
->Lock
);
1248 // FIXME: Remove after fixing OnTimer
1250 InvalidateCell(PGUI_CONSOLE_DATA GuiData
,
1254 OnTimer(PGUI_CONSOLE_DATA GuiData
)
1256 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1257 PCONSOLE_SCREEN_BUFFER Buff
;
1259 /* Do nothing if the window is hidden */
1260 if (!GuiData
->IsWindowVisible
) return;
1262 SetTimer(GuiData
->hWindow
, CONGUI_UPDATE_TIMER
, CURSOR_BLINK_TIME
, NULL
);
1264 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
1266 Buff
= GuiData
->ActiveBuffer
;
1268 if (GetType(Buff
) == TEXTMODE_BUFFER
)
1270 InvalidateCell(GuiData
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
1271 Buff
->CursorBlinkOn
= !Buff
->CursorBlinkOn
;
1273 if ((GuiData
->OldCursor
.x
!= Buff
->CursorPosition
.X
) ||
1274 (GuiData
->OldCursor
.y
!= Buff
->CursorPosition
.Y
))
1277 int OldScrollX
= -1, OldScrollY
= -1;
1278 int NewScrollX
= -1, NewScrollY
= -1;
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
;
1286 // If we successfully got the info for the horizontal scrollbar
1287 if (OldScrollX
>= 0)
1289 if ((Buff
->CursorPosition
.X
< Buff
->ViewOrigin
.X
) ||
1290 (Buff
->CursorPosition
.X
>= (Buff
->ViewOrigin
.X
+ Buff
->ViewSize
.X
)))
1292 // Handle the horizontal scroll bar
1293 if (Buff
->CursorPosition
.X
>= Buff
->ViewSize
.X
)
1294 NewScrollX
= Buff
->CursorPosition
.X
- Buff
->ViewSize
.X
+ 1;
1300 NewScrollX
= OldScrollX
;
1303 // If we successfully got the info for the vertical scrollbar
1304 if (OldScrollY
>= 0)
1306 if ((Buff
->CursorPosition
.Y
< Buff
->ViewOrigin
.Y
) ||
1307 (Buff
->CursorPosition
.Y
>= (Buff
->ViewOrigin
.Y
+ Buff
->ViewSize
.Y
)))
1309 // Handle the vertical scroll bar
1310 if (Buff
->CursorPosition
.Y
>= Buff
->ViewSize
.Y
)
1311 NewScrollY
= Buff
->CursorPosition
.Y
- Buff
->ViewSize
.Y
+ 1;
1317 NewScrollY
= OldScrollY
;
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
))
1327 Buff
->ViewOrigin
.X
= NewScrollX
;
1328 Buff
->ViewOrigin
.Y
= NewScrollY
;
1329 ScrollWindowEx(GuiData
->hWindow
,
1330 (OldScrollX
- NewScrollX
) * GuiData
->CharWidth
,
1331 (OldScrollY
- NewScrollY
) * GuiData
->CharHeight
,
1337 if (NewScrollX
>= 0)
1339 sInfo
.nPos
= NewScrollX
;
1340 SetScrollInfo(GuiData
->hWindow
, SB_HORZ
, &sInfo
, TRUE
);
1342 if (NewScrollY
>= 0)
1344 sInfo
.nPos
= NewScrollY
;
1345 SetScrollInfo(GuiData
->hWindow
, SB_VERT
, &sInfo
, TRUE
);
1347 UpdateWindow(GuiData
->hWindow
);
1348 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1349 GuiData
->OldCursor
.x
= Buff
->CursorPosition
.X
;
1350 GuiData
->OldCursor
.y
= Buff
->CursorPosition
.Y
;
1354 else /* if (GetType(Buff) == GRAPHICS_BUFFER) */
1358 LeaveCriticalSection(&Console
->Lock
);
1362 OnClose(PGUI_CONSOLE_DATA GuiData
)
1364 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1366 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
))
1369 // TODO: Prompt for termination ? (Warn the user about possible apps running in this console)
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.
1376 ConSrvConsoleProcessCtrlEvent(Console
, 0, CTRL_CLOSE_EVENT
);
1378 LeaveCriticalSection(&Console
->Lock
);
1383 OnNcDestroy(HWND hWnd
)
1385 PGUI_CONSOLE_DATA GuiData
= GuiGetGuiData(hWnd
);
1387 /* Free the GuiData registration */
1388 SetWindowLongPtrW(hWnd
, GWLP_USERDATA
, (DWORD_PTR
)NULL
);
1390 GetSystemMenu(hWnd
, TRUE
);
1394 if (GuiData
->IsWindowVisible
)
1395 KillTimer(hWnd
, CONGUI_UPDATE_TIMER
);
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
);
1404 return DefWindowProcW(hWnd
, WM_NCDESTROY
, 0, 0);
1408 OnScroll(PGUI_CONSOLE_DATA GuiData
, INT nBar
, WORD sbCode
)
1410 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1411 PCONSOLE_SCREEN_BUFFER Buff
;
1413 INT oldPos
, Maximum
;
1416 ASSERT(nBar
== SB_HORZ
|| nBar
== SB_VERT
);
1418 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
1420 Buff
= GuiData
->ActiveBuffer
;
1422 if (nBar
== SB_HORZ
)
1424 Maximum
= Buff
->ScreenBufferSize
.X
- Buff
->ViewSize
.X
;
1425 pOriginXY
= &Buff
->ViewOrigin
.X
;
1427 else // if (nBar == SB_VERT)
1429 Maximum
= Buff
->ScreenBufferSize
.Y
- Buff
->ViewSize
.Y
;
1430 pOriginXY
= &Buff
->ViewOrigin
.Y
;
1433 /* Set scrollbar sizes */
1434 sInfo
.cbSize
= sizeof(sInfo
);
1435 sInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
| SIF_TRACKPOS
;
1437 if (!GetScrollInfo(GuiData
->hWindow
, nBar
, &sInfo
)) goto Quit
;
1439 oldPos
= sInfo
.nPos
;
1443 case SB_LINEUP
: // SB_LINELEFT:
1447 case SB_LINEDOWN
: // SB_LINERIGHT:
1451 case SB_PAGEUP
: // SB_PAGELEFT:
1452 sInfo
.nPos
-= sInfo
.nPage
;
1455 case SB_PAGEDOWN
: // SB_PAGERIGHT:
1456 sInfo
.nPos
+= sInfo
.nPage
;
1460 sInfo
.nPos
= sInfo
.nTrackPos
;
1461 ConioPause(Console
, PAUSED_FROM_SCROLLBAR
);
1464 case SB_THUMBPOSITION
:
1465 sInfo
.nPos
= sInfo
.nTrackPos
;
1466 ConioUnpause(Console
, PAUSED_FROM_SCROLLBAR
);
1469 case SB_TOP
: // SB_LEFT:
1470 sInfo
.nPos
= sInfo
.nMin
;
1473 case SB_BOTTOM
: // SB_RIGHT:
1474 sInfo
.nPos
= sInfo
.nMax
;
1481 sInfo
.nPos
= min(max(sInfo
.nPos
, 0), Maximum
);
1483 if (oldPos
!= sInfo
.nPos
)
1485 USHORT OldX
= Buff
->ViewOrigin
.X
;
1486 USHORT OldY
= Buff
->ViewOrigin
.Y
;
1487 UINT WidthUnit
, HeightUnit
;
1489 /* We now modify Buff->ViewOrigin */
1490 *pOriginXY
= sInfo
.nPos
;
1492 GetScreenBufferSizeUnits(Buff
, GuiData
, &WidthUnit
, &HeightUnit
);
1494 ScrollWindowEx(GuiData
->hWindow
,
1495 (OldX
- Buff
->ViewOrigin
.X
) * WidthUnit
,
1496 (OldY
- Buff
->ViewOrigin
.Y
) * HeightUnit
,
1503 sInfo
.fMask
= SIF_POS
;
1504 SetScrollInfo(GuiData
->hWindow
, nBar
, &sInfo
, TRUE
);
1506 UpdateWindow(GuiData
->hWindow
);
1507 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1511 LeaveCriticalSection(&Console
->Lock
);
1516 PointToCoord(PGUI_CONSOLE_DATA GuiData
, LPARAM lParam
)
1518 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1520 UINT WidthUnit
, HeightUnit
;
1522 GetScreenBufferSizeUnits(Buffer
, GuiData
, &WidthUnit
, &HeightUnit
);
1524 Coord
.X
= Buffer
->ViewOrigin
.X
+ ((SHORT
)LOWORD(lParam
) / (int)WidthUnit
);
1525 Coord
.Y
= Buffer
->ViewOrigin
.Y
+ ((SHORT
)HIWORD(lParam
) / (int)HeightUnit
);
1527 /* Clip coordinate to ensure it's inside buffer */
1530 else if (Coord
.X
>= Buffer
->ScreenBufferSize
.X
)
1531 Coord
.X
= Buffer
->ScreenBufferSize
.X
- 1;
1535 else if (Coord
.Y
>= Buffer
->ScreenBufferSize
.Y
)
1536 Coord
.Y
= Buffer
->ScreenBufferSize
.Y
- 1;
1542 OnMouse(PGUI_CONSOLE_DATA GuiData
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1544 BOOL DoDefault
= FALSE
;
1545 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1548 * HACK FOR CORE-8394 (Part 2):
1550 * Check whether we should ignore the next mouse move event.
1551 * In either case we reset the HACK flag.
1553 * See Part 1 of this hack below.
1555 if (GuiData
->HackCORE8394IgnoreNextMove
&& msg
== WM_MOUSEMOVE
)
1557 GuiData
->HackCORE8394IgnoreNextMove
= FALSE
;
1560 GuiData
->HackCORE8394IgnoreNextMove
= FALSE
;
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.
1566 if (GuiData
->IgnoreNextMouseSignal
)
1568 if (msg
!= WM_LBUTTONDOWN
&&
1569 msg
!= WM_MBUTTONDOWN
&&
1570 msg
!= WM_RBUTTONDOWN
&&
1571 msg
!= WM_XBUTTONDOWN
)
1574 * If this mouse signal is not a button-down action
1575 * then this is the last one being ignored.
1577 GuiData
->IgnoreNextMouseSignal
= FALSE
;
1582 * This mouse signal is a button-down action.
1583 * Ignore it and perform default action.
1590 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
))
1596 if ( (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) ||
1597 (Console
->QuickEdit
) )
1601 case WM_LBUTTONDOWN
:
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
) )
1609 * A mouse selection is currently in progress and the user
1610 * has pressed the SHIFT key and clicked somewhere, update
1613 GuiData
->dwSelectionCursor
= PointToCoord(GuiData
, lParam
);
1614 UpdateSelection(GuiData
, NULL
, &GuiData
->dwSelectionCursor
);
1618 /* Clear the old selection */
1619 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
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
);
1635 if (!(GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_DOWN
)) break;
1637 // GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1638 GuiData
->Selection
.dwFlags
&= ~CONSOLE_MOUSE_DOWN
;
1639 // UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1645 case WM_LBUTTONDBLCLK
:
1647 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1649 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
1651 #define IS_WORD_SEP(c) \
1652 ((c) == L'\0' || (c) == L' ' || (c) == L'\t' || (c) == L'\r' || (c) == L'\n')
1654 PTEXTMODE_SCREEN_BUFFER TextBuffer
= (PTEXTMODE_SCREEN_BUFFER
)Buffer
;
1656 PCHAR_INFO ptrL
, ptrR
;
1658 /* Starting point */
1659 cL
= cR
= PointToCoord(GuiData
, lParam
);
1660 ptrL
= ptrR
= ConioCoordToPointer(TextBuffer
, cL
.X
, cL
.Y
);
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
))
1669 while ((cR
.X
< TextBuffer
->ScreenBufferSize
.X
- 1) &&
1670 !IS_WORD_SEP(ptrR
->Char
.UnicodeChar
) &&
1671 !IS_WORD_SEP((ptrR
+1)->Char
.UnicodeChar
))
1678 * Update the selection started with the single
1679 * left-click that preceded this double-click.
1681 GuiData
->Selection
.dwFlags
|= CONSOLE_MOUSE_SELECTION
| CONSOLE_MOUSE_DOWN
;
1682 UpdateSelection(GuiData
, &cL
, &cR
);
1684 /* Ignore the next mouse move signal */
1685 GuiData
->IgnoreNextMouseSignal
= TRUE
;
1692 case WM_RBUTTONDOWN
:
1693 case WM_RBUTTONDBLCLK
:
1695 if (!(GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
))
1704 /* Ignore the next mouse move signal */
1705 GuiData
->IgnoreNextMouseSignal
= TRUE
;
1711 if (!(GET_KEYSTATE_WPARAM(wParam
) & MK_LBUTTON
)) break;
1712 if (!(GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_DOWN
)) break;
1714 GuiData
->dwSelectionCursor
= PointToCoord(GuiData
, lParam
);
1715 UpdateSelection(GuiData
, NULL
, &GuiData
->dwSelectionCursor
);
1720 DoDefault
= TRUE
; // FALSE;
1724 else if (Console
->InputBuffer
.Mode
& ENABLE_MOUSE_INPUT
)
1727 WORD wKeyState
= GET_KEYSTATE_WPARAM(wParam
);
1728 DWORD dwButtonState
= 0;
1729 DWORD dwControlKeyState
= 0;
1730 DWORD dwEventFlags
= 0;
1734 case WM_LBUTTONDOWN
:
1735 SetCapture(GuiData
->hWindow
);
1736 dwButtonState
= FROM_LEFT_1ST_BUTTON_PRESSED
;
1740 case WM_MBUTTONDOWN
:
1741 SetCapture(GuiData
->hWindow
);
1742 dwButtonState
= FROM_LEFT_2ND_BUTTON_PRESSED
;
1746 case WM_RBUTTONDOWN
:
1747 SetCapture(GuiData
->hWindow
);
1748 dwButtonState
= RIGHTMOST_BUTTON_PRESSED
;
1752 case WM_XBUTTONDOWN
:
1754 /* Get which X-button was pressed */
1755 WORD wButton
= GET_XBUTTON_WPARAM(wParam
);
1757 /* Check for X-button validity */
1758 if (wButton
& ~(XBUTTON1
| XBUTTON2
))
1760 DPRINT1("X-button 0x%04x invalid\n", wButton
);
1765 SetCapture(GuiData
->hWindow
);
1766 dwButtonState
= (wButton
== XBUTTON1
? FROM_LEFT_3RD_BUTTON_PRESSED
1767 : FROM_LEFT_4TH_BUTTON_PRESSED
);
1792 /* Get which X-button was released */
1793 WORD wButton
= GET_XBUTTON_WPARAM(wParam
);
1795 /* Check for X-button validity */
1796 if (wButton
& ~(XBUTTON1
| XBUTTON2
))
1798 DPRINT1("X-button 0x%04x invalid\n", wButton
);
1799 /* Ok, just release the button anyway... */
1808 case WM_LBUTTONDBLCLK
:
1809 dwButtonState
= FROM_LEFT_1ST_BUTTON_PRESSED
;
1810 dwEventFlags
= DOUBLE_CLICK
;
1813 case WM_MBUTTONDBLCLK
:
1814 dwButtonState
= FROM_LEFT_2ND_BUTTON_PRESSED
;
1815 dwEventFlags
= DOUBLE_CLICK
;
1818 case WM_RBUTTONDBLCLK
:
1819 dwButtonState
= RIGHTMOST_BUTTON_PRESSED
;
1820 dwEventFlags
= DOUBLE_CLICK
;
1823 case WM_XBUTTONDBLCLK
:
1825 /* Get which X-button was double-clicked */
1826 WORD wButton
= GET_XBUTTON_WPARAM(wParam
);
1828 /* Check for X-button validity */
1829 if (wButton
& ~(XBUTTON1
| XBUTTON2
))
1831 DPRINT1("X-button 0x%04x invalid\n", wButton
);
1836 dwButtonState
= (wButton
== XBUTTON1
? FROM_LEFT_3RD_BUTTON_PRESSED
1837 : FROM_LEFT_4TH_BUTTON_PRESSED
);
1838 dwEventFlags
= DOUBLE_CLICK
;
1844 dwEventFlags
= MOUSE_MOVED
;
1848 dwButtonState
= GET_WHEEL_DELTA_WPARAM(wParam
) << 16;
1849 dwEventFlags
= MOUSE_WHEELED
;
1852 case WM_MOUSEHWHEEL
:
1853 dwButtonState
= GET_WHEEL_DELTA_WPARAM(wParam
) << 16;
1854 dwEventFlags
= MOUSE_HWHEELED
;
1863 * HACK FOR CORE-8394 (Part 1):
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-
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.
1877 case WM_LBUTTONDOWN
:
1878 case WM_MBUTTONDOWN
:
1879 case WM_RBUTTONDOWN
:
1880 case WM_XBUTTONDOWN
:
1881 GuiData
->HackCORE8394IgnoreNextMove
= TRUE
;
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
;
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
;
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
;
1926 ConioProcessInputEvent(Console
, &er
);
1934 LeaveCriticalSection(&Console
->Lock
);
1940 if (msg
== WM_MOUSEWHEEL
|| msg
== WM_MOUSEHWHEEL
)
1944 // WORD wKeyState = GET_KEYSTATE_WPARAM(wParam);
1945 SHORT wDelta
= GET_WHEEL_DELTA_WPARAM(wParam
);
1947 if (msg
== WM_MOUSEWHEEL
)
1949 else // if (msg == WM_MOUSEHWHEEL)
1952 // NOTE: We currently do not support zooming...
1953 // if (wKeyState & MK_CONTROL)
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
);
1961 sbCode
= (wDelta
>= 0 ? SB_LINEUP
: SB_LINEDOWN
);
1963 OnScroll(GuiData
, nBar
, sbCode
);
1966 return DefWindowProcW(GuiData
->hWindow
, msg
, wParam
, lParam
);
1970 GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer
,
1971 PGUI_CONSOLE_DATA GuiData
);
1973 GuiCopyFromGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer
,
1974 PGUI_CONSOLE_DATA GuiData
);
1977 Copy(PGUI_CONSOLE_DATA GuiData
)
1979 if (OpenClipboard(GuiData
->hWindow
))
1981 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1983 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
1985 GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER
)Buffer
, GuiData
);
1987 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1989 GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER
)Buffer
, GuiData
);
1995 /* Clear the selection */
1996 UpdateSelection(GuiData
, NULL
, NULL
);
2000 GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer
,
2001 PGUI_CONSOLE_DATA GuiData
);
2003 GuiPasteToGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer
,
2004 PGUI_CONSOLE_DATA GuiData
);
2007 Paste(PGUI_CONSOLE_DATA GuiData
)
2009 if (OpenClipboard(GuiData
->hWindow
))
2011 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
2013 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
2015 GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER
)Buffer
, GuiData
);
2017 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
2019 GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER
)Buffer
, GuiData
);
2027 OnGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData
, PMINMAXINFO minMaxInfo
)
2029 PCONSRV_CONSOLE Console
= GuiData
->Console
;
2030 PCONSOLE_SCREEN_BUFFER ActiveBuffer
;
2032 UINT WidthUnit
, HeightUnit
;
2034 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
2036 ActiveBuffer
= GuiData
->ActiveBuffer
;
2038 GetScreenBufferSizeUnits(ActiveBuffer
, GuiData
, &WidthUnit
, &HeightUnit
);
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
);
2043 minMaxInfo
->ptMinTrackSize
.x
= windx
;
2044 minMaxInfo
->ptMinTrackSize
.y
= windy
;
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
);
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
2052 minMaxInfo
->ptMaxTrackSize
.x
= windx
;
2053 minMaxInfo
->ptMaxTrackSize
.y
= windy
;
2055 LeaveCriticalSection(&Console
->Lock
);
2059 OnSize(PGUI_CONSOLE_DATA GuiData
, WPARAM wParam
, LPARAM lParam
)
2061 PCONSRV_CONSOLE Console
= GuiData
->Console
;
2063 /* Do nothing if the window is hidden */
2064 if (!GuiData
->IsWindowVisible
) return;
2066 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
2068 if (!GuiData
->WindowSizeLock
&&
2069 (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
|| wParam
== SIZE_MINIMIZED
))
2071 PCONSOLE_SCREEN_BUFFER Buff
= GuiData
->ActiveBuffer
;
2072 DWORD windx
, windy
, charx
, chary
;
2073 UINT WidthUnit
, HeightUnit
;
2075 GetScreenBufferSizeUnits(Buff
, GuiData
, &WidthUnit
, &HeightUnit
);
2077 GuiData
->WindowSizeLock
= TRUE
;
2079 windx
= LOWORD(lParam
);
2080 windy
= HIWORD(lParam
);
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
2086 charx
= windx
/ (int)WidthUnit
;
2087 chary
= windy
/ (int)HeightUnit
;
2089 /* Character alignment (round size up or down) */
2090 if ((windx
% WidthUnit
) >= (WidthUnit
/ 2)) ++charx
;
2091 if ((windy
% HeightUnit
) >= (HeightUnit
/ 2)) ++chary
;
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
2097 charx
= windx
/ (int)WidthUnit
;
2098 chary
= windy
/ (int)HeightUnit
;
2100 /* Character alignment (round size up or down) */
2101 if ((windx
% WidthUnit
) >= (WidthUnit
/ 2)) ++charx
;
2102 if ((windy
% HeightUnit
) >= (HeightUnit
/ 2)) ++chary
;
2105 if ((charx
!= Buff
->ViewSize
.X
) || (chary
!= Buff
->ViewSize
.Y
))
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
;
2111 ResizeConWnd(GuiData
, WidthUnit
, HeightUnit
);
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
);
2118 GuiData
->WindowSizeLock
= FALSE
;
2121 LeaveCriticalSection(&Console
->Lock
);
2125 OnMove(PGUI_CONSOLE_DATA GuiData
)
2129 // TODO: Simplify the code.
2130 // See: GuiConsoleNotifyWndProc() PM_CREATE_CONSOLE.
2132 /* Retrieve our real position */
2133 GetWindowRect(GuiData
->hWindow
, &rcWnd
);
2134 GuiData
->GuiInfo
.WindowOrigin
.x
= rcWnd
.left
;
2135 GuiData
->GuiInfo
.WindowOrigin
.y
= rcWnd
.top
;
2139 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
2142 GuiConsoleHandleScrollbarMenu(VOID)
2146 hMenu = CreatePopupMenu();
2149 DPRINT("CreatePopupMenu failed\n");
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);
2167 static LRESULT CALLBACK
2168 ConWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2171 PGUI_CONSOLE_DATA GuiData
= NULL
;
2172 PCONSRV_CONSOLE Console
= NULL
;
2175 * - If it's the first time we create a window for the terminal,
2176 * just initialize it and return.
2178 * - If we are destroying the window, just do it and return.
2180 if (msg
== WM_NCCREATE
)
2182 return (LRESULT
)OnNcCreate(hWnd
, (LPCREATESTRUCTW
)lParam
);
2184 else if (msg
== WM_NCDESTROY
)
2186 return OnNcDestroy(hWnd
);
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.
2194 GuiData
= GuiGetGuiData(hWnd
);
2195 if (GuiData
== NULL
) return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
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
);
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.
2205 Console
= GuiData
->Console
;
2207 /* We have a console, start message dispatching */
2211 OnActivate(GuiData
, wParam
);
2215 if (OnClose(GuiData
)) goto Default
;
2226 case WM_PALETTECHANGED
:
2228 DPRINT("WM_PALETTECHANGED called\n");
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)
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.
2243 if ((HWND
)wParam
== hWnd
) break;
2245 DPRINT("WM_PALETTECHANGED ok\n");
2246 OnPaletteChanged(GuiData
);
2247 DPRINT("WM_PALETTECHANGED quit\n");
2258 case WM_SYSDEADCHAR
:
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
)
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
);
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
))
2272 return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
2275 OnKey(GuiData
, msg
, wParam
, lParam
);
2281 /* Do nothing if the window is hidden */
2282 if (!GuiData
->IsWindowVisible
) goto Default
;
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.
2289 if (wParam
== -1 && lParam
== -1)
2294 /* Get the placement of the mouse */
2295 GetCursorPos(&mouseCoords
);
2297 /* On which window is placed the mouse ? */
2298 hWndHit
= WindowFromPoint(mouseCoords
);
2300 /* It's our window. Perform the hit-test to be used later on. */
2301 if (hWndHit
== hWnd
)
2303 wParam
= (WPARAM
)hWnd
;
2304 lParam
= DefWindowProcW(hWndHit
, WM_NCHITTEST
, 0,
2305 MAKELPARAM(mouseCoords
.x
, mouseCoords
.y
));
2309 /* Set the mouse cursor only when we are in the client area */
2310 if ((HWND
)wParam
== hWnd
&& LOWORD(lParam
) == HTCLIENT
)
2312 if (GuiData
->MouseCursorRefCount
>= 0)
2314 /* Show the cursor */
2315 SetCursor(GuiData
->hCursor
);
2319 /* Hide the cursor if the reference count is negative */
2330 case WM_LBUTTONDOWN
:
2331 case WM_MBUTTONDOWN
:
2332 case WM_RBUTTONDOWN
:
2333 case WM_XBUTTONDOWN
:
2338 case WM_LBUTTONDBLCLK
:
2339 case WM_MBUTTONDBLCLK
:
2340 case WM_RBUTTONDBLCLK
:
2341 case WM_XBUTTONDBLCLK
:
2344 case WM_MOUSEHWHEEL
:
2346 Result
= OnMouse(GuiData
, msg
, wParam
, lParam
);
2351 OnScroll(GuiData
, SB_HORZ
, LOWORD(wParam
));
2355 OnScroll(GuiData
, SB_VERT
, LOWORD(wParam
));
2358 case WM_CONTEXTMENU
:
2360 /* Do nothing if the window is hidden */
2361 if (!GuiData
->IsWindowVisible
) break;
2363 if (DefWindowProcW(hWnd
/*GuiData->hWindow*/, WM_NCHITTEST
, 0, lParam
) == HTCLIENT
)
2365 HMENU hMenu
= CreatePopupMenu();
2368 AppendMenuItems(hMenu
, GuiConsoleEditMenuItems
);
2369 TrackPopupMenuEx(hMenu
,
2371 GET_X_LPARAM(lParam
),
2372 GET_Y_LPARAM(lParam
),
2387 HMENU hMenu
= (HMENU
)wParam
;
2390 /* Enable or disable the Close menu item */
2391 EnableMenuItem(hMenu
, SC_CLOSE
, MF_BYCOMMAND
|
2392 (GuiData
->IsCloseButtonEnabled
? MF_ENABLED
: MF_GRAYED
));
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
));
2405 SendMenuEvent(Console
, WM_INITMENU
);
2411 if (HIWORD(wParam
) == 0xFFFF) // Allow all the menu flags
2413 SendMenuEvent(Console
, WM_MENUSELECT
);
2421 Result
= OnCommand(GuiData
, wParam
, lParam
);
2427 OnFocus(GuiData
, (msg
== WM_SETFOCUS
));
2430 case WM_GETMINMAXINFO
:
2431 OnGetMinMaxInfo(GuiData
, (PMINMAXINFO
)lParam
);
2438 #if 0 // This code is here to prepare & control dynamic console SB resizing.
2441 PRECT dragRect
= (PRECT
)lParam
;
2445 DPRINT1("WMSZ_LEFT\n");
2448 DPRINT1("WMSZ_RIGHT\n");
2451 DPRINT1("WMSZ_TOP\n");
2454 DPRINT1("WMSZ_TOPLEFT\n");
2457 DPRINT1("WMSZ_TOPRIGHT\n");
2460 DPRINT1("WMSZ_BOTTOM\n");
2462 case WMSZ_BOTTOMLEFT
:
2463 DPRINT1("WMSZ_BOTTOMLEFT\n");
2465 case WMSZ_BOTTOMRIGHT
:
2466 DPRINT1("WMSZ_BOTTOMRIGHT\n");
2469 DPRINT1("wParam = %d\n", wParam
);
2472 DPRINT1("dragRect = {.left = %d ; .top = %d ; .right = %d ; .bottom = %d}\n",
2473 dragRect
->left
, dragRect
->top
, dragRect
->right
, dragRect
->bottom
);
2479 OnSize(GuiData
, wParam
, lParam
);
2482 case PM_RESIZE_TERMINAL
:
2484 PCONSOLE_SCREEN_BUFFER Buff
= GuiData
->ActiveBuffer
;
2488 DWORD Width
, Height
;
2489 UINT WidthUnit
, HeightUnit
;
2491 /* Do nothing if the window is hidden */
2492 if (!GuiData
->IsWindowVisible
) break;
2494 GetScreenBufferSizeUnits(Buff
, GuiData
, &WidthUnit
, &HeightUnit
);
2496 Width
= Buff
->ScreenBufferSize
.X
* WidthUnit
;
2497 Height
= Buff
->ScreenBufferSize
.Y
* HeightUnit
;
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
)
2506 if (hold
== GuiData
->hBitmap
) DeleteObject(GuiData
->hBitmap
);
2508 GuiData
->hBitmap
= hnew
;
2510 /* Resize the window to the user's values */
2511 GuiData
->WindowSizeLock
= TRUE
;
2512 ResizeConWnd(GuiData
, WidthUnit
, HeightUnit
);
2513 GuiData
->WindowSizeLock
= FALSE
;
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.
2523 case WM_SETCONSOLEINFO
:
2525 GuiApplyUserSettings(GuiData
, (HANDLE
)wParam
);
2529 case PM_CONSOLE_BEEP
:
2534 // case PM_CONSOLE_SET_TITLE:
2535 // SetWindowTextW(GuiData->hWindow, GuiData->Console->Title.Buffer);
2539 Result
= DefWindowProcW(hWnd
, msg
, wParam
, lParam
);