Skip to content

Commit

Permalink
Implement on demand stdio allocation
Browse files Browse the repository at this point in the history
  • Loading branch information
TheNNX committed Apr 14, 2024
1 parent b52ee3c commit b70902e
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 19 deletions.
2 changes: 1 addition & 1 deletion COREDLL/Exports.def
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ EXPORTS
_wcsupr @232
vfwprintf=vfwprintf_WCECL @721
fflush @1122
fgetws=fgetws_WCECL @1143
fgetws @1143
fputwc @1141
fputws @1144
_getstdfilex=_getstdfilex_WCECL @1100
Expand Down
22 changes: 6 additions & 16 deletions COREDLL/other.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ void CeLogSetZones(DWORD dwZoneUser, // User-defined zones

void* _fileno_WCECL(FILE* file)
{
void* result = WceclTryGetOrAllocStdHandle(file);
if (result != NULL)
{
return result;
}

return (void*)_get_osfhandle(_fileno(file));
}

Expand All @@ -220,22 +226,6 @@ int WINAPI WideCharToMultiByte_WCECL(
lpUsedDefaultChar);
}

wchar_t* fgetws_WCECL(wchar_t* w, int count, FILE* file)
{
wchar_t* result = fgetws(w, count, file);
if (result == NULL &&
file == stdin && count > 2)
{
result = w;
wsprintf(w, L"");
}
else
{
return result;
}
return result;
}

// Stubs
Stub(_chkstk_WCECL);
Stub(WaitForAPIReady);
Expand Down
194 changes: 192 additions & 2 deletions COREDLL/stdio_wcecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,47 @@
#include <assert.h>
#include <io.h>

/* https://learn.microsoft.com/en-us/windows/console/clearing-the-screen */
static BOOL WceclConsoleClearScreen(
HANDLE hDevice)
{
CHAR_INFO charInfo;
COORD scrollTarget;
SMALL_RECT scrollRect;
CONSOLE_SCREEN_BUFFER_INFO screenInfo;

if (!GetConsoleScreenBufferInfo(hDevice, &screenInfo))
{
return FALSE;
}

charInfo.Char.UnicodeChar = L' ';
charInfo.Attributes = screenInfo.wAttributes;

scrollRect.Left = 0;
scrollRect.Top = 0;
scrollRect.Right = screenInfo.dwSize.X;
scrollRect.Bottom = screenInfo.dwSize.Y;

scrollTarget.X = 0;
scrollTarget.Y = -screenInfo.dwSize.Y;

if (ScrollConsoleScreenBufferW(
hDevice,
&scrollRect,
NULL,
scrollTarget,
&charInfo) == 0)
{
return FALSE;
}

screenInfo.dwCursorPosition.X = 0;
screenInfo.dwCursorPosition.Y = 0;

SetConsoleCursorPosition(hDevice, screenInfo.dwCursorPosition);
}

