/* * Win32 kernel functions * * Copyright 1995 Martin von Loewis and Cameron Heide * Copyright 1997 Karl Garrison * Copyright 1998 John Richardson * Copyright 1998 Marcus Meissner * Copyright 2001 Eric Pouech * Copyright 2001 Alexandre Julliard */ /* Reference applications: * - IDA (interactive disassembler) full version 3.75. Works. * - LYNX/W32. Works mostly, some keys crash it. */ #include "config.h" #include #include #include #include #include "winbase.h" #include "winnls.h" #include "winerror.h" #include "wincon.h" #include "wine/server.h" #include "wine/exception.h" #include "wine/unicode.h" #include "debugtools.h" DEFAULT_DEBUG_CHANNEL(console); static UINT console_input_codepage; static UINT console_output_codepage; /* map input records to ASCII */ static void input_records_WtoA( INPUT_RECORD *buffer, int count ) { int i; char ch; for (i = 0; i < count; i++) { if (buffer[i].EventType != KEY_EVENT) continue; WideCharToMultiByte( GetConsoleCP(), 0, &buffer[i].Event.KeyEvent.uChar.UnicodeChar, 1, &ch, 1, NULL, NULL ); buffer[i].Event.KeyEvent.uChar.AsciiChar = ch; } } /* map input records to Unicode */ static void input_records_AtoW( INPUT_RECORD *buffer, int count ) { int i; WCHAR ch; for (i = 0; i < count; i++) { if (buffer[i].EventType != KEY_EVENT) continue; MultiByteToWideChar( GetConsoleCP(), 0, &buffer[i].Event.KeyEvent.uChar.AsciiChar, 1, &ch, 1 ); buffer[i].Event.KeyEvent.uChar.UnicodeChar = ch; } } /* map char infos to ASCII */ static void char_info_WtoA( CHAR_INFO *buffer, int count ) { char ch; while (count-- > 0) { WideCharToMultiByte( GetConsoleOutputCP(), 0, &buffer->Char.UnicodeChar, 1, &ch, 1, NULL, NULL ); buffer->Char.AsciiChar = ch; buffer++; } } /* map char infos to Unicode */ static void char_info_AtoW( CHAR_INFO *buffer, int count ) { WCHAR ch; while (count-- > 0) { MultiByteToWideChar( GetConsoleOutputCP(), 0, &buffer->Char.AsciiChar, 1, &ch, 1 ); buffer->Char.UnicodeChar = ch; buffer++; } } /****************************************************************************** * GetConsoleCP [KERNEL32.@] Returns the OEM code page for the console * * RETURNS * Code page code */ UINT WINAPI GetConsoleCP(VOID) { if (!console_input_codepage) console_input_codepage = GetOEMCP(); return console_input_codepage; } /****************************************************************************** * SetConsoleCP [KERNEL32.@] */ BOOL WINAPI SetConsoleCP(UINT cp) { if (!IsValidCodePage( cp )) return FALSE; console_input_codepage = cp; return TRUE; } /*********************************************************************** * GetConsoleOutputCP (KERNEL32.@) */ UINT WINAPI GetConsoleOutputCP(VOID) { if (!console_output_codepage) console_output_codepage = GetOEMCP(); return console_output_codepage; } /****************************************************************************** * SetConsoleOutputCP [KERNEL32.@] Set the output codepage used by the console * * PARAMS * cp [I] code page to set * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI SetConsoleOutputCP(UINT cp) { if (!IsValidCodePage( cp )) return FALSE; console_output_codepage = cp; return TRUE; } /****************************************************************************** * WriteConsoleInputA [KERNEL32.@] */ BOOL WINAPI WriteConsoleInputA( HANDLE handle, const INPUT_RECORD *buffer, DWORD count, LPDWORD written ) { INPUT_RECORD *recW; BOOL ret; if (!(recW = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*recW) ))) return FALSE; memcpy( recW, buffer, count*sizeof(*recW) ); input_records_AtoW( recW, count ); ret = WriteConsoleInputW( handle, recW, count, written ); HeapFree( GetProcessHeap(), 0, recW ); return ret; } /****************************************************************************** * WriteConsoleInputW [KERNEL32.@] */ BOOL WINAPI WriteConsoleInputW( HANDLE handle, const INPUT_RECORD *buffer, DWORD count, LPDWORD written ) { BOOL ret; TRACE("(%d,%p,%ld,%p)\n", handle, buffer, count, written); if (written) *written = 0; SERVER_START_REQ( write_console_input ) { req->handle = handle; wine_server_add_data( req, buffer, count * sizeof(INPUT_RECORD) ); if ((ret = !wine_server_call_err( req ))) { if (written) *written = reply->written; } } SERVER_END_REQ; return ret; } /*********************************************************************** * WriteConsoleOutputA (KERNEL32.@) */ BOOL WINAPI WriteConsoleOutputA( HANDLE hConsoleOutput, const CHAR_INFO *lpBuffer, COORD size, COORD coord, LPSMALL_RECT region ) { int y; BOOL ret; COORD new_size, new_coord; CHAR_INFO *ciw; new_size.X = min( region->Right - region->Left + 1, size.X - coord.X ); new_size.Y = min( region->Bottom - region->Top + 1, size.Y - coord.Y ); if (new_size.X <= 0 || new_size.Y <= 0) { region->Bottom = region->Top + new_size.Y - 1; region->Right = region->Left + new_size.X - 1; return TRUE; } /* only copy the useful rectangle */ if (!(ciw = HeapAlloc( GetProcessHeap(), 0, sizeof(CHAR_INFO) * new_size.X * new_size.Y ))) return FALSE; for (y = 0; y < new_size.Y; y++) { memcpy( &ciw[y * new_size.X], &lpBuffer[(y + coord.Y) * size.X + coord.X], new_size.X * sizeof(CHAR_INFO) ); char_info_AtoW( ciw, new_size.X ); } new_coord.X = new_coord.Y = 0; ret = WriteConsoleOutputW( hConsoleOutput, ciw, new_size, new_coord, region ); if (ciw) HeapFree( GetProcessHeap(), 0, ciw ); return ret; } /*********************************************************************** * WriteConsoleOutputW (KERNEL32.@) */ BOOL WINAPI WriteConsoleOutputW( HANDLE hConsoleOutput, const CHAR_INFO *lpBuffer, COORD size, COORD coord, LPSMALL_RECT region ) { int width, height, y; BOOL ret = TRUE; TRACE("(%x,%p,(%d,%d),(%d,%d),(%d,%dx%d,%d)\n", hConsoleOutput, lpBuffer, size.X, size.Y, coord.X, coord.Y, region->Left, region->Top, region->Right, region->Bottom); width = min( region->Right - region->Left + 1, size.X - coord.X ); height = min( region->Bottom - region->Top + 1, size.Y - coord.Y ); if (width > 0 && height > 0) { for (y = 0; y < height; y++) { SERVER_START_REQ( write_console_output ) { req->handle = hConsoleOutput; req->x = region->Left; req->y = region->Top + y; req->mode = CHAR_INFO_MODE_TEXTATTR; req->wrap = FALSE; wine_server_add_data( req, &lpBuffer[(y + coord.Y) * size.X + coord.X], width * sizeof(CHAR_INFO)); if ((ret = !wine_server_call_err( req ))) { width = min( width, reply->width - region->Left ); height = min( height, reply->height - region->Top ); } } SERVER_END_REQ; if (!ret) break; } } region->Bottom = region->Top + height - 1; region->Right = region->Left + width - 1; return ret; } /****************************************************************************** * WriteConsoleOutputCharacterA [KERNEL32.@] Copies character to consecutive * cells in the console screen buffer * * PARAMS * hConsoleOutput [I] Handle to screen buffer * str [I] Pointer to buffer with chars to write * length [I] Number of cells to write to * coord [I] Coords of first cell * lpNumCharsWritten [O] Pointer to number of cells written */ BOOL WINAPI WriteConsoleOutputCharacterA( HANDLE hConsoleOutput, LPCSTR str, DWORD length, COORD coord, LPDWORD lpNumCharsWritten ) { BOOL ret; LPWSTR strW; DWORD lenW; TRACE("(%d,%s,%ld,%dx%d,%p)\n", hConsoleOutput, debugstr_an(str, length), length, coord.X, coord.Y, lpNumCharsWritten); lenW = MultiByteToWideChar( GetConsoleOutputCP(), 0, str, length, NULL, 0 ); if (lpNumCharsWritten) *lpNumCharsWritten = 0; if (!(strW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) ))) return FALSE; MultiByteToWideChar( GetConsoleOutputCP(), 0, str, length, strW, lenW ); ret = WriteConsoleOutputCharacterW( hConsoleOutput, strW, lenW, coord, lpNumCharsWritten ); HeapFree( GetProcessHeap(), 0, strW ); return ret; } /****************************************************************************** * WriteConsoleOutputAttribute [KERNEL32.@] Sets attributes for some cells in * the console screen buffer * * PARAMS * hConsoleOutput [I] Handle to screen buffer * attr [I] Pointer to buffer with write attributes * length [I] Number of cells to write to * coord [I] Coords of first cell * lpNumAttrsWritten [O] Pointer to number of cells written * * RETURNS * Success: TRUE * Failure: FALSE * */ BOOL WINAPI WriteConsoleOutputAttribute( HANDLE hConsoleOutput, CONST WORD *attr, DWORD length, COORD coord, LPDWORD lpNumAttrsWritten ) { BOOL ret; TRACE("(%d,%p,%ld,%dx%d,%p)\n", hConsoleOutput,attr,length,coord.X,coord.Y,lpNumAttrsWritten); SERVER_START_REQ( write_console_output ) { req->handle = hConsoleOutput; req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_ATTR; req->wrap = TRUE; wine_server_add_data( req, attr, length * sizeof(WORD) ); if ((ret = !wine_server_call_err( req ))) { if (lpNumAttrsWritten) *lpNumAttrsWritten = reply->written; } } SERVER_END_REQ; return ret; } /****************************************************************************** * FillConsoleOutputCharacterA [KERNEL32.@] * * PARAMS * hConsoleOutput [I] Handle to screen buffer * ch [I] Character to write * length [I] Number of cells to write to * coord [I] Coords of first cell * lpNumCharsWritten [O] Pointer to number of cells written * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI FillConsoleOutputCharacterA( HANDLE hConsoleOutput, CHAR ch, DWORD length, COORD coord, LPDWORD lpNumCharsWritten ) { WCHAR wch; MultiByteToWideChar( GetConsoleOutputCP(), 0, &ch, 1, &wch, 1 ); return FillConsoleOutputCharacterW(hConsoleOutput, wch, length, coord, lpNumCharsWritten); } /****************************************************************************** * FillConsoleOutputCharacterW [KERNEL32.@] Writes characters to console * * PARAMS * hConsoleOutput [I] Handle to screen buffer * ch [I] Character to write * length [I] Number of cells to write to * coord [I] Coords of first cell * lpNumCharsWritten [O] Pointer to number of cells written * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI FillConsoleOutputCharacterW( HANDLE hConsoleOutput, WCHAR ch, DWORD length, COORD coord, LPDWORD lpNumCharsWritten) { BOOL ret; TRACE("(%d,%s,%ld,(%dx%d),%p)\n", hConsoleOutput, debugstr_wn(&ch, 1), length, coord.X, coord.Y, lpNumCharsWritten); SERVER_START_REQ( fill_console_output ) { req->handle = hConsoleOutput; req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_TEXT; req->wrap = TRUE; req->data.ch = ch; req->count = length; if ((ret = !wine_server_call_err( req ))) { if (lpNumCharsWritten) *lpNumCharsWritten = reply->written; } } SERVER_END_REQ; return ret; } /****************************************************************************** * FillConsoleOutputAttribute [KERNEL32.@] Sets attributes for console * * PARAMS * hConsoleOutput [I] Handle to screen buffer * attr [I] Color attribute to write * length [I] Number of cells to write to * coord [I] Coords of first cell * lpNumAttrsWritten [O] Pointer to number of cells written * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI FillConsoleOutputAttribute( HANDLE hConsoleOutput, WORD attr, DWORD length, COORD coord, LPDWORD lpNumAttrsWritten ) { BOOL ret; TRACE("(%d,%d,%ld,(%dx%d),%p)\n", hConsoleOutput, attr, length, coord.X, coord.Y, lpNumAttrsWritten); SERVER_START_REQ( fill_console_output ) { req->handle = hConsoleOutput; req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_ATTR; req->wrap = TRUE; req->data.attr = attr; req->count = length; if ((ret = !wine_server_call_err( req ))) { if (lpNumAttrsWritten) *lpNumAttrsWritten = reply->written; } } SERVER_END_REQ; return ret; } /****************************************************************************** * ReadConsoleOutputCharacterA [KERNEL32.@] * */ BOOL WINAPI ReadConsoleOutputCharacterA(HANDLE hConsoleOutput, LPSTR lpstr, DWORD count, COORD coord, LPDWORD read_count) { DWORD read; BOOL ret; LPWSTR wptr = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR)); if (read_count) *read_count = 0; if (!wptr) return FALSE; if ((ret = ReadConsoleOutputCharacterW( hConsoleOutput, wptr, count, coord, &read ))) { read = WideCharToMultiByte( GetConsoleOutputCP(), 0, wptr, read, lpstr, count, NULL, NULL); if (read_count) *read_count = read; } HeapFree( GetProcessHeap(), 0, wptr ); return ret; } /****************************************************************************** * ReadConsoleOutputCharacterW [KERNEL32.@] * */ BOOL WINAPI ReadConsoleOutputCharacterW( HANDLE hConsoleOutput, LPWSTR buffer, DWORD count, COORD coord, LPDWORD read_count ) { BOOL ret; TRACE( "(%d,%p,%ld,%dx%d,%p)\n", hConsoleOutput, buffer, count, coord.X, coord.Y, read_count ); SERVER_START_REQ( read_console_output ) { req->handle = hConsoleOutput; req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_TEXT; req->wrap = TRUE; wine_server_set_reply( req, buffer, count * sizeof(WCHAR) ); if ((ret = !wine_server_call_err( req ))) { if (read_count) *read_count = wine_server_reply_size(reply) / sizeof(WCHAR); } } SERVER_END_REQ; return ret; } /****************************************************************************** * ReadConsoleOutputAttribute [KERNEL32.@] */ BOOL WINAPI ReadConsoleOutputAttribute(HANDLE hConsoleOutput, LPWORD lpAttribute, DWORD length, COORD coord, LPDWORD read_count) { BOOL ret; TRACE("(%d,%p,%ld,%dx%d,%p)\n", hConsoleOutput, lpAttribute, length, coord.X, coord.Y, read_count); SERVER_START_REQ( read_console_output ) { req->handle = hConsoleOutput; req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_ATTR; req->wrap = TRUE; wine_server_set_reply( req, lpAttribute, length * sizeof(WORD) ); if ((ret = !wine_server_call_err( req ))) { if (read_count) *read_count = wine_server_reply_size(reply) / sizeof(WORD); } } SERVER_END_REQ; return ret; } /****************************************************************************** * ReadConsoleOutputA [KERNEL32.@] * */ BOOL WINAPI ReadConsoleOutputA( HANDLE hConsoleOutput, LPCHAR_INFO lpBuffer, COORD size, COORD coord, LPSMALL_RECT region ) { BOOL ret; int y; ret = ReadConsoleOutputW( hConsoleOutput, lpBuffer, size, coord, region ); if (ret && region->Right >= region->Left) { for (y = 0; y <= region->Bottom - region->Top; y++) { char_info_WtoA( &lpBuffer[(coord.Y + y) * size.X + coord.X], region->Right - region->Left + 1 ); } } return ret; } /****************************************************************************** * ReadConsoleOutputW [KERNEL32.@] * * NOTE: The NT4 (sp5) kernel crashes on me if size is (0,0). I don't * think we need to be *that* compatible. -- AJ */ BOOL WINAPI ReadConsoleOutputW( HANDLE hConsoleOutput, LPCHAR_INFO lpBuffer, COORD size, COORD coord, LPSMALL_RECT region ) { int width, height, y; BOOL ret = TRUE; width = min( region->Right - region->Left + 1, size.X - coord.X ); height = min( region->Bottom - region->Top + 1, size.Y - coord.Y ); if (width > 0 && height > 0) { for (y = 0; y < height; y++) { SERVER_START_REQ( read_console_output ) { req->handle = hConsoleOutput; req->x = region->Left; req->y = region->Top + y; req->mode = CHAR_INFO_MODE_TEXTATTR; req->wrap = FALSE; wine_server_set_reply( req, &lpBuffer[(y+coord.Y) * size.X + coord.X], width * sizeof(CHAR_INFO) ); if ((ret = !wine_server_call_err( req ))) { width = min( width, reply->width - region->Left ); height = min( height, reply->height - region->Top ); } } SERVER_END_REQ; if (!ret) break; } } region->Bottom = region->Top + height - 1; region->Right = region->Left + width - 1; return ret; } /****************************************************************************** * ReadConsoleInputA [KERNEL32.@] Reads data from a console * * PARAMS * handle [I] Handle to console input buffer * buffer [O] Address of buffer for read data * count [I] Number of records to read * pRead [O] Address of number of records read * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI ReadConsoleInputA( HANDLE handle, LPINPUT_RECORD buffer, DWORD count, LPDWORD pRead ) { DWORD read; if (!ReadConsoleInputW( handle, buffer, count, &read )) return FALSE; input_records_WtoA( buffer, read ); if (pRead) *pRead = read; return TRUE; } /*********************************************************************** * PeekConsoleInputA (KERNEL32.@) * * Gets 'count' first events (or less) from input queue. */ BOOL WINAPI PeekConsoleInputA( HANDLE handle, LPINPUT_RECORD buffer, DWORD count, LPDWORD pRead ) { DWORD read; if (!PeekConsoleInputW( handle, buffer, count, &read )) return FALSE; input_records_WtoA( buffer, read ); if (pRead) *pRead = read; return TRUE; } /*********************************************************************** * PeekConsoleInputW (KERNEL32.@) */ BOOL WINAPI PeekConsoleInputW( HANDLE handle, LPINPUT_RECORD buffer, DWORD count, LPDWORD read ) { BOOL ret; SERVER_START_REQ( read_console_input ) { req->handle = handle; req->flush = FALSE; wine_server_set_reply( req, buffer, count * sizeof(INPUT_RECORD) ); if ((ret = !wine_server_call_err( req ))) { if (read) *read = count ? reply->read : 0; } } SERVER_END_REQ; return ret; } /*********************************************************************** * GetNumberOfConsoleInputEvents (KERNEL32.@) */ BOOL WINAPI GetNumberOfConsoleInputEvents( HANDLE handle, LPDWORD nrofevents ) { BOOL ret; SERVER_START_REQ( read_console_input ) { req->handle = handle; req->flush = FALSE; if ((ret = !wine_server_call_err( req ))) { if (nrofevents) *nrofevents = reply->read; } } SERVER_END_REQ; return ret; } /*********************************************************************** * FlushConsoleInputBuffer (KERNEL32.@) */ BOOL WINAPI FlushConsoleInputBuffer( HANDLE handle ) { BOOL ret; SERVER_START_REQ( read_console_input ) { req->handle = handle; req->flush = TRUE; ret = !wine_server_call_err( req ); } SERVER_END_REQ; return ret; } /*********************************************************************** * SetConsoleTitleA (KERNEL32.@) */ BOOL WINAPI SetConsoleTitleA( LPCSTR title ) { LPWSTR titleW; BOOL ret; DWORD len = MultiByteToWideChar( GetConsoleOutputCP(), 0, title, -1, NULL, 0 ); if (!(titleW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)))) return FALSE; MultiByteToWideChar( GetConsoleOutputCP(), 0, title, -1, titleW, len ); ret = SetConsoleTitleW(titleW); HeapFree(GetProcessHeap(), 0, titleW); return ret; } /*********************************************************************** * GetConsoleTitleA (KERNEL32.@) */ DWORD WINAPI GetConsoleTitleA(LPSTR title, DWORD size) { WCHAR *ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * size); DWORD ret; if (!ptr) return 0; ret = GetConsoleTitleW( ptr, size ); if (ret) { WideCharToMultiByte( GetConsoleOutputCP(), 0, ptr, ret + 1, title, size, NULL, NULL); ret = strlen(title); } return ret; } /****************************************************************************** * GetConsoleTitleW [KERNEL32.@] Retrieves title string for console * * PARAMS * title [O] Address of buffer for title * size [I] Size of buffer * * RETURNS * Success: Length of string copied * Failure: 0 */ DWORD WINAPI GetConsoleTitleW(LPWSTR title, DWORD size) { DWORD ret = 0; SERVER_START_REQ( get_console_input_info ) { req->handle = 0; wine_server_set_reply( req, title, (size-1) * sizeof(WCHAR) ); if (!wine_server_call_err( req )) { ret = wine_server_reply_size(reply) / sizeof(WCHAR); title[ret] = 0; } } SERVER_END_REQ; return ret; } /*********************************************************************** * GetLargestConsoleWindowSize (KERNEL32.@) * * NOTE * This should return a COORD, but calling convention for returning * structures is different between Windows and gcc on i386. * * VERSION: [i386] */ #ifdef __i386__ #undef GetLargestConsoleWindowSize DWORD WINAPI GetLargestConsoleWindowSize(HANDLE hConsoleOutput) { COORD c; c.X = 80; c.Y = 24; return *(DWORD *)&c; } #endif /* defined(__i386__) */ /*********************************************************************** * GetLargestConsoleWindowSize (KERNEL32.@) * * NOTE * This should return a COORD, but calling convention for returning * structures is different between Windows and gcc on i386. * * VERSION: [!i386] */ #ifndef __i386__ COORD WINAPI GetLargestConsoleWindowSize(HANDLE hConsoleOutput) { COORD c; c.X = 80; c.Y = 24; return c; } #endif /* defined(__i386__) */