- 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 */