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
;
52 ProcessData
= ConSrvGetConsoleLeaderProcess(GuiData
->Console
);
54 ASSERT(ProcessData
!= NULL
);
55 DPRINT("ProcessData: %p, ProcessData->Process %p.\n", ProcessData
, ProcessData
->Process
);
57 if (ProcessData
->Process
)
59 CLIENT_ID ConsoleLeaderCID
= ProcessData
->Process
->ClientId
;
60 SetWindowLongPtrW(GuiData
->hWindow
, GWLP_CONSOLE_LEADER_PID
,
61 (LONG_PTR
)(ConsoleLeaderCID
.UniqueProcess
));
62 SetWindowLongPtrW(GuiData
->hWindow
, GWLP_CONSOLE_LEADER_TID
,
63 (LONG_PTR
)(ConsoleLeaderCID
.UniqueThread
));
67 SetWindowLongPtrW(GuiData
->hWindow
, GWLP_CONSOLE_LEADER_PID
, 0);
68 SetWindowLongPtrW(GuiData
->hWindow
, GWLP_CONSOLE_LEADER_TID
, 0);
71 /**************************************************************/
73 HICON ghDefaultIcon
= NULL
;
74 HICON ghDefaultIconSm
= NULL
;
75 HCURSOR ghDefaultCursor
= NULL
;
77 typedef struct _GUICONSOLE_MENUITEM
80 const struct _GUICONSOLE_MENUITEM
*SubMenu
;
82 } GUICONSOLE_MENUITEM
, *PGUICONSOLE_MENUITEM
;
84 static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems
[] =
86 { IDS_MARK
, NULL
, ID_SYSTEM_EDIT_MARK
},
87 { IDS_COPY
, NULL
, ID_SYSTEM_EDIT_COPY
},
88 { IDS_PASTE
, NULL
, ID_SYSTEM_EDIT_PASTE
},
89 { IDS_SELECTALL
, NULL
, ID_SYSTEM_EDIT_SELECTALL
},
90 { IDS_SCROLL
, NULL
, ID_SYSTEM_EDIT_SCROLL
},
91 { IDS_FIND
, NULL
, ID_SYSTEM_EDIT_FIND
},
93 { 0, NULL
, 0 } /* End of list */
96 static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems
[] =
98 { IDS_EDIT
, GuiConsoleEditMenuItems
, 0 },
99 { IDS_DEFAULTS
, NULL
, ID_SYSTEM_DEFAULTS
},
100 { IDS_PROPERTIES
, NULL
, ID_SYSTEM_PROPERTIES
},
102 { 0, NULL
, 0 } /* End of list */
106 * Default 16-color palette for foreground and background
107 * (corresponding flags in comments).
109 const COLORREF s_Colors
[16] =
111 RGB(0, 0, 0), // (Black)
112 RGB(0, 0, 128), // BLUE
113 RGB(0, 128, 0), // GREEN
114 RGB(0, 128, 128), // BLUE | GREEN
115 RGB(128, 0, 0), // RED
116 RGB(128, 0, 128), // BLUE | RED
117 RGB(128, 128, 0), // GREEN | RED
118 RGB(192, 192, 192), // BLUE | GREEN | RED
120 RGB(128, 128, 128), // (Grey) INTENSITY
121 RGB(0, 0, 255), // BLUE | INTENSITY
122 RGB(0, 255, 0), // GREEN | INTENSITY
123 RGB(0, 255, 255), // BLUE | GREEN | INTENSITY
124 RGB(255, 0, 0), // RED | INTENSITY
125 RGB(255, 0, 255), // BLUE | RED | INTENSITY
126 RGB(255, 255, 0), // GREEN | RED | INTENSITY
127 RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY
130 /* FUNCTIONS ******************************************************************/
132 static LRESULT CALLBACK
133 ConWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
136 RegisterConWndClass(IN HINSTANCE hInstance
)
138 WNDCLASSEXW WndClass
;
141 ghDefaultIcon
= LoadImageW(hInstance
,
142 MAKEINTRESOURCEW(IDI_TERMINAL
),
144 GetSystemMetrics(SM_CXICON
),
145 GetSystemMetrics(SM_CYICON
),
147 ghDefaultIconSm
= LoadImageW(hInstance
,
148 MAKEINTRESOURCEW(IDI_TERMINAL
),
150 GetSystemMetrics(SM_CXSMICON
),
151 GetSystemMetrics(SM_CYSMICON
),
153 ghDefaultCursor
= LoadCursorW(NULL
, MAKEINTRESOURCEW(IDC_ARROW
));
155 WndClass
.cbSize
= sizeof(WNDCLASSEXW
);
156 WndClass
.lpszClassName
= GUI_CONWND_CLASS
;
157 WndClass
.lpfnWndProc
= ConWndProc
;
158 WndClass
.style
= CS_DBLCLKS
/* | CS_HREDRAW | CS_VREDRAW */;
159 WndClass
.hInstance
= hInstance
;
160 WndClass
.hIcon
= ghDefaultIcon
;
161 WndClass
.hIconSm
= ghDefaultIconSm
;
162 WndClass
.hCursor
= ghDefaultCursor
;
163 WndClass
.hbrBackground
= NULL
;
164 WndClass
.lpszMenuName
= NULL
;
165 WndClass
.cbClsExtra
= 0;
166 WndClass
.cbWndExtra
= GWLP_CONWND_ALLOC
;
168 WndClassAtom
= RegisterClassExW(&WndClass
);
169 if (WndClassAtom
== 0)
171 DPRINT1("Failed to register GUI console class\n");
175 NtUserConsoleControl(GuiConsoleWndClassAtom
, &WndClassAtom
, sizeof(ATOM
));
178 return (WndClassAtom
!= 0);
182 UnRegisterConWndClass(HINSTANCE hInstance
)
184 return !!UnregisterClassW(GUI_CONWND_CLASS
, hInstance
);
188 AppendMenuItems(HMENU hMenu
,
189 const GUICONSOLE_MENUITEM
*Items
)
192 WCHAR szMenuString
[255];
197 if (Items
[i
].uID
!= (UINT
)-1)
199 if (LoadStringW(ConSrvDllInstance
,
202 ARRAYSIZE(szMenuString
)) > 0)
204 if (Items
[i
].SubMenu
!= NULL
)
206 hSubMenu
= CreatePopupMenu();
207 if (hSubMenu
!= NULL
)
209 AppendMenuItems(hSubMenu
, Items
[i
].SubMenu
);
211 if (!AppendMenuW(hMenu
,
212 MF_STRING
| MF_POPUP
,
216 DestroyMenu(hSubMenu
);
237 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
242 CreateSysMenu(HWND hWnd
)
247 WCHAR szMenuStringBack
[255];
249 hMenu
= GetSystemMenu(hWnd
, FALSE
);
253 mii
.cbSize
= sizeof(mii
);
254 mii
.fMask
= MIIM_STRING
;
255 mii
.dwTypeData
= szMenuStringBack
;
256 mii
.cch
= ARRAYSIZE(szMenuStringBack
);
258 GetMenuItemInfoW(hMenu
, SC_CLOSE
, FALSE
, &mii
);
260 ptrTab
= wcschr(szMenuStringBack
, L
'\t');
264 mii
.cch
= (UINT
)wcslen(szMenuStringBack
);
266 SetMenuItemInfoW(hMenu
, SC_CLOSE
, FALSE
, &mii
);
269 AppendMenuItems(hMenu
, GuiConsoleMainMenuItems
);
274 SendMenuEvent(PCONSRV_CONSOLE Console
, UINT CmdId
)
278 DPRINT("Menu item ID: %d\n", CmdId
);
280 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
282 /* Send a menu event */
283 er
.EventType
= MENU_EVENT
;
284 er
.Event
.MenuEvent
.dwCommandId
= CmdId
;
285 ConioProcessInputEvent(Console
, &er
);
287 LeaveCriticalSection(&Console
->Lock
);
291 Copy(PGUI_CONSOLE_DATA GuiData
);
293 Paste(PGUI_CONSOLE_DATA GuiData
);
295 UpdateSelection(PGUI_CONSOLE_DATA GuiData
,
296 PCOORD SelectionAnchor OPTIONAL
,
300 Mark(PGUI_CONSOLE_DATA GuiData
)
302 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
304 /* Clear the old selection */
305 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
307 /* Restart a new selection */
308 GuiData
->dwSelectionCursor
= ActiveBuffer
->ViewOrigin
;
309 UpdateSelection(GuiData
,
310 &GuiData
->dwSelectionCursor
,
311 &GuiData
->dwSelectionCursor
);
315 SelectAll(PGUI_CONSOLE_DATA GuiData
)
317 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
318 COORD SelectionAnchor
;
320 /* Clear the old selection */
321 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
324 * The selection area extends to the whole screen buffer's width.
326 SelectionAnchor
.X
= SelectionAnchor
.Y
= 0;
327 GuiData
->dwSelectionCursor
.X
= ActiveBuffer
->ScreenBufferSize
.X
- 1;
330 * Determine whether the selection must extend to just some part
331 * (for text-mode screen buffers) or to all of the screen buffer's
332 * height (for graphics ones).
334 if (GetType(ActiveBuffer
) == TEXTMODE_BUFFER
)
337 * We select all the characters from the first line
338 * to the line where the cursor is positioned.
340 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->CursorPosition
.Y
;
342 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
345 * We select all the screen buffer area.
347 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->ScreenBufferSize
.Y
- 1;
350 /* Restart a new selection */
351 GuiData
->Selection
.dwFlags
|= CONSOLE_MOUSE_SELECTION
;
352 UpdateSelection(GuiData
, &SelectionAnchor
, &GuiData
->dwSelectionCursor
);
356 OnCommand(PGUI_CONSOLE_DATA GuiData
, WPARAM wParam
, LPARAM lParam
)
359 PCONSRV_CONSOLE Console
= GuiData
->Console
;
362 * In case the selected menu item belongs to the user-reserved menu id range,
363 * send to him a menu event and return directly. The user must handle those
364 * reserved menu commands...
366 if (GuiData
->CmdIdLow
<= (UINT
)wParam
&& (UINT
)wParam
<= GuiData
->CmdIdHigh
)
368 SendMenuEvent(Console
, (UINT
)wParam
);
372 /* ... otherwise, perform actions. */
375 case ID_SYSTEM_EDIT_MARK
:
379 case ID_SYSTEM_EDIT_COPY
:
383 case ID_SYSTEM_EDIT_PASTE
:
387 case ID_SYSTEM_EDIT_SELECTALL
:
391 case ID_SYSTEM_EDIT_SCROLL
:
392 DPRINT1("Scrolling is not handled yet\n");
395 case ID_SYSTEM_EDIT_FIND
:
396 DPRINT1("Finding is not handled yet\n");
399 case ID_SYSTEM_DEFAULTS
:
400 GuiConsoleShowConsoleProperties(GuiData
, TRUE
);
403 case ID_SYSTEM_PROPERTIES
:
404 GuiConsoleShowConsoleProperties(GuiData
, FALSE
);
414 Ret
= DefWindowProcW(GuiData
->hWindow
, WM_SYSCOMMAND
, wParam
, lParam
);
419 static PGUI_CONSOLE_DATA
420 GuiGetGuiData(HWND hWnd
)
422 /* This function ensures that the console pointer is not NULL */
423 PGUI_CONSOLE_DATA GuiData
= (PGUI_CONSOLE_DATA
)GetWindowLongPtrW(hWnd
, GWLP_USERDATA
);
424 return ( ((GuiData
== NULL
) || (GuiData
->hWindow
== hWnd
&& GuiData
->Console
!= NULL
)) ? GuiData
: NULL
);
428 ResizeConWnd(PGUI_CONSOLE_DATA GuiData
, DWORD WidthUnit
, DWORD HeightUnit
)
430 PCONSOLE_SCREEN_BUFFER Buff
= GuiData
->ActiveBuffer
;
435 Width
= Buff
->ViewSize
.X
* WidthUnit
+
436 2 * (GetSystemMetrics(SM_CXFRAME
) + GetSystemMetrics(SM_CXEDGE
));
437 Height
= Buff
->ViewSize
.Y
* HeightUnit
+
438 2 * (GetSystemMetrics(SM_CYFRAME
) + GetSystemMetrics(SM_CYEDGE
)) + GetSystemMetrics(SM_CYCAPTION
);
440 /* Set scrollbar sizes */
441 sInfo
.cbSize
= sizeof(sInfo
);
442 sInfo
.fMask
= SIF_RANGE
| SIF_PAGE
| SIF_POS
;
444 if (Buff
->ScreenBufferSize
.Y
> Buff
->ViewSize
.Y
)
446 sInfo
.nMax
= Buff
->ScreenBufferSize
.Y
- 1;
447 sInfo
.nPage
= Buff
->ViewSize
.Y
;
448 sInfo
.nPos
= Buff
->ViewOrigin
.Y
;
449 SetScrollInfo(GuiData
->hWindow
, SB_VERT
, &sInfo
, TRUE
);
450 Width
+= GetSystemMetrics(SM_CXVSCROLL
);
451 ShowScrollBar(GuiData
->hWindow
, SB_VERT
, TRUE
);
455 ShowScrollBar(GuiData
->hWindow
, SB_VERT
, FALSE
);
458 if (Buff
->ScreenBufferSize
.X
> Buff
->ViewSize
.X
)
460 sInfo
.nMax
= Buff
->ScreenBufferSize
.X
- 1;
461 sInfo
.nPage
= Buff
->ViewSize
.X
;
462 sInfo
.nPos
= Buff
->ViewOrigin
.X
;
463 SetScrollInfo(GuiData
->hWindow
, SB_HORZ
, &sInfo
, TRUE
);
464 Height
+= GetSystemMetrics(SM_CYHSCROLL
);
465 ShowScrollBar(GuiData
->hWindow
, SB_HORZ
, TRUE
);
469 ShowScrollBar(GuiData
->hWindow
, SB_HORZ
, FALSE
);
472 /* Resize the window */
473 SetWindowPos(GuiData
->hWindow
, NULL
, 0, 0, Width
, Height
,
474 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOACTIVATE
| SWP_NOCOPYBITS
);
475 // NOTE: The SWP_NOCOPYBITS flag can be replaced by a subsequent call
476 // to: InvalidateRect(GuiData->hWindow, NULL, TRUE);
481 DeleteFonts(PGUI_CONSOLE_DATA GuiData
)
484 for (i
= 0; i
< ARRAYSIZE(GuiData
->Font
); ++i
)
486 if (GuiData
->Font
[i
] != NULL
) DeleteObject(GuiData
->Font
[i
]);
487 GuiData
->Font
[i
] = NULL
;
492 CreateDerivedFont(HFONT OrgFont
,
501 /* Initialize the LOGFONT structure */
502 RtlZeroMemory(&lf
, sizeof(lf
));
504 /* Retrieve the details of the current font */
505 if (GetObjectW(OrgFont
, sizeof(lf
), &lf
) == 0)
508 /* Change the font attributes */
509 // lf.lfHeight = FontSize.Y;
510 // lf.lfWidth = FontSize.X;
511 lf
.lfWeight
= FontWeight
;
512 // lf.lfItalic = bItalic;
513 lf
.lfUnderline
= bUnderline
;
514 lf
.lfStrikeOut
= bStrikeOut
;
516 /* Build a new font */
517 return CreateFontIndirectW(&lf
);
522 _Inout_ PGUI_CONSOLE_DATA GuiData
,
523 _In_reads_or_z_(LF_FACESIZE
)
525 _In_ ULONG FontWeight
,
526 _In_ ULONG FontFamily
,
528 _In_opt_ UINT CodePage
,
529 _In_ BOOL UseDefaultFallback
)
534 UINT OldCharWidth
= GuiData
->CharWidth
;
535 UINT OldCharHeight
= GuiData
->CharHeight
;
536 COORD OldFontSize
= GuiData
->GuiInfo
.FontSize
;
537 WCHAR NewFaceName
[LF_FACESIZE
];
539 /* Default to current code page if none has been provided */
541 CodePage
= GuiData
->Console
->OutputCodePage
;
544 * Initialize a new NORMAL font.
547 /* Copy the requested face name into the local buffer.
548 * It will be modified in output by CreateConsoleFontEx()
549 * to hold a possible fallback font face name. */
550 StringCchCopyNW(NewFaceName
, ARRAYSIZE(NewFaceName
),
551 FaceName
, LF_FACESIZE
);
553 /* NOTE: FontSize is always in cell height/width units (pixels) */
554 hFont
= CreateConsoleFontEx((LONG
)(ULONG
)FontSize
.Y
,
555 (LONG
)(ULONG
)FontSize
.X
,
564 DPRINT1("InitFonts: CreateConsoleFontEx('%S') failed\n", NewFaceName
);
568 /* Retrieve its character cell size */
569 hDC
= GetDC(GuiData
->hWindow
);
570 if (!GetFontCellSize(hDC
, hFont
, &GuiData
->CharHeight
, &GuiData
->CharWidth
))
572 DPRINT1("InitFonts: GetFontCellSize failed\n");
573 ReleaseDC(GuiData
->hWindow
, hDC
);
577 ReleaseDC(GuiData
->hWindow
, hDC
);
580 * Initialization succeeded.
582 // Delete all the old fonts first.
583 DeleteFonts(GuiData
);
584 GuiData
->Font
[FONT_NORMAL
] = hFont
;
587 * Now build the optional fonts (bold, underlined, mixed).
588 * Do not error in case they fail to be created.
590 GuiData
->Font
[FONT_BOLD
] =
591 CreateDerivedFont(GuiData
->Font
[FONT_NORMAL
],
592 max(FW_BOLD
, FontData
.Weight
),
595 GuiData
->Font
[FONT_UNDERLINE
] =
596 CreateDerivedFont(GuiData
->Font
[FONT_NORMAL
],
600 GuiData
->Font
[FONT_BOLD
| FONT_UNDERLINE
] =
601 CreateDerivedFont(GuiData
->Font
[FONT_NORMAL
],
602 max(FW_BOLD
, FontData
.Weight
),
607 * Save the new font characteristics.
609 StringCchCopyNW(GuiData
->GuiInfo
.FaceName
,
610 ARRAYSIZE(GuiData
->GuiInfo
.FaceName
),
611 NewFaceName
, ARRAYSIZE(NewFaceName
));
612 GuiData
->GuiInfo
.FontWeight
= FontData
.Weight
;
613 GuiData
->GuiInfo
.FontFamily
= FontData
.Family
;
614 GuiData
->GuiInfo
.FontSize
= FontData
.Size
;
616 /* Resize the terminal, in case the new font has a different size */
617 if ((OldCharWidth
!= GuiData
->CharWidth
) ||
618 (OldCharHeight
!= GuiData
->CharHeight
) ||
619 (OldFontSize
.X
!= FontData
.Size
.X
||
620 OldFontSize
.Y
!= FontData
.Size
.Y
))
622 TermResizeTerminal(GuiData
->Console
);
630 OnNcCreate(HWND hWnd
, LPCREATESTRUCTW Create
)
632 PGUI_CONSOLE_DATA GuiData
= (PGUI_CONSOLE_DATA
)Create
->lpCreateParams
;
633 PCONSRV_CONSOLE Console
;
637 DPRINT1("GuiConsoleNcCreate: No GUI data\n");
641 Console
= GuiData
->Console
;
643 GuiData
->hWindow
= hWnd
;
644 GuiData
->hSysMenu
= GetSystemMenu(hWnd
, FALSE
);
645 GuiData
->IsWindowActive
= FALSE
;
647 /* Initialize the fonts */
648 if (!InitFonts(GuiData
,
649 GuiData
->GuiInfo
.FaceName
,
650 GuiData
->GuiInfo
.FontWeight
,
651 GuiData
->GuiInfo
.FontFamily
,
652 GuiData
->GuiInfo
.FontSize
,
655 /* Reset only the output code page if we don't have a suitable
656 * font for it, possibly falling back to "United States (OEM)". */
657 UINT AltCodePage
= GetOEMCP();
659 if (AltCodePage
== Console
->OutputCodePage
)
660 AltCodePage
= CP_USA
;
662 DPRINT1("Could not initialize font '%S' for code page %d - Resetting CP to %d\n",
663 GuiData
->GuiInfo
.FaceName
, Console
->OutputCodePage
, AltCodePage
);
665 CON_SET_OUTPUT_CP(Console
, AltCodePage
);
667 /* We will use a fallback font if we cannot find
668 * anything for this replacement code page. */
669 if (!InitFonts(GuiData
,
670 GuiData
->GuiInfo
.FaceName
,
671 GuiData
->GuiInfo
.FontWeight
,
672 GuiData
->GuiInfo
.FontFamily
,
673 GuiData
->GuiInfo
.FontSize
,
676 DPRINT1("Failed to initialize font '%S' for code page %d\n",
677 GuiData
->GuiInfo
.FaceName
, Console
->OutputCodePage
);
679 DPRINT1("GuiConsoleNcCreate: InitFonts failed\n");
680 GuiData
->hWindow
= NULL
;
681 NtSetEvent(GuiData
->hGuiInitEvent
, NULL
);
686 /* Initialize the terminal framebuffer */
687 GuiData
->hMemDC
= CreateCompatibleDC(NULL
);
688 GuiData
->hBitmap
= NULL
;
689 GuiData
->hSysPalette
= NULL
; /* Original system palette */
691 /* Update the icons of the window */
692 if (GuiData
->hIcon
!= ghDefaultIcon
)
694 DefWindowProcW(GuiData
->hWindow
, WM_SETICON
, ICON_BIG
, (LPARAM
)GuiData
->hIcon
);
695 DefWindowProcW(GuiData
->hWindow
, WM_SETICON
, ICON_SMALL
, (LPARAM
)GuiData
->hIconSm
);
698 // FIXME: Keep these instructions here ? ///////////////////////////////////
699 Console
->ActiveBuffer
->CursorBlinkOn
= TRUE
;
700 Console
->ActiveBuffer
->ForceCursorOff
= FALSE
;
701 ////////////////////////////////////////////////////////////////////////////
703 SetWindowLongPtrW(GuiData
->hWindow
, GWLP_USERDATA
, (DWORD_PTR
)GuiData
);
705 if (GuiData
->IsWindowVisible
)
707 SetTimer(GuiData
->hWindow
, CONGUI_UPDATE_TIMER
, CONGUI_UPDATE_TIME
, NULL
);
710 // FIXME: HACK: Potential HACK for CORE-8129; see revision 63595.
711 //CreateSysMenu(GuiData->hWindow);
713 DPRINT("OnNcCreate - setting start event\n");
714 NtSetEvent(GuiData
->hGuiInitEvent
, NULL
);
716 /* We accept dropped files */
717 DragAcceptFiles(GuiData
->hWindow
, TRUE
);
719 return (BOOL
)DefWindowProcW(GuiData
->hWindow
, WM_NCCREATE
, 0, (LPARAM
)Create
);
723 OnActivate(PGUI_CONSOLE_DATA GuiData
, WPARAM wParam
)
725 WORD ActivationState
= LOWORD(wParam
);
727 DPRINT("WM_ACTIVATE - ActivationState = %d\n", ActivationState
);
729 GuiData
->IsWindowActive
= (ActivationState
!= WA_INACTIVE
);
731 if ( ActivationState
== WA_ACTIVE
||
732 ActivationState
== WA_CLICKACTIVE
)
734 if (GuiData
->GuiInfo
.FullScreen
)
736 EnterFullScreen(GuiData
);
737 // // PostMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
738 // SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
741 else // if (ActivationState == WA_INACTIVE)
743 if (GuiData
->GuiInfo
.FullScreen
)
745 SendMessageW(GuiData
->hWindow
, WM_SYSCOMMAND
, SC_MINIMIZE
, 0);
746 LeaveFullScreen(GuiData
);
747 // // PostMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
748 // SendMessageW(GuiData->hWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
753 * Ignore the next mouse event when we are going to be enabled again via
754 * the mouse, in order to prevent, e.g. when we are in Edit mode, erroneous
755 * mouse actions from the user that could spoil text selection or copy/pastes.
757 if (ActivationState
== WA_CLICKACTIVE
)
758 GuiData
->IgnoreNextMouseEvent
= TRUE
;
762 OnFocus(PGUI_CONSOLE_DATA GuiData
, BOOL SetFocus
)
764 PCONSRV_CONSOLE Console
= GuiData
->Console
;
767 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
769 /* Set console focus state */
770 Console
->HasFocus
= SetFocus
;
773 * Set the priority of the processes of this console
774 * in accordance with the console focus state.
776 ConSrvSetConsoleProcessFocus(Console
, SetFocus
);
778 /* Send a focus event */
779 er
.EventType
= FOCUS_EVENT
;
780 er
.Event
.FocusEvent
.bSetFocus
= SetFocus
;
781 ConioProcessInputEvent(Console
, &er
);
783 LeaveCriticalSection(&Console
->Lock
);
786 DPRINT("TODO: Create console caret\n");
788 DPRINT("TODO: Destroy console caret\n");
792 GetSelectionBeginEnd(PCOORD Begin
, PCOORD End
,
793 PCOORD SelectionAnchor
,
794 PSMALL_RECT SmallRect
)
796 if (Begin
== NULL
|| End
== NULL
) return;
798 *Begin
= *SelectionAnchor
;
799 End
->X
= (SelectionAnchor
->X
== SmallRect
->Left
) ? SmallRect
->Right
800 /* Case X != Left, must be == Right */ : SmallRect
->Left
;
801 End
->Y
= (SelectionAnchor
->Y
== SmallRect
->Top
) ? SmallRect
->Bottom
802 /* Case Y != Top, must be == Bottom */ : SmallRect
->Top
;
804 /* Exchange Begin / End if Begin > End lexicographically */
805 if (Begin
->Y
> End
->Y
|| (Begin
->Y
== End
->Y
&& Begin
->X
> End
->X
))
807 End
->X
= _InterlockedExchange16(&Begin
->X
, End
->X
);
808 End
->Y
= _InterlockedExchange16(&Begin
->Y
, End
->Y
);
813 CreateSelectionRgn(PGUI_CONSOLE_DATA GuiData
,
815 PCOORD SelectionAnchor
,
816 PSMALL_RECT SmallRect
)
821 SmallRectToRect(GuiData
, &rect
, SmallRect
);
822 return CreateRectRgnIndirect(&rect
);
829 GetSelectionBeginEnd(&Begin
, &End
, SelectionAnchor
, SmallRect
);
831 if (Begin
.Y
== End
.Y
)
841 // Debug thingie to see whether I can put this corner case
842 // together with the previous one.
843 if (SmallRect
->Left
!= sr
.Left
||
844 SmallRect
->Top
!= sr
.Top
||
845 SmallRect
->Right
!= sr
.Right
||
846 SmallRect
->Bottom
!= sr
.Bottom
)
849 "SmallRect = (%d, %d, %d, %d)\n"
850 "sr = (%d, %d, %d, %d)\n"
852 SmallRect
->Left
, SmallRect
->Top
, SmallRect
->Right
, SmallRect
->Bottom
,
853 sr
.Left
, sr
.Top
, sr
.Right
, sr
.Bottom
);
856 SmallRectToRect(GuiData
, &r
, &sr
);
857 SelRgn
= CreateRectRgnIndirect(&r
);
861 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
864 SMALL_RECT sr1
, sr2
, sr3
;
869 sr1
.Right
= ActiveBuffer
->ScreenBufferSize
.X
- 1;
870 sr1
.Bottom
= Begin
.Y
;
873 sr2
.Top
= Begin
.Y
+ 1;
874 sr2
.Right
= ActiveBuffer
->ScreenBufferSize
.X
- 1;
875 sr2
.Bottom
= End
.Y
- 1;
882 SmallRectToRect(GuiData
, &r1
, &sr1
);
883 SmallRectToRect(GuiData
, &r2
, &sr2
);
884 SmallRectToRect(GuiData
, &r3
, &sr3
);
886 rg1
= CreateRectRgnIndirect(&r1
);
887 rg2
= CreateRectRgnIndirect(&r2
);
888 rg3
= CreateRectRgnIndirect(&r3
);
890 CombineRgn(rg1
, rg1
, rg2
, RGN_XOR
);
891 CombineRgn(rg1
, rg1
, rg3
, RGN_XOR
);
903 PaintSelectionRect(PGUI_CONSOLE_DATA GuiData
, PPAINTSTRUCT pps
)
905 HRGN rgnPaint
= CreateRectRgnIndirect(&pps
->rcPaint
);
906 HRGN rgnSel
= CreateSelectionRgn(GuiData
, GuiData
->LineSelection
,
907 &GuiData
->Selection
.dwSelectionAnchor
,
908 &GuiData
->Selection
.srSelection
);
910 /* Invert the selection */
912 int ErrorCode
= CombineRgn(rgnPaint
, rgnPaint
, rgnSel
, RGN_AND
);
913 if (ErrorCode
!= ERROR
&& ErrorCode
!= NULLREGION
)
915 InvertRgn(pps
->hdc
, rgnPaint
);
918 DeleteObject(rgnSel
);
919 DeleteObject(rgnPaint
);
923 UpdateSelection(PGUI_CONSOLE_DATA GuiData
,
924 PCOORD SelectionAnchor OPTIONAL
,
927 PCONSRV_CONSOLE Console
= GuiData
->Console
;
928 HRGN oldRgn
= CreateSelectionRgn(GuiData
, GuiData
->LineSelection
,
929 &GuiData
->Selection
.dwSelectionAnchor
,
930 &GuiData
->Selection
.srSelection
);
932 /* Update the anchor if needed (use the old one if NULL) */
934 GuiData
->Selection
.dwSelectionAnchor
= *SelectionAnchor
;
936 // TODO: Scroll buffer to bring 'coord' into view
944 * Pressing the Control key while selecting text, allows us to enter
945 * into line-selection mode, the selection mode of *nix terminals.
947 BOOL OldLineSel
= GuiData
->LineSelection
;
948 GuiData
->LineSelection
= !!(GetKeyState(VK_CONTROL
) & KEY_PRESSED
);
950 /* Exchange left/top with right/bottom if required */
951 rc
.Left
= min(GuiData
->Selection
.dwSelectionAnchor
.X
, coord
->X
);
952 rc
.Top
= min(GuiData
->Selection
.dwSelectionAnchor
.Y
, coord
->Y
);
953 rc
.Right
= max(GuiData
->Selection
.dwSelectionAnchor
.X
, coord
->X
);
954 rc
.Bottom
= max(GuiData
->Selection
.dwSelectionAnchor
.Y
, coord
->Y
);
956 newRgn
= CreateSelectionRgn(GuiData
, GuiData
->LineSelection
,
957 &GuiData
->Selection
.dwSelectionAnchor
,
960 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
962 if (OldLineSel
!= GuiData
->LineSelection
||
963 memcmp(&rc
, &GuiData
->Selection
.srSelection
, sizeof(SMALL_RECT
)) != 0)
965 /* Calculate the region that needs to be updated */
966 if (oldRgn
&& newRgn
&& CombineRgn(newRgn
, newRgn
, oldRgn
, RGN_XOR
) != ERROR
)
968 InvalidateRgn(GuiData
->hWindow
, newRgn
, FALSE
);
974 InvalidateRgn(GuiData
->hWindow
, newRgn
, FALSE
);
977 DeleteObject(newRgn
);
979 GuiData
->Selection
.dwFlags
|= CONSOLE_SELECTION_NOT_EMPTY
;
980 GuiData
->Selection
.srSelection
= rc
;
981 GuiData
->dwSelectionCursor
= *coord
;
983 if ((GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) == 0)
985 LPWSTR SelTypeStr
= NULL
, WindowTitle
= NULL
;
986 SIZE_T SelTypeStrLength
= 0, Length
= 0;
988 /* Clear the old selection */
989 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
991 InvalidateRgn(GuiData
->hWindow
, oldRgn
, FALSE
);
995 * When passing a zero-length buffer size, LoadString(...) returns
996 * a read-only pointer buffer to the program's resource string.
999 LoadStringW(ConSrvDllInstance
,
1000 (GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_SELECTION
)
1001 ? IDS_SELECT_TITLE
: IDS_MARK_TITLE
,
1002 (LPWSTR
)&SelTypeStr
, 0);
1005 * Prepend the selection type string to the current console title
1006 * if we succeeded in retrieving a valid localized string.
1010 // 3 for " - " and 1 for NULL
1011 Length
= Console
->Title
.Length
+ (SelTypeStrLength
+ 3 + 1) * sizeof(WCHAR
);
1012 WindowTitle
= ConsoleAllocHeap(0, Length
);
1014 wcsncpy(WindowTitle
, SelTypeStr
, SelTypeStrLength
);
1015 WindowTitle
[SelTypeStrLength
] = UNICODE_NULL
;
1016 wcscat(WindowTitle
, L
" - ");
1017 wcscat(WindowTitle
, Console
->Title
.Buffer
);
1019 SetWindowTextW(GuiData
->hWindow
, WindowTitle
);
1020 ConsoleFreeHeap(WindowTitle
);
1023 GuiData
->Selection
.dwFlags
|= CONSOLE_SELECTION_IN_PROGRESS
;
1024 ConioPause(Console
, PAUSED_FROM_SELECTION
);
1029 /* Clear the selection */
1030 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
1032 InvalidateRgn(GuiData
->hWindow
, oldRgn
, FALSE
);
1035 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
1036 ConioUnpause(Console
, PAUSED_FROM_SELECTION
);
1038 /* Restore the console title */
1039 SetWindowTextW(GuiData
->hWindow
, Console
->Title
.Buffer
);
1042 DeleteObject(oldRgn
);
1046 OnPaint(PGUI_CONSOLE_DATA GuiData
)
1048 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
1052 /* Do nothing if the window is hidden */
1053 if (!GuiData
->IsWindowVisible
) return;
1055 BeginPaint(GuiData
->hWindow
, &ps
);
1056 if (ps
.hdc
!= NULL
&&
1057 ps
.rcPaint
.left
< ps
.rcPaint
.right
&&
1058 ps
.rcPaint
.top
< ps
.rcPaint
.bottom
)
1060 EnterCriticalSection(&GuiData
->Lock
);
1062 /* Compose the current screen-buffer on-memory */
1063 if (GetType(ActiveBuffer
) == TEXTMODE_BUFFER
)
1065 GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER
)ActiveBuffer
,
1066 GuiData
, &ps
.rcPaint
, &rcPaint
);
1068 else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
1070 GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER
)ActiveBuffer
,
1071 GuiData
, &ps
.rcPaint
, &rcPaint
);
1074 /* Send it to screen */
1078 rcPaint
.right
- rcPaint
.left
,
1079 rcPaint
.bottom
- rcPaint
.top
,
1085 /* Draw the selection region if needed */
1086 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
)
1088 PaintSelectionRect(GuiData
, &ps
);
1091 // TODO: Move cursor display here!
1093 LeaveCriticalSection(&GuiData
->Lock
);
1095 EndPaint(GuiData
->hWindow
, &ps
);
1101 OnPaletteChanged(PGUI_CONSOLE_DATA GuiData
)
1103 PCONSOLE_SCREEN_BUFFER ActiveBuffer
= GuiData
->ActiveBuffer
;
1105 /* Do nothing if the window is hidden */
1106 if (!GuiData
->IsWindowVisible
) return;
1108 // See WM_PALETTECHANGED message
1109 // if ((HWND)wParam == hWnd) break;
1111 // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
1112 if (ActiveBuffer
->PaletteHandle
)
1114 DPRINT("WM_PALETTECHANGED changing palette\n");
1116 /* Specify the use of the system palette for the framebuffer */
1117 SetSystemPaletteUse(GuiData
->hMemDC
, ActiveBuffer
->PaletteUsage
);
1119 /* Realize the (logical) palette */
1120 RealizePalette(GuiData
->hMemDC
);
1125 IsSystemKey(WORD VirtualKeyCode
)
1127 switch (VirtualKeyCode
)
1129 /* From MSDN, "Virtual-Key Codes" */
1148 OnKey(PGUI_CONSOLE_DATA GuiData
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1150 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1151 PCONSOLE_SCREEN_BUFFER ActiveBuffer
;
1153 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
1155 ActiveBuffer
= GuiData
->ActiveBuffer
;
1157 if (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
)
1159 WORD VirtualKeyCode
= LOWORD(wParam
);
1161 if (msg
!= WM_KEYDOWN
) goto Quit
;
1163 if (VirtualKeyCode
== VK_RETURN
)
1165 /* Copy (and clear) selection if ENTER is pressed */
1169 else if ( VirtualKeyCode
== VK_ESCAPE
||
1170 (VirtualKeyCode
== 'C' && (GetKeyState(VK_CONTROL
) & KEY_PRESSED
)) )
1172 /* Cancel selection if ESC or Ctrl-C are pressed */
1173 UpdateSelection(GuiData
, NULL
, NULL
);
1177 if ((GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_SELECTION
) == 0)
1179 /* Keyboard selection mode */
1180 BOOL Interpreted
= FALSE
;
1181 BOOL MajPressed
= !!(GetKeyState(VK_SHIFT
) & KEY_PRESSED
);
1183 switch (VirtualKeyCode
)
1188 if (GuiData
->dwSelectionCursor
.X
> 0)
1189 GuiData
->dwSelectionCursor
.X
--;
1197 if (GuiData
->dwSelectionCursor
.X
< ActiveBuffer
->ScreenBufferSize
.X
- 1)
1198 GuiData
->dwSelectionCursor
.X
++;
1206 if (GuiData
->dwSelectionCursor
.Y
> 0)
1207 GuiData
->dwSelectionCursor
.Y
--;
1215 if (GuiData
->dwSelectionCursor
.Y
< ActiveBuffer
->ScreenBufferSize
.Y
- 1)
1216 GuiData
->dwSelectionCursor
.Y
++;
1224 GuiData
->dwSelectionCursor
.X
= 0;
1225 GuiData
->dwSelectionCursor
.Y
= 0;
1232 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->ScreenBufferSize
.Y
- 1;
1239 GuiData
->dwSelectionCursor
.Y
-= ActiveBuffer
->ViewSize
.Y
;
1240 if (GuiData
->dwSelectionCursor
.Y
< 0)
1241 GuiData
->dwSelectionCursor
.Y
= 0;
1249 GuiData
->dwSelectionCursor
.Y
+= ActiveBuffer
->ViewSize
.Y
;
1250 if (GuiData
->dwSelectionCursor
.Y
>= ActiveBuffer
->ScreenBufferSize
.Y
)
1251 GuiData
->dwSelectionCursor
.Y
= ActiveBuffer
->ScreenBufferSize
.Y
- 1;
1262 UpdateSelection(GuiData
,
1263 !MajPressed
? &GuiData
->dwSelectionCursor
: NULL
,
1264 &GuiData
->dwSelectionCursor
);
1266 else if (!IsSystemKey(VirtualKeyCode
))
1268 /* Emit an error beep sound */
1269 SendNotifyMessage(GuiData
->hWindow
, PM_CONSOLE_BEEP
, 0, 0);
1276 /* Mouse selection mode */
1278 if (!IsSystemKey(VirtualKeyCode
))
1280 /* Clear the selection and send the key into the input buffer */
1281 UpdateSelection(GuiData
, NULL
, NULL
);
1290 if ((GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) == 0)
1294 Message
.hwnd
= GuiData
->hWindow
;
1295 Message
.message
= msg
;
1296 Message
.wParam
= wParam
;
1297 Message
.lParam
= lParam
;
1299 ConioProcessKey(Console
, &Message
);
1303 LeaveCriticalSection(&Console
->Lock
);
1307 // FIXME: Remove after fixing OnTimer
1309 InvalidateCell(PGUI_CONSOLE_DATA GuiData
,
1313 OnTimer(PGUI_CONSOLE_DATA GuiData
)
1315 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1316 PCONSOLE_SCREEN_BUFFER Buff
;
1318 /* Do nothing if the window is hidden */
1319 if (!GuiData
->IsWindowVisible
) return;
1321 SetTimer(GuiData
->hWindow
, CONGUI_UPDATE_TIMER
, CURSOR_BLINK_TIME
, NULL
);
1323 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
1325 Buff
= GuiData
->ActiveBuffer
;
1327 if (GetType(Buff
) == TEXTMODE_BUFFER
)
1329 /* Repaint the caret */
1330 if (GuiData
->IsWindowActive
|| Buff
->CursorBlinkOn
)
1332 InvalidateCell(GuiData
, Buff
->CursorPosition
.X
, Buff
->CursorPosition
.Y
);
1333 Buff
->CursorBlinkOn
= !Buff
->CursorBlinkOn
;
1336 if ((GuiData
->OldCursor
.x
!= Buff
->CursorPosition
.X
) ||
1337 (GuiData
->OldCursor
.y
!= Buff
->CursorPosition
.Y
))
1340 int OldScrollX
= -1, OldScrollY
= -1;
1341 int NewScrollX
= -1, NewScrollY
= -1;
1343 sInfo
.cbSize
= sizeof(sInfo
);
1344 sInfo
.fMask
= SIF_POS
;
1345 // Capture the original position of the scroll bars and save them.
1346 if (GetScrollInfo(GuiData
->hWindow
, SB_HORZ
, &sInfo
)) OldScrollX
= sInfo
.nPos
;
1347 if (GetScrollInfo(GuiData
->hWindow
, SB_VERT
, &sInfo
)) OldScrollY
= sInfo
.nPos
;
1349 // If we successfully got the info for the horizontal scrollbar
1350 if (OldScrollX
>= 0)
1352 if ((Buff
->CursorPosition
.X
< Buff
->ViewOrigin
.X
) ||
1353 (Buff
->CursorPosition
.X
>= (Buff
->ViewOrigin
.X
+ Buff
->ViewSize
.X
)))
1355 // Handle the horizontal scroll bar
1356 if (Buff
->CursorPosition
.X
>= Buff
->ViewSize
.X
)
1357 NewScrollX
= Buff
->CursorPosition
.X
- Buff
->ViewSize
.X
+ 1;
1363 NewScrollX
= OldScrollX
;
1366 // If we successfully got the info for the vertical scrollbar
1367 if (OldScrollY
>= 0)
1369 if ((Buff
->CursorPosition
.Y
< Buff
->ViewOrigin
.Y
) ||
1370 (Buff
->CursorPosition
.Y
>= (Buff
->ViewOrigin
.Y
+ Buff
->ViewSize
.Y
)))
1372 // Handle the vertical scroll bar
1373 if (Buff
->CursorPosition
.Y
>= Buff
->ViewSize
.Y
)
1374 NewScrollY
= Buff
->CursorPosition
.Y
- Buff
->ViewSize
.Y
+ 1;
1380 NewScrollY
= OldScrollY
;
1384 // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area
1385 // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar
1386 // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling)
1387 // and their associated scrollbar is left alone.
1388 if ((OldScrollX
!= NewScrollX
) || (OldScrollY
!= NewScrollY
))
1390 Buff
->ViewOrigin
.X
= NewScrollX
;
1391 Buff
->ViewOrigin
.Y
= NewScrollY
;
1392 ScrollWindowEx(GuiData
->hWindow
,
1393 (OldScrollX
- NewScrollX
) * GuiData
->CharWidth
,
1394 (OldScrollY
- NewScrollY
) * GuiData
->CharHeight
,
1400 if (NewScrollX
>= 0)
1402 sInfo
.nPos
= NewScrollX
;
1403 SetScrollInfo(GuiData
->hWindow
, SB_HORZ
, &sInfo
, TRUE
);
1405 if (NewScrollY
>= 0)
1407 sInfo
.nPos
= NewScrollY
;
1408 SetScrollInfo(GuiData
->hWindow
, SB_VERT
, &sInfo
, TRUE
);
1410 UpdateWindow(GuiData
->hWindow
);
1411 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1412 GuiData
->OldCursor
.x
= Buff
->CursorPosition
.X
;
1413 GuiData
->OldCursor
.y
= Buff
->CursorPosition
.Y
;
1417 else /* if (GetType(Buff) == GRAPHICS_BUFFER) */
1421 LeaveCriticalSection(&Console
->Lock
);
1425 OnClose(PGUI_CONSOLE_DATA GuiData
)
1427 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1429 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
))
1432 // TODO: Prompt for termination ? (Warn the user about possible apps running in this console)
1435 * FIXME: Windows will wait up to 5 seconds for the thread to exit.
1436 * We shouldn't wait here, though, since the console lock is entered.
1437 * A copy of the thread list probably needs to be made.
1439 ConSrvConsoleProcessCtrlEvent(Console
, 0, CTRL_CLOSE_EVENT
);
1441 LeaveCriticalSection(&Console
->Lock
);
1446 OnNcDestroy(HWND hWnd
)
1448 PGUI_CONSOLE_DATA GuiData
= GuiGetGuiData(hWnd
);
1450 /* Free the GuiData registration */
1451 SetWindowLongPtrW(hWnd
, GWLP_USERDATA
, (DWORD_PTR
)NULL
);
1453 /* Reset the system menu back to default and destroy the previous menu */
1454 GetSystemMenu(hWnd
, TRUE
);
1458 if (GuiData
->IsWindowVisible
)
1459 KillTimer(hWnd
, CONGUI_UPDATE_TIMER
);
1461 /* Free the terminal framebuffer */
1462 if (GuiData
->hMemDC
) DeleteDC(GuiData
->hMemDC
);
1463 if (GuiData
->hBitmap
) DeleteObject(GuiData
->hBitmap
);
1464 // if (GuiData->hSysPalette) DeleteObject(GuiData->hSysPalette);
1465 DeleteFonts(GuiData
);
1468 return DefWindowProcW(hWnd
, WM_NCDESTROY
, 0, 0);
1472 OnScroll(PGUI_CONSOLE_DATA GuiData
, INT nBar
, WORD sbCode
)
1474 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1475 PCONSOLE_SCREEN_BUFFER Buff
;
1477 INT oldPos
, Maximum
;
1480 ASSERT(nBar
== SB_HORZ
|| nBar
== SB_VERT
);
1482 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
1484 Buff
= GuiData
->ActiveBuffer
;
1486 if (nBar
== SB_HORZ
)
1488 Maximum
= Buff
->ScreenBufferSize
.X
- Buff
->ViewSize
.X
;
1489 pOriginXY
= &Buff
->ViewOrigin
.X
;
1491 else // if (nBar == SB_VERT)
1493 Maximum
= Buff
->ScreenBufferSize
.Y
- Buff
->ViewSize
.Y
;
1494 pOriginXY
= &Buff
->ViewOrigin
.Y
;
1497 /* Set scrollbar sizes */
1498 sInfo
.cbSize
= sizeof(sInfo
);
1499 sInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
| SIF_TRACKPOS
;
1501 if (!GetScrollInfo(GuiData
->hWindow
, nBar
, &sInfo
)) goto Quit
;
1503 oldPos
= sInfo
.nPos
;
1507 case SB_LINEUP
: // SB_LINELEFT:
1511 case SB_LINEDOWN
: // SB_LINERIGHT:
1515 case SB_PAGEUP
: // SB_PAGELEFT:
1516 sInfo
.nPos
-= sInfo
.nPage
;
1519 case SB_PAGEDOWN
: // SB_PAGERIGHT:
1520 sInfo
.nPos
+= sInfo
.nPage
;
1524 sInfo
.nPos
= sInfo
.nTrackPos
;
1525 ConioPause(Console
, PAUSED_FROM_SCROLLBAR
);
1528 case SB_THUMBPOSITION
:
1529 sInfo
.nPos
= sInfo
.nTrackPos
;
1530 ConioUnpause(Console
, PAUSED_FROM_SCROLLBAR
);
1533 case SB_TOP
: // SB_LEFT:
1534 sInfo
.nPos
= sInfo
.nMin
;
1537 case SB_BOTTOM
: // SB_RIGHT:
1538 sInfo
.nPos
= sInfo
.nMax
;
1545 sInfo
.nPos
= min(max(sInfo
.nPos
, 0), Maximum
);
1547 if (oldPos
!= sInfo
.nPos
)
1549 USHORT OldX
= Buff
->ViewOrigin
.X
;
1550 USHORT OldY
= Buff
->ViewOrigin
.Y
;
1551 UINT WidthUnit
, HeightUnit
;
1553 /* We now modify Buff->ViewOrigin */
1554 *pOriginXY
= sInfo
.nPos
;
1556 GetScreenBufferSizeUnits(Buff
, GuiData
, &WidthUnit
, &HeightUnit
);
1558 ScrollWindowEx(GuiData
->hWindow
,
1559 (OldX
- Buff
->ViewOrigin
.X
) * WidthUnit
,
1560 (OldY
- Buff
->ViewOrigin
.Y
) * HeightUnit
,
1567 sInfo
.fMask
= SIF_POS
;
1568 SetScrollInfo(GuiData
->hWindow
, nBar
, &sInfo
, TRUE
);
1570 UpdateWindow(GuiData
->hWindow
);
1571 // InvalidateRect(GuiData->hWindow, NULL, FALSE);
1575 LeaveCriticalSection(&Console
->Lock
);
1580 PointToCoord(PGUI_CONSOLE_DATA GuiData
, LPARAM lParam
)
1582 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1584 UINT WidthUnit
, HeightUnit
;
1586 GetScreenBufferSizeUnits(Buffer
, GuiData
, &WidthUnit
, &HeightUnit
);
1588 Coord
.X
= Buffer
->ViewOrigin
.X
+ ((SHORT
)LOWORD(lParam
) / (int)WidthUnit
);
1589 Coord
.Y
= Buffer
->ViewOrigin
.Y
+ ((SHORT
)HIWORD(lParam
) / (int)HeightUnit
);
1591 /* Clip coordinate to ensure it's inside buffer */
1594 else if (Coord
.X
>= Buffer
->ScreenBufferSize
.X
)
1595 Coord
.X
= Buffer
->ScreenBufferSize
.X
- 1;
1599 else if (Coord
.Y
>= Buffer
->ScreenBufferSize
.Y
)
1600 Coord
.Y
= Buffer
->ScreenBufferSize
.Y
- 1;
1606 OnMouse(PGUI_CONSOLE_DATA GuiData
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1608 BOOL DoDefault
= FALSE
;
1609 PCONSRV_CONSOLE Console
= GuiData
->Console
;
1612 * HACK FOR CORE-8394 (Part 2):
1614 * Check whether we should ignore the next mouse move event.
1615 * In either case we reset the HACK flag.
1617 * See Part 1 of this hack below.
1619 if (GuiData
->HackCORE8394IgnoreNextMove
&& msg
== WM_MOUSEMOVE
)
1621 GuiData
->HackCORE8394IgnoreNextMove
= FALSE
;
1624 GuiData
->HackCORE8394IgnoreNextMove
= FALSE
;
1626 // FIXME: It's here that we need to check whether we have focus or not
1627 // and whether we are or not in edit mode, in order to know if we need
1628 // to deal with the mouse.
1630 if (GuiData
->IgnoreNextMouseEvent
)
1632 if (msg
!= WM_LBUTTONDOWN
&&
1633 msg
!= WM_MBUTTONDOWN
&&
1634 msg
!= WM_RBUTTONDOWN
&&
1635 msg
!= WM_XBUTTONDOWN
)
1638 * If this mouse event is not a button-down action
1639 * then this is the last one being ignored.
1641 GuiData
->IgnoreNextMouseEvent
= FALSE
;
1646 * This mouse event is a button-down action.
1647 * Ignore it and perform default action.
1654 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
))
1660 if ( (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) ||
1661 (Console
->QuickEdit
) )
1665 case WM_LBUTTONDOWN
:
1667 /* Check for selection state */
1668 if ( (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) &&
1669 (GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_SELECTION
) &&
1670 (GetKeyState(VK_SHIFT
) & KEY_PRESSED
) )
1673 * A mouse selection is currently in progress and the user
1674 * has pressed the SHIFT key and clicked somewhere, update
1677 GuiData
->dwSelectionCursor
= PointToCoord(GuiData
, lParam
);
1678 UpdateSelection(GuiData
, NULL
, &GuiData
->dwSelectionCursor
);
1682 /* Clear the old selection */
1683 GuiData
->Selection
.dwFlags
= CONSOLE_NO_SELECTION
;
1685 /* Restart a new selection */
1686 GuiData
->dwSelectionCursor
= PointToCoord(GuiData
, lParam
);
1687 SetCapture(GuiData
->hWindow
);
1688 GuiData
->Selection
.dwFlags
|= CONSOLE_MOUSE_SELECTION
| CONSOLE_MOUSE_DOWN
;
1689 UpdateSelection(GuiData
,
1690 &GuiData
->dwSelectionCursor
,
1691 &GuiData
->dwSelectionCursor
);
1699 if (!(GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_DOWN
)) break;
1701 // GuiData->dwSelectionCursor = PointToCoord(GuiData, lParam);
1702 GuiData
->Selection
.dwFlags
&= ~CONSOLE_MOUSE_DOWN
;
1703 // UpdateSelection(GuiData, NULL, &GuiData->dwSelectionCursor);
1709 case WM_LBUTTONDBLCLK
:
1711 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
1713 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
1715 #define IS_WORD_SEP(c) \
1716 ((c) == L'\0' || (c) == L' ' || (c) == L'\t' || (c) == L'\r' || (c) == L'\n')
1718 PTEXTMODE_SCREEN_BUFFER TextBuffer
= (PTEXTMODE_SCREEN_BUFFER
)Buffer
;
1720 PCHAR_INFO ptrL
, ptrR
;
1722 /* Starting point */
1723 cL
= cR
= PointToCoord(GuiData
, lParam
);
1724 ptrL
= ptrR
= ConioCoordToPointer(TextBuffer
, cL
.X
, cL
.Y
);
1726 /* Enlarge the selection by checking for whitespace */
1727 while ((0 < cL
.X
) && !IS_WORD_SEP(ptrL
->Char
.UnicodeChar
)
1728 && !IS_WORD_SEP((ptrL
-1)->Char
.UnicodeChar
))
1733 while ((cR
.X
< TextBuffer
->ScreenBufferSize
.X
- 1) &&
1734 !IS_WORD_SEP(ptrR
->Char
.UnicodeChar
) &&
1735 !IS_WORD_SEP((ptrR
+1)->Char
.UnicodeChar
))
1742 * Update the selection started with the single
1743 * left-click that preceded this double-click.
1745 GuiData
->Selection
.dwFlags
|= CONSOLE_MOUSE_SELECTION
| CONSOLE_MOUSE_DOWN
;
1746 UpdateSelection(GuiData
, &cL
, &cR
);
1748 /* Ignore the next mouse move event */
1749 GuiData
->IgnoreNextMouseEvent
= TRUE
;
1756 case WM_RBUTTONDOWN
:
1757 case WM_RBUTTONDBLCLK
:
1759 if (!(GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
))
1768 /* Ignore the next mouse move event */
1769 GuiData
->IgnoreNextMouseEvent
= TRUE
;
1775 if (!(GET_KEYSTATE_WPARAM(wParam
) & MK_LBUTTON
)) break;
1776 if (!(GuiData
->Selection
.dwFlags
& CONSOLE_MOUSE_DOWN
)) break;
1778 GuiData
->dwSelectionCursor
= PointToCoord(GuiData
, lParam
);
1779 UpdateSelection(GuiData
, NULL
, &GuiData
->dwSelectionCursor
);
1784 DoDefault
= TRUE
; // FALSE;
1789 * HACK FOR CORE-8394 (Part 1):
1791 * It appears that when running ReactOS on VBox with Mouse Integration
1792 * enabled, the next mouse event coming after a button-down action is
1793 * a mouse-move. However it is NOT always a rule, so that we cannot use
1794 * the IgnoreNextMouseEvent flag to just "ignore" the next mouse event,
1795 * thinking it would always be a mouse-move event.
1797 * To work around this problem (that should really be fixed in Win32k),
1798 * we use a second flag to ignore this possible next mouse move event.
1802 case WM_LBUTTONDOWN
:
1803 case WM_MBUTTONDOWN
:
1804 case WM_RBUTTONDOWN
:
1805 case WM_XBUTTONDOWN
:
1806 GuiData
->HackCORE8394IgnoreNextMove
= TRUE
;
1811 else if (GetConsoleInputBufferMode(Console
) & ENABLE_MOUSE_INPUT
)
1814 WORD wKeyState
= GET_KEYSTATE_WPARAM(wParam
);
1815 DWORD dwButtonState
= 0;
1816 DWORD dwControlKeyState
= 0;
1817 DWORD dwEventFlags
= 0;
1821 case WM_LBUTTONDOWN
:
1822 SetCapture(GuiData
->hWindow
);
1823 dwButtonState
= FROM_LEFT_1ST_BUTTON_PRESSED
;
1827 case WM_MBUTTONDOWN
:
1828 SetCapture(GuiData
->hWindow
);
1829 dwButtonState
= FROM_LEFT_2ND_BUTTON_PRESSED
;
1833 case WM_RBUTTONDOWN
:
1834 SetCapture(GuiData
->hWindow
);
1835 dwButtonState
= RIGHTMOST_BUTTON_PRESSED
;
1839 case WM_XBUTTONDOWN
:
1841 /* Get which X-button was pressed */
1842 WORD wButton
= GET_XBUTTON_WPARAM(wParam
);
1844 /* Check for X-button validity */
1845 if (wButton
& ~(XBUTTON1
| XBUTTON2
))
1847 DPRINT1("X-button 0x%04x invalid\n", wButton
);
1852 SetCapture(GuiData
->hWindow
);
1853 dwButtonState
= (wButton
== XBUTTON1
? FROM_LEFT_3RD_BUTTON_PRESSED
1854 : FROM_LEFT_4TH_BUTTON_PRESSED
);
1879 /* Get which X-button was released */
1880 WORD wButton
= GET_XBUTTON_WPARAM(wParam
);
1882 /* Check for X-button validity */
1883 if (wButton
& ~(XBUTTON1
| XBUTTON2
))
1885 DPRINT1("X-button 0x%04x invalid\n", wButton
);
1886 /* Ok, just release the button anyway... */
1895 case WM_LBUTTONDBLCLK
:
1896 dwButtonState
= FROM_LEFT_1ST_BUTTON_PRESSED
;
1897 dwEventFlags
= DOUBLE_CLICK
;
1900 case WM_MBUTTONDBLCLK
:
1901 dwButtonState
= FROM_LEFT_2ND_BUTTON_PRESSED
;
1902 dwEventFlags
= DOUBLE_CLICK
;
1905 case WM_RBUTTONDBLCLK
:
1906 dwButtonState
= RIGHTMOST_BUTTON_PRESSED
;
1907 dwEventFlags
= DOUBLE_CLICK
;
1910 case WM_XBUTTONDBLCLK
:
1912 /* Get which X-button was double-clicked */
1913 WORD wButton
= GET_XBUTTON_WPARAM(wParam
);
1915 /* Check for X-button validity */
1916 if (wButton
& ~(XBUTTON1
| XBUTTON2
))
1918 DPRINT1("X-button 0x%04x invalid\n", wButton
);
1923 dwButtonState
= (wButton
== XBUTTON1
? FROM_LEFT_3RD_BUTTON_PRESSED
1924 : FROM_LEFT_4TH_BUTTON_PRESSED
);
1925 dwEventFlags
= DOUBLE_CLICK
;
1931 dwEventFlags
= MOUSE_MOVED
;
1935 dwButtonState
= GET_WHEEL_DELTA_WPARAM(wParam
) << 16;
1936 dwEventFlags
= MOUSE_WHEELED
;
1939 case WM_MOUSEHWHEEL
:
1940 dwButtonState
= GET_WHEEL_DELTA_WPARAM(wParam
) << 16;
1941 dwEventFlags
= MOUSE_HWHEELED
;
1950 * HACK FOR CORE-8394 (Part 1):
1952 * It appears that when running ReactOS on VBox with Mouse Integration
1953 * enabled, the next mouse event coming after a button-down action is
1954 * a mouse-move. However it is NOT always a rule, so that we cannot use
1955 * the IgnoreNextMouseEvent flag to just "ignore" the next mouse event,
1956 * thinking it would always be a mouse-move event.
1958 * To work around this problem (that should really be fixed in Win32k),
1959 * we use a second flag to ignore this possible next mouse move event.
1963 case WM_LBUTTONDOWN
:
1964 case WM_MBUTTONDOWN
:
1965 case WM_RBUTTONDOWN
:
1966 case WM_XBUTTONDOWN
:
1967 GuiData
->HackCORE8394IgnoreNextMove
= TRUE
;
1974 if (wKeyState
& MK_LBUTTON
)
1975 dwButtonState
|= FROM_LEFT_1ST_BUTTON_PRESSED
;
1976 if (wKeyState
& MK_MBUTTON
)
1977 dwButtonState
|= FROM_LEFT_2ND_BUTTON_PRESSED
;
1978 if (wKeyState
& MK_RBUTTON
)
1979 dwButtonState
|= RIGHTMOST_BUTTON_PRESSED
;
1980 if (wKeyState
& MK_XBUTTON1
)
1981 dwButtonState
|= FROM_LEFT_3RD_BUTTON_PRESSED
;
1982 if (wKeyState
& MK_XBUTTON2
)
1983 dwButtonState
|= FROM_LEFT_4TH_BUTTON_PRESSED
;
1985 if (GetKeyState(VK_RMENU
) & KEY_PRESSED
)
1986 dwControlKeyState
|= RIGHT_ALT_PRESSED
;
1987 if (GetKeyState(VK_LMENU
) & KEY_PRESSED
)
1988 dwControlKeyState
|= LEFT_ALT_PRESSED
;
1989 if (GetKeyState(VK_RCONTROL
) & KEY_PRESSED
)
1990 dwControlKeyState
|= RIGHT_CTRL_PRESSED
;
1991 if (GetKeyState(VK_LCONTROL
) & KEY_PRESSED
)
1992 dwControlKeyState
|= LEFT_CTRL_PRESSED
;
1993 if (GetKeyState(VK_SHIFT
) & KEY_PRESSED
)
1994 dwControlKeyState
|= SHIFT_PRESSED
;
1995 if (GetKeyState(VK_NUMLOCK
) & KEY_TOGGLED
)
1996 dwControlKeyState
|= NUMLOCK_ON
;
1997 if (GetKeyState(VK_SCROLL
) & KEY_TOGGLED
)
1998 dwControlKeyState
|= SCROLLLOCK_ON
;
1999 if (GetKeyState(VK_CAPITAL
) & KEY_TOGGLED
)
2000 dwControlKeyState
|= CAPSLOCK_ON
;
2001 /* See WM_CHAR MSDN documentation for instance */
2002 if (HIWORD(lParam
) & KF_EXTENDED
)
2003 dwControlKeyState
|= ENHANCED_KEY
;
2005 /* Send a mouse event */
2006 er
.EventType
= MOUSE_EVENT
;
2007 er
.Event
.MouseEvent
.dwMousePosition
= PointToCoord(GuiData
, lParam
);
2008 er
.Event
.MouseEvent
.dwButtonState
= dwButtonState
;
2009 er
.Event
.MouseEvent
.dwControlKeyState
= dwControlKeyState
;
2010 er
.Event
.MouseEvent
.dwEventFlags
= dwEventFlags
;
2012 ConioProcessInputEvent(Console
, &er
);
2020 LeaveCriticalSection(&Console
->Lock
);
2026 if (msg
== WM_MOUSEWHEEL
|| msg
== WM_MOUSEHWHEEL
)
2030 // WORD wKeyState = GET_KEYSTATE_WPARAM(wParam);
2031 SHORT wDelta
= GET_WHEEL_DELTA_WPARAM(wParam
);
2033 if (msg
== WM_MOUSEWHEEL
)
2035 else // if (msg == WM_MOUSEHWHEEL)
2038 // NOTE: We currently do not support zooming...
2039 // if (wKeyState & MK_CONTROL)
2041 // FIXME: For some reason our win32k does not set the key states
2042 // when sending WM_MOUSEWHEEL or WM_MOUSEHWHEEL ...
2043 // if (wKeyState & MK_SHIFT)
2044 if (GetKeyState(VK_SHIFT
) & KEY_PRESSED
)
2045 sbCode
= (wDelta
>= 0 ? SB_PAGEUP
: SB_PAGEDOWN
);
2047 sbCode
= (wDelta
>= 0 ? SB_LINEUP
: SB_LINEDOWN
);
2049 OnScroll(GuiData
, nBar
, sbCode
);
2052 return DefWindowProcW(GuiData
->hWindow
, msg
, wParam
, lParam
);
2057 Copy(PGUI_CONSOLE_DATA GuiData
)
2059 if (OpenClipboard(GuiData
->hWindow
))
2061 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
2063 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
2065 GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER
)Buffer
, GuiData
);
2067 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
2069 GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER
)Buffer
, GuiData
);
2075 /* Clear the selection */
2076 UpdateSelection(GuiData
, NULL
, NULL
);
2080 Paste(PGUI_CONSOLE_DATA GuiData
)
2082 if (OpenClipboard(GuiData
->hWindow
))
2084 PCONSOLE_SCREEN_BUFFER Buffer
= GuiData
->ActiveBuffer
;
2086 if (GetType(Buffer
) == TEXTMODE_BUFFER
)
2088 GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER
)Buffer
, GuiData
);
2090 else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
2092 GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER
)Buffer
, GuiData
);
2100 OnGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData
, PMINMAXINFO minMaxInfo
)
2102 PCONSRV_CONSOLE Console
= GuiData
->Console
;
2103 PCONSOLE_SCREEN_BUFFER ActiveBuffer
;
2105 UINT WidthUnit
, HeightUnit
;
2107 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
2109 ActiveBuffer
= GuiData
->ActiveBuffer
;
2111 GetScreenBufferSizeUnits(ActiveBuffer
, GuiData
, &WidthUnit
, &HeightUnit
);
2113 windx
= CONGUI_MIN_WIDTH
* WidthUnit
+ 2 * (GetSystemMetrics(SM_CXFRAME
) + GetSystemMetrics(SM_CXEDGE
));
2114 windy
= CONGUI_MIN_HEIGHT
* HeightUnit
+ 2 * (GetSystemMetrics(SM_CYFRAME
) + GetSystemMetrics(SM_CYEDGE
)) + GetSystemMetrics(SM_CYCAPTION
);
2116 minMaxInfo
->ptMinTrackSize
.x
= windx
;
2117 minMaxInfo
->ptMinTrackSize
.y
= windy
;
2119 windx
= (ActiveBuffer
->ScreenBufferSize
.X
) * WidthUnit
+ 2 * (GetSystemMetrics(SM_CXFRAME
) + GetSystemMetrics(SM_CXEDGE
));
2120 windy
= (ActiveBuffer
->ScreenBufferSize
.Y
) * HeightUnit
+ 2 * (GetSystemMetrics(SM_CYFRAME
) + GetSystemMetrics(SM_CYEDGE
)) + GetSystemMetrics(SM_CYCAPTION
);
2122 if (ActiveBuffer
->ViewSize
.X
< ActiveBuffer
->ScreenBufferSize
.X
) windy
+= GetSystemMetrics(SM_CYHSCROLL
); // Window currently has a horizontal scrollbar
2123 if (ActiveBuffer
->ViewSize
.Y
< ActiveBuffer
->ScreenBufferSize
.Y
) windx
+= GetSystemMetrics(SM_CXVSCROLL
); // Window currently has a vertical scrollbar
2125 minMaxInfo
->ptMaxTrackSize
.x
= windx
;
2126 minMaxInfo
->ptMaxTrackSize
.y
= windy
;
2128 LeaveCriticalSection(&Console
->Lock
);
2132 OnSize(PGUI_CONSOLE_DATA GuiData
, WPARAM wParam
, LPARAM lParam
)
2134 PCONSRV_CONSOLE Console
= GuiData
->Console
;
2136 /* Do nothing if the window is hidden */
2137 if (!GuiData
->IsWindowVisible
|| IsIconic(GuiData
->hWindow
)) return;
2139 if (!ConDrvValidateConsoleUnsafe((PCONSOLE
)Console
, CONSOLE_RUNNING
, TRUE
)) return;
2141 if (!GuiData
->WindowSizeLock
&&
2142 (wParam
== SIZE_RESTORED
|| wParam
== SIZE_MAXIMIZED
|| wParam
== SIZE_MINIMIZED
))
2144 PCONSOLE_SCREEN_BUFFER Buff
= GuiData
->ActiveBuffer
;
2145 DWORD windx
, windy
, charx
, chary
;
2146 UINT WidthUnit
, HeightUnit
;
2148 GetScreenBufferSizeUnits(Buff
, GuiData
, &WidthUnit
, &HeightUnit
);
2150 GuiData
->WindowSizeLock
= TRUE
;
2152 windx
= LOWORD(lParam
);
2153 windy
= HIWORD(lParam
);
2155 /* Compensate for existing scroll bars (because lParam values do not accommodate scroll bar) */
2156 if (Buff
->ViewSize
.X
< Buff
->ScreenBufferSize
.X
) windy
+= GetSystemMetrics(SM_CYHSCROLL
); // Window currently has a horizontal scrollbar
2157 if (Buff
->ViewSize
.Y
< Buff
->ScreenBufferSize
.Y
) windx
+= GetSystemMetrics(SM_CXVSCROLL
); // Window currently has a vertical scrollbar
2159 charx
= windx
/ (int)WidthUnit
;
2160 chary
= windy
/ (int)HeightUnit
;
2162 /* Character alignment (round size up or down) */
2163 if ((windx
% WidthUnit
) >= (WidthUnit
/ 2)) ++charx
;
2164 if ((windy
% HeightUnit
) >= (HeightUnit
/ 2)) ++chary
;
2166 /* Compensate for added scroll bars in window */
2167 if (Buff
->ViewSize
.X
< Buff
->ScreenBufferSize
.X
) windy
-= GetSystemMetrics(SM_CYHSCROLL
); // Window will have a horizontal scroll bar
2168 if (Buff
->ViewSize
.Y
< Buff
->ScreenBufferSize
.Y
) windx
-= GetSystemMetrics(SM_CXVSCROLL
); // Window will have a vertical scroll bar
2170 charx
= windx
/ (int)WidthUnit
;
2171 chary
= windy
/ (int)HeightUnit
;
2173 /* Character alignment (round size up or down) */
2174 if ((windx
% WidthUnit
) >= (WidthUnit
/ 2)) ++charx
;
2175 if ((windy
% HeightUnit
) >= (HeightUnit
/ 2)) ++chary
;
2178 if ((charx
!= Buff
->ViewSize
.X
) || (chary
!= Buff
->ViewSize
.Y
))
2180 Buff
->ViewSize
.X
= (charx
<= (DWORD
)Buff
->ScreenBufferSize
.X
) ? charx
: Buff
->ScreenBufferSize
.X
;
2181 Buff
->ViewSize
.Y
= (chary
<= (DWORD
)Buff
->ScreenBufferSize
.Y
) ? chary
: Buff
->ScreenBufferSize
.Y
;
2184 ResizeConWnd(GuiData
, WidthUnit
, HeightUnit
);
2186 /* Adjust the start of the visible area if we are attempting to show nonexistent areas */
2187 if ((Buff
->ScreenBufferSize
.X
- Buff
->ViewOrigin
.X
) < Buff
->ViewSize
.X
) Buff
->ViewOrigin
.X
= Buff
->ScreenBufferSize
.X
- Buff
->ViewSize
.X
;
2188 if ((Buff
->ScreenBufferSize
.Y
- Buff
->ViewOrigin
.Y
) < Buff
->ViewSize
.Y
) Buff
->ViewOrigin
.Y
= Buff
->ScreenBufferSize
.Y
- Buff
->ViewSize
.Y
;
2189 InvalidateRect(GuiData
->hWindow
, NULL
, TRUE
);
2191 GuiData
->WindowSizeLock
= FALSE
;
2194 LeaveCriticalSection(&Console
->Lock
);
2198 OnMove(PGUI_CONSOLE_DATA GuiData
)
2202 // TODO: Simplify the code.
2203 // See: GuiConsoleNotifyWndProc() PM_CREATE_CONSOLE.
2205 /* Retrieve our real position */
2206 GetWindowRect(GuiData
->hWindow
, &rcWnd
);
2207 GuiData
->GuiInfo
.WindowOrigin
.x
= rcWnd
.left
;
2208 GuiData
->GuiInfo
.WindowOrigin
.y
= rcWnd
.top
;
2212 OnDropFiles(PCONSRV_CONSOLE Console
, HDROP hDrop
)
2215 WCHAR szPath
[MAX_PATH
+ 2];
2219 DragQueryFileW(hDrop
, 0, &szPath
[1], ARRAYSIZE(szPath
) - 1);
2222 if (wcschr(&szPath
[1], L
' ') != NULL
)
2224 StringCchCatW(szPath
, ARRAYSIZE(szPath
), L
"\"");
2229 pszPath
= &szPath
[1];
2232 PasteText(Console
, pszPath
, wcslen(pszPath
));
2236 // HACK: This functionality is standard for general scrollbars. Don't add it by hand.
2239 GuiConsoleHandleScrollbarMenu(VOID)
2243 hMenu = CreatePopupMenu();
2246 DPRINT("CreatePopupMenu failed\n");
2250 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
2251 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2252 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
2253 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
2254 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2255 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
2256 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
2257 //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
2258 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
2259 //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
2264 CreateFrameBufferBitmap(HDC hDC
, int width
, int height
)
2267 ZeroMemory(&bmi
, sizeof(BITMAPINFO
));
2268 bmi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
2269 bmi
.bmiHeader
.biWidth
= width
;
2270 bmi
.bmiHeader
.biHeight
= height
;
2271 bmi
.bmiHeader
.biPlanes
= 1;
2272 bmi
.bmiHeader
.biBitCount
= GetDeviceCaps(hDC
, BITSPIXEL
);
2273 bmi
.bmiHeader
.biCompression
= BI_RGB
;
2274 return CreateDIBSection(NULL
, &bmi
, DIB_RGB_COLORS
, NULL
, NULL
, 0);
2277 static LRESULT CALLBACK
2278 ConWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
2281 PGUI_CONSOLE_DATA GuiData
= NULL
;
2282 PCONSRV_CONSOLE Console
= NULL
;
2285 * - If it's the first time we create a window for the terminal,
2286 * just initialize it and return.
2288 * - If we are destroying the window, just do it and return.
2290 if (msg
== WM_NCCREATE
)
2292 return (LRESULT
)OnNcCreate(hWnd
, (LPCREATESTRUCTW
)lParam
);
2294 else if (msg
== WM_NCDESTROY
)
2296 return OnNcDestroy(hWnd
);
2300 * Now the terminal window is initialized.
2301 * Get the terminal data via the window's data.
2302 * If there is no data, just go away.
2304 GuiData
= GuiGetGuiData(hWnd
);
2305 if (GuiData
== NULL
) return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
2307 // TEMPORARY HACK until all of the functions can deal with a NULL GuiData->ActiveBuffer ...
2308 if (GuiData
->ActiveBuffer
== NULL
) return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
2311 * Just retrieve a pointer to the console in case somebody needs it.
2312 * It is not NULL because it was checked in GuiGetGuiData.
2313 * Each helper function which needs the console has to validate and lock it.
2315 Console
= GuiData
->Console
;
2317 /* We have a console, start message dispatching */
2321 OnActivate(GuiData
, wParam
);
2325 if (OnClose(GuiData
)) goto Default
;
2339 case WM_PALETTECHANGED
:
2341 DPRINT("WM_PALETTECHANGED called\n");
2344 * Protects against infinite loops:
2345 * "... A window that receives this message must not realize
2346 * its palette, unless it determines that wParam does not contain
2347 * its own window handle." (WM_PALETTECHANGED description - MSDN)
2349 * This message is sent to all windows, including the one that
2350 * changed the system palette and caused this message to be sent.
2351 * The wParam of this message contains the handle of the window
2352 * that caused the system palette to change. To avoid an infinite
2353 * loop, care must be taken to check that the wParam of this message
2354 * does not match the window's handle.
2356 if ((HWND
)wParam
== hWnd
) break;
2358 DPRINT("WM_PALETTECHANGED ok\n");
2359 OnPaletteChanged(GuiData
);
2360 DPRINT("WM_PALETTECHANGED quit\n");
2371 case WM_SYSDEADCHAR
:
2373 /* Detect Alt-Enter presses and switch back and forth to fullscreen mode */
2374 if (msg
== WM_SYSKEYDOWN
&& (HIWORD(lParam
) & KF_ALTDOWN
) && wParam
== VK_RETURN
)
2376 /* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */
2377 if ((HIWORD(lParam
) & (KF_UP
| KF_REPEAT
)) != KF_REPEAT
)
2378 GuiConsoleSwitchFullScreen(GuiData
);
2382 /* Detect Alt-Esc/Space/Tab presses defer to DefWindowProc */
2383 if ( (HIWORD(lParam
) & KF_ALTDOWN
) && (wParam
== VK_ESCAPE
|| wParam
== VK_SPACE
|| wParam
== VK_TAB
))
2385 return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
2387 /* Detect Alt+Shift */
2388 if (wParam
== VK_SHIFT
&& (msg
== WM_SYSKEYDOWN
|| msg
== WM_SYSKEYUP
))
2390 return DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
2393 OnKey(GuiData
, msg
, wParam
, lParam
);
2399 /* Do nothing if the window is hidden */
2400 if (!GuiData
->IsWindowVisible
) goto Default
;
2403 * The message was sent because we are manually triggering a change.
2404 * Check whether the mouse is indeed present on this console window
2405 * and take appropriate decisions.
2407 if (wParam
== -1 && lParam
== -1)
2412 /* Get the placement of the mouse */
2413 GetCursorPos(&mouseCoords
);
2415 /* On which window is placed the mouse ? */
2416 hWndHit
= WindowFromPoint(mouseCoords
);
2418 /* It's our window. Perform the hit-test to be used later on. */
2419 if (hWndHit
== hWnd
)
2421 wParam
= (WPARAM
)hWnd
;
2422 lParam
= DefWindowProcW(hWndHit
, WM_NCHITTEST
, 0,
2423 MAKELPARAM(mouseCoords
.x
, mouseCoords
.y
));
2427 /* Set the mouse cursor only when we are in the client area */
2428 if ((HWND
)wParam
== hWnd
&& LOWORD(lParam
) == HTCLIENT
)
2430 if (GuiData
->MouseCursorRefCount
>= 0)
2432 /* Show the cursor */
2433 SetCursor(GuiData
->hCursor
);
2437 /* Hide the cursor if the reference count is negative */
2448 case WM_LBUTTONDOWN
:
2449 case WM_MBUTTONDOWN
:
2450 case WM_RBUTTONDOWN
:
2451 case WM_XBUTTONDOWN
:
2456 case WM_LBUTTONDBLCLK
:
2457 case WM_MBUTTONDBLCLK
:
2458 case WM_RBUTTONDBLCLK
:
2459 case WM_XBUTTONDBLCLK
:
2462 case WM_MOUSEHWHEEL
:
2464 Result
= OnMouse(GuiData
, msg
, wParam
, lParam
);
2469 OnScroll(GuiData
, SB_HORZ
, LOWORD(wParam
));
2473 OnScroll(GuiData
, SB_VERT
, LOWORD(wParam
));
2476 case WM_CONTEXTMENU
:
2478 /* Do nothing if the window is hidden */
2479 if (!GuiData
->IsWindowVisible
) break;
2481 if (DefWindowProcW(hWnd
/*GuiData->hWindow*/, WM_NCHITTEST
, 0, lParam
) == HTCLIENT
)
2483 HMENU hMenu
= CreatePopupMenu();
2486 AppendMenuItems(hMenu
, GuiConsoleEditMenuItems
);
2487 TrackPopupMenuEx(hMenu
,
2489 GET_X_LPARAM(lParam
),
2490 GET_Y_LPARAM(lParam
),
2505 HMENU hMenu
= (HMENU
)wParam
;
2508 /* Enable or disable the Close menu item */
2509 EnableMenuItem(hMenu
, SC_CLOSE
, MF_BYCOMMAND
|
2510 (GuiData
->IsCloseButtonEnabled
? MF_ENABLED
: MF_GRAYED
));
2512 /* Enable or disable the Copy and Paste items */
2513 EnableMenuItem(hMenu
, ID_SYSTEM_EDIT_COPY
, MF_BYCOMMAND
|
2514 ((GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) &&
2515 (GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_NOT_EMPTY
) ? MF_ENABLED
: MF_GRAYED
));
2516 // FIXME: Following whether the active screen buffer is text-mode
2517 // or graphics-mode, search for CF_UNICODETEXT or CF_BITMAP formats.
2518 EnableMenuItem(hMenu
, ID_SYSTEM_EDIT_PASTE
, MF_BYCOMMAND
|
2519 (!(GuiData
->Selection
.dwFlags
& CONSOLE_SELECTION_IN_PROGRESS
) &&
2520 IsClipboardFormatAvailable(CF_UNICODETEXT
) ? MF_ENABLED
: MF_GRAYED
));
2523 SendMenuEvent(Console
, WM_INITMENU
);
2529 if (HIWORD(wParam
) == 0xFFFF) // Allow all the menu flags
2531 SendMenuEvent(Console
, WM_MENUSELECT
);
2539 Result
= OnCommand(GuiData
, wParam
, lParam
);
2544 OnDropFiles(Console
, (HDROP
)wParam
);
2549 OnFocus(GuiData
, (msg
== WM_SETFOCUS
));
2552 case WM_GETMINMAXINFO
:
2553 OnGetMinMaxInfo(GuiData
, (PMINMAXINFO
)lParam
);
2560 #if 0 // This code is here to prepare & control dynamic console SB resizing.
2563 PRECT dragRect
= (PRECT
)lParam
;
2567 DPRINT1("WMSZ_LEFT\n");
2570 DPRINT1("WMSZ_RIGHT\n");
2573 DPRINT1("WMSZ_TOP\n");
2576 DPRINT1("WMSZ_TOPLEFT\n");
2579 DPRINT1("WMSZ_TOPRIGHT\n");
2582 DPRINT1("WMSZ_BOTTOM\n");
2584 case WMSZ_BOTTOMLEFT
:
2585 DPRINT1("WMSZ_BOTTOMLEFT\n");
2587 case WMSZ_BOTTOMRIGHT
:
2588 DPRINT1("WMSZ_BOTTOMRIGHT\n");
2591 DPRINT1("wParam = %d\n", wParam
);
2594 DPRINT1("dragRect = {.left = %d ; .top = %d ; .right = %d ; .bottom = %d}\n",
2595 dragRect
->left
, dragRect
->top
, dragRect
->right
, dragRect
->bottom
);
2601 OnSize(GuiData
, wParam
, lParam
);
2604 case PM_RESIZE_TERMINAL
:
2606 PCONSOLE_SCREEN_BUFFER Buff
= GuiData
->ActiveBuffer
;
2610 DWORD Width
, Height
;
2611 UINT WidthUnit
, HeightUnit
;
2613 /* Do nothing if the window is hidden */
2614 if (!GuiData
->IsWindowVisible
) break;
2616 GetScreenBufferSizeUnits(Buff
, GuiData
, &WidthUnit
, &HeightUnit
);
2618 Width
= Buff
->ScreenBufferSize
.X
* WidthUnit
;
2619 Height
= Buff
->ScreenBufferSize
.Y
* HeightUnit
;
2621 /* Recreate the framebuffer */
2622 hDC
= GetDC(GuiData
->hWindow
);
2623 hnew
= CreateFrameBufferBitmap(hDC
, Width
, Height
);
2624 ReleaseDC(GuiData
->hWindow
, hDC
);
2625 hold
= SelectObject(GuiData
->hMemDC
, hnew
);
2626 if (GuiData
->hBitmap
)
2628 if (hold
== GuiData
->hBitmap
) DeleteObject(GuiData
->hBitmap
);
2630 GuiData
->hBitmap
= hnew
;
2632 /* Resize the window to the user's values */
2633 GuiData
->WindowSizeLock
= TRUE
;
2634 ResizeConWnd(GuiData
, WidthUnit
, HeightUnit
);
2635 GuiData
->WindowSizeLock
= FALSE
;
2640 * Undocumented message sent by Windows' console.dll for applying console info.
2641 * See http://www.catch22.net/sites/default/source/files/setconsoleinfo.c
2642 * and http://www.scn.rain.com/~neighorn/PDF/MSBugPaper.pdf
2643 * for more information.
2645 case WM_SETCONSOLEINFO
:
2647 GuiApplyUserSettings(GuiData
, (HANDLE
)wParam
);
2651 case PM_CONSOLE_BEEP
:
2656 case PM_CONSOLE_SET_TITLE
:
2657 SetWindowTextW(GuiData
->hWindow
, GuiData
->Console
->Title
.Buffer
);
2661 Result
= DefWindowProcW(hWnd
, msg
, wParam
, lParam
);