Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
March 2022
- 80 participants
- 2903 messages
[PATCH 2/5] include: Add Windows.Gaming.Input.IHidGameControllerInputSink interface definition.
by Rémi Bernon
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
include/windows.gaming.input.custom.idl | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/include/windows.gaming.input.custom.idl b/include/windows.gaming.input.custom.idl
index 93e5866a324..5a3e94aa978 100644
--- a/include/windows.gaming.input.custom.idl
+++ b/include/windows.gaming.input.custom.idl
@@ -36,6 +36,7 @@ namespace Windows.Gaming.Input.Custom {
typedef struct GameControllerVersionInfo GameControllerVersionInfo;
interface IGameControllerInputSink;
interface IGameControllerProvider;
+ interface IHidGameControllerInputSink;
interface ICustomGameControllerFactory;
interface IGameControllerFactoryManagerStatics;
interface IGameControllerFactoryManagerStatics2;
@@ -96,6 +97,17 @@ namespace Windows.Gaming.Input.Custom {
[propget] HRESULT IsConnected([out, retval] boolean *value);
}
+ [
+ contract(Windows.Foundation.UniversalApiContract, 4.0),
+ uuid(f754c322-182d-40e4-a126-fcee4ffa1e31)
+ ]
+ interface IHidGameControllerInputSink : IInspectable
+ requires Windows.Gaming.Input.Custom.IGameControllerInputSink
+ {
+ HRESULT OnInputReportReceived([in] UINT64 timestamp, [in] BYTE id, [in] UINT32 report_len,
+ [in, size_is(report_len)] BYTE *report_buf);
+ }
+
[
contract(Windows.Foundation.UniversalApiContract, 3.0),
uuid(69a0ae5e-758e-4cbe-ace6-62155fe9126f)
--
2.34.1
March 1, 2022
[PATCH 1/5] include: Add Windows.Gaming.Input.IGameControllerInputSink interface definition.
by Rémi Bernon
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
The Xusb interfaces aren't really required as I don't intend to use them
in the implementation, but I'll use the IIDs to check how the providers
are supposed to be implemented.
include/windows.gaming.input.custom.idl | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/include/windows.gaming.input.custom.idl b/include/windows.gaming.input.custom.idl
index fe20ce3d0cc..93e5866a324 100644
--- a/include/windows.gaming.input.custom.idl
+++ b/include/windows.gaming.input.custom.idl
@@ -34,6 +34,7 @@ namespace Windows.Gaming.Input.Custom {
typedef enum XusbDeviceSubtype XusbDeviceSubtype;
typedef enum XusbDeviceType XusbDeviceType;
typedef struct GameControllerVersionInfo GameControllerVersionInfo;
+ interface IGameControllerInputSink;
interface IGameControllerProvider;
interface ICustomGameControllerFactory;
interface IGameControllerFactoryManagerStatics;
@@ -72,6 +73,16 @@ namespace Windows.Gaming.Input.Custom {
UINT16 Revision;
};
+ [
+ contract(Windows.Foundation.UniversalApiContract, 3.0),
+ uuid(1ff6f922-c640-4c78-a820-9a715c558bcb)
+ ]
+ interface IGameControllerInputSink : IInspectable
+ {
+ HRESULT OnInputResumed([in] UINT64 timestamp);
+ HRESULT OnInputSuspended([in] UINT64 timestamp);
+ }
+
[
contract(Windows.Foundation.UniversalApiContract, 3.0),
uuid(e6d73982-2996-4559-b16c-3e57d46e58d6)
--
2.34.1
March 1, 2022
[PATCH v3 4/4] programs/cmd: implement filename completion
by Eric Pouech
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
programs/cmd/batch.c | 252 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 246 insertions(+), 6 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c
index 9a262c5fec5..34385a12a0e 100644
--- a/programs/cmd/batch.c
+++ b/programs/cmd/batch.c
@@ -227,6 +227,158 @@ WCHAR *WCMD_parameter (WCHAR *s, int n, WCHAR **start, BOOL raw,
return WCMD_parameter_with_delims (s, n, start, raw, wholecmdline, L" \t,=;");
}
+struct completion_info
+{
+ WCHAR** subfiles;
+ DWORD index_search;
+ DWORD num_results;
+};
+
+static WCHAR* dup_quoted(const WCHAR* path, const WCHAR* in)
+{
+ const WCHAR* last = wcsrchr(path, L'/');
+ size_t dirlen;
+ size_t len;
+ WCHAR* ret;
+
+ if (last)
+ {
+ WCHAR* last1 = wcschr(last + 1, L'\\');
+ if (last1) last = last1;
+ }
+ else last = wcsrchr(path, L'\\');
+ dirlen = last ? (last - path) + 1 : 0;
+ len = wcslen(in);
+ ret = malloc((dirlen + len + 1) * sizeof(WCHAR));
+ if (ret)
+ {
+ if (dirlen)
+ {
+ memcpy(ret, path, (dirlen - 1) * sizeof(WCHAR));
+ ret[dirlen - 1] = *last;
+ }
+ memcpy(ret + dirlen, in, len * sizeof(WCHAR));
+ ret[dirlen + len] = L'\0';
+ if (wcspbrk(ret, L" \t\"")) /* need quotes */
+ {
+ WCHAR* new = realloc(ret, (1 + dirlen + len + 1 + 1) * sizeof(WCHAR));
+ if (!new)
+ {
+ free(ret);
+ return NULL;
+ }
+ ret = new;
+ memmove(ret + 1, ret, (dirlen + len) * sizeof(WCHAR));
+ ret[0] = ret[1 + dirlen + len] = L'"';
+ ret[1 + dirlen + len + 1] = L'\0';
+ }
+ }
+ return ret;
+}
+
+static BOOL init_completion(struct completion_info* ci, const WCHAR* from, DWORD len, BOOL forward)
+{
+ WCHAR* ptr;
+ HANDLE hff;
+ WIN32_FIND_DATAW fd;
+ BOOL in_quotes = FALSE;
+ DWORD i, j = 0;
+
+ ptr = malloc((len + 1 + 1) * sizeof(WCHAR));
+ if (!ptr) return FALSE;
+ for (i = 0; i < len; i++)
+ {
+ switch (from[i])
+ {
+ case L'"':
+ in_quotes = !in_quotes;
+ continue;
+ case L'\\':
+ if (!in_quotes && i + 1 < len && wcschr(L" \t\"\\", from[i + 1]) != NULL)
+ ++i;
+ break;
+ case L' ':
+ case L'\t':
+ /* shouldn't happen, as 'from' should contain a single argument */
+ if (!in_quotes)
+ {
+ free(ptr);
+ return FALSE;
+ }
+ break;
+ }
+ ptr[j++] = from[i];
+ }
+ ptr[j] = L'*';
+ ptr[j + 1] = L'\0';
+ hff = FindFirstFileW(ptr, &fd);
+ ci->num_results = 0;
+ ci->subfiles = NULL;
+ if (hff != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ WCHAR** new;
+ if (!wcscmp(fd.cFileName, L".") || !wcscmp(fd.cFileName, L".."))
+ continue;
+ new = realloc(ci->subfiles, (ci->num_results + 1) * sizeof(*ci->subfiles));
+ if (!new)
+ {
+ FindClose(hff);
+ free(ptr);
+ return FALSE;
+ }
+ ci->subfiles = new;
+ if (!(ci->subfiles[ci->num_results++] = dup_quoted(ptr, fd.cFileName)))
+ {
+ FindClose(hff);
+ free(ptr);
+ return FALSE;
+ }
+ } while (FindNextFileW(hff, &fd));
+ FindClose(hff);
+ }
+ free(ptr);
+ if (!ci->num_results) return FALSE;
+ ci->index_search = forward ? 0 : ci->num_results - 1;
+ return TRUE;
+}
+
+static void dispose_completion(struct completion_info* ci)
+{
+ if (ci->subfiles)
+ {
+ unsigned i;
+ for (i = 0; i < ci->num_results; i++)
+ free(ci->subfiles[i]);
+ free(ci->subfiles);
+ }
+}
+
+static WCHAR* next_completion(struct completion_info* ci, const WCHAR* from, DWORD len, BOOL forward)
+{
+ if (!ci->subfiles || len != wcslen(ci->subfiles[ci->index_search]) || /* FIXME better check no wcslen, just check last char */
+ memcmp(ci->subfiles[ci->index_search], from, len * sizeof(WCHAR)))
+ {
+ dispose_completion(ci);
+ if (!init_completion(ci, from, len, forward)) return NULL;
+ }
+ else
+ {
+ if (forward)
+ {
+ if (++ci->index_search >= ci->num_results)
+ ci->index_search = 0;
+ }
+ else
+ {
+ if (ci->index_search-- == 0)
+ ci->index_search = ci->num_results - 1;
+ }
+ }
+ return ci->subfiles[ci->index_search];
+}
+
/****************************************************************************
* WCMD_fgets
*
@@ -249,7 +401,7 @@ WCHAR *WCMD_fgets(WCHAR *buf, DWORD noChars, HANDLE h)
/* We can't use the native f* functions because of the filename syntax differences
between DOS and Unix. Also need to lose the LF (or CRLF) from the line. */
- if (!ReadConsoleW(h, buf, noChars, &charsRead, NULL)) {
+ if (!VerifyConsoleIoHandle(h)) {
LARGE_INTEGER filepos;
char *bufA;
UINT cp;
@@ -282,13 +434,101 @@ WCHAR *WCMD_fgets(WCHAR *buf, DWORD noChars, HANDLE h)
heap_free(bufA);
}
else {
- if (!charsRead) return NULL;
+ CONSOLE_READCONSOLE_CONTROL crc;
+ CONSOLE_SCREEN_BUFFER_INFO sbi;
+ struct completion_info ci = {NULL, 0, 0};
+ DWORD nwritten;
+ HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ crc.nLength = sizeof(crc);
+ crc.nInitialChars = 0;
+ crc.dwCtrlWakeupMask = 1 << '\t';
+ crc.dwConsoleKeyState = 0;
+ GetConsoleScreenBufferInfo(hout, &sbi);
+ for (;;)
+ {
+ WCHAR* ptr;
+ /* we need to 0-terminate the string anyway */
+ if (!ReadConsoleW(h, buf, noChars - 1, &charsRead, &crc) || !charsRead)
+ {
+ dispose_completion(&ci);
+ return NULL;
+ }
- /* Find first EOL */
- for (i = 0; i < charsRead; i++) {
- if (buf[i] == '\n' || buf[i] == '\r')
- break;
+ ptr = wmemchr(buf, L'\t', charsRead);
+ if (ptr)
+ {
+ int n = 0;
+ WCHAR* start;
+ WCHAR* completion;
+
+ /* find start of word containing ptr (if any) */
+ buf[charsRead] = '\0';
+ for (;;)
+ {
+ WCHAR* next;
+ WCHAR* pmt = WCMD_parameter(buf, n, &next, TRUE, FALSE);
+ if (!pmt[0] || next > ptr)
+ {
+ start = ptr;
+ break;
+ }
+ if (ptr <= next + wcslen(pmt))
+ {
+ start = next;
+ break;
+ }
+ n++;
+ }
+ completion = next_completion(&ci, start, ptr - start, !(crc.dwConsoleKeyState & SHIFT_PRESSED));
+ if (completion)
+ {
+ DWORD newlen;
+ if (start - buf + wcslen(completion) + 1 > noChars)
+ {
+ memcpy(start, completion, (start - buf + noChars - 1) * sizeof(WCHAR));
+ buf[noChars - 1] = L'\0';
+ newlen = noChars - 1;
+ }
+ else
+ {
+ wcscpy(start, completion);
+ newlen = wcslen(buf);
+ }
+ SetConsoleCursorPosition(hout, sbi.dwCursorPosition);
+ WriteConsoleW(hout, buf, newlen, &nwritten, NULL);
+ if (newlen < charsRead)
+ {
+ /* clear remaining stuff on input line (FIXME doesn't handle escaped characters) */
+ WCHAR space = L' ';
+ DWORD i;
+ CONSOLE_SCREEN_BUFFER_INFO saved_sbi;
+
+ GetConsoleScreenBufferInfo(hout, &saved_sbi);
+ for (i = newlen; i < charsRead; i++)
+ WriteConsoleW(hout, &space, 1, &nwritten, NULL);
+ SetConsoleCursorPosition(hout, saved_sbi.dwCursorPosition);
+ }
+ charsRead = newlen;
+ buf[charsRead] = L'\0';
+ }
+ else
+ {
+ /* Beep() -- not loud enough, maybe user won't hear it */
+ charsRead = ptr - buf;
+ }
+ crc.nInitialChars = charsRead;
+ continue;
+ }
+
+ /* Find first EOL */
+ for (i = 0; i < charsRead; i++) {
+ if (buf[i] == '\n' || buf[i] == '\r')
+ break;
+ }
+ if (i < charsRead) break;
}
+ dispose_completion(&ci);
}
/* Truncate at EOL (or end of buffer) */
March 1, 2022
[PATCH v3 3/4] programs/conhost/tests: add tests for ReadConsoleW with control
by Eric Pouech
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
programs/conhost/tests/tty.c | 179 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 175 insertions(+), 4 deletions(-)
diff --git a/programs/conhost/tests/tty.c b/programs/conhost/tests/tty.c
index 1857cdd4aad..849986a1c29 100644
--- a/programs/conhost/tests/tty.c
+++ b/programs/conhost/tests/tty.c
@@ -153,6 +153,7 @@ enum req_type
REQ_READ_CONSOLE,
REQ_READ_CONSOLE_A,
REQ_READ_CONSOLE_FILE,
+ REQ_READ_CONSOLE_CONTROL,
REQ_SCROLL,
REQ_SET_ACTIVE,
REQ_SET_CURSOR,
@@ -201,6 +202,12 @@ struct pseudoconsole_req
DWORD count;
COORD coord;
} fill;
+ struct
+ {
+ size_t size;
+ DWORD mask;
+ WCHAR initial[1];
+ } control;
} u;
};
@@ -415,6 +422,21 @@ static void child_read_console_file(HANDLE pipe, size_t size)
ok(ret, "WriteFile failed: %lu\n", GetLastError());
}
+static void child_read_console_control(HANDLE pipe, size_t size, DWORD control, const WCHAR* recall)
+{
+ char tmp[4096];
+ struct pseudoconsole_req *req = (void *)tmp;
+ DWORD count;
+ BOOL ret;
+
+ req->type = REQ_READ_CONSOLE_CONTROL;
+ req->u.control.size = size;
+ req->u.control.mask = control;
+ wcscpy(req->u.control.initial, recall);
+ ret = WriteFile(pipe, req, sizeof(*req) + wcslen(recall) * sizeof(WCHAR), &count, NULL);
+ ok(ret, "WriteFile failed: %lu\n", GetLastError());
+}
+
#define child_expect_read_result(a,b) child_expect_read_result_(__LINE__,a,b)
static void child_expect_read_result_(unsigned int line, HANDLE pipe, const WCHAR *expect)
{
@@ -431,6 +453,25 @@ static void child_expect_read_result_(unsigned int line, HANDLE pipe, const WCHA
ok_(__FILE__,line)(!memcmp(expect, buf, count), "unexpected data %s\n", wine_dbgstr_w(buf));
}
+#define child_expect_read_control_result(a,b,c) child_expect_read_control_result_(__LINE__,a,b,c)
+static void child_expect_read_control_result_(unsigned int line, HANDLE pipe, const WCHAR *expect, DWORD state)
+{
+ size_t exlen = wcslen(expect);
+ WCHAR buf[4096];
+ WCHAR *ptr = (void *)((char *)buf + sizeof(DWORD));
+ DWORD count;
+ BOOL ret;
+
+ ret = ReadFile(pipe, buf, sizeof(buf), &count, NULL);
+ ok_(__FILE__,line)(ret, "ReadFile failed: %lu\n", GetLastError());
+ ok_(__FILE__,line)(count == sizeof(DWORD) + exlen * sizeof(WCHAR), "got %lu, expected %Iu\n",
+ count, sizeof(DWORD) + exlen * sizeof(WCHAR));
+ buf[count / sizeof(WCHAR)] = 0;
+ todo_wine_if(*(DWORD *)buf != state && *(DWORD *)buf == 0)
+ ok_(__FILE__,line)(*(DWORD *)buf == state, "keyboard state: got %lx, expected %lx\n", *(DWORD *)buf, state);
+ ok_(__FILE__,line)(!memcmp(expect, ptr, count - sizeof(DWORD)), "unexpected data %s %s\n", wine_dbgstr_w(ptr), wine_dbgstr_w(expect));
+}
+
#define child_expect_read_result_a(a,b) child_expect_read_result_a_(__LINE__,a,b)
static void child_expect_read_result_a_(unsigned int line, HANDLE pipe, const char *expect)
{
@@ -552,19 +593,25 @@ static void expect_char_key_(unsigned int line, WCHAR ch)
expect_key_pressed_(line, ch, ch, vk, ctrl);
}
-#define test_cursor_pos(a,b) _test_cursor_pos(__LINE__,a,b)
-static void _test_cursor_pos(unsigned line, int expect_x, int expect_y)
+static void fetch_child_sb_info(CONSOLE_SCREEN_BUFFER_INFO *info)
{
struct pseudoconsole_req req = { REQ_GET_SB_INFO };
- CONSOLE_SCREEN_BUFFER_INFO info;
DWORD read;
BOOL ret;
ret = WriteFile(child_pipe, &req, sizeof(req), &read, NULL);
ok(ret, "WriteFile failed: %lu\n", GetLastError());
- ret = ReadFile(child_pipe, &info, sizeof(info), &read, NULL);
+ ret = ReadFile(child_pipe, info, sizeof(*info), &read, NULL);
ok(ret, "ReadFile failed: %lu\n", GetLastError());
+}
+
+#define test_cursor_pos(a,b) _test_cursor_pos(__LINE__,a,b)
+static void _test_cursor_pos(unsigned line, int expect_x, int expect_y)
+{
+ CONSOLE_SCREEN_BUFFER_INFO info;
+
+ fetch_child_sb_info(&info);
ok_(__FILE__,line)(info.dwCursorPosition.X == expect_x, "dwCursorPosition.X = %u, expected %u\n",
info.dwCursorPosition.X, expect_x);
@@ -1276,6 +1323,108 @@ static void test_read_console(void)
expect_empty_output();
}
+static void test_read_console_control(void)
+{
+ CONSOLE_SCREEN_BUFFER_INFO info1, info2;
+ char ctrl;
+ char buf[16];
+ WCHAR bufw[16];
+
+ child_set_input_mode(child_pipe, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
+ ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT | ENABLE_INSERT_MODE |
+ ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_AUTO_POSITION);
+
+ /* test simple behavior */
+ for (ctrl = 0; ctrl < ' '; ctrl++)
+ {
+ /* don't play with fire */
+ if (ctrl == 0 || ctrl == 3 || ctrl == '\n' || ctrl == 27) continue;
+
+ /* Simulate initial characters
+ * Note: as ReadConsole with CONTROL structure will need to back up the cursor
+ * up to the length of the initial characters, all the following tests ensure that
+ * this backup doesn't imply backing up to the previous line.
+ * This is the "regular" behavior when using completion.
+ */
+ child_string_request(REQ_WRITE_CONSOLE, L"\rabc");
+ skip_sequence("\x1b[25l"); /* broken hide cursor */
+ skip_sequence("\x1b[?25l"); /* hide cursor */
+ skip_sequence("\x1b[H");
+ skip_sequence("\r");
+ expect_output_sequence("abc");
+ skip_sequence("\x1b[?25h"); /* show cursor */
+
+ fetch_child_sb_info(&info1);
+ child_read_console_control(child_pipe, 100, 1ul << ctrl, L"abc");
+ strcpy(buf, "def."); buf[3] = ctrl;
+ write_console_pipe(buf);
+ wcscpy(bufw, L"abcdef."); bufw[6] = (WCHAR)ctrl;
+ child_expect_read_control_result(child_pipe, bufw,
+ (ctrl == '\t' || ctrl == '\r') ? 0
+ : ((ctrl == 30 || ctrl == 31) ? (LEFT_CTRL_PRESSED | SHIFT_PRESSED)
+ : LEFT_CTRL_PRESSED));
+ skip_sequence("\x1b[?25l"); /* hide cursor */
+ expect_output_sequence("def");
+ skip_sequence("\x1b[?25h"); /* show cursor */
+ fetch_child_sb_info(&info2);
+ ok(info1.dwCursorPosition.X + 3 == info2.dwCursorPosition.X,
+ "Bad x-position: expected %u => %u but got => %u instead\n",
+ info1.dwCursorPosition.X, info1.dwCursorPosition.X + 3, info2.dwCursorPosition.X);
+ ok(info1.dwCursorPosition.Y == info2.dwCursorPosition.Y, "Cursor shouldn't have changed line\n");
+ }
+
+ /* test two different control characters in input */
+ fetch_child_sb_info(&info1);
+ child_read_console_control(child_pipe, 100, 1ul << '\x01', L"abc");
+ write_console_pipe("d\x02""ef\x01");
+ child_expect_read_control_result(child_pipe, L"abcd\x02""ef\x01", LEFT_CTRL_PRESSED);
+ skip_sequence("\x1b[?25l"); /* hide cursor */
+ expect_output_sequence("d^Bef");
+ skip_sequence("\x1b[?25h"); /* show cursor */
+ fetch_child_sb_info(&info2);
+ ok(info1.dwCursorPosition.X + 5 == info2.dwCursorPosition.X,
+ "Bad x-position: expected %u => %u but got => %u instead\n",
+ info1.dwCursorPosition.X, info1.dwCursorPosition.X + 5, info2.dwCursorPosition.X);
+ ok(info1.dwCursorPosition.Y == info2.dwCursorPosition.Y, "Cursor shouldn't have changed line\n");
+
+ /* test that ctrl character not in mask is handled as without ctrl mask */
+ child_read_console_control(child_pipe, 100, 1ul << '\x01', L"abc");
+ write_console_pipe("d\ref\x01");
+ child_expect_read_control_result(child_pipe, L"abcd\r\n", 0);
+ skip_sequence("\x1b[?25l"); /* hide cursor */
+ expect_output_sequence("d\r\n");
+ skip_sequence("\x1b[?25h"); /* show cursor */
+ expect_empty_output();
+ /* see note above... ditto */
+ child_string_request(REQ_WRITE_CONSOLE, L"abc");
+ skip_sequence("\x1b[?25l"); /* hide cursor */
+ expect_output_sequence("abc");
+ skip_sequence("\x1b[?25h"); /* show cursor */
+
+ child_read_console_control(child_pipe, 100, 1ul << '\x01', L"abc");
+ child_expect_read_control_result(child_pipe, L"abcef\x01", LEFT_CTRL_PRESSED);
+ skip_sequence("\x1b[?25l"); /* hide cursor */
+ expect_output_sequence("ef");
+ skip_sequence("\x1b[?25h"); /* show cursor */
+
+ /* test when output buffer becomes full before control event */
+ child_read_console_control(child_pipe, 4, 1ul << '\x01', L"abc");
+ write_console_pipe("def\x01");
+ child_expect_read_control_result(child_pipe, L"abcd", LEFT_CTRL_PRESSED);
+ skip_sequence("\x1b[?25l"); /* hide cursor */
+ expect_output_sequence("def");
+ skip_sequence("\x1b[?25h"); /* show cursor */
+ expect_empty_output();
+ child_read_console_control(child_pipe, 20, 1ul << '\x01', L"abc");
+ child_expect_read_control_result(child_pipe, L"ef\x01", 0);
+
+ /* TODO: add tests:
+ * - when initial characters go back to previous line
+ * - edition inside initial characters can occur
+ */
+ expect_empty_output();
+}
+
static void test_tty_input(void)
{
INPUT_RECORD ir;
@@ -1481,6 +1630,27 @@ static void child_process(HANDLE pipe)
ok(ret, "WriteFile failed: %lu\n", GetLastError());
break;
+ case REQ_READ_CONSOLE_CONTROL:
+ {
+ CONSOLE_READCONSOLE_CONTROL crc;
+ WCHAR result[1024];
+ WCHAR *ptr = (void *)((char *)result + sizeof(DWORD));
+
+ count = req->u.control.size;
+ memset(result, 0xcc, sizeof(result));
+ crc.nLength = sizeof(crc);
+ crc.dwCtrlWakeupMask = req->u.control.mask;
+ crc.nInitialChars = wcslen(req->u.control.initial);
+ crc.dwConsoleKeyState = 0xa5;
+ memcpy(ptr, req->u.control.initial, crc.nInitialChars * sizeof(WCHAR));
+ ret = ReadConsoleW(input, ptr, count, &count, &crc);
+ ok(ret, "ReadConsoleW failed: %lu\n", GetLastError());
+ *(DWORD *)result = crc.dwConsoleKeyState;
+ ret = WriteFile(pipe, result, sizeof(DWORD) + count * sizeof(WCHAR), NULL, NULL);
+ ok(ret, "WriteFile failed: %lu\n", GetLastError());
+ }
+ break;
+
case REQ_SCROLL:
ret = ScrollConsoleScreenBufferW(output, &req->u.scroll.rect, NULL, req->u.scroll.dst, &req->u.scroll.fill);
ok(ret, "ScrollConsoleScreenBuffer failed: %lu\n", GetLastError());
@@ -1641,6 +1811,7 @@ static void test_pseudoconsole(void)
if (!broken_version)
{
test_tty_output();
+ test_read_console_control();
test_read_console();
test_tty_input();
}
March 1, 2022
[PATCH v3 2/4] dlls/kernelbase, programs/conhost: return key state in ReadConsoleW with control
by Eric Pouech
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
dlls/kernelbase/console.c | 12 ++++++++----
programs/conhost/conhost.c | 10 ++++++++--
programs/conhost/conhost.h | 1 +
3 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/dlls/kernelbase/console.c b/dlls/kernelbase/console.c
index 1f23292d467..b2bb6c53fd9 100644
--- a/dlls/kernelbase/console.c
+++ b/dlls/kernelbase/console.c
@@ -2031,7 +2031,7 @@ BOOL WINAPI ReadConsoleW( HANDLE handle, void *buffer, DWORD length, DWORD *coun
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
- if (!(tmp = HeapAlloc( GetProcessHeap(), 0, sizeof(DWORD) + crc->nInitialChars * sizeof(WCHAR) )))
+ if (!(tmp = HeapAlloc( GetProcessHeap(), 0, sizeof(DWORD) + length * sizeof(WCHAR) )))
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return FALSE;
@@ -2041,9 +2041,13 @@ BOOL WINAPI ReadConsoleW( HANDLE handle, void *buffer, DWORD length, DWORD *coun
memcpy( tmp + sizeof(DWORD), buffer, crc->nInitialChars * sizeof(WCHAR) );
ret = console_ioctl( handle, IOCTL_CONDRV_READ_CONSOLE_CONTROL,
tmp, sizeof(DWORD) + crc->nInitialChars * sizeof(WCHAR),
- buffer, length * sizeof(WCHAR),
- count );
- crc->dwConsoleKeyState = 0;
+ tmp, sizeof(DWORD) + length * sizeof(WCHAR), count );
+ if (ret)
+ {
+ memcpy( &crc->dwConsoleKeyState, tmp, sizeof(DWORD) );
+ *count -= sizeof(DWORD);
+ memcpy( buffer, tmp + sizeof(DWORD), *count );
+ }
HeapFree( GetProcessHeap(), 0, tmp );
}
else
diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c
index b08d26c2641..11e97993fd8 100644
--- a/programs/conhost/conhost.c
+++ b/programs/conhost/conhost.c
@@ -457,6 +457,8 @@ static NTSTATUS read_complete( struct console *console, NTSTATUS status, const v
req->signal = signal;
req->read = 1;
req->status = status;
+ if (console->read_ioctl == IOCTL_CONDRV_READ_CONSOLE_CONTROL)
+ wine_server_add_data( req, &console->key_state, sizeof(console->key_state) );
wine_server_add_data( req, buf, size );
status = wine_server_call( req );
}
@@ -1250,6 +1252,7 @@ static NTSTATUS process_console_input( struct console *console )
struct edit_line *ctx = &console->edit_line;
unsigned int i;
WCHAR ctrl_value = FIRST_NON_CONTROL_CHAR;
+ unsigned int ctrl_keyvalue = 0;
switch (console->read_ioctl)
{
@@ -1297,6 +1300,7 @@ static NTSTATUS process_console_input( struct console *console )
if (ctx->ctrl_mask & (1u << ir.Event.KeyEvent.uChar.UnicodeChar))
{
ctrl_value = ir.Event.KeyEvent.uChar.UnicodeChar;
+ ctrl_keyvalue = ir.Event.KeyEvent.dwControlKeyState;
ctx->status = STATUS_SUCCESS;
TRACE("Found ctrl char in mask: ^%lc %x\n", ir.Event.KeyEvent.uChar.UnicodeChar + '@', ctx->ctrl_mask);
continue;
@@ -1363,6 +1367,7 @@ static NTSTATUS process_console_input( struct console *console )
if (ctrl_value < FIRST_NON_CONTROL_CHAR)
{
edit_line_insert( console, &ctrl_value, 1 );
+ console->key_state = ctrl_keyvalue;
}
else
{
@@ -1404,6 +1409,7 @@ static NTSTATUS read_console( struct console *console, unsigned int ioctl, size_
}
console->read_ioctl = ioctl;
+ console->key_state = 0;
if (!out_size || console->read_buffer_count)
{
read_from_buffer( console, out_size );
@@ -2562,10 +2568,10 @@ static NTSTATUS console_input_ioctl( struct console *console, unsigned int code,
case IOCTL_CONDRV_READ_CONSOLE_CONTROL:
if ((in_size < sizeof(DWORD)) || ((in_size - sizeof(DWORD)) % sizeof(WCHAR)) ||
- (*out_size % sizeof(WCHAR)))
+ (*out_size < sizeof(DWORD)) || ((*out_size - sizeof(DWORD)) % sizeof(WCHAR)))
return STATUS_INVALID_PARAMETER;
ensure_tty_input_thread( console );
- status = read_console( console, code, *out_size,
+ status = read_console( console, code, *out_size - sizeof(DWORD),
(const WCHAR*)((const char*)in_data + sizeof(DWORD)),
(in_size - sizeof(DWORD)) / sizeof(WCHAR),
*(DWORD*)in_data );
diff --git a/programs/conhost/conhost.h b/programs/conhost/conhost.h
index e5f70aa3ca4..35876689419 100644
--- a/programs/conhost/conhost.h
+++ b/programs/conhost/conhost.h
@@ -88,6 +88,7 @@ struct console
unsigned int read_ioctl; /* current read ioctl */
size_t pending_read; /* size of pending read buffer */
struct edit_line edit_line; /* edit line context */
+ unsigned int key_state;
struct console_window *window;
WCHAR *title; /* console title */
struct history_line **history; /* lines history */
March 1, 2022
[PATCH v3 1/4] kernelbase/console, programs/conhost: support CONSOLE_READCONSOLE_CONTROL in ReadConsoleW
by Eric Pouech
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
dlls/kernelbase/console.c | 32 +++++++++++++-
include/wine/condrv.h | 1
programs/conhost/conhost.c | 104 ++++++++++++++++++++++++++++++++++++--------
programs/conhost/conhost.h | 1
server/console.c | 1
5 files changed, 118 insertions(+), 21 deletions(-)
diff --git a/dlls/kernelbase/console.c b/dlls/kernelbase/console.c
index 1f9eda845a1..1f23292d467 100644
--- a/dlls/kernelbase/console.c
+++ b/dlls/kernelbase/console.c
@@ -2021,8 +2021,36 @@ BOOL WINAPI ReadConsoleW( HANDLE handle, void *buffer, DWORD length, DWORD *coun
return FALSE;
}
- ret = console_ioctl( handle, IOCTL_CONDRV_READ_CONSOLE, NULL, 0, buffer,
- length * sizeof(WCHAR), count );
+ if (reserved)
+ {
+ CONSOLE_READCONSOLE_CONTROL* crc = reserved;
+ char *tmp;
+
+ if (crc->nLength != sizeof(*crc) || crc->nInitialChars >= length)
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+ if (!(tmp = HeapAlloc( GetProcessHeap(), 0, sizeof(DWORD) + crc->nInitialChars * sizeof(WCHAR) )))
+ {
+ SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+ return FALSE;
+ }
+
+ memcpy( tmp, &crc->dwCtrlWakeupMask, sizeof(DWORD) );
+ memcpy( tmp + sizeof(DWORD), buffer, crc->nInitialChars * sizeof(WCHAR) );
+ ret = console_ioctl( handle, IOCTL_CONDRV_READ_CONSOLE_CONTROL,
+ tmp, sizeof(DWORD) + crc->nInitialChars * sizeof(WCHAR),
+ buffer, length * sizeof(WCHAR),
+ count );
+ crc->dwConsoleKeyState = 0;
+ HeapFree( GetProcessHeap(), 0, tmp );
+ }
+ else
+ {
+ ret = console_ioctl( handle, IOCTL_CONDRV_READ_CONSOLE, NULL, 0, buffer,
+ length * sizeof(WCHAR), count );
+ }
if (ret) *count /= sizeof(WCHAR);
return ret;
}
diff --git a/include/wine/condrv.h b/include/wine/condrv.h
index 4515249a673..1000e62e765 100644
--- a/include/wine/condrv.h
+++ b/include/wine/condrv.h
@@ -44,6 +44,7 @@
#define IOCTL_CONDRV_FLUSH CTL_CODE(FILE_DEVICE_CONSOLE, 21, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_CONDRV_GET_WINDOW CTL_CODE(FILE_DEVICE_CONSOLE, 22, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_CONDRV_GET_PROCESS_LIST CTL_CODE(FILE_DEVICE_CONSOLE, 23, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_CONDRV_READ_CONSOLE_CONTROL CTL_CODE(FILE_DEVICE_CONSOLE, 24, METHOD_BUFFERED, FILE_READ_ACCESS)
/* console output ioctls */
#define IOCTL_CONDRV_WRITE_CONSOLE CTL_CODE(FILE_DEVICE_CONSOLE, 30, METHOD_BUFFERED, FILE_WRITE_ACCESS)
diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c
index ae1f4e35f41..b08d26c2641 100644
--- a/programs/conhost/conhost.c
+++ b/programs/conhost/conhost.c
@@ -492,8 +492,9 @@ static void read_from_buffer( struct console *console, size_t out_size )
switch( console->read_ioctl )
{
case IOCTL_CONDRV_READ_CONSOLE:
+ case IOCTL_CONDRV_READ_CONSOLE_CONTROL:
out_size = min( out_size, console->read_buffer_count * sizeof(WCHAR) );
- read_complete( console, STATUS_SUCCESS, console->read_buffer, out_size, console->record_count != 0 );
+ read_complete( console, STATUS_SUCCESS, console->read_buffer, out_size, console->record_count != 0 );
read_len = out_size / sizeof(WCHAR);
break;
case IOCTL_CONDRV_READ_FILE:
@@ -1155,7 +1156,7 @@ static unsigned int edit_line_string_width( const WCHAR *str, unsigned int len)
return offset;
}
-static void update_read_output( struct console *console )
+static void update_read_output( struct console *console, BOOL newline )
{
struct screen_buffer *screen_buffer = console->active;
struct edit_line *ctx = &console->edit_line;
@@ -1203,7 +1204,7 @@ static void update_read_output( struct console *console )
}
}
- if (!ctx->status)
+ if (newline)
{
offset = edit_line_string_width( ctx->buf, ctx->len );
screen_buffer->cursor_x = 0;
@@ -1241,10 +1242,14 @@ static void update_read_output( struct console *console )
update_window_config( screen_buffer->console, TRUE );
}
+/* can end on any ctrl-character: from 0x00 up to 0x1F) */
+#define FIRST_NON_CONTROL_CHAR (L' ')
+
static NTSTATUS process_console_input( struct console *console )
{
struct edit_line *ctx = &console->edit_line;
unsigned int i;
+ WCHAR ctrl_value = FIRST_NON_CONTROL_CHAR;
switch (console->read_ioctl)
{
@@ -1252,6 +1257,7 @@ static NTSTATUS process_console_input( struct console *console )
if (console->record_count) read_console_input( console, console->pending_read );
return STATUS_SUCCESS;
case IOCTL_CONDRV_READ_CONSOLE:
+ case IOCTL_CONDRV_READ_CONSOLE_CONTROL:
case IOCTL_CONDRV_READ_FILE:
break;
default:
@@ -1284,6 +1290,19 @@ static NTSTATUS process_console_input( struct console *console )
/* mask out some bits which don't interest us */
state = ir.Event.KeyEvent.dwControlKeyState & ~(NUMLOCK_ON|SCROLLLOCK_ON|CAPSLOCK_ON|ENHANCED_KEY);
+ if (ctx->ctrl_mask &&
+ ir.Event.KeyEvent.uChar.UnicodeChar &&
+ ir.Event.KeyEvent.uChar.UnicodeChar < FIRST_NON_CONTROL_CHAR)
+ {
+ if (ctx->ctrl_mask & (1u << ir.Event.KeyEvent.uChar.UnicodeChar))
+ {
+ ctrl_value = ir.Event.KeyEvent.uChar.UnicodeChar;
+ ctx->status = STATUS_SUCCESS;
+ TRACE("Found ctrl char in mask: ^%lc %x\n", ir.Event.KeyEvent.uChar.UnicodeChar + '@', ctx->ctrl_mask);
+ continue;
+ }
+ if (ir.Event.KeyEvent.uChar.UnicodeChar == 10) continue;
+ }
func = NULL;
for (map = console->edition_mode ? emacs_key_map : win32_key_map; map->entries != NULL; map++)
{
@@ -1326,7 +1345,7 @@ static NTSTATUS process_console_input( struct console *console )
}
else if (ctx->len >= console->pending_read / sizeof(WCHAR))
ctx->status = STATUS_SUCCESS;
- }
+ }
}
if (console->record_count > i) memmove( console->records, console->records + i,
@@ -1336,19 +1355,26 @@ static NTSTATUS process_console_input( struct console *console )
if (ctx->status == STATUS_PENDING && !(console->mode & ENABLE_LINE_INPUT) && ctx->len)
ctx->status = STATUS_SUCCESS;
- if (console->mode & ENABLE_ECHO_INPUT) update_read_output( console );
+ if (console->mode & ENABLE_ECHO_INPUT) update_read_output( console, !ctx->status && ctrl_value == FIRST_NON_CONTROL_CHAR );
if (ctx->status == STATUS_PENDING) return STATUS_SUCCESS;
if (!ctx->status && (console->mode & ENABLE_LINE_INPUT))
{
- if (ctx->len) append_input_history( console, ctx->buf, ctx->len * sizeof(WCHAR) );
- if (edit_line_grow(console, 2))
+ if (ctrl_value < FIRST_NON_CONTROL_CHAR)
{
- ctx->buf[ctx->len++] = '\r';
- ctx->buf[ctx->len++] = '\n';
- ctx->buf[ctx->len] = 0;
- TRACE( "return %s\n", debugstr_wn( ctx->buf, ctx->len ));
+ edit_line_insert( console, &ctrl_value, 1 );
+ }
+ else
+ {
+ if (ctx->len) append_input_history( console, ctx->buf, ctx->len * sizeof(WCHAR) );
+ if (edit_line_grow(console, 2))
+ {
+ ctx->buf[ctx->len++] = '\r';
+ ctx->buf[ctx->len++] = '\n';
+ ctx->buf[ctx->len] = 0;
+ }
}
+ TRACE( "return %s\n", debugstr_wn( ctx->buf, ctx->len ));
}
console->read_buffer = ctx->buf;
@@ -1365,8 +1391,10 @@ static NTSTATUS process_console_input( struct console *console )
return STATUS_SUCCESS;
}
-static NTSTATUS read_console( struct console *console, unsigned int ioctl, size_t out_size )
+static NTSTATUS read_console( struct console *console, unsigned int ioctl, size_t out_size,
+ const WCHAR *initial, unsigned int initial_len, unsigned int ctrl_mask )
{
+ struct edit_line *ctx = &console->edit_line;
TRACE("\n");
if (out_size > INT_MAX)
@@ -1382,11 +1410,37 @@ static NTSTATUS read_console( struct console *console, unsigned int ioctl, size_
return STATUS_SUCCESS;
}
- console->edit_line.history_index = console->history_index;
- console->edit_line.home_x = console->active->cursor_x;
- console->edit_line.home_y = console->active->cursor_y;
- console->edit_line.status = STATUS_PENDING;
- if (edit_line_grow( console, 1 )) console->edit_line.buf[0] = 0;
+ ctx->history_index = console->history_index;
+ ctx->home_x = console->active->cursor_x;
+ ctx->home_y = console->active->cursor_y;
+ ctx->status = STATUS_PENDING;
+ if (initial_len && edit_line_grow( console, initial_len + 1 ))
+ {
+ unsigned offset = edit_line_string_width( initial, initial_len );
+ if (offset > ctx->home_x)
+ {
+ int deltay;
+ offset -= ctx->home_x;
+ deltay = offset / console->active->width;
+ if (ctx->home_y >= deltay)
+ ctx->home_y -= deltay;
+ else
+ {
+ ctx->home_y = 0;
+ FIXME("Support for negative ordinates is missing\n");
+ }
+ ctx->home_x = console->active->width - 1 - (offset % console->active->width);
+ }
+ else
+ ctx->home_x -= offset;
+ ctx->cursor = initial_len;
+ memcpy( ctx->buf, initial, initial_len * sizeof(WCHAR) );
+ ctx->buf[initial_len] = 0;
+ ctx->len = initial_len;
+ ctx->end_offset = initial_len;
+ }
+ else if (edit_line_grow( console, 1 )) ctx->buf[0] = 0;
+ ctx->ctrl_mask = ctrl_mask;
console->pending_read = out_size;
return process_console_input( console );
@@ -2502,13 +2556,25 @@ static NTSTATUS console_input_ioctl( struct console *console, unsigned int code,
case IOCTL_CONDRV_READ_CONSOLE:
if (in_size || *out_size % sizeof(WCHAR)) return STATUS_INVALID_PARAMETER;
ensure_tty_input_thread( console );
- status = read_console( console, code, *out_size );
+ status = read_console( console, code, *out_size, NULL, 0, 0 );
+ *out_size = 0;
+ return status;
+
+ case IOCTL_CONDRV_READ_CONSOLE_CONTROL:
+ if ((in_size < sizeof(DWORD)) || ((in_size - sizeof(DWORD)) % sizeof(WCHAR)) ||
+ (*out_size % sizeof(WCHAR)))
+ return STATUS_INVALID_PARAMETER;
+ ensure_tty_input_thread( console );
+ status = read_console( console, code, *out_size,
+ (const WCHAR*)((const char*)in_data + sizeof(DWORD)),
+ (in_size - sizeof(DWORD)) / sizeof(WCHAR),
+ *(DWORD*)in_data );
*out_size = 0;
return status;
case IOCTL_CONDRV_READ_FILE:
ensure_tty_input_thread( console );
- status = read_console( console, code, *out_size );
+ status = read_console( console, code, *out_size, NULL, 0, 0 );
*out_size = 0;
return status;
diff --git a/programs/conhost/conhost.h b/programs/conhost/conhost.h
index 3cbd2ed2d80..e5f70aa3ca4 100644
--- a/programs/conhost/conhost.h
+++ b/programs/conhost/conhost.h
@@ -69,6 +69,7 @@ struct edit_line
unsigned int end_offset; /* offset of the last written char */
unsigned int home_x; /* home position */
unsigned int home_y;
+ unsigned int ctrl_mask; /* mask for ctrl characters for completion */
};
struct console
diff --git a/server/console.c b/server/console.c
index 3fb7d8ab237..5f3f50d006f 100644
--- a/server/console.c
+++ b/server/console.c
@@ -952,6 +952,7 @@ static int is_blocking_read_ioctl( unsigned int code )
{
case IOCTL_CONDRV_READ_INPUT:
case IOCTL_CONDRV_READ_CONSOLE:
+ case IOCTL_CONDRV_READ_CONSOLE_CONTROL:
case IOCTL_CONDRV_READ_FILE:
return 1;
default:
March 1, 2022
[PATCH v3 0/4] Supporting filename completion in cmd.exe
by Eric Pouech
This serie implements:
- filename completion in cmd
- CONSOLE_READCONSOLE_CONTROL parameter in ReadConsoleW
v2 -> v3:
+ rebased (the shift-tab patches from V2 have been committed)
+ fixed inverted logic in ordinate cursor computation
+ added FIXME for negative ordinate
+ some cleanups (white space, debugging cruft)
v1 -> v2:
+ added tests in conhost for that
(Jacek: thanks for warning, but it's a real pain
+ minor updates thanks to the tests
A+
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
Eric Pouech (4):
kernelbase/console, programs/conhost: support CONSOLE_READCONSOLE_CONTROL in ReadConsoleW
dlls/kernelbase, programs/conhost: return key state in ReadConsoleW with control
programs/conhost/tests: add tests for ReadConsoleW with control
programs/cmd: implement filename completion
dlls/kernelbase/console.c | 36 ++++-
include/wine/condrv.h | 1 +
programs/cmd/batch.c | 252 ++++++++++++++++++++++++++++++++++-
programs/conhost/conhost.c | 110 ++++++++++++---
programs/conhost/conhost.h | 2 +
programs/conhost/tests/tty.c | 179 ++++++++++++++++++++++++-
server/console.c | 1 +
7 files changed, 550 insertions(+), 31 deletions(-)
March 1, 2022
[PATCH 5/5] dinput/tests: Add tests for IGameControllerFactoryManagerStatics_RegisterCustomFactoryForHardwareId.
by Rémi Bernon
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
dlls/dinput/tests/hotplug.c | 141 +++++++++++++++++++++++++++++++++++-
1 file changed, 139 insertions(+), 2 deletions(-)
diff --git a/dlls/dinput/tests/hotplug.c b/dlls/dinput/tests/hotplug.c
index 2f30cc19d9d..e8f5157d73f 100644
--- a/dlls/dinput/tests/hotplug.c
+++ b/dlls/dinput/tests/hotplug.c
@@ -45,7 +45,8 @@
#define WIDL_using_Windows_Foundation_Collections
#include "windows.foundation.h"
#define WIDL_using_Windows_Gaming_Input
-#include "windows.gaming.input.h"
+#define WIDL_using_Windows_Gaming_Input_Custom
+#include "windows.gaming.input.custom.h"
#define MAKE_FUNC(f) static typeof(f) *p ## f
MAKE_FUNC( RoGetActivationFactory );
@@ -527,6 +528,104 @@ static const IEventHandler_RawGameControllerVtbl controller_handler_vtbl =
static struct controller_handler controller_removed = {{&controller_handler_vtbl}};
static struct controller_handler controller_added = {{&controller_handler_vtbl}};
+struct custom_factory
+{
+ ICustomGameControllerFactory ICustomGameControllerFactory_iface;
+ BOOL create_controller_called;
+};
+
+static inline struct custom_factory *impl_from_ICustomGameControllerFactory( ICustomGameControllerFactory *iface )
+{
+ return CONTAINING_RECORD( iface, struct custom_factory, ICustomGameControllerFactory_iface );
+}
+
+static HRESULT WINAPI custom_factory_QueryInterface( ICustomGameControllerFactory *iface, REFIID iid, void **out )
+{
+ struct custom_factory *impl = impl_from_ICustomGameControllerFactory( iface );
+
+ if (IsEqualGUID( iid, &IID_IUnknown ) ||
+ IsEqualGUID( iid, &IID_IInspectable ) ||
+ IsEqualGUID( iid, &IID_IAgileObject ) ||
+ IsEqualGUID( iid, &IID_ICustomGameControllerFactory ))
+ {
+ IInspectable_AddRef( (*out = &impl->ICustomGameControllerFactory_iface) );
+ return S_OK;
+ }
+
+ ok( 0, "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
+ *out = NULL;
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI custom_factory_AddRef( ICustomGameControllerFactory *iface )
+{
+ return 2;
+}
+
+static ULONG WINAPI custom_factory_Release( ICustomGameControllerFactory *iface )
+{
+ return 1;
+}
+
+static HRESULT WINAPI custom_factory_GetIids( ICustomGameControllerFactory *iface, ULONG *iid_count, IID **iids )
+{
+ ok( 0, "unexpected call\n" );
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI custom_factory_GetRuntimeClassName( ICustomGameControllerFactory *iface, HSTRING *class_name )
+{
+ ok( 0, "unexpected call\n" );
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI custom_factory_GetTrustLevel( ICustomGameControllerFactory *iface, TrustLevel *trust_level )
+{
+ ok( 0, "unexpected call\n" );
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI custom_factory_CreateGameController( ICustomGameControllerFactory *iface, IGameControllerProvider *provider,
+ IInspectable **value )
+{
+ struct custom_factory *impl = impl_from_ICustomGameControllerFactory( iface );
+
+ ok( !controller_added.invoked, "controller added handler invoked\n" );
+ ok( !impl->create_controller_called, "unexpected call\n" );
+ impl->create_controller_called = TRUE;
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI custom_factory_OnGameControllerAdded( ICustomGameControllerFactory *iface, IGameController *value )
+{
+ ok( 0, "unexpected call\n" );
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI custom_factory_OnGameControllerRemoved( ICustomGameControllerFactory *iface, IGameController *value )
+{
+ ok( 0, "unexpected call\n" );
+ return E_NOTIMPL;
+}
+
+static const struct ICustomGameControllerFactoryVtbl custom_factory_vtbl =
+{
+ custom_factory_QueryInterface,
+ custom_factory_AddRef,
+ custom_factory_Release,
+ /* IInspectable methods */
+ custom_factory_GetIids,
+ custom_factory_GetRuntimeClassName,
+ custom_factory_GetTrustLevel,
+ /* ICustomGameControllerFactory methods */
+ custom_factory_CreateGameController,
+ custom_factory_OnGameControllerAdded,
+ custom_factory_OnGameControllerRemoved,
+};
+
+static struct custom_factory custom_factory = {{&custom_factory_vtbl}};
+
static LRESULT CALLBACK windows_gaming_input_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
if (msg == WM_DEVICECHANGE)
@@ -552,6 +651,7 @@ static LRESULT CALLBACK windows_gaming_input_wndproc( HWND hwnd, UINT msg, WPARA
static void test_windows_gaming_input(void)
{
+ static const WCHAR *manager_class_name = RuntimeClass_Windows_Gaming_Input_Custom_GameControllerFactoryManager;
static const WCHAR *class_name = RuntimeClass_Windows_Gaming_Input_RawGameController;
DEV_BROADCAST_DEVICEINTERFACE_A iface_filter_a =
{
@@ -566,6 +666,10 @@ static void test_windows_gaming_input(void)
.lpszClassName = L"devnotify",
.lpfnWndProc = windows_gaming_input_wndproc,
};
+ IGameControllerFactoryManagerStatics2 *manager_statics2;
+ IRawGameController *raw_controller, *tmp_raw_controller;
+ IGameController *game_controller, *tmp_game_controller;
+ IGameControllerFactoryManagerStatics *manager_statics;
EventRegistrationToken controller_removed_token;
IVectorView_RawGameController *controller_view;
EventRegistrationToken controller_added_token;
@@ -594,11 +698,24 @@ static void test_windows_gaming_input(void)
return;
}
+ hr = pWindowsCreateString( manager_class_name, wcslen( manager_class_name ), &str );
+ ok( hr == S_OK, "WindowsCreateString failed, hr %#lx\n", hr );
+ hr = pRoGetActivationFactory( str, &IID_IGameControllerFactoryManagerStatics, (void **)&manager_statics );
+ ok( hr == S_OK, "RoGetActivationFactory failed, hr %#lx\n", hr );
+ hr = pRoGetActivationFactory( str, &IID_IGameControllerFactoryManagerStatics2, (void **)&manager_statics2 );
+ ok( hr == S_OK, "RoGetActivationFactory failed, hr %#lx\n", hr );
+ pWindowsDeleteString( str );
+
controller_added.event = CreateEventW( NULL, FALSE, FALSE, NULL );
ok( !!controller_added.event, "CreateEventW failed, error %lu\n", GetLastError() );
controller_removed.event = CreateEventW( NULL, FALSE, FALSE, NULL );
ok( !!controller_removed.event, "CreateEventW failed, error %lu\n", GetLastError() );
+ hr = IGameControllerFactoryManagerStatics_RegisterCustomFactoryForHardwareId( manager_statics, &custom_factory.ICustomGameControllerFactory_iface,
+ LOWORD(EXPECT_VIDPID), HIWORD(EXPECT_VIDPID) );
+ todo_wine
+ ok( hr == S_OK, "RegisterCustomFactoryForHardwareId returned %#lx\n", hr );
+
hr = IRawGameControllerStatics_add_RawGameControllerAdded( statics, &controller_added.IEventHandler_RawGameController_iface,
&controller_added_token );
ok( hr == S_OK, "add_RawGameControllerAdded returned %#lx\n", hr );
@@ -633,6 +750,7 @@ static void test_windows_gaming_input(void)
ok( controller_added.invoked, "controller added handler not invoked\n" );
ok( !controller_removed.invoked, "controller removed handler invoked\n" );
+ ok( custom_factory.create_controller_called, "CreateGameController not called\n" );
hr = IVectorView_RawGameController_get_Size( controller_view, &size );
ok( hr == S_OK, "get_Size returned %#lx\n", hr );
@@ -645,6 +763,23 @@ static void test_windows_gaming_input(void)
hr = IVectorView_RawGameController_get_Size( controller_view, &size );
ok( hr == S_OK, "get_Size returned %#lx\n", hr );
ok( size == 1, "got size %u\n", size );
+ hr = IVectorView_RawGameController_GetAt( controller_view, 0, &raw_controller );
+ ok( hr == S_OK, "GetAt returned %#lx\n", hr );
+ hr = IRawGameController_QueryInterface( raw_controller, &IID_IGameController, (void **)&game_controller );
+ ok( hr == S_OK, "QueryInterface returned %#lx\n", hr );
+
+ hr = IGameControllerFactoryManagerStatics2_TryGetFactoryControllerFromGameController( manager_statics2,
+ &custom_factory.ICustomGameControllerFactory_iface, game_controller, &tmp_game_controller );
+ ok( hr == S_OK, "TryGetFactoryControllerFromGameController returned %#lx\n", hr );
+ ok( !tmp_game_controller, "got controller %p\n", tmp_game_controller );
+
+ hr = IRawGameControllerStatics_FromGameController( statics, game_controller, &tmp_raw_controller );
+ ok( hr == S_OK, "FromGameController returned %#lx\n", hr );
+ ok( tmp_raw_controller == raw_controller, "got controller %p\n", tmp_raw_controller );
+ IRawGameController_Release( tmp_raw_controller );
+
+ IGameController_Release( game_controller );
+ IRawGameController_Release( raw_controller );
SetEvent( stop_event );
wait_for_events( 1, &controller_removed.event, INFINITE );
@@ -672,8 +807,10 @@ static void test_windows_gaming_input(void)
ok( hr == S_OK, "remove_RawGameControllerRemoved returned %#lx\n", hr );
IVectorView_RawGameController_Release( controller_view );
- IRawGameControllerStatics_Release( statics );
+ IGameControllerFactoryManagerStatics2_Release( manager_statics2 );
+ IGameControllerFactoryManagerStatics_Release( manager_statics );
+ IRawGameControllerStatics_Release( statics );
WaitForSingleObject( thread, INFINITE );
CloseHandle( thread );
CloseHandle( stop_event );
--
2.34.1
March 1, 2022
[PATCH 4/5] windows.gaming.input: Register Windows.Gaming.Input.Custom classes.
by Rémi Bernon
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
dlls/windows.gaming.input/classes.idl | 15 +++++++++++++++
include/windows.gaming.input.custom.idl | 2 ++
include/windows.gaming.input.idl | 2 ++
3 files changed, 19 insertions(+)
diff --git a/dlls/windows.gaming.input/classes.idl b/dlls/windows.gaming.input/classes.idl
index c9cb91e3182..fd52d8ebf66 100644
--- a/dlls/windows.gaming.input/classes.idl
+++ b/dlls/windows.gaming.input/classes.idl
@@ -20,4 +20,19 @@
#pragma makedep register
+#ifdef __WIDL__
+#pragma winrt ns_prefix
+#endif
+
+import "inspectable.idl";
+import "asyncinfo.idl";
+import "eventtoken.idl";
+import "windowscontracts.idl";
+import "windows.foundation.idl";
+import "windows.devices.haptics.idl";
+import "windows.gaming.input.forcefeedback.idl";
+import "windows.system.idl";
+
+#define DO_NO_IMPORTS
#include "windows.gaming.input.idl"
+#include "windows.gaming.input.custom.idl"
diff --git a/include/windows.gaming.input.custom.idl b/include/windows.gaming.input.custom.idl
index a95ec9db482..fe20ce3d0cc 100644
--- a/include/windows.gaming.input.custom.idl
+++ b/include/windows.gaming.input.custom.idl
@@ -20,6 +20,7 @@
#pragma winrt ns_prefix
#endif
+#ifndef DO_NO_IMPORTS
import "inspectable.idl";
import "asyncinfo.idl";
import "eventtoken.idl";
@@ -27,6 +28,7 @@ import "windowscontracts.idl";
import "windows.foundation.idl";
import "windows.gaming.input.idl";
import "windows.storage.streams.idl";
+#endif
namespace Windows.Gaming.Input.Custom {
typedef enum XusbDeviceSubtype XusbDeviceSubtype;
diff --git a/include/windows.gaming.input.idl b/include/windows.gaming.input.idl
index a7d5e71381d..90253b3a8f6 100644
--- a/include/windows.gaming.input.idl
+++ b/include/windows.gaming.input.idl
@@ -20,6 +20,7 @@
#pragma winrt ns_prefix
#endif
+#ifndef DO_NO_IMPORTS
import "inspectable.idl";
import "asyncinfo.idl";
import "eventtoken.idl";
@@ -28,6 +29,7 @@ import "windows.foundation.idl";
import "windows.devices.haptics.idl";
import "windows.gaming.input.forcefeedback.idl";
import "windows.system.idl";
+#endif
namespace Windows.Gaming.Input {
typedef enum GamepadButtons GamepadButtons;
--
2.34.1
March 1, 2022
[PATCH 3/5] windows.gaming.input: Stub GameControllerFactoryManager runtimeclass.
by Rémi Bernon
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
dlls/windows.gaming.input/Makefile.in | 1 +
dlls/windows.gaming.input/main.c | 2 +
dlls/windows.gaming.input/manager.c | 201 ++++++++++++++++++++++++++
dlls/windows.gaming.input/private.h | 4 +-
4 files changed, 207 insertions(+), 1 deletion(-)
create mode 100644 dlls/windows.gaming.input/manager.c
diff --git a/dlls/windows.gaming.input/Makefile.in b/dlls/windows.gaming.input/Makefile.in
index 5031e72f08d..d7f93283bec 100644
--- a/dlls/windows.gaming.input/Makefile.in
+++ b/dlls/windows.gaming.input/Makefile.in
@@ -5,6 +5,7 @@ C_SRCS = \
controller.c \
gamepad.c \
main.c \
+ manager.c \
vector.c
IDL_SRCS = classes.idl
diff --git a/dlls/windows.gaming.input/main.c b/dlls/windows.gaming.input/main.c
index aac21a0c33c..c050d1d5113 100644
--- a/dlls/windows.gaming.input/main.c
+++ b/dlls/windows.gaming.input/main.c
@@ -49,6 +49,8 @@ HRESULT WINAPI DllGetActivationFactory( HSTRING class_str, IActivationFactory **
IActivationFactory_AddRef( (*factory = controller_factory) );
if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_Gamepad ))
IActivationFactory_AddRef( (*factory = gamepad_factory) );
+ if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_Custom_GameControllerFactoryManager ))
+ IActivationFactory_AddRef( (*factory = manager_factory) );
if (*factory) return S_OK;
return REGDB_E_CLASSNOTREG;
diff --git a/dlls/windows.gaming.input/manager.c b/dlls/windows.gaming.input/manager.c
new file mode 100644
index 00000000000..04f166b036f
--- /dev/null
+++ b/dlls/windows.gaming.input/manager.c
@@ -0,0 +1,201 @@
+/* WinRT Windows.Gaming.Input implementation
+ *
+ * Copyright 2021 Rémi Bernon for CodeWeavers
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "private.h"
+
+#include "wine/debug.h"
+#include "wine/list.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(input);
+
+struct manager_statics
+{
+ IActivationFactory IActivationFactory_iface;
+ IGameControllerFactoryManagerStatics IGameControllerFactoryManagerStatics_iface;
+ IGameControllerFactoryManagerStatics2 IGameControllerFactoryManagerStatics2_iface;
+ LONG ref;
+};
+
+static inline struct manager_statics *impl_from_IActivationFactory( IActivationFactory *iface )
+{
+ return CONTAINING_RECORD( iface, struct manager_statics, IActivationFactory_iface );
+}
+
+static HRESULT WINAPI factory_QueryInterface( IActivationFactory *iface, REFIID iid, void **out )
+{
+ struct manager_statics *impl = impl_from_IActivationFactory( iface );
+
+ TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out );
+
+ if (IsEqualGUID( iid, &IID_IUnknown ) ||
+ IsEqualGUID( iid, &IID_IInspectable ) ||
+ IsEqualGUID( iid, &IID_IAgileObject ) ||
+ IsEqualGUID( iid, &IID_IActivationFactory ))
+ {
+ IInspectable_AddRef( (*out = &impl->IActivationFactory_iface) );
+ return S_OK;
+ }
+
+ if (IsEqualGUID( iid, &IID_IGameControllerFactoryManagerStatics ))
+ {
+ IInspectable_AddRef( (*out = &impl->IGameControllerFactoryManagerStatics_iface) );
+ return S_OK;
+ }
+
+ if (IsEqualGUID( iid, &IID_IGameControllerFactoryManagerStatics2 ))
+ {
+ IInspectable_AddRef( (*out = &impl->IGameControllerFactoryManagerStatics2_iface) );
+ return S_OK;
+ }
+
+ FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
+ *out = NULL;
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI factory_AddRef( IActivationFactory *iface )
+{
+ struct manager_statics *impl = impl_from_IActivationFactory( iface );
+ ULONG ref = InterlockedIncrement( &impl->ref );
+ TRACE( "iface %p increasing refcount to %lu.\n", iface, ref );
+ return ref;
+}
+
+static ULONG WINAPI factory_Release( IActivationFactory *iface )
+{
+ struct manager_statics *impl = impl_from_IActivationFactory( iface );
+ ULONG ref = InterlockedDecrement( &impl->ref );
+ TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref );
+ return ref;
+}
+
+static HRESULT WINAPI factory_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids )
+{
+ FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids );
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI factory_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name )
+{
+ FIXME( "iface %p, class_name %p stub!\n", iface, class_name );
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI factory_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level )
+{
+ FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level );
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInspectable **instance )
+{
+ FIXME( "iface %p, instance %p stub!\n", iface, instance );
+ return E_NOTIMPL;
+}
+
+static const struct IActivationFactoryVtbl factory_vtbl =
+{
+ factory_QueryInterface,
+ factory_AddRef,
+ factory_Release,
+ /* IInspectable methods */
+ factory_GetIids,
+ factory_GetRuntimeClassName,
+ factory_GetTrustLevel,
+ /* IActivationFactory methods */
+ factory_ActivateInstance,
+};
+
+DEFINE_IINSPECTABLE( statics, IGameControllerFactoryManagerStatics, struct manager_statics, IActivationFactory_iface )
+
+static HRESULT WINAPI
+statics_RegisterCustomFactoryForGipInterface( IGameControllerFactoryManagerStatics *iface,
+ ICustomGameControllerFactory *factory,
+ GUID interface_id )
+{
+ FIXME( "iface %p, factory %p, interface_id %s stub!\n", iface, factory, debugstr_guid(&interface_id) );
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+statics_RegisterCustomFactoryForHardwareId( IGameControllerFactoryManagerStatics *iface,
+ ICustomGameControllerFactory *factory,
+ UINT16 vendor_id, UINT16 product_id )
+{
+ FIXME( "iface %p, factory %p, vendor_id %u, product_id %u stub!\n", iface, factory, vendor_id, product_id );
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+statics_RegisterCustomFactoryForXusbType( IGameControllerFactoryManagerStatics *iface,
+ ICustomGameControllerFactory *factory,
+ XusbDeviceType type, XusbDeviceSubtype subtype )
+{
+ FIXME( "iface %p, factory %p, type %d, subtype %d stub!\n", iface, factory, type, subtype );
+ return E_NOTIMPL;
+}
+
+static const struct IGameControllerFactoryManagerStaticsVtbl statics_vtbl =
+{
+ statics_QueryInterface,
+ statics_AddRef,
+ statics_Release,
+ /* IInspectable methods */
+ statics_GetIids,
+ statics_GetRuntimeClassName,
+ statics_GetTrustLevel,
+ /* IGameControllerFactoryManagerStatics methods */
+ statics_RegisterCustomFactoryForGipInterface,
+ statics_RegisterCustomFactoryForHardwareId,
+ statics_RegisterCustomFactoryForXusbType,
+};
+
+DEFINE_IINSPECTABLE( statics2, IGameControllerFactoryManagerStatics2, struct manager_statics, IActivationFactory_iface )
+
+static HRESULT WINAPI
+statics2_TryGetFactoryControllerFromGameController( IGameControllerFactoryManagerStatics2 *iface,
+ ICustomGameControllerFactory *factory,
+ IGameController *controller, IGameController **value )
+{
+ FIXME( "iface %p, factory %p, controller %p, value %p stub!\n", iface, factory, controller, value );
+ return E_NOTIMPL;
+}
+
+static const struct IGameControllerFactoryManagerStatics2Vtbl statics2_vtbl =
+{
+ statics2_QueryInterface,
+ statics2_AddRef,
+ statics2_Release,
+ /* IInspectable methods */
+ statics2_GetIids,
+ statics2_GetRuntimeClassName,
+ statics2_GetTrustLevel,
+ /* IGameControllerFactoryManagerStatics2 methods */
+ statics2_TryGetFactoryControllerFromGameController,
+};
+
+static struct manager_statics manager_statics =
+{
+ {&factory_vtbl},
+ {&statics_vtbl},
+ {&statics2_vtbl},
+ 1,
+};
+
+IActivationFactory *manager_factory = &manager_statics.IActivationFactory_iface;
diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h
index 612d11135f4..ba89af9199e 100644
--- a/dlls/windows.gaming.input/private.h
+++ b/dlls/windows.gaming.input/private.h
@@ -32,9 +32,11 @@
#define WIDL_using_Windows_Foundation_Collections
#include "windows.foundation.h"
#define WIDL_using_Windows_Gaming_Input
-#include "windows.gaming.input.h"
+#define WIDL_using_Windows_Gaming_Input_Custom
+#include "windows.gaming.input.custom.h"
extern IActivationFactory *controller_factory;
extern IActivationFactory *gamepad_factory;
+extern IActivationFactory *manager_factory;
extern HRESULT vector_create( REFIID iid, REFIID view_iid, void **out );
--
2.34.1
March 1, 2022