diff --git a/COREDLL/Exports.def b/COREDLL/Exports.def index 5b139af..36295d3 100644 --- a/COREDLL/Exports.def +++ b/COREDLL/Exports.def @@ -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 diff --git a/COREDLL/other.cpp b/COREDLL/other.cpp index 6112ed8..0ac01ac 100644 --- a/COREDLL/other.cpp +++ b/COREDLL/other.cpp @@ -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)); } @@ -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); diff --git a/COREDLL/stdio_wcecl.cpp b/COREDLL/stdio_wcecl.cpp index 57c1bda..72888ef 100644 --- a/COREDLL/stdio_wcecl.cpp +++ b/COREDLL/stdio_wcecl.cpp @@ -2,6 +2,47 @@ #include #include +/* 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; @@ -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, @@ -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; @@ -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; } \ No newline at end of file diff --git a/COREDLL/stdio_wcecl.h b/COREDLL/stdio_wcecl.h new file mode 100644 index 0000000..84d2680 --- /dev/null +++ b/COREDLL/stdio_wcecl.h @@ -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);