reactos/win32ss/user/winsrv/consrv/frontends/gui/guisettings.c
Hermès Bélusca-Maïto 5d3915d0fc
[CONCFG:FONT][CONSRV] Use a suitable font when changing console output CP; fail if none found. (#4337)
CORE-12451, CORE-17601, CORE-17803
Replaces PR #4281.

When changing the console output code page, check whether the current
font can support it. If not, try to find a suitable font for the new
code page. If none can be found:

- if we are creating a new console, forcefully switch to codepage 437
  (OEM USA) and retry finding a font, falling back to "Terminal" if
  none could be found;

- if we were just changing the current CP, just fail and keep the old
  code page and font.

Rework the console font selection/creation functions for this new job
(see CreateConsoleFontEx() and friends). Elements of implementation
based from https://github.com/microsoft/terminal ; see code for more
information.

Silence the noisy IsValidConsoleFont2() diagnostic messages.

Add Doxygen documentation.

[CONSOLE.CPL] Only add "Terminal" to the enumerated list of faces
+ add a TODO implementation comment.
2022-02-08 15:59:07 +01:00

384 lines
13 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Server DLL
* FILE: win32ss/user/winsrv/consrv/frontends/gui/guisettings.c
* PURPOSE: GUI Terminal Front-End Settings Management
* PROGRAMMERS: Johannes Anderwald
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
/* INCLUDES *******************************************************************/
#include <consrv.h>
#define NDEBUG
#include <debug.h>
#include "concfg/font.h"
#include "guiterm.h"
#include "guisettings.h"
/* FUNCTIONS ******************************************************************/
BOOL
GuiConsoleReadUserSettings(IN OUT PGUI_CONSOLE_INFO TermInfo)
{
/* Do nothing */
return TRUE;
}
BOOL
GuiConsoleWriteUserSettings(IN OUT PGUI_CONSOLE_INFO TermInfo)
{
/* Do nothing */
return TRUE;
}
VOID
GuiConsoleGetDefaultSettings(IN OUT PGUI_CONSOLE_INFO TermInfo)
{
/* Do nothing */
}
VOID
GuiConsoleShowConsoleProperties(PGUI_CONSOLE_DATA GuiData,
BOOL Defaults)
{
NTSTATUS Status;
PCONSRV_CONSOLE Console = GuiData->Console;
PCONSOLE_PROCESS_DATA ProcessData;
HANDLE hSection = NULL, hClientSection = NULL;
PVOID ThreadParameter = NULL; // Is either hClientSection or the console window handle,
// depending on whether we display the default settings or
// the settings of a particular console.
DPRINT("GuiConsoleShowConsoleProperties entered\n");
if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
/* Get the console leader process, our client */
ProcessData = ConSrvGetConsoleLeaderProcess(Console);
/*
* Be sure we effectively have a properties dialog routine (that launches
* the console control panel applet). It resides in kernel32.dll (client).
*/
if (ProcessData->PropRoutine == NULL) goto Quit;
/*
* Create a memory section to be shared with the console control panel applet
* in the case we are displaying the settings of a particular console.
* In that case the ThreadParameter is the hClientSection handle.
* In the case we display the default console parameters, we don't need to
* create a memory section. We just need to open the applet, and in this case
* the ThreadParameter is the parent window handle of the applet's window,
* that is, the console window.
*/
if (!Defaults)
{
PCONSOLE_SCREEN_BUFFER ActiveBuffer = GuiData->ActiveBuffer;
LARGE_INTEGER SectionSize;
SIZE_T ViewSize = 0;
PCONSOLE_STATE_INFO pSharedInfo = NULL;
/*
* Create a memory section to share with the applet, and map it.
*/
SectionSize.QuadPart = sizeof(CONSOLE_STATE_INFO); // Standard size
SectionSize.QuadPart += Console->OriginalTitle.Length; // Add the length in bytes of the console title string
Status = NtCreateSection(&hSection,
SECTION_ALL_ACCESS,
NULL,
&SectionSize,
PAGE_READWRITE,
SEC_COMMIT,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("Error: Impossible to create a shared section, Status = 0x%08lx\n", Status);
goto Quit;
}
Status = NtMapViewOfSection(hSection,
NtCurrentProcess(),
(PVOID*)&pSharedInfo,
0,
0,
NULL,
&ViewSize,
ViewUnmap,
0,
PAGE_READWRITE);
if (!NT_SUCCESS(Status))
{
DPRINT1("Error: Impossible to map the shared section, Status = 0x%08lx\n", Status);
goto Quit;
}
/*
* Setup the shared console properties structure.
*/
/* Store the real size of the structure */
pSharedInfo->cbSize = SectionSize.QuadPart;
/*
* When we setup the settings of a particular console, the parent window
* of the applet's window is the console window, and it is given via the
* hWnd member of the shared console info structure.
*/
pSharedInfo->hWnd = GuiData->hWindow;
/* Console information */
pSharedInfo->QuickEdit = Console->QuickEdit;
pSharedInfo->InsertMode = Console->InsertMode;
pSharedInfo->NumberOfHistoryBuffers = Console->MaxNumberOfHistoryBuffers;
pSharedInfo->HistoryBufferSize = Console->HistoryBufferSize;
pSharedInfo->HistoryNoDup = Console->HistoryNoDup;
/// pSharedInfo->InputBufferSize = 0;
pSharedInfo->ScreenBufferSize = ActiveBuffer->ScreenBufferSize;
pSharedInfo->WindowSize = ActiveBuffer->ViewSize;
pSharedInfo->CursorSize = ActiveBuffer->CursorInfo.dwSize;
if (GetType(ActiveBuffer) == TEXTMODE_BUFFER)
{
PTEXTMODE_SCREEN_BUFFER Buffer = (PTEXTMODE_SCREEN_BUFFER)ActiveBuffer;
pSharedInfo->ScreenAttributes = Buffer->ScreenDefaultAttrib;
pSharedInfo->PopupAttributes = Buffer->PopupDefaultAttrib;
}
else // if (GetType(ActiveBuffer) == GRAPHICS_BUFFER)
{
// PGRAPHICS_SCREEN_BUFFER Buffer = (PGRAPHICS_SCREEN_BUFFER)ActiveBuffer;
// FIXME: Gather defaults from the registry ?
pSharedInfo->ScreenAttributes = DEFAULT_SCREEN_ATTRIB;
pSharedInfo->PopupAttributes = DEFAULT_POPUP_ATTRIB;
}
/* We display the output code page only */
pSharedInfo->CodePage = Console->OutputCodePage;
/* GUI Information */
StringCchCopyNW(pSharedInfo->FaceName, ARRAYSIZE(pSharedInfo->FaceName),
GuiData->GuiInfo.FaceName, ARRAYSIZE(GuiData->GuiInfo.FaceName));
pSharedInfo->FontWeight = GuiData->GuiInfo.FontWeight;
pSharedInfo->FontFamily = GuiData->GuiInfo.FontFamily;
pSharedInfo->FontSize = GuiData->GuiInfo.FontSize;
pSharedInfo->FullScreen = GuiData->GuiInfo.FullScreen;
pSharedInfo->AutoPosition = GuiData->GuiInfo.AutoPosition;
pSharedInfo->WindowPosition = GuiData->GuiInfo.WindowOrigin;
/* Palette */
RtlCopyMemory(pSharedInfo->ColorTable,
Console->Colors, sizeof(Console->Colors));
/* Copy the original title of the console and null-terminate it */
RtlCopyMemory(pSharedInfo->ConsoleTitle,
Console->OriginalTitle.Buffer,
Console->OriginalTitle.Length);
pSharedInfo->ConsoleTitle[Console->OriginalTitle.Length / sizeof(WCHAR)] = UNICODE_NULL;
/* Unmap the view */
NtUnmapViewOfSection(NtCurrentProcess(), pSharedInfo);
/* Duplicate the section handle for the client */
Status = NtDuplicateObject(NtCurrentProcess(),
hSection,
ProcessData->Process->ProcessHandle,
&hClientSection,
0, 0, DUPLICATE_SAME_ACCESS);
if (!NT_SUCCESS(Status))
{
DPRINT1("Error: Impossible to duplicate section handle for client, Status = 0x%08lx\n", Status);
goto Quit;
}
/* For the settings of a particular console, use the shared client section handle as the thread parameter */
ThreadParameter = (PVOID)hClientSection;
}
else
{
/* For the default settings, use the console window handle as the thread parameter */
ThreadParameter = (PVOID)GuiData->hWindow;
}
/* Start the console control panel applet */
_SEH2_TRY
{
HANDLE Thread = NULL;
_SEH2_TRY
{
Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
ProcessData->PropRoutine,
ThreadParameter, 0, NULL);
if (NULL == Thread)
{
DPRINT1("Failed thread creation (Error: 0x%x)\n", GetLastError());
}
else
{
DPRINT("ProcessData->PropRoutine remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n",
ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
}
}
_SEH2_FINALLY
{
CloseHandle(Thread);
}
_SEH2_END;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
DPRINT1("GuiConsoleShowConsoleProperties - Caught an exception, Status = 0x%08lx\n", Status);
}
_SEH2_END;
Quit:
/* We have finished, close the section handle if any */
if (hSection) NtClose(hSection);
LeaveCriticalSection(&Console->Lock);
return;
}
/*
* Function for dealing with the undocumented message and structure used by
* Windows' console.dll for setting console info.
* See http://www.catch22.net/sites/default/source/files/setconsoleinfo.c
* and http://www.scn.rain.com/~neighorn/PDF/MSBugPaper.pdf
* for more information.
*/
VOID
GuiApplyUserSettings(PGUI_CONSOLE_DATA GuiData,
HANDLE hClientSection)
{
NTSTATUS Status = STATUS_SUCCESS;
PCONSRV_CONSOLE Console = GuiData->Console;
PCONSOLE_PROCESS_DATA ProcessData;
HANDLE hSection = NULL;
SIZE_T ViewSize = 0;
PCONSOLE_STATE_INFO pConInfo = NULL;
if (!ConDrvValidateConsoleUnsafe((PCONSOLE)Console, CONSOLE_RUNNING, TRUE)) return;
/* Get the console leader process, our client */
ProcessData = ConSrvGetConsoleLeaderProcess(Console);
/* Duplicate the section handle for ourselves */
Status = NtDuplicateObject(ProcessData->Process->ProcessHandle,
hClientSection,
NtCurrentProcess(),
&hSection,
0, 0, DUPLICATE_SAME_ACCESS);
if (!NT_SUCCESS(Status))
{
DPRINT1("Error when mapping client handle, Status = 0x%08lx\n", Status);
goto Quit;
}
/* Get a view of the shared section */
Status = NtMapViewOfSection(hSection,
NtCurrentProcess(),
(PVOID*)&pConInfo,
0,
0,
NULL,
&ViewSize,
ViewUnmap,
0,
PAGE_READWRITE);
if (!NT_SUCCESS(Status))
{
DPRINT1("Error when mapping view of file, Status = 0x%08lx\n", Status);
goto Quit;
}
_SEH2_TRY
{
/* Check that the section is well-sized */
if ( (ViewSize < sizeof(CONSOLE_STATE_INFO)) ||
(pConInfo->cbSize < sizeof(CONSOLE_STATE_INFO)) )
{
DPRINT1("Error: section bad-sized: sizeof(Section) < sizeof(CONSOLE_STATE_INFO)\n");
Status = STATUS_INVALID_VIEW_SIZE;
_SEH2_YIELD(goto Quit);
}
// TODO: Check that GuiData->hWindow == pConInfo->hWnd
/* Console information */
/*
* Apply the settings
*/
/* Refresh the additional TrueType fonts cache now,
* as ConSrvApplyUserSettings() could change the output
* code page and trigger a font change in the terminal. */
RefreshTTFontCache();
/* Apply the generic console settings */
ConSrvApplyUserSettings(Console, pConInfo);
/* Set the terminal settings */
/* Now, attempt to change the font to what the user specified */
InitFonts(GuiData,
pConInfo->FaceName,
pConInfo->FontWeight,
pConInfo->FontFamily,
pConInfo->FontSize,
0, FALSE);
/* Move the window to the user's values */
GuiData->GuiInfo.AutoPosition = !!pConInfo->AutoPosition;
GuiData->GuiInfo.WindowOrigin = pConInfo->WindowPosition;
GuiConsoleMoveWindow(GuiData);
InvalidateRect(GuiData->hWindow, NULL, TRUE);
/*
* Apply full-screen mode.
*/
if (!!pConInfo->FullScreen != GuiData->GuiInfo.FullScreen)
{
SwitchFullScreen(GuiData, !!pConInfo->FullScreen);
}
/*
* The settings are saved in the registry by console.dll itself, if needed.
*/
// if (SaveSettings)
// {
// GuiConsoleWriteUserSettings(GuiInfo);
// }
Status = STATUS_SUCCESS;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
DPRINT1("GuiApplyUserSettings - Caught an exception, Status = 0x%08lx\n", Status);
}
_SEH2_END;
Quit:
/* Finally, close the section and return */
if (hSection)
{
NtUnmapViewOfSection(NtCurrentProcess(), pConInfo);
NtClose(hSection);
}
LeaveCriticalSection(&Console->Lock);
return;
}
/* EOF */