- adapted kernel32 so that it no longer (directly) manages console handles as wineserver handles - console input handle object is no longer waitable (input record synchronisation is now implemented as a simple semaphore), and removed FD_TYPE_CONSOLE from fd types in wineserver - console handles now always have their two lower bit set so one can distinguish a console handle from a kernel object handle - implemented some undocumented kernel32 console related APIs (CloseConsoleHandle, GetConsoleInputWaitHandle, OpenConsoleW, VerifyConsoleIoHandle, DuplicateConsoleHandle) - allowed a few kernel32 APIs to take console pseudo-handles (FlushFileBuffer, GetFileType, WaitFor*Object*) - simplified the console inheritance at process creation - in console tests, no longer create a console if one already exists
diff --git a/dlls/kernel/console.c b/dlls/kernel/console.c index d065178..7b3eba1 100644 --- a/dlls/kernel/console.c +++ b/dlls/kernel/console.c
@@ -48,12 +48,14 @@ #include "wine/debug.h" #include "excpt.h" #include "console_private.h" +#include "kernel_private.h" WINE_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 ) { @@ -189,20 +191,22 @@ DWORD count, LPDWORD written ) { BOOL ret; - + DWORD w; TRACE("(%p,%p,%ld,%p)\n", handle, buffer, count, written); if (written) *written = 0; SERVER_START_REQ( write_console_input ) { - req->handle = handle; + req->handle = console_handle_unmap(handle); wine_server_add_data( req, buffer, count * sizeof(INPUT_RECORD) ); - if ((ret = !wine_server_call_err( req ))) - { - if (written) *written = reply->written; - } + if ((ret = !wine_server_call_err( req ))) w = reply->written; } SERVER_END_REQ; + if (ret) + { + ReleaseSemaphore( GetConsoleInputWaitHandle(), w, NULL ); + if (written) *written = w; + } return ret; } @@ -266,7 +270,7 @@ { SERVER_START_REQ( write_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = region->Left; req->y = region->Top + y; req->mode = CHAR_INFO_MODE_TEXTATTR; @@ -348,7 +352,7 @@ SERVER_START_REQ( write_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_ATTR; @@ -412,7 +416,7 @@ SERVER_START_REQ( fill_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_TEXT; @@ -453,7 +457,7 @@ SERVER_START_REQ( fill_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_ATTR; @@ -507,7 +511,7 @@ SERVER_START_REQ( read_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_TEXT; @@ -536,7 +540,7 @@ SERVER_START_REQ( read_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_ATTR; @@ -596,7 +600,7 @@ { SERVER_START_REQ( read_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = region->Left; req->y = region->Top + y; req->mode = CHAR_INFO_MODE_TEXTATTR; @@ -667,7 +671,7 @@ BOOL ret; SERVER_START_REQ( read_console_input ) { - req->handle = handle; + req->handle = console_handle_unmap(handle); req->flush = FALSE; wine_server_set_reply( req, buffer, count * sizeof(INPUT_RECORD) ); if ((ret = !wine_server_call_err( req ))) @@ -688,7 +692,7 @@ BOOL ret; SERVER_START_REQ( read_console_input ) { - req->handle = handle; + req->handle = console_handle_unmap(handle); req->flush = FALSE; if ((ret = !wine_server_call_err( req ))) { @@ -700,20 +704,46 @@ } +/****************************************************************************** + * read_console_input + * + * Helper function for ReadConsole, ReadConsoleInput and FlushConsoleInputBuffer + * + * Returns + * 0 for error, 1 for no INPUT_RECORD ready, 2 with INPUT_RECORD ready + */ +enum read_console_input_return {rci_error = 0, rci_timeout = 1, rci_gotone = 2}; +static enum read_console_input_return read_console_input(HANDLE handle, LPINPUT_RECORD ir, DWORD timeout) +{ + enum read_console_input_return ret; + + if (WaitForSingleObject(GetConsoleInputWaitHandle(), timeout) != WAIT_OBJECT_0) + return rci_timeout; + SERVER_START_REQ( read_console_input ) + { + req->handle = console_handle_unmap(handle); + req->flush = TRUE; + wine_server_set_reply( req, ir, sizeof(INPUT_RECORD) ); + if (wine_server_call_err( req ) || !reply->read) ret = rci_error; + else ret = rci_gotone; + } + 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; + enum read_console_input_return last; + INPUT_RECORD ir; + + while ((last = read_console_input(handle, &ir, 0)) == rci_gotone); + + return last == rci_timeout; } @@ -990,30 +1020,6 @@ } -/****************************************************************************** - * read_console_input - * - * Helper function for ReadConsole, ReadConsoleInput and PeekConsoleInput - */ -static BOOL read_console_input(HANDLE handle, LPINPUT_RECORD buffer, DWORD count, - LPDWORD pRead, BOOL flush) -{ - BOOL ret; - unsigned read = 0; - - SERVER_START_REQ( read_console_input ) - { - req->handle = handle; - req->flush = flush; - wine_server_set_reply( req, buffer, count * sizeof(INPUT_RECORD) ); - if ((ret = !wine_server_call_err( req ))) read = reply->read; - } - SERVER_END_REQ; - if (pRead) *pRead = read; - return ret; -} - - /*********************************************************************** * ReadConsoleA (KERNEL32.@) */ @@ -1066,23 +1072,26 @@ else { INPUT_RECORD ir; - DWORD count; + DWORD timeout = INFINITE; /* FIXME: should we read at least 1 char? The SDK does not say */ /* wait for at least one available input record (it doesn't mean we'll have - * chars stored in xbuf... + * chars stored in xbuf...) */ - WaitForSingleObject(hConsoleInput, INFINITE); - for (charsread = 0; charsread < nNumberOfCharsToRead;) + charsread = 0; + do { - if (!read_console_input(hConsoleInput, &ir, 1, &count, TRUE)) return FALSE; - if (count && ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown && + if (read_console_input(hConsoleInput, &ir, timeout) != rci_gotone) break; + timeout = 0; + if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown && ir.Event.KeyEvent.uChar.UnicodeChar && !(ir.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY)) { xbuf[charsread++] = ir.Event.KeyEvent.uChar.UnicodeChar; } - } + } while (charsread < nNumberOfCharsToRead); + /* nothing has been read */ + if (timeout == INFINITE) return FALSE; } if (lpNumberOfCharsRead) *lpNumberOfCharsRead = charsread; @@ -1097,7 +1106,8 @@ BOOL WINAPI ReadConsoleInputW(HANDLE hConsoleInput, LPINPUT_RECORD lpBuffer, DWORD nLength, LPDWORD lpNumberOfEventsRead) { - DWORD count; + DWORD idx = 0; + DWORD timeout = INFINITE; if (!nLength) { @@ -1106,17 +1116,12 @@ } /* loop until we get at least one event */ - for (;;) - { - WaitForSingleObject(hConsoleInput, INFINITE); - if (!read_console_input(hConsoleInput, lpBuffer, nLength, &count, TRUE)) - return FALSE; - if (count) - { - if (lpNumberOfEventsRead) *lpNumberOfEventsRead = count; - return TRUE; - } - } + while (read_console_input(hConsoleInput, &lpBuffer[idx], timeout) == rci_gotone && + ++idx < nLength) + timeout = 0; + + if (lpNumberOfEventsRead) *lpNumberOfEventsRead = idx; + return idx != 0; } @@ -1146,7 +1151,7 @@ SERVER_START_REQ( write_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_TEXT; @@ -1474,7 +1479,7 @@ SERVER_START_REQ(get_console_output_info) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); if ((ret = !wine_server_call_err( req ))) { csbi->dwSize.X = reply->width; @@ -1530,7 +1535,7 @@ SERVER_START_REQ(get_console_mode) { - req->handle = hcon; + req->handle = console_handle_unmap(hcon); ret = !wine_server_call_err( req ); if (ret && mode) *mode = reply->mode; } @@ -1563,7 +1568,7 @@ SERVER_START_REQ(set_console_mode) { - req->handle = hcon; + req->handle = console_handle_unmap(hcon); req->mode = mode; ret = !wine_server_call_err( req ); } @@ -1592,7 +1597,7 @@ SERVER_START_REQ( write_console_output ) { - req->handle = hCon; + req->handle = console_handle_unmap(hCon); req->x = pos->X; req->y = pos->Y; req->mode = CHAR_INFO_MODE_TEXTSTDATTR; @@ -1822,7 +1827,7 @@ SERVER_START_REQ(set_console_output_info) { - req->handle = hcon; + req->handle = console_handle_unmap(hcon); req->cursor_x = pos.X; req->cursor_y = pos.Y; req->mask = SET_CONSOLE_OUTPUT_INFO_CURSOR_POS; @@ -1882,7 +1887,7 @@ SERVER_START_REQ(get_console_output_info) { - req->handle = hcon; + req->handle = console_handle_unmap(hcon); ret = !wine_server_call_err( req ); if (ret && cinfo) { @@ -1911,7 +1916,7 @@ SERVER_START_REQ(set_console_output_info) { - req->handle = hCon; + req->handle = console_handle_unmap(hCon); req->cursor_size = cinfo->dwSize; req->cursor_visible = cinfo->bVisible; req->mask = SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM; @@ -1950,7 +1955,7 @@ } SERVER_START_REQ(set_console_output_info) { - req->handle = hCon; + req->handle = console_handle_unmap(hCon); req->win_left = p.Left; req->win_top = p.Top; req->win_right = p.Right; @@ -1980,7 +1985,7 @@ SERVER_START_REQ(set_console_output_info) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->attr = wAttr; req->mask = SET_CONSOLE_OUTPUT_INFO_ATTR; ret = !wine_server_call_err( req ); @@ -2007,7 +2012,7 @@ SERVER_START_REQ(set_console_output_info) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->width = dwSize.X; req->height = dwSize.Y; req->mask = SET_CONSOLE_OUTPUT_INFO_SIZE; @@ -2045,7 +2050,7 @@ { SERVER_START_REQ( fill_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->mode = CHAR_INFO_MODE_TEXTATTR; req->x = i; req->y = j; @@ -2123,7 +2128,7 @@ /* step 3: transfer the bits */ SERVER_START_REQ(move_console_output) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x_src = lpScrollRect->Left; req->y_src = lpScrollRect->Top; req->x_dst = dst.Left; @@ -2254,7 +2259,7 @@ unsigned ret = FALSE; SERVER_START_REQ(get_console_input_info) { - req->handle = hConIn; + req->handle = console_handle_unmap(hConIn); if ((ret = !wine_server_call_err( req ))) *mode = reply->edition_mode; }
diff --git a/dlls/kernel/editline.c b/dlls/kernel/editline.c index 086b2bc..48998cd 100644 --- a/dlls/kernel/editline.c +++ b/dlls/kernel/editline.c
@@ -88,29 +88,10 @@ static BOOL WCEL_Get(WCEL_Context* ctx, INPUT_RECORD* ir) { - DWORD retv; - - for (;;) - { - /* data available ? */ - if (ReadConsoleInputW(ctx->hConIn, ir, 1, &retv) && retv == 1) - return TRUE; - /* then wait... */ - switch (WaitForSingleObject(ctx->hConIn, INFINITE)) - { - case WAIT_OBJECT_0: - break; - default: - /* we have checked that hConIn was a console handle (could be sb) */ - ERR("Shouldn't happen\n"); - /* fall thru */ - case WAIT_ABANDONED: - case WAIT_TIMEOUT: - ctx->error = 1; - ERR("hmm bad situation\n"); - return FALSE; - } - } + if (ReadConsoleInputW(ctx->hConIn, ir, 1, NULL)) return TRUE; + ERR("hmm bad situation\n"); + ctx->error = 1; + return FALSE; } static inline void WCEL_Beep(WCEL_Context* ctx) @@ -633,7 +614,7 @@ static void WCEL_RepeatCount(WCEL_Context* ctx) { #if 0 -/* FIXME: wait untill all console code is in kernel32 */ +/* FIXME: wait until all console code is in kernel32 */ INPUT_RECORD ir; unsigned repeat = 0; @@ -822,11 +803,12 @@ while (!ctx.done && !ctx.error && WCEL_Get(&ctx, &ir)) { - if (ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown) continue; + if (ir.EventType != KEY_EVENT) continue; TRACE("key%s repeatCount=%u, keyCode=%02x scanCode=%02x char=%02x keyState=%08lx\n", ir.Event.KeyEvent.bKeyDown ? "Down" : "Up ", ir.Event.KeyEvent.wRepeatCount, ir.Event.KeyEvent.wVirtualKeyCode, ir.Event.KeyEvent.wVirtualScanCode, ir.Event.KeyEvent.uChar.UnicodeChar, ir.Event.KeyEvent.dwControlKeyState); + if (!ir.Event.KeyEvent.bKeyDown) continue; /* EPP WCEL_Dump(&ctx, "before func"); */ ofs = ctx.ofs;
diff --git a/dlls/kernel/kernel32.spec b/dlls/kernel/kernel32.spec index c572e9d..6797b4d 100644 --- a/dlls/kernel/kernel32.spec +++ b/dlls/kernel/kernel32.spec
@@ -913,7 +913,7 @@ @ stub AddConsoleAliasW @ stub BaseAttachCompleteThunk @ stub BasepDebugDump -@ stub CloseConsoleHandle +@ stdcall CloseConsoleHandle(long) @ stub CmdBatNotification @ stub ConsoleMenuControl @ stub ConsoleSubst @@ -941,7 +941,7 @@ @ stub GetConsoleFontInfo @ stub GetConsoleFontSize @ stub GetConsoleHardwareState -@ stub GetConsoleInputWaitHandle +@ stdcall GetConsoleInputWaitHandle() @ stub GetCurrentConsoleFont @ stub GetNextVDMCommand @ stub GetNumberOfConsoleFonts @@ -953,7 +953,7 @@ @ stub HeapUsage @ stub InvalidateConsoleDIBits @ stdcall IsDebuggerPresent() -@ stub OpenConsoleW +@ stdcall OpenConsoleW(wstr long ptr long) @ stub QueryWin31IniFilesMappedToRegistry @ stub RegisterConsoleVDM @ stub RegisterWaitForInputIdle @@ -976,7 +976,7 @@ @ stub TrimVirtualBuffer @ stub VDMConsoleOperation @ stub VDMOperationStarted -@ stub VerifyConsoleIoHandle +@ stdcall VerifyConsoleIoHandle(long) @ stub VirtualBufferExceptionHandler @ stub WriteConsoleInputVDMA @ stub WriteConsoleInputVDMW @@ -991,7 +991,7 @@ @ stdcall CreateWaitableTimerA(ptr long str) @ stdcall CreateWaitableTimerW(ptr long wstr) @ stdcall DeleteFiber(ptr) -@ stub DuplicateConsoleHandle +@ stdcall DuplicateConsoleHandle(long long long long) @ stdcall FindFirstFileExA(str long ptr long ptr long) @ stdcall FindFirstFileExW(wstr long ptr long ptr long) @ stub GetConsoleInputExeNameA
diff --git a/dlls/kernel/kernel_private.h b/dlls/kernel/kernel_private.h new file mode 100644 index 0000000..fa3b99c --- /dev/null +++ b/dlls/kernel/kernel_private.h
@@ -0,0 +1,47 @@ +/* + * Kernel32 undocumented and private functions definition + * + * Copyright 2003 Eric Pouech + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __WINE_KERNEL_PRIVATE_H +#define __WINE_KERNEL_PRIVATE_H + +HANDLE WINAPI OpenConsoleW(LPCWSTR, DWORD, LPSECURITY_ATTRIBUTES, DWORD); +BOOL WINAPI VerifyConsoleIoHandle(HANDLE); +HANDLE WINAPI DuplicateConsoleHandle(HANDLE, DWORD, BOOL, DWORD); +BOOL WINAPI CloseConsoleHandle(HANDLE handle); +HANDLE WINAPI GetConsoleInputWaitHandle(void); + +static inline BOOL is_console_handle(HANDLE h) +{ + return h != INVALID_HANDLE_VALUE && ((DWORD)h & 3) == 3; +} + +/* map a real wineserver handle onto a kernel32 console handle */ +static inline HANDLE console_handle_map(HANDLE h) +{ + return h != INVALID_HANDLE_VALUE ? (HANDLE)((DWORD)h ^ 3) : INVALID_HANDLE_VALUE; +} + +/* map a kernel32 console handle onto a real wineserver handle */ +static inline HANDLE console_handle_unmap(HANDLE h) +{ + return h != INVALID_HANDLE_VALUE ? (HANDLE)((DWORD)h ^ 3) : INVALID_HANDLE_VALUE; +} + +#endif
diff --git a/dlls/kernel/tests/console.c b/dlls/kernel/tests/console.c index 5dcd115..06cba39 100644 --- a/dlls/kernel/tests/console.c +++ b/dlls/kernel/tests/console.c
@@ -538,7 +538,7 @@ START_TEST(console) { - HANDLE hCon; + HANDLE hConIn, hConOut; BOOL ret; CONSOLE_SCREEN_BUFFER_INFO sbi; @@ -548,16 +548,29 @@ * Another solution would be to rerun the test under wineconsole with * the curses backend */ - FreeConsole(); - AllocConsole(); - hCon = GetStdHandle(STD_OUTPUT_HANDLE); - ok(ret = GetConsoleScreenBufferInfo(hCon, &sbi), "Getting sb info"); + + hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + + /* first, we need to be sure we're attached to a console */ + if (hConIn == INVALID_HANDLE_VALUE || hConOut == INVALID_HANDLE_VALUE) + { + /* we're not attached to a console, let's do it */ + AllocConsole(); + hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + } + /* now verify everything's ok */ + ok(hConIn != INVALID_HANDLE_VALUE, "Opening ConIn"); + ok(hConOut != INVALID_HANDLE_VALUE, "Opening ConOut"); + + ok(ret = GetConsoleScreenBufferInfo(hConOut, &sbi), "Getting sb info"); if (!ret) return; /* Non interactive tests */ - testCursor(hCon, sbi.dwSize); + testCursor(hConOut, sbi.dwSize); /* will test wrapped (on/off) & processed (on/off) strings output */ - testWrite(hCon, sbi.dwSize); + testWrite(hConOut, sbi.dwSize); /* will test line scrolling at the bottom of the screen */ /* testBottomScroll(); */ /* will test all the scrolling operations */