reactos/win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.c

2961 lines
94 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Server DLL
* FILE: win32ss/user/winsrv/consrv_new/frontends/gui/guiterm.c
* PURPOSE: GUI Terminal Front-End
* PROGRAMMERS: Gé van Geldorp
* Johannes Anderwald
* Jeffrey Morlan
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
/* INCLUDES *******************************************************************/
#define COBJMACROS
#define NONAMELESSUNION
#include "consrv.h"
#include "include/conio.h"
#include "include/console.h"
#include "include/settings.h"
#include "conoutput.h"
#include "guiterm.h"
#include "guisettings.h"
#include "resource.h"
#include <windowsx.h>
#include <shlwapi.h>
#include <shlobj.h>
#define NDEBUG
#include <debug.h>
/* GUI Console Window Class name */
#define GUI_CONSOLE_WINDOW_CLASS L"ConsoleWindowClass"
#ifndef WM_APP
#define WM_APP 0x8000
#endif
#define PM_CREATE_CONSOLE (WM_APP + 1)
#define PM_DESTROY_CONSOLE (WM_APP + 2)
#define PM_RESIZE_TERMINAL (WM_APP + 3)
#define PM_CONSOLE_BEEP (WM_APP + 4)
#define PM_CONSOLE_SET_TITLE (WM_APP + 5)
/* Not defined in any header file */
extern VOID WINAPI PrivateCsrssManualGuiCheck(LONG Check);
// See winsrv/usersrv/init.c line 234
/* GLOBALS ********************************************************************/
typedef struct _GUI_INIT_INFO
{
PCONSOLE_INFO ConsoleInfo;
PCONSOLE_START_INFO ConsoleStartInfo;
ULONG ProcessId;
} GUI_INIT_INFO, *PGUI_INIT_INFO;
/**************************************************************\
\** Define the Console Leader Process for the console window **/
#define GWLP_CONSOLEWND_ALLOC (2 * sizeof(LONG_PTR))
#define GWLP_CONSOLE_LEADER_PID 0
#define GWLP_CONSOLE_LEADER_TID 4
#define SetConsoleWndConsoleLeaderCID(GuiData) \
do { \
PCONSOLE_PROCESS_DATA ProcessData; \
CLIENT_ID ConsoleLeaderCID; \
ProcessData = CONTAINING_RECORD((GuiData)->Console->ProcessList.Blink, \
CONSOLE_PROCESS_DATA, \
ConsoleLink); \
ConsoleLeaderCID = ProcessData->Process->ClientId; \
SetWindowLongPtrW((GuiData)->hWindow, GWLP_CONSOLE_LEADER_PID, (LONG_PTR)(ConsoleLeaderCID.UniqueProcess)); \
SetWindowLongPtrW((GuiData)->hWindow, GWLP_CONSOLE_LEADER_TID, (LONG_PTR)(ConsoleLeaderCID.UniqueThread )); \
} while (0)
/**************************************************************/
static BOOL ConsInitialized = FALSE;
static HICON ghDefaultIcon = NULL;
static HICON ghDefaultIconSm = NULL;
static HCURSOR ghDefaultCursor = NULL;
static HWND NotifyWnd = NULL;
typedef struct _GUICONSOLE_MENUITEM
{
UINT uID;
const struct _GUICONSOLE_MENUITEM *SubMenu;
WORD wCmdID;
} GUICONSOLE_MENUITEM, *PGUICONSOLE_MENUITEM;
static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems[] =
{
{ IDS_MARK, NULL, ID_SYSTEM_EDIT_MARK },
{ IDS_COPY, NULL, ID_SYSTEM_EDIT_COPY },
{ IDS_PASTE, NULL, ID_SYSTEM_EDIT_PASTE },
{ IDS_SELECTALL, NULL, ID_SYSTEM_EDIT_SELECTALL },
{ IDS_SCROLL, NULL, ID_SYSTEM_EDIT_SCROLL },
{ IDS_FIND, NULL, ID_SYSTEM_EDIT_FIND },
{ 0, NULL, 0 } /* End of list */
};
static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems[] =
{
{ IDS_EDIT, GuiConsoleEditMenuItems, 0 },
{ IDS_DEFAULTS, NULL, ID_SYSTEM_DEFAULTS },
{ IDS_PROPERTIES, NULL, ID_SYSTEM_PROPERTIES },
{ 0, NULL, 0 } /* End of list */
};
/*
* Default 16-color palette for foreground and background
* (corresponding flags in comments).
*/
const COLORREF s_Colors[16] =
{
RGB(0, 0, 0), // (Black)
RGB(0, 0, 128), // BLUE
RGB(0, 128, 0), // GREEN
RGB(0, 128, 128), // BLUE | GREEN
RGB(128, 0, 0), // RED
RGB(128, 0, 128), // BLUE | RED
RGB(128, 128, 0), // GREEN | RED
RGB(192, 192, 192), // BLUE | GREEN | RED
RGB(128, 128, 128), // (Grey) INTENSITY
RGB(0, 0, 255), // BLUE | INTENSITY
RGB(0, 255, 0), // GREEN | INTENSITY
RGB(0, 255, 255), // BLUE | GREEN | INTENSITY
RGB(255, 0, 0), // RED | INTENSITY
RGB(255, 0, 255), // BLUE | RED | INTENSITY
RGB(255, 255, 0), // GREEN | RED | INTENSITY
RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY
};
/* FUNCTIONS ******************************************************************/
static VOID
GetScreenBufferSizeUnits(IN PCONSOLE_SCREEN_BUFFER Buffer,
IN PGUI_CONSOLE_DATA GuiData,
OUT PUINT WidthUnit,
OUT PUINT HeightUnit)
{
if (Buffer == NULL || GuiData == NULL ||
WidthUnit == NULL || HeightUnit == NULL)
{
return;
}
if (GetType(Buffer) == TEXTMODE_BUFFER)
{
*WidthUnit = GuiData->CharWidth ;
*HeightUnit = GuiData->CharHeight;
}
else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
{
*WidthUnit = 1;
*HeightUnit = 1;
}
}
static VOID
GuiConsoleAppendMenuItems(HMENU hMenu,
const GUICONSOLE_MENUITEM *Items)
{
UINT i = 0;
WCHAR szMenuString[255];
HMENU hSubMenu;
do
{
if (Items[i].uID != (UINT)-1)
{
if (LoadStringW(ConSrvDllInstance,
Items[i].uID,
szMenuString,
sizeof(szMenuString) / sizeof(szMenuString[0])) > 0)
{
if (Items[i].SubMenu != NULL)
{
hSubMenu = CreatePopupMenu();
if (hSubMenu != NULL)
{
GuiConsoleAppendMenuItems(hSubMenu,
Items[i].SubMenu);
if (!AppendMenuW(hMenu,
MF_STRING | MF_POPUP,
(UINT_PTR)hSubMenu,
szMenuString))
{
DestroyMenu(hSubMenu);
}
}
}
else
{
AppendMenuW(hMenu,
MF_STRING,
Items[i].wCmdID,
szMenuString);
}
}
}
else
{
AppendMenuW(hMenu,
MF_SEPARATOR,
0,
NULL);
}
i++;
} while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
}
static VOID
GuiConsoleCreateSysMenu(HWND hWnd)
{
HMENU hMenu = GetSystemMenu(hWnd, FALSE);
if (hMenu != NULL)
{
GuiConsoleAppendMenuItems(hMenu, GuiConsoleMainMenuItems);
DrawMenuBar(hWnd);
}
}
static VOID
GuiSendMenuEvent(PCONSOLE Console, UINT CmdId)
{
INPUT_RECORD er;
er.EventType = MENU_EVENT;
er.Event.MenuEvent.dwCommandId = CmdId;
ConioProcessInputEvent(Console, &er);
}
static VOID
GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData);
static VOID
GuiConsolePaste(PGUI_CONSOLE_DATA GuiData);
static VOID
GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord);
static VOID WINAPI
GuiDrawRegion(IN OUT PFRONTEND This, SMALL_RECT* Region);
static VOID
GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData);
static LRESULT
GuiConsoleHandleSysMenuCommand(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
{
LRESULT Ret = TRUE;
PCONSOLE Console = GuiData->Console;
PCONSOLE_SCREEN_BUFFER ActiveBuffer;
if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
{
Ret = FALSE;
goto Quit;
}
ActiveBuffer = ConDrvGetActiveScreenBuffer(Console);
/*
* In case the selected menu item belongs to the user-reserved menu id range,
* send to him a menu event and return directly. The user must handle those
* reserved menu commands...
*/
if (GuiData->cmdIdLow <= (UINT)wParam && (UINT)wParam <= GuiData->cmdIdHigh)
{
GuiSendMenuEvent(Console, (UINT)wParam);
goto Unlock_Quit;
}
/* ... otherwise, perform actions. */
switch (wParam)
{
case ID_SYSTEM_EDIT_MARK:
{
LPWSTR WindowTitle = NULL;
SIZE_T Length = 0;
Console->dwSelectionCursor.X = ActiveBuffer->ViewOrigin.X;
Console->dwSelectionCursor.Y = ActiveBuffer->ViewOrigin.Y;
Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor;
Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS;
GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor);
Length = Console->Title.Length + sizeof(L"Mark - ")/sizeof(WCHAR) + 1;
WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR));
wcscpy(WindowTitle, L"Mark - ");
wcscat(WindowTitle, Console->Title.Buffer);
SetWindowText(GuiData->hWindow, WindowTitle);
ConsoleFreeHeap(WindowTitle);
break;
}
case ID_SYSTEM_EDIT_COPY:
GuiConsoleCopy(GuiData);
break;
case ID_SYSTEM_EDIT_PASTE:
GuiConsolePaste(GuiData);
break;
case ID_SYSTEM_EDIT_SELECTALL:
{
LPWSTR WindowTitle = NULL;
SIZE_T Length = 0;
Console->Selection.dwSelectionAnchor.X = 0;
Console->Selection.dwSelectionAnchor.Y = 0;
Console->dwSelectionCursor.X = ActiveBuffer->ViewSize.X - 1;
Console->dwSelectionCursor.Y = ActiveBuffer->ViewSize.Y - 1;
Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS | CONSOLE_MOUSE_SELECTION;
GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor);
Length = Console->Title.Length + sizeof(L"Selection - ")/sizeof(WCHAR) + 1;
WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR));
wcscpy(WindowTitle, L"Selection - ");
wcscat(WindowTitle, Console->Title.Buffer);
SetWindowText(GuiData->hWindow, WindowTitle);
ConsoleFreeHeap(WindowTitle);
break;
}
case ID_SYSTEM_EDIT_SCROLL:
DPRINT1("Scrolling is not handled yet\n");
break;
case ID_SYSTEM_EDIT_FIND:
DPRINT1("Finding is not handled yet\n");
break;
case ID_SYSTEM_DEFAULTS:
GuiConsoleShowConsoleProperties(GuiData, TRUE);
break;
case ID_SYSTEM_PROPERTIES:
GuiConsoleShowConsoleProperties(GuiData, FALSE);
break;
default:
Ret = FALSE;
break;
}
Unlock_Quit:
LeaveCriticalSection(&Console->Lock);
Quit:
if (!Ret)
Ret = DefWindowProcW(GuiData->hWindow, WM_SYSCOMMAND, wParam, lParam);
return Ret;
}
static PGUI_CONSOLE_DATA
GuiGetGuiData(HWND hWnd)
{
/* This function ensures that the console pointer is not NULL */
PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
return ( ((GuiData == NULL) || (GuiData->hWindow == hWnd && GuiData->Console != NULL)) ? GuiData : NULL );
}
VOID
GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData)
{
/* Move the window if needed (not positioned by the system) */
if (!GuiData->GuiInfo.AutoPosition)
{
SetWindowPos(GuiData->hWindow,
NULL,
GuiData->GuiInfo.WindowOrigin.x,
GuiData->GuiInfo.WindowOrigin.y,
0,
0,
SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
}
}
static VOID
GuiConsoleResizeWindow(PGUI_CONSOLE_DATA GuiData)
{
PCONSOLE Console = GuiData->Console;
PCONSOLE_SCREEN_BUFFER Buff = ConDrvGetActiveScreenBuffer(Console);
SCROLLINFO sInfo;
DWORD Width, Height;
UINT WidthUnit, HeightUnit;
GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
Width = Buff->ViewSize.X * WidthUnit +
2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
Height = Buff->ViewSize.Y * HeightUnit +
2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
/* Set scrollbar sizes */
sInfo.cbSize = sizeof(SCROLLINFO);
sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
sInfo.nMin = 0;
if (Buff->ScreenBufferSize.Y > Buff->ViewSize.Y)
{
sInfo.nMax = Buff->ScreenBufferSize.Y - 1;
sInfo.nPage = Buff->ViewSize.Y;
sInfo.nPos = Buff->ViewOrigin.Y;
SetScrollInfo(GuiData->hWindow, SB_VERT, &sInfo, TRUE);
Width += GetSystemMetrics(SM_CXVSCROLL);
ShowScrollBar(GuiData->hWindow, SB_VERT, TRUE);
}
else
{
ShowScrollBar(GuiData->hWindow, SB_VERT, FALSE);
}
if (Buff->ScreenBufferSize.X > Buff->ViewSize.X)
{
sInfo.nMax = Buff->ScreenBufferSize.X - 1;
sInfo.nPage = Buff->ViewSize.X;
sInfo.nPos = Buff->ViewOrigin.X;
SetScrollInfo(GuiData->hWindow, SB_HORZ, &sInfo, TRUE);
Height += GetSystemMetrics(SM_CYHSCROLL);
ShowScrollBar(GuiData->hWindow, SB_HORZ, TRUE);
}
else
{
ShowScrollBar(GuiData->hWindow, SB_HORZ, FALSE);
}
/* Resize the window */
SetWindowPos(GuiData->hWindow, NULL, 0, 0, Width, Height,
SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS);
// NOTE: The SWP_NOCOPYBITS flag can be replaced by a subsequent call
// to: InvalidateRect(GuiData->hWindow, NULL, TRUE);
}
static VOID
GuiConsoleSwitchFullScreen(PGUI_CONSOLE_DATA GuiData)
{
PCONSOLE Console = GuiData->Console;
// DEVMODE dmScreenSettings;
if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
/* Switch to full-screen or to windowed mode */
GuiData->GuiInfo.FullScreen = !GuiData->GuiInfo.FullScreen;
DPRINT1("GuiConsoleSwitchFullScreen - Switch to %s ...\n",
(GuiData->GuiInfo.FullScreen ? "full-screen" : "windowed mode"));
// TODO: Change window appearance.
// See:
// http://stackoverflow.com/questions/2382464/win32-full-screen-and-hiding-taskbar
// http://stackoverflow.com/questions/3549148/fullscreen-management-with-winapi
// http://blogs.msdn.com/b/oldnewthing/archive/2010/04/12/9994016.aspx
// http://stackoverflow.com/questions/1400654/how-do-i-put-my-opengl-app-into-fullscreen-mode
// http://nehe.gamedev.net/tutorial/creating_an_opengl_window_win32/13001/
#if 0
if (GuiData->GuiInfo.FullScreen)
{
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmDisplayFixedOutput = DMDFO_CENTER; // DMDFO_STRETCH // DMDFO_DEFAULT
dmScreenSettings.dmPelsWidth = 640; // Console->ActiveBuffer->ViewSize.X * GuiData->CharWidth;
dmScreenSettings.dmPelsHeight = 480; // Console->ActiveBuffer->ViewSize.Y * GuiData->CharHeight;
dmScreenSettings.dmBitsPerPel = 32;
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
}
else
{
}
#endif
LeaveCriticalSection(&Console->Lock);
}
static BOOL
GuiConsoleHandleNcCreate(HWND hWnd, LPCREATESTRUCTW Create)
{
PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)Create->lpCreateParams;
PCONSOLE Console;
HDC Dc;
HFONT OldFont;
TEXTMETRICW Metrics;
SIZE CharSize;
DPRINT("GuiConsoleHandleNcCreate\n");
if (NULL == GuiData)
{
DPRINT1("GuiConsoleNcCreate: No GUI data\n");
return FALSE;
}
Console = GuiData->Console;
GuiData->hWindow = hWnd;
GuiData->Font = CreateFontW(LOWORD(GuiData->GuiInfo.FontSize),
0, // HIWORD(GuiData->GuiInfo.FontSize),
0,
TA_BASELINE,
GuiData->GuiInfo.FontWeight,
FALSE,
FALSE,
FALSE,
OEM_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
NONANTIALIASED_QUALITY,
FIXED_PITCH | GuiData->GuiInfo.FontFamily /* FF_DONTCARE */,
GuiData->GuiInfo.FaceName);
if (NULL == GuiData->Font)
{
DPRINT1("GuiConsoleNcCreate: CreateFont failed\n");
GuiData->hWindow = NULL;
SetEvent(GuiData->hGuiInitEvent);
return FALSE;
}
Dc = GetDC(GuiData->hWindow);
if (NULL == Dc)
{
DPRINT1("GuiConsoleNcCreate: GetDC failed\n");
DeleteObject(GuiData->Font);
GuiData->hWindow = NULL;
SetEvent(GuiData->hGuiInitEvent);
return FALSE;
}
OldFont = SelectObject(Dc, GuiData->Font);
if (NULL == OldFont)
{
DPRINT1("GuiConsoleNcCreate: SelectObject failed\n");
ReleaseDC(GuiData->hWindow, Dc);
DeleteObject(GuiData->Font);
GuiData->hWindow = NULL;
SetEvent(GuiData->hGuiInitEvent);
return FALSE;
}
if (!GetTextMetricsW(Dc, &Metrics))
{
DPRINT1("GuiConsoleNcCreate: GetTextMetrics failed\n");
SelectObject(Dc, OldFont);
ReleaseDC(GuiData->hWindow, Dc);
DeleteObject(GuiData->Font);
GuiData->hWindow = NULL;
SetEvent(GuiData->hGuiInitEvent);
return FALSE;
}
GuiData->CharWidth = Metrics.tmMaxCharWidth;
GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading;
/* Measure real char width more precisely if possible. */
if (GetTextExtentPoint32W(Dc, L"R", 1, &CharSize))
GuiData->CharWidth = CharSize.cx;
SelectObject(Dc, OldFont);
ReleaseDC(GuiData->hWindow, Dc);
// FIXME: Keep these instructions here ? ///////////////////////////////////
Console->ActiveBuffer->CursorBlinkOn = TRUE;
Console->ActiveBuffer->ForceCursorOff = FALSE;
////////////////////////////////////////////////////////////////////////////
SetWindowLongPtrW(GuiData->hWindow, GWLP_USERDATA, (DWORD_PTR)GuiData);
SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
GuiConsoleCreateSysMenu(GuiData->hWindow);
DPRINT("GuiConsoleHandleNcCreate - setting start event\n");
SetEvent(GuiData->hGuiInitEvent);
return (BOOL)DefWindowProcW(GuiData->hWindow, WM_NCCREATE, 0, (LPARAM)Create);
}
static VOID
SmallRectToRect(PGUI_CONSOLE_DATA GuiData, PRECT Rect, PSMALL_RECT SmallRect)
{
PCONSOLE Console = GuiData->Console;
PCONSOLE_SCREEN_BUFFER Buffer = ConDrvGetActiveScreenBuffer(Console);
UINT WidthUnit, HeightUnit;
GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit);
Rect->left = (SmallRect->Left - Buffer->ViewOrigin.X) * WidthUnit ;
Rect->top = (SmallRect->Top - Buffer->ViewOrigin.Y) * HeightUnit;
Rect->right = (SmallRect->Right + 1 - Buffer->ViewOrigin.X) * WidthUnit ;
Rect->bottom = (SmallRect->Bottom + 1 - Buffer->ViewOrigin.Y) * HeightUnit;
}
static VOID
GuiConsoleUpdateSelection(PCONSOLE Console, PCOORD coord)
{
PGUI_CONSOLE_DATA GuiData = Console->TermIFace.Data;
RECT oldRect, newRect;
SmallRectToRect(GuiData, &oldRect, &Console->Selection.srSelection);
if (coord != NULL)
{
SMALL_RECT rc;
/* exchange left/top with right/bottom if required */
rc.Left = min(Console->Selection.dwSelectionAnchor.X, coord->X);
rc.Top = min(Console->Selection.dwSelectionAnchor.Y, coord->Y);
rc.Right = max(Console->Selection.dwSelectionAnchor.X, coord->X);
rc.Bottom = max(Console->Selection.dwSelectionAnchor.Y, coord->Y);
SmallRectToRect(GuiData, &newRect, &rc);
if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
{
if (memcmp(&rc, &Console->Selection.srSelection, sizeof(SMALL_RECT)) != 0)
{
HRGN rgn1, rgn2;
/* calculate the region that needs to be updated */
if ((rgn1 = CreateRectRgnIndirect(&oldRect)))
{
if ((rgn2 = CreateRectRgnIndirect(&newRect)))
{
if (CombineRgn(rgn1, rgn2, rgn1, RGN_XOR) != ERROR)
{
InvalidateRgn(GuiData->hWindow, rgn1, FALSE);
}
DeleteObject(rgn2);
}
DeleteObject(rgn1);
}
}
}
else
{
InvalidateRect(GuiData->hWindow, &newRect, FALSE);
}
Console->Selection.dwFlags |= CONSOLE_SELECTION_NOT_EMPTY;
Console->Selection.srSelection = rc;
Console->dwSelectionCursor = *coord;
ConioPause(Console, PAUSED_FROM_SELECTION);
}
else
{
/* clear the selection */
if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
{
InvalidateRect(GuiData->hWindow, &oldRect, FALSE);
}
Console->Selection.dwFlags = CONSOLE_NO_SELECTION;
ConioUnpause(Console, PAUSED_FROM_SELECTION);
}
}
VOID
GuiPaintTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer,
PGUI_CONSOLE_DATA GuiData,
HDC hDC,
PRECT rc);
VOID
GuiPaintGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer,
PGUI_CONSOLE_DATA GuiData,
HDC hDC,
PRECT rc);
static VOID
GuiConsoleHandlePaint(PGUI_CONSOLE_DATA GuiData)
{
BOOL Success = TRUE;
PCONSOLE Console = GuiData->Console;
PCONSOLE_SCREEN_BUFFER ActiveBuffer;
HDC hDC;
PAINTSTRUCT ps;
if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
{
Success = FALSE;
goto Quit;
}
ActiveBuffer = ConDrvGetActiveScreenBuffer(Console);
hDC = BeginPaint(GuiData->hWindow, &ps);
if (hDC != NULL &&
ps.rcPaint.left < ps.rcPaint.right &&
ps.rcPaint.top < ps.rcPaint.bottom)
{
EnterCriticalSection(&GuiData->Lock);
if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
{
GuiPaintTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)ActiveBuffer,
GuiData, hDC, &ps.rcPaint);
}
else /* if (GetType(ActiveBuffer) == GRAPHICS_BUFFER) */
{
GuiPaintGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)ActiveBuffer,
GuiData, hDC, &ps.rcPaint);
}
if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)
{
RECT rc;
SmallRectToRect(GuiData, &rc, &Console->Selection.srSelection);
/* invert the selection */
if (IntersectRect(&rc, &ps.rcPaint, &rc))
{
PatBlt(hDC,
rc.left,
rc.top,
rc.right - rc.left,
rc.bottom - rc.top,
DSTINVERT);
}
}
LeaveCriticalSection(&GuiData->Lock);
}
EndPaint(GuiData->hWindow, &ps);
Quit:
if (Success)
LeaveCriticalSection(&Console->Lock);
else
DefWindowProcW(GuiData->hWindow, WM_PAINT, 0, 0);
return;
}
static BOOL
IsSystemKey(WORD VirtualKeyCode)
{
switch (VirtualKeyCode)
{
/* From MSDN, "Virtual-Key Codes" */
case VK_RETURN:
case VK_SHIFT:
case VK_CONTROL:
case VK_MENU:
case VK_PAUSE:
case VK_CAPITAL:
case VK_ESCAPE:
case VK_LWIN:
case VK_RWIN:
case VK_NUMLOCK:
case VK_SCROLL:
return TRUE;
default:
return FALSE;
}
}
static VOID
GuiConsoleHandleKey(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
{
PCONSOLE Console = GuiData->Console;
PCONSOLE_SCREEN_BUFFER ActiveBuffer;
if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
ActiveBuffer = ConDrvGetActiveScreenBuffer(Console);
if (Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS)
{
WORD VirtualKeyCode = LOWORD(wParam);
if (msg != WM_KEYDOWN) goto Quit;
if (VirtualKeyCode == VK_RETURN)
{
/* Copy (and clear) selection if ENTER is pressed */
GuiConsoleCopy(GuiData);
goto Quit;
}
else if ( VirtualKeyCode == VK_ESCAPE ||
(VirtualKeyCode == 'C' && GetKeyState(VK_CONTROL) & 0x8000) )
{
/* Cancel selection if ESC or Ctrl-C are pressed */
GuiConsoleUpdateSelection(Console, NULL);
SetWindowText(GuiData->hWindow, Console->Title.Buffer);
goto Quit;
}
if ((Console->Selection.dwFlags & CONSOLE_MOUSE_SELECTION) == 0)
{
/* Keyboard selection mode */
BOOL Interpreted = FALSE;
BOOL MajPressed = (GetKeyState(VK_SHIFT) & 0x8000);
switch (VirtualKeyCode)
{
case VK_LEFT:
{
Interpreted = TRUE;
if (Console->dwSelectionCursor.X > 0)
Console->dwSelectionCursor.X--;
break;
}
case VK_RIGHT:
{
Interpreted = TRUE;
if (Console->dwSelectionCursor.X < ActiveBuffer->ScreenBufferSize.X - 1)
Console->dwSelectionCursor.X++;
break;
}
case VK_UP:
{
Interpreted = TRUE;
if (Console->dwSelectionCursor.Y > 0)
Console->dwSelectionCursor.Y--;
break;
}
case VK_DOWN:
{
Interpreted = TRUE;
if (Console->dwSelectionCursor.Y < ActiveBuffer->ScreenBufferSize.Y - 1)
Console->dwSelectionCursor.Y++;
break;
}
case VK_HOME:
{
Interpreted = TRUE;
Console->dwSelectionCursor.X = 0;
Console->dwSelectionCursor.Y = 0;
break;
}
case VK_END:
{
Interpreted = TRUE;
Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
break;
}
case VK_PRIOR:
{
Interpreted = TRUE;
Console->dwSelectionCursor.Y -= ActiveBuffer->ViewSize.Y;
if (Console->dwSelectionCursor.Y < 0)
Console->dwSelectionCursor.Y = 0;
break;
}
case VK_NEXT:
{
Interpreted = TRUE;
Console->dwSelectionCursor.Y += ActiveBuffer->ViewSize.Y;
if (Console->dwSelectionCursor.Y >= ActiveBuffer->ScreenBufferSize.Y)
Console->dwSelectionCursor.Y = ActiveBuffer->ScreenBufferSize.Y - 1;
break;
}
default:
break;
}
if (Interpreted)
{
if (!MajPressed)
Console->Selection.dwSelectionAnchor = Console->dwSelectionCursor;
GuiConsoleUpdateSelection(Console, &Console->dwSelectionCursor);
}
else if (!IsSystemKey(VirtualKeyCode))
{
/* Emit an error beep sound */
SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0);
}
goto Quit;
}
else
{
/* Mouse selection mode */
if (!IsSystemKey(VirtualKeyCode))
{
/* Clear the selection and send the key into the input buffer */
GuiConsoleUpdateSelection(Console, NULL);
SetWindowText(GuiData->hWindow, Console->Title.Buffer);
}
else
{
goto Quit;
}
}
}
if ((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) == 0)
{
MSG Message;
Message.hwnd = GuiData->hWindow;
Message.message = msg;
Message.wParam = wParam;
Message.lParam = lParam;
ConioProcessKey(Console, &Message);
}
Quit:
LeaveCriticalSection(&Console->Lock);
}
static VOID
GuiInvalidateCell(IN OUT PFRONTEND This, SHORT x, SHORT y)
{
SMALL_RECT CellRect = { x, y, x, y };
GuiDrawRegion(This, &CellRect);
}
static VOID
GuiConsoleHandleTimer(PGUI_CONSOLE_DATA GuiData)
{
PCONSOLE Console = GuiData->Console;
PCONSOLE_SCREEN_BUFFER Buff;
SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CURSOR_BLINK_TIME, NULL);
if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
Buff = ConDrvGetActiveScreenBuffer(Console);
if (GetType(Buff) == TEXTMODE_BUFFER)
{
GuiInvalidateCell(&Console->TermIFace, Buff->CursorPosition.X, Buff->CursorPosition.Y);
Buff->CursorBlinkOn = !Buff->CursorBlinkOn;
if ((GuiData->OldCursor.x != Buff->CursorPosition.X) || (GuiData->OldCursor.y != Buff->CursorPosition.Y))
{
SCROLLINFO xScroll;
int OldScrollX = -1, OldScrollY = -1;
int NewScrollX = -1, NewScrollY = -1;
xScroll.cbSize = sizeof(SCROLLINFO);
xScroll.fMask = SIF_POS;
// Capture the original position of the scroll bars and save them.
if (GetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll)) OldScrollX = xScroll.nPos;
if (GetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll)) OldScrollY = xScroll.nPos;
// If we successfully got the info for the horizontal scrollbar
if (OldScrollX >= 0)
{
if ((Buff->CursorPosition.X < Buff->ViewOrigin.X) || (Buff->CursorPosition.X >= (Buff->ViewOrigin.X + Buff->ViewSize.X)))
{
// Handle the horizontal scroll bar
if (Buff->CursorPosition.X >= Buff->ViewSize.X) NewScrollX = Buff->CursorPosition.X - Buff->ViewSize.X + 1;
else NewScrollX = 0;
}
else
{
NewScrollX = OldScrollX;
}
}
// If we successfully got the info for the vertical scrollbar
if (OldScrollY >= 0)
{
if ((Buff->CursorPosition.Y < Buff->ViewOrigin.Y) || (Buff->CursorPosition.Y >= (Buff->ViewOrigin.Y + Buff->ViewSize.Y)))
{
// Handle the vertical scroll bar
if (Buff->CursorPosition.Y >= Buff->ViewSize.Y) NewScrollY = Buff->CursorPosition.Y - Buff->ViewSize.Y + 1;
else NewScrollY = 0;
}
else
{
NewScrollY = OldScrollY;
}
}
// Adjust scroll bars and refresh the window if the cursor has moved outside the visible area
// NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar
// was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling)
// and their associated scrollbar is left alone.
if ((OldScrollX != NewScrollX) || (OldScrollY != NewScrollY))
{
Buff->ViewOrigin.X = NewScrollX;
Buff->ViewOrigin.Y = NewScrollY;
ScrollWindowEx(GuiData->hWindow,
(OldScrollX - NewScrollX) * GuiData->CharWidth,
(OldScrollY - NewScrollY) * GuiData->CharHeight,
NULL,
NULL,
NULL,
NULL,
SW_INVALIDATE);
if (NewScrollX >= 0)
{
xScroll.nPos = NewScrollX;
SetScrollInfo(GuiData->hWindow, SB_HORZ, &xScroll, TRUE);
}
if (NewScrollY >= 0)
{
xScroll.nPos = NewScrollY;
SetScrollInfo(GuiData->hWindow, SB_VERT, &xScroll, TRUE);
}
UpdateWindow(GuiData->hWindow);
GuiData->OldCursor.x = Buff->CursorPosition.X;
GuiData->OldCursor.y = Buff->CursorPosition.Y;
}
}
}
else /* if (GetType(Buff) == GRAPHICS_BUFFER) */
{
}
LeaveCriticalSection(&Console->Lock);
}
static BOOL
GuiConsoleHandleClose(PGUI_CONSOLE_DATA GuiData)
{
PCONSOLE Console = GuiData->Console;
if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
return TRUE;
// TODO: Prompt for termination ? (Warn the user about possible apps running in this console)
/*
* FIXME: Windows will wait up to 5 seconds for the thread to exit.
* We shouldn't wait here, though, since the console lock is entered.
* A copy of the thread list probably needs to be made.
*/
ConDrvConsoleProcessCtrlEvent(Console, 0, CTRL_CLOSE_EVENT);
LeaveCriticalSection(&Console->Lock);
return FALSE;
}
static LRESULT
GuiConsoleHandleNcDestroy(HWND hWnd)
{
KillTimer(hWnd, CONGUI_UPDATE_TIMER);
GetSystemMenu(hWnd, TRUE);
/* Free the GuiData registration */
SetWindowLongPtrW(hWnd, GWLP_USERDATA, (DWORD_PTR)NULL);
return DefWindowProcW(hWnd, WM_NCDESTROY, 0, 0);
}
static COORD
PointToCoord(PGUI_CONSOLE_DATA GuiData, LPARAM lParam)
{
PCONSOLE Console = GuiData->Console;
PCONSOLE_SCREEN_BUFFER Buffer = ConDrvGetActiveScreenBuffer(Console);
COORD Coord;
UINT WidthUnit, HeightUnit;
GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit);
Coord.X = Buffer->ViewOrigin.X + ((SHORT)LOWORD(lParam) / (int)WidthUnit );
Coord.Y = Buffer->ViewOrigin.Y + ((SHORT)HIWORD(lParam) / (int)HeightUnit);
/* Clip coordinate to ensure it's inside buffer */
if (Coord.X < 0)
Coord.X = 0;
else if (Coord.X >= Buffer->ScreenBufferSize.X)
Coord.X = Buffer->ScreenBufferSize.X - 1;
if (Coord.Y < 0)
Coord.Y = 0;
else if (Coord.Y >= Buffer->ScreenBufferSize.Y)
Coord.Y = Buffer->ScreenBufferSize.Y - 1;
return Coord;
}
static LRESULT
GuiConsoleHandleMouse(PGUI_CONSOLE_DATA GuiData, UINT msg, WPARAM wParam, LPARAM lParam)
{
BOOL Err = FALSE;
PCONSOLE Console = GuiData->Console;
if (GuiData->IgnoreNextMouseSignal)
{
if (msg != WM_LBUTTONDOWN &&
msg != WM_MBUTTONDOWN &&
msg != WM_RBUTTONDOWN)
{
/*
* If this mouse signal is not a button-down action,
* then it is the last signal being ignored.
*/
GuiData->IgnoreNextMouseSignal = FALSE;
}
else
{
/*
* This mouse signal is a button-down action.
* Ignore it and perform default action.
*/
Err = TRUE;
}
goto Quit;
}
if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
{
Err = TRUE;
goto Quit;
}
if ( (Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) ||
(Console->QuickEdit) )
{
switch (msg)
{
case WM_LBUTTONDOWN:
{
LPWSTR WindowTitle = NULL;
SIZE_T Length = 0;
Console->Selection.dwSelectionAnchor = PointToCoord(GuiData, lParam);
SetCapture(GuiData->hWindow);
Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS | CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN;
GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor);
Length = Console->Title.Length + sizeof(L"Selection - ")/sizeof(WCHAR) + 1;
WindowTitle = ConsoleAllocHeap(0, Length * sizeof(WCHAR));
wcscpy(WindowTitle, L"Selection - ");
wcscat(WindowTitle, Console->Title.Buffer);
SetWindowText(GuiData->hWindow, WindowTitle);
ConsoleFreeHeap(WindowTitle);
break;
}
case WM_LBUTTONUP:
{
COORD c;
if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
c = PointToCoord(GuiData, lParam);
Console->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN;
GuiConsoleUpdateSelection(Console, &c);
ReleaseCapture();
break;
}
case WM_LBUTTONDBLCLK:
{
DPRINT1("Handle left-double-click for selecting a word\n");
break;
}
case WM_RBUTTONDOWN:
case WM_RBUTTONDBLCLK:
{
if (!(Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY))
{
GuiConsolePaste(GuiData);
}
else
{
GuiConsoleCopy(GuiData);
}
GuiData->IgnoreNextMouseSignal = TRUE;
break;
}
case WM_MOUSEMOVE:
{
COORD c;
if (!(wParam & MK_LBUTTON)) break;
if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) break;
c = PointToCoord(GuiData, lParam); /* TODO: Scroll buffer to bring c into view */
GuiConsoleUpdateSelection(Console, &c);
break;
}
default:
Err = FALSE; // TRUE;
break;
}
}
else if (Console->InputBuffer.Mode & ENABLE_MOUSE_INPUT)
{
INPUT_RECORD er;
WORD wKeyState = GET_KEYSTATE_WPARAM(wParam);
DWORD dwButtonState = 0;
DWORD dwControlKeyState = 0;
DWORD dwEventFlags = 0;
switch (msg)
{
case WM_LBUTTONDOWN:
SetCapture(GuiData->hWindow);
dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
dwEventFlags = 0;
break;
case WM_MBUTTONDOWN:
SetCapture(GuiData->hWindow);
dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
dwEventFlags = 0;
break;
case WM_RBUTTONDOWN:
SetCapture(GuiData->hWindow);
dwButtonState = RIGHTMOST_BUTTON_PRESSED;
dwEventFlags = 0;
break;
case WM_LBUTTONUP:
ReleaseCapture();
dwButtonState = 0;
dwEventFlags = 0;
break;
case WM_MBUTTONUP:
ReleaseCapture();
dwButtonState = 0;
dwEventFlags = 0;
break;
case WM_RBUTTONUP:
ReleaseCapture();
dwButtonState = 0;
dwEventFlags = 0;
break;
case WM_LBUTTONDBLCLK:
dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
dwEventFlags = DOUBLE_CLICK;
break;
case WM_MBUTTONDBLCLK:
dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
dwEventFlags = DOUBLE_CLICK;
break;
case WM_RBUTTONDBLCLK:
dwButtonState = RIGHTMOST_BUTTON_PRESSED;
dwEventFlags = DOUBLE_CLICK;
break;
case WM_MOUSEMOVE:
dwButtonState = 0;
dwEventFlags = MOUSE_MOVED;
break;
case WM_MOUSEWHEEL:
dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
dwEventFlags = MOUSE_WHEELED;
break;
case WM_MOUSEHWHEEL:
dwButtonState = GET_WHEEL_DELTA_WPARAM(wParam) << 16;
dwEventFlags = MOUSE_HWHEELED;
break;
default:
Err = TRUE;
break;
}
if (!Err)
{
if (wKeyState & MK_LBUTTON)
dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED;
if (wKeyState & MK_MBUTTON)
dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED;
if (wKeyState & MK_RBUTTON)
dwButtonState |= RIGHTMOST_BUTTON_PRESSED;
if (GetKeyState(VK_RMENU) & 0x8000)
dwControlKeyState |= RIGHT_ALT_PRESSED;
if (GetKeyState(VK_LMENU) & 0x8000)
dwControlKeyState |= LEFT_ALT_PRESSED;
if (GetKeyState(VK_RCONTROL) & 0x8000)
dwControlKeyState |= RIGHT_CTRL_PRESSED;
if (GetKeyState(VK_LCONTROL) & 0x8000)
dwControlKeyState |= LEFT_CTRL_PRESSED;
if (GetKeyState(VK_SHIFT) & 0x8000)
dwControlKeyState |= SHIFT_PRESSED;
if (GetKeyState(VK_NUMLOCK) & 0x0001)
dwControlKeyState |= NUMLOCK_ON;
if (GetKeyState(VK_SCROLL) & 0x0001)
dwControlKeyState |= SCROLLLOCK_ON;
if (GetKeyState(VK_CAPITAL) & 0x0001)
dwControlKeyState |= CAPSLOCK_ON;
/* See WM_CHAR MSDN documentation for instance */
if (lParam & 0x01000000)
dwControlKeyState |= ENHANCED_KEY;
er.EventType = MOUSE_EVENT;
er.Event.MouseEvent.dwMousePosition = PointToCoord(GuiData, lParam);
er.Event.MouseEvent.dwButtonState = dwButtonState;
er.Event.MouseEvent.dwControlKeyState = dwControlKeyState;
er.Event.MouseEvent.dwEventFlags = dwEventFlags;
ConioProcessInputEvent(Console, &er);
}
}
else
{
Err = TRUE;
}
LeaveCriticalSection(&Console->Lock);
Quit:
if (Err)
return DefWindowProcW(GuiData->hWindow, msg, wParam, lParam);
else
return 0;
}
VOID GuiCopyFromTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer);
VOID GuiCopyFromGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer);
static VOID
GuiConsoleCopy(PGUI_CONSOLE_DATA GuiData)
{
if (OpenClipboard(GuiData->hWindow))
{
PCONSOLE Console = GuiData->Console;
PCONSOLE_SCREEN_BUFFER Buffer = ConDrvGetActiveScreenBuffer(Console);
if (GetType(Buffer) == TEXTMODE_BUFFER)
{
GuiCopyFromTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer);
}
else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
{
GuiCopyFromGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer);
}
CloseClipboard();
/* Clear the selection */
GuiConsoleUpdateSelection(Console, NULL);
SetWindowText(GuiData->hWindow, Console->Title.Buffer);
}
}
VOID GuiPasteToTextModeBuffer(PTEXTMODE_SCREEN_BUFFER Buffer);
VOID GuiPasteToGraphicsBuffer(PGRAPHICS_SCREEN_BUFFER Buffer);
static VOID
GuiConsolePaste(PGUI_CONSOLE_DATA GuiData)
{
if (OpenClipboard(GuiData->hWindow))
{
PCONSOLE Console = GuiData->Console;
PCONSOLE_SCREEN_BUFFER Buffer = ConDrvGetActiveScreenBuffer(Console);
if (GetType(Buffer) == TEXTMODE_BUFFER)
{
GuiPasteToTextModeBuffer((PTEXTMODE_SCREEN_BUFFER)Buffer);
}
else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
{
GuiPasteToGraphicsBuffer((PGRAPHICS_SCREEN_BUFFER)Buffer);
}
CloseClipboard();
}
}
static VOID
GuiConsoleGetMinMaxInfo(PGUI_CONSOLE_DATA GuiData, PMINMAXINFO minMaxInfo)
{
PCONSOLE Console = GuiData->Console;
PCONSOLE_SCREEN_BUFFER ActiveBuffer;
DWORD windx, windy;
UINT WidthUnit, HeightUnit;
if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
ActiveBuffer = ConDrvGetActiveScreenBuffer(Console);
GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
windx = CONGUI_MIN_WIDTH * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
windy = CONGUI_MIN_HEIGHT * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
minMaxInfo->ptMinTrackSize.x = windx;
minMaxInfo->ptMinTrackSize.y = windy;
windx = (ActiveBuffer->ScreenBufferSize.X) * WidthUnit + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE));
windy = (ActiveBuffer->ScreenBufferSize.Y) * HeightUnit + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION);
if (ActiveBuffer->ViewSize.X < ActiveBuffer->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
if (ActiveBuffer->ViewSize.Y < ActiveBuffer->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
minMaxInfo->ptMaxTrackSize.x = windx;
minMaxInfo->ptMaxTrackSize.y = windy;
LeaveCriticalSection(&Console->Lock);
}
static VOID
GuiConsoleResize(PGUI_CONSOLE_DATA GuiData, WPARAM wParam, LPARAM lParam)
{
PCONSOLE Console = GuiData->Console;
if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return;
if ((GuiData->WindowSizeLock == FALSE) &&
(wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED))
{
PCONSOLE_SCREEN_BUFFER Buff = ConDrvGetActiveScreenBuffer(Console);
DWORD windx, windy, charx, chary;
UINT WidthUnit, HeightUnit;
GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
GuiData->WindowSizeLock = TRUE;
windx = LOWORD(lParam);
windy = HIWORD(lParam);
// Compensate for existing scroll bars (because lParam values do not accommodate scroll bar)
if (Buff->ViewSize.X < Buff->ScreenBufferSize.X) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar
if (Buff->ViewSize.Y < Buff->ScreenBufferSize.Y) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar
charx = windx / (int)WidthUnit ;
chary = windy / (int)HeightUnit;
// Character alignment (round size up or down)
if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx;
if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
// Compensate for added scroll bars in new window
if (charx < Buff->ScreenBufferSize.X) windy -= GetSystemMetrics(SM_CYHSCROLL); // new window will have a horizontal scroll bar
if (chary < Buff->ScreenBufferSize.Y) windx -= GetSystemMetrics(SM_CXVSCROLL); // new window will have a vertical scroll bar
charx = windx / (int)WidthUnit ;
chary = windy / (int)HeightUnit;
// Character alignment (round size up or down)
if ((windx % WidthUnit ) >= (WidthUnit / 2)) ++charx;
if ((windy % HeightUnit) >= (HeightUnit / 2)) ++chary;
// Resize window
if ((charx != Buff->ViewSize.X) || (chary != Buff->ViewSize.Y))
{
Buff->ViewSize.X = (charx <= Buff->ScreenBufferSize.X) ? charx : Buff->ScreenBufferSize.X;
Buff->ViewSize.Y = (chary <= Buff->ScreenBufferSize.Y) ? chary : Buff->ScreenBufferSize.Y;
}
GuiConsoleResizeWindow(GuiData);
// Adjust the start of the visible area if we are attempting to show nonexistent areas
if ((Buff->ScreenBufferSize.X - Buff->ViewOrigin.X) < Buff->ViewSize.X) Buff->ViewOrigin.X = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
if ((Buff->ScreenBufferSize.Y - Buff->ViewOrigin.Y) < Buff->ViewSize.Y) Buff->ViewOrigin.Y = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
InvalidateRect(GuiData->hWindow, NULL, TRUE);
GuiData->WindowSizeLock = FALSE;
}
LeaveCriticalSection(&Console->Lock);
}
/*
// HACK: This functionality is standard for general scrollbars. Don't add it by hand.
VOID
FASTCALL
GuiConsoleHandleScrollbarMenu(VOID)
{
HMENU hMenu;
hMenu = CreatePopupMenu();
if (hMenu == NULL)
{
DPRINT("CreatePopupMenu failed\n");
return;
}
//InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE);
//InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
//InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP);
//InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM);
//InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
//InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP);
//InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN);
//InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1);
//InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP);
//InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN);
}
*/
static LRESULT
GuiConsoleHandleScroll(PGUI_CONSOLE_DATA GuiData, UINT uMsg, WPARAM wParam)
{
PCONSOLE Console = GuiData->Console;
PCONSOLE_SCREEN_BUFFER Buff;
SCROLLINFO sInfo;
int fnBar;
int old_pos, Maximum;
PSHORT pShowXY;
if (!ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE)) return 0;
Buff = ConDrvGetActiveScreenBuffer(Console);
if (uMsg == WM_HSCROLL)
{
fnBar = SB_HORZ;
Maximum = Buff->ScreenBufferSize.X - Buff->ViewSize.X;
pShowXY = &Buff->ViewOrigin.X;
}
else
{
fnBar = SB_VERT;
Maximum = Buff->ScreenBufferSize.Y - Buff->ViewSize.Y;
pShowXY = &Buff->ViewOrigin.Y;
}
/* set scrollbar sizes */
sInfo.cbSize = sizeof(SCROLLINFO);
sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS;
if (!GetScrollInfo(GuiData->hWindow, fnBar, &sInfo)) goto Quit;
old_pos = sInfo.nPos;
switch (LOWORD(wParam))
{
case SB_LINELEFT:
sInfo.nPos -= 1;
break;
case SB_LINERIGHT:
sInfo.nPos += 1;
break;
case SB_PAGELEFT:
sInfo.nPos -= sInfo.nPage;
break;
case SB_PAGERIGHT:
sInfo.nPos += sInfo.nPage;
break;
case SB_THUMBTRACK:
sInfo.nPos = sInfo.nTrackPos;
ConioPause(Console, PAUSED_FROM_SCROLLBAR);
break;
case SB_THUMBPOSITION:
ConioUnpause(Console, PAUSED_FROM_SCROLLBAR);
break;
case SB_TOP:
sInfo.nPos = sInfo.nMin;
break;
case SB_BOTTOM:
sInfo.nPos = sInfo.nMax;
break;
default:
break;
}
sInfo.nPos = max(sInfo.nPos, 0);
sInfo.nPos = min(sInfo.nPos, Maximum);
if (old_pos != sInfo.nPos)
{
USHORT OldX = Buff->ViewOrigin.X;
USHORT OldY = Buff->ViewOrigin.Y;
UINT WidthUnit, HeightUnit;
*pShowXY = sInfo.nPos;
GetScreenBufferSizeUnits(Buff, GuiData, &WidthUnit, &HeightUnit);
ScrollWindowEx(GuiData->hWindow,
(OldX - Buff->ViewOrigin.X) * WidthUnit ,
(OldY - Buff->ViewOrigin.Y) * HeightUnit,
NULL,
NULL,
NULL,
NULL,
SW_INVALIDATE);
sInfo.fMask = SIF_POS;
SetScrollInfo(GuiData->hWindow, fnBar, &sInfo, TRUE);
UpdateWindow(GuiData->hWindow);
}
Quit:
LeaveCriticalSection(&Console->Lock);
return 0;
}
static LRESULT CALLBACK
GuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LRESULT Result = 0;
PGUI_CONSOLE_DATA GuiData = NULL;
PCONSOLE Console = NULL;
/*
* - If it's the first time we create a window for the terminal,
* just initialize it and return.
*
* - If we are destroying the window, just do it and return.
*/
if (msg == WM_NCCREATE)
{
return (LRESULT)GuiConsoleHandleNcCreate(hWnd, (LPCREATESTRUCTW)lParam);
}
else if (msg == WM_NCDESTROY)
{
return GuiConsoleHandleNcDestroy(hWnd);
}
/*
* Now the terminal window is initialized.
* Get the terminal data via the window's data.
* If there is no data, just go away.
*/
GuiData = GuiGetGuiData(hWnd);
if (GuiData == NULL) return DefWindowProcW(hWnd, msg, wParam, lParam);
/*
* Just retrieve a pointer to the console in case somebody needs it.
* It is not NULL because it was checked in GuiGetGuiData.
* Each helper function which needs the console has to validate and lock it.
*/
Console = GuiData->Console;
/* We have a console, start message dispatching */
switch (msg)
{
case WM_ACTIVATE:
{
if (LOWORD(wParam) == WA_CLICKACTIVE) GuiData->IgnoreNextMouseSignal = TRUE;
break;
}
case WM_CLOSE:
if (GuiConsoleHandleClose(GuiData)) goto Default;
break;
case WM_PAINT:
GuiConsoleHandlePaint(GuiData);
break;
case WM_KEYDOWN:
case WM_KEYUP:
case WM_CHAR:
case WM_DEADCHAR:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_SYSCHAR:
case WM_SYSDEADCHAR:
{
/* Detect Alt-Enter presses and switch back and forth to fullscreen mode */
if (msg == WM_SYSKEYDOWN && (HIWORD(lParam) & KF_ALTDOWN) && wParam == VK_RETURN)
{
/* Switch only at first Alt-Enter press, and ignore subsequent key repetitions */
if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT) GuiConsoleSwitchFullScreen(GuiData);
break;
}
GuiConsoleHandleKey(GuiData, msg, wParam, lParam);
break;
}
case WM_TIMER:
GuiConsoleHandleTimer(GuiData);
break;
case WM_SETCURSOR:
{
/*
* The message was sent because we are manually triggering a change.
* Check whether the mouse is indeed present on this console window
* and take appropriate decisions.
*/
if (wParam == -1 && lParam == -1)
{
POINT mouseCoords;
HWND hWndHit;
/* Get the placement of the mouse */
GetCursorPos(&mouseCoords);
/* On which window is placed the mouse ? */
hWndHit = WindowFromPoint(mouseCoords);
/* It's our window. Perform the hit-test to be used later on. */
if (hWndHit == hWnd)
{
wParam = (WPARAM)hWnd;
lParam = DefWindowProcW(hWndHit, WM_NCHITTEST, 0,
MAKELPARAM(mouseCoords.x, mouseCoords.y));
}
}
/* Set the mouse cursor only when we are in the client area */
if ((HWND)wParam == hWnd && LOWORD(lParam) == HTCLIENT)
{
if (GuiData->MouseCursorRefCount >= 0)
{
/* Show the cursor */
SetCursor(GuiData->hCursor);
}
else
{
/* Hide the cursor if the reference count is negative */
SetCursor(NULL);
}
return TRUE;
}
else
{
goto Default;
}
}
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
case WM_MOUSEMOVE:
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
{
Result = GuiConsoleHandleMouse(GuiData, msg, wParam, lParam);
break;
}
case WM_HSCROLL:
case WM_VSCROLL:
{
Result = GuiConsoleHandleScroll(GuiData, msg, wParam);
break;
}
case WM_NCRBUTTONDOWN:
{
DPRINT1("WM_NCRBUTTONDOWN\n");
/*
* HACK: !! Because, when we deal with WM_RBUTTON* and we do not
* call after that DefWindowProc, on ReactOS, right-clicks on the
* (non-client) application title-bar does not display the system
* menu and does not trigger a WM_NCRBUTTONUP message too.
* See: http://git.reactos.org/?p=reactos.git;a=blob;f=reactos/win32ss/user/user32/windows/defwnd.c;hb=HEAD#l1103
* and line 1135 too.
*/
if (DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam) == HTCAPTION)
{
/* Call DefWindowProcW with the WM_CONTEXTMENU message */
msg = WM_CONTEXTMENU;
}
goto Default;
}
// case WM_NCRBUTTONUP:
// DPRINT1("WM_NCRBUTTONUP\n");
// goto Default;
case WM_CONTEXTMENU:
{
if (DefWindowProcW(hWnd /*GuiData->hWindow*/, WM_NCHITTEST, 0, lParam) == HTCLIENT)
{
HMENU hMenu = CreatePopupMenu();
if (hMenu != NULL)
{
GuiConsoleAppendMenuItems(hMenu, GuiConsoleEditMenuItems);
TrackPopupMenuEx(hMenu,
TPM_RIGHTBUTTON,
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam),
hWnd,
NULL);
DestroyMenu(hMenu);
}
break;
}
else
{
goto Default;
}
}
case WM_INITMENU:
{
HMENU hMenu = (HMENU)wParam;
if (hMenu != NULL)
{
/* Enable or disable the Close menu item */
EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND |
(GuiData->IsCloseButtonEnabled ? MF_ENABLED : MF_GRAYED));
/* Enable or disable the Copy and Paste items */
EnableMenuItem(hMenu, ID_SYSTEM_EDIT_COPY , MF_BYCOMMAND |
((Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
(Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) ? MF_ENABLED : MF_GRAYED));
EnableMenuItem(hMenu, ID_SYSTEM_EDIT_PASTE, MF_BYCOMMAND |
(!(Console->Selection.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) &&
IsClipboardFormatAvailable(CF_UNICODETEXT) ? MF_ENABLED : MF_GRAYED));
}
if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
{
GuiSendMenuEvent(Console, WM_INITMENU);
LeaveCriticalSection(&Console->Lock);
}
break;
}
case WM_MENUSELECT:
{
if (HIWORD(wParam) == 0xFFFF) // Allow all the menu flags
{
if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
{
GuiSendMenuEvent(Console, WM_MENUSELECT);
LeaveCriticalSection(&Console->Lock);
}
}
break;
}
case WM_COMMAND:
case WM_SYSCOMMAND:
{
Result = GuiConsoleHandleSysMenuCommand(GuiData, wParam, lParam);
break;
}
case WM_SETFOCUS:
case WM_KILLFOCUS:
{
if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
{
INPUT_RECORD er;
er.EventType = FOCUS_EVENT;
er.Event.FocusEvent.bSetFocus = (msg == WM_SETFOCUS);
ConioProcessInputEvent(Console, &er);
if (msg == WM_SETFOCUS)
DPRINT1("TODO: Create console caret\n");
else // if (msg == WM_KILLFOCUS)
DPRINT1("TODO: Destroy console caret\n");
LeaveCriticalSection(&Console->Lock);
}
break;
}
case WM_GETMINMAXINFO:
GuiConsoleGetMinMaxInfo(GuiData, (PMINMAXINFO)lParam);
break;
case WM_SIZE:
GuiConsoleResize(GuiData, wParam, lParam);
break;
case PM_RESIZE_TERMINAL:
{
/* Resize the window to the user's values */
GuiData->WindowSizeLock = TRUE;
GuiConsoleResizeWindow(GuiData);
GuiData->WindowSizeLock = FALSE;
break;
}
case PM_APPLY_CONSOLE_INFO:
{
if (ConDrvValidateConsoleUnsafe(Console, CONSOLE_RUNNING, TRUE))
{
GuiApplyUserSettings(GuiData, (HANDLE)wParam, (BOOL)lParam);
LeaveCriticalSection(&Console->Lock);
}
break;
}
case PM_CONSOLE_BEEP:
DPRINT1("Beep !!\n");
Beep(800, 200);
break;
// case PM_CONSOLE_SET_TITLE:
// SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
// break;
default: Default:
Result = DefWindowProcW(hWnd, msg, wParam, lParam);
break;
}
return Result;
}
/******************************************************************************
* GUI Terminal Initialization *
******************************************************************************/
static LRESULT CALLBACK
GuiConsoleNotifyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND NewWindow;
LONG WindowCount;
MSG Msg;
switch (msg)
{
case WM_CREATE:
{
SetWindowLongW(hWnd, GWL_USERDATA, 0);
return 0;
}
case PM_CREATE_CONSOLE:
{
PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)lParam;
PCONSOLE Console = GuiData->Console;
NewWindow = CreateWindowExW(WS_EX_CLIENTEDGE,
GUI_CONSOLE_WINDOW_CLASS,
Console->Title.Buffer,
WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
ConSrvDllInstance,
(PVOID)GuiData);
if (NULL != NewWindow)
{
WindowCount = GetWindowLongW(hWnd, GWL_USERDATA);
WindowCount++;
SetWindowLongW(hWnd, GWL_USERDATA, WindowCount);
DPRINT("Set icons via PM_CREATE_CONSOLE\n");
if (GuiData->hIcon == NULL)
{
DPRINT("Not really /o\\...\n");
GuiData->hIcon = ghDefaultIcon;
GuiData->hIconSm = ghDefaultIconSm;
}
else if (GuiData->hIcon != ghDefaultIcon)
{
DPRINT("Yes \\o/\n");
SendMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon);
SendMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
}
/* Move and resize the window to the user's values */
/* CAN WE DEADLOCK ?? */
GuiConsoleMoveWindow(GuiData);
GuiData->WindowSizeLock = TRUE;
GuiConsoleResizeWindow(GuiData);
GuiData->WindowSizeLock = FALSE;
// ShowWindow(NewWindow, (int)wParam);
ShowWindowAsync(NewWindow, (int)wParam);
DPRINT("Window showed\n");
}
return (LRESULT)NewWindow;
}
case PM_DESTROY_CONSOLE:
{
PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)lParam;
/*
* Window creation is done using a PostMessage(), so it's possible
* that the window that we want to destroy doesn't exist yet.
* So first empty the message queue.
*/
/*
while (PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&Msg);
DispatchMessageW(&Msg);
}*/
while (PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE)) ;
if (GuiData->hWindow != NULL) /* && DestroyWindow(GuiData->hWindow) */
{
DestroyWindow(GuiData->hWindow);
WindowCount = GetWindowLongW(hWnd, GWL_USERDATA);
WindowCount--;
SetWindowLongW(hWnd, GWL_USERDATA, WindowCount);
if (0 == WindowCount)
{
NotifyWnd = NULL;
DestroyWindow(hWnd);
DPRINT("CONSRV: Going to quit the Gui Thread!!\n");
PostQuitMessage(0);
}
}
return 0;
}
default:
return DefWindowProcW(hWnd, msg, wParam, lParam);
}
}
static DWORD WINAPI
GuiConsoleGuiThread(PVOID Data)
{
MSG msg;
PHANDLE GraphicsStartupEvent = (PHANDLE)Data;
/*
* This thread dispatches all the console notifications to the notify window.
* It is common for all the console windows.
*/
PrivateCsrssManualGuiCheck(+1);
NotifyWnd = CreateWindowW(L"ConSrvCreateNotify",
L"",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
ConSrvDllInstance,
NULL);
if (NULL == NotifyWnd)
{
PrivateCsrssManualGuiCheck(-1);
SetEvent(*GraphicsStartupEvent);
return 1;
}
SetEvent(*GraphicsStartupEvent);
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
DPRINT("CONSRV: Quit the Gui Thread!!\n");
PrivateCsrssManualGuiCheck(-1);
return 1;
}
static BOOL
GuiInit(VOID)
{
WNDCLASSEXW wc;
ATOM ConsoleClassAtom;
/* Exit if we were already initialized */
// if (ConsInitialized) return TRUE;
/*
* Initialize and register the different window classes, if needed.
*/
if (!ConsInitialized)
{
/* Initialize the notification window class */
wc.cbSize = sizeof(WNDCLASSEXW);
wc.lpszClassName = L"ConSrvCreateNotify";
wc.lpfnWndProc = GuiConsoleNotifyWndProc;
wc.style = 0;
wc.hInstance = ConSrvDllInstance;
wc.hIcon = NULL;
wc.hIconSm = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
if (RegisterClassExW(&wc) == 0)
{
DPRINT1("Failed to register GUI notify wndproc\n");
return FALSE;
}
/* Initialize the console window class */
ghDefaultIcon = LoadImageW(ConSrvDllInstance,
MAKEINTRESOURCEW(IDI_TERMINAL),
IMAGE_ICON,
GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON),
LR_SHARED);
ghDefaultIconSm = LoadImageW(ConSrvDllInstance,
MAKEINTRESOURCEW(IDI_TERMINAL),
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_SHARED);
ghDefaultCursor = LoadCursorW(NULL, IDC_ARROW);
wc.cbSize = sizeof(WNDCLASSEXW);
wc.lpszClassName = GUI_CONSOLE_WINDOW_CLASS;
wc.lpfnWndProc = GuiConsoleWndProc;
wc.style = CS_DBLCLKS /* | CS_HREDRAW | CS_VREDRAW */;
wc.hInstance = ConSrvDllInstance;
wc.hIcon = ghDefaultIcon;
wc.hIconSm = ghDefaultIconSm;
wc.hCursor = ghDefaultCursor;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // The color of a terminal when it is switch off.
wc.lpszMenuName = NULL;
wc.cbClsExtra = 0;
wc.cbWndExtra = GWLP_CONSOLEWND_ALLOC;
ConsoleClassAtom = RegisterClassExW(&wc);
if (ConsoleClassAtom == 0)
{
DPRINT1("Failed to register GUI console wndproc\n");
return FALSE;
}
else
{
NtUserConsoleControl(GuiConsoleWndClassAtom, &ConsoleClassAtom, sizeof(ATOM));
}
ConsInitialized = TRUE;
}
/*
* Set-up the notification window
*/
if (NULL == NotifyWnd)
{
HANDLE ThreadHandle;
HANDLE GraphicsStartupEvent;
GraphicsStartupEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if (NULL == GraphicsStartupEvent) return FALSE;
ThreadHandle = CreateThread(NULL,
0,
GuiConsoleGuiThread,
(PVOID)&GraphicsStartupEvent,
0,
NULL);
if (NULL == ThreadHandle)
{
CloseHandle(GraphicsStartupEvent);
DPRINT1("CONSRV: Failed to create graphics console thread. Expect problems\n");
return FALSE;
}
SetThreadPriority(ThreadHandle, THREAD_PRIORITY_HIGHEST);
CloseHandle(ThreadHandle);
WaitForSingleObject(GraphicsStartupEvent, INFINITE);
CloseHandle(GraphicsStartupEvent);
if (NULL == NotifyWnd)
{
DPRINT1("CONSRV: Failed to create notification window.\n");
return FALSE;
}
}
// ConsInitialized = TRUE;
return TRUE;
}
/******************************************************************************
* GUI Console Driver *
******************************************************************************/
static VOID WINAPI
GuiDeinitFrontEnd(IN OUT PFRONTEND This);
NTSTATUS NTAPI
GuiInitFrontEnd(IN OUT PFRONTEND This,
IN PCONSOLE Console)
{
PGUI_INIT_INFO GuiInitInfo;
PCONSOLE_INFO ConsoleInfo;
PCONSOLE_START_INFO ConsoleStartInfo;
PGUI_CONSOLE_DATA GuiData;
GUI_CONSOLE_INFO TermInfo;
SIZE_T Length = 0;
LPWSTR IconPath = NULL;
INT IconIndex = 0;
if (This == NULL || Console == NULL || This->OldData == NULL)
return STATUS_INVALID_PARAMETER;
ASSERT(This->Console == Console);
GuiInitInfo = This->OldData;
if (GuiInitInfo->ConsoleInfo == NULL || GuiInitInfo->ConsoleStartInfo == NULL)
return STATUS_INVALID_PARAMETER;
ConsoleInfo = GuiInitInfo->ConsoleInfo;
ConsoleStartInfo = GuiInitInfo->ConsoleStartInfo;
IconPath = ConsoleStartInfo->IconPath;
IconIndex = ConsoleStartInfo->IconIndex;
/* Terminal data allocation */
GuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_CONSOLE_DATA));
if (!GuiData)
{
DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n");
return STATUS_UNSUCCESSFUL;
}
/* HACK */ Console->TermIFace.Data = (PVOID)GuiData; /* HACK */
GuiData->Console = Console;
GuiData->hWindow = NULL;
/* The console can be resized */
Console->FixedSize = FALSE;
InitializeCriticalSection(&GuiData->Lock);
/*
* Load terminal settings
*/
/* 1. Load the default settings */
GuiConsoleGetDefaultSettings(&TermInfo, GuiInitInfo->ProcessId);
/* 3. Load the remaining console settings via the registry. */
if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
{
/* Load the terminal infos from the registry. */
GuiConsoleReadUserSettings(&TermInfo,
ConsoleInfo->ConsoleTitle,
GuiInitInfo->ProcessId);
/*
* Now, update them with the properties the user might gave to us
* via the STARTUPINFO structure before calling CreateProcess
* (and which was transmitted via the ConsoleStartInfo structure).
* We therefore overwrite the values read in the registry.
*/
if (ConsoleStartInfo->dwStartupFlags & STARTF_USESHOWWINDOW)
{
TermInfo.ShowWindow = ConsoleStartInfo->ShowWindow;
}
if (ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
{
TermInfo.AutoPosition = FALSE;
TermInfo.WindowOrigin = ConsoleStartInfo->ConsoleWindowOrigin;
}
if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
{
TermInfo.FullScreen = TRUE;
}
}
/*
* Set up GUI data
*/
Length = min(wcslen(TermInfo.FaceName) + 1, LF_FACESIZE); // wcsnlen
wcsncpy(GuiData->GuiInfo.FaceName, TermInfo.FaceName, LF_FACESIZE);
GuiData->GuiInfo.FaceName[Length] = L'\0';
GuiData->GuiInfo.FontFamily = TermInfo.FontFamily;
GuiData->GuiInfo.FontSize = TermInfo.FontSize;
GuiData->GuiInfo.FontWeight = TermInfo.FontWeight;
GuiData->GuiInfo.UseRasterFonts = TermInfo.UseRasterFonts;
GuiData->GuiInfo.FullScreen = TermInfo.FullScreen;
GuiData->GuiInfo.ShowWindow = TermInfo.ShowWindow;
GuiData->GuiInfo.AutoPosition = TermInfo.AutoPosition;
GuiData->GuiInfo.WindowOrigin = TermInfo.WindowOrigin;
/* Initialize the icon handles to their default values */
GuiData->hIcon = ghDefaultIcon;
GuiData->hIconSm = ghDefaultIconSm;
/* Get the associated icon, if any */
if (IconPath == NULL || IconPath[0] == L'\0')
{
IconPath = ConsoleStartInfo->AppPath;
IconIndex = 0;
}
DPRINT("IconPath = %S ; IconIndex = %lu\n", (IconPath ? IconPath : L"n/a"), IconIndex);
if (IconPath && IconPath[0] != L'\0')
{
HICON hIcon = NULL, hIconSm = NULL;
PrivateExtractIconExW(IconPath,
IconIndex,
&hIcon,
&hIconSm,
1);
DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm);
if (hIcon != NULL)
{
DPRINT("Effectively set the icons\n");
GuiData->hIcon = hIcon;
GuiData->hIconSm = hIconSm;
}
}
/* Mouse is shown by default with its default cursor shape */
GuiData->hCursor = ghDefaultCursor;
GuiData->MouseCursorRefCount = 0;
/* A priori don't ignore mouse signals */
GuiData->IgnoreNextMouseSignal = FALSE;
/* Close button and the corresponding system menu item are enabled by default */
GuiData->IsCloseButtonEnabled = TRUE;
/* There is no user-reserved menu id range by default */
GuiData->cmdIdLow = GuiData->cmdIdHigh = 0;
/*
* We need to wait until the GUI has been fully initialized
* to retrieve custom settings i.e. WindowSize etc...
* Ideally we could use SendNotifyMessage for this but its not
* yet implemented.
*/
GuiData->hGuiInitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
DPRINT("GUI - Checkpoint\n");
/* Create the terminal window */
PostMessageW(NotifyWnd, PM_CREATE_CONSOLE, GuiData->GuiInfo.ShowWindow, (LPARAM)GuiData);
/* Wait until initialization has finished */
WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE);
DPRINT("OK we created the console window\n");
CloseHandle(GuiData->hGuiInitEvent);
GuiData->hGuiInitEvent = NULL;
/* Check whether we really succeeded in initializing the terminal window */
if (GuiData->hWindow == NULL)
{
DPRINT("GuiInitConsole - We failed at creating a new terminal window\n");
GuiDeinitFrontEnd(This);
return STATUS_UNSUCCESSFUL;
}
/* Finally, finish to initialize the frontend structure */
This->Data = GuiData;
if (This->OldData) ConsoleFreeHeap(This->OldData);
This->OldData = NULL;
return STATUS_SUCCESS;
}
static VOID WINAPI
GuiDeinitFrontEnd(IN OUT PFRONTEND This)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
SendMessageW(NotifyWnd, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData);
DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm);
if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
{
DPRINT("Destroy hIcon\n");
DestroyIcon(GuiData->hIcon);
}
if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
{
DPRINT("Destroy hIconSm\n");
DestroyIcon(GuiData->hIconSm);
}
This->Data = NULL;
DeleteCriticalSection(&GuiData->Lock);
ConsoleFreeHeap(GuiData);
DPRINT("Quit GuiDeinitFrontEnd\n");
}
static VOID WINAPI
GuiDrawRegion(IN OUT PFRONTEND This,
SMALL_RECT* Region)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
RECT RegionRect;
SmallRectToRect(GuiData, &RegionRect, Region);
/* Do not erase the background: it speeds up redrawing and reduce flickering */
InvalidateRect(GuiData->hWindow, &RegionRect, FALSE);
}
static VOID WINAPI
GuiWriteStream(IN OUT PFRONTEND This,
SMALL_RECT* Region,
SHORT CursorStartX,
SHORT CursorStartY,
UINT ScrolledLines,
PWCHAR Buffer,
UINT Length)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
PCONSOLE_SCREEN_BUFFER Buff;
SHORT CursorEndX, CursorEndY;
RECT ScrollRect;
if (NULL == GuiData || NULL == GuiData->hWindow) return;
Buff = ConDrvGetActiveScreenBuffer(GuiData->Console);
if (GetType(Buff) != TEXTMODE_BUFFER) return;
if (0 != ScrolledLines)
{
ScrollRect.left = 0;
ScrollRect.top = 0;
ScrollRect.right = Buff->ViewSize.X * GuiData->CharWidth;
ScrollRect.bottom = Region->Top * GuiData->CharHeight;
ScrollWindowEx(GuiData->hWindow,
0,
-(int)(ScrolledLines * GuiData->CharHeight),
&ScrollRect,
NULL,
NULL,
NULL,
SW_INVALIDATE);
}
GuiDrawRegion(This, Region);
if (CursorStartX < Region->Left || Region->Right < CursorStartX
|| CursorStartY < Region->Top || Region->Bottom < CursorStartY)
{
GuiInvalidateCell(This, CursorStartX, CursorStartY);
}
CursorEndX = Buff->CursorPosition.X;
CursorEndY = Buff->CursorPosition.Y;
if ((CursorEndX < Region->Left || Region->Right < CursorEndX
|| CursorEndY < Region->Top || Region->Bottom < CursorEndY)
&& (CursorEndX != CursorStartX || CursorEndY != CursorStartY))
{
GuiInvalidateCell(This, CursorEndX, CursorEndY);
}
// Set up the update timer (very short interval) - this is a "hack" for getting the OS to
// repaint the window without having it just freeze up and stay on the screen permanently.
Buff->CursorBlinkOn = TRUE;
SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
}
static BOOL WINAPI
GuiSetCursorInfo(IN OUT PFRONTEND This,
PCONSOLE_SCREEN_BUFFER Buff)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
if (ConDrvGetActiveScreenBuffer(GuiData->Console) == Buff)
{
GuiInvalidateCell(This, Buff->CursorPosition.X, Buff->CursorPosition.Y);
}
return TRUE;
}
static BOOL WINAPI
GuiSetScreenInfo(IN OUT PFRONTEND This,
PCONSOLE_SCREEN_BUFFER Buff,
SHORT OldCursorX,
SHORT OldCursorY)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
if (ConDrvGetActiveScreenBuffer(GuiData->Console) == Buff)
{
/* Redraw char at old position (remove cursor) */
GuiInvalidateCell(This, OldCursorX, OldCursorY);
/* Redraw char at new position (show cursor) */
GuiInvalidateCell(This, Buff->CursorPosition.X, Buff->CursorPosition.Y);
}
return TRUE;
}
static VOID WINAPI
GuiResizeTerminal(IN OUT PFRONTEND This)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
/* Resize the window to the user's values */
// GuiData->WindowSizeLock = TRUE;
// GuiConsoleResizeWindow(GuiData);
// GuiData->WindowSizeLock = FALSE;
// NOTE: This code ^^ causes deadlocks...
PostMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0);
}
static BOOL WINAPI
GuiProcessKeyCallback(IN OUT PFRONTEND This,
MSG* msg,
BYTE KeyStateMenu,
DWORD ShiftState,
UINT VirtualKeyCode,
BOOL Down)
{
if ((ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) || KeyStateMenu & 0x80) &&
(VirtualKeyCode == VK_ESCAPE || VirtualKeyCode == VK_TAB || VirtualKeyCode == VK_SPACE))
{
DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam);
return TRUE;
}
return FALSE;
}
static BOOL WINAPI
GuiSetMouseCursor(IN OUT PFRONTEND This,
HCURSOR hCursor);
static VOID WINAPI
GuiRefreshInternalInfo(IN OUT PFRONTEND This)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
/* Update the console leader information held by the window */
SetConsoleWndConsoleLeaderCID(GuiData);
/*
* HACK:
* We reset the cursor here so that, when a console app quits, we reset
* the cursor to the default one. It's quite a hack since it doesn't proceed
* per - console process... This must be fixed.
*
* See GuiInitConsole(...) for more information.
*/
/* Mouse is shown by default with its default cursor shape */
GuiData->MouseCursorRefCount = 0; // Reinitialize the reference counter
GuiSetMouseCursor(This, NULL);
}
static VOID WINAPI
GuiChangeTitle(IN OUT PFRONTEND This)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
// PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
SetWindowText(GuiData->hWindow, GuiData->Console->Title.Buffer);
}
static BOOL WINAPI
GuiChangeIcon(IN OUT PFRONTEND This,
HICON hWindowIcon)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
HICON hIcon, hIconSm;
if (hWindowIcon == NULL)
{
hIcon = ghDefaultIcon;
hIconSm = ghDefaultIconSm;
}
else
{
hIcon = CopyIcon(hWindowIcon);
hIconSm = CopyIcon(hWindowIcon);
}
if (hIcon == NULL)
{
return FALSE;
}
if (hIcon != GuiData->hIcon)
{
if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
{
DestroyIcon(GuiData->hIcon);
}
if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
{
DestroyIcon(GuiData->hIconSm);
}
GuiData->hIcon = hIcon;
GuiData->hIconSm = hIconSm;
DPRINT("Set icons in GuiChangeIcon\n");
PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG, (LPARAM)GuiData->hIcon);
PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
}
return TRUE;
}
static HWND WINAPI
GuiGetConsoleWindowHandle(IN OUT PFRONTEND This)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
return GuiData->hWindow;
}
static VOID WINAPI
GuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This,
PCOORD pSize)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
PCONSOLE_SCREEN_BUFFER ActiveBuffer;
RECT WorkArea;
LONG width, height;
UINT WidthUnit, HeightUnit;
if (!pSize) return;
if (!SystemParametersInfoW(SPI_GETWORKAREA, 0, &WorkArea, 0))
{
DPRINT1("SystemParametersInfoW failed - What to do ??\n");
return;
}
ActiveBuffer = ConDrvGetActiveScreenBuffer(GuiData->Console);
if (ActiveBuffer)
{
GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
}
else
{
/* Default: text mode */
WidthUnit = GuiData->CharWidth ;
HeightUnit = GuiData->CharHeight;
}
width = WorkArea.right;
height = WorkArea.bottom;
width -= (2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)));
height -= (2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION));
if (width < 0) width = 0;
if (height < 0) height = 0;
pSize->X = (SHORT)(width / (int)WidthUnit ) /* HACK */ + 2;
pSize->Y = (SHORT)(height / (int)HeightUnit) /* HACK */ + 1;
}
static ULONG WINAPI
GuiGetDisplayMode(IN OUT PFRONTEND This)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
ULONG DisplayMode = 0;
if (GuiData->GuiInfo.FullScreen)
DisplayMode |= CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN
else
DisplayMode |= CONSOLE_WINDOWED;
return DisplayMode;
}
static BOOL WINAPI
GuiSetDisplayMode(IN OUT PFRONTEND This,
ULONG NewMode)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
return FALSE;
GuiData->GuiInfo.FullScreen = (NewMode & CONSOLE_FULLSCREEN_MODE);
// TODO: Change the display mode
return TRUE;
}
static INT WINAPI
GuiShowMouseCursor(IN OUT PFRONTEND This,
BOOL Show)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
/* Set the reference count */
if (Show) ++GuiData->MouseCursorRefCount;
else --GuiData->MouseCursorRefCount;
/* Effectively show (or hide) the cursor (use special values for (w|l)Param) */
PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
return GuiData->MouseCursorRefCount;
}
static BOOL WINAPI
GuiSetMouseCursor(IN OUT PFRONTEND This,
HCURSOR hCursor)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
/*
* Set the cursor's handle. If the given handle is NULL,
* then restore the default cursor.
*/
GuiData->hCursor = (hCursor ? hCursor : ghDefaultCursor);
/* Effectively modify the shape of the cursor (use special values for (w|l)Param) */
PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
return TRUE;
}
static HMENU WINAPI
GuiMenuControl(IN OUT PFRONTEND This,
UINT cmdIdLow,
UINT cmdIdHigh)
{
PGUI_CONSOLE_DATA GuiData = This->Data;
GuiData->cmdIdLow = cmdIdLow ;
GuiData->cmdIdHigh = cmdIdHigh;
return GetSystemMenu(GuiData->hWindow, FALSE);
}
static BOOL WINAPI
GuiSetMenuClose(IN OUT PFRONTEND This,
BOOL Enable)
{
/*
* NOTE: See http://www.mail-archive.com/harbour@harbour-project.org/msg27509.html
* or http://harbour-devel.1590103.n2.nabble.com/Question-about-hb-gt-win-CtrlHandler-usage-td4670862i20.html
* for more information.
*/
PGUI_CONSOLE_DATA GuiData = This->Data;
HMENU hSysMenu = GetSystemMenu(GuiData->hWindow, FALSE);
if (hSysMenu == NULL) return FALSE;
GuiData->IsCloseButtonEnabled = Enable;
EnableMenuItem(hSysMenu, SC_CLOSE, MF_BYCOMMAND | (Enable ? MF_ENABLED : MF_GRAYED));
return TRUE;
}
static FRONTEND_VTBL GuiVtbl =
{
GuiInitFrontEnd,
GuiDeinitFrontEnd,
GuiDrawRegion,
GuiWriteStream,
GuiSetCursorInfo,
GuiSetScreenInfo,
GuiResizeTerminal,
GuiProcessKeyCallback,
GuiRefreshInternalInfo,
GuiChangeTitle,
GuiChangeIcon,
GuiGetConsoleWindowHandle,
GuiGetLargestConsoleWindowSize,
GuiGetDisplayMode,
GuiSetDisplayMode,
GuiShowMouseCursor,
GuiSetMouseCursor,
GuiMenuControl,
GuiSetMenuClose,
};
static BOOL
LoadShellLinkConsoleInfo(IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
IN OUT PCONSOLE_INFO ConsoleInfo)
{
#define PATH_SEPARATOR L'\\'
BOOL RetVal = FALSE;
HRESULT hRes = S_OK;
LPWSTR LinkName = NULL;
SIZE_T Length = 0;
if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
return FALSE;
ConsoleStartInfo->IconPath[0] = L'\0';
ConsoleStartInfo->IconIndex = 0;
/* 1- Find the last path separator if any */
LinkName = wcsrchr(ConsoleStartInfo->ConsoleTitle, PATH_SEPARATOR);
if (LinkName == NULL)
{
LinkName = ConsoleStartInfo->ConsoleTitle;
}
else
{
/* Skip the path separator */
++LinkName;
}
/* 2- Check for the link extension. The name ".lnk" is considered invalid. */
Length = wcslen(LinkName);
if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) )
return FALSE;
/* 3- It may be a link. Try to retrieve some properties */
hRes = CoInitialize(NULL);
if (SUCCEEDED(hRes))
{
/* Get a pointer to the IShellLink interface */
IShellLinkW* pshl = NULL;
hRes = CoCreateInstance(&CLSID_ShellLink,
NULL,
CLSCTX_INPROC_SERVER,
&IID_IShellLinkW,
(LPVOID*)&pshl);
if (SUCCEEDED(hRes))
{
/* Get a pointer to the IPersistFile interface */
IPersistFile* ppf = NULL;
hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hRes))
{
/* Load the shortcut */
hRes = IPersistFile_Load(ppf, ConsoleStartInfo->ConsoleTitle, STGM_READ);
if (SUCCEEDED(hRes))
{
/*
* Finally we can get the properties !
* Update the old ones if needed.
*/
INT ShowCmd = 0;
// WORD HotKey = 0;
/* Reset the name of the console with the name of the shortcut */
Length = min(/*Length*/ Length - 4, // 4 == len(".lnk")
sizeof(ConsoleInfo->ConsoleTitle) / sizeof(ConsoleInfo->ConsoleTitle[0]) - 1);
wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length);
ConsoleInfo->ConsoleTitle[Length] = L'\0';
/* Get the window showing command */
hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd);
if (SUCCEEDED(hRes)) ConsoleStartInfo->ShowWindow = (WORD)ShowCmd;
/* Get the hotkey */
// hRes = pshl->GetHotkey(&ShowCmd);
// if (SUCCEEDED(hRes)) ConsoleStartInfo->HotKey = HotKey;
/* Get the icon location, if any */
hRes = IShellLinkW_GetIconLocation(pshl,
ConsoleStartInfo->IconPath,
sizeof(ConsoleStartInfo->IconPath)/sizeof(ConsoleStartInfo->IconPath[0]) - 1, // == MAX_PATH
&ConsoleStartInfo->IconIndex);
if (!SUCCEEDED(hRes))
{
ConsoleStartInfo->IconPath[0] = L'\0';
ConsoleStartInfo->IconIndex = 0;
}
// FIXME: Since we still don't load console properties from the shortcut,
// return false. When this will be done, we will return true instead.
RetVal = FALSE;
}
IPersistFile_Release(ppf);
}
IShellLinkW_Release(pshl);
}
}
CoUninitialize();
return RetVal;
}
NTSTATUS NTAPI
GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
IN OUT PCONSOLE_INFO ConsoleInfo,
IN OUT PVOID ExtraConsoleInfo,
IN ULONG ProcessId)
{
PCONSOLE_START_INFO ConsoleStartInfo = ExtraConsoleInfo;
PGUI_INIT_INFO GuiInitInfo;
if (FrontEnd == NULL || ConsoleInfo == NULL || ConsoleStartInfo == NULL)
return STATUS_INVALID_PARAMETER;
/* Initialize GUI terminal emulator common functionalities */
if (!GuiInit()) return STATUS_UNSUCCESSFUL;
/*
* Load per-application terminal settings.
*
* Check whether the process creating the console was launched via
* a shell-link. ConsoleInfo->ConsoleTitle may be updated with the
* name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too.
*/
if (ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME)
{
if (!LoadShellLinkConsoleInfo(ConsoleStartInfo, ConsoleInfo))
{
ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
}
}
/*
* Initialize a private initialization info structure for later use.
* It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd.
*/
GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_INIT_INFO));
if (GuiInitInfo == NULL) return STATUS_NO_MEMORY;
// HACK: We suppose that the pointers will be valid in GuiInitFrontEnd...
GuiInitInfo->ConsoleInfo = ConsoleInfo;
GuiInitInfo->ConsoleStartInfo = ConsoleStartInfo;
GuiInitInfo->ProcessId = ProcessId;
/* Finally, initialize the frontend structure */
FrontEnd->Vtbl = &GuiVtbl;
FrontEnd->Data = NULL;
FrontEnd->OldData = GuiInitInfo;
return STATUS_SUCCESS;
}
NTSTATUS NTAPI
GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd)
{
if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
if (FrontEnd->Data) GuiDeinitFrontEnd(FrontEnd);
if (FrontEnd->OldData) ConsoleFreeHeap(FrontEnd->OldData);
return STATUS_SUCCESS;
}
/* EOF */