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
= (HBRUSH
)GetStockObject(BLACK_BRUSH
); // The color of a terminal when it is switched off.
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 GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER
)ActiveBuffer
,
995 GuiData
, &ps
.rcPaint
, &rcPaint
);
997 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
999 GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER
)ActiveBuffer
,
1000 GuiData
, &ps
.rcPaint
, &rcPaint
);
1003 /* Send it to screen */
1007 rcPaint
.right
- rcPaint
.left
,
1008 rcPaint
.bottom
- rcPaint
.top
,
1014 /* Draw the selection region if needed */
1015 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
1017 PaintSelectionRect(GuiData
, &ps
);
1020 LeaveCriticalSection(&GuiData
->Lock
);
1022 EndPaint(GuiData
->hWindow
, &ps
);
1028 OnPaletteChanged(PGUI_CONSOLE_DATA GuiData
)
1030 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
1032 /* Do nothing if the window is hidden */
1033 if (!GuiData
->IsWindowVisible
) return;
1035 // See WM_PALETTECHANGED message
1036 // if ((HWND)wParam == hWnd) break;
1038 // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
1039 if (ActiveBuffer
->PaletteHandle
)
1041 DPRINT("WM_PALETTECHANGED changing palette\n");
1043 /* Specify the use of the system palette for the framebuffer */
1044 SetSystemPaletteUse(GuiData
->hMemDC
, ActiveBuffer
->PaletteUsage
);
1046 /* Realize the (logical) palette */
1047 RealizePalette(GuiData
->hMemDC
);
1052 IsSystemKey(WORD VirtualKeyCode
)
1054 switch (VirtualKeyCode
)
1056 /* From MSDN, "Virtual-Key Codes" */
1075 OnKey(PGUI_CONSOLE_DATA GuiData
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1077 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1078 PCONSOLE_SCREEN_BUFFER ActiveBuffer
;
1080 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
1082 ActiveBuffer
= GuiData
->ActiveBuffer
;
1084 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
)
1086 WORD VirtualKeyCode
= LOWORD(wParam
);
1088 if (msg
!= WM_KEYDOWN
) goto Quit
;
1090 if (VirtualKeyCode
== VK_RETURN
)
1092 /* Copy (and clear) selection if ENTER is pressed */
1096 else if ( VirtualKeyCode
== VK_ESCAPE
||
1097 (VirtualKeyCode
== 'C' && (GetKeyState(VK_CONTROL
) & KEY_PRESSED
)) )
1099 /* Cancel selection if ESC or Ctrl-C are pressed */
1100 UpdateSelection(GuiData
, NULL
, NULL
);
1104 if ((GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_SELECTION
) == 0)
1106 /* Keyboard selection mode */
1107 BOOL Interpreted
= FALSE
;
1108 BOOL MajPressed
= !!(GetKeyState(VK_SHIFT
) & KEY_PRESSED
);
1110 switch (VirtualKeyCode
)
1115 if (GuiData
->dwSelectionCursor
.X
> 0)
1116 GuiData
->dwSelectionCursor
.X
--;
1124 if (GuiData
->dwSelectionCursor
.X
< ActiveBuffer
->ScreenBufferSize
.X
- 1)
1125 GuiData
->dwSelectionCursor
.X
++;
1133 if (GuiData
->dwSelectionCursor
.Y
> 0)
1134 GuiData
->dwSelectionCursor
.Y
--;
1142 if (GuiData
->dwSelectionCursor
.Y
< ActiveBuffer
->ScreenBufferSize
.Y
- 1)
1143 GuiData
->dwSelectionCursor
.Y
++;
1151 GuiData
->dwSelectionCursor
.X
= 0;
1152 GuiData
->dwSelectionCursor
.Y
= 0;
1159 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->ScreenBufferSize
.Y
- 1;
1166 GuiData
->dwSelectionCursor
.Y
-= ActiveBuffer
->ViewSize
.Y
;
1167 if (GuiData
->dwSelectionCursor
.Y
< 0)
1168 GuiData
->dwSelectionCursor
.Y
= 0;
1176 GuiData
->dwSelectionCursor
.Y
+= ActiveBuffer
->ViewSize
.Y
;
1177 if (GuiData
->dwSelectionCursor
.Y
>= ActiveBuffer
->ScreenBufferSize
.Y
)
1178 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->ScreenBufferSize
.Y
- 1;
1189 UpdateSelection(GuiData
,
1190 !MajPressed
? &GuiData
->dwSelectionCursor
: NULL
,
1191 &GuiData
->dwSelectionCursor
);
1193 else if (!IsSystemKey(VirtualKeyCode
))
1195 /* Emit an error beep sound */
1196 SendNotifyMessage(GuiData
->hWindow
, PM_CONSOLE_BEEP
, 0, 0);
1203 /* Mouse selection mode */
1205 if (!IsSystemKey(VirtualKeyCode
))
1207 /* Clear the selection and send the key into the input buffer */
1208 UpdateSelection(GuiData
, NULL
, NULL
);
1217 if ((GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) == 0)
1221 Message
.hwnd
= GuiData
->hWindow
;
1222 Message
.message
= msg
;
1223 Message
.wParam
= wParam
;
1224 Message
.lParam
= lParam
;
1226 ConioProcessKey(Console
, &Message
);
1230 LeaveCriticalSection(&Console
->Lock
);
1234 // FIXME: Remove after fixing OnTimer
1236 InvalidateCell(PGUI_CONSOLE_DATA GuiData
,
1240 OnTimer(PGUI_CONSOLE_DATA GuiData
)
1242 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1243 PCONSOLE_SCREEN_BUFFER Buff
;
1245 /* Do nothing if the window is hidden */
1246 if (!GuiData
->IsWindowVisible
) return;
1248 SetTimer(GuiData
->hWindow
, CONGUI_UPDATE_TIMER
, CURSOR_BLINK_TIME
, NULL
);
1250 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
1252 Buff
= GuiData
->ActiveBuffer
;
1254 if (GetType(Buff
) == TEXTMODE_BUFFER
)
1256 InvalidateCell(GuiData
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
1257 Buff
->CursorBlinkOn
= !Buff
->CursorBlinkOn
;
1259 if ((GuiData
->OldCursor
.x
!= Buff
->CursorPosition
.X
) ||
1260 (GuiData
->OldCursor
.y
!= Buff
->CursorPosition
.Y
))
1263 int OldScrollX
= -1, OldScrollY
= -1;
1264 int NewScrollX
= -1, NewScrollY
= -1;
1266 sInfo
.cbSize
= sizeof(sInfo
);
1267 sInfo
.fMask
= SIF_POS
;
1268 // Capture the original position of the scroll bars and save them.
1269 if (GetScrollInfo(GuiData
->hWindow
, SB_HORZ
, &sInfo
)) OldScrollX
= sInfo
.nPos
;
1270 if (GetScrollInfo(GuiData
->hWindow
, SB_VERT
, &sInfo
)) OldScrollY
= sInfo
.nPos
;
1272 // If we successfully got the info for the horizontal scrollbar
1273 if (OldScrollX
>= 0)
1275 if ((Buff
->CursorPosition
.X
< Buff
->ViewOrigin
.X
) ||
1276 (Buff
->CursorPosition
.X
>= (Buff
->ViewOrigin
.X
+ Buff
->ViewSize
.X
)))
1278 // Handle the horizontal scroll bar
1279 if (Buff
->CursorPosition
.X
>= Buff
->ViewSize
.X
)
1280 NewScrollX
= Buff
->CursorPosition
.X
- Buff
->ViewSize
.X
+ 1;
1286 NewScrollX
= OldScrollX
;
1289 // If we successfully got the info for the vertical scrollbar
1290 if (OldScrollY
>= 0)
1292 if ((Buff
->CursorPosition
.Y
< Buff
->ViewOrigin
.Y
) ||
1293 (Buff
->CursorPosition
.Y
>= (Buff
->ViewOrigin
.Y
+ Buff
->ViewSize
.Y
)))
1295 // Handle the vertical scroll bar
1296 if (Buff
->CursorPosition
.Y
>= Buff
->ViewSize
.Y
)
1297 NewScrollY
= Buff
->CursorPosition
.Y
- Buff
->ViewSize
.Y
+ 1;
1303 NewScrollY
= OldScrollY
;
1307 // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area
1308 // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar
1309 // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling)
1310 // and their associated scrollbar is left alone.
1311 if ((OldScrollX
!= NewScrollX
) || (OldScrollY
!= NewScrollY
))
1313 Buff
->ViewOrigin
.X
= NewScrollX
;
1314 Buff
->ViewOrigin
.Y
= NewScrollY
;
1315 ScrollWindowEx(GuiData
->hWindow
,
1316 (OldScrollX
- NewScrollX
) * GuiData
->CharWidth
,
1317 (OldScrollY
- NewScrollY
) * GuiData
->CharHeight
,
1323 if (NewScrollX
>= 0)
1325 sInfo
.nPos
= NewScrollX
;
1326 SetScrollInfo(GuiData
->hWindow
, SB_HORZ
, &sInfo
, TRUE
);
1328 if (NewScrollY
>= 0)
1330 sInfo
.nPos
= NewScrollY
;
1331 SetScrollInfo(GuiData
->hWindow
, SB_VERT
, &sInfo
, TRUE
);
1333 UpdateWindow(GuiData
->hWindow
);
1334 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1335 GuiData
->OldCursor
.x
= Buff
->CursorPosition
.X
;
1336 GuiData
->OldCursor
.y
= Buff
->CursorPosition
.Y
;
1340 else /* if (GetType(Buff) == GRAPHICS_BUFFER) */
1344 LeaveCriticalSection(&Console
->Lock
);
1348 OnClose(PGUI_CONSOLE_DATA GuiData
)
1350 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1352 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
))
1355 // TODO: Prompt for termination ? (Warn the user about possible apps running in this console)
1358 * FIXME: Windows will wait up to 5 seconds for the thread to exit.
1359 * We shouldn't wait here, though, since the console lock is entered.
1360 * A copy of the thread list probably needs to be made.
1362 ConSrvConsoleProcessCtrlEvent(Console
, 0, CTRL_CLOSE_EVENT
);
1364 LeaveCriticalSection(&Console
->Lock
);
1369 OnNcDestroy(HWND hWnd
)
1371 PGUI_CONSOLE_DATA GuiData
= GuiGetGuiData(hWnd
);
1373 /* Free the GuiData registration */
1374 SetWindowLongPtrW(hWnd
, GWLP_USERDATA
, (DWORD_PTR
)NULL
);
1376 /* Reset the system menu back to default and destroy the previous menu */
1377 GetSystemMenu(hWnd
, TRUE
);
1381 if (GuiData
->IsWindowVisible
)
1382 KillTimer(hWnd
, CONGUI_UPDATE_TIMER
);
1384 /* Free the terminal framebuffer */
1385 if (GuiData
->hMemDC
) DeleteDC(GuiData
->hMemDC
);
1386 if (GuiData
->hBitmap
) DeleteObject(GuiData
->hBitmap
);
1387 // if (GuiData->hSysPalette) DeleteObject(GuiData->hSysPalette);
1388 DeleteFonts(GuiData
);
1391 return DefWindowProcW(hWnd
, WM_NCDESTROY
, 0, 0);
1395 OnScroll(PGUI_CONSOLE_DATA GuiData
, INT nBar
, WORD sbCode
)
1397 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1398 PCONSOLE_SCREEN_BUFFER Buff
;
1400 INT oldPos
, Maximum
;
1403 ASSERT(nBar
== SB_HORZ
|| nBar
== SB_VERT
);
1405 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
1407 Buff
= GuiData
->ActiveBuffer
;
1409 if (nBar
== SB_HORZ
)
1411 Maximum
= Buff
->ScreenBufferSize
.X
- Buff
->ViewSize
.X
;
1412 pOriginXY
= &Buff
->ViewOrigin
.X
;
1414 else // if (nBar == SB_VERT)
1416 Maximum
= Buff
->ScreenBufferSize
.Y
- Buff
->ViewSize
.Y
;
1417 pOriginXY
= &Buff
->ViewOrigin
.Y
;
1420 /* Set scrollbar sizes */
1421 sInfo
.cbSize
= sizeof(sInfo
);
1422 sInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
| SIF_TRACKPOS
;
1424 if (!GetScrollInfo(GuiData
->hWindow
, nBar
, &sInfo
)) goto Quit
;
1426 oldPos
= sInfo
.nPos
;
1430 case SB_LINEUP
: // SB_LINELEFT:
1434 case SB_LINEDOWN
: // SB_LINERIGHT:
1438 case SB_PAGEUP
: // SB_PAGELEFT:
1439 sInfo
.nPos
-= sInfo
.nPage
;
1442 case SB_PAGEDOWN
: // SB_PAGERIGHT:
1443 sInfo
.nPos
+= sInfo
.nPage
;
1447 sInfo
.nPos
= sInfo
.nTrackPos
;
1448 ConioPause(Console
, PAUSED_FROM_SCROLLBAR
);
1451 case SB_THUMBPOSITION
:
1452 sInfo
.nPos
= sInfo
.nTrackPos
;
1453 ConioUnpause(Console
, PAUSED_FROM_SCROLLBAR
);
1456 case SB_TOP
: // SB_LEFT:
1457 sInfo
.nPos
= sInfo
.nMin
;
1460 case SB_BOTTOM
: // SB_RIGHT:
1461 sInfo
.nPos
= sInfo
.nMax
;
1468 sInfo
.nPos
= min(max(sInfo
.nPos
, 0), Maximum
);
1470 if (oldPos
!= sInfo
.nPos
)
1472 USHORT OldX
= Buff
->ViewOrigin
.X
;
1473 USHORT OldY
= Buff
->ViewOrigin
.Y
;
1474 UINT WidthUnit
, HeightUnit
;
1476 /* We now modify Buff->ViewOrigin */
1477 *pOriginXY
= sInfo
.nPos
;
1479 GetScreenBufferSizeUnits(Buff
, GuiData
, &WidthUnit
, &HeightUnit
);
1481 ScrollWindowEx(GuiData
->hWindow
,
1482 (OldX
- Buff
->ViewOrigin
.X
) * WidthUnit
,
1483 (OldY
- Buff
->ViewOrigin
.Y
) * HeightUnit
,
1490 sInfo
.fMask
= SIF_POS
;
1491 SetScrollInfo(GuiData
->hWindow
, nBar
, &sInfo
, TRUE
);
1493 UpdateWindow(GuiData
->hWindow
);
1494 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1498 LeaveCriticalSection(&Console
->Lock
);
1503 PointToCoord(PGUI_CONSOLE_DATA GuiData
, LPARAM lParam
)
1505 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1507 UINT WidthUnit
, HeightUnit
;
1509 GetScreenBufferSizeUnits(Buffer
, GuiData
, &WidthUnit
, &HeightUnit
);
1511 Coord
.X
= Buffer
->ViewOrigin
.X
+ ((SHORT
)LOWORD(lParam
) / (int)WidthUnit
);
1512 Coord
.Y
= Buffer
->ViewOrigin
.Y
+ ((SHORT
)HIWORD(lParam
) / (int)HeightUnit
);
1514 /* Clip coordinate to ensure it's inside buffer */
1517 else if (Coord
.X
>= Buffer
->ScreenBufferSize
.X
)
1518 Coord
.X
= Buffer
->ScreenBufferSize
.X
- 1;
1522 else if (Coord
.Y
>= Buffer
->ScreenBufferSize
.Y
)
1523 Coord
.Y
= Buffer
->ScreenBufferSize
.Y
- 1;
1529 OnMouse(PGUI_CONSOLE_DATA GuiData
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1531 BOOL DoDefault
= FALSE
;
1532 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1535 * HACK FOR CORE-8394 (Part 2):
1537 * Check whether we should ignore the next mouse move event.
1538 * In either case we reset the HACK flag.
1540 * See Part 1 of this hack below.
1542 if (GuiData
->HackCORE8394IgnoreNextMove
&& msg
== WM_MOUSEMOVE
)
1544 GuiData
->HackCORE8394IgnoreNextMove
= FALSE
;
1547 GuiData
->HackCORE8394IgnoreNextMove
= FALSE
;
1549 // FIXME: It's here that we need to check whether we have focus or not
1550 // and whether we are or not in edit mode, in order to know if we need
1551 // to deal with the mouse.
1553 if (GuiData
->IgnoreNextMouseSignal
)
1555 if (msg
!= WM_LBUTTONDOWN
&&
1556 msg
!= WM_MBUTTONDOWN
&&
1557 msg
!= WM_RBUTTONDOWN
&&
1558 msg
!= WM_XBUTTONDOWN
)
1561 * If this mouse signal is not a button-down action
1562 * then this is the last one being ignored.
1564 GuiData
->IgnoreNextMouseSignal
= FALSE
;
1569 * This mouse signal is a button-down action.
1570 * Ignore it and perform default action.
1577 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
))
1583 if ( (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) ||
1584 (Console
->QuickEdit
) )
1588 case WM_LBUTTONDOWN
:
1590 /* Check for selection state */
1591 if ( (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) &&
1592 (GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_SELECTION
) &&
1593 (GetKeyState(VK_SHIFT
) & KEY_PRESSED
) )
1596 * A mouse selection is currently in progress and the user
1597 * has pressed the SHIFT key and clicked somewhere, update
1600 GuiData
->dwSelectionCursor
= PointToCoord(GuiData
, lParam
);
1601 UpdateSelection(GuiData
, NULL
, &GuiData
->dwSelectionCursor
);
1605 /* Clear the old selection */
1606 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
1608 /* Restart a new selection */
1609 GuiData
->dwSelectionCursor
= PointToCoord(GuiData
, lParam
);
1610 SetCapture(GuiData
->hWindow
);
1611 GuiData
->Selection
.dwFlags
|= CONSOLE_MOUSE_SELECTION
| CONSOLE_MOUSE_DOWN
;
1612 UpdateSelection(GuiData
,
1613 &GuiData
->dwSelectionCursor
,
1614 &GuiData
->dwSelectionCursor
);
1622 if (!(GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_DOWN
)) break;
1624 // GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1625 GuiData
->Selection
.dwFlags
&= ~CONSOLE_MOUSE_DOWN
;
1626 // UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1632 case WM_LBUTTONDBLCLK
:
1634 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1636 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
1638 #define IS_WORD_SEP(c) \
1639 ((c) == L'\0' || (c) == L' ' || (c) == L'\t' || (c) == L'\r' || (c) == L'\n')
1641 PTEXTMODE_SCREEN_BUFFER TextBuffer
= (PTEXTMODE_SCREEN_BUFFER
)Buffer
;
1643 PCHAR_INFO ptrL
, ptrR
;
1645 /* Starting point */
1646 cL
= cR
= PointToCoord(GuiData
, lParam
);
1647 ptrL
= ptrR
= ConioCoordToPointer(TextBuffer
, cL
.X
, cL
.Y
);
1649 /* Enlarge the selection by checking for whitespace */
1650 while ((0 < cL
.X
) && !IS_WORD_SEP(ptrL
->Char
.UnicodeChar
)
1651 && !IS_WORD_SEP((ptrL
-1)->Char
.UnicodeChar
))
1656 while ((cR
.X
< TextBuffer
->ScreenBufferSize
.X
- 1) &&
1657 !IS_WORD_SEP(ptrR
->Char
.UnicodeChar
) &&
1658 !IS_WORD_SEP((ptrR
+1)->Char
.UnicodeChar
))
1665 * Update the selection started with the single
1666 * left-click that preceded this double-click.
1668 GuiData
->Selection
.dwFlags
|= CONSOLE_MOUSE_SELECTION
| CONSOLE_MOUSE_DOWN
;
1669 UpdateSelection(GuiData
, &cL
, &cR
);
1671 /* Ignore the next mouse move signal */
1672 GuiData
->IgnoreNextMouseSignal
= TRUE
;
1679 case WM_RBUTTONDOWN
:
1680 case WM_RBUTTONDBLCLK
:
1682 if (!(GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
))
1691 /* Ignore the next mouse move signal */
1692 GuiData
->IgnoreNextMouseSignal
= TRUE
;
1698 if (!(GET_KEYSTATE_WPARAM(wParam
) & MK_LBUTTON
)) break;
1699 if (!(GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_DOWN
)) break;
1701 GuiData
->dwSelectionCursor
= PointToCoord(GuiData
, lParam
);
1702 UpdateSelection(GuiData
, NULL
, &GuiData
->dwSelectionCursor
);
1707 DoDefault
= TRUE
; // FALSE;
1711 else if (Console
->InputBuffer
.Mode
& ENABLE_MOUSE_INPUT
)
1714 WORD wKeyState
= GET_KEYSTATE_WPARAM(wParam
);
1715 DWORD dwButtonState
= 0;
1716 DWORD dwControlKeyState
= 0;
1717 DWORD dwEventFlags
= 0;
1721 case WM_LBUTTONDOWN
:
1722 SetCapture(GuiData
->hWindow
);
1723 dwButtonState
= FROM_LEFT_1ST_BUTTON_PRESSED
;
1727 case WM_MBUTTONDOWN
:
1728 SetCapture(GuiData
->hWindow
);
1729 dwButtonState
= FROM_LEFT_2ND_BUTTON_PRESSED
;
1733 case WM_RBUTTONDOWN
:
1734 SetCapture(GuiData
->hWindow
);
1735 dwButtonState
= RIGHTMOST_BUTTON_PRESSED
;
1739 case WM_XBUTTONDOWN
:
1741 /* Get which X-button was pressed */
1742 WORD wButton
= GET_XBUTTON_WPARAM(wParam
);
1744 /* Check for X-button validity */
1745 if (wButton
& ~(XBUTTON1
| XBUTTON2
))
1747 DPRINT1("X-button 0x%04x invalid\n", wButton
);
1752 SetCapture(GuiData
->hWindow
);
1753 dwButtonState
= (wButton
== XBUTTON1
? FROM_LEFT_3RD_BUTTON_PRESSED
1754 : FROM_LEFT_4TH_BUTTON_PRESSED
);
1779 /* Get which X-button was released */
1780 WORD wButton
= GET_XBUTTON_WPARAM(wParam
);
1782 /* Check for X-button validity */
1783 if (wButton
& ~(XBUTTON1
| XBUTTON2
))
1785 DPRINT1("X-button 0x%04x invalid\n", wButton
);
1786 /* Ok, just release the button anyway... */
1795 case WM_LBUTTONDBLCLK
:
1796 dwButtonState
= FROM_LEFT_1ST_BUTTON_PRESSED
;
1797 dwEventFlags
= DOUBLE_CLICK
;
1800 case WM_MBUTTONDBLCLK
:
1801 dwButtonState
= FROM_LEFT_2ND_BUTTON_PRESSED
;
1802 dwEventFlags
= DOUBLE_CLICK
;
1805 case WM_RBUTTONDBLCLK
:
1806 dwButtonState
= RIGHTMOST_BUTTON_PRESSED
;
1807 dwEventFlags
= DOUBLE_CLICK
;
1810 case WM_XBUTTONDBLCLK
:
1812 /* Get which X-button was double-clicked */
1813 WORD wButton
= GET_XBUTTON_WPARAM(wParam
);
1815 /* Check for X-button validity */
1816 if (wButton
& ~(XBUTTON1
| XBUTTON2
))
1818 DPRINT1("X-button 0x%04x invalid\n", wButton
);
1823 dwButtonState
= (wButton
== XBUTTON1
? FROM_LEFT_3RD_BUTTON_PRESSED
1824 : FROM_LEFT_4TH_BUTTON_PRESSED
);
1825 dwEventFlags
= DOUBLE_CLICK
;
1831 dwEventFlags
= MOUSE_MOVED
;
1835 dwButtonState
= GET_WHEEL_DELTA_WPARAM(wParam
) << 16;
1836 dwEventFlags
= MOUSE_WHEELED
;
1839 case WM_MOUSEHWHEEL
:
1840 dwButtonState
= GET_WHEEL_DELTA_WPARAM(wParam
) << 16;
1841 dwEventFlags
= MOUSE_HWHEELED
;
1850 * HACK FOR CORE-8394 (Part 1):
1852 * It appears that depending on which VM ReactOS runs, the next mouse
1853 * signal coming after a button-down action can be a mouse-move (e.g.
1854 * on VBox, whereas on QEMU it is not the case). However it is NOT a
1855 * rule, so that we cannot use the IgnoreNextMouseSignal flag to just
1856 * "ignore" the next mouse event, thinking it would always be a mouse-
1859 * To work around this problem (that should really be fixed in Win32k),
1860 * we use a second flag to ignore this possible next mouse move signal.
1864 case WM_LBUTTONDOWN
:
1865 case WM_MBUTTONDOWN
:
1866 case WM_RBUTTONDOWN
:
1867 case WM_XBUTTONDOWN
:
1868 GuiData
->HackCORE8394IgnoreNextMove
= TRUE
;
1875 if (wKeyState
& MK_LBUTTON
)
1876 dwButtonState
|= FROM_LEFT_1ST_BUTTON_PRESSED
;
1877 if (wKeyState
& MK_MBUTTON
)
1878 dwButtonState
|= FROM_LEFT_2ND_BUTTON_PRESSED
;
1879 if (wKeyState
& MK_RBUTTON
)
1880 dwButtonState
|= RIGHTMOST_BUTTON_PRESSED
;
1881 if (wKeyState
& MK_XBUTTON1
)
1882 dwButtonState
|= FROM_LEFT_3RD_BUTTON_PRESSED
;
1883 if (wKeyState
& MK_XBUTTON2
)
1884 dwButtonState
|= FROM_LEFT_4TH_BUTTON_PRESSED
;
1886 if (GetKeyState(VK_RMENU
) & KEY_PRESSED
)
1887 dwControlKeyState
|= RIGHT_ALT_PRESSED
;
1888 if (GetKeyState(VK_LMENU
) & KEY_PRESSED
)
1889 dwControlKeyState
|= LEFT_ALT_PRESSED
;
1890 if (GetKeyState(VK_RCONTROL
) & KEY_PRESSED
)
1891 dwControlKeyState
|= RIGHT_CTRL_PRESSED
;
1892 if (GetKeyState(VK_LCONTROL
) & KEY_PRESSED
)
1893 dwControlKeyState
|= LEFT_CTRL_PRESSED
;
1894 if (GetKeyState(VK_SHIFT
) & KEY_PRESSED
)
1895 dwControlKeyState
|= SHIFT_PRESSED
;
1896 if (GetKeyState(VK_NUMLOCK
) & KEY_TOGGLED
)
1897 dwControlKeyState
|= NUMLOCK_ON
;
1898 if (GetKeyState(VK_SCROLL
) & KEY_TOGGLED
)
1899 dwControlKeyState
|= SCROLLLOCK_ON
;
1900 if (GetKeyState(VK_CAPITAL
) & KEY_TOGGLED
)
1901 dwControlKeyState
|= CAPSLOCK_ON
;
1902 /* See WM_CHAR MSDN documentation for instance */
1903 if (lParam
& 0x01000000)
1904 dwControlKeyState
|= ENHANCED_KEY
;
1906 /* Send a mouse event */
1907 er
.EventType
= MOUSE_EVENT
;
1908 er
.Event
.MouseEvent
.dwMousePosition
= PointToCoord(GuiData
, lParam
);
1909 er
.Event
.MouseEvent
.dwButtonState
= dwButtonState
;
1910 er
.Event
.MouseEvent
.dwControlKeyState
= dwControlKeyState
;
1911 er
.Event
.MouseEvent
.dwEventFlags
= dwEventFlags
;
1913 ConioProcessInputEvent(Console
, &er
);
1921 LeaveCriticalSection(&Console
->Lock
);
1927 if (msg
== WM_MOUSEWHEEL
|| msg
== WM_MOUSEHWHEEL
)
1931 // WORD wKeyState = GET_KEYSTATE_WPARAM(wParam);
1932 SHORT wDelta
= GET_WHEEL_DELTA_WPARAM(wParam
);
1934 if (msg
== WM_MOUSEWHEEL
)
1936 else // if (msg == WM_MOUSEHWHEEL)
1939 // NOTE: We currently do not support zooming...
1940 // if (wKeyState & MK_CONTROL)
1942 // FIXME: For some reason our win32k does not set the key states
1943 // when sending WM_MOUSEWHEEL or WM_MOUSEHWHEEL ...
1944 // if (wKeyState & MK_SHIFT)
1945 if (GetKeyState(VK_SHIFT
) & KEY_PRESSED
)
1946 sbCode
= (wDelta
>= 0 ? SB_PAGEUP
: SB_PAGEDOWN
);
1948 sbCode
= (wDelta
>= 0 ? SB_LINEUP
: SB_LINEDOWN
);
1950 OnScroll(GuiData
, nBar
, sbCode
);
1953 return DefWindowProcW(GuiData
->hWindow
, msg
, wParam
, lParam
);
1958 Copy(PGUI_CONSOLE_DATA GuiData
)
1960 if (OpenClipboard(GuiData
->hWindow
))
1962 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1964 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
1966 GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER
)Buffer
, GuiData
);
1968 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1970 GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER
)Buffer
, GuiData
);
1976 /* Clear the selection */
1977 UpdateSelection(GuiData
, NULL
, NULL
);
1981 Paste(PGUI_CONSOLE_DATA GuiData
)
1983 if (OpenClipboard(GuiData
->hWindow
))
1985 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1987 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
1989 GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER
)Buffer
, GuiData
);
1991 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
1993 GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER
)Buffer
, GuiData
);
2001 OnGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData
, PMINMAXINFO minMaxInfo
)
2003 PCONSRV_CONSOLE Console
= GuiData
->Console
;
2004 PCONSOLE_SCREEN_BUFFER ActiveBuffer
;
2006 UINT WidthUnit
, HeightUnit
;
2008 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
2010 ActiveBuffer
= GuiData
->ActiveBuffer
;
2012 GetScreenBufferSizeUnits(ActiveBuffer
, GuiData
, &WidthUnit
, &HeightUnit
);
2014 windx
= CONGUI_MIN_WIDTH
* WidthUnit
+ 2 * (GetSystemMetrics(SM_CXFRAME
) + GetSystemMetrics(SM_CXEDGE
));
2015 windy
= CONGUI_MIN_HEIGHT
* HeightUnit
+ 2 * (GetSystemMetrics(SM_CYFRAME
) + GetSystemMetrics(SM_CYEDGE
)) + GetSystemMetrics(SM_CYCAPTION
);
2017 minMaxInfo
->ptMinTrackSize
.x
= windx
;
2018 minMaxInfo
->ptMinTrackSize
.y
= windy
;
2020 windx
= (ActiveBuffer
->ScreenBufferSize
.X
) * WidthUnit
+ 2 * (GetSystemMetrics(SM_CXFRAME
) + GetSystemMetrics(SM_CXEDGE
));
2021 windy
= (ActiveBuffer
->ScreenBufferSize
.Y
) * HeightUnit
+ 2 * (GetSystemMetrics(SM_CYFRAME
) + GetSystemMetrics(SM_CYEDGE
)) + GetSystemMetrics(SM_CYCAPTION
);
2023 if (ActiveBuffer
->ViewSize
.X
< ActiveBuffer
->ScreenBufferSize
.X
) windy
+= GetSystemMetrics(SM_CYHSCROLL
); // Window currently has a horizontal scrollbar
2024 if (ActiveBuffer
->ViewSize
.Y
< ActiveBuffer
->ScreenBufferSize
.Y
) windx
+= GetSystemMetrics(SM_CXVSCROLL
); // Window currently has a vertical scrollbar
2026 minMaxInfo
->ptMaxTrackSize
.x
= windx
;
2027 minMaxInfo
->ptMaxTrackSize
.y
= windy
;
2029 LeaveCriticalSection(&Console
->Lock
);
2033 OnSize(PGUI_CONSOLE_DATA GuiData
, WPARAM wParam
, LPARAM lParam
)
2035 PCONSRV_CONSOLE Console
= GuiData
->Console
;
2037 /* Do nothing if the window is hidden */
2038 if (!GuiData
->IsWindowVisible
) return;
2040 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
2042 if (!GuiData
->WindowSizeLock
&&
2043 (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
|| wParam
== SIZE_MINIMIZED
))
2045 PCONSOLE_SCREEN_BUFFER Buff
= GuiData
->ActiveBuffer
;
2046 DWORD windx
, windy
, charx
, chary
;
2047 UINT WidthUnit
, HeightUnit
;
2049 GetScreenBufferSizeUnits(Buff
, GuiData
, &WidthUnit
, &HeightUnit
);
2051 GuiData
->WindowSizeLock
= TRUE
;
2053 windx
= LOWORD(lParam
);
2054 windy
= HIWORD(lParam
);
2056 /* Compensate for existing scroll bars (because lParam values do not accommodate scroll bar) */
2057 if (Buff
->ViewSize
.X
< Buff
->ScreenBufferSize
.X
) windy
+= GetSystemMetrics(SM_CYHSCROLL
); // Window currently has a horizontal scrollbar
2058 if (Buff
->ViewSize
.Y
< Buff
->ScreenBufferSize
.Y
) windx
+= GetSystemMetrics(SM_CXVSCROLL
); // Window currently has a vertical scrollbar
2060 charx
= windx
/ (int)WidthUnit
;
2061 chary
= windy
/ (int)HeightUnit
;
2063 /* Character alignment (round size up or down) */
2064 if ((windx
% WidthUnit
) >= (WidthUnit
/ 2)) ++charx
;
2065 if ((windy
% HeightUnit
) >= (HeightUnit
/ 2)) ++chary
;
2067 /* Compensate for added scroll bars in window */
2068 if (charx
< (DWORD
)Buff
->ScreenBufferSize
.X
) windy
-= GetSystemMetrics(SM_CYHSCROLL
); // Window will have a horizontal scroll bar
2069 if (chary
< (DWORD
)Buff
->ScreenBufferSize
.Y
) windx
-= GetSystemMetrics(SM_CXVSCROLL
); // Window will have a vertical scroll bar
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
;
2079 if ((charx
!= Buff
->ViewSize
.X
) || (chary
!= Buff
->ViewSize
.Y
))
2081 Buff
->ViewSize
.X
= (charx
<= (DWORD
)Buff
->ScreenBufferSize
.X
) ? charx
: Buff
->ScreenBufferSize
.X
;
2082 Buff
->ViewSize
.Y
= (chary
<= (DWORD
)Buff
->ScreenBufferSize
.Y
) ? chary
: Buff
->ScreenBufferSize
.Y
;
2085 ResizeConWnd(GuiData
, WidthUnit
, HeightUnit
);
2087 /* Adjust the start of the visible area if we are attempting to show nonexistent areas */
2088 if ((Buff
->ScreenBufferSize
.X
- Buff
->ViewOrigin
.X
) < Buff
->ViewSize
.X
) Buff
->ViewOrigin
.X
= Buff
->ScreenBufferSize
.X
- Buff
->ViewSize
.X
;
2089 if ((Buff
->ScreenBufferSize
.Y
- Buff
->ViewOrigin
.Y
) < Buff
->ViewSize
.Y
) Buff
->ViewOrigin
.Y
= Buff
->ScreenBufferSize
.Y
- Buff
->ViewSize
.Y
;
2090 InvalidateRect(GuiData
->hWindow
, NULL
, TRUE
);
2092 GuiData
->WindowSizeLock
= FALSE
;
2095 LeaveCriticalSection(&Console
->Lock
);
2099 OnMove(PGUI_CONSOLE_DATA GuiData
)
2103 // TODO: Simplify the code.
2104 // See: GuiConsoleNotifyWndProc() PM_CREATE_CONSOLE.
2106 /* Retrieve our real position */
2107 GetWindowRect(GuiData
->hWindow
, &rcWnd
);
2108 GuiData
->GuiInfo
.WindowOrigin
.x
= rcWnd
.left
;
2109 GuiData
->GuiInfo
.WindowOrigin
.y
= rcWnd
.top
;
2113 OnDropFiles(PCONSRV_CONSOLE Console
, HDROP hDrop
)
2116 WCHAR szPath
[MAX_PATH
+ 2];
2120 DragQueryFileW(hDrop
, 0, &szPath
[1], ARRAYSIZE(szPath
) - 1);
2123 if (wcschr(&szPath
[1], L
' ') != NULL
)
2125 StringCchCatW(szPath
, ARRAYSIZE(szPath
), L
"\"");
2130 pszPath
= &szPath
[1];
2133 PasteText(Console
, pszPath
, wcslen(pszPath
));
2137 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
2140 GuiConsoleHandleScrollbarMenu(VOID)
2144 hMenu = CreatePopupMenu();
2147 DPRINT("CreatePopupMenu failed\n");
2151 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
2152 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2153 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
2154 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
2155 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2156 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
2157 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
2158 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2159 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
2160 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
2164 static LRESULT CALLBACK
2165 ConWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2168 PGUI_CONSOLE_DATA GuiData
= NULL
;
2169 PCONSRV_CONSOLE Console
= NULL
;
2172 * - If it's the first time we create a window for the terminal,
2173 * just initialize it and return.
2175 * - If we are destroying the window, just do it and return.
2177 if (msg
== WM_NCCREATE
)
2179 return (LRESULT
)OnNcCreate(hWnd
, (LPCREATESTRUCTW
)lParam
);
2181 else if (msg
== WM_NCDESTROY
)
2183 return OnNcDestroy(hWnd
);
2187 * Now the terminal window is initialized.
2188 * Get the terminal data via the window's data.
2189 * If there is no data, just go away.
2191 GuiData
= GuiGetGuiData(hWnd
);
2192 if (GuiData
== NULL
) return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
2194 // TEMPORARY HACK until all of the functions can deal with a NULL GuiData->ActiveBuffer ...
2195 if (GuiData
->ActiveBuffer
== NULL
) return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
2198 * Just retrieve a pointer to the console in case somebody needs it.
2199 * It is not NULL because it was checked in GuiGetGuiData.
2200 * Each helper function which needs the console has to validate and lock it.
2202 Console
= GuiData
->Console
;
2204 /* We have a console, start message dispatching */
2208 OnActivate(GuiData
, wParam
);
2212 if (OnClose(GuiData
)) goto Default
;
2223 case WM_PALETTECHANGED
:
2225 DPRINT("WM_PALETTECHANGED called\n");
2228 * Protects against infinite loops:
2229 * "... A window that receives this message must not realize
2230 * its palette, unless it determines that wParam does not contain
2231 * its own window handle." (WM_PALETTECHANGED description - MSDN)
2233 * This message is sent to all windows, including the one that
2234 * changed the system palette and caused this message to be sent.
2235 * The wParam of this message contains the handle of the window
2236 * that caused the system palette to change. To avoid an infinite
2237 * loop, care must be taken to check that the wParam of this message
2238 * does not match the window's handle.
2240 if ((HWND
)wParam
== hWnd
) break;
2242 DPRINT("WM_PALETTECHANGED ok\n");
2243 OnPaletteChanged(GuiData
);
2244 DPRINT("WM_PALETTECHANGED quit\n");
2255 case WM_SYSDEADCHAR
:
2257 /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */
2258 if (msg
== WM_SYSKEYDOWN
&& (HIWORD(lParam
) & KF_ALTDOWN
) && wParam
== VK_RETURN
)
2260 /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */
2261 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
2262 GuiConsoleSwitchFullScreen(GuiData
);
2266 /* Detect Alt-Esc/Space/Tab presses defer to DefWindowProc */
2267 if ( (HIWORD(lParam
) & KF_ALTDOWN
) && (wParam
== VK_ESCAPE
|| wParam
== VK_SPACE
|| wParam
== VK_TAB
))
2269 return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
2272 OnKey(GuiData
, msg
, wParam
, lParam
);
2278 /* Do nothing if the window is hidden */
2279 if (!GuiData
->IsWindowVisible
) goto Default
;
2282 * The message was sent because we are manually triggering a change.
2283 * Check whether the mouse is indeed present on this console window
2284 * and take appropriate decisions.
2286 if (wParam
== -1 && lParam
== -1)
2291 /* Get the placement of the mouse */
2292 GetCursorPos(&mouseCoords
);
2294 /* On which window is placed the mouse ? */
2295 hWndHit
= WindowFromPoint(mouseCoords
);
2297 /* It's our window. Perform the hit-test to be used later on. */
2298 if (hWndHit
== hWnd
)
2300 wParam
= (WPARAM
)hWnd
;
2301 lParam
= DefWindowProcW(hWndHit
, WM_NCHITTEST
, 0,
2302 MAKELPARAM(mouseCoords
.x
, mouseCoords
.y
));
2306 /* Set the mouse cursor only when we are in the client area */
2307 if ((HWND
)wParam
== hWnd
&& LOWORD(lParam
) == HTCLIENT
)
2309 if (GuiData
->MouseCursorRefCount
>= 0)
2311 /* Show the cursor */
2312 SetCursor(GuiData
->hCursor
);
2316 /* Hide the cursor if the reference count is negative */
2327 case WM_LBUTTONDOWN
:
2328 case WM_MBUTTONDOWN
:
2329 case WM_RBUTTONDOWN
:
2330 case WM_XBUTTONDOWN
:
2335 case WM_LBUTTONDBLCLK
:
2336 case WM_MBUTTONDBLCLK
:
2337 case WM_RBUTTONDBLCLK
:
2338 case WM_XBUTTONDBLCLK
:
2341 case WM_MOUSEHWHEEL
:
2343 Result
= OnMouse(GuiData
, msg
, wParam
, lParam
);
2348 OnScroll(GuiData
, SB_HORZ
, LOWORD(wParam
));
2352 OnScroll(GuiData
, SB_VERT
, LOWORD(wParam
));
2355 case WM_CONTEXTMENU
:
2357 /* Do nothing if the window is hidden */
2358 if (!GuiData
->IsWindowVisible
) break;
2360 if (DefWindowProcW(hWnd
/*GuiData->hWindow*/, WM_NCHITTEST
, 0, lParam
) == HTCLIENT
)
2362 HMENU hMenu
= CreatePopupMenu();
2365 AppendMenuItems(hMenu
, GuiConsoleEditMenuItems
);
2366 TrackPopupMenuEx(hMenu
,
2368 GET_X_LPARAM(lParam
),
2369 GET_Y_LPARAM(lParam
),
2384 HMENU hMenu
= (HMENU
)wParam
;
2387 /* Enable or disable the Close menu item */
2388 EnableMenuItem(hMenu
, SC_CLOSE
, MF_BYCOMMAND
|
2389 (GuiData
->IsCloseButtonEnabled
? MF_ENABLED
: MF_GRAYED
));
2391 /* Enable or disable the Copy and Paste items */
2392 EnableMenuItem(hMenu
, ID_SYSTEM_EDIT_COPY
, MF_BYCOMMAND
|
2393 ((GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) &&
2394 (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
) ? MF_ENABLED
: MF_GRAYED
));
2395 // FIXME: Following whether the active screen buffer is text-mode
2396 // or graphics-mode, search for CF_UNICODETEXT or CF_BITMAP formats.
2397 EnableMenuItem(hMenu
, ID_SYSTEM_EDIT_PASTE
, MF_BYCOMMAND
|
2398 (!(GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) &&
2399 IsClipboardFormatAvailable(CF_UNICODETEXT
) ? MF_ENABLED
: MF_GRAYED
));
2402 SendMenuEvent(Console
, WM_INITMENU
);
2408 if (HIWORD(wParam
) == 0xFFFF) // Allow all the menu flags
2410 SendMenuEvent(Console
, WM_MENUSELECT
);
2418 Result
= OnCommand(GuiData
, wParam
, lParam
);
2423 OnDropFiles(Console
, (HDROP
)wParam
);
2428 OnFocus(GuiData
, (msg
== WM_SETFOCUS
));
2431 case WM_GETMINMAXINFO
:
2432 OnGetMinMaxInfo(GuiData
, (PMINMAXINFO
)lParam
);
2439 #if 0 // This code is here to prepare & control dynamic console SB resizing.
2442 PRECT dragRect
= (PRECT
)lParam
;
2446 DPRINT1("WMSZ_LEFT\n");
2449 DPRINT1("WMSZ_RIGHT\n");
2452 DPRINT1("WMSZ_TOP\n");
2455 DPRINT1("WMSZ_TOPLEFT\n");
2458 DPRINT1("WMSZ_TOPRIGHT\n");
2461 DPRINT1("WMSZ_BOTTOM\n");
2463 case WMSZ_BOTTOMLEFT
:
2464 DPRINT1("WMSZ_BOTTOMLEFT\n");
2466 case WMSZ_BOTTOMRIGHT
:
2467 DPRINT1("WMSZ_BOTTOMRIGHT\n");
2470 DPRINT1("wParam = %d\n", wParam
);
2473 DPRINT1("dragRect = {.left = %d ; .top = %d ; .right = %d ; .bottom = %d}\n",
2474 dragRect
->left
, dragRect
->top
, dragRect
->right
, dragRect
->bottom
);
2480 OnSize(GuiData
, wParam
, lParam
);
2483 case PM_RESIZE_TERMINAL
:
2485 PCONSOLE_SCREEN_BUFFER Buff
= GuiData
->ActiveBuffer
;
2489 DWORD Width
, Height
;
2490 UINT WidthUnit
, HeightUnit
;
2492 /* Do nothing if the window is hidden */
2493 if (!GuiData
->IsWindowVisible
) break;
2495 GetScreenBufferSizeUnits(Buff
, GuiData
, &WidthUnit
, &HeightUnit
);
2497 Width
= Buff
->ScreenBufferSize
.X
* WidthUnit
;
2498 Height
= Buff
->ScreenBufferSize
.Y
* HeightUnit
;
2500 /* Recreate the framebuffer */
2501 hDC
= GetDC(GuiData
->hWindow
);
2502 hnew
= CreateCompatibleBitmap(hDC
, Width
, Height
);
2503 ReleaseDC(GuiData
->hWindow
, hDC
);
2504 hold
= SelectObject(GuiData
->hMemDC
, hnew
);
2505 if (GuiData
->hBitmap
)
2507 if (hold
== GuiData
->hBitmap
) DeleteObject(GuiData
->hBitmap
);
2509 GuiData
->hBitmap
= hnew
;
2511 /* Resize the window to the user's values */
2512 GuiData
->WindowSizeLock
= TRUE
;
2513 ResizeConWnd(GuiData
, WidthUnit
, HeightUnit
);
2514 GuiData
->WindowSizeLock
= FALSE
;
2519 * Undocumented message sent by Windows' console.dll for applying console info.
2520 * See http://www.catch22.net/sites/default/source/files/setconsoleinfo.c
2521 * and http://www.scn.rain.com/~neighorn/PDF/MSBugPaper.pdf
2522 * for more information.
2524 case WM_SETCONSOLEINFO
:
2526 GuiApplyUserSettings(GuiData
, (HANDLE
)wParam
);
2530 case PM_CONSOLE_BEEP
:
2535 case PM_CONSOLE_SET_TITLE
:
2536 SetWindowTextW(GuiData
->hWindow
, GuiData
->Console
->Title
.Buffer
);
2540 Result
= DefWindowProcW(hWnd
, msg
, wParam
, lParam
);