static DWORD GetWin32ConsoleModeFromWce(DWORD wceConsoleMode)
{
DWORD out = 0;
Expand Down Expand Up @@ -102,6 +143,44 @@ static BOOL WceclConsoleGetTitle(
return GetConsoleTitleA(lpOutBuf, nOutBufSize);
}


static BOOL WceclConsoleGetRowsCols(
HANDLE hDevice,
PDWORD lpCols,
PDWORD lpRows,
DWORD nOutBufSize)
{
CONSOLE_SCREEN_BUFFER_INFO screenInfo;
DWORD win32ConsoleMode;
BOOL result;

if (nOutBufSize < sizeof(DWORD))
{
return FALSE;
}

if (!GetConsoleScreenBufferInfo(hDevice, &screenInfo))
{
return FALSE;
}

if (lpRows != NULL)
{
*lpRows = screenInfo.dwSize.Y;
}
if (lpCols != NULL)
{
*lpCols = screenInfo.dwSize.X;
}
if (lpCols == NULL && lpRows == NULL)
{
return FALSE;
}

return TRUE;
}


BOOL WceclConsoleIoControl(
HANDLE hDevice,
DWORD dwIoControlCode,
Expand All @@ -123,8 +202,13 @@ BOOL WceclConsoleIoControl(
case IOCTL_CONSOLE_GETTITLE:
return WceclConsoleGetTitle((LPSTR)lpOutBuf, nOutBufSize);
case IOCTL_CONSOLE_CLS:
/* TODO */
return TRUE;
return WceclConsoleClearScreen(hDevice);
case IOCTL_CONSOLE_FLUSHINPUT:
return FlushConsoleInputBuffer(hDevice);
case IOCTL_CONSOLE_GETSCREENROWS:
return WceclConsoleGetRowsCols(hDevice, NULL, (PDWORD)lpOutBuf, nOutBufSize);
case IOCTL_CONSOLE_GETSCREENCOLS:
return WceclConsoleGetRowsCols(hDevice, (PDWORD)lpOutBuf, NULL, nOutBufSize);
default:
/* TODO */
return FALSE;
Expand Down Expand Up @@ -182,4 +266,110 @@ BOOL WINAPI GetStdioPathW_WCECL(
}

return FALSE;
}

static BOOL WceclTryGetStdHandle(FILE* file, PHANDLE handle)
{
if (file == stdin)
{
*handle = GetStdHandle(STD_INPUT_HANDLE);
}
else if (file == stdout)
{
*handle = GetStdHandle(STD_OUTPUT_HANDLE);
}
else if (file == stderr)
{
*handle = GetStdHandle(STD_ERROR_HANDLE);
}
else
{
return FALSE;
}
return TRUE;
}

static BOOL WceclAllocateStdio()
{
HWND hWndConsole;
BOOL bConsoleAllocated;

HANDLE hOldStdIn, hOldStdOut, hOldStdErr;

hWndConsole = GetConsoleWindow();

hOldStdIn = GetStdHandle(STD_INPUT_HANDLE);
hOldStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
hOldStdErr = GetStdHandle(STD_ERROR_HANDLE);

if (hWndConsole == NULL)
{
AllocConsole();
hWndConsole = GetConsoleWindow();

if (hWndConsole == NULL)
{
return FALSE;
}

bConsoleAllocated = TRUE;
}
else
{
bConsoleAllocated = FALSE;
}

assert(hOldStdErr == NULL || hOldStdIn == NULL || hOldStdOut == NULL);

if (hOldStdIn == NULL)
{
if (freopen("CONIN$", "r", stdin) == NULL)
{
goto CLEANUP;
}
}
if (hOldStdOut == NULL)
{
if(freopen("CONOUT$", "w", stdout) == NULL)
{
goto CLEANUP;
}
}
if (hOldStdErr == NULL)
{
if(freopen("CONERR$", "w", stderr) == NULL)
{
goto CLEANUP;
}
}

return TRUE;
CLEANUP:
if (bConsoleAllocated)
{
FreeConsole();
}
return FALSE;
}

/* It seems that CE programs launch a console only when it is about to be
used. */
HANDLE WceclTryGetOrAllocStdHandle(FILE* file)
{
HANDLE hFile;

if (WceclTryGetStdHandle(file, &hFile) == FALSE)
{
return NULL;
}

if (hFile == NULL)
{
if (WceclAllocateStdio() == FALSE)
{
return FALSE;
}
}

return hFile;
}
32 changes: 32 additions & 0 deletions COREDLL/stdio_wcecl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once
#include "stdafx.h"

#define FILE_DEVICE_CONSOLE 0x0102

#define CECONSOLE_MODE_ECHO_INPUT 0x01
#define CECONSOLE_MODE_LINE_INPUT 0x02
#define CECONSOLE_MODE_PROCESSED_OUTPUT 0x04

#define CONSOLE_CTL(i) ((FILE_DEVICE_CONSOLE<<16)|(i << 2))
#define IOCTL_CONSOLE_SETMODE CONSOLE_CTL(1)
#define IOCTL_CONSOLE_GETMODE CONSOLE_CTL(2)
#define IOCTL_CONSOLE_SETTITLE CONSOLE_CTL(3)
#define IOCTL_CONSOLE_GETTITLE CONSOLE_CTL(4)
#define IOCTL_CONSOLE_CLS CONSOLE_CTL(5)
#define IOCTL_CONSOLE_FLUSHINPUT CONSOLE_CTL(6)
#define IOCTL_CONSOLE_GETSCREENROWS CONSOLE_CTL(7)
#define IOCTL_CONSOLE_SETCONTROLCHANDLER CONSOLE_CTL(8)
#define IOCTL_CONSOLE_GETSCREENCOLS CONSOLE_CTL(9)
#define IOCTL_CONSOLE_SETCONTROLCEVENT CONSOLE_CTL(10)

BOOL WceclConsoleIoControl(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuf,
DWORD nInBufSize,
LPVOID lpOutBuf,
DWORD nOutBufSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped);

HANDLE WceclTryGetOrAllocStdHandle(FILE* file);

0 comments on commit b70902e

Please sign in to comment.