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)
10 * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
13 /* INCLUDES *******************************************************************/
23 #include "concfg/font.h"
27 /* GLOBALS ********************************************************************/
29 // #define PM_CREATE_CONSOLE (WM_APP + 1)
30 // #define PM_DESTROY_CONSOLE (WM_APP + 2)
33 #define CONGUI_MIN_WIDTH 10
34 #define CONGUI_MIN_HEIGHT 10
35 #define CONGUI_UPDATE_TIME 0
36 #define CONGUI_UPDATE_TIMER 1
38 #define CURSOR_BLINK_TIME 500
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
48 SetConWndConsoleLeaderCID(IN PGUI_CONSOLE_DATA GuiData
)
50 PCONSOLE_PROCESS_DATA ProcessData
;
51 CLIENT_ID ConsoleLeaderCID
;
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
));
60 /**************************************************************/
62 HICON ghDefaultIcon
= NULL
;
63 HICON ghDefaultIconSm
= NULL
;
64 HCURSOR ghDefaultCursor
= NULL
;
66 typedef struct _GUICONSOLE_MENUITEM
69 const struct _GUICONSOLE_MENUITEM
*SubMenu
;
71 } GUICONSOLE_MENUITEM
, *PGUICONSOLE_MENUITEM
;
73 static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems
[] =
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
},
82 { 0, NULL
, 0 } /* End of list */
85 static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems
[] =
87 { IDS_EDIT
, GuiConsoleEditMenuItems
, 0 },
88 { IDS_DEFAULTS
, NULL
, ID_SYSTEM_DEFAULTS
},
89 { IDS_PROPERTIES
, NULL
, ID_SYSTEM_PROPERTIES
},
91 { 0, NULL
, 0 } /* End of list */
95 * Default 16-color palette for foreground and background
96 * (corresponding flags in comments).
98 const COLORREF s_Colors
[16] =
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
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
119 /* FUNCTIONS ******************************************************************/
121 static LRESULT CALLBACK
122 ConWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
125 RegisterConWndClass(IN HINSTANCE hInstance
)
127 WNDCLASSEXW WndClass
;
130 ghDefaultIcon
= LoadImageW(hInstance
,
131 MAKEINTRESOURCEW(IDI_TERMINAL
),
133 GetSystemMetrics(SM_CXICON
),
134 GetSystemMetrics(SM_CYICON
),
136 ghDefaultIconSm
= LoadImageW(hInstance
,
137 MAKEINTRESOURCEW(IDI_TERMINAL
),
139 GetSystemMetrics(SM_CXSMICON
),
140 GetSystemMetrics(SM_CYSMICON
),
142 ghDefaultCursor
= LoadCursorW(NULL
, MAKEINTRESOURCEW(IDC_ARROW
));
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
;
157 WndClassAtom
= RegisterClassExW(&WndClass
);
158 if (WndClassAtom
== 0)
160 DPRINT1("Failed to register GUI console class\n");
164 NtUserConsoleControl(GuiConsoleWndClassAtom
, &WndClassAtom
, sizeof(ATOM
));
167 return (WndClassAtom
!= 0);
171 UnRegisterConWndClass(HINSTANCE hInstance
)
173 return !!UnregisterClassW(GUI_CONWND_CLASS
, hInstance
);
177 AppendMenuItems(HMENU hMenu
,
178 const GUICONSOLE_MENUITEM
*Items
)
181 WCHAR szMenuString
[255];
186 if (Items
[i
].uID
!= (UINT
)-1)
188 if (LoadStringW(ConSrvDllInstance
,
191 ARRAYSIZE(szMenuString
)) > 0)
193 if (Items
[i
].SubMenu
!= NULL
)
195 hSubMenu
= CreatePopupMenu();
196 if (hSubMenu
!= NULL
)
198 AppendMenuItems(hSubMenu
, Items
[i
].SubMenu
);
200 if (!AppendMenuW(hMenu
,
201 MF_STRING
| MF_POPUP
,
205 DestroyMenu(hSubMenu
);
226 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
231 CreateSysMenu(HWND hWnd
)
236 WCHAR szMenuStringBack
[255];
238 hMenu
= GetSystemMenu(hWnd
, FALSE
);
242 mii
.cbSize
= sizeof(mii
);
243 mii
.fMask
= MIIM_STRING
;
244 mii
.dwTypeData
= szMenuStringBack
;
245 mii
.cch
= ARRAYSIZE(szMenuStringBack
);
247 GetMenuItemInfoW(hMenu
, SC_CLOSE
, FALSE
, &mii
);
249 ptrTab
= wcschr(szMenuStringBack
, L
'\t');
253 mii
.cch
= wcslen(szMenuStringBack
);
255 SetMenuItemInfoW(hMenu
, SC_CLOSE
, FALSE
, &mii
);
258 AppendMenuItems(hMenu
, GuiConsoleMainMenuItems
);
263 SendMenuEvent(PCONSRV_CONSOLE Console
, UINT CmdId
)
267 DPRINT("Menu item ID: %d\n", CmdId
);
269 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
271 /* Send a menu event */
272 er
.EventType
= MENU_EVENT
;
273 er
.Event
.MenuEvent
.dwCommandId
= CmdId
;
274 ConioProcessInputEvent(Console
, &er
);
276 LeaveCriticalSection(&Console
->Lock
);
280 Copy(PGUI_CONSOLE_DATA GuiData
);
282 Paste(PGUI_CONSOLE_DATA GuiData
);
284 UpdateSelection(PGUI_CONSOLE_DATA GuiData
,
285 PCOORD SelectionAnchor OPTIONAL
,
289 Mark(PGUI_CONSOLE_DATA GuiData
)
291 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
293 /* Clear the old selection */
294 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
296 /* Restart a new selection */
297 GuiData
->dwSelectionCursor
= ActiveBuffer
->ViewOrigin
;
298 UpdateSelection(GuiData
,
299 &GuiData
->dwSelectionCursor
,
300 &GuiData
->dwSelectionCursor
);
304 SelectAll(PGUI_CONSOLE_DATA GuiData
)
306 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
307 COORD SelectionAnchor
;
309 /* Clear the old selection */
310 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
313 * The selection area extends to the whole screen buffer's width.
315 SelectionAnchor
.X
= SelectionAnchor
.Y
= 0;
316 GuiData
->dwSelectionCursor
.X
= ActiveBuffer
->ScreenBufferSize
.X
- 1;
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).
323 if (GetType(ActiveBuffer
) == TEXTMODE_BUFFER
)
326 * We select all the characters from the first line
327 * to the line where the cursor is positioned.
329 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->CursorPosition
.Y
;
331 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
334 * We select all the screen buffer area.
336 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->ScreenBufferSize
.Y
- 1;
339 /* Restart a new selection */
340 GuiData
->Selection
.dwFlags
|= CONSOLE_MOUSE_SELECTION
;
341 UpdateSelection(GuiData
, &SelectionAnchor
, &GuiData
->dwSelectionCursor
);
345 OnCommand(PGUI_CONSOLE_DATA GuiData
, WPARAM wParam
, LPARAM lParam
)
348 PCONSRV_CONSOLE Console
= GuiData
->Console
;
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...
355 if (GuiData
->CmdIdLow
<= (UINT
)wParam
&& (UINT
)wParam
<= GuiData
->CmdIdHigh
)
357 SendMenuEvent(Console
, (UINT
)wParam
);
361 /* ... otherwise, perform actions. */
364 case ID_SYSTEM_EDIT_MARK
:
368 case ID_SYSTEM_EDIT_COPY
:
372 case ID_SYSTEM_EDIT_PASTE
:
376 case ID_SYSTEM_EDIT_SELECTALL
:
380 case ID_SYSTEM_EDIT_SCROLL
:
381 DPRINT1("Scrolling is not handled yet\n");
384 case ID_SYSTEM_EDIT_FIND
:
385 DPRINT1("Finding is not handled yet\n");
388 case ID_SYSTEM_DEFAULTS
:
389 GuiConsoleShowConsoleProperties(GuiData
, TRUE
);
392 case ID_SYSTEM_PROPERTIES
:
393 GuiConsoleShowConsoleProperties(GuiData
, FALSE
);
403 Ret
= DefWindowProcW(GuiData
->hWindow
, WM_SYSCOMMAND
, wParam
, lParam
);
408 static PGUI_CONSOLE_DATA
409 GuiGetGuiData(HWND hWnd
)
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
);
417 ResizeConWnd(PGUI_CONSOLE_DATA GuiData
, DWORD WidthUnit
, DWORD HeightUnit
)
419 PCONSOLE_SCREEN_BUFFER Buff
= GuiData
->ActiveBuffer
;
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
);
429 /* Set scrollbar sizes */
430 sInfo
.cbSize
= sizeof(sInfo
);
431 sInfo
.fMask
= SIF_RANGE
| SIF_PAGE
| SIF_POS
;
433 if (Buff
->ScreenBufferSize
.Y
> Buff
->ViewSize
.Y
)
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
);
444 ShowScrollBar(GuiData
->hWindow
, SB_VERT
, FALSE
);
447 if (Buff
->ScreenBufferSize
.X
> Buff
->ViewSize
.X
)
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
);
458 ShowScrollBar(GuiData
->hWindow
, SB_HORZ
, FALSE
);
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);
470 DeleteFonts(PGUI_CONSOLE_DATA GuiData
)
473 for (i
= 0; i
< ARRAYSIZE(GuiData
->Font
); ++i
)
475 if (GuiData
->Font
[i
] != NULL
) DeleteObject(GuiData
->Font
[i
]);
476 GuiData
->Font
[i
] = NULL
;
481 CreateDerivedFont(HFONT OrgFont
,
490 /* Initialize the LOGFONT structure */
491 RtlZeroMemory(&lf
, sizeof(lf
));
493 /* Retrieve the details of the current font */
494 if (GetObjectW(OrgFont
, sizeof(lf
), &lf
) == 0)
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
;
505 /* Build a new font */
506 return CreateFontIndirectW(&lf
);
510 InitFonts(PGUI_CONSOLE_DATA GuiData
,
511 LPWSTR FaceName
, // Points to a WCHAR array of LF_FACESIZE elements.
520 * Initialize a new NORMAL font and get its character cell size.
522 /* NOTE: FontSize is always in cell height/width units (pixels) */
523 hFont
= CreateConsoleFontEx((LONG
)(ULONG
)FontSize
.Y
,
524 (LONG
)(ULONG
)FontSize
.X
,
528 GuiData
->Console
->OutputCodePage
);
531 DPRINT1("InitFonts: CreateConsoleFontEx failed\n");
535 hDC
= GetDC(GuiData
->hWindow
);
536 if (!GetFontCellSize(hDC
, hFont
, &GuiData
->CharHeight
, &GuiData
->CharWidth
))
538 DPRINT1("InitFonts: GetFontCellSize failed\n");
539 ReleaseDC(GuiData
->hWindow
, hDC
);
543 ReleaseDC(GuiData
->hWindow
, hDC
);
546 * Initialization succeeded.
548 // Delete all the old fonts first.
549 DeleteFonts(GuiData
);
550 GuiData
->Font
[FONT_NORMAL
] = hFont
;
553 * Now build the other fonts (bold, underlined, mixed).
555 GuiData
->Font
[FONT_BOLD
] =
556 CreateDerivedFont(GuiData
->Font
[FONT_NORMAL
],
557 FontWeight
< FW_BOLD
? FW_BOLD
: FontWeight
,
560 GuiData
->Font
[FONT_UNDERLINE
] =
561 CreateDerivedFont(GuiData
->Font
[FONT_NORMAL
],
565 GuiData
->Font
[FONT_BOLD
| FONT_UNDERLINE
] =
566 CreateDerivedFont(GuiData
->Font
[FONT_NORMAL
],
567 FontWeight
< FW_BOLD
? FW_BOLD
: FontWeight
,
574 if (FaceName
!= GuiData
->GuiInfo
.FaceName
)
576 StringCchCopyNW(GuiData
->GuiInfo
.FaceName
, ARRAYSIZE(GuiData
->GuiInfo
.FaceName
),
577 FaceName
, LF_FACESIZE
);
579 GuiData
->GuiInfo
.FontFamily
= FontFamily
;
580 GuiData
->GuiInfo
.FontSize
= FontSize
;
581 GuiData
->GuiInfo
.FontWeight
= FontWeight
;
588 OnNcCreate(HWND hWnd
, LPCREATESTRUCTW Create
)
590 PGUI_CONSOLE_DATA GuiData
= (PGUI_CONSOLE_DATA
)Create
->lpCreateParams
;
591 PCONSRV_CONSOLE Console
;
595 DPRINT1("GuiConsoleNcCreate: No GUI data\n");
599 Console
= GuiData
->Console
;
601 GuiData
->hWindow
= hWnd
;
602 GuiData
->hSysMenu
= GetSystemMenu(hWnd
, FALSE
);
604 /* Initialize the fonts */
605 if (!InitFonts(GuiData
,
606 GuiData
->GuiInfo
.FaceName
,
607 GuiData
->GuiInfo
.FontFamily
,
608 GuiData
->GuiInfo
.FontSize
,
609 GuiData
->GuiInfo
.FontWeight
))
611 DPRINT1("GuiConsoleNcCreate: InitFonts failed\n");
612 GuiData
->hWindow
= NULL
;
613 NtSetEvent(GuiData
->hGuiInitEvent
, NULL
);
617 /* Initialize the terminal framebuffer */
618 GuiData
->hMemDC
= CreateCompatibleDC(NULL
);
619 GuiData
->hBitmap
= NULL
;
620 GuiData
->hSysPalette
= NULL
; /* Original system palette */
622 /* Update the icons of the window */
623 if (GuiData
->hIcon
!= ghDefaultIcon
)
625 DefWindowProcW(GuiData
->hWindow
, WM_SETICON
, ICON_BIG
, (LPARAM
)GuiData
->hIcon
);
626 DefWindowProcW(GuiData
->hWindow
, WM_SETICON
, ICON_SMALL
, (LPARAM
)GuiData
->hIconSm
);
629 // FIXME: Keep these instructions here ? ///////////////////////////////////
630 Console
->ActiveBuffer
->CursorBlinkOn
= TRUE
;
631 Console
->ActiveBuffer
->ForceCursorOff
= FALSE
;
632 ////////////////////////////////////////////////////////////////////////////
634 SetWindowLongPtrW(GuiData
->hWindow
, GWLP_USERDATA
, (DWORD_PTR
)GuiData
);
636 if (GuiData
->IsWindowVisible
)
638 SetTimer(GuiData
->hWindow
, CONGUI_UPDATE_TIMER
, CONGUI_UPDATE_TIME
, NULL
);
641 // FIXME: HACK: Potential HACK for CORE-8129; see revision 63595.
642 //CreateSysMenu(GuiData->hWindow);
644 DPRINT("OnNcCreate - setting start event\n");
645 NtSetEvent(GuiData
->hGuiInitEvent
, NULL
);
647 /* We accept dropped files */
648 DragAcceptFiles(GuiData
->hWindow
, TRUE
);
650 return (BOOL
)DefWindowProcW(GuiData
->hWindow
, WM_NCCREATE
, 0, (LPARAM
)Create
);
654 OnActivate(PGUI_CONSOLE_DATA GuiData
, WPARAM wParam
)
656 WORD ActivationState
= LOWORD(wParam
);
658 DPRINT("WM_ACTIVATE - ActivationState = %d\n", ActivationState
);
660 if ( ActivationState
== WA_ACTIVE
||
661 ActivationState
== WA_CLICKACTIVE
)
663 if (GuiData
->GuiInfo
.FullScreen
)
665 EnterFullScreen(GuiData
);
666 // // PostMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
667 // SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
670 else // if (ActivationState == WA_INACTIVE)
672 if (GuiData
->GuiInfo
.FullScreen
)
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);
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.
686 if (ActivationState
== WA_CLICKACTIVE
)
687 GuiData
->IgnoreNextMouseSignal
= TRUE
;
691 OnFocus(PGUI_CONSOLE_DATA GuiData
, BOOL SetFocus
)
693 PCONSRV_CONSOLE Console
= GuiData
->Console
;
696 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
698 /* Set console focus state */
699 Console
->HasFocus
= SetFocus
;
702 * Set the priority of the processes of this console
703 * in accordance with the console focus state.
705 ConSrvSetConsoleProcessFocus(Console
, SetFocus
);
707 /* Send a focus event */
708 er
.EventType
= FOCUS_EVENT
;
709 er
.Event
.FocusEvent
.bSetFocus
= SetFocus
;
710 ConioProcessInputEvent(Console
, &er
);
712 LeaveCriticalSection(&Console
->Lock
);
715 DPRINT("TODO: Create console caret\n");
717 DPRINT("TODO: Destroy console caret\n");
721 GetSelectionBeginEnd(PCOORD Begin
, PCOORD End
,
722 PCOORD SelectionAnchor
,
723 PSMALL_RECT SmallRect
)
725 if (Begin
== NULL
|| End
== NULL
) return;
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
;
733 /* Exchange Begin / End if Begin > End lexicographically */
734 if (Begin
->Y
> End
->Y
|| (Begin
->Y
== End
->Y
&& Begin
->X
> End
->X
))
736 End
->X
= _InterlockedExchange16(&Begin
->X
, End
->X
);
737 End
->Y
= _InterlockedExchange16(&Begin
->Y
, End
->Y
);
742 CreateSelectionRgn(PGUI_CONSOLE_DATA GuiData
,
744 PCOORD SelectionAnchor
,
745 PSMALL_RECT SmallRect
)
750 SmallRectToRect(GuiData
, &rect
, SmallRect
);
751 return CreateRectRgnIndirect(&rect
);
758 GetSelectionBeginEnd(&Begin
, &End
, SelectionAnchor
, SmallRect
);
760 if (Begin
.Y
== End
.Y
)
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
)
778 "SmallRect = (%d, %d, %d, %d)\n"
779 "sr = (%d, %d, %d, %d)\n"
781 SmallRect
->Left
, SmallRect
->Top
, SmallRect
->Right
, SmallRect
->Bottom
,
782 sr
.Left
, sr
.Top
, sr
.Right
, sr
.Bottom
);
785 SmallRectToRect(GuiData
, &r
, &sr
);
786 SelRgn
= CreateRectRgnIndirect(&r
);
790 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
793 SMALL_RECT sr1
, sr2
, sr3
;
798 sr1
.Right
= ActiveBuffer
->ScreenBufferSize
.X
- 1;
799 sr1
.Bottom
= Begin
.Y
;
802 sr2
.Top
= Begin
.Y
+ 1;
803 sr2
.Right
= ActiveBuffer
->ScreenBufferSize
.X
- 1;
804 sr2
.Bottom
= End
.Y
- 1;
811 SmallRectToRect(GuiData
, &r1
, &sr1
);
812 SmallRectToRect(GuiData
, &r2
, &sr2
);
813 SmallRectToRect(GuiData
, &r3
, &sr3
);
815 rg1
= CreateRectRgnIndirect(&r1
);
816 rg2
= CreateRectRgnIndirect(&r2
);
817 rg3
= CreateRectRgnIndirect(&r3
);
819 CombineRgn(rg1
, rg1
, rg2
, RGN_XOR
);
820 CombineRgn(rg1
, rg1
, rg3
, RGN_XOR
);
832 PaintSelectionRect(PGUI_CONSOLE_DATA GuiData
, PPAINTSTRUCT pps
)
834 HRGN rgnPaint
= CreateRectRgnIndirect(&pps
->rcPaint
);
835 HRGN rgnSel
= CreateSelectionRgn(GuiData
, GuiData
->LineSelection
,
836 &GuiData
->Selection
.dwSelectionAnchor
,
837 &GuiData
->Selection
.srSelection
);
839 /* Invert the selection */
841 int ErrorCode
= CombineRgn(rgnPaint
, rgnPaint
, rgnSel
, RGN_AND
);
842 if (ErrorCode
!= ERROR
&& ErrorCode
!= NULLREGION
)
844 InvertRgn(pps
->hdc
, rgnPaint
);
847 DeleteObject(rgnSel
);
848 DeleteObject(rgnPaint
);
852 UpdateSelection(PGUI_CONSOLE_DATA GuiData
,
853 PCOORD SelectionAnchor OPTIONAL
,
856 PCONSRV_CONSOLE Console
= GuiData
->Console
;
857 HRGN oldRgn
= CreateSelectionRgn(GuiData
, GuiData
->LineSelection
,
858 &GuiData
->Selection
.dwSelectionAnchor
,
859 &GuiData
->Selection
.srSelection
);
861 /* Update the anchor if needed (use the old one if NULL) */
863 GuiData
->Selection
.dwSelectionAnchor
= *SelectionAnchor
;
865 // TODO: Scroll buffer to bring 'coord' into view
873 * Pressing the Control key while selecting text, allows us to enter
874 * into line-selection mode, the selection mode of *nix terminals.
876 BOOL OldLineSel
= GuiData
->LineSelection
;
877 GuiData
->LineSelection
= !!(GetKeyState(VK_CONTROL
) & KEY_PRESSED
);
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
);
885 newRgn
= CreateSelectionRgn(GuiData
, GuiData
->LineSelection
,
886 &GuiData
->Selection
.dwSelectionAnchor
,
889 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
891 if (OldLineSel
!= GuiData
->LineSelection
||
892 memcmp(&rc
, &GuiData
->Selection
.srSelection
, sizeof(SMALL_RECT
)) != 0)
894 /* Calculate the region that needs to be updated */
895 if (oldRgn
&& newRgn
&& CombineRgn(newRgn
, newRgn
, oldRgn
, RGN_XOR
) != ERROR
)
897 InvalidateRgn(GuiData
->hWindow
, newRgn
, FALSE
);
903 InvalidateRgn(GuiData
->hWindow
, newRgn
, FALSE
);
906 DeleteObject(newRgn
);
908 GuiData
->Selection
.dwFlags
|= CONSOLE_SELECTION_NOT_EMPTY
;
909 GuiData
->Selection
.srSelection
= rc
;
910 GuiData
->dwSelectionCursor
= *coord
;
912 if ((GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) == 0)
914 LPWSTR SelTypeStr
= NULL
, WindowTitle
= NULL
;
915 SIZE_T SelTypeStrLength
= 0, Length
= 0;
917 /* Clear the old selection */
918 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
920 InvalidateRgn(GuiData
->hWindow
, oldRgn
, FALSE
);
924 * When passing a zero-length buffer size, LoadString(...) returns
925 * a read-only pointer buffer to the program's resource string.
928 LoadStringW(ConSrvDllInstance
,
929 (GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_SELECTION
)
930 ? IDS_SELECT_TITLE
: IDS_MARK_TITLE
,
931 (LPWSTR
)&SelTypeStr
, 0);
934 * Prepend the selection type string to the current console title
935 * if we succeeded in retrieving a valid localized string.
939 // 3 for " - " and 1 for NULL
940 Length
= Console
->Title
.Length
+ (SelTypeStrLength
+ 3 + 1) * sizeof(WCHAR
);
941 WindowTitle
= ConsoleAllocHeap(0, Length
);
943 wcsncpy(WindowTitle
, SelTypeStr
, SelTypeStrLength
);
944 WindowTitle
[SelTypeStrLength
] = UNICODE_NULL
;
945 wcscat(WindowTitle
, L
" - ");
946 wcscat(WindowTitle
, Console
->Title
.Buffer
);
948 SetWindowTextW(GuiData
->hWindow
, WindowTitle
);
949 ConsoleFreeHeap(WindowTitle
);
952 GuiData
->Selection
.dwFlags
|= CONSOLE_SELECTION_IN_PROGRESS
;
953 ConioPause(Console
, PAUSED_FROM_SELECTION
);
958 /* Clear the selection */
959 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
961 InvalidateRgn(GuiData
->hWindow
, oldRgn
, FALSE
);
964 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
965 ConioUnpause(Console
, PAUSED_FROM_SELECTION
);
967 /* Restore the console title */
968 SetWindowTextW(GuiData
->hWindow
, Console
->Title
.Buffer
);
971 DeleteObject(oldRgn
);
975 OnPaint(PGUI_CONSOLE_DATA GuiData
)
977 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
981 /* Do nothing if the window is hidden */
982 if (!GuiData
->IsWindowVisible
) return;
984 BeginPaint(GuiData
->hWindow
, &ps
);
985 if (ps
.hdc
!= NULL
&&
986 ps
.rcPaint
.left
< ps
.rcPaint
.right
&&
987 ps
.rcPaint
.top
< ps
.rcPaint
.bottom
)
989 EnterCriticalSection(&GuiData
->Lock
);
991 /* Compose the current screen-buffer on-memory */
992 if (GetType(ActiveBuffer
) == TEXTMODE_BUFFER
)
994 if (IsCJKCodePage(ActiveBuffer
->Header
.Console
->OutputCodePage
))
996 /* For Chinese, Japanese and Korean */
997 GuiPaintTextModeBufferCJK((PTEXTMODE_SCREEN_BUFFER
)ActiveBuffer
,
998 GuiData
, &ps
.rcPaint
, &rcPaint
);
1002 GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER
)ActiveBuffer
,
1003 GuiData
, &ps
.rcPaint
, &rcPaint
);
1006 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
1008 GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER
)ActiveBuffer
,
1009 GuiData
, &ps
.rcPaint
, &rcPaint
);
1012 /* Send it to screen */
1016 rcPaint
.right
- rcPaint
.left
,
1017 rcPaint
.bottom
- rcPaint
.top
,
1023 /* Draw the selection region if needed */
1024 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
1026 PaintSelectionRect(GuiData
, &ps
);
1029 // TODO: Move cursor display here!
1031 LeaveCriticalSection(&GuiData
->Lock
);
1033 EndPaint(GuiData
->hWindow
, &ps
);
1039 OnPaletteChanged(PGUI_CONSOLE_DATA GuiData
)
1041 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
1043 /* Do nothing if the window is hidden */
1044 if (!GuiData
->IsWindowVisible
) return;
1046 // See WM_PALETTECHANGED message
1047 // if ((HWND)wParam == hWnd) break;
1049 // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
1050 if (ActiveBuffer
->PaletteHandle
)
1052 DPRINT("WM_PALETTECHANGED changing palette\n");
1054 /* Specify the use of the system palette for the framebuffer */
1055 SetSystemPaletteUse(GuiData
->hMemDC
, ActiveBuffer
->PaletteUsage
);
1057 /* Realize the (logical) palette */
1058 RealizePalette(GuiData
->hMemDC
);
1063 IsSystemKey(WORD VirtualKeyCode
)
1065 switch (VirtualKeyCode
)
1067 /* From MSDN, "Virtual-Key Codes" */
1086 OnKey(PGUI_CONSOLE_DATA GuiData
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1088 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1089 PCONSOLE_SCREEN_BUFFER ActiveBuffer
;
1091 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
1093 ActiveBuffer
= GuiData
->ActiveBuffer
;
1095 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
)
1097 WORD VirtualKeyCode
= LOWORD(wParam
);
1099 if (msg
!= WM_KEYDOWN
) goto Quit
;
1101 if (VirtualKeyCode
== VK_RETURN
)
1103 /* Copy (and clear) selection if ENTER is pressed */
1107 else if ( VirtualKeyCode
== VK_ESCAPE
||
1108 (VirtualKeyCode
== 'C' && (GetKeyState(VK_CONTROL
) & KEY_PRESSED
)) )
1110 /* Cancel selection if ESC or Ctrl-C are pressed */
1111 UpdateSelection(GuiData
, NULL
, NULL
);
1115 if ((GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_SELECTION
) == 0)
1117 /* Keyboard selection mode */
1118 BOOL Interpreted
= FALSE
;
1119 BOOL MajPressed
= !!(GetKeyState(VK_SHIFT
) & KEY_PRESSED
);
1121 switch (VirtualKeyCode
)
1126 if (GuiData
->dwSelectionCursor
.X
> 0)
1127 GuiData
->dwSelectionCursor
.X
--;
1135 if (GuiData
->dwSelectionCursor
.X
< ActiveBuffer
->ScreenBufferSize
.X
- 1)
1136 GuiData
->dwSelectionCursor
.X
++;
1144 if (GuiData
->dwSelectionCursor
.Y
> 0)
1145 GuiData
->dwSelectionCursor
.Y
--;
1153 if (GuiData
->dwSelectionCursor
.Y
< ActiveBuffer
->ScreenBufferSize
.Y
- 1)
1154 GuiData
->dwSelectionCursor
.Y
++;
1162 GuiData
->dwSelectionCursor
.X
= 0;
1163 GuiData
->dwSelectionCursor
.Y
= 0;
1170 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->ScreenBufferSize
.Y
- 1;
1177 GuiData
->dwSelectionCursor
.Y
-= ActiveBuffer
->ViewSize
.Y
;
1178 if (GuiData
->dwSelectionCursor
.Y
< 0)
1179 GuiData
->dwSelectionCursor
.Y
= 0;
1187 GuiData
->dwSelectionCursor
.Y
+= ActiveBuffer
->ViewSize
.Y
;
1188 if (GuiData
->dwSelectionCursor
.Y
>= ActiveBuffer
->ScreenBufferSize
.Y
)
1189 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->ScreenBufferSize
.Y
- 1;
1200 UpdateSelection(GuiData
,
1201 !MajPressed
? &GuiData
->dwSelectionCursor
: NULL
,
1202 &GuiData
->dwSelectionCursor
);
1204 else if (!IsSystemKey(VirtualKeyCode
))
1206 /* Emit an error beep sound */
1207 SendNotifyMessage(GuiData
->hWindow
, PM_CONSOLE_BEEP
, 0, 0);
1214 /* Mouse selection mode */
1216 if (!IsSystemKey(VirtualKeyCode
))
1218 /* Clear the selection and send the key into the input buffer */
1219 UpdateSelection(GuiData
, NULL
, NULL
);
1228 if ((GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) == 0)
1232 Message
.hwnd
= GuiData
->hWindow
;
1233 Message
.message
= msg
;
1234 Message
.wParam
= wParam
;
1235 Message
.lParam
= lParam
;
1237 ConioProcessKey(Console
, &Message
);
1241 LeaveCriticalSection(&Console
->Lock
);
1245 // FIXME: Remove after fixing OnTimer
1247 InvalidateCell(PGUI_CONSOLE_DATA GuiData
,
1251 OnTimer(PGUI_CONSOLE_DATA GuiData
)
1253 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1254 PCONSOLE_SCREEN_BUFFER Buff
;
1256 /* Do nothing if the window is hidden */
1257 if (!GuiData
->IsWindowVisible
) return;
1259 SetTimer(GuiData
->hWindow
, CONGUI_UPDATE_TIMER
, CURSOR_BLINK_TIME
, NULL
);
1261 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
1263 Buff
= GuiData
->ActiveBuffer
;
1265 if (GetType(Buff
) == TEXTMODE_BUFFER
)
1267 InvalidateCell(GuiData
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
1268 Buff
->CursorBlinkOn
= !Buff
->CursorBlinkOn
;
1270 if ((GuiData
->OldCursor
.x
!= Buff
->CursorPosition
.X
) ||
1271 (GuiData
->OldCursor
.y
!= Buff
->CursorPosition
.Y
))
1274 int OldScrollX
= -1, OldScrollY
= -1;
1275 int NewScrollX
= -1, NewScrollY
= -1;
1277 sInfo
.cbSize
= sizeof(sInfo
);
1278 sInfo
.fMask
= SIF_POS
;
1279 // Capture the original position of the scroll bars and save them.
1280 if (GetScrollInfo(GuiData
->hWindow
, SB_HORZ
, &sInfo
)) OldScrollX
= sInfo
.nPos
;
1281 if (GetScrollInfo(GuiData
->hWindow
, SB_VERT
, &sInfo
)) OldScrollY
= sInfo
.nPos
;
1283 // If we successfully got the info for the horizontal scrollbar
1284 if (OldScrollX
>= 0)
1286 if ((Buff
->CursorPosition
.X
< Buff
->ViewOrigin
.X
) ||
1287 (Buff
->CursorPosition
.X
>= (Buff
->ViewOrigin
.X
+ Buff
->ViewSize
.X
)))
1289 // Handle the horizontal scroll bar
1290 if (Buff
->CursorPosition
.X
>= Buff
->ViewSize
.X
)
1291 NewScrollX
= Buff
->CursorPosition
.X
- Buff
->ViewSize
.X
+ 1;
1297 NewScrollX
= OldScrollX
;
1300 // If we successfully got the info for the vertical scrollbar
1301 if (OldScrollY
>= 0)
1303 if ((Buff
->CursorPosition
.Y
< Buff
->ViewOrigin
.Y
) ||
1304 (Buff
->CursorPosition
.Y
>= (Buff
->ViewOrigin
.Y
+ Buff
->ViewSize
.Y
)))
1306 // Handle the vertical scroll bar
1307 if (Buff
->CursorPosition
.Y
>= Buff
->ViewSize
.Y
)
1308 NewScrollY
= Buff
->CursorPosition
.Y
- Buff
->ViewSize
.Y
+ 1;
1314 NewScrollY
= OldScrollY
;
1318 // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area
1319 // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar
1320 // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling)
1321 // and their associated scrollbar is left alone.
1322 if ((OldScrollX
!= NewScrollX
) || (OldScrollY
!= NewScrollY
))
1324 Buff
->ViewOrigin
.X
= NewScrollX
;
1325 Buff
->ViewOrigin
.Y
= NewScrollY
;
1326 ScrollWindowEx(GuiData
->hWindow
,
1327 (OldScrollX
- NewScrollX
) * GuiData
->CharWidth
,
1328 (OldScrollY
- NewScrollY
) * GuiData
->CharHeight
,
1334 if (NewScrollX
>= 0)
1336 sInfo
.nPos
= NewScrollX
;
1337 SetScrollInfo(GuiData
->hWindow
, SB_HORZ
, &sInfo
, TRUE
);
1339 if (NewScrollY
>= 0)
1341 sInfo
.nPos
= NewScrollY
;
1342 SetScrollInfo(GuiData
->hWindow
, SB_VERT
, &sInfo
, TRUE
);
1344 UpdateWindow(GuiData
->hWindow
);
1345 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1346 GuiData
->OldCursor
.x
= Buff
->CursorPosition
.X
;
1347 GuiData
->OldCursor
.y
= Buff
->CursorPosition
.Y
;
1351 else /* if (GetType(Buff) == GRAPHICS_BUFFER) */
1355 LeaveCriticalSection(&Console
->Lock
);
1359 OnClose(PGUI_CONSOLE_DATA GuiData
)
1361 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1363 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
))
1366 // TODO: Prompt for termination ? (Warn the user about possible apps running in this console)
1369 * FIXME: Windows will wait up to 5 seconds for the thread to exit.
1370 * We shouldn't wait here, though, since the console lock is entered.
1371 * A copy of the thread list probably needs to be made.
1373 ConSrvConsoleProcessCtrlEvent(Console
, 0, CTRL_CLOSE_EVENT
);
1375 LeaveCriticalSection(&Console
->Lock
);
1380 OnNcDestroy(HWND hWnd
)
1382 PGUI_CONSOLE_DATA GuiData
= GuiGetGuiData(hWnd
);
1384 /* Free the GuiData registration */
1385 SetWindowLongPtrW(hWnd
, GWLP_USERDATA
, (DWORD_PTR
)NULL
);
1387 /* Reset the system menu back to default and destroy the previous menu */
1388 GetSystemMenu(hWnd
, TRUE
);
1392 if (GuiData
->IsWindowVisible
)
1393 KillTimer(hWnd
, CONGUI_UPDATE_TIMER
);
1395 /* Free the terminal framebuffer */
1396 if (GuiData
->hMemDC
) DeleteDC(GuiData
->hMemDC
);
1397 if (GuiData
->hBitmap
) DeleteObject(GuiData
->hBitmap
);
1398 // if (GuiData->hSysPalette) DeleteObject(GuiData->hSysPalette);
1399 DeleteFonts(GuiData
);
1402 return DefWindowProcW(hWnd
, WM_NCDESTROY
, 0, 0);
1406 OnScroll(PGUI_CONSOLE_DATA GuiData
, INT nBar
, WORD sbCode
)
1408 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1409 PCONSOLE_SCREEN_BUFFER Buff
;
1411 INT oldPos
, Maximum
;
1414 ASSERT(nBar
== SB_HORZ
|| nBar
== SB_VERT
);
1416 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
1418 Buff
= GuiData
->ActiveBuffer
;
1420 if (nBar
== SB_HORZ
)
1422 Maximum
= Buff
->ScreenBufferSize
.X
- Buff
->ViewSize
.X
;
1423 pOriginXY
= &Buff
->ViewOrigin
.X
;
1425 else // if (nBar == SB_VERT)
1427 Maximum
= Buff
->ScreenBufferSize
.Y
- Buff
->ViewSize
.Y
;
1428 pOriginXY
= &Buff
->ViewOrigin
.Y
;
1431 /* Set scrollbar sizes */
1432 sInfo
.cbSize
= sizeof(sInfo
);
1433 sInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
| SIF_TRACKPOS
;
1435 if (!GetScrollInfo(GuiData
->hWindow
, nBar
, &sInfo
)) goto Quit
;
1437 oldPos
= sInfo
.nPos
;
1441 case SB_LINEUP
: // SB_LINELEFT:
1445 case SB_LINEDOWN
: // SB_LINERIGHT:
1449 case SB_PAGEUP
: // SB_PAGELEFT:
1450 sInfo
.nPos
-= sInfo
.nPage
;
1453 case SB_PAGEDOWN
: // SB_PAGERIGHT:
1454 sInfo
.nPos
+= sInfo
.nPage
;
1458 sInfo
.nPos
= sInfo
.nTrackPos
;
1459 ConioPause(Console
, PAUSED_FROM_SCROLLBAR
);
1462 case SB_THUMBPOSITION
:
1463 sInfo
.nPos
= sInfo
.nTrackPos
;
1464 ConioUnpause(Console
, PAUSED_FROM_SCROLLBAR
);
1467 case SB_TOP
: // SB_LEFT:
1468 sInfo
.nPos
= sInfo
.nMin
;
1471 case SB_BOTTOM
: // SB_RIGHT:
1472 sInfo
.nPos
= sInfo
.nMax
;
1479 sInfo
.nPos
= min(max(sInfo
.nPos
, 0), Maximum
);
1481 if (oldPos
!= sInfo
.nPos
)
1483 USHORT OldX
= Buff
->ViewOrigin
.X
;
1484 USHORT OldY
= Buff
->ViewOrigin
.Y
;
1485 UINT WidthUnit
, HeightUnit
;
1487 /* We now modify Buff->ViewOrigin */
1488 *pOriginXY
= sInfo
.nPos
;
1490 GetScreenBufferSizeUnits(Buff
, GuiData
, &WidthUnit
, &HeightUnit
);
1492 ScrollWindowEx(GuiData
->hWindow
,
1493 (OldX
- Buff
->ViewOrigin
.X
) * WidthUnit
,
1494 (OldY
- Buff
->ViewOrigin
.Y
) * HeightUnit
,
1501 sInfo
.fMask
= SIF_POS
;
1502 SetScrollInfo(GuiData
->hWindow
, nBar
, &sInfo
, TRUE
);
1504 UpdateWindow(GuiData
->hWindow
);
1505 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1509 LeaveCriticalSection(&Console
->Lock
);
1514 PointToCoord(PGUI_CONSOLE_DATA GuiData
, LPARAM lParam
)
1516 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1518 UINT WidthUnit
, HeightUnit
;
1520 GetScreenBufferSizeUnits(Buffer
, GuiData
, &WidthUnit
, &HeightUnit
);
1522 Coord
.X
= Buffer
->ViewOrigin
.X
+ ((SHORT
)LOWORD(lParam
) / (int)WidthUnit
);
1523 Coord
.Y
= Buffer
->ViewOrigin
.Y
+ ((SHORT
)HIWORD(lParam
) / (int)HeightUnit
);
1525 /* Clip coordinate to ensure it's inside buffer */
1528 else if (Coord
.X
>= Buffer
->ScreenBufferSize
.X
)
1529 Coord
.X
= Buffer
->ScreenBufferSize
.X
- 1;
1533 else if (Coord
.Y
>= Buffer
->ScreenBufferSize
.Y
)
1534 Coord
.Y
= Buffer
->ScreenBufferSize
.Y
- 1;
1540 OnMouse(PGUI_CONSOLE_DATA GuiData
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1542 BOOL DoDefault
= FALSE
;
1543 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1546 * HACK FOR CORE-8394 (Part 2):
1548 * Check whether we should ignore the next mouse move event.
1549 * In either case we reset the HACK flag.
1551 * See Part 1 of this hack below.
1553 if (GuiData
->HackCORE8394IgnoreNextMove
&& msg
== WM_MOUSEMOVE
)
1555 GuiData
->HackCORE8394IgnoreNextMove
= FALSE
;
1558 GuiData
->HackCORE8394IgnoreNextMove
= FALSE
;
1560 // FIXME: It's here that we need to check whether we have focus or not
1561 // and whether we are or not in edit mode, in order to know if we need
1562 // to deal with the mouse.
1564 if (GuiData
->IgnoreNextMouseSignal
)
1566 if (msg
!= WM_LBUTTONDOWN
&&
1567 msg
!= WM_MBUTTONDOWN
&&
1568 msg
!= WM_RBUTTONDOWN
&&
1569 msg
!= WM_XBUTTONDOWN
)
1572 * If this mouse signal is not a button-down action
1573 * then this is the last one being ignored.
1575 GuiData
->IgnoreNextMouseSignal
= FALSE
;
1580 * This mouse signal is a button-down action.
1581 * Ignore it and perform default action.
1588 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
))
1594 if ( (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) ||
1595 (Console
->QuickEdit
) )
1599 case WM_LBUTTONDOWN
:
1601 /* Check for selection state */
1602 if ( (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) &&
1603 (GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_SELECTION
) &&
1604 (GetKeyState(VK_SHIFT
) & KEY_PRESSED
) )
1607 * A mouse selection is currently in progress and the user
1608 * has pressed the SHIFT key and clicked somewhere, update
1611 GuiData
->dwSelectionCursor
= PointToCoord(GuiData
, lParam
);
1612 UpdateSelection(GuiData
, NULL
, &GuiData
->dwSelectionCursor
);
1616 /* Clear the old selection */
1617 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
1619 /* Restart a new selection */
1620 GuiData
->dwSelectionCursor
= PointToCoord(GuiData
, lParam
);
1621 SetCapture(GuiData
->hWindow
);
1622 GuiData
->Selection
.dwFlags
|= CONSOLE_MOUSE_SELECTION
| CONSOLE_MOUSE_DOWN
;
1623 UpdateSelection(GuiData
,
1624 &GuiData
->dwSelectionCursor
,
1625 &GuiData
->dwSelectionCursor
);
1633 if (!(GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_DOWN
)) break;
1635 // GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1636 GuiData
->Selection
.dwFlags
&= ~CONSOLE_MOUSE_DOWN
;
1637 // UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1643 case WM_LBUTTONDBLCLK
:
1645 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1647 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
1649 #define IS_WORD_SEP(c) \
1650 ((c) == L'\0' || (c) == L' ' || (c) == L'\t' || (c) == L'\r' || (c) == L'\n')
1652 PTEXTMODE_SCREEN_BUFFER TextBuffer
= (PTEXTMODE_SCREEN_BUFFER
)Buffer
;
1654 PCHAR_INFO ptrL
, ptrR
;
1656 /* Starting point */
1657 cL
= cR
= PointToCoord(GuiData
, lParam
);
1658 ptrL
= ptrR
= ConioCoordToPointer(TextBuffer
, cL
.X
, cL
.Y
);
1660 /* Enlarge the selection by checking for whitespace */
1661 while ((0 < cL
.X
) && !IS_WORD_SEP(ptrL
->Char
.UnicodeChar
)
1662 && !IS_WORD_SEP((ptrL
-1)->Char
.UnicodeChar
))
1667 while ((cR
.X
< TextBuffer
->ScreenBufferSize
.X
- 1) &&
1668 !IS_WORD_SEP(ptrR
->Char
.UnicodeChar
) &&
1669 !IS_WORD_SEP((ptrR
+1)->Char
.UnicodeChar
))
1676 * Update the selection started with the single
1677 * left-click that preceded this double-click.
1679 GuiData
->Selection
.dwFlags
|= CONSOLE_MOUSE_SELECTION
| CONSOLE_MOUSE_DOWN
;
1680 UpdateSelection(GuiData
, &cL
, &cR
);
1682 /* Ignore the next mouse move signal */
1683 GuiData
->IgnoreNextMouseSignal
= TRUE
;
1690 case WM_RBUTTONDOWN
:
1691 case WM_RBUTTONDBLCLK
:
1693 if (!(GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
))
1702 /* Ignore the next mouse move signal */
1703 GuiData
->IgnoreNextMouseSignal
= TRUE
;
1709 if (!(GET_KEYSTATE_WPARAM(wParam
) & MK_LBUTTON
)) break;
1710 if (!(GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_DOWN
)) break;
1712 GuiData
->dwSelectionCursor
= PointToCoord(GuiData
, lParam
);
1713 UpdateSelection(GuiData
, NULL
, &GuiData
->dwSelectionCursor
);
1718 DoDefault
= TRUE
; // FALSE;
1722 else if (Console
->InputBuffer
.Mode
& ENABLE_MOUSE_INPUT
)
1725 WORD wKeyState
= GET_KEYSTATE_WPARAM(wParam
);
1726 DWORD dwButtonState
= 0;
1727 DWORD dwControlKeyState
= 0;
1728 DWORD dwEventFlags
= 0;
1732 case WM_LBUTTONDOWN
:
1733 SetCapture(GuiData
->hWindow
);
1734 dwButtonState
= FROM_LEFT_1ST_BUTTON_PRESSED
;
1738 case WM_MBUTTONDOWN
:
1739 SetCapture(GuiData
->hWindow
);
1740 dwButtonState
= FROM_LEFT_2ND_BUTTON_PRESSED
;
1744 case WM_RBUTTONDOWN
:
1745 SetCapture(GuiData
->hWindow
);
1746 dwButtonState
= RIGHTMOST_BUTTON_PRESSED
;
1750 case WM_XBUTTONDOWN
:
1752 /* Get which X-button was pressed */
1753 WORD wButton
= GET_XBUTTON_WPARAM(wParam
);
1755 /* Check for X-button validity */
1756 if (wButton
& ~(XBUTTON1
| XBUTTON2
))
1758 DPRINT1("X-button 0x%04x invalid\n", wButton
);
1763 SetCapture(GuiData
->hWindow
);
1764 dwButtonState
= (wButton
== XBUTTON1
? FROM_LEFT_3RD_BUTTON_PRESSED
1765 : FROM_LEFT_4TH_BUTTON_PRESSED
);
1790 /* Get which X-button was released */
1791 WORD wButton
= GET_XBUTTON_WPARAM(wParam
);
1793 /* Check for X-button validity */
1794 if (wButton
& ~(XBUTTON1
| XBUTTON2
))
1796 DPRINT1("X-button 0x%04x invalid\n", wButton
);
1797 /* Ok, just release the button anyway... */
1806 case WM_LBUTTONDBLCLK
:
1807 dwButtonState
= FROM_LEFT_1ST_BUTTON_PRESSED
;
1808 dwEventFlags
= DOUBLE_CLICK
;
1811 case WM_MBUTTONDBLCLK
:
1812 dwButtonState
= FROM_LEFT_2ND_BUTTON_PRESSED
;
1813 dwEventFlags
= DOUBLE_CLICK
;
1816 case WM_RBUTTONDBLCLK
:
1817 dwButtonState
= RIGHTMOST_BUTTON_PRESSED
;
1818 dwEventFlags
= DOUBLE_CLICK
;
1821 case WM_XBUTTONDBLCLK
:
1823 /* Get which X-button was double-clicked */
1824 WORD wButton
= GET_XBUTTON_WPARAM(wParam
);
1826 /* Check for X-button validity */
1827 if (wButton
& ~(XBUTTON1
| XBUTTON2
))
1829 DPRINT1("X-button 0x%04x invalid\n", wButton
);
1834 dwButtonState
= (wButton
== XBUTTON1
? FROM_LEFT_3RD_BUTTON_PRESSED
1835 : FROM_LEFT_4TH_BUTTON_PRESSED
);
1836 dwEventFlags
= DOUBLE_CLICK
;
1842 dwEventFlags
= MOUSE_MOVED
;
1846 dwButtonState
= GET_WHEEL_DELTA_WPARAM(wParam
) << 16;
1847 dwEventFlags
= MOUSE_WHEELED
;
1850 case WM_MOUSEHWHEEL
:
1851 dwButtonState
= GET_WHEEL_DELTA_WPARAM(wParam
) << 16;
1852 dwEventFlags
= MOUSE_HWHEELED
;
1861 * HACK FOR CORE-8394 (Part 1):
1863 * It appears that depending on which VM ReactOS runs, the next mouse
1864 * signal coming after a button-down action can be a mouse-move (e.g.
1865 * on VBox, whereas on QEMU it is not the case). However it is NOT a
1866 * rule, so that we cannot use the IgnoreNextMouseSignal flag to just
1867 * "ignore" the next mouse event, thinking it would always be a mouse-
1870 * To work around this problem (that should really be fixed in Win32k),
1871 * we use a second flag to ignore this possible next mouse move signal.
1875 case WM_LBUTTONDOWN
:
1876 case WM_MBUTTONDOWN
:
1877 case WM_RBUTTONDOWN
:
1878 case WM_XBUTTONDOWN
:
1879 GuiData
->HackCORE8394IgnoreNextMove
= TRUE
;
1886 if (wKeyState
& MK_LBUTTON
)
1887 dwButtonState
|= FROM_LEFT_1ST_BUTTON_PRESSED
;
1888 if (wKeyState
& MK_MBUTTON
)
1889 dwButtonState
|= FROM_LEFT_2ND_BUTTON_PRESSED
;
1890 if (wKeyState
& MK_RBUTTON
)
1891 dwButtonState
|= RIGHTMOST_BUTTON_PRESSED
;
1892 if (wKeyState
& MK_XBUTTON1
)
1893 dwButtonState
|= FROM_LEFT_3RD_BUTTON_PRESSED
;
1894 if (wKeyState
& MK_XBUTTON2
)
1895 dwButtonState
|= FROM_LEFT_4TH_BUTTON_PRESSED
;
1897 if (GetKeyState(VK_RMENU
) & KEY_PRESSED
)
1898 dwControlKeyState
|= RIGHT_ALT_PRESSED
;
1899 if (GetKeyState(VK_LMENU
) & KEY_PRESSED
)
1900 dwControlKeyState
|= LEFT_ALT_PRESSED
;
1901 if (GetKeyState(VK_RCONTROL
) & KEY_PRESSED
)
1902 dwControlKeyState
|= RIGHT_CTRL_PRESSED
;
1903 if (GetKeyState(VK_LCONTROL
) & KEY_PRESSED
)
1904 dwControlKeyState
|= LEFT_CTRL_PRESSED
;
1905 if (GetKeyState(VK_SHIFT
) & KEY_PRESSED
)
1906 dwControlKeyState
|= SHIFT_PRESSED
;
1907 if (GetKeyState(VK_NUMLOCK
) & KEY_TOGGLED
)
1908 dwControlKeyState
|= NUMLOCK_ON
;
1909 if (GetKeyState(VK_SCROLL
) & KEY_TOGGLED
)
1910 dwControlKeyState
|= SCROLLLOCK_ON
;
1911 if (GetKeyState(VK_CAPITAL
) & KEY_TOGGLED
)
1912 dwControlKeyState
|= CAPSLOCK_ON
;
1913 /* See WM_CHAR MSDN documentation for instance */
1914 if (lParam
& 0x01000000)
1915 dwControlKeyState
|= ENHANCED_KEY
;
1917 /* Send a mouse event */
1918 er
.EventType
= MOUSE_EVENT
;
1919 er
.Event
.MouseEvent
.dwMousePosition
= PointToCoord(GuiData
, lParam
);
1920 er
.Event
.MouseEvent
.dwButtonState
= dwButtonState
;
1921 er
.Event
.MouseEvent
.dwControlKeyState
= dwControlKeyState
;
1922 er
.Event
.MouseEvent
.dwEventFlags
= dwEventFlags
;
1924 ConioProcessInputEvent(Console
, &er
);
1932 LeaveCriticalSection(&Console
->Lock
);
1938 if (msg
== WM_MOUSEWHEEL
|| msg
== WM_MOUSEHWHEEL
)
1942 // WORD wKeyState = GET_KEYSTATE_WPARAM(wParam);
1943 SHORT wDelta
= GET_WHEEL_DELTA_WPARAM(wParam
);
1945 if (msg
== WM_MOUSEWHEEL
)
1947 else // if (msg == WM_MOUSEHWHEEL)
1950 // NOTE: We currently do not support zooming...
1951 // if (wKeyState & MK_CONTROL)
1953 // FIXME: For some reason our win32k does not set the key states
1954 // when sending WM_MOUSEWHEEL or WM_MOUSEHWHEEL ...
1955 // if (wKeyState & MK_SHIFT)
1956 if (GetKeyState(VK_SHIFT
) & KEY_PRESSED
)
1957 sbCode
= (wDelta
>= 0 ? SB_PAGEUP
: SB_PAGEDOWN
);
1959 sbCode
= (wDelta
>= 0 ? SB_LINEUP
: SB_LINEDOWN
);
1961 OnScroll(GuiData
, nBar
, sbCode
);
1964 return DefWindowProcW(GuiData
->hWindow
, msg
, wParam
, lParam
);
1969 Copy(PGUI_CONSOLE_DATA GuiData
)
1971 if (OpenClipboard(GuiData
->hWindow
))
1973 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1975 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
1977 GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER
)Buffer
, GuiData
);
1979 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1981 GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER
)Buffer
, GuiData
);
1987 /* Clear the selection */
1988 UpdateSelection(GuiData
, NULL
, NULL
);
1992 Paste(PGUI_CONSOLE_DATA GuiData
)
1994 if (OpenClipboard(GuiData
->hWindow
))
1996 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1998 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
2000 GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER
)Buffer
, GuiData
);
2002 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
2004 GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER
)Buffer
, GuiData
);
2012 OnGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData
, PMINMAXINFO minMaxInfo
)
2014 PCONSRV_CONSOLE Console
= GuiData
->Console
;
2015 PCONSOLE_SCREEN_BUFFER ActiveBuffer
;
2017 UINT WidthUnit
, HeightUnit
;
2019 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
2021 ActiveBuffer
= GuiData
->ActiveBuffer
;
2023 GetScreenBufferSizeUnits(ActiveBuffer
, GuiData
, &WidthUnit
, &HeightUnit
);
2025 windx
= CONGUI_MIN_WIDTH
* WidthUnit
+ 2 * (GetSystemMetrics(SM_CXFRAME
) + GetSystemMetrics(SM_CXEDGE
));
2026 windy
= CONGUI_MIN_HEIGHT
* HeightUnit
+ 2 * (GetSystemMetrics(SM_CYFRAME
) + GetSystemMetrics(SM_CYEDGE
)) + GetSystemMetrics(SM_CYCAPTION
);
2028 minMaxInfo
->ptMinTrackSize
.x
= windx
;
2029 minMaxInfo
->ptMinTrackSize
.y
= windy
;
2031 windx
= (ActiveBuffer
->ScreenBufferSize
.X
) * WidthUnit
+ 2 * (GetSystemMetrics(SM_CXFRAME
) + GetSystemMetrics(SM_CXEDGE
));
2032 windy
= (ActiveBuffer
->ScreenBufferSize
.Y
) * HeightUnit
+ 2 * (GetSystemMetrics(SM_CYFRAME
) + GetSystemMetrics(SM_CYEDGE
)) + GetSystemMetrics(SM_CYCAPTION
);
2034 if (ActiveBuffer
->ViewSize
.X
< ActiveBuffer
->ScreenBufferSize
.X
) windy
+= GetSystemMetrics(SM_CYHSCROLL
); // Window currently has a horizontal scrollbar
2035 if (ActiveBuffer
->ViewSize
.Y
< ActiveBuffer
->ScreenBufferSize
.Y
) windx
+= GetSystemMetrics(SM_CXVSCROLL
); // Window currently has a vertical scrollbar
2037 minMaxInfo
->ptMaxTrackSize
.x
= windx
;
2038 minMaxInfo
->ptMaxTrackSize
.y
= windy
;
2040 LeaveCriticalSection(&Console
->Lock
);
2044 OnSize(PGUI_CONSOLE_DATA GuiData
, WPARAM wParam
, LPARAM lParam
)
2046 PCONSRV_CONSOLE Console
= GuiData
->Console
;
2048 /* Do nothing if the window is hidden */
2049 if (!GuiData
->IsWindowVisible
|| IsIconic(GuiData
->hWindow
)) return;
2051 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
2053 if (!GuiData
->WindowSizeLock
&&
2054 (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
|| wParam
== SIZE_MINIMIZED
))
2056 PCONSOLE_SCREEN_BUFFER Buff
= GuiData
->ActiveBuffer
;
2057 DWORD windx
, windy
, charx
, chary
;
2058 UINT WidthUnit
, HeightUnit
;
2060 GetScreenBufferSizeUnits(Buff
, GuiData
, &WidthUnit
, &HeightUnit
);
2062 GuiData
->WindowSizeLock
= TRUE
;
2064 windx
= LOWORD(lParam
);
2065 windy
= HIWORD(lParam
);
2067 /* Compensate for existing scroll bars (because lParam values do not accommodate scroll bar) */
2068 if (Buff
->ViewSize
.X
< Buff
->ScreenBufferSize
.X
) windy
+= GetSystemMetrics(SM_CYHSCROLL
); // Window currently has a horizontal scrollbar
2069 if (Buff
->ViewSize
.Y
< Buff
->ScreenBufferSize
.Y
) windx
+= GetSystemMetrics(SM_CXVSCROLL
); // Window currently has a vertical scrollbar
2071 charx
= windx
/ (int)WidthUnit
;
2072 chary
= windy
/ (int)HeightUnit
;
2074 /* Character alignment (round size up or down) */
2075 if ((windx
% WidthUnit
) >= (WidthUnit
/ 2)) ++charx
;
2076 if ((windy
% HeightUnit
) >= (HeightUnit
/ 2)) ++chary
;
2078 /* Compensate for added scroll bars in window */
2079 if (Buff
->ViewSize
.X
< Buff
->ScreenBufferSize
.X
) windy
-= GetSystemMetrics(SM_CYHSCROLL
); // Window will have a horizontal scroll bar
2080 if (Buff
->ViewSize
.Y
< Buff
->ScreenBufferSize
.Y
) windx
-= GetSystemMetrics(SM_CXVSCROLL
); // Window will have a vertical scroll bar
2082 charx
= windx
/ (int)WidthUnit
;
2083 chary
= windy
/ (int)HeightUnit
;
2085 /* Character alignment (round size up or down) */
2086 if ((windx
% WidthUnit
) >= (WidthUnit
/ 2)) ++charx
;
2087 if ((windy
% HeightUnit
) >= (HeightUnit
/ 2)) ++chary
;
2090 if ((charx
!= Buff
->ViewSize
.X
) || (chary
!= Buff
->ViewSize
.Y
))
2092 Buff
->ViewSize
.X
= (charx
<= (DWORD
)Buff
->ScreenBufferSize
.X
) ? charx
: Buff
->ScreenBufferSize
.X
;
2093 Buff
->ViewSize
.Y
= (chary
<= (DWORD
)Buff
->ScreenBufferSize
.Y
) ? chary
: Buff
->ScreenBufferSize
.Y
;
2096 ResizeConWnd(GuiData
, WidthUnit
, HeightUnit
);
2098 /* Adjust the start of the visible area if we are attempting to show nonexistent areas */
2099 if ((Buff
->ScreenBufferSize
.X
- Buff
->ViewOrigin
.X
) < Buff
->ViewSize
.X
) Buff
->ViewOrigin
.X
= Buff
->ScreenBufferSize
.X
- Buff
->ViewSize
.X
;
2100 if ((Buff
->ScreenBufferSize
.Y
- Buff
->ViewOrigin
.Y
) < Buff
->ViewSize
.Y
) Buff
->ViewOrigin
.Y
= Buff
->ScreenBufferSize
.Y
- Buff
->ViewSize
.Y
;
2101 InvalidateRect(GuiData
->hWindow
, NULL
, TRUE
);
2103 GuiData
->WindowSizeLock
= FALSE
;
2106 LeaveCriticalSection(&Console
->Lock
);
2110 OnMove(PGUI_CONSOLE_DATA GuiData
)
2114 // TODO: Simplify the code.
2115 // See: GuiConsoleNotifyWndProc() PM_CREATE_CONSOLE.
2117 /* Retrieve our real position */
2118 GetWindowRect(GuiData
->hWindow
, &rcWnd
);
2119 GuiData
->GuiInfo
.WindowOrigin
.x
= rcWnd
.left
;
2120 GuiData
->GuiInfo
.WindowOrigin
.y
= rcWnd
.top
;
2124 OnDropFiles(PCONSRV_CONSOLE Console
, HDROP hDrop
)
2127 WCHAR szPath
[MAX_PATH
+ 2];
2131 DragQueryFileW(hDrop
, 0, &szPath
[1], ARRAYSIZE(szPath
) - 1);
2134 if (wcschr(&szPath
[1], L
' ') != NULL
)
2136 StringCchCatW(szPath
, ARRAYSIZE(szPath
), L
"\"");
2141 pszPath
= &szPath
[1];
2144 PasteText(Console
, pszPath
, wcslen(pszPath
));
2148 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
2151 GuiConsoleHandleScrollbarMenu(VOID)
2155 hMenu = CreatePopupMenu();
2158 DPRINT("CreatePopupMenu failed\n");
2162 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
2163 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2164 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
2165 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
2166 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2167 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
2168 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
2169 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2170 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
2171 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
2175 static LRESULT CALLBACK
2176 ConWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2179 PGUI_CONSOLE_DATA GuiData
= NULL
;
2180 PCONSRV_CONSOLE Console
= NULL
;
2183 * - If it's the first time we create a window for the terminal,
2184 * just initialize it and return.
2186 * - If we are destroying the window, just do it and return.
2188 if (msg
== WM_NCCREATE
)
2190 return (LRESULT
)OnNcCreate(hWnd
, (LPCREATESTRUCTW
)lParam
);
2192 else if (msg
== WM_NCDESTROY
)
2194 return OnNcDestroy(hWnd
);
2198 * Now the terminal window is initialized.
2199 * Get the terminal data via the window's data.
2200 * If there is no data, just go away.
2202 GuiData
= GuiGetGuiData(hWnd
);
2203 if (GuiData
== NULL
) return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
2205 // TEMPORARY HACK until all of the functions can deal with a NULL GuiData->ActiveBuffer ...
2206 if (GuiData
->ActiveBuffer
== NULL
) return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
2209 * Just retrieve a pointer to the console in case somebody needs it.
2210 * It is not NULL because it was checked in GuiGetGuiData.
2211 * Each helper function which needs the console has to validate and lock it.
2213 Console
= GuiData
->Console
;
2215 /* We have a console, start message dispatching */
2219 OnActivate(GuiData
, wParam
);
2223 if (OnClose(GuiData
)) goto Default
;
2237 case WM_PALETTECHANGED
:
2239 DPRINT("WM_PALETTECHANGED called\n");
2242 * Protects against infinite loops:
2243 * "... A window that receives this message must not realize
2244 * its palette, unless it determines that wParam does not contain
2245 * its own window handle." (WM_PALETTECHANGED description - MSDN)
2247 * This message is sent to all windows, including the one that
2248 * changed the system palette and caused this message to be sent.
2249 * The wParam of this message contains the handle of the window
2250 * that caused the system palette to change. To avoid an infinite
2251 * loop, care must be taken to check that the wParam of this message
2252 * does not match the window's handle.
2254 if ((HWND
)wParam
== hWnd
) break;
2256 DPRINT("WM_PALETTECHANGED ok\n");
2257 OnPaletteChanged(GuiData
);
2258 DPRINT("WM_PALETTECHANGED quit\n");
2269 case WM_SYSDEADCHAR
:
2271 /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */
2272 if (msg
== WM_SYSKEYDOWN
&& (HIWORD(lParam
) & KF_ALTDOWN
) && wParam
== VK_RETURN
)
2274 /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */
2275 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
2276 GuiConsoleSwitchFullScreen(GuiData
);
2280 /* Detect Alt-Esc/Space/Tab presses defer to DefWindowProc */
2281 if ( (HIWORD(lParam
) & KF_ALTDOWN
) && (wParam
== VK_ESCAPE
|| wParam
== VK_SPACE
|| wParam
== VK_TAB
))
2283 return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
2286 OnKey(GuiData
, msg
, wParam
, lParam
);
2292 /* Do nothing if the window is hidden */
2293 if (!GuiData
->IsWindowVisible
) goto Default
;
2296 * The message was sent because we are manually triggering a change.
2297 * Check whether the mouse is indeed present on this console window
2298 * and take appropriate decisions.
2300 if (wParam
== -1 && lParam
== -1)
2305 /* Get the placement of the mouse */
2306 GetCursorPos(&mouseCoords
);
2308 /* On which window is placed the mouse ? */
2309 hWndHit
= WindowFromPoint(mouseCoords
);
2311 /* It's our window. Perform the hit-test to be used later on. */
2312 if (hWndHit
== hWnd
)
2314 wParam
= (WPARAM
)hWnd
;
2315 lParam
= DefWindowProcW(hWndHit
, WM_NCHITTEST
, 0,
2316 MAKELPARAM(mouseCoords
.x
, mouseCoords
.y
));
2320 /* Set the mouse cursor only when we are in the client area */
2321 if ((HWND
)wParam
== hWnd
&& LOWORD(lParam
) == HTCLIENT
)
2323 if (GuiData
->MouseCursorRefCount
>= 0)
2325 /* Show the cursor */
2326 SetCursor(GuiData
->hCursor
);
2330 /* Hide the cursor if the reference count is negative */
2341 case WM_LBUTTONDOWN
:
2342 case WM_MBUTTONDOWN
:
2343 case WM_RBUTTONDOWN
:
2344 case WM_XBUTTONDOWN
:
2349 case WM_LBUTTONDBLCLK
:
2350 case WM_MBUTTONDBLCLK
:
2351 case WM_RBUTTONDBLCLK
:
2352 case WM_XBUTTONDBLCLK
:
2355 case WM_MOUSEHWHEEL
:
2357 Result
= OnMouse(GuiData
, msg
, wParam
, lParam
);
2362 OnScroll(GuiData
, SB_HORZ
, LOWORD(wParam
));
2366 OnScroll(GuiData
, SB_VERT
, LOWORD(wParam
));
2369 case WM_CONTEXTMENU
:
2371 /* Do nothing if the window is hidden */
2372 if (!GuiData
->IsWindowVisible
) break;
2374 if (DefWindowProcW(hWnd
/*GuiData->hWindow*/, WM_NCHITTEST
, 0, lParam
) == HTCLIENT
)
2376 HMENU hMenu
= CreatePopupMenu();
2379 AppendMenuItems(hMenu
, GuiConsoleEditMenuItems
);
2380 TrackPopupMenuEx(hMenu
,
2382 GET_X_LPARAM(lParam
),
2383 GET_Y_LPARAM(lParam
),
2398 HMENU hMenu
= (HMENU
)wParam
;
2401 /* Enable or disable the Close menu item */
2402 EnableMenuItem(hMenu
, SC_CLOSE
, MF_BYCOMMAND
|
2403 (GuiData
->IsCloseButtonEnabled
? MF_ENABLED
: MF_GRAYED
));
2405 /* Enable or disable the Copy and Paste items */
2406 EnableMenuItem(hMenu
, ID_SYSTEM_EDIT_COPY
, MF_BYCOMMAND
|
2407 ((GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) &&
2408 (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
) ? MF_ENABLED
: MF_GRAYED
));
2409 // FIXME: Following whether the active screen buffer is text-mode
2410 // or graphics-mode, search for CF_UNICODETEXT or CF_BITMAP formats.
2411 EnableMenuItem(hMenu
, ID_SYSTEM_EDIT_PASTE
, MF_BYCOMMAND
|
2412 (!(GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) &&
2413 IsClipboardFormatAvailable(CF_UNICODETEXT
) ? MF_ENABLED
: MF_GRAYED
));
2416 SendMenuEvent(Console
, WM_INITMENU
);
2422 if (HIWORD(wParam
) == 0xFFFF) // Allow all the menu flags
2424 SendMenuEvent(Console
, WM_MENUSELECT
);
2432 Result
= OnCommand(GuiData
, wParam
, lParam
);
2437 OnDropFiles(Console
, (HDROP
)wParam
);
2442 OnFocus(GuiData
, (msg
== WM_SETFOCUS
));
2445 case WM_GETMINMAXINFO
:
2446 OnGetMinMaxInfo(GuiData
, (PMINMAXINFO
)lParam
);
2453 #if 0 // This code is here to prepare & control dynamic console SB resizing.
2456 PRECT dragRect
= (PRECT
)lParam
;
2460 DPRINT1("WMSZ_LEFT\n");
2463 DPRINT1("WMSZ_RIGHT\n");
2466 DPRINT1("WMSZ_TOP\n");
2469 DPRINT1("WMSZ_TOPLEFT\n");
2472 DPRINT1("WMSZ_TOPRIGHT\n");
2475 DPRINT1("WMSZ_BOTTOM\n");
2477 case WMSZ_BOTTOMLEFT
:
2478 DPRINT1("WMSZ_BOTTOMLEFT\n");
2480 case WMSZ_BOTTOMRIGHT
:
2481 DPRINT1("WMSZ_BOTTOMRIGHT\n");
2484 DPRINT1("wParam = %d\n", wParam
);
2487 DPRINT1("dragRect = {.left = %d ; .top = %d ; .right = %d ; .bottom = %d}\n",
2488 dragRect
->left
, dragRect
->top
, dragRect
->right
, dragRect
->bottom
);
2494 OnSize(GuiData
, wParam
, lParam
);
2497 case PM_RESIZE_TERMINAL
:
2499 PCONSOLE_SCREEN_BUFFER Buff
= GuiData
->ActiveBuffer
;
2503 DWORD Width
, Height
;
2504 UINT WidthUnit
, HeightUnit
;
2506 /* Do nothing if the window is hidden */
2507 if (!GuiData
->IsWindowVisible
) break;
2509 GetScreenBufferSizeUnits(Buff
, GuiData
, &WidthUnit
, &HeightUnit
);
2511 Width
= Buff
->ScreenBufferSize
.X
* WidthUnit
;
2512 Height
= Buff
->ScreenBufferSize
.Y
* HeightUnit
;
2514 /* Recreate the framebuffer */
2515 hDC
= GetDC(GuiData
->hWindow
);
2516 hnew
= CreateCompatibleBitmap(hDC
, Width
, Height
);
2517 ReleaseDC(GuiData
->hWindow
, hDC
);
2518 hold
= SelectObject(GuiData
->hMemDC
, hnew
);
2519 if (GuiData
->hBitmap
)
2521 if (hold
== GuiData
->hBitmap
) DeleteObject(GuiData
->hBitmap
);
2523 GuiData
->hBitmap
= hnew
;
2525 /* Resize the window to the user's values */
2526 GuiData
->WindowSizeLock
= TRUE
;
2527 ResizeConWnd(GuiData
, WidthUnit
, HeightUnit
);
2528 GuiData
->WindowSizeLock
= FALSE
;
2533 * Undocumented message sent by Windows' console.dll for applying console info.
2534 * See http://www.catch22.net/sites/default/source/files/setconsoleinfo.c
2535 * and http://www.scn.rain.com/~neighorn/PDF/MSBugPaper.pdf
2536 * for more information.
2538 case WM_SETCONSOLEINFO
:
2540 GuiApplyUserSettings(GuiData
, (HANDLE
)wParam
);
2544 case PM_CONSOLE_BEEP
:
2549 case PM_CONSOLE_SET_TITLE
:
2550 SetWindowTextW(GuiData
->hWindow
, GuiData
->Console
->Title
.Buffer
);
2554 Result
= DefWindowProcW(hWnd
, msg
, wParam
, lParam
